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