1 /* 2 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 /* 25 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29 #include "includes.h" 30 31 #ifdef GSSAPI 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include "includes.h" 36 #include "ssh.h" 37 #include "ssh2.h" 38 #include "xmalloc.h" 39 #include "buffer.h" 40 #include "bufaux.h" 41 #include "packet.h" 42 #include "compat.h" 43 #include <openssl/evp.h> 44 #include "cipher.h" 45 #include "kex.h" 46 #include "auth.h" 47 #include "log.h" 48 #include "channels.h" 49 #include "session.h" 50 #include "dispatch.h" 51 #include "servconf.h" 52 #include "uidswap.h" 53 #include "compat.h" 54 #include <pwd.h> 55 56 #include "ssh-gss.h" 57 58 extern char **environ; 59 60 extern ServerOptions options; 61 extern u_char *session_id2; 62 extern int session_id2_len; 63 64 Gssctxt *xxx_gssctxt; 65 66 void 67 ssh_gssapi_server_kex_hook(Kex *kex, char **proposal) 68 { 69 gss_OID_set mechs = GSS_C_NULL_OID_SET; 70 71 if (kex == NULL || !kex->server) 72 fatal("INTERNAL ERROR (%s)", __func__); 73 74 ssh_gssapi_server_mechs(&mechs); 75 ssh_gssapi_modify_kex(kex, mechs, proposal); 76 } 77 78 void 79 ssh_gssapi_server_mechs(gss_OID_set *mechs) 80 { 81 static gss_OID_set supported = GSS_C_NULL_OID_SET; 82 gss_OID_set s, acquired, indicated = GSS_C_NULL_OID_SET; 83 gss_cred_id_t creds; 84 OM_uint32 maj, min; 85 int i; 86 87 if (!mechs) { 88 (void) gss_release_oid_set(&min, &supported); 89 return; 90 } 91 92 if (supported != GSS_C_NULL_OID_SET) { 93 *mechs = supported; 94 return; 95 } 96 97 *mechs = GSS_C_NULL_OID_SET; 98 99 maj = gss_create_empty_oid_set(&min, &s); 100 if (GSS_ERROR(maj)) { 101 debug("Could not allocate GSS-API resources (%s)", 102 ssh_gssapi_last_error(NULL, &maj, &min)); 103 return; 104 } 105 106 maj = gss_indicate_mechs(&min, &indicated); 107 if (GSS_ERROR(maj)) { 108 debug("No GSS-API mechanisms are installed"); 109 return; 110 } 111 112 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, 0, indicated, 113 GSS_C_ACCEPT, &creds, &acquired, NULL); 114 115 if (GSS_ERROR(maj)) 116 debug("Failed to acquire GSS-API credentials for any " 117 "mechanisms (%s)", 118 ssh_gssapi_last_error(NULL, &maj, &min)); 119 120 (void) gss_release_oid_set(&min, &indicated); 121 (void) gss_release_cred(&min, &creds); 122 123 if (acquired == GSS_C_NULL_OID_SET || acquired->count == 0) 124 return; 125 126 for (i = 0 ; i < acquired->count ; i++ ) { 127 if (ssh_gssapi_is_spnego(&acquired->elements[i])) 128 continue; 129 130 maj = gss_add_oid_set_member(&min, &acquired->elements[i], &s); 131 if (GSS_ERROR(maj)) { 132 debug("Could not allocate GSS-API resources (%s)", 133 ssh_gssapi_last_error(NULL, &maj, &min)); 134 return; 135 } 136 } 137 (void) gss_release_oid_set(&min, &acquired); 138 139 if (s->count) { 140 supported = s; 141 *mechs = s; 142 } 143 } 144 145 /* Wrapper around accept_sec_context 146 * Requires that the context contains: 147 * oid 148 * credentials (from ssh_gssapi_acquire_cred) 149 */ 150 /* Priviledged */ 151 OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_t recv_tok, 152 gss_buffer_t send_tok) 153 { 154 /* 155 * Acquiring a cred for the ctx->desired_mech for GSS_C_NO_NAME 156 * may well be probably better than using GSS_C_NO_CREDENTIAL 157 * and then checking that ctx->desired_mech agrees with 158 * ctx->actual_mech... 159 */ 160 ctx->major=gss_accept_sec_context(&ctx->minor, 161 &ctx->context, 162 GSS_C_NO_CREDENTIAL, 163 recv_tok, 164 GSS_C_NO_CHANNEL_BINDINGS, 165 &ctx->src_name, 166 &ctx->actual_mech, 167 send_tok, 168 &ctx->flags, 169 NULL, 170 &ctx->deleg_creds); 171 if (GSS_ERROR(ctx->major)) 172 ssh_gssapi_error(ctx, "accepting security context"); 173 174 if (ctx->major == GSS_S_CONTINUE_NEEDED && send_tok->length == 0) 175 fatal("Zero length GSS context token output when continue needed"); 176 else if (GSS_ERROR(ctx->major) && send_tok->length == 0) 177 debug2("Zero length GSS context error token output"); 178 179 if (ctx->major == GSS_S_COMPLETE && 180 ctx->desired_mech != GSS_C_NULL_OID && 181 (ctx->desired_mech->length != ctx->actual_mech->length || 182 memcmp(ctx->desired_mech->elements, 183 ctx->actual_mech->elements, 184 ctx->desired_mech->length) != 0)) { 185 gss_OID_set supported; 186 OM_uint32 min; 187 int present = 0; 188 189 debug("The client did not use the GSS-API mechanism it asked for"); 190 191 /* Let it slide as long as the mech is supported */ 192 ssh_gssapi_server_mechs(&supported); 193 if (supported != GSS_C_NULL_OID_SET) 194 (void) gss_test_oid_set_member(&min, 195 ctx->actual_mech, 196 supported, &present); 197 if (!present) 198 ctx->major = GSS_S_BAD_MECH; 199 } 200 201 if (ctx->deleg_creds) 202 debug("Received delegated GSS credentials"); 203 204 if (ctx->major == GSS_S_COMPLETE) { 205 ctx->major = gss_inquire_context(&ctx->minor, ctx->context, 206 NULL, &ctx->dst_name, NULL, NULL, 207 NULL, NULL, &ctx->established); 208 209 if (GSS_ERROR(ctx->major)) { 210 ssh_gssapi_error(ctx, "inquiring established sec context"); 211 return (ctx->major); 212 } 213 214 xxx_gssctxt = ctx; 215 } 216 217 return (ctx->major); 218 } 219 220 221 /* As user - called through fatal cleanup hook */ 222 void 223 ssh_gssapi_cleanup_creds(Gssctxt *ctx) 224 { 225 #ifdef HAVE_GSS_STORE_CRED 226 /* pam_setcred() will take care of this */ 227 return; 228 #else 229 return; 230 /*#error "Portability broken in cleanup of stored creds"*/ 231 #endif /* HAVE_GSS_STORE_CRED */ 232 } 233 234 void 235 ssh_gssapi_storecreds(Gssctxt *ctx, Authctxt *authctxt) 236 { 237 #ifdef USE_PAM 238 char **penv, **tmp_env; 239 #endif /* USE_PAM */ 240 241 if (authctxt == NULL) { 242 error("Missing context while storing GSS-API credentials"); 243 return; 244 } 245 246 if (ctx == NULL && xxx_gssctxt == NULL) 247 return; 248 249 if (ctx == NULL) 250 ctx = xxx_gssctxt; 251 252 if (!options.gss_cleanup_creds || 253 ctx->deleg_creds == GSS_C_NO_CREDENTIAL) { 254 debug3("Not storing delegated GSS credentials" 255 " (none delegated)"); 256 return; 257 } 258 259 if (!authctxt->valid || authctxt->pw == NULL) { 260 debug3("Not storing delegated GSS credentials" 261 " for invalid user"); 262 return; 263 } 264 265 debug("Storing delegated GSS-API credentials"); 266 267 /* 268 * The GSS-API has a flaw in that it does not provide a 269 * mechanism by which delegated credentials can be made 270 * available for acquisition by GSS_Acquire_cred() et. al.; 271 * gss_store_cred() is the proposed GSS-API extension for 272 * generically storing delegated credentials. 273 * 274 * gss_store_cred() does not speak to how credential stores are 275 * referenced. Generically this may be done by switching to the 276 * user context of the user in whose default credential store we 277 * wish to place delegated credentials. But environment 278 * variables could conceivably affect the choice of credential 279 * store as well, and perhaps in a mechanism-specific manner. 280 * 281 * SUNW -- On Solaris the euid selects the current credential 282 * store, but PAM modules could select alternate stores by 283 * setting, for example, KRB5CCNAME, so we also use the PAM 284 * environment temporarily. 285 */ 286 287 #ifdef HAVE_GSS_STORE_CRED 288 #ifdef USE_PAM 289 /* 290 * PAM may have set mechanism-specific variables (e.g., 291 * KRB5CCNAME). fetch_pam_environment() protects against LD_* 292 * and other environment variables. 293 */ 294 penv = fetch_pam_environment(authctxt); 295 tmp_env = environ; 296 environ = penv; 297 #endif /* USE_PAM */ 298 if (authctxt->pw->pw_uid != geteuid()) { 299 temporarily_use_uid(authctxt->pw); 300 ctx->major = gss_store_cred(&ctx->minor, ctx->deleg_creds, 301 GSS_C_INITIATE, GSS_C_NULL_OID, 0, 302 ctx->default_creds, NULL, NULL); 303 restore_uid(); 304 } else { 305 /* only when logging in as the privileged user used by sshd */ 306 ctx->major = gss_store_cred(&ctx->minor, ctx->deleg_creds, 307 GSS_C_INITIATE, GSS_C_NULL_OID, 0, 308 ctx->default_creds, NULL, NULL); 309 } 310 #ifdef USE_PAM 311 environ = tmp_env; 312 free_pam_environment(penv); 313 #endif /* USE_PAM */ 314 if (GSS_ERROR(ctx->major)) 315 ssh_gssapi_error(ctx, "storing delegated credentials"); 316 317 #else 318 #ifdef KRB5_GSS 319 #error "MIT/Heimdal krb5-specific code missing in ssh_gssapi_storecreds()" 320 if (ssh_gssapi_is_krb5(ctx->mech)) 321 ssh_gssapi_krb5_storecreds(ctx); 322 #endif /* KRB5_GSS */ 323 #ifdef GSI_GSS 324 #error "GSI krb5-specific code missing in ssh_gssapi_storecreds()" 325 if (ssh_gssapi_is_gsi(ctx->mech)) 326 ssh_gssapi_krb5_storecreds(ctx); 327 #endif /* GSI_GSS */ 328 /*#error "Mechanism-specific code missing in ssh_gssapi_storecreds()"*/ 329 return; 330 #endif /* HAVE_GSS_STORE_CRED */ 331 } 332 333 void 334 ssh_gssapi_do_child(Gssctxt *ctx, char ***envp, u_int *envsizep) 335 { 336 /* 337 * MIT/Heimdal/GSI specific code goes here. 338 * 339 * On Solaris there's nothing to do here as the GSS store and 340 * related environment variables are to be set by PAM, if at all 341 * (no environment variables are needed to address the default 342 * credential store -- the euid does that). 343 */ 344 #ifdef KRB5_GSS 345 #error "MIT/Heimdal krb5-specific code missing in ssh_gssapi_storecreds()" 346 #endif /* KRB5_GSS */ 347 #ifdef GSI_GSS 348 #error "GSI krb5-specific code missing in ssh_gssapi_storecreds()" 349 #endif /* GSI_GSS */ 350 return; 351 } 352 353 int 354 ssh_gssapi_userok(Gssctxt *ctx, char *user) 355 { 356 if (ctx == NULL) { 357 debug3("INTERNAL ERROR: %s", __func__); 358 return (0); 359 } 360 361 if (user == NULL || *user == '\0') 362 return (0); 363 364 #ifdef HAVE___GSS_USEROK 365 { 366 int user_ok = 0; 367 368 ctx->major = __gss_userok(&ctx->minor, ctx->src_name, user, 369 &user_ok); 370 if (GSS_ERROR(ctx->major)) { 371 debug2("__GSS_userok() failed"); 372 return (0); 373 } 374 375 if (user_ok) 376 return (1); 377 378 /* fall through */ 379 } 380 #else 381 #ifdef GSSAPI_SIMPLE_USEROK 382 { 383 /* Mechanism-generic */ 384 OM_uint32 min; 385 gss_buffer_desc buf, ename1, ename2; 386 gss_name_t iname, cname; 387 int eql; 388 389 buf.value = user; 390 buf.length = strlen(user); 391 ctx->major = gss_import_name(&ctx->minor, &buf, 392 GSS_C_NULL_OID, &iname); 393 if (GSS_ERROR(ctx->major)) { 394 ssh_gssapi_error(ctx, 395 "importing name for authorizing initiator"); 396 goto failed_simple_userok; 397 } 398 399 ctx->major = gss_canonicalize_name(&ctx->minor, iname, 400 ctx->actual_mech, &cname); 401 (void) gss_release_name(&min, &iname); 402 if (GSS_ERROR(ctx->major)) { 403 ssh_gssapi_error(ctx, "canonicalizing name"); 404 goto failed_simple_userok; 405 } 406 407 ctx->major = gss_export_name(&ctx->minor, cname, &ename1); 408 (void) gss_release_name(&min, &cname); 409 if (GSS_ERROR(ctx->major)) { 410 ssh_gssapi_error(ctx, "exporting name"); 411 goto failed_simple_userok; 412 } 413 414 ctx->major = gss_export_name(&ctx->minor, ctx->src_name, 415 &ename2); 416 if (GSS_ERROR(ctx->major)) { 417 ssh_gssapi_error(ctx, 418 "exporting client principal name"); 419 (void) gss_release_buffer(&min, &ename1); 420 goto failed_simple_userok; 421 } 422 423 eql = (ename1.length == ename2.length && 424 memcmp(ename1.value, ename2.value, ename1.length) == 0); 425 426 (void) gss_release_buffer(&min, &ename1); 427 (void) gss_release_buffer(&min, &ename2); 428 429 if (eql) 430 return (1); 431 /* fall through */ 432 } 433 failed_simple_userok: 434 #endif /* GSSAPI_SIMPLE_USEROK */ 435 #ifdef HAVE_GSSCRED_API 436 { 437 /* Mechanism-generic, Solaris-specific */ 438 OM_uint32 maj; 439 uid_t uid; 440 struct passwd *pw; 441 442 maj = gsscred_name_to_unix_cred(ctx->src_name, 443 ctx->actual_mech, &uid, NULL, NULL, NULL); 444 445 if (GSS_ERROR(maj)) 446 goto failed_simple_gsscred_userok; 447 448 if ((pw = getpwnam(user)) == NULL) 449 goto failed_simple_gsscred_userok; 450 451 if (pw->pw_uid == uid) 452 return (1); 453 /* fall through */ 454 } 455 failed_simple_gsscred_userok: 456 #endif /* HAVE_GSSCRED_API */ 457 #ifdef KRB5_GSS 458 if (ssh_gssapi_is_krb5(ctx->mech)) 459 if (ssh_gssapi_krb5_userok(ctx->src_name, user)) 460 return (1); 461 #endif /* KRB5_GSS */ 462 #ifdef GSI_GSS 463 if (ssh_gssapi_is_gsi(ctx->mech)) 464 if (ssh_gssapi_gsi_userok(ctx->src_name, user)) 465 return (1); 466 #endif /* GSI_GSS */ 467 #endif /* HAVE___GSS_USEROK */ 468 469 /* default to not authorized */ 470 return (0); 471 } 472 473 char * 474 ssh_gssapi_localname(Gssctxt *ctx) 475 { 476 if (ctx == NULL) { 477 debug3("INTERNAL ERROR: %s", __func__); 478 return (NULL); 479 } 480 481 debug2("Mapping initiator GSS-API principal to local username"); 482 #ifdef HAVE_GSSCRED_API 483 { 484 /* Mechanism-generic, Solaris-specific */ 485 OM_uint32 maj; 486 uid_t uid; 487 struct passwd *pw; 488 489 if (ctx->src_name == GSS_C_NO_NAME) 490 goto failed_gsscred_localname; 491 492 maj = gsscred_name_to_unix_cred(ctx->src_name, 493 ctx->actual_mech, &uid, NULL, NULL, NULL); 494 495 if (GSS_ERROR(maj)) 496 goto failed_gsscred_localname; 497 498 if ((pw = getpwuid(uid)) == NULL) 499 goto failed_gsscred_localname; 500 501 debug2("Mapped the initiator to: %s", pw->pw_name); 502 return (xstrdup(pw->pw_name)); 503 } 504 failed_gsscred_localname: 505 #endif /* HAVE_GSSCRED_API */ 506 #ifdef KRB5_GSS 507 #error "ssh_gssapi_krb5_localname() not implemented" 508 if (ssh_gssapi_is_krb5(ctx->mech)) 509 return (ssh_gssapi_krb5_localname(ctx->src_name)); 510 #endif /* KRB5_GSS */ 511 #ifdef GSI_GSS 512 #error "ssh_gssapi_gsi_localname() not implemented" 513 if (ssh_gssapi_is_gsi(ctx->mech)) 514 return (ssh_gssapi_gsi_localname(ctx->src_name)); 515 #endif /* GSI_GSS */ 516 return (NULL); 517 } 518 #endif /*GSSAPI */ 519