]> andersk Git - gssapi-openssh.git/blobdiff - openssh/auth2-gss.c
This fixes an issue with determining the auth method, for reporting
[gssapi-openssh.git] / openssh / auth2-gss.c
index 45cd5d1614d28b758592e9374ba9577bb8966693..c8f28acef0bc7668b82b3684cdbcb8d5e1d3a6ce 100644 (file)
@@ -1,5 +1,7 @@
+/* $OpenBSD: auth2-gss.c,v 1.16 2007/10/29 00:52:45 dtucker Exp $ */
+
 /*
- * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved.
+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "includes.h"
 
 #ifdef GSSAPI
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+
+#include "xmalloc.h"
+#include "key.h"
+#include "hostfile.h"
 #include "auth.h"
 #include "ssh2.h"
-#include "ssh1.h"
-#include "xmalloc.h"
 #include "log.h"
 #include "dispatch.h"
+#include "buffer.h"
 #include "servconf.h"
-#include "compat.h"
 #include "packet.h"
-#include "monitor_wrap.h"
-
 #include "ssh-gss.h"
+#include "monitor_wrap.h"
 
 extern ServerOptions options;
-extern unsigned char ssh1_key_digest[16];
 
+static void ssh_gssapi_userauth_error(Gssctxt *ctxt);
+static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_errtok(int, u_int32_t, void *);
+
+/* 
+ * The 'gssapi_keyex' userauth mechanism.
+ */
 static int
-userauth_external(Authctxt *authctxt)
+userauth_gsskeyex(Authctxt *authctxt)
 {
-        packet_check_eom();
+       int authenticated = 0;
+       Buffer b, b2;
+       gss_buffer_desc mic, gssbuf, gssbuf2;
+       u_int len;
 
-        return(PRIVSEP(ssh_gssapi_userok(authctxt->user)));
-}
+       mic.value = packet_get_string(&len);
+       mic.length = len;
 
-static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
-static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
+       packet_check_eom();
+
+       ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
+           "gssapi-keyex");
+
+       gssbuf.value = buffer_ptr(&b);
+       gssbuf.length = buffer_len(&b);
+
+       /* client may have used empty username to determine target
+          name from GSSAPI context */
+       ssh_gssapi_buildmic(&b2, "", authctxt->service, "gssapi-keyex");
 
-/* We only support those mechanisms that we know about (ie ones that we know
- * how to check local user kuserok and the like
+       gssbuf2.value = buffer_ptr(&b2);
+       gssbuf2.length = buffer_len(&b2);
+
+       /* gss_kex_context is NULL with privsep, so we can't check it here */
+       if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, 
+                                                  &gssbuf, &mic))) ||
+           !GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, 
+                                                  &gssbuf2, &mic)))) {
+           if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+            authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+                                                      authctxt->pw,
+                                                      1 /* gssapi-keyex */));
+           }
+       }
+       
+       buffer_free(&b);
+       buffer_free(&b2);
+       xfree(mic.value);
+
+       return (authenticated);
+}
+
+/*
+ * We only support those mechanisms that we know about (ie ones that we know
+ * how to check local user kuserok and the like)
  */
 static int
 userauth_gssapi(Authctxt *authctxt)
 {
-        gss_OID_desc    oid= {0,NULL};
-        Gssctxt         *ctxt = NULL;
-        int             mechs;
-        gss_OID_set     supported;
-        int             present;
-        OM_uint32       ms;
-        u_int           len;
-        
-        if (!authctxt->valid || authctxt->user == NULL)
-                return 0;
-                
-        if (datafellows & SSH_OLD_GSSAPI) {
-                debug("Early drafts of GSSAPI userauth not supported");
-                return 0;
-        }
-        
-        mechs=packet_get_int();
-        if (mechs==0) {
-                debug("Mechanism negotiation is not supported");
-                return 0;
-        }
+       gss_OID_desc goid = {0, NULL};
+       Gssctxt *ctxt = NULL;
+       int mechs;
+       gss_OID_set supported;
+       int present;
+       OM_uint32 ms;
+       u_int len;
+       u_char *doid = NULL;
 
-        ssh_gssapi_supported_oids(&supported);
-        do {
-                if (oid.elements)
-                        xfree(oid.elements);
-                oid.elements = packet_get_string(&len);
-                oid.length = len;
-                gss_test_oid_set_member(&ms, &oid, supported, &present);
-                mechs--;
-        } while (mechs>0 && !present);
-        
-        if (!present) {
-                xfree(oid.elements);
-                return(0);
-        }
-                
-       if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,&oid))))
-               return(0);
-       
-        authctxt->methoddata=(void *)ctxt;
+       /* authctxt->valid may be 0 if we haven't yet determined
+          username from gssapi context. */
 
-        /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */
+       if (authctxt->user == NULL)
+               return (0);
+
+       mechs = packet_get_int();
+       if (mechs == 0) {
+               debug("Mechanism negotiation is not supported");
+               return (0);
+       }
+
+       ssh_gssapi_supported_oids(&supported);
+       do {
+               mechs--;
+
+               if (doid)
+                       xfree(doid);
+
+               present = 0;
+               doid = packet_get_string(&len);
+
+               if (len > 2 && doid[0] == SSH_GSS_OIDTYPE &&
+                   doid[1] == len - 2) {
+                       goid.elements = doid + 2;
+                       goid.length   = len - 2;
+                       gss_test_oid_set_member(&ms, &goid, supported,
+                           &present);
+               } else {
+                       logit("Badly formed OID received");
+               }
+       } while (mechs > 0 && !present);
+
+       gss_release_oid_set(&ms, &supported);
+
+       if (!present) {
+               xfree(doid);
+               authctxt->server_caused_failure = 1;
+               return (0);
+       }
+
+       if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
+               if (ctxt != NULL)
+                       ssh_gssapi_delete_ctx(&ctxt);
+               xfree(doid);
+               authctxt->server_caused_failure = 1;
+               return (0);
+       }
+
+       authctxt->methoddata = (void *)ctxt;
 
-       if (!compat20)
-        packet_start(SSH_SMSG_AUTH_GSSAPI_RESPONSE);
-       else
        packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
-        packet_put_string(oid.elements,oid.length);
-        packet_send();
-        packet_write_wait();
-        xfree(oid.elements);
-
-       if (!compat20)
-       dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN,
-                               &input_gssapi_token);
-       else
-       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, 
-                    &input_gssapi_token);
-        authctxt->postponed = 1;
-        
-        return 0;
+
+       /* Return the OID that we received */
+       packet_put_string(doid, len);
+
+       packet_send();
+       xfree(doid);
+
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
+       authctxt->postponed = 1;
+
+       return (0);
 }
 
 static void
 input_gssapi_token(int type, u_int32_t plen, void *ctxt)
 {
-        Authctxt *authctxt = ctxt;
-        Gssctxt *gssctxt;
-        gss_buffer_desc send_tok,recv_tok;
-        OM_uint32 maj_status, min_status;
-       int len;
-        
-        if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
-                fatal("No authentication or GSSAPI context");
-                
-        gssctxt=authctxt->methoddata;
-        recv_tok.value=packet_get_string(&len);
-       recv_tok.length=len; /* int vs. size_t */
-        
-        maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, 
-                                                &send_tok, NULL));
-        packet_check_eom();
-        
-        if (send_tok.length != 0) {
-                /* Send a packet back to the client */
-               if (!compat20)
-               packet_start(SSH_MSG_AUTH_GSSAPI_TOKEN);
-               else
-               packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
-                packet_put_string(send_tok.value,send_tok.length);
-                packet_send();
-                packet_write_wait();
-                gss_release_buffer(&min_status, &send_tok);        
-        }
-        
-        if (GSS_ERROR(maj_status)) {
-                /* Failure <sniff> */
-               ssh_gssapi_send_error(gssctxt->oid,maj_status,min_status);
-                authctxt->postponed = 0;
-               dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL);
-                dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
-                userauth_finish(authctxt, 0, "gssapi");
-        }
-                        
-        if (maj_status == GSS_S_COMPLETE) {
-               dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL);
-                dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
-               /* ssh1 does not have an extra message here */
-               if (!compat20)
-               input_gssapi_exchange_complete(0, 0, ctxt);
-               else
-               dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
-                            &input_gssapi_exchange_complete);
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc recv_tok;
+       OM_uint32 maj_status, min_status, flags=0;
+       u_int len;
+
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+               fatal("No authentication or GSSAPI context");
+
+       gssctxt = authctxt->methoddata;
+       recv_tok.value = packet_get_string(&len);
+       recv_tok.length = len; /* u_int vs. size_t */
+
+       packet_check_eom();
+
+       maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
+           &send_tok, &flags));
+
+       xfree(recv_tok.value);
+
+       if (GSS_ERROR(maj_status)) {
+               ssh_gssapi_userauth_error(gssctxt);
+               if (send_tok.length != 0) {
+                       packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+                       packet_put_string(send_tok.value, send_tok.length);
+                       packet_send();
+               }
+               authctxt->postponed = 0;
+               dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+               userauth_finish(authctxt, 0, "gssapi-with-mic");
+       } else {
+               if (send_tok.length != 0) {
+                       packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+                       packet_put_string(send_tok.value, send_tok.length);
+                       packet_send();
+               }
+               if (maj_status == GSS_S_COMPLETE) {
+                       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+                       if (flags & GSS_C_INTEG_FLAG)
+                               dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC,
+                                   &input_gssapi_mic);
+                       else
+                               dispatch_set(
+                                   SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
+                                   &input_gssapi_exchange_complete);
+               }
+       }
+
+       gss_release_buffer(&min_status, &send_tok);
+}
+
+static void
+input_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
+{
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc recv_tok;
+       OM_uint32 maj_status;
+       u_int len;
+
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+               fatal("No authentication or GSSAPI context");
+
+       gssctxt = authctxt->methoddata;
+       recv_tok.value = packet_get_string(&len);
+       recv_tok.length = len;
+
+       packet_check_eom();
+
+       /* Push the error token into GSSAPI to see what it says */
+       maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
+           &send_tok, NULL));
+
+       xfree(recv_tok.value);
+
+       /* We can't return anything to the client, even if we wanted to */
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+
+       /* The client will have already moved on to the next auth */
+
+       gss_release_buffer(&maj_status, &send_tok);
+}
+
+static void
+gssapi_set_username(Authctxt *authctxt)
+{
+    char *lname = NULL;
+
+    if ((authctxt->user == NULL) || (authctxt->user[0] == '\0')) {
+        PRIVSEP(ssh_gssapi_localname(&lname));
+        if (lname && lname[0] != '\0') {
+            if (authctxt->user) xfree(authctxt->user);
+            authctxt->user = lname;
+            debug("set username to %s from gssapi context", lname);
+            authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user));
+            if (authctxt->pw) {
+                authctxt->valid = 1;
+#ifdef USE_PAM
+                if (options.use_pam)
+                    PRIVSEP(start_pam(authctxt));
+#endif
+            }
+        } else {
+            debug("failed to set username from gssapi context");
+            packet_send_debug("failed to set username from gssapi context");
         }
+    }
 }
 
-/* This is called when the client thinks we've completed authentication.
+/*
+ * This is called when the client thinks we've completed authentication.
  * It should only be enabled in the dispatch handler by the function above,
  * which only enables it once the GSSAPI exchange is complete.
  */
+
 static void
 input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
 {
-        Authctxt *authctxt = ctxt;
-        Gssctxt *gssctxt;
-        int authenticated;
-        
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       int authenticated;
+
        if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
-                fatal("No authentication or GSSAPI context");
-                
-       if ((strcmp(authctxt->user, "") == 0) && (authctxt->pw == NULL)) {
-           char *lname = NULL;
-           PRIVSEP(ssh_gssapi_localname(&lname));
-           if (lname && lname[0] != '\0') {
-               xfree(authctxt->user);
-               authctxt->user = lname;
-               debug("set username to %s from gssapi context", lname);
-               authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user));
-           } else {
-               debug("failed to set username from gssapi context");
-           }
-       }
-       if (authctxt->pw) {
-#ifdef USE_PAM
-           PRIVSEP(start_pam(authctxt->pw->pw_name));
-#endif
+               fatal("No authentication or GSSAPI context");
+
+       gssapi_set_username(authctxt);
+
+       gssctxt = authctxt->methoddata;
+
+       /*
+        * We don't need to check the status, because we're only enabled in
+        * the dispatcher once the exchange is complete
+        */
+
+       packet_check_eom();
+
+       /* user should be set if valid but we double-check here */
+       if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+           authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+                                       authctxt->pw, 0 /* !gssapi-keyex */));
        } else {
-           authctxt->valid = 0;
            authenticated = 0;
-           goto finish;
        }
 
-        gssctxt=authctxt->methoddata;
+       authctxt->postponed = 0;
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+       userauth_finish(authctxt, authenticated, "gssapi-with-mic");
+}
 
-       /* ssh1 needs to exchange the hash of the keys */
-       if (!compat20) {
+static void
+input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
+{
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       int authenticated = 0;
+       Buffer b;
+       gss_buffer_desc mic, gssbuf;
+       u_int len;
 
-               OM_uint32 min_status;
-               gss_buffer_desc dummy, msg_tok;
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+               fatal("No authentication or GSSAPI context");
 
-               /* ssh1 wraps the keys, in the monitor */
+       gssctxt = authctxt->methoddata;
 
-               dummy.value=malloc(sizeof(ssh1_key_digest));
-               memcpy(dummy.value,ssh1_key_digest,sizeof(ssh1_key_digest));
-               dummy.length=sizeof(ssh1_key_digest);
-               if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(gssctxt,&dummy,&msg_tok))))
-                   fatal("Couldn't wrap keys");
-               packet_start(SSH_SMSG_AUTH_GSSAPI_HASH);
-               packet_put_string((char *)msg_tok.value,msg_tok.length);
-               packet_send();
-               packet_write_wait();
-               gss_release_buffer(&min_status,&msg_tok);
-       }
+       mic.value = packet_get_string(&len);
+       mic.length = len;
 
-  
-       /* We don't need to check the status, because the stored credentials
-        * which userok uses are only populated once the context init step
-        * has returned complete.
-        */
+       ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
+           "gssapi-with-mic");
+
+       gssbuf.value = buffer_ptr(&b);
+       gssbuf.length = buffer_len(&b);
 
-        authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
+    gssapi_set_username(authctxt);
 
-finish:
-        authctxt->postponed = 0;
-        dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
-        dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
-        userauth_finish(authctxt, authenticated, "gssapi");
+       if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
+           if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+            authenticated =
+                PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw,
+                                              0 /* !gssapi-keyex */));
+           } else {
+            authenticated = 0;
+           }
+       else
+               logit("GSSAPI MIC check failed");
+
+       buffer_free(&b);
+       xfree(mic.value);
+
+       authctxt->postponed = 0;
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+       userauth_finish(authctxt, authenticated, "gssapi-with-mic");
+}
+
+static void ssh_gssapi_userauth_error(Gssctxt *ctxt) {
+       char *errstr;
+       OM_uint32 maj,min;
+       
+       errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min));
+       if (errstr) {
+               packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR);
+               packet_put_int(maj);
+               packet_put_int(min);
+               packet_put_cstring(errstr);
+               packet_put_cstring("");
+               packet_send();
+               packet_write_wait();
+               xfree(errstr);
+       }
 }
 
-Authmethod method_external = {
-       "external-keyx",
-       userauth_external,
+Authmethod method_gsskeyex = {
+       "gssapi-keyex",
+       userauth_gsskeyex,
        &options.gss_authentication
 };
-       
+
 Authmethod method_gssapi = {
-        "gssapi",
-        userauth_gssapi,
-        &options.gss_authentication
+       "gssapi-with-mic",
+       userauth_gssapi,
+       &options.gss_authentication
 };
 
 #endif /* GSSAPI */
This page took 0.611289 seconds and 4 git commands to generate.