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