1 /* $OpenBSD: gss-serv.c,v 1.28 2015/01/20 23:14:00 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "includes.h" 28 29 #ifdef GSSAPI 30 31 #include <sys/types.h> 32 33 #include <stdarg.h> 34 #include <string.h> 35 #include <unistd.h> 36 37 #include "openbsd-compat/sys-queue.h" 38 #include "xmalloc.h" 39 #include "buffer.h" 40 #include "key.h" 41 #include "hostfile.h" 42 #include "auth.h" 43 #include "log.h" 44 #include "channels.h" 45 #include "session.h" 46 #include "misc.h" 47 48 #include "ssh-gss.h" 49 50 static ssh_gssapi_client gssapi_client = 51 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 52 GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; 53 54 ssh_gssapi_mech gssapi_null_mech = 55 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; 56 57 #ifdef KRB5 58 extern ssh_gssapi_mech gssapi_kerberos_mech; 59 #endif 60 61 ssh_gssapi_mech* supported_mechs[]= { 62 #ifdef KRB5 63 &gssapi_kerberos_mech, 64 #endif 65 &gssapi_null_mech, 66 }; 67 68 /* 69 * ssh_gssapi_supported_oids() can cause sandbox violations, so prepare the 70 * list of supported mechanisms before privsep is set up. 71 */ 72 static gss_OID_set supported_oids; 73 74 void 75 ssh_gssapi_prepare_supported_oids(void) 76 { 77 ssh_gssapi_supported_oids(&supported_oids); 78 } 79 80 OM_uint32 81 ssh_gssapi_test_oid_supported(OM_uint32 *ms, gss_OID member, int *present) 82 { 83 if (supported_oids == NULL) 84 ssh_gssapi_prepare_supported_oids(); 85 return gss_test_oid_set_member(ms, member, supported_oids, present); 86 } 87 88 /* 89 * Acquire credentials for a server running on the current host. 90 * Requires that the context structure contains a valid OID 91 */ 92 93 /* Returns a GSSAPI error code */ 94 /* Privileged (called from ssh_gssapi_server_ctx) */ 95 static OM_uint32 96 ssh_gssapi_acquire_cred(Gssctxt *ctx) 97 { 98 OM_uint32 status; 99 char lname[NI_MAXHOST]; 100 gss_OID_set oidset; 101 102 gss_create_empty_oid_set(&status, &oidset); 103 gss_add_oid_set_member(&status, ctx->oid, &oidset); 104 105 if (gethostname(lname, sizeof(lname))) { 106 gss_release_oid_set(&status, &oidset); 107 return (-1); 108 } 109 110 if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { 111 gss_release_oid_set(&status, &oidset); 112 return (ctx->major); 113 } 114 115 if ((ctx->major = gss_acquire_cred(&ctx->minor, 116 ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL))) 117 ssh_gssapi_error(ctx); 118 119 gss_release_oid_set(&status, &oidset); 120 return (ctx->major); 121 } 122 123 /* Privileged */ 124 OM_uint32 125 ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) 126 { 127 if (*ctx) 128 ssh_gssapi_delete_ctx(ctx); 129 ssh_gssapi_build_ctx(ctx); 130 ssh_gssapi_set_oid(*ctx, oid); 131 return (ssh_gssapi_acquire_cred(*ctx)); 132 } 133 134 /* Unprivileged */ 135 void 136 ssh_gssapi_supported_oids(gss_OID_set *oidset) 137 { 138 int i = 0; 139 OM_uint32 min_status; 140 int present; 141 gss_OID_set supported; 142 143 gss_create_empty_oid_set(&min_status, oidset); 144 gss_indicate_mechs(&min_status, &supported); 145 146 while (supported_mechs[i]->name != NULL) { 147 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 148 &supported_mechs[i]->oid, supported, &present))) 149 present = 0; 150 if (present) 151 gss_add_oid_set_member(&min_status, 152 &supported_mechs[i]->oid, oidset); 153 i++; 154 } 155 156 gss_release_oid_set(&min_status, &supported); 157 } 158 159 160 /* Wrapper around accept_sec_context 161 * Requires that the context contains: 162 * oid 163 * credentials (from ssh_gssapi_acquire_cred) 164 */ 165 /* Privileged */ 166 OM_uint32 167 ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok, 168 gss_buffer_desc *send_tok, OM_uint32 *flags) 169 { 170 OM_uint32 status; 171 gss_OID mech; 172 173 ctx->major = gss_accept_sec_context(&ctx->minor, 174 &ctx->context, ctx->creds, recv_tok, 175 GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech, 176 send_tok, flags, NULL, &ctx->client_creds); 177 178 if (GSS_ERROR(ctx->major)) 179 ssh_gssapi_error(ctx); 180 181 if (ctx->client_creds) 182 debug("Received some client credentials"); 183 else 184 debug("Got no client credentials"); 185 186 status = ctx->major; 187 188 /* Now, if we're complete and we have the right flags, then 189 * we flag the user as also having been authenticated 190 */ 191 192 if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) && 193 (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) { 194 if (ssh_gssapi_getclient(ctx, &gssapi_client)) 195 fatal("Couldn't convert client name"); 196 } 197 198 return (status); 199 } 200 201 /* 202 * This parses an exported name, extracting the mechanism specific portion 203 * to use for ACL checking. It verifies that the name belongs the mechanism 204 * originally selected. 205 */ 206 static OM_uint32 207 ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name) 208 { 209 u_char *tok; 210 OM_uint32 offset; 211 OM_uint32 oidl; 212 213 tok = ename->value; 214 215 /* 216 * Check that ename is long enough for all of the fixed length 217 * header, and that the initial ID bytes are correct 218 */ 219 220 if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0) 221 return GSS_S_FAILURE; 222 223 /* 224 * Extract the OID, and check it. Here GSSAPI breaks with tradition 225 * and does use the OID type and length bytes. To confuse things 226 * there are two lengths - the first including these, and the 227 * second without. 228 */ 229 230 oidl = get_u16(tok+2); /* length including next two bytes */ 231 oidl = oidl-2; /* turn it into the _real_ length of the variable OID */ 232 233 /* 234 * Check the BER encoding for correct type and length, that the 235 * string is long enough and that the OID matches that in our context 236 */ 237 if (tok[4] != 0x06 || tok[5] != oidl || 238 ename->length < oidl+6 || 239 !ssh_gssapi_check_oid(ctx, tok+6, oidl)) 240 return GSS_S_FAILURE; 241 242 offset = oidl+6; 243 244 if (ename->length < offset+4) 245 return GSS_S_FAILURE; 246 247 name->length = get_u32(tok+offset); 248 offset += 4; 249 250 if (UINT_MAX - offset < name->length) 251 return GSS_S_FAILURE; 252 if (ename->length < offset+name->length) 253 return GSS_S_FAILURE; 254 255 name->value = xmalloc(name->length+1); 256 memcpy(name->value, tok+offset, name->length); 257 ((char *)name->value)[name->length] = 0; 258 259 return GSS_S_COMPLETE; 260 } 261 262 /* Extract the client details from a given context. This can only reliably 263 * be called once for a context */ 264 265 /* Privileged (called from accept_secure_ctx) */ 266 OM_uint32 267 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 268 { 269 int i = 0; 270 271 gss_buffer_desc ename; 272 273 client->mech = NULL; 274 275 while (supported_mechs[i]->name != NULL) { 276 if (supported_mechs[i]->oid.length == ctx->oid->length && 277 (memcmp(supported_mechs[i]->oid.elements, 278 ctx->oid->elements, ctx->oid->length) == 0)) 279 client->mech = supported_mechs[i]; 280 i++; 281 } 282 283 if (client->mech == NULL) 284 return GSS_S_FAILURE; 285 286 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 287 &client->displayname, NULL))) { 288 ssh_gssapi_error(ctx); 289 return (ctx->major); 290 } 291 292 if ((ctx->major = gss_export_name(&ctx->minor, ctx->client, 293 &ename))) { 294 ssh_gssapi_error(ctx); 295 return (ctx->major); 296 } 297 298 if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename, 299 &client->exportedname))) { 300 return (ctx->major); 301 } 302 303 /* We can't copy this structure, so we just move the pointer to it */ 304 client->creds = ctx->client_creds; 305 ctx->client_creds = GSS_C_NO_CREDENTIAL; 306 return (ctx->major); 307 } 308 309 /* As user - called on fatal/exit */ 310 void 311 ssh_gssapi_cleanup_creds(void) 312 { 313 if (gssapi_client.store.filename != NULL) { 314 /* Unlink probably isn't sufficient */ 315 debug("removing gssapi cred file\"%s\"", 316 gssapi_client.store.filename); 317 unlink(gssapi_client.store.filename); 318 } 319 } 320 321 /* As user */ 322 void 323 ssh_gssapi_storecreds(void) 324 { 325 if (gssapi_client.mech && gssapi_client.mech->storecreds) { 326 (*gssapi_client.mech->storecreds)(&gssapi_client); 327 } else 328 debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism"); 329 } 330 331 /* This allows GSSAPI methods to do things to the childs environment based 332 * on the passed authentication process and credentials. 333 */ 334 /* As user */ 335 void 336 ssh_gssapi_do_child(char ***envp, u_int *envsizep) 337 { 338 339 if (gssapi_client.store.envvar != NULL && 340 gssapi_client.store.envval != NULL) { 341 debug("Setting %s to %s", gssapi_client.store.envvar, 342 gssapi_client.store.envval); 343 child_set_env(envp, envsizep, gssapi_client.store.envvar, 344 gssapi_client.store.envval); 345 } 346 } 347 348 /* Privileged */ 349 int 350 ssh_gssapi_userok(char *user) 351 { 352 OM_uint32 lmin; 353 354 if (gssapi_client.exportedname.length == 0 || 355 gssapi_client.exportedname.value == NULL) { 356 debug("No suitable client data"); 357 return 0; 358 } 359 if (gssapi_client.mech && gssapi_client.mech->userok) 360 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 361 return 1; 362 else { 363 /* Destroy delegated credentials if userok fails */ 364 gss_release_buffer(&lmin, &gssapi_client.displayname); 365 gss_release_buffer(&lmin, &gssapi_client.exportedname); 366 gss_release_cred(&lmin, &gssapi_client.creds); 367 explicit_bzero(&gssapi_client, 368 sizeof(ssh_gssapi_client)); 369 return 0; 370 } 371 else 372 debug("ssh_gssapi_userok: Unknown GSSAPI mechanism"); 373 return (0); 374 } 375 376 /* Privileged */ 377 OM_uint32 378 ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 379 { 380 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 381 gssbuf, gssmic, NULL); 382 383 return (ctx->major); 384 } 385 386 #endif 387