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 #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 u_char *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 return; 100 } 101 102 static void ssh_gssapi_userauth_error(Gssctxt *ctxt); 103 static void input_gssapi_token(int type, u_int32_t plen, void *ctxt); 104 static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt); 105 static void input_gssapi_errtok(int, u_int32_t, void *); 106 static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); 107 108 static void 109 userauth_gssapi_abandon(Authctxt *authctxt, Authmethod *method) 110 { 111 ssh_gssapi_delete_ctx((Gssctxt **)&method->method_data); 112 xxx_gssctxt = NULL; 113 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 114 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 115 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 116 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 117 } 118 119 static void 120 userauth_gssapi(Authctxt *authctxt) 121 { 122 gss_OID_set supported_mechs; 123 int mechs; 124 int present = 0; 125 OM_uint32 min_status; 126 u_int len; 127 char *doid = NULL; 128 gss_OID oid = GSS_C_NULL_OID; 129 130 if (datafellows & SSH_OLD_GSSAPI) { 131 debug("Early drafts of GSSAPI userauth not supported"); 132 return; 133 } 134 135 mechs=packet_get_int(); 136 if (mechs==0) { 137 packet_check_eom(); 138 debug("Mechanism negotiation is not supported"); 139 return; 140 } 141 142 ssh_gssapi_server_mechs(&supported_mechs); 143 144 do { 145 mechs--; 146 147 if (oid != GSS_C_NULL_OID) 148 ssh_gssapi_release_oid(&oid); 149 150 doid = packet_get_string(&len); 151 152 /* ick */ 153 if (doid[0]!=0x06 || (len > 2 && doid[1]!=len-2)) { 154 log("Mechanism OID received using the old encoding form"); 155 oid = ssh_gssapi_make_oid(len, doid); 156 } else { 157 oid = ssh_gssapi_make_oid(len - 2, doid + 2); 158 } 159 (void) gss_test_oid_set_member(&min_status, oid, 160 supported_mechs, &present); 161 debug("Client offered gssapi userauth with %s (%s)", 162 ssh_gssapi_oid_to_str(oid), 163 present ? "supported" : "unsupported"); 164 } while (!present && (mechs > 0)); 165 166 if (!present) { 167 /* userauth_finish() will send SSH2_MSG_USERAUTH_FAILURE */ 168 debug2("No mechanism offered by the client is available"); 169 ssh_gssapi_release_oid(&oid); 170 return; 171 } 172 173 ssh_gssapi_build_ctx((Gssctxt **)&authctxt->method->method_data, 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 return; 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 u_int len; 202 203 if (authctxt == NULL || authctxt->method == NULL || 204 (authctxt->method->method_data == NULL)) 205 fatal("No authentication or GSSAPI context during gssapi-with-mic userauth"); 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 gssapi-with-mic userauth"); 254 255 gssctxt=authctxt->method->method_data; 256 recv_tok.value=packet_get_string(&recv_tok.length); 257 packet_check_eom(); 258 259 /* Push the error token into GSSAPI to see what it says */ 260 (void) ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok); 261 262 debug("Client sent GSS-API error token during GSS userauth-- %s", 263 ssh_gssapi_last_error(gssctxt, NULL, NULL)); 264 265 /* We can't return anything to the client, even if we wanted to */ 266 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 267 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,NULL); 268 269 270 /* 271 * The client will have already moved on to the next auth and 272 * will send a new userauth request. The spec says that the 273 * server MUST NOT send a SSH_MSG_USERAUTH_FAILURE packet in 274 * response to this. 275 * 276 * We leave authctxt->method->postponed == 1 here so that a call 277 * to input_userauth_request() will detect this failure (as 278 * userauth abandonment) and act accordingly. 279 */ 280 } 281 282 static void 283 input_gssapi_mic(int type, u_int32_t plen, void *ctxt) 284 { 285 Authctxt *authctxt = ctxt; 286 Gssctxt *gssctxt; 287 gss_buffer_desc g_mic_data, mic_tok; 288 Buffer mic_data; 289 OM_uint32 maj_status, min_status; 290 291 if (authctxt == NULL || authctxt->method == NULL || 292 (authctxt->method->method_data == NULL)) { 293 debug3("No authentication or GSSAPI context during gssapi-with-mic userauth"); 294 return; 295 } 296 297 gssctxt=authctxt->method->method_data; 298 299 /* Make data buffer to verify MIC with */ 300 buffer_init(&mic_data); 301 buffer_put_string(&mic_data, session_id2, session_id2_len); 302 buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); 303 buffer_put_cstring(&mic_data, authctxt->user); 304 buffer_put_cstring(&mic_data, authctxt->service); 305 buffer_put_cstring(&mic_data, authctxt->method->name); 306 307 g_mic_data.value = buffer_ptr(&mic_data); 308 g_mic_data.length = buffer_len(&mic_data); 309 310 mic_tok.value=packet_get_string(&mic_tok.length); 311 312 maj_status = gss_verify_mic(&min_status, gssctxt->context, 313 &g_mic_data, &mic_tok, NULL); 314 315 packet_check_eom(); 316 buffer_free(&mic_data); 317 318 if (maj_status != GSS_S_COMPLETE) 319 debug2("MIC verification failed, GSSAPI userauth failed"); 320 else 321 userauth_gssapi_finish(authctxt, gssctxt); 322 323 /* Delete context from keyex */ 324 if (xxx_gssctxt != gssctxt) 325 ssh_gssapi_delete_ctx(&xxx_gssctxt); 326 327 /* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */ 328 if (gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL) 329 ssh_gssapi_delete_ctx(&gssctxt); 330 331 xxx_gssctxt = gssctxt; 332 333 authctxt->method->postponed = 0; 334 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 335 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 336 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 337 userauth_finish(authctxt, authctxt->method->name); 338 } 339 340 /* This is called when the client thinks we've completed authentication. 341 * It should only be enabled in the dispatch handler by the function above, 342 * which only enables it once the GSSAPI exchange is complete. 343 */ 344 static void 345 input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) 346 { 347 Authctxt *authctxt = ctxt; 348 Gssctxt *gssctxt; 349 350 packet_check_eom(); 351 352 if (authctxt == NULL || authctxt->method == NULL || 353 (authctxt->method->method_data == NULL)) 354 fatal("No authentication or GSSAPI context"); 355 356 gssctxt=authctxt->method->method_data; 357 358 /* 359 * SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE -> gssapi userauth 360 * failure, the client should use SSH2_MSG_USERAUTH_GSSAPI_MIC 361 * instead. 362 * 363 * There's two reasons for this: 364 * 365 * 1) we don't have GSS mechs that don't support integrity 366 * protection, and even if we did we'd not want to use them with 367 * SSHv2, and, 368 * 369 * 2) we currently have no way to dynamically detect whether a 370 * given mechanism does or does not support integrity 371 * protection, so when a context's flags do not indicate 372 * integrity protection we can't know if the client simply 373 * didn't request it, so we assume it didn't and reject the 374 * userauth. 375 * 376 * We could fail partially (i.e., force the use of other 377 * userauth methods without counting this one as failed). But 378 * this will do for now. 379 */ 380 #if 0 381 authctxt->method->authenticated = ssh_gssapi_userok(gssctxt, authctxt->user); 382 #endif 383 384 if (xxx_gssctxt != gssctxt) 385 ssh_gssapi_delete_ctx(&gssctxt); 386 ssh_gssapi_delete_ctx(&gssctxt); 387 authctxt->method->postponed = 0; 388 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 389 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 390 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 391 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 392 userauth_finish(authctxt, authctxt->method->name); 393 } 394 395 static void ssh_gssapi_userauth_error(Gssctxt *ctxt) { 396 char *errstr; 397 OM_uint32 maj,min; 398 399 errstr=ssh_gssapi_last_error(ctxt,&maj,&min); 400 if (errstr) { 401 packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR); 402 packet_put_int(maj); 403 packet_put_int(min); 404 packet_put_cstring(errstr); 405 packet_put_cstring(""); 406 packet_send(); 407 packet_write_wait(); 408 xfree(errstr); 409 } 410 } 411 412 /* 413 * Code common to gssapi-keyex and gssapi-with-mic userauth. 414 * 415 * Does authorization, figures out how to store delegated creds. 416 */ 417 static 418 void 419 userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt) 420 { 421 char *local_user = NULL; 422 gss_buffer_desc dispname; 423 OM_uint32 major; 424 425 if (*authctxt->user != '\0' && 426 ssh_gssapi_userok(gssctxt, authctxt->user)) { 427 428 /* 429 * If the client princ did not map to the requested 430 * username then we don't want to clobber existing creds 431 * for the user with the delegated creds. 432 */ 433 local_user = ssh_gssapi_localname(gssctxt); 434 if (local_user == NULL || 435 strcmp(local_user, authctxt->user) == 0) 436 gssctxt->default_creds = 1; /* store creds as default */ 437 438 authctxt->method->authenticated = 439 do_pam_non_initial_userauth(authctxt); 440 441 } else if (*authctxt->user == '\0') { 442 /* Requested username == ""; derive username from princ name */ 443 if ((local_user = ssh_gssapi_localname(gssctxt)) == NULL) 444 return; 445 446 /* Changed username (from implicit, '') */ 447 userauth_user_svc_change(authctxt, local_user, NULL); 448 449 gssctxt->default_creds = 1; /* store creds as default */ 450 451 authctxt->method->authenticated = 452 do_pam_non_initial_userauth(authctxt); 453 } 454 455 if (local_user != NULL) 456 xfree(local_user); 457 458 if (*authctxt->user != '\0' && authctxt->method->authenticated != 0) { 459 major = gss_display_name(&gssctxt->minor, gssctxt->src_name, 460 &dispname, NULL); 461 if (major == GSS_S_COMPLETE) { 462 log("Authorized principal %.*s, authenticated with " 463 "GSS mechanism %s, to: %s", 464 dispname.length, (char *)dispname.value, 465 ssh_gssapi_oid_to_name(gssctxt->actual_mech), 466 authctxt->user); 467 } 468 (void) gss_release_buffer(&gssctxt->minor, &dispname); 469 } 470 } 471 472 #if 0 473 /* Deprecated userauths -- should not be enabled */ 474 Authmethod method_external = { 475 "external-keyx", 476 &options.gss_authentication, 477 userauth_gssapi_keyex, 478 NULL, /* no abandon function */ 479 NULL, 480 NULL, 481 /* State counters */ 482 0, 0, 0, 0, 483 /* State flags */ 484 0, 0, 0, 0, 0, 0 485 }; 486 487 Authmethod method_gssapi = { 488 "gssapi", 489 &options.gss_authentication, 490 userauth_gssapi, 491 userauth_gssapi_abandon, 492 NULL, 493 NULL, 494 /* State counters */ 495 0, 0, 0, 0, 496 /* State flags */ 497 0, 0, 0, 0, 0, 0 498 }; 499 #endif 500 501 Authmethod method_external = { 502 "gssapi-keyex", 503 &options.gss_authentication, 504 userauth_gssapi_keyex, 505 NULL, /* no abandon function */ 506 NULL, 507 NULL, 508 /* State counters */ 509 0, 0, 0, 0, 510 /* State flags */ 511 0, 0, 0, 0, 0, 0 512 }; 513 514 Authmethod method_gssapi = { 515 "gssapi-with-mic", 516 &options.gss_authentication, 517 userauth_gssapi, 518 userauth_gssapi_abandon, 519 NULL, 520 NULL, 521 /* State counters */ 522 0, 0, 0, 0, 523 /* State flags */ 524 0, 0, 0, 0, 0, 0 525 }; 526 527 #endif /* GSSAPI */ 528