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 #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 #include "monitor_wrap.h" 45 46 #include <gssapi/gssapi.h> 47 #include "ssh-gss.h" 48 49 extern ServerOptions options; 50 extern u_char *session_id2; 51 extern int session_id2_len; 52 extern Gssctxt *xxx_gssctxt; 53 54 static void userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt); 55 56 static void 57 userauth_gssapi_keyex(Authctxt *authctxt) 58 { 59 gss_buffer_desc g_mic_data, mic_tok; 60 Buffer mic_data; 61 OM_uint32 maj_status, min_status; 62 63 if (authctxt == NULL || authctxt->method == NULL) 64 fatal("No authentication context during gssapi-keyex userauth"); 65 66 if (xxx_gssctxt == NULL || xxx_gssctxt->context == GSS_C_NO_CONTEXT) { 67 /* fatal()? or return? */ 68 debug("No GSS-API context during gssapi-keyex userauth"); 69 return; 70 } 71 72 /* Make data buffer to verify MIC with */ 73 buffer_init(&mic_data); 74 buffer_put_string(&mic_data, session_id2, session_id2_len); 75 buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); 76 buffer_put_cstring(&mic_data, authctxt->user); 77 buffer_put_cstring(&mic_data, authctxt->service); 78 buffer_put_cstring(&mic_data, authctxt->method->name); 79 80 g_mic_data.value = buffer_ptr(&mic_data); 81 g_mic_data.length = buffer_len(&mic_data); 82 83 mic_tok.value=packet_get_string(&mic_tok.length); 84 85 maj_status = gss_verify_mic(&min_status, xxx_gssctxt->context, 86 &g_mic_data, &mic_tok, NULL); 87 88 packet_check_eom(); 89 buffer_clear(&mic_data); 90 91 if (maj_status != GSS_S_COMPLETE) 92 debug2("MIC verification failed, GSSAPI userauth failed"); 93 else 94 userauth_gssapi_finish(authctxt, xxx_gssctxt); 95 96 /* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */ 97 if (xxx_gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL) 98 ssh_gssapi_delete_ctx(&xxx_gssctxt); 99 100 return; 101 } 102 103 static void ssh_gssapi_userauth_error(Gssctxt *ctxt); 104 static void input_gssapi_token(int type, u_int32_t plen, void *ctxt); 105 static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt); 106 static void input_gssapi_errtok(int, u_int32_t, void *); 107 static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); 108 109 static void 110 userauth_gssapi_abandon(Authctxt *authctxt, Authmethod *method) 111 { 112 ssh_gssapi_delete_ctx((Gssctxt **)&method->method_data); 113 xxx_gssctxt = NULL; 114 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 115 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 116 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 117 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 118 } 119 120 static void 121 userauth_gssapi(Authctxt *authctxt) 122 { 123 gss_OID_set supported_mechs; 124 int mechs; 125 int present = 0; 126 OM_uint32 min_status; 127 u_int len; 128 char *doid = NULL; 129 gss_OID oid = GSS_C_NULL_OID; 130 131 if (datafellows & SSH_OLD_GSSAPI) { 132 debug("Early drafts of GSSAPI userauth not supported"); 133 return; 134 } 135 136 mechs=packet_get_int(); 137 if (mechs==0) { 138 packet_check_eom(); 139 debug("Mechanism negotiation is not supported"); 140 return; 141 } 142 143 ssh_gssapi_server_mechs(&supported_mechs); 144 145 do { 146 mechs--; 147 148 if (oid != GSS_C_NULL_OID) 149 ssh_gssapi_release_oid(&oid); 150 151 doid = packet_get_string(&len); 152 153 /* ick */ 154 if (doid[0]!=0x06 || (len > 2 && doid[1]!=len-2)) { 155 log("Mechanism OID received using the old encoding form"); 156 oid = ssh_gssapi_make_oid(len, doid); 157 } else { 158 oid = ssh_gssapi_make_oid(len - 2, doid + 2); 159 } 160 (void) gss_test_oid_set_member(&min_status, oid, 161 supported_mechs, &present); 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, 0, oid); 175 ssh_gssapi_release_oid(&oid); 176 /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */ 177 178 packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE); 179 180 /* Just return whatever we found -- the matched mech does us no good */ 181 packet_put_string(doid, len); 182 xfree(doid); 183 184 packet_send(); 185 packet_write_wait(); 186 187 /* Setup rest of gssapi userauth conversation */ 188 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); 189 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); 190 authctxt->method->postponed = 1; 191 192 return; 193 } 194 195 static void 196 input_gssapi_token(int type, u_int32_t plen, void *ctxt) 197 { 198 Authctxt *authctxt = ctxt; 199 Gssctxt *gssctxt; 200 gss_buffer_desc send_tok,recv_tok; 201 OM_uint32 maj_status, min_status; 202 u_int len; 203 204 if (authctxt == NULL || authctxt->method == NULL || 205 (authctxt->method->method_data == NULL && !use_privsep)) 206 fatal("No authentication or GSSAPI context during gssapi-with-mic userauth"); 207 208 gssctxt=authctxt->method->method_data; 209 recv_tok.value=packet_get_string(&len); 210 recv_tok.length=len; /* u_int vs. size_t */ 211 212 maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, 213 &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 && !use_privsep)) 255 fatal("No authentication or GSSAPI context during gssapi-with-mic userauth"); 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) PRIVSEP(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 gssapi-with-mic userauth"); 296 return; 297 } 298 299 gssctxt=authctxt->method->method_data; 300 301 /* Make data buffer to verify MIC with */ 302 buffer_init(&mic_data); 303 buffer_put_string(&mic_data, session_id2, session_id2_len); 304 buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); 305 buffer_put_cstring(&mic_data, authctxt->user); 306 buffer_put_cstring(&mic_data, authctxt->service); 307 buffer_put_cstring(&mic_data, authctxt->method->name); 308 309 g_mic_data.value = buffer_ptr(&mic_data); 310 g_mic_data.length = buffer_len(&mic_data); 311 312 mic_tok.value=packet_get_string(&mic_tok.length); 313 314 maj_status = gss_verify_mic(&min_status, gssctxt->context, 315 &g_mic_data, &mic_tok, NULL); 316 317 packet_check_eom(); 318 buffer_free(&mic_data); 319 320 if (maj_status != GSS_S_COMPLETE) 321 debug2("MIC verification failed, GSSAPI userauth failed"); 322 else 323 userauth_gssapi_finish(authctxt, gssctxt); 324 325 /* Delete context from keyex */ 326 if (xxx_gssctxt != gssctxt) 327 ssh_gssapi_delete_ctx(&xxx_gssctxt); 328 329 /* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */ 330 if (gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL) 331 ssh_gssapi_delete_ctx(&gssctxt); 332 333 xxx_gssctxt = gssctxt; 334 335 authctxt->method->postponed = 0; 336 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 337 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 338 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 339 userauth_finish(authctxt, authctxt->method->name); 340 } 341 342 /* This is called when the client thinks we've completed authentication. 343 * It should only be enabled in the dispatch handler by the function above, 344 * which only enables it once the GSSAPI exchange is complete. 345 */ 346 static void 347 input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) 348 { 349 Authctxt *authctxt = ctxt; 350 Gssctxt *gssctxt; 351 352 packet_check_eom(); 353 354 if (authctxt == NULL || authctxt->method == NULL || 355 (authctxt->method->method_data == NULL && !use_privsep)) 356 fatal("No authentication or GSSAPI context"); 357 358 gssctxt=authctxt->method->method_data; 359 360 /* 361 * SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE -> gssapi userauth 362 * failure, the client should use SSH2_MSG_USERAUTH_GSSAPI_MIC 363 * instead. 364 * 365 * There's two reasons for this: 366 * 367 * 1) we don't have GSS mechs that don't support integrity 368 * protection, and even if we did we'd not want to use them with 369 * SSHv2, and, 370 * 371 * 2) we currently have no way to dynamically detect whether a 372 * given mechanism does or does not support integrity 373 * protection, so when a context's flags do not indicate 374 * integrity protection we can't know if the client simply 375 * didn't request it, so we assume it didn't and reject the 376 * userauth. 377 * 378 * We could fail partially (i.e., force the use of other 379 * userauth methods without counting this one as failed). But 380 * this will do for now. 381 */ 382 #if 0 383 authctxt->method->authenticated = PRIVSEP(ssh_gssapi_userok(gssctxt, authctxt->user)); 384 #endif 385 386 if (xxx_gssctxt != gssctxt) 387 ssh_gssapi_delete_ctx(&gssctxt); 388 ssh_gssapi_delete_ctx(&gssctxt); 389 authctxt->method->postponed = 0; 390 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 391 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 392 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 393 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 394 userauth_finish(authctxt, authctxt->method->name); 395 } 396 397 static void ssh_gssapi_userauth_error(Gssctxt *ctxt) { 398 char *errstr; 399 OM_uint32 maj,min; 400 401 errstr=ssh_gssapi_last_error(ctxt,&maj,&min); 402 if (errstr) { 403 packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR); 404 packet_put_int(maj); 405 packet_put_int(min); 406 packet_put_cstring(errstr); 407 packet_put_cstring(""); 408 packet_send(); 409 packet_write_wait(); 410 xfree(errstr); 411 } 412 } 413 414 /* 415 * Code common to gssapi-keyex and gssapi-with-mic userauth. 416 * 417 * Does authorization, figures out how to store delegated creds. 418 */ 419 static 420 void 421 userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt) 422 { 423 char *local_user = NULL; 424 425 if (*authctxt->user != '\0' && 426 PRIVSEP(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 459 #if 0 460 /* Deprecated userauths -- should not be enabled */ 461 Authmethod method_external = { 462 "external-keyx", 463 &options.gss_authentication, 464 userauth_gssapi_keyex, 465 NULL, /* no abandon function */ 466 NULL, 467 NULL, 468 /* State counters */ 469 0, 0, 0, 0, 470 /* State flags */ 471 0, 0, 0, 0, 0, 0 472 }; 473 474 Authmethod method_gssapi = { 475 "gssapi", 476 &options.gss_authentication, 477 userauth_gssapi, 478 userauth_gssapi_abandon, 479 NULL, 480 NULL, 481 /* State counters */ 482 0, 0, 0, 0, 483 /* State flags */ 484 0, 0, 0, 0, 0, 0 485 }; 486 #endif 487 488 Authmethod method_external = { 489 "gssapi-keyex", 490 &options.gss_authentication, 491 userauth_gssapi_keyex, 492 NULL, /* no abandon function */ 493 NULL, 494 NULL, 495 /* State counters */ 496 0, 0, 0, 0, 497 /* State flags */ 498 0, 0, 0, 0, 0, 0 499 }; 500 501 Authmethod method_gssapi = { 502 "gssapi-with-mic", 503 &options.gss_authentication, 504 userauth_gssapi, 505 userauth_gssapi_abandon, 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 #endif /* GSSAPI */ 515