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 2008 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29 #include "includes.h" 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #ifdef GSSAPI 34 #include "auth.h" 35 #include "ssh2.h" 36 #include "xmalloc.h" 37 #include "log.h" 38 #include "dispatch.h" 39 #include "servconf.h" 40 #include "compat.h" 41 #include "buffer.h" 42 #include "bufaux.h" 43 #include "packet.h" 44 45 #include <gssapi/gssapi.h> 46 #include "ssh-gss.h" 47 48 extern ServerOptions options; 49 extern uchar_t *session_id2; 50 extern int session_id2_len; 51 extern Gssctxt *xxx_gssctxt; 52 53 static void userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt); 54 55 static void 56 userauth_gssapi_keyex(Authctxt *authctxt) 57 { 58 gss_buffer_desc g_mic_data, mic_tok; 59 Buffer mic_data; 60 OM_uint32 maj_status, min_status; 61 62 if (authctxt == NULL || authctxt->method == NULL) 63 fatal("No authentication context during gssapi-keyex userauth"); 64 65 if (xxx_gssctxt == NULL || xxx_gssctxt->context == GSS_C_NO_CONTEXT) { 66 /* fatal()? or return? */ 67 debug("No GSS-API context during gssapi-keyex userauth"); 68 return; 69 } 70 71 /* Make data buffer to verify MIC with */ 72 buffer_init(&mic_data); 73 buffer_put_string(&mic_data, session_id2, session_id2_len); 74 buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); 75 buffer_put_cstring(&mic_data, authctxt->user); 76 buffer_put_cstring(&mic_data, authctxt->service); 77 buffer_put_cstring(&mic_data, authctxt->method->name); 78 79 g_mic_data.value = buffer_ptr(&mic_data); 80 g_mic_data.length = buffer_len(&mic_data); 81 82 mic_tok.value = packet_get_string(&mic_tok.length); 83 84 maj_status = gss_verify_mic(&min_status, xxx_gssctxt->context, 85 &g_mic_data, &mic_tok, NULL); 86 87 packet_check_eom(); 88 buffer_clear(&mic_data); 89 90 if (maj_status != GSS_S_COMPLETE) 91 debug2("MIC verification failed, GSSAPI userauth failed"); 92 else 93 userauth_gssapi_finish(authctxt, xxx_gssctxt); 94 95 /* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */ 96 if (xxx_gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL) 97 ssh_gssapi_delete_ctx(&xxx_gssctxt); 98 } 99 100 static void ssh_gssapi_userauth_error(Gssctxt *ctxt); 101 static void input_gssapi_token(int type, u_int32_t plen, void *ctxt); 102 static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt); 103 static void input_gssapi_errtok(int, u_int32_t, void *); 104 static void input_gssapi_exchange_complete(int type, u_int32_t plen, 105 void *ctxt); 106 107 static void 108 userauth_gssapi_abandon(Authctxt *authctxt, Authmethod *method) 109 { 110 ssh_gssapi_delete_ctx((Gssctxt **)&method->method_data); 111 xxx_gssctxt = NULL; 112 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 113 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 114 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 115 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 116 } 117 118 static void 119 userauth_gssapi(Authctxt *authctxt) 120 { 121 gss_OID_set supported_mechs; 122 int mechs, present = 0; 123 OM_uint32 min_status; 124 uint_t len; 125 char *doid = NULL; 126 gss_OID oid = GSS_C_NULL_OID; 127 128 if (datafellows & SSH_OLD_GSSAPI) { 129 debug("Early drafts of GSSAPI userauth not supported"); 130 return; 131 } 132 133 mechs = packet_get_int(); 134 if (mechs == 0) { 135 packet_check_eom(); 136 debug("Mechanism negotiation is not supported"); 137 return; 138 } 139 140 ssh_gssapi_server_mechs(&supported_mechs); 141 142 do { 143 mechs--; 144 145 if (oid != GSS_C_NULL_OID) 146 ssh_gssapi_release_oid(&oid); 147 148 doid = packet_get_string(&len); 149 150 /* ick */ 151 if (doid[0] != 0x06 || (len > 2 && doid[1] != len - 2)) { 152 log("Mechanism OID received using the old " 153 "encoding form"); 154 oid = ssh_gssapi_make_oid(len, doid); 155 } else { 156 oid = ssh_gssapi_make_oid(len - 2, doid + 2); 157 } 158 159 (void) gss_test_oid_set_member(&min_status, oid, 160 supported_mechs, &present); 161 162 debug("Client offered gssapi userauth with %s (%s)", 163 ssh_gssapi_oid_to_str(oid), 164 present ? "supported" : "unsupported"); 165 } while (!present && (mechs > 0)); 166 167 if (!present) { 168 /* userauth_finish() will send SSH2_MSG_USERAUTH_FAILURE */ 169 debug2("No mechanism offered by the client is available"); 170 ssh_gssapi_release_oid(&oid); 171 return; 172 } 173 174 ssh_gssapi_build_ctx((Gssctxt **)&authctxt->method->method_data, 175 0, oid); 176 ssh_gssapi_release_oid(&oid); 177 /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */ 178 179 packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE); 180 181 /* Just return whatever we found -- the matched mech does us no good */ 182 packet_put_string(doid, len); 183 xfree(doid); 184 185 packet_send(); 186 packet_write_wait(); 187 188 /* Setup rest of gssapi userauth conversation */ 189 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); 190 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); 191 authctxt->method->postponed = 1; 192 } 193 194 static void 195 input_gssapi_token(int type, u_int32_t plen, void *ctxt) 196 { 197 Authctxt *authctxt = ctxt; 198 Gssctxt *gssctxt; 199 gss_buffer_desc send_tok, recv_tok; 200 OM_uint32 maj_status, min_status; 201 uint_t len; 202 203 if (authctxt == NULL || authctxt->method == NULL || 204 (authctxt->method->method_data == NULL)) { 205 fatal("No authentication or GSSAPI context during " 206 "gssapi-with-mic userauth"); 207 } 208 209 gssctxt = authctxt->method->method_data; 210 recv_tok.value = packet_get_string(&len); 211 recv_tok.length = len; /* u_int vs. size_t */ 212 213 maj_status = ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok); 214 packet_check_eom(); 215 216 if (GSS_ERROR(maj_status)) { 217 ssh_gssapi_userauth_error(gssctxt); 218 if (send_tok.length != 0) { 219 packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); 220 packet_put_string(send_tok.value, send_tok.length); 221 packet_send(); 222 packet_write_wait(); 223 } 224 authctxt->method->postponed = 0; 225 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 226 userauth_finish(authctxt, authctxt->method->name); 227 } else { 228 if (send_tok.length != 0) { 229 packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); 230 packet_put_string(send_tok.value, send_tok.length); 231 packet_send(); 232 packet_write_wait(); 233 } 234 if (maj_status == GSS_S_COMPLETE) { 235 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 236 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, 237 &input_gssapi_mic); 238 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, 239 &input_gssapi_exchange_complete); 240 } 241 } 242 243 gss_release_buffer(&min_status, &send_tok); 244 } 245 246 static void 247 input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) 248 { 249 Authctxt *authctxt = ctxt; 250 Gssctxt *gssctxt; 251 gss_buffer_desc send_tok, recv_tok; 252 253 if (authctxt == NULL || authctxt->method == NULL || 254 (authctxt->method->method_data == NULL)) { 255 fatal("No authentication or GSSAPI context during " 256 "gssapi-with-mic userauth"); 257 } 258 259 gssctxt = authctxt->method->method_data; 260 recv_tok.value = packet_get_string(&recv_tok.length); 261 packet_check_eom(); 262 263 /* Push the error token into GSSAPI to see what it says */ 264 (void) ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok); 265 266 debug("Client sent GSS-API error token during GSS userauth-- %s", 267 ssh_gssapi_last_error(gssctxt, NULL, NULL)); 268 269 /* We can't return anything to the client, even if we wanted to */ 270 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 271 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 272 273 274 /* 275 * The client will have already moved on to the next auth and 276 * will send a new userauth request. The spec says that the 277 * server MUST NOT send a SSH_MSG_USERAUTH_FAILURE packet in 278 * response to this. 279 * 280 * We leave authctxt->method->postponed == 1 here so that a call 281 * to input_userauth_request() will detect this failure (as 282 * userauth abandonment) and act accordingly. 283 */ 284 } 285 286 static void 287 input_gssapi_mic(int type, u_int32_t plen, void *ctxt) 288 { 289 Authctxt *authctxt = ctxt; 290 Gssctxt *gssctxt; 291 gss_buffer_desc g_mic_data, mic_tok; 292 Buffer mic_data; 293 OM_uint32 maj_status, min_status; 294 295 if (authctxt == NULL || authctxt->method == NULL || 296 (authctxt->method->method_data == NULL)) { 297 debug3("No authentication or GSSAPI context during " 298 "gssapi-with-mic userauth"); 299 return; 300 } 301 302 gssctxt = authctxt->method->method_data; 303 304 /* Make data buffer to verify MIC with */ 305 buffer_init(&mic_data); 306 buffer_put_string(&mic_data, session_id2, session_id2_len); 307 buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); 308 buffer_put_cstring(&mic_data, authctxt->user); 309 buffer_put_cstring(&mic_data, authctxt->service); 310 buffer_put_cstring(&mic_data, authctxt->method->name); 311 312 g_mic_data.value = buffer_ptr(&mic_data); 313 g_mic_data.length = buffer_len(&mic_data); 314 315 mic_tok.value = packet_get_string(&mic_tok.length); 316 317 maj_status = gss_verify_mic(&min_status, gssctxt->context, 318 &g_mic_data, &mic_tok, NULL); 319 320 packet_check_eom(); 321 buffer_free(&mic_data); 322 323 if (maj_status != GSS_S_COMPLETE) 324 debug2("MIC verification failed, GSSAPI userauth failed"); 325 else 326 userauth_gssapi_finish(authctxt, gssctxt); 327 328 /* Delete context from keyex */ 329 if (xxx_gssctxt != gssctxt) 330 ssh_gssapi_delete_ctx(&xxx_gssctxt); 331 332 /* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */ 333 if (gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL) 334 ssh_gssapi_delete_ctx(&gssctxt); 335 336 xxx_gssctxt = gssctxt; 337 338 authctxt->method->postponed = 0; 339 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 340 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 341 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 342 userauth_finish(authctxt, authctxt->method->name); 343 } 344 345 /* 346 * This is called when the client thinks we've completed authentication. 347 * It should only be enabled in the dispatch handler by the function above, 348 * which only enables it once the GSSAPI exchange is complete. 349 */ 350 static void 351 input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) 352 { 353 Authctxt *authctxt = ctxt; 354 Gssctxt *gssctxt; 355 356 packet_check_eom(); 357 358 if (authctxt == NULL || authctxt->method == NULL || 359 (authctxt->method->method_data == NULL)) 360 fatal("No authentication or GSSAPI context"); 361 362 gssctxt = authctxt->method->method_data; 363 364 /* 365 * SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE -> gssapi userauth 366 * failure, the client should use SSH2_MSG_USERAUTH_GSSAPI_MIC 367 * instead. 368 * 369 * There's two reasons for this: 370 * 371 * 1) we don't have GSS mechs that don't support integrity 372 * protection, and even if we did we'd not want to use them with 373 * SSHv2, and, 374 * 375 * 2) we currently have no way to dynamically detect whether a 376 * given mechanism does or does not support integrity 377 * protection, so when a context's flags do not indicate 378 * integrity protection we can't know if the client simply 379 * didn't request it, so we assume it didn't and reject the 380 * userauth. 381 * 382 * We could fail partially (i.e., force the use of other 383 * userauth methods without counting this one as failed). But 384 * this will do for now. 385 */ 386 #if 0 387 authctxt->method->authenticated = ssh_gssapi_userok(gssctxt, 388 authctxt->user); 389 #endif 390 391 if (xxx_gssctxt != gssctxt) 392 ssh_gssapi_delete_ctx(&gssctxt); 393 ssh_gssapi_delete_ctx(&gssctxt); 394 authctxt->method->postponed = 0; 395 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 396 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 397 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 398 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 399 userauth_finish(authctxt, authctxt->method->name); 400 } 401 402 static void 403 ssh_gssapi_userauth_error(Gssctxt *ctxt) 404 { 405 char *errstr; 406 OM_uint32 maj, min; 407 408 errstr = ssh_gssapi_last_error(ctxt, &maj, &min); 409 if (errstr) { 410 packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR); 411 packet_put_int(maj); 412 packet_put_int(min); 413 packet_put_cstring(errstr); 414 packet_put_cstring(""); 415 packet_send(); 416 packet_write_wait(); 417 xfree(errstr); 418 } 419 } 420 421 /* 422 * Code common to gssapi-keyex and gssapi-with-mic userauth. 423 * 424 * Does authorization, figures out how to store delegated creds. 425 */ 426 static void 427 userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt) 428 { 429 char *local_user = NULL; 430 gss_buffer_desc dispname; 431 OM_uint32 major; 432 433 if (*authctxt->user != '\0' && 434 ssh_gssapi_userok(gssctxt, authctxt->user)) { 435 436 /* 437 * If the client princ did not map to the requested 438 * username then we don't want to clobber existing creds 439 * for the user with the delegated creds. 440 */ 441 local_user = ssh_gssapi_localname(gssctxt); 442 if (local_user == NULL || 443 strcmp(local_user, authctxt->user) == 0) 444 gssctxt->default_creds = 1; /* store creds as default */ 445 446 authctxt->method->authenticated = 447 do_pam_non_initial_userauth(authctxt); 448 449 } else if (*authctxt->user == '\0') { 450 /* Requested username == ""; derive username from princ name */ 451 if ((local_user = ssh_gssapi_localname(gssctxt)) == NULL) 452 return; 453 454 /* Changed username (from implicit, '') */ 455 userauth_user_svc_change(authctxt, local_user, NULL); 456 457 gssctxt->default_creds = 1; /* store creds as default */ 458 459 authctxt->method->authenticated = 460 do_pam_non_initial_userauth(authctxt); 461 } 462 463 if (local_user != NULL) 464 xfree(local_user); 465 466 if (*authctxt->user != '\0' && authctxt->method->authenticated != 0) { 467 major = gss_display_name(&gssctxt->minor, gssctxt->src_name, 468 &dispname, NULL); 469 if (major == GSS_S_COMPLETE) { 470 log("Authorized principal %.*s, authenticated with " 471 "GSS mechanism %s, to: %s", 472 dispname.length, (char *)dispname.value, 473 ssh_gssapi_oid_to_name(gssctxt->actual_mech), 474 authctxt->user); 475 } 476 (void) gss_release_buffer(&gssctxt->minor, &dispname); 477 } 478 } 479 480 #if 0 481 /* Deprecated userauths -- should not be enabled */ 482 Authmethod method_external = { 483 "external-keyx", 484 &options.gss_authentication, 485 userauth_gssapi_keyex, 486 NULL, /* no abandon function */ 487 NULL, 488 NULL, 489 /* State counters */ 490 0, 0, 0, 0, 491 /* State flags */ 492 0, 0, 0, 0, 0, 0 493 }; 494 495 Authmethod method_gssapi = { 496 "gssapi", 497 &options.gss_authentication, 498 userauth_gssapi, 499 userauth_gssapi_abandon, 500 NULL, 501 NULL, 502 /* State counters */ 503 0, 0, 0, 0, 504 /* State flags */ 505 0, 0, 0, 0, 0, 0 506 }; 507 #endif 508 509 Authmethod method_external = { 510 "gssapi-keyex", 511 &options.gss_authentication, 512 userauth_gssapi_keyex, 513 NULL, /* no abandon function */ 514 NULL, 515 NULL, 516 /* State counters */ 517 0, 0, 0, 0, 518 /* State flags */ 519 0, 0, 0, 0, 0, 0 520 }; 521 522 Authmethod method_gssapi = { 523 "gssapi-with-mic", 524 &options.gss_authentication, 525 userauth_gssapi, 526 userauth_gssapi_abandon, 527 NULL, 528 NULL, 529 /* State counters */ 530 0, 0, 0, 0, 531 /* State flags */ 532 0, 0, 0, 0, 0, 0 533 }; 534 535 #endif /* GSSAPI */ 536