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