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 */