]> andersk Git - gssapi-openssh.git/commitdiff
openssh-3.6.1p2-gssapi-20030430.diff from Simon OPENSSH_3_6_1P2_SIMON-branch OPENSSH_3_6_1P2_SIMON_20030430
authorjbasney <jbasney>
Mon, 5 May 2003 16:46:08 +0000 (16:46 +0000)
committerjbasney <jbasney>
Mon, 5 May 2003 16:46:08 +0000 (16:46 +0000)
36 files changed:
openssh/Makefile.in
openssh/acconfig.h
openssh/auth-pam.c
openssh/auth-pam.h
openssh/auth.c
openssh/auth.h
openssh/auth2-gss.c [new file with mode: 0644]
openssh/auth2.c
openssh/compat.c
openssh/compat.h
openssh/configure.ac
openssh/gss-genr.c [new file with mode: 0644]
openssh/gss-serv-gsi.c [new file with mode: 0644]
openssh/gss-serv-krb5.c [new file with mode: 0644]
openssh/gss-serv.c [new file with mode: 0644]
openssh/kex.c
openssh/kex.h
openssh/kexgssc.c [new file with mode: 0644]
openssh/kexgsss.c [new file with mode: 0644]
openssh/key.c
openssh/key.h
openssh/makegssname.pl [new file with mode: 0644]
openssh/monitor.c
openssh/monitor.h
openssh/monitor_wrap.c
openssh/monitor_wrap.h
openssh/readconf.c
openssh/readconf.h
openssh/servconf.c
openssh/servconf.h
openssh/session.c
openssh/session.h
openssh/ssh-gss.h [new file with mode: 0644]
openssh/sshconnect2.c
openssh/sshd.c
openssh/sshd_config.5

index 270b267799756244d5c1c2f62afff8e955ad3e65..a13f295c78bde8e7a926ca6c44838b0c71a4a29d 100644 (file)
@@ -67,7 +67,7 @@ LIBSSH_OBJS=authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o \
        key.o dispatch.o kex.o mac.o uuencode.o misc.o \
        rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o kexgex.o \
        kexdhc.o kexgexc.o scard.o msg.o progressmeter.o \
-       entropy.o
+       entropy.o kexgssc.o gss-genr.o
 
 SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
        sshconnect.o sshconnect1.o sshconnect2.o
@@ -79,8 +79,9 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
        auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
        auth2-none.o auth2-passwd.o auth2-pubkey.o \
        monitor_mm.o monitor.o monitor_wrap.o monitor_fdpass.o \
-       kexdhs.o kexgexs.o \
-       auth-krb5.o auth-krb4.o \
+       kexdhs.o kexgexs.o kexgsss.o \
+       auth-krb5.o auth-krb4.o auth2-gss.o \
+       gss-serv.o gss-serv-krb5.o gss-serv-gsi.o \
        loginrec.o auth-pam.o auth2-pam.o auth-sia.o md5crypt.o
 
 MANPAGES       = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out
index 3b919d553ea67e1292492fea708d0b809cf953e4..76ba0240a9b3cf99134162c453cef7adebb9a3ae 100644 (file)
 /* Define if compiler implements __func__ */
 #undef HAVE___func__
 
+/* Define this is you want GSSAPI support in the version 2 protocol */
+#undef GSSAPI
+
 /* Define if you want Kerberos 5 support */
 #undef KRB5
 
 /* Define if you want AFS support */
 #undef AFS
 
+/* Define if you want GSI/Globus authentication support */
+#undef GSI
+
 /* Define if you want S/Key support */
 #undef SKEY
 
index d58e74d760da744926ae2ba50ab65f40034dd7b7..da74bc0a7f9b901ed30823f3c7dd21c57223e653 100644 (file)
@@ -423,6 +423,26 @@ void free_pam_environment(char **env)
        }
 }
 
+/* Set a PAM environment string. We need to do this so that the session
+ * modules can handle things like Kerberos/GSI credentials that appear
+ * during the ssh authentication process.
+ */
+
+int do_pam_putenv(char *name, char *value) {
+       char *compound;
+       int ret=1;
+
+#ifdef HAVE_PAM_PUTENV 
+       compound=xmalloc(strlen(name)+strlen(value)+2);
+       if (compound) {
+               sprintf(compound,"%s=%s",name,value);
+               ret=pam_putenv(__pamh,compound);
+               xfree(compound);
+       }
+#endif
+       return(ret);
+}
+
 /* Print any messages that have been generated during authentication */
 /* or account checking to stderr */
 void print_pam_messages(void)
index 585ceb8df2ab17a365a35c7c45b187ae52ea3016..50bf8f3e21500ce4cf666a42ec929ae268332e00 100644 (file)
@@ -44,6 +44,7 @@ void print_pam_messages(void);
 int is_pam_password_change_required(void);
 void do_pam_chauthtok(void);
 void do_pam_set_conv(struct pam_conv *);
+int do_pam_putenv(char *, char *);
 void message_cat(char **p, const char *a);
 
 #endif /* USE_PAM */
index 1268accb1ba1bb08c578b64218be00c476de21b0..ccedf9dcb0f6163a803373a4b8e369760745e94d 100644 (file)
@@ -263,7 +263,7 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
            authmsg,
            method,
            authctxt->valid ? "" : "illegal user ",
-           authctxt->user,
+           (authctxt->user[0]) ? authctxt->user : "<implicit>",
            get_remote_ipaddr(),
            get_remote_port(),
            info);
index c75d75366bca2ff823691d572b28d5fa4bafc04d..8e63132c92df6307a1f0f062237ea17b6278a600 100644 (file)
@@ -70,6 +70,7 @@ struct Authctxt {
        krb5_principal   krb5_user;
        char            *krb5_ticket_file;
 #endif
+       void *methoddata;
 };
 
 struct Authmethod {
diff --git a/openssh/auth2-gss.c b/openssh/auth2-gss.c
new file mode 100644 (file)
index 0000000..774382b
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+#include "auth.h"
+#include "ssh2.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "dispatch.h"
+#include "servconf.h"
+#include "compat.h"
+#include "packet.h"
+#include "monitor_wrap.h"
+
+#include "ssh-gss.h"
+
+extern ServerOptions options;
+
+static int
+userauth_external(Authctxt *authctxt)
+{
+        packet_check_eom();
+
+        return(PRIVSEP(ssh_gssapi_userok(authctxt->user)));
+}
+
+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_exchange_complete(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_errtok(int, u_int32_t, void *);
+
+/* 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;
+        char *         doid = NULL;
+        
+        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;
+        }
+
+        ssh_gssapi_supported_oids(&supported);
+        do {
+                mechs--;
+                
+                if (doid)
+                        xfree(doid);
+                
+                debug("Trying to get OID string");
+                doid = packet_get_string(&len);
+                debug("Got string");
+                
+                       if (doid[0]!=0x06 || doid[1]!=len-2) {
+                               log("Mechanism OID received using the old encoding form");
+                               oid.elements = doid;
+                               oid.length = len;
+                       } else {
+                               oid.elements = doid + 2;
+                               oid.length   = len - 2;
+                       }
+               gss_test_oid_set_member(&ms, &oid, supported, &present);
+        } while (mechs>0 && !present);
+        
+        if (!present) {
+                xfree(doid);
+                return(0);
+        }
+                
+       if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,&oid)))) {
+               ssh_gssapi_userauth_error(ctxt);
+               return(0);
+       }
+       
+        authctxt->methoddata=(void *)ctxt;
+
+        /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */
+
+               packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
+
+       /* Just return whatever they sent */
+       packet_put_string(doid,len);
+               
+        packet_send();
+        packet_write_wait();
+        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;
+       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 */
+        
+        maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, 
+                                                &send_tok, NULL));
+        packet_check_eom();
+        
+        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();
+                               packet_write_wait();
+                       }
+                authctxt->postponed = 0;
+                dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+                userauth_finish(authctxt, 0, "gssapi");
+        } else {
+                       if (send_tok.length != 0) {
+                               packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+                               packet_put_string(send_tok.value,send_tok.length);
+                               packet_send();
+                               packet_write_wait();
+                }
+               if (maj_status == GSS_S_COMPLETE) {
+                       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
+                       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,recv_tok;
+        OM_uint32 maj_status;
+        
+        if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+                fatal("No authentication or GSSAPI context");
+                
+        gssctxt=authctxt->methoddata;
+        recv_tok.value=packet_get_string(&recv_tok.length);
+        
+        /* Push the error token into GSSAPI to see what it says */
+        maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, 
+                                                &send_tok, NULL));
+        packet_check_eom();
+
+       /* 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 */
+       
+}
+
+/* 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;
+        
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+                fatal("No authentication or GSSAPI context");
+                
+        gssctxt=authctxt->methoddata;
+        
+       /* 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.
+        */
+
+        authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
+
+        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_EXCHANGE_COMPLETE, NULL);
+        userauth_finish(authctxt, authenticated, "gssapi");
+}
+
+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,
+       &options.gss_authentication
+};
+       
+Authmethod method_gssapi = {
+        "gssapi",
+        userauth_gssapi,
+        &options.gss_authentication
+};
+
+#endif /* GSSAPI */
index 1b21eb2dab7b25cf493996f09bdb4873a6c992bf..922073cf803e0c926ad47ea43bcf29499310c6e5 100644 (file)
@@ -36,6 +36,10 @@ RCSID("$OpenBSD: auth2.c,v 1.96 2003/02/06 21:22:43 markus Exp $");
 #include "pathnames.h"
 #include "monitor_wrap.h"
 
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
 /* import */
 extern ServerOptions options;
 extern u_char *session_id2;
@@ -50,9 +54,15 @@ extern Authmethod method_pubkey;
 extern Authmethod method_passwd;
 extern Authmethod method_kbdint;
 extern Authmethod method_hostbased;
+extern Authmethod method_external;
+extern Authmethod method_gssapi;
 
 Authmethod *authmethods[] = {
        &method_none,
+#ifdef GSSAPI
+       &method_external,
+       &method_gssapi,
+#endif
        &method_pubkey,
        &method_passwd,
        &method_kbdint,
@@ -180,6 +190,12 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
        }
        /* reset state */
        auth2_challenge_stop(authctxt);
+
+#ifdef GSSAPI
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+#endif
+
        authctxt->postponed = 0;
 
        /* try to authenticate user */
index 5e1774ab63d5fa1eb50740a5c9217d0dab36a208..bd37d4c51df88ee8880423b10894c2e21f94ea0b 100644 (file)
@@ -71,15 +71,21 @@ compat_datafellows(const char *version)
                { "OpenSSH_2.5.0p1*,"
                  "OpenSSH_2.5.1p1*",
                                        SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX|
-                                       SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
+                                       SSH_BUG_NOREKEY|SSH_BUG_EXTEOF|
+                                       SSH_OLD_GSSAPI},
                { "OpenSSH_2.5.0*,"
                  "OpenSSH_2.5.1*,"
                  "OpenSSH_2.5.2*",     SSH_OLD_DHGEX|SSH_BUG_NOREKEY|
                                        SSH_BUG_EXTEOF},
                { "OpenSSH_2.5.3*",     SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
+               { "OpenSSH_2.9p*",      SSH_BUG_EXTEOF|SSH_OLD_GSSAPI},
                { "OpenSSH_2.*,"
                  "OpenSSH_3.0*,"
-                 "OpenSSH_3.1*",       SSH_BUG_EXTEOF},
+                 "OpenSSH_3.1*",       SSH_BUG_EXTEOF|SSH_BUG_GSSAPI_BER},
+               { "OpenSSH_3.2*,"
+                 "OpenSSH_3.3*,"
+                 "OpenSSH_3.4*,"
+                 "OpenSSH_3.5*",       SSH_BUG_GSSAPI_BER},
                { "Sun_SSH_1.0*",       SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
                { "OpenSSH*",           0 },
                { "*MindTerm*",         0 },
index 881e450d39ea34682198a0ca1294e2612bc18f4e..75bc083dd054f8968caf390c054386a0575735a8 100644 (file)
@@ -56,6 +56,8 @@
 #define SSH_BUG_K5USER         0x00400000
 #define SSH_BUG_PROBE          0x00800000
 #define SSH_BUG_FIRSTKEX       0x01000000
+#define SSH_OLD_GSSAPI         0x02000000
+#define SSH_BUG_GSSAPI_BER     0x04000000
 
 void     enable_compat13(void);
 void     enable_compat20(void);
index dfd87c9d07745eea01af92d63d6e716920851e56..5bd6eb90031f2d5f6a7eefdcf61d0c381e421320 100644 (file)
@@ -741,6 +741,7 @@ AC_ARG_WITH(pam,
                        AC_CHECK_LIB(dl, dlopen, , )
                        AC_CHECK_LIB(pam, pam_set_item, , AC_MSG_ERROR([*** libpam missing]))
                        AC_CHECK_FUNCS(pam_getenvlist)
+                       AC_CHECK_FUNCS(pam_putenv)
 
                        disable_shadow=yes
                        PAM_MSG="yes"
@@ -751,6 +752,7 @@ AC_ARG_WITH(pam,
                        else
                                LIBPAM="-lpam"
                        fi
+
                        AC_SUBST(LIBPAM)
                fi
        ]
@@ -781,6 +783,47 @@ if test "x$check_for_libcrypt_before" = "x1"; then
        AC_CHECK_LIB(crypt, crypt)
 fi
 
+# Start of GSI/Globus 2.0 mods
+# Check whether the user wants GSI (Globus 2.0) support
+# if we are using GSI, we will also use the 
+# OPenSSL that is built by Globus. This is called
+# -lcrypto_FLAVOR
+# and it will be in the GSI path. 
+# The includes will be in the include/FLAVOR/openssl
+# therfore we will not process the -with-ssl parameter.
+
+gsi_path="no"
+AC_ARG_WITH(gsi,
+    [  --with-gsi=PATH         Enable GSI/Globus GSSAPI support],
+    [
+        gsi_path="$withval"
+    ]
+)
+
+gsi_flavor=gcc32dbg
+AC_ARG_WITH(gsi-flavor,
+      [  --with-gsi-flavor=FLAVOR Globus build flavor ],
+      [
+              gsi_flavor="$withval"
+      ]
+)
+
+if test "x$gsi_path" != "xno" ; then
+    # Globus GSSAPI configuration
+    AC_DEFINE(GSSAPI)
+    AC_DEFINE(GSI)
+      AC_DEFINE(HAVE_OPENSSL)
+      LDFLAGS="$LDFLAGS -L${gsi_path}/lib"
+    if test ! -z "$need_dash_r" ; then
+        LDFLAGS="$LDFLAGS -R${gsi_path}/lib"
+    fi
+    if test ! -z "$blibpath" ; then
+        blibpath="$blibpath:${gsi_path}/lib"
+    fi
+      LIBS="$LIBS -lcrypto_${gsi_flavor} -lglobus_gss_assist_${gsi_flavor} -lglobus_gssapi_gsi_${gsi_flavor}"
+      CPPFLAGS="$CPPFLAGS -I${gsi_path}/include -I${gsi_path}/include/${gsi_flavor}"
+else
+
 # Search for OpenSSL
 saved_CPPFLAGS="$CPPFLAGS"
 saved_LDFLAGS="$LDFLAGS"
@@ -915,6 +958,8 @@ if test "x$PAM_MSG" = "xno" -a "x$check_for_libcrypt_later" = "x1"; then
        AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt")
 fi
 
+fi
+#end of GSI/Globus 2.0 mods
 
 ### Configure cryptographic random number support
 
@@ -1836,6 +1881,31 @@ AC_ARG_WITH(kerberos5,
                         fi
                         AC_CHECK_LIB(resolv, dn_expand, , )
 
+                       AC_CHECK_LIB(gssapi,gss_init_sec_context,
+                               [ AC_DEFINE(GSSAPI)
+                                 K5LIBS="-lgssapi $K5LIBS" ],
+                               [ AC_CHECK_LIB(gssapi_krb5,gss_init_sec_context,
+                                       [ AC_DEFINE(GSSAPI)
+                                         K5LIBS="-lgssapi_krb5 $K5LIBS" ],
+                                       AC_MSG_WARN([Cannot find any suitable gss-api library - build may fail]),
+                                       $K5LIBS)
+                               ],
+                               $K5LIBS)
+                       
+                       AC_CHECK_HEADER(gssapi.h, ,
+                               [ unset ac_cv_header_gssapi_h
+                                 CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" 
+                                 AC_CHECK_HEADERS(gssapi.h, ,
+                                       AC_MSG_WARN([Cannot find any suitable gss-api header - build may fail])
+                                 ) 
+                               ]
+                       )
+
+                       oldCPP="$CPPFLAGS"
+                       CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi"
+                       AC_CHECK_HEADER(gssapi_krb5.h, ,
+                                       [ CPPFLAGS="$oldCPP" ])
+
                         KRB5=yes
                 fi
         ]
diff --git a/openssh/gss-genr.c b/openssh/gss-genr.c
new file mode 100644 (file)
index 0000000..85dbb86
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+
+#include "ssh.h"
+#include "ssh2.h"
+#include "xmalloc.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "packet.h"
+#include "compat.h"
+#include <openssl/evp.h>
+#include "cipher.h"
+#include "kex.h"
+#include "log.h"
+#include "compat.h"
+#include "monitor_wrap.h"
+
+#include <netdb.h>
+
+#include "ssh-gss.h"
+
+typedef struct {
+       char *encoded;
+       gss_OID oid;
+} ssh_gss_kex_mapping;
+       
+static ssh_gss_kex_mapping *gss_enc2oid;
+
+/* Return a list of the gss-group1-sha1-x mechanisms supported by this
+ * program.
+ *
+ * On the client side, we don't need to worry about whether we 'know'
+ * about the mechanism or not - we assume that any mechanism that we've been
+ * linked against is suitable for inclusion.
+ *
+ * XXX - We might want to make this configurable in the future, so as to
+ * XXX - allow the user control over which mechanisms to use.
+ */
+char * 
+ssh_gssapi_client_mechanisms(char *host) {
+       gss_OID_set     supported;
+       OM_uint32       min_status;
+       Buffer          buf;
+       int             i = 0;
+       char            *mechs;
+       char            *encoded;
+       int             enclen;
+       char            digest[EVP_MAX_MD_SIZE];
+       char            deroid[2];
+       const EVP_MD    *evp_md = EVP_md5();
+       EVP_MD_CTX      md;
+       int             oidpos=0;
+       
+       if (datafellows & SSH_OLD_GSSAPI) return NULL;
+       
+       gss_indicate_mechs(&min_status,&supported);
+       if (datafellows & SSH_BUG_GSSAPI_BER) {
+               gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping)
+                                       *((supported->count*2)+1));
+       } else {
+               gss_enc2oid=xmalloc(sizeof(ssh_gss_kex_mapping)
+                                       *(supported->count+1));
+       }
+       
+       buffer_init(&buf);
+
+
+       for (i=0;i<supported->count;i++) {
+
+               gss_enc2oid[oidpos].encoded=NULL;
+               
+               if (supported->elements[i].length<128 &&
+                   ssh_gssapi_check_mechanism(&(supported->elements[i]),host)) {
+
+                       /* Earlier versions of this code interpreted the
+                        * spec incorrectly with regard to OID encoding. They
+                        * also mis-encoded the krb5 OID. The following
+                        * _temporary_ code interfaces with these broken
+                        * servers */
+
+                       if (datafellows & SSH_BUG_GSSAPI_BER) {
+                               char *bodge=NULL;
+                               gss_OID_desc krb5oid={9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
+                               gss_OID_desc gsioid={9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"};
+                               
+                               if (supported->elements[i].length==krb5oid.length &&
+                                   memcmp(supported->elements[i].elements,
+                                          krb5oid.elements, krb5oid.length)==0) {
+                                       bodge="Se3H81ismmOC3OE+FwYCiQ==";
+                               }
+                               
+                               if (supported->elements[i].length==gsioid.length &&
+                                   memcmp(supported->elements[i].elements,
+                                          gsioid.elements, gsioid.length)==0) {
+                                       bodge="N3+k7/4wGxHyuP8Yxi4RhA==";
+                               }
+
+                               if (bodge) {                            
+                                       if (oidpos!=0) {
+                                               buffer_put_char(&buf,',');
+                                       }
+                               
+                                       buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1);
+                                       buffer_append(&buf, bodge, strlen(bodge));
+
+                                       gss_enc2oid[oidpos].oid=&(supported->elements[i]);
+                                       gss_enc2oid[oidpos].encoded=bodge;
+                       
+                                       oidpos++;
+                               }
+                       }
+                       
+                       /* Add the required DER encoding octets and MD5 hash */
+                       deroid[0]=0x06; /* Object Identifier */
+                       deroid[1]=supported->elements[i].length;
+
+                       EVP_DigestInit(&md, evp_md);
+                       EVP_DigestUpdate(&md,deroid,2);
+                       EVP_DigestUpdate(&md,
+                                        supported->elements[i].elements,
+                                        supported->elements[i].length);
+                       EVP_DigestFinal(&md, digest, NULL);
+                       
+                       /* Base64 encode it */
+                       encoded=xmalloc(EVP_MD_size(evp_md)*2);
+                       enclen=__b64_ntop(digest, EVP_MD_size(evp_md),
+                                         encoded,EVP_MD_size(evp_md)*2);
+                       if (oidpos!=0) {
+                               buffer_put_char(&buf,',');
+                       }       
+                       buffer_append(&buf, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1);
+                       buffer_append(&buf, encoded, enclen);
+
+                       debug("Mechanism encoded as %s",encoded);
+
+                       gss_enc2oid[oidpos].oid=&(supported->elements[i]);
+                       gss_enc2oid[oidpos].encoded=encoded;                    
+                       oidpos++;
+               }
+       }
+       gss_enc2oid[oidpos].oid=NULL;
+       gss_enc2oid[oidpos].encoded=NULL;
+       
+       buffer_put_char(&buf,'\0');
+       
+       mechs=xmalloc(buffer_len(&buf));
+       buffer_get(&buf,mechs,buffer_len(&buf));
+       buffer_free(&buf);
+       if (strlen(mechs)==0)
+               return(NULL);
+       else
+               return(mechs);
+}
+
+gss_OID
+ssh_gssapi_client_id_kex(Gssctxt *ctx, char *name) {
+       int i=0;
+       
+       if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) !=0) {
+               return(NULL);
+       }
+       
+       name+=sizeof(KEX_GSS_SHA1)-1; /* Move to the start of the ID string */
+       
+       while (gss_enc2oid[i].encoded!=NULL &&
+               strcmp(name,gss_enc2oid[i].encoded)!=0) {
+               i++;
+       }
+       
+       if (gss_enc2oid[i].oid!=NULL) {
+               ssh_gssapi_set_oid(ctx,gss_enc2oid[i].oid);
+       }
+
+       return gss_enc2oid[i].oid;
+}
+
+/* Check that the OID in a data stream matches that in the context */
+int ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) {
+  
+  return (ctx!=NULL && ctx->oid != GSS_C_NO_OID && 
+         ctx->oid->length == len &&
+         memcmp(ctx->oid->elements,data,len)==0);
+}
+       
+/* Set the contexts OID from a data stream */
+void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) { 
+  if (ctx->oid != GSS_C_NO_OID) {
+       xfree(ctx->oid->elements);
+       xfree(ctx->oid);
+  }
+  ctx->oid=xmalloc(sizeof(gss_OID_desc));
+  ctx->oid->length=len;
+  ctx->oid->elements=xmalloc(len);
+  memcpy(ctx->oid->elements,data,len);
+}
+
+/* Set the contexts OID */
+void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) {  
+  ssh_gssapi_set_oid_data(ctx,oid->elements,oid->length);
+}
+
+/* All this effort to report an error ... */
+
+void
+ssh_gssapi_error(Gssctxt *ctxt) {
+       
+       debug(ssh_gssapi_last_error(ctxt,NULL,NULL));
+}
+
+char *
+ssh_gssapi_last_error(Gssctxt *ctxt, 
+                     OM_uint32 *major_status, OM_uint32 *minor_status) {
+       OM_uint32 lmin;
+        gss_buffer_desc msg;
+        OM_uint32 ctx;
+        Buffer b;
+        char *ret;
+        
+        buffer_init(&b);
+
+       if (major_status!=NULL) *major_status=ctxt->major;
+       if (minor_status!=NULL) *minor_status=ctxt->minor;
+       
+        ctx = 0;
+       /* The GSSAPI error */
+        do {
+               gss_display_status(&lmin, ctxt->major,
+                                  GSS_C_GSS_CODE, GSS_C_NULL_OID,
+                                  &ctx, &msg);
+
+               buffer_append(&b,msg.value,msg.length);
+               buffer_put_char(&b,'\n');
+               
+                       gss_release_buffer(&lmin, &msg);
+        } while (ctx!=0);
+
+        /* The mechanism specific error */
+        do {
+               gss_display_status(&lmin, ctxt->minor,
+                                  GSS_C_MECH_CODE, GSS_C_NULL_OID,
+                                  &ctx, &msg);
+               
+               buffer_append(&b,msg.value,msg.length);
+               buffer_put_char(&b,'\n');
+               
+               gss_release_buffer(&lmin, &msg);
+        } while (ctx!=0);
+        
+        buffer_put_char(&b,'\0');
+        ret=xmalloc(buffer_len(&b));
+        buffer_get(&b,ret,buffer_len(&b));
+        buffer_free(&b);
+        return(ret);
+}
+
+/* Initialise our GSSAPI context. We use this opaque structure to contain all
+ * of the data which both the client and server need to persist across
+ * {accept,init}_sec_context calls, so that when we do it from the userauth
+ * stuff life is a little easier
+ */
+void
+ssh_gssapi_build_ctx(Gssctxt **ctx)
+{
+       *ctx=xmalloc(sizeof (Gssctxt));
+       (*ctx)->major=0;
+       (*ctx)->minor=0;
+       (*ctx)->context=GSS_C_NO_CONTEXT;
+       (*ctx)->name=GSS_C_NO_NAME;
+       (*ctx)->oid=GSS_C_NO_OID;
+       (*ctx)->creds=GSS_C_NO_CREDENTIAL;
+       (*ctx)->client=GSS_C_NO_NAME;
+       (*ctx)->client_creds=GSS_C_NO_CREDENTIAL;
+}
+
+/* Delete our context, providing it has been built correctly */
+void
+ssh_gssapi_delete_ctx(Gssctxt **ctx)
+{
+       OM_uint32 ms;
+       
+       /* Return if there's no context */
+       if ((*ctx)==NULL)
+               return;
+               
+       if ((*ctx)->context != GSS_C_NO_CONTEXT) 
+               gss_delete_sec_context(&ms,&(*ctx)->context,GSS_C_NO_BUFFER);
+       if ((*ctx)->name != GSS_C_NO_NAME)
+               gss_release_name(&ms,&(*ctx)->name);
+       if ((*ctx)->oid != GSS_C_NO_OID) {
+               xfree((*ctx)->oid->elements);
+               xfree((*ctx)->oid);
+               (*ctx)->oid = GSS_C_NO_OID;
+       }
+       if ((*ctx)->creds != GSS_C_NO_CREDENTIAL)
+               gss_release_cred(&ms,&(*ctx)->creds);
+       if ((*ctx)->client != GSS_C_NO_NAME)
+               gss_release_name(&ms,&(*ctx)->client);  
+       if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL)
+               gss_release_cred(&ms,&(*ctx)->client_creds);
+       
+       xfree(*ctx);
+       *ctx=NULL; 
+}
+
+/* Wrapper to init_sec_context 
+ * Requires that the context contains:
+ *     oid
+ *     server name (from ssh_gssapi_import_name)
+ */
+OM_uint32 
+ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
+                           gss_buffer_desc* send_tok, OM_uint32 *flags) 
+{
+       int deleg_flag = 0;
+       
+       if (deleg_creds) {
+               deleg_flag=GSS_C_DELEG_FLAG;
+               debug("Delegating credentials");
+       }
+               
+       ctx->major=gss_init_sec_context(&ctx->minor,
+                                       GSS_C_NO_CREDENTIAL, /* def. cred */
+                                       &ctx->context,
+                                       ctx->name,
+                                       ctx->oid,
+                                       GSS_C_MUTUAL_FLAG |
+                                       GSS_C_INTEG_FLAG |
+                                       deleg_flag,
+                                       0, /* default lifetime */
+                                       NULL, /* no channel bindings */
+                                       recv_tok,
+                                       NULL,
+                                       send_tok,
+                                       flags,
+                                       NULL);
+       if (GSS_ERROR(ctx->major)) {
+               ssh_gssapi_error(ctx);
+       }
+       return(ctx->major);
+}
+
+/* Create a service name for the given host */
+OM_uint32
+ssh_gssapi_import_name(Gssctxt *ctx, const char *host) {
+       gss_buffer_desc gssbuf;
+
+        gssbuf.length = sizeof("host@")+strlen(host);
+
+        gssbuf.value = xmalloc(gssbuf.length);
+        if (gssbuf.value == NULL) {
+               return(-1);
+        }
+        snprintf(gssbuf.value,gssbuf.length,"host@%s",host);
+        if ((ctx->major=gss_import_name(&ctx->minor,
+                                       &gssbuf,
+                                        GSS_C_NT_HOSTBASED_SERVICE,
+                                        &ctx->name))) {
+               ssh_gssapi_error(ctx);
+       }
+       
+       xfree(gssbuf.value);
+       return(ctx->major);
+}
+
+/* Acquire credentials for a server running on the current host.
+ * Requires that the context structure contains a valid OID
+ */
+/* Returns a GSSAPI error code */
+OM_uint32
+ssh_gssapi_acquire_cred(Gssctxt *ctx) {
+       OM_uint32 status;
+       char lname[MAXHOSTNAMELEN];
+       gss_OID_set oidset;
+       
+       gss_create_empty_oid_set(&status,&oidset);
+       gss_add_oid_set_member(&status,ctx->oid,&oidset);
+       
+        if (gethostname(lname, MAXHOSTNAMELEN)) {
+                return(-1);
+        }
+
+       if (GSS_ERROR(ssh_gssapi_import_name(ctx,lname))) {
+               return(ctx->major);
+       }
+       
+       if ((ctx->major=gss_acquire_cred(&ctx->minor,
+                                   ctx->name,
+                                   0,
+                                   oidset,
+                                   GSS_C_ACCEPT,
+                                   &ctx->creds,
+                                   NULL,
+                                   NULL))) {
+               ssh_gssapi_error(ctx);
+       }
+                               
+       gss_release_oid_set(&status, &oidset);
+       return(ctx->major);
+}
+
+OM_uint32
+ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) {
+       
+       if ((ctx->major=gss_get_mic(&ctx->minor,ctx->context,
+                                   GSS_C_QOP_DEFAULT, buffer, hash))) {
+               ssh_gssapi_error(ctx);
+       }
+       
+       return(ctx->major);
+}
+
+OM_uint32
+ssh_gssapi_server_ctx(Gssctxt **ctx,gss_OID oid) {
+       if (*ctx) ssh_gssapi_delete_ctx(ctx);
+       ssh_gssapi_build_ctx(ctx);
+       ssh_gssapi_set_oid(*ctx,oid);
+       return(ssh_gssapi_acquire_cred(*ctx));
+}
+
+int
+ssh_gssapi_check_mechanism(gss_OID oid, char *host) {
+       Gssctxt * ctx = NULL;
+       gss_buffer_desc token;
+       OM_uint32 major,minor;
+       
+       ssh_gssapi_build_ctx(&ctx);
+       ssh_gssapi_set_oid(ctx,oid);
+       ssh_gssapi_import_name(ctx,host);
+       major=ssh_gssapi_init_ctx(ctx,0, GSS_C_NO_BUFFER, &token, NULL);
+       gss_release_buffer(&minor,&token);
+       ssh_gssapi_delete_ctx(&ctx);
+       return(!GSS_ERROR(major));
+}
+
+#endif /* GSSAPI */
diff --git a/openssh/gss-serv-gsi.c b/openssh/gss-serv-gsi.c
new file mode 100644 (file)
index 0000000..69ef4c4
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+#ifdef GSI
+
+#include "auth.h"
+#include "auth-pam.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "servconf.h"
+
+#include "ssh-gss.h"
+
+#include <globus_gss_assist.h>
+
+static int ssh_gssapi_gsi_userok(ssh_gssapi_client *client, char *name);
+static int ssh_gssapi_gsi_localname(ssh_gssapi_client *client, char **user);
+static void ssh_gssapi_gsi_storecreds(ssh_gssapi_client *client);
+
+ssh_gssapi_mech gssapi_gsi_mech_old = {
+       "N3+k7/4wGxHyuP8Yxi4RhA==",
+       "GSI",
+       {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"},
+       NULL,
+       &ssh_gssapi_gsi_userok,
+       &ssh_gssapi_gsi_localname,
+       &ssh_gssapi_gsi_storecreds
+};
+
+ssh_gssapi_mech gssapi_gsi_mech = {
+       "dZuIebMjgUqaxvbF7hDbAw==",
+       "GSI",
+       {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"},
+       NULL,
+       &ssh_gssapi_gsi_userok,
+       &ssh_gssapi_gsi_localname,
+       &ssh_gssapi_gsi_storecreds
+};
+
+/*
+ * Check if this user is OK to login under GSI. User has been authenticated
+ * as identity in global 'client_name.value' and is trying to log in as passed
+ * username in 'name'.
+ *
+ * Returns non-zero if user is authorized, 0 otherwise.
+ */
+static int
+ssh_gssapi_gsi_userok(ssh_gssapi_client *client, char *name)
+{
+    int authorized = 0;
+    
+#ifdef GLOBUS_GSI_GSS_ASSIST_MODULE
+    if (globus_module_activate(GLOBUS_GSI_GSS_ASSIST_MODULE) != 0) {
+       return 0;
+    }
+#endif
+
+    /* This returns 0 on success */
+    authorized = (globus_gss_assist_userok(client->name.value,
+                                          name) == 0);
+    
+    log("GSI user %s is%s authorized as target user %s",
+       (char *) client->name.value, (authorized ? "" : " not"), name);
+    
+    return authorized;
+}
+
+/*
+ * Return the local username associated with the GSI credentials.
+ */
+int
+ssh_gssapi_gsi_localname(ssh_gssapi_client *client, char **user)
+{
+#ifdef GLOBUS_GSI_GSS_ASSIST_MODULE
+    if (globus_module_activate(GLOBUS_GSI_GSS_ASSIST_MODULE) != 0) {
+       return 0;
+    }
+#endif
+    return(globus_gss_assist_gridmap(client->name.value, user) == 0);
+}
+
+/*
+ * Export GSI credentials to disk.
+ */
+static void
+ssh_gssapi_gsi_storecreds(ssh_gssapi_client *client)
+{
+       OM_uint32       major_status;
+       OM_uint32       minor_status;
+       gss_buffer_desc export_cred = GSS_C_EMPTY_BUFFER;
+       char *          p;
+       
+       if (!client || !client->creds) {
+           return;
+       }
+       
+       major_status = gss_export_cred(&minor_status,
+                                        client->creds,
+                                      GSS_C_NO_OID,
+                                      1,
+                                      &export_cred);
+       if (GSS_ERROR(major_status) && major_status != GSS_S_UNAVAILABLE) {
+           Gssctxt *ctx;
+           ssh_gssapi_build_ctx(&ctx);
+           ctx->major = major_status;
+           ctx->minor = minor_status;
+           ssh_gssapi_set_oid(ctx, &gssapi_gsi_mech.oid);
+           ssh_gssapi_error(ctx);
+           ssh_gssapi_delete_ctx(&ctx);
+                               return;
+                       }
+       
+       p = strchr((char *) export_cred.value, '=');
+       if (p == NULL) {
+           log("Failed to parse exported credentials string '%.100s'",
+               (char *)export_cred.value);
+           gss_release_buffer(&minor_status, &export_cred);
+           return;
+                       }
+       *p++ = '\0';
+       if (strcmp((char *)export_cred.value,"X509_USER_DELEG_PROXY") == 0) {
+           client->store.envvar = strdup("X509_USER_PROXY");
+       } else {
+           client->store.envvar = strdup((char *)export_cred.value);
+               }
+       client->store.envval = strdup(p);
+#ifdef USE_PAM
+       do_pam_putenv(client->store.envvar, client->store.envval);
+#endif
+       if (strncmp(p, "FILE:", 5) == 0) {
+           p += 5;
+               }
+       if (access(p, R_OK) == 0) {
+           client->store.filename = strdup(p);
+       }       
+       gss_release_buffer(&minor_status, &export_cred);
+}
+
+#endif /* GSI */
+#endif /* GSSAPI */
diff --git a/openssh/gss-serv-krb5.c b/openssh/gss-serv-krb5.c
new file mode 100644 (file)
index 0000000..d9f0677
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+#ifdef KRB5
+
+#include "auth.h"
+#include "auth-pam.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "servconf.h"
+
+#include "ssh-gss.h"
+
+extern ServerOptions options;
+
+#ifdef HEIMDAL
+#include <krb5.h>
+#else
+#include <gssapi_krb5.h>
+#define krb5_get_err_text(context,code) error_message(code)
+#endif
+
+static krb5_context krb_context = NULL;
+
+/* Initialise the krb5 library, so we can use it for those bits that
+ * GSSAPI won't do */
+
+static int 
+ssh_gssapi_krb5_init() {
+       krb5_error_code problem;
+       
+       if (krb_context !=NULL)
+               return 1;
+               
+       problem = krb5_init_context(&krb_context);
+       if (problem) {
+               log("Cannot initialize krb5 context");
+               return 0;
+       }
+       krb5_init_ets(krb_context);
+
+       return 1;       
+}                      
+
+/* Check if this user is OK to login. This only works with krb5 - other 
+ * GSSAPI mechanisms will need their own.
+ * Returns true if the user is OK to log in, otherwise returns 0
+ */
+
+static int
+ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) {
+       krb5_principal princ;
+       int retval;
+
+       if (ssh_gssapi_krb5_init() == 0)
+               return 0;
+               
+       if ((retval=krb5_parse_name(krb_context, client->name.value, 
+                                   &princ))) {
+               log("krb5_parse_name(): %.100s", 
+                       krb5_get_err_text(krb_context,retval));
+               return 0;
+       }
+       if (krb5_kuserok(krb_context, princ, name)) {
+               retval = 1;
+               log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name,
+                   (char *)client->name.value);
+       }
+       else
+               retval = 0;
+       
+       krb5_free_principal(krb_context, princ);
+       return retval;
+}
+
+/* Retrieve the local username associated with a set of Kerberos 
+ * credentials. Hopefully we can use this for the 'empty' username
+ * logins discussed in the draft  */
+static int
+ssh_gssapi_krb5_localname(ssh_gssapi_client *client, char **user) {
+       krb5_principal princ;
+       int retval;
+       
+       if (ssh_gssapi_krb5_init() == 0)
+               return 0;
+
+       if ((retval=krb5_parse_name(krb_context, client->name.value, 
+                                   &princ))) {
+               log("krb5_parse_name(): %.100s", 
+                       krb5_get_err_text(krb_context,retval));
+               return 0;
+       }
+       
+       /* We've got to return a malloc'd string */
+       *user = (char *)xmalloc(256);
+       if (krb5_aname_to_localname(krb_context, princ, 256, *user)) {
+               xfree(*user);
+               *user = NULL;
+               return(0);
+       }
+       
+       return(1);
+}
+       
+/* Make sure that this is called _after_ we've setuid to the user */
+
+/* This writes out any forwarded credentials. Its specific to the Kerberos
+ * GSSAPI mechanism
+ *
+ * We assume that our caller has made sure that the user has selected
+ * delegated credentials, and that the client_creds structure is correctly
+ * populated.
+ */
+
+static void
+ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) {
+       krb5_ccache ccache;
+       krb5_error_code problem;
+       krb5_principal princ;
+       char ccname[35];
+       static char name[40];
+       int tmpfd;
+       OM_uint32 maj_status,min_status;
+
+       if (client->creds==NULL) {
+               debug("No credentials stored"); 
+               return;
+       }
+               
+       if (ssh_gssapi_krb5_init() == 0)
+               return;
+
+       if (options.gss_use_session_ccache) {
+               snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid());
+       
+               if ((tmpfd = mkstemp(ccname))==-1) {
+                       log("mkstemp(): %.100s", strerror(errno));
+                       return;
+               }
+               if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
+                       log("fchmod(): %.100s", strerror(errno));
+                       close(tmpfd);
+                       return;
+               }
+        } else {
+               snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid());
+               tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+               if (tmpfd == -1) {
+                       log("open(): %.100s", strerror(errno));
+                       return;
+               }
+        }
+
+               close(tmpfd);
+        snprintf(name, sizeof(name), "FILE:%s",ccname);
+        if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) {
+                log("krb5_cc_default(): %.100s", 
+                       krb5_get_err_text(krb_context,problem));
+                return;
+        }
+
+       if ((problem = krb5_parse_name(krb_context, client->name.value, 
+                                      &princ))) {
+               log("krb5_parse_name(): %.100s", 
+                       krb5_get_err_text(krb_context,problem));
+               krb5_cc_destroy(krb_context,ccache);
+               return;
+       }
+       
+       if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) {
+               log("krb5_cc_initialize(): %.100s", 
+                       krb5_get_err_text(krb_context,problem));
+               krb5_free_principal(krb_context,princ);
+               krb5_cc_destroy(krb_context,ccache);
+               return;
+       }
+       
+       krb5_free_principal(krb_context,princ);
+
+       #ifdef HEIMDAL
+       if ((problem = krb5_cc_copy_cache(krb_context, 
+                                          client->creds->ccache,
+                                          ccache))) {
+               log("krb5_cc_copy_cache(): %.100s", 
+                       krb5_get_err_text(krb_context,problem));
+               krb5_cc_destroy(krb_context,ccache);
+               return;
+       }
+       #else
+       if ((maj_status = gss_krb5_copy_ccache(&min_status, 
+                                              client->creds, 
+                                              ccache))) {
+               log("gss_krb5_copy_ccache() failed");
+               krb5_cc_destroy(krb_context,ccache);
+               return;
+       }
+       #endif
+       
+       krb5_cc_close(krb_context,ccache);
+
+#ifdef USE_PAM
+       do_pam_putenv("KRB5CCNAME",name);
+#endif
+
+       client->store.filename=strdup(ccname);
+       client->store.envvar="KRB5CCNAME";
+       client->store.envval=strdup(name);
+
+       return;
+}
+
+/* We've been using a wrongly encoded mechanism ID for yonks */
+
+ssh_gssapi_mech gssapi_kerberos_mech_old = {
+       "Se3H81ismmOC3OE+FwYCiQ==",
+       "Kerberos",
+       {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"},
+       &ssh_gssapi_krb5_init,
+       &ssh_gssapi_krb5_userok,
+       &ssh_gssapi_krb5_localname,
+       &ssh_gssapi_krb5_storecreds
+};
+
+ssh_gssapi_mech gssapi_kerberos_mech = {
+       "toWM5Slw5Ew8Mqkay+al2g==",
+       "Kerberos",
+       {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"},
+       NULL,
+       &ssh_gssapi_krb5_userok,
+       &ssh_gssapi_krb5_localname,
+       &ssh_gssapi_krb5_storecreds
+};
+       
+#endif /* KRB5 */
+
+#endif /* GSSAPI */
diff --git a/openssh/gss-serv.c b/openssh/gss-serv.c
new file mode 100644 (file)
index 0000000..b75bfaf
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+
+#include "ssh.h"
+#include "ssh2.h"
+#include "xmalloc.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "packet.h"
+#include "compat.h"
+#include <openssl/evp.h>
+#include "cipher.h"
+#include "kex.h"
+#include "auth.h"
+#include "log.h"
+#include "channels.h"
+#include "session.h"
+#include "dispatch.h"
+#include "servconf.h"
+#include "compat.h"
+#include "monitor_wrap.h"
+
+#include "ssh-gss.h"
+
+extern ServerOptions options;
+extern u_char *session_id2;
+extern int session_id2_len;
+
+static ssh_gssapi_client gssapi_client =
+       { {0,NULL}, GSS_C_NO_CREDENTIAL, NULL, {NULL,NULL,NULL}};
+
+ssh_gssapi_mech gssapi_null_mech 
+  = {NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
+
+#ifdef KRB5
+extern ssh_gssapi_mech gssapi_kerberos_mech;
+extern ssh_gssapi_mech gssapi_kerberos_mech_old;
+#endif
+#ifdef GSI
+extern ssh_gssapi_mech gssapi_gsi_mech;
+extern ssh_gssapi_mech gssapi_gsi_mech_old;
+#endif
+
+ssh_gssapi_mech* supported_mechs[]= {
+#ifdef KRB5
+  &gssapi_kerberos_mech,
+  &gssapi_kerberos_mech_old, /* Support for legacy clients */
+#endif
+#ifdef GSI
+  &gssapi_gsi_mech,
+  &gssapi_gsi_mech_old,        /* Support for legacy clients */
+#endif
+  &gssapi_null_mech,
+};
+
+/* Return a list of the gss-group1-sha1-x mechanisms supported by this
+ * program.
+ *
+ * We only support the mechanisms that we've indicated in the list above,
+ * but we check that they're supported by the GSSAPI mechanism on the 
+ * machine. We also check, before including them in the list, that
+ * we have the necesary information in order to carry out the key exchange
+ * (that is, that the user has credentials, the server's creds are accessible,
+ * etc)
+ *
+ * The way that this is done is fairly nasty, as we do a lot of work that
+ * is then thrown away. This should possibly be implemented with a cache
+ * that stores the results (in an expanded Gssctxt structure), which are
+ * then used by the first calls if that key exchange mechanism is chosen.
+ */
+
+/* Unpriviledged */ 
+char * 
+ssh_gssapi_server_mechanisms() {
+       gss_OID_set     supported;
+       Gssctxt         *ctx = NULL;
+       OM_uint32       maj_status, min_status;
+       Buffer          buf;
+       int             i = 0;
+       int             first = 0;
+       int             present;
+       char *          mechs;
+
+       if (datafellows & SSH_OLD_GSSAPI) return NULL;
+       
+       ssh_gssapi_supported_oids(&supported);
+       
+       buffer_init(&buf);
+
+       while(supported_mechs[i]->name != NULL) {
+               if ((maj_status=gss_test_oid_set_member(&min_status,
+                                                       &supported_mechs[i]->oid,
+                                                       supported,
+                                                       &present))) {
+                       present=0;
+               }
+
+               if (present) {
+                   if (!GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx,
+                                           &supported_mechs[i]->oid)))) {
+                       /* Append gss_group1_sha1_x to our list */
+                       if (first++!=0)
+                               buffer_put_char(&buf,',');
+                       buffer_append(&buf, KEX_GSS_SHA1,
+                                     sizeof(KEX_GSS_SHA1)-1);
+                       buffer_append(&buf, 
+                                     supported_mechs[i]->enc_name,
+                                     strlen(supported_mechs[i]->enc_name));
+                       debug("GSSAPI mechanism %s (%s%s) supported",
+                             supported_mechs[i]->name, KEX_GSS_SHA1,
+                             supported_mechs[i]->enc_name);
+                   } else {
+                       debug("no credentials for GSSAPI mechanism %s",
+                             supported_mechs[i]->name);
+                   }
+               } else {
+                   debug("GSSAPI mechanism %s not supported",
+                         supported_mechs[i]->name);
+               }
+               ssh_gssapi_delete_ctx(&ctx);
+               i++;
+       }
+       
+       buffer_put_char(&buf,'\0');
+       
+       mechs=xmalloc(buffer_len(&buf));
+       buffer_get(&buf,mechs,buffer_len(&buf));
+       buffer_free(&buf);
+       if (strlen(mechs)==0)
+          return(NULL);
+       else
+          return(mechs);
+}
+
+/* Unpriviledged */
+void ssh_gssapi_supported_oids(gss_OID_set *oidset) {
+       int i =0;
+       OM_uint32 maj_status,min_status;
+       int present;
+       gss_OID_set supported;
+       
+       gss_create_empty_oid_set(&min_status,oidset);
+       PRIVSEP(gss_indicate_mechs(&min_status, &supported));
+
+       while (supported_mechs[i]->name!=NULL) {
+               if ((maj_status=gss_test_oid_set_member(&min_status,
+                                                      &supported_mechs[i]->oid,
+                                                      supported,
+                                                      &present))) {
+                       present=0;
+               }
+               if (present) {
+                       gss_add_oid_set_member(&min_status,
+                                              &supported_mechs[i]->oid,
+                                              oidset); 
+               }
+               i++;
+       }
+}      
+
+/* Find out which GSS type (out of the list we define in ssh-gss.h) a
+ * particular connection is using 
+ */
+
+/* Priviledged (called ssh_gssapi_accept_ctx -> ssh_gssapi_getclient ->) */
+ssh_gssapi_mech *
+ssh_gssapi_get_ctype(Gssctxt *ctxt) {
+       int i=0;
+       
+       while(supported_mechs[i]->name!=NULL) {
+           if (supported_mechs[i]->oid.length == ctxt->oid->length &&
+               (memcmp(supported_mechs[i]->oid.elements,
+                       ctxt->oid->elements,ctxt->oid->length)==0)) {
+               return supported_mechs[i];
+           }
+          i++;
+       }
+       return NULL;
+}
+
+/* Return the OID that corresponds to the given context name */
+/* Unpriviledged */
+gss_OID 
+ssh_gssapi_server_id_kex(char *name) {
+  int i=0;
+  
+  if (strncmp(name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) !=0) {
+     return(NULL);
+  }
+  
+  name+=sizeof(KEX_GSS_SHA1)-1; /* Move to the start of the MIME string */
+  
+  while (supported_mechs[i]->name!=NULL &&
+        strcmp(name,supported_mechs[i]->enc_name)!=0) {
+       i++;
+  }
+
+  if (supported_mechs[i]->name==NULL)
+     return (NULL);
+
+  debug("using GSSAPI mechanism %s (%s%s)", supported_mechs[i]->name,
+       KEX_GSS_SHA1, supported_mechs[i]->enc_name);
+
+  return &supported_mechs[i]->oid;
+}
+
+/* Wrapper around accept_sec_context
+ * Requires that the context contains:
+ *    oid              
+ *    credentials      (from ssh_gssapi_acquire_cred)
+ */
+/* Priviledged */
+OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx,gss_buffer_desc *recv_tok,
+                               gss_buffer_desc *send_tok, OM_uint32 *flags) 
+{
+       OM_uint32 status;
+       gss_OID mech;
+       
+       ctx->major=gss_accept_sec_context(&ctx->minor,
+                                         &ctx->context,
+                                         ctx->creds,
+                                         recv_tok,
+                                         GSS_C_NO_CHANNEL_BINDINGS,
+                                         &ctx->client,
+                                         &mech, /* read-only pointer */
+                                         send_tok,
+                                         flags,
+                                         NULL,
+                                         &ctx->client_creds);
+       if (GSS_ERROR(ctx->major)) {
+               ssh_gssapi_error(ctx);
+       }
+       
+       if (ctx->client_creds) {
+               debug("Received some client credentials");
+       } else {
+               debug("Got no client credentials");
+       }
+
+       /* FIXME: We should check that the me
+        * the one that we asked for (in ctx->oid) */
+
+       status=ctx->major;
+       
+       /* Now, if we're complete and we have the right flags, then
+        * we flag the user as also having been authenticated
+        */
+       
+       if (((flags==NULL) || ((*flags & GSS_C_MUTUAL_FLAG) && 
+                              (*flags & GSS_C_INTEG_FLAG))) &&
+           (ctx->major == GSS_S_COMPLETE)) {
+               if (ssh_gssapi_getclient(ctx,&gssapi_client.mech,
+                                        &gssapi_client.name,
+                                        &gssapi_client.creds))
+                       fatal("Couldn't convert client name");
+       }
+
+       /* Make sure that the getclient call hasn't stamped on this */
+       return(status);
+}
+
+/* Extract the client details from a given context. This can only reliably
+ * be called once for a context */
+
+/* Priviledged (called from accept_secure_ctx) */
+OM_uint32 
+ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_mech **type,
+                    gss_buffer_desc *name, gss_cred_id_t *creds) {
+
+       *type=ssh_gssapi_get_ctype(ctx);
+       if ((ctx->major=gss_display_name(&ctx->minor,ctx->client,name,NULL))) {
+               ssh_gssapi_error(ctx);
+               return(ctx->major);
+       }
+       
+       /* This is icky. There appears to be no way to copy this structure,
+        * rather than the pointer to it, so we simply copy the pointer and
+        * mark the originator as empty so we don't destroy it. 
+        */
+       *creds=ctx->client_creds;
+       ctx->client_creds=GSS_C_NO_CREDENTIAL;
+       return(ctx->major);
+}
+
+/* As user - called through fatal cleanup hook */
+void
+ssh_gssapi_cleanup_creds(void *ignored)
+{
+       if (gssapi_client.store.filename!=NULL) {
+               /* Unlink probably isn't sufficient */
+               debug("removing gssapi cred file\"%s\"",gssapi_client.store.filename);
+               unlink(gssapi_client.store.filename);
+       }
+}
+
+/* As user */
+void 
+ssh_gssapi_storecreds()
+{
+       if (gssapi_client.mech && gssapi_client.mech->storecreds) {
+               (*gssapi_client.mech->storecreds)(&gssapi_client);
+               if (options.gss_cleanup_creds) {
+                       fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
+               }
+       } else {
+               debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
+       }
+}
+
+/* This allows GSSAPI methods to do things to the childs environment based
+ * on the passed authentication process and credentials.
+ */
+/* As user */
+void 
+ssh_gssapi_do_child(char ***envp, u_int *envsizep) 
+{
+
+       if (gssapi_client.store.envvar!=NULL && 
+           gssapi_client.store.envval!=NULL) {
+           
+               debug("Setting %s to %s", gssapi_client.store.envvar,
+                                         gssapi_client.store.envval);                            
+               child_set_env(envp, envsizep, gssapi_client.store.envvar, 
+                                             gssapi_client.store.envval);
+       }
+}
+
+/* Priviledged */
+int
+ssh_gssapi_userok(char *user)
+{
+       if (gssapi_client.name.length==0 || 
+           gssapi_client.name.value==NULL) {
+               debug("No suitable client data");
+               return 0;
+       }
+       if (gssapi_client.mech && gssapi_client.mech->userok) {
+               return((*gssapi_client.mech->userok)(&gssapi_client,user));
+       } else {
+               debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
+       }
+       return(0);
+}
+
+/* Priviledged */
+int
+ssh_gssapi_localname(char **user)
+{
+       *user = NULL;
+       if (gssapi_client.name.length==0 || 
+           gssapi_client.name.value==NULL) {
+               debug("No suitable client data");
+               return(0);;
+       }
+       if (gssapi_client.mech && gssapi_client.mech->localname) {
+               return((*gssapi_client.mech->localname)(&gssapi_client,user));
+       } else {
+               debug("Unknown client authentication type");
+       }
+       return(0);
+}
+#endif
index b070ccf42eb2fb66402d6d511c19ec5e14628b4f..046b87a6e9cf2e2cc8a14bc2fc1622425e4d6f09 100644 (file)
@@ -42,6 +42,10 @@ RCSID("$OpenBSD: kex.c,v 1.55 2003/04/01 10:31:26 markus Exp $");
 #include "dispatch.h"
 #include "monitor.h"
 
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
 #define KEX_COOKIE_LEN 16
 
 /* prototype */
@@ -295,6 +299,10 @@ choose_kex(Kex *k, char *client, char *server)
                k->kex_type = KEX_DH_GRP1_SHA1;
        } else if (strcmp(k->name, KEX_DHGEX) == 0) {
                k->kex_type = KEX_DH_GEX_SHA1;
+#ifdef GSSAPI
+       } else if (strncmp(k->name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) == 0) {
+               k->kex_type = KEX_GSS_GRP1_SHA1;
+#endif
        } else
                fatal("bad kex alg %s", k->name);
 }
index 52d442e9a7d19b39068f7cc534f714e01f215dca..50b8659e9bb4f50a20f2ff85101f5bae27b612f7 100644 (file)
@@ -57,6 +57,7 @@ enum kex_modes {
 enum kex_exchange {
        KEX_DH_GRP1_SHA1,
        KEX_DH_GEX_SHA1,
+       KEX_GSS_GRP1_SHA1,
        KEX_MAX
 };
 
@@ -95,6 +96,11 @@ struct Newkeys {
        Mac     mac;
        Comp    comp;
 };
+
+struct KexOptions {
+       int     gss_deleg_creds;
+};
+
 struct Kex {
        u_char  *session_id;
        u_int   session_id_len;
@@ -110,6 +116,7 @@ struct Kex {
        int     flags;
        char    *client_version_string;
        char    *server_version_string;
+       struct  KexOptions options;
        int     (*verify_host_key)(Key *);
        Key     *(*load_host_key)(int);
        int     (*host_key_index)(Key *);
@@ -129,6 +136,10 @@ void        kexdh_client(Kex *);
 void    kexdh_server(Kex *);
 void    kexgex_client(Kex *);
 void    kexgex_server(Kex *);
+#ifdef GSSAPI
+void     kexgss_client(Kex *);
+void     kexgss_server(Kex *);
+#endif
 
 u_char *
 kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int,
diff --git a/openssh/kexgssc.c b/openssh/kexgssc.c
new file mode 100644 (file)
index 0000000..52b569f
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+
+#include <openssl/crypto.h>
+#include <openssl/bn.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "canohost.h"
+#include "ssh2.h"
+#include "ssh-gss.h"
+
+void
+kexgss_client(Kex *kex)
+{
+       gss_buffer_desc gssbuf,send_tok,recv_tok, msg_tok, *token_ptr;
+       Gssctxt *ctxt;
+       OM_uint32 maj_status, min_status, ret_flags;
+       unsigned int klen, kout;
+       DH *dh; 
+       BIGNUM *dh_server_pub = 0;
+       BIGNUM *shared_secret = 0;      
+       unsigned char *kbuf;
+       unsigned char *hash;
+       unsigned char *serverhostkey;
+       char *msg;
+       char *lang;
+       int type = 0;
+       int first = 1;
+       int slen = 0;
+       u_int strlen;
+       
+       /* Initialise our GSSAPI world */
+       ssh_gssapi_build_ctx(&ctxt);
+       if (ssh_gssapi_client_id_kex(ctxt,kex->name)==NULL) {
+               fatal("Couldn't identify host exchange");
+       }
+
+       if (ssh_gssapi_import_name(ctxt,get_canonical_hostname(1))) {
+               fatal("Couldn't import hostname ");
+       }
+       
+       /* This code should match that in ssh_dh1_client */
+               
+       /* Step 1 - e is dh->pub_key */
+       dh = dh_new_group1();
+       dh_gen_key(dh, kex->we_need * 8);
+
+       /* This is f, we initialise it now to make life easier */
+       dh_server_pub = BN_new();
+       if (dh_server_pub == NULL) {
+               fatal("dh_server_pub == NULL");
+       }
+               
+       token_ptr = GSS_C_NO_BUFFER;
+                        
+       do {
+               debug("Calling gss_init_sec_context");
+               
+               maj_status=ssh_gssapi_init_ctx(ctxt,
+                                              kex->options.gss_deleg_creds,
+                                              token_ptr,&send_tok,
+                                              &ret_flags);
+
+               if (GSS_ERROR(maj_status)) {
+                       if (send_tok.length!=0) {
+                               packet_start(SSH2_MSG_KEXGSS_CONTINUE);
+                               packet_put_string(send_tok.value,
+                                                 send_tok.length);
+                       }                         
+                       fatal("gss_init_context failed");
+               }
+
+               /* If we've got an old receive buffer get rid of it */
+               if (token_ptr != GSS_C_NO_BUFFER)
+                       (void) gss_release_buffer(&min_status, &recv_tok);
+       
+               
+               if (maj_status == GSS_S_COMPLETE) {
+                       /* If mutual state flag is not true, kex fails */
+                       if (!(ret_flags & GSS_C_MUTUAL_FLAG)) {
+                               fatal("Mutual authentication failed");
+                       }
+                       /* If integ avail flag is not true kex fails */
+                       if (!(ret_flags & GSS_C_INTEG_FLAG)) {
+                               fatal("Integrity check failed");
+                       }
+               }
+               
+               /* If we have data to send, then the last message that we
+                * received cannot have been a 'complete'. */
+               if (send_tok.length !=0) {
+                       if (first) {
+                               packet_start(SSH2_MSG_KEXGSS_INIT);
+                               packet_put_string(send_tok.value,
+                                                 send_tok.length);
+                               packet_put_bignum2(dh->pub_key);
+                               first=0;
+                       } else {
+                               packet_start(SSH2_MSG_KEXGSS_CONTINUE);
+                               packet_put_string(send_tok.value,
+                                                 send_tok.length);
+                       }
+                       packet_send();
+                       packet_write_wait();
+
+                       
+                       /* If we've sent them data, they'd better be polite
+                        * and reply. */
+               
+                       type = packet_read();
+                       switch (type) {
+                       case SSH2_MSG_KEXGSS_HOSTKEY:
+                               debug("Received KEXGSS_HOSTKEY");
+                               serverhostkey=packet_get_string(&slen);
+                               break;
+                       case SSH2_MSG_KEXGSS_CONTINUE:
+                               debug("Received GSSAPI_CONTINUE");
+                               if (maj_status == GSS_S_COMPLETE) 
+                                       fatal("GSSAPI Continue received from server when complete");
+                               recv_tok.value=packet_get_string(&strlen);
+                               recv_tok.length=strlen; /* u_int vs. size_t */
+                               break;
+                       case SSH2_MSG_KEXGSS_COMPLETE:
+                               debug("Received GSSAPI_COMPLETE");
+                               packet_get_bignum2(dh_server_pub);
+                               msg_tok.value=packet_get_string(&strlen);
+                               msg_tok.length=strlen; /* u_int vs. size_t */
+
+                               /* Is there a token included? */
+                               if (packet_get_char()) {
+                                       recv_tok.value=
+                                           packet_get_string(&strlen);
+                                       recv_tok.length=strlen; /*u_int/size_t*/
+                                       /* If we're already complete - protocol error */
+                                       if (maj_status == GSS_S_COMPLETE)
+                                               packet_disconnect("Protocol error: received token when complete");
+                               } else {
+                                       /* No token included */
+                                       if (maj_status != GSS_S_COMPLETE)
+                                               packet_disconnect("Protocol error: did not receive final token");
+                               }
+                               break;
+                       case SSH2_MSG_KEXGSS_ERROR:
+                               debug("Received Error");
+                               maj_status=packet_get_int();
+                               min_status=packet_get_int();
+                               msg=packet_get_string(NULL);
+                               lang=packet_get_string(NULL);
+                               fprintf(stderr,"GSSAPI Error: \n%s",msg);
+                       default:
+                               packet_disconnect("Protocol error: didn't expect packet type %d",
+                               type);
+                       }
+                       token_ptr=&recv_tok;
+               } else {
+                       /* No data, and not complete */
+                       if (maj_status!=GSS_S_COMPLETE) {
+                               fatal("Not complete, and no token output");
+                       }
+               }
+       } while (maj_status & GSS_S_CONTINUE_NEEDED);
+       
+       /* We _must_ have received a COMPLETE message in reply from the 
+        * server, which will have set dh_server_pub and msg_tok */
+        
+       if (type!=SSH2_MSG_KEXGSS_COMPLETE)
+          fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
+                       
+       /* Check f in range [1, p-1] */
+        if (!dh_pub_is_valid(dh, dh_server_pub))
+                        packet_disconnect("bad server public DH value");
+                        
+        /* compute K=f^x mod p */
+        klen = DH_size(dh);
+        kbuf = xmalloc(klen);
+        kout = DH_compute_key(kbuf, dh_server_pub, dh);
+        
+        shared_secret = BN_new();
+        BN_bin2bn(kbuf,kout, shared_secret);
+        memset(kbuf, 0, klen);
+        xfree(kbuf);
+        
+        /* The GSS hash is identical to the DH one */
+        hash = kex_dh_hash(
+           kex->client_version_string,
+            kex->server_version_string,
+            buffer_ptr(&kex->my), buffer_len(&kex->my),
+            buffer_ptr(&kex->peer), buffer_len(&kex->peer),
+            serverhostkey, slen, /* server host key */
+            dh->pub_key,       /* e */
+            dh_server_pub,     /* f */
+            shared_secret      /* K */
+        );
+        
+        gssbuf.value=hash;
+        gssbuf.length=20;
+        
+        /* Verify that H matches the token we just got. */
+                if ((maj_status = gss_verify_mic(&min_status,
+                                        ctxt->context,
+                                        &gssbuf,
+                                        &msg_tok,
+                                        NULL))) {
+
+               packet_disconnect("Hash's MIC didn't verify");
+       }       
+        
+        DH_free(dh);
+               ssh_gssapi_delete_ctx(&ctxt);
+        /* save session id */
+        if (kex->session_id == NULL) {
+               kex->session_id_len = 20;
+               kex->session_id = xmalloc(kex->session_id_len);
+               memcpy(kex->session_id, hash, kex->session_id_len);
+        }
+        
+       kex_derive_keys(kex, hash, shared_secret);
+       BN_clear_free(shared_secret);
+        kex_finish(kex);
+}
+
+#endif /* GSSAPI */
diff --git a/openssh/kexgsss.c b/openssh/kexgsss.c
new file mode 100644 (file)
index 0000000..5ec58bf
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#ifdef GSSAPI
+
+#include <openssl/crypto.h>
+#include <openssl/bn.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "ssh2.h"
+#include "ssh-gss.h"
+#include "monitor_wrap.h"
+
+static void kex_gss_send_error(Gssctxt *ctxt);
+
+void
+kexgss_server(Kex *kex)
+{
+       OM_uint32 maj_status, min_status;
+       
+       /* Some GSSAPI implementations use the input value of ret_flags (an
+        * output variable) as a means of triggering mechanism specific 
+        * features. Initializing it to zero avoids inadvertently 
+        * activating this non-standard behaviour.*/
+
+       OM_uint32 ret_flags = 0;
+       gss_buffer_desc gssbuf,send_tok,recv_tok,msg_tok;
+       Gssctxt *ctxt = NULL;
+        unsigned int klen, kout;
+        unsigned char *kbuf;
+        unsigned char *hash;
+        DH *dh;
+        BIGNUM *shared_secret = NULL;
+        BIGNUM *dh_client_pub = NULL;
+       int type =0;
+       u_int slen;
+       gss_OID oid;
+       
+       /* Initialise GSSAPI */
+
+       debug2("%s: Identifying %s",__func__,kex->name);
+       oid=ssh_gssapi_server_id_kex(kex->name);
+       if (oid==NULL) {
+          fatal("Unknown gssapi mechanism");
+       }
+       
+       debug2("%s: Acquiring credentials",__func__);
+       
+       if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,oid)))) {
+               kex_gss_send_error(ctxt);
+               fatal("Unable to acquire credentials for the server");
+        }
+                                                                                                                                
+       do {
+               debug("Wait SSH2_MSG_GSSAPI_INIT");
+               type = packet_read();
+               switch(type) {
+               case SSH2_MSG_KEXGSS_INIT:
+                       if (dh_client_pub!=NULL) 
+                               fatal("Received KEXGSS_INIT after initialising");
+                       recv_tok.value=packet_get_string(&slen);
+                       recv_tok.length=slen; /* int vs. size_t */
+
+                       dh_client_pub = BN_new();
+                       
+                       if (dh_client_pub == NULL)
+                               fatal("dh_client_pub == NULL");
+                       packet_get_bignum2(dh_client_pub);
+                       
+                       /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
+                       break;
+               case SSH2_MSG_KEXGSS_CONTINUE:
+                       recv_tok.value=packet_get_string(&slen);
+                       recv_tok.length=slen; /* int vs. size_t */
+                       break;
+               default:
+                       packet_disconnect("Protocol error: didn't expect packet type %d",
+                                          type);
+               }
+               
+               maj_status=PRIVSEP(ssh_gssapi_accept_ctx(ctxt,&recv_tok, 
+                                                        &send_tok, &ret_flags));
+
+               gss_release_buffer(&min_status,&recv_tok);
+               
+               if (maj_status!=GSS_S_COMPLETE && send_tok.length==0) {
+                       fatal("Zero length token output when incomplete");
+               }
+
+               if (dh_client_pub == NULL)
+                       fatal("No client public key");
+               
+               if (maj_status & GSS_S_CONTINUE_NEEDED) {
+                       debug("Sending GSSAPI_CONTINUE");
+                       packet_start(SSH2_MSG_KEXGSS_CONTINUE);
+                       packet_put_string(send_tok.value,send_tok.length);
+                       packet_send();
+                       packet_write_wait();
+                       gss_release_buffer(&min_status, &send_tok);
+               }
+       } while (maj_status & GSS_S_CONTINUE_NEEDED);
+
+       if (GSS_ERROR(maj_status)) {
+               kex_gss_send_error(ctxt);
+               if (send_tok.length>0) {
+                       packet_start(SSH2_MSG_KEXGSS_CONTINUE);
+                       packet_put_string(send_tok.value,send_tok.length);
+                       packet_send();
+                       packet_write_wait();
+               }       
+               fatal("accept_ctx died");
+       }
+       
+       debug("gss_complete");
+       if (!(ret_flags & GSS_C_MUTUAL_FLAG))
+               fatal("mutual authentication flag wasn't set");
+               
+       if (!(ret_flags & GSS_C_INTEG_FLAG))
+               fatal("Integrity flag wasn't set");
+                       
+       dh = dh_new_group1();
+       dh_gen_key(dh, kex->we_need * 8);
+       
+        if (!dh_pub_is_valid(dh, dh_client_pub))
+                packet_disconnect("bad client public DH value");
+
+        klen = DH_size(dh);
+        kbuf = xmalloc(klen); 
+        kout = DH_compute_key(kbuf, dh_client_pub, dh);
+
+       shared_secret = BN_new();
+       BN_bin2bn(kbuf, kout, shared_secret);
+       memset(kbuf, 0, klen);
+       xfree(kbuf);
+       
+       /* The GSSAPI hash is identical to the Diffie Helman one */
+        hash = kex_dh_hash(
+            kex->client_version_string,
+            kex->server_version_string,
+            buffer_ptr(&kex->peer), buffer_len(&kex->peer),
+            buffer_ptr(&kex->my), buffer_len(&kex->my),
+            NULL, 0, /* Change this if we start sending host keys */
+            dh_client_pub,
+            dh->pub_key,
+            shared_secret
+       );
+       BN_free(dh_client_pub);
+               
+       if (kex->session_id == NULL) {
+               kex->session_id_len = 20;
+               kex->session_id = xmalloc(kex->session_id_len);
+               memcpy(kex->session_id, hash, kex->session_id_len);
+       }
+                               
+       gssbuf.value = hash;
+       gssbuf.length = 20; /* Hashlen appears to always be 20 */
+       
+       if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) {
+               kex_gss_send_error(ctxt);
+               fatal("Couldn't get MIC");
+       }
+       
+       packet_start(SSH2_MSG_KEXGSS_COMPLETE);
+       packet_put_bignum2(dh->pub_key);
+       packet_put_string((char *)msg_tok.value,msg_tok.length);
+
+       if (send_tok.length!=0) {
+               packet_put_char(1); /* true */
+               packet_put_string((char *)send_tok.value,send_tok.length);
+       } else {
+               packet_put_char(0); /* false */
+       }
+       packet_send();
+       packet_write_wait();
+
+        /* We used to store the client name and credentials here for later
+         * use. With privsep, its easier to do this as a by product of the
+         * call to accept_context, which stores delegated information when
+         * the context is complete */
+         
+       gss_release_buffer(&min_status, &send_tok);     
+
+       /* If we've got a context, delete it. It may be NULL if we've been
+        * using privsep */
+       ssh_gssapi_delete_ctx(&ctxt);
+       
+       DH_free(dh);
+
+       kex_derive_keys(kex, hash, shared_secret);
+       BN_clear_free(shared_secret);
+       kex_finish(kex);
+}
+
+static void 
+kex_gss_send_error(Gssctxt *ctxt) {
+       char *errstr;
+       OM_uint32 maj,min;
+               
+       errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min));
+       if (errstr) {
+               packet_start(SSH2_MSG_KEXGSS_ERROR);
+               packet_put_int(maj);
+               packet_put_int(min);
+               packet_put_cstring(errstr);
+               packet_put_cstring("");
+               packet_send();
+               packet_write_wait();
+               /* XXX - We should probably log the error locally here */
+               xfree(errstr);
+       }
+}
+#endif /* GSSAPI */
index 060b63745a3d2762bbd5b9a14edf4bca8a54463d..22710045eca50f19184202258214c4189f6b4392 100644 (file)
@@ -646,6 +646,8 @@ key_type_from_name(char *name)
                return KEY_RSA;
        } else if (strcmp(name, "ssh-dss") == 0) {
                return KEY_DSA;
+       } else if (strcmp(name, "null") == 0){
+               return KEY_NULL;
        }
        debug2("key_type_from_name: unknown key type '%s'", name);
        return KEY_UNSPEC;
index 725c7a04a7bf2d6cfaf2a488be20c0381c11cbfe..4ad321f7eca4f80349d5bde74af217ab64f65285 100644 (file)
@@ -34,6 +34,7 @@ enum types {
        KEY_RSA1,
        KEY_RSA,
        KEY_DSA,
+       KEY_NULL,
        KEY_UNSPEC
 };
 enum fp_type {
diff --git a/openssh/makegssname.pl b/openssh/makegssname.pl
new file mode 100644 (file)
index 0000000..1350cdd
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/perl
+
+use Convert::ASN1 qw(:tag);
+use Digest::MD5 qw(md5);
+use MIME::Base64;
+use Data::Dumper;
+$oid=shift;
+my $asn=Convert::ASN1->new;
+$asn->prepare("oid OBJECT IDENTIFIER");
+$encoded=$asn->encode(oid => $oid);
+Convert::ASN1::asn_dump($encoded);
+print Dumper($asn->decode($encoded));
+
+@entries=unpack("C*",$encoded);
+
+print "DER representation: ";
+foreach $entry (@entries) {
+  print "\\x";
+  printf "%02X",$entry;
+}
+print "\n";
+
+$digest = md5($encoded);
+# We only want the first 10 characters;
+# Conversations with the authors suggest that we want to use all of the
+# characters of the digest.
+#$digest = substr($digest,0,10);
+print "gsskeyex representation: ",encode_base64($digest),"\n";
+
+sub encode_object_id {
+  $string="";
+
+  my @data = ($_[0] =~ /(\d+)/g);
+
+  if(@data < 2) {
+      @data = (0);
+  }
+  else {
+      my $first = $data[1] + ($data[0] * 40);
+      splice(@data,0,2,$first);
+  }
+
+#  my $l = length $string;
+  $string .= pack("cw*", 0, @data);
+#  substr($string,$l,1) = asn_encode_length(length($string) - $l - 1);
+  return $string;
+}
+
+
index bce9e684c89669c5bd4f34028faa72a0b2b657a4..fe021f8003f1256fec184e1b37fe26481146e6dc 100644 (file)
@@ -59,6 +59,11 @@ RCSID("$OpenBSD: monitor.c,v 1.36 2003/04/01 10:22:21 markus Exp $");
 #include "ssh2.h"
 #include "mpaux.h"
 
+#ifdef GSSAPI
+#include "ssh-gss.h"
+static Gssctxt *gsscontext = NULL;
+#endif
+
 /* Imports */
 extern ServerOptions options;
 extern u_int utmp_len;
@@ -127,6 +132,16 @@ int mm_answer_krb4(int, Buffer *);
 int mm_answer_krb5(int, Buffer *);
 #endif
 
+#ifdef GSSAPI
+int mm_answer_gss_setup_ctx(int, Buffer *);
+int mm_answer_gss_accept_ctx(int, Buffer *);
+int mm_answer_gss_userok(int, Buffer *);
+int mm_answer_gss_sign(int, Buffer *);
+int mm_answer_gss_error(int, Buffer *);
+int mm_answer_gss_indicate_mechs(int, Buffer *);
+int mm_answer_gss_localname(int, Buffer *);
+#endif
+
 static Authctxt *authctxt;
 static BIGNUM *ssh1_challenge = NULL;  /* used for ssh1 rsa auth */
 
@@ -171,6 +186,18 @@ struct mon_table mon_dispatch_proto20[] = {
 #ifdef SKEY
     {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery},
     {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond},
+#endif
+#ifdef GSSAPI
+    {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx},
+    {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
+    {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
+    {MONITOR_REQ_GSSERR, MON_ISAUTH | MON_ONCE, mm_answer_gss_error},
+    {MONITOR_REQ_GSSMECHS, MON_ISAUTH, mm_answer_gss_indicate_mechs},
+    {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
+/* Turn this off until we use it */
+#if 0
+    {MONITOR_REQ_GSSLOCALNAME, MON_ISAUTH, mm_answer_gss_localname},
+#endif
 #endif
     {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed},
     {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify},
@@ -178,6 +205,13 @@ struct mon_table mon_dispatch_proto20[] = {
 };
 
 struct mon_table mon_dispatch_postauth20[] = {
+#ifdef GSSAPI
+    {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
+    {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
+    {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
+    {MONITOR_REQ_GSSERR, 0, mm_answer_gss_error},
+    {MONITOR_REQ_GSSMECHS, 0, mm_answer_gss_indicate_mechs},
+#endif
     {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
     {MONITOR_REQ_SIGN, 0, mm_answer_sign},
     {MONITOR_REQ_PTY, 0, mm_answer_pty},
@@ -267,6 +301,12 @@ monitor_child_preauth(struct monitor *pmonitor)
                /* Permit requests for moduli and signatures */
                monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
                monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
+#ifdef GSSAPI          
+               /* and for the GSSAPI key exchange */
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS, 1);
+#endif
        } else {
                mon_dispatch = mon_dispatch_proto15;
 
@@ -321,6 +361,13 @@ monitor_child_postauth(struct monitor *pmonitor)
                monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
                monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
 
+#ifdef GSSAPI
+               /* and for the GSSAPI key exchange */
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS,1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP,1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR,1);
+#endif
+
        } else {
                mon_dispatch = mon_dispatch_postauth15;
                monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
@@ -1468,6 +1515,9 @@ mm_get_kex(Buffer *m)
        kex->we_need = buffer_get_int(m);
        kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
        kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
+#ifdef GSSAPI
+       kex->kex[KEX_GSS_GRP1_SHA1] =kexgss_server;
+#endif
        kex->server = 1;
        kex->hostkey_type = buffer_get_int(m);
        kex->kex_type = buffer_get_int(m);
@@ -1648,3 +1698,170 @@ monitor_reinit(struct monitor *mon)
        mon->m_recvfd = pair[0];
        mon->m_sendfd = pair[1];
 }
+
+#ifdef GSSAPI
+
+int
+mm_answer_gss_setup_ctx(int socket, Buffer *m) {
+        gss_OID_desc oid;
+        OM_uint32 major;
+       u_int len;
+
+        oid.elements=buffer_get_string(m,&len);
+       oid.length=len;
+                
+        major=ssh_gssapi_server_ctx(&gsscontext,&oid);
+
+        xfree(oid.elements);
+
+        buffer_clear(m);
+        buffer_put_int(m,major);
+
+        mm_request_send(socket,MONITOR_ANS_GSSSETUP,m);
+
+       /* Now we have a context, enable the step and sign */
+       monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP,1);
+
+        return(0);
+}
+
+int
+mm_answer_gss_accept_ctx(int socket, Buffer *m) {
+        gss_buffer_desc in,out;
+        OM_uint32 major,minor;
+        OM_uint32 flags = 0; /* GSI needs this */
+
+        in.value = buffer_get_string(m,&in.length);
+        major=ssh_gssapi_accept_ctx(gsscontext,&in,&out,&flags);
+        xfree(in.value);
+
+        buffer_clear(m);
+        buffer_put_int(m, major);
+        buffer_put_string(m, out.value, out.length);
+        buffer_put_int(m, flags);
+        mm_request_send(socket,MONITOR_ANS_GSSSTEP,m);
+
+        gss_release_buffer(&minor, &out);
+
+       /* Complete - now we can do signing */
+       if (major==GSS_S_COMPLETE) {
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP,0);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN,1);            
+       }
+        return(0);
+}
+
+int
+mm_answer_gss_userok(int socket, Buffer *m) {
+       int authenticated;
+
+        authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
+
+        buffer_clear(m);
+        buffer_put_int(m, authenticated);
+
+        debug3("%s: sending result %d", __func__, authenticated);
+        mm_request_send(socket, MONITOR_ANS_GSSUSEROK, m);
+
+       /* XXX - auth method could also be 'external' */
+       auth_method="gssapi";
+       
+        /* Monitor loop will terminate if authenticated */
+        return(authenticated);
+}
+
+int
+mm_answer_gss_sign(int socket, Buffer *m) {
+        gss_buffer_desc data,hash;
+        OM_uint32 major,minor;
+
+        data.value = buffer_get_string(m,&data.length);
+        if (data.length != 20)
+                fatal("%s: data length incorrect: %d", __func__, data.length);
+
+        /* Save the session ID - only first time round */
+        if (session_id2_len == 0) {
+                session_id2_len=data.length;
+                session_id2 = xmalloc(session_id2_len);
+                memcpy(session_id2, data.value, session_id2_len);
+        }
+        major=ssh_gssapi_sign(gsscontext, &data, &hash);
+
+        xfree(data.value);
+
+        buffer_clear(m);
+        buffer_put_int(m, major);
+        buffer_put_string(m, hash.value, hash.length);
+
+        mm_request_send(socket,MONITOR_ANS_GSSSIGN,m);
+
+        gss_release_buffer(&minor,&hash);
+
+       /* Turn on permissions for getpwnam */
+       monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
+       
+        return(0);
+}
+
+int
+mm_answer_gss_error(int socket, Buffer *m) {
+        OM_uint32 major,minor;
+        char *msg;
+
+       msg=ssh_gssapi_last_error(gsscontext,&major,&minor);
+       buffer_clear(m);
+       buffer_put_int(m,major);
+       buffer_put_int(m,minor);
+       buffer_put_cstring(m,msg);
+
+       mm_request_send(socket,MONITOR_ANS_GSSERR,m);
+
+       xfree(msg);
+       
+        return(0);
+}
+
+int
+mm_answer_gss_indicate_mechs(int socket, Buffer *m) {
+        OM_uint32 major,minor;
+       gss_OID_set mech_set;
+       int i;
+
+       major=gss_indicate_mechs(&minor, &mech_set);
+
+       buffer_clear(m);
+       buffer_put_int(m, major);
+       buffer_put_int(m, mech_set->count);
+       for (i=0; i < mech_set->count; i++) {
+           buffer_put_string(m, mech_set->elements[i].elements,
+                             mech_set->elements[i].length);
+       }
+
+       gss_release_oid_set(&minor,&mech_set);
+       
+       mm_request_send(socket,MONITOR_ANS_GSSMECHS,m);
+
+       return(0);
+}
+
+int
+mm_answer_gss_localname(int socket, Buffer *m) {
+       char *name;
+
+       ssh_gssapi_localname(&name);
+
+        buffer_clear(m);
+       if (name) {
+           buffer_put_cstring(m, name);
+           debug3("%s: sending result %s", __func__, name);
+           xfree(name);
+       } else {
+           buffer_put_cstring(m, "");
+           debug3("%s: sending result \"\"", __func__);
+       }
+
+        mm_request_send(socket, MONITOR_ANS_GSSLOCALNAME, m);
+
+        return(0);
+}
+#endif /* GSSAPI */
index 668ac98970dd383881837b3685233731710199d3..086bab684e267cc9d83339b7237ac99be0155e49 100644 (file)
@@ -39,6 +39,15 @@ enum monitor_reqtype {
        MONITOR_REQ_BSDAUTHRESPOND, MONITOR_ANS_BSDAUTHRESPOND,
        MONITOR_REQ_SKEYQUERY, MONITOR_ANS_SKEYQUERY,
        MONITOR_REQ_SKEYRESPOND, MONITOR_ANS_SKEYRESPOND,
+#ifdef GSSAPI
+       MONITOR_REQ_GSSSETUP,MONITOR_ANS_GSSSETUP,
+       MONITOR_REQ_GSSSTEP,MONITOR_ANS_GSSSTEP,
+       MONITOR_REQ_GSSSIGN,MONITOR_ANS_GSSSIGN,
+       MONITOR_REQ_GSSUSEROK,MONITOR_ANS_GSSUSEROK,
+       MONITOR_REQ_GSSMECHS,MONITOR_ANS_GSSMECHS,
+       MONITOR_REQ_GSSLOCALNAME,MONITOR_ANS_GSSLOCALNAME,
+       MONITOR_REQ_GSSERR,MONITOR_ANS_GSSERR,
+#endif
        MONITOR_REQ_KEYALLOWED, MONITOR_ANS_KEYALLOWED,
        MONITOR_REQ_KEYVERIFY, MONITOR_ANS_KEYVERIFY,
        MONITOR_REQ_KEYEXPORT,
index c9714138ada711a746f0f8f0d4f2123198c6cf42..0beeb9b6c5fcfcbc67a97f18934b9daa9f37dc10 100644 (file)
@@ -52,6 +52,10 @@ RCSID("$OpenBSD: monitor_wrap.c,v 1.24 2003/04/01 10:22:21 markus Exp $");
 #include "channels.h"
 #include "session.h"
 
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
 /* Imports */
 extern int compat20;
 extern Newkeys *newkeys[];
@@ -1019,3 +1023,160 @@ mm_auth_krb5(void *ctx, void *argp, char **userp, void *resp)
        return (success);
 }
 #endif
+#ifdef GSSAPI
+OM_uint32
+mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) {
+        Buffer m;
+        OM_uint32 major;
+                
+        /* Client doesn't get to see the context */
+        *ctx=NULL;
+
+        buffer_init(&m);
+        buffer_put_string(&m,oid->elements,oid->length);
+
+        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, &m);
+        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSETUP, &m);
+
+        major=buffer_get_int(&m);
+
+       buffer_free(&m);
+        return(major);
+}
+
+OM_uint32
+mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in,
+                         gss_buffer_desc *out, OM_uint32 *flags) {
+        Buffer m;
+        OM_uint32 major;
+
+        buffer_init(&m);
+        buffer_put_string(&m, in->value, in->length);
+
+        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTEP, &m);
+        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTEP, &m);
+
+        major=buffer_get_int(&m);
+        out->value=buffer_get_string(&m,&out->length);
+        if (flags) *flags=buffer_get_int(&m);
+
+       buffer_free(&m);
+       
+        return(major);
+}
+
+int
+mm_ssh_gssapi_userok(char *user) {
+        Buffer m;
+        int authenticated = 0;
+
+        buffer_init(&m);
+        
+        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m);
+        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK,
+                                  &m);
+
+        authenticated = buffer_get_int(&m);
+
+        buffer_free(&m);
+        debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
+        return(authenticated);
+}
+
+OM_uint32
+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) {
+        Buffer m;
+        OM_uint32 major;
+
+        buffer_init(&m);
+        buffer_put_string(&m, data->value, data->length);
+
+        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
+        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
+
+        major=buffer_get_int(&m);
+        hash->value = buffer_get_string(&m, &hash->length);
+
+       buffer_free(&m);
+       
+        return(major);
+}
+
+char *
+mm_ssh_gssapi_last_error(Gssctxt *ctx, OM_uint32 *major, OM_uint32 *minor) {
+       Buffer m;
+       OM_uint32 maj,min;
+       char *errstr;
+       
+       buffer_init(&m);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSERR, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSERR, &m);
+
+       maj = buffer_get_int(&m);
+       min = buffer_get_int(&m);
+
+       if (major) *major=maj;
+       if (minor) *minor=min;
+       
+       errstr=buffer_get_string(&m,NULL);
+
+       buffer_free(&m);
+       
+       return(errstr);
+}      
+
+OM_uint32
+mm_gss_indicate_mechs(OM_uint32 *minor_status, gss_OID_set *mech_set)
+{
+        Buffer m;
+       OM_uint32 major,minor;
+       int count;
+       gss_OID_desc oid;
+        u_int length;
+
+       buffer_init(&m);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSMECHS, &m);
+        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSMECHS,
+                                 &m);
+        major=buffer_get_int(&m);
+       count=buffer_get_int(&m);
+       
+        gss_create_empty_oid_set(&minor,mech_set);
+       while(count-->0) {
+           oid.elements=buffer_get_string(&m,&length);
+           oid.length=length;
+           gss_add_oid_set_member(&minor,&oid,mech_set);
+       }
+
+       buffer_free(&m);
+       
+        return(major);
+}
+
+int
+mm_ssh_gssapi_localname(char **lname)
+{
+        Buffer m;
+
+       buffer_init(&m);
+        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSLOCALNAME, &m);
+
+        debug3("%s: waiting for MONITOR_ANS_GSSLOCALNAME", __func__);
+        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSLOCALNAME,
+                                  &m);
+
+       *lname = buffer_get_string(&m, NULL);
+
+        buffer_free(&m);
+       if (lname[0] == '\0') {
+           debug3("%s: gssapi identity mapping failed", __func__);
+       } else {
+           debug3("%s: gssapi identity mapped to %s", __func__, *lname);
+       }
+       
+        return(0);
+}      
+#endif /* GSSAPI */
index d960a3d0b4c122d062d4296c8b428f88b542b17e..ffd2f2884b5200bbef58f042a3638aa53d4d0634 100644 (file)
@@ -59,6 +59,18 @@ BIGNUM *mm_auth_rsa_generate_challenge(Key *);
 void mm_start_pam(char *);
 #endif
 
+#ifdef GSSAPI
+#include "ssh-gss.h"
+OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **ctxt, gss_OID oid);
+OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *ctxt, gss_buffer_desc *recv,
+                                  gss_buffer_desc *send, OM_uint32 *flags);
+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *ctxt, gss_buffer_desc *buffer,
+                            gss_buffer_desc *hash);
+int mm_ssh_gssapi_userok(char *user);
+char *mm_ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min);
+
+#endif
+
 void mm_terminate(void);
 int mm_pty_allocate(int *, int *, char *, int);
 void mm_session_pty_cleanup2(void *);
index 1df5ce2d9b9d8d2210fc43972940e45fd8fed626..7bf3b08049fb3f6a81cc2f3841e7832d3160f615 100644 (file)
@@ -97,6 +97,12 @@ typedef enum {
 #if defined(KRB4) || defined(KRB5)
        oKerberosAuthentication,
 #endif
+#ifdef GSSAPI
+       oGssAuthentication, oGssDelegateCreds,
+#ifdef GSI
+       oGssGlobusDelegateLimitedCreds,
+#endif /* GSI */
+#endif /* GSSAPI */
 #if defined(AFS) || defined(KRB5)
        oKerberosTgtPassing,
 #endif
@@ -144,6 +150,15 @@ static struct {
 #if defined(KRB4) || defined(KRB5)
        { "kerberosauthentication", oKerberosAuthentication },
 #endif
+#ifdef GSSAPI
+       { "gssapiauthentication", oGssAuthentication },
+       { "gssapidelegatecredentials", oGssDelegateCreds },
+#ifdef GSI
+       /* For backwards compatability with old 1.2.27 client code */
+       { "forwardgssapiglobusproxy", oGssDelegateCreds }, /* alias */
+       { "forwardgssapiglobuslimitedproxy", oGssGlobusDelegateLimitedCreds },
+#endif /* GSI */
+#endif /* GSSAPI */
 #if defined(AFS) || defined(KRB5)
        { "kerberostgtpassing", oKerberosTgtPassing },
 #endif
@@ -366,6 +381,23 @@ parse_flag:
                intptr = &options->kerberos_authentication;
                goto parse_flag;
 #endif
+#ifdef GSSAPI
+       case oGssAuthentication:
+               intptr = &options->gss_authentication;
+               goto parse_flag;
+      
+       case oGssDelegateCreds:
+               intptr = &options->gss_deleg_creds;
+               goto parse_flag;
+#ifdef GSI
+       case oGssGlobusDelegateLimitedCreds:
+               intptr = &options->gss_globus_deleg_limited_proxy;
+               goto parse_flag;
+#endif /* GSI */
+
+#endif /* GSSAPI */
+
 #if defined(AFS) || defined(KRB5)
        case oKerberosTgtPassing:
                intptr = &options->kerberos_tgt_passing;
@@ -748,6 +780,14 @@ initialize_options(Options * options)
        options->rsa_authentication = -1;
        options->pubkey_authentication = -1;
        options->challenge_response_authentication = -1;
+#ifdef GSSAPI
+        options->gss_authentication = -1;
+        options->gss_deleg_creds = -1;
+#ifdef GSI
+        options->gss_globus_deleg_limited_proxy = -1;
+#endif /* GSI */
+#endif /* GSSAPI */
+
 #if defined(KRB4) || defined(KRB5)
        options->kerberos_authentication = -1;
 #endif
@@ -825,6 +865,16 @@ fill_default_options(Options * options)
                options->pubkey_authentication = 1;
        if (options->challenge_response_authentication == -1)
                options->challenge_response_authentication = 1;
+#ifdef GSSAPI
+       if (options->gss_authentication == -1)
+               options->gss_authentication = 1;
+       if (options->gss_deleg_creds == -1)
+               options->gss_deleg_creds = 1;
+#ifdef GSI
+       if (options->gss_globus_deleg_limited_proxy == -1)
+               options->gss_globus_deleg_limited_proxy = 0;
+#endif /* GSI */
+#endif /* GSSAPI */
 #if defined(KRB4) || defined(KRB5)
        if (options->kerberos_authentication == -1)
                options->kerberos_authentication = 1;
index 78e04fedf98b10729e33f44602daae5c3cc01a10..aad8c82c1c06aa5179d8a2d1012c1a75114819e2 100644 (file)
@@ -47,6 +47,15 @@ typedef struct {
 #if defined(AFS) || defined(KRB5)
        int     kerberos_tgt_passing;   /* Try Kerberos TGT passing. */
 #endif
+
+#ifdef GSSAPI
+       int     gss_authentication;
+       int     gss_deleg_creds;
+#ifdef GSI
+       int     gss_globus_deleg_limited_proxy;
+#endif /* GSI */
+#endif /* GSSAPI */
+
 #ifdef AFS
        int     afs_token_passing;      /* Try AFS token passing. */
 #endif
index 2510659ee4b916c9fb985c51e67deadb40cb3be8..f9b89960166b16d3ffbd87a97937a9b7d7020c52 100644 (file)
@@ -86,6 +86,12 @@ initialize_server_options(ServerOptions *options)
        options->hostbased_uses_name_from_packet_only = -1;
        options->rsa_authentication = -1;
        options->pubkey_authentication = -1;
+#ifdef GSSAPI
+       options->gss_authentication=-1;
+       options->gss_keyex=-1;
+       options->gss_use_session_ccache = -1;
+       options->gss_cleanup_creds = -1;
+#endif
 #if defined(KRB4) || defined(KRB5)
        options->kerberos_authentication = -1;
        options->kerberos_or_local_passwd = -1;
@@ -200,6 +206,16 @@ fill_default_server_options(ServerOptions *options)
                options->rsa_authentication = 1;
        if (options->pubkey_authentication == -1)
                options->pubkey_authentication = 1;
+#ifdef GSSAPI
+       if (options->gss_authentication == -1)
+               options->gss_authentication = 1;
+       if (options->gss_keyex == -1)
+               options->gss_keyex =1;
+       if (options->gss_use_session_ccache == -1)
+               options->gss_use_session_ccache = 1;
+       if (options->gss_cleanup_creds == -1)
+               options->gss_cleanup_creds = 1;
+#endif
 #if defined(KRB4) || defined(KRB5)
        if (options->kerberos_authentication == -1)
                options->kerberos_authentication = 0;
@@ -280,6 +296,9 @@ typedef enum {
        sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime,
        sPermitRootLogin, sLogFacility, sLogLevel,
        sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication,
+#ifdef GSSAPI
+       sGssAuthentication, sGssKeyEx, sGssUseSessionCredCache, sGssCleanupCreds,
+#endif
 #if defined(KRB4) || defined(KRB5)
        sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
 #endif
@@ -330,6 +349,13 @@ static struct {
        { "rsaauthentication", sRSAAuthentication },
        { "pubkeyauthentication", sPubkeyAuthentication },
        { "dsaauthentication", sPubkeyAuthentication },                 /* alias */
+#ifdef GSSAPI
+       { "gssapiauthentication", sGssAuthentication },
+       { "gssapikeyexchange", sGssKeyEx },
+       { "gssusesessionccache", sGssUseSessionCredCache },
+       { "gssapiusesessioncredcache", sGssUseSessionCredCache },
+       { "gssapicleanupcreds", sGssCleanupCreds },
+#endif
 #if defined(KRB4) || defined(KRB5)
        { "kerberosauthentication", sKerberosAuthentication },
        { "kerberosorlocalpasswd", sKerberosOrLocalPasswd },
@@ -645,6 +671,20 @@ parse_flag:
        case sPubkeyAuthentication:
                intptr = &options->pubkey_authentication;
                goto parse_flag;
+#ifdef GSSAPI
+       case sGssAuthentication:
+               intptr = &options->gss_authentication;
+               goto parse_flag;
+       case sGssKeyEx:
+               intptr = &options->gss_keyex;
+               goto parse_flag;
+       case sGssUseSessionCredCache:
+               intptr = &options->gss_use_session_ccache;
+               goto parse_flag;
+       case sGssCleanupCreds:
+               intptr = &options->gss_cleanup_creds;
+               goto parse_flag;
+#endif
 #if defined(KRB4) || defined(KRB5)
        case sKerberosAuthentication:
                intptr = &options->kerberos_authentication;
index 024987dd6983cfd8a743115e9451f29a97d6cc9b..1c947da6ce03b1794bc5cd83572b62b6e4015d27 100644 (file)
@@ -73,6 +73,13 @@ typedef struct {
        int     hostbased_uses_name_from_packet_only; /* experimental */
        int     rsa_authentication;     /* If true, permit RSA authentication. */
        int     pubkey_authentication;  /* If true, permit ssh2 pubkey authentication. */
+       #ifdef GSSAPI
+       int     gss_authentication;
+       int     gss_keyex;
+       int     gss_use_session_ccache;        /* If true, delegated credentials are
+                                               * stored in a session specific cache */
+       int     gss_cleanup_creds;             /* If true, destroy cred cache on logout */
+#endif 
 #if defined(KRB4) || defined(KRB5)
        int     kerberos_authentication;        /* If true, permit Kerberos
                                                 * authentication. */
index c75fea9669330b60fff51c01ede0c957099a008d..e85bc9ee858738c9597db751f4cf7d439356ffb7 100644 (file)
@@ -58,6 +58,10 @@ RCSID("$OpenBSD: session.c,v 1.154 2003/03/05 22:33:43 markus Exp $");
 #include "session.h"
 #include "monitor_wrap.h"
 
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
 #ifdef HAVE_CYGWIN
 #include <windows.h>
 #include <sys/cygwin.h>
@@ -455,6 +459,12 @@ do_exec_no_pty(Session *s, const char *command)
 
        session_proctitle(s);
 
+#if defined(GSSAPI)
+       temporarily_use_uid(s->pw);
+       ssh_gssapi_storecreds();
+       restore_uid();
+#endif
+
 #if defined(USE_PAM)
        do_pam_session(s->pw->pw_name, NULL);
        do_pam_setcred(1);
@@ -582,6 +592,12 @@ do_exec_pty(Session *s, const char *command)
        ptyfd = s->ptyfd;
        ttyfd = s->ttyfd;
 
+#if defined(GSSAPI)
+       temporarily_use_uid(s->pw);
+       ssh_gssapi_storecreds();
+       restore_uid();
+#endif
+
 #if defined(USE_PAM)
        do_pam_session(s->pw->pw_name, s->tty);
        do_pam_setcred(1);
@@ -840,7 +856,7 @@ check_quietlogin(Session *s, const char *command)
  * Sets the value of the given variable in the environment.  If the variable
  * already exists, its value is overriden.
  */
-static void
+void
 child_set_env(char ***envp, u_int *envsizep, const char *name,
        const char *value)
 {
@@ -967,6 +983,13 @@ do_setup_env(Session *s, const char *shell)
        copy_environment(environ, &env, &envsize);
 #endif
 
+#ifdef GSSAPI
+       /* Allow any GSSAPI methods that we've used to alter 
+        * the childs environment as they see fit
+        */
+       ssh_gssapi_do_child(&env,&envsize);
+#endif
+
        if (!options.use_login) {
                /* Set basic environment. */
                child_set_env(&env, &envsize, "USER", pw->pw_name);
@@ -2121,4 +2144,7 @@ static void
 do_authenticated2(Authctxt *authctxt)
 {
        server_loop2(authctxt);
+#if defined(GSSAPI)
+       ssh_gssapi_cleanup_creds(NULL);
+#endif
 }
index d3ddfab75f98644b7724986b02c26e193674dca3..7fc3653cde92e42e8141ab4bcd25864ae0c22809 100644 (file)
@@ -68,4 +68,7 @@ Session       *session_new(void);
 Session        *session_by_tty(char *);
 void    session_close(Session *);
 void    do_setusercontext(struct passwd *);
+
+void    child_set_env(char ***envp, u_int *envsizep, const char *name,
+                      const char *value);
 #endif
diff --git a/openssh/ssh-gss.h b/openssh/ssh-gss.h
new file mode 100644 (file)
index 0000000..2260232
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SSH_GSS_H
+#define _SSH_GSS_H
+
+#ifdef GSSAPI
+
+#include "kex.h"
+#include "buffer.h"
+
+#include <gssapi.h>
+
+#ifdef KRB5
+#ifndef HEIMDAL
+#include <gssapi_generic.h>
+
+/* MIT Kerberos doesn't seem to define GSS_NT_HOSTBASED_SERVICE */
+#ifndef GSS_C_NT_HOSTBASED_SERVICE
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#endif /* GSS_C_NT_... */
+#endif /* !HEIMDAL */
+#endif /* KRB5 */
+
+/* draft-ietf-secsh-gsskeyex-03 */
+#define SSH2_MSG_KEXGSS_INIT                           30
+#define SSH2_MSG_KEXGSS_CONTINUE                       31
+#define SSH2_MSG_KEXGSS_COMPLETE                       32
+#define SSH2_MSG_KEXGSS_HOSTKEY                                33
+#define SSH2_MSG_KEXGSS_ERROR                          34
+#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE              60
+#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN                 61
+#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE     63    
+#define SSH2_MSG_USERAUTH_GSSAPI_ERROR                 64  
+#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK                        65
+
+#define KEX_GSS_SHA1                                   "gss-group1-sha1-"
+
+typedef struct {
+        char *filename;
+        char *envvar;
+        char *envval;
+        void *data;
+} ssh_gssapi_ccache;
+
+typedef struct {
+       gss_buffer_desc name;
+       gss_cred_id_t   creds;
+       struct ssh_gssapi_mech_struct *mech;
+       ssh_gssapi_ccache store;
+} ssh_gssapi_client;
+
+typedef struct ssh_gssapi_mech_struct {
+        char *enc_name;
+        char *name;
+        gss_OID_desc oid;
+       int (*dochild) (ssh_gssapi_client *);
+       int (*userok) (ssh_gssapi_client *, char *);
+       int (*localname) (ssh_gssapi_client *, char **);
+       void (*storecreds) (ssh_gssapi_client *);
+} ssh_gssapi_mech;
+
+
+
+typedef struct {
+       OM_uint32       major; /* both */
+       OM_uint32       minor; /* both */
+       gss_ctx_id_t    context; /* both */
+       gss_name_t      name; /* both */
+       gss_OID         oid; /* client */
+       gss_cred_id_t   creds; /* server */
+       gss_name_t      client; /* server */
+       gss_cred_id_t   client_creds; /* server */
+} Gssctxt;
+
+extern ssh_gssapi_mech *supported_mechs[];
+
+char *ssh_gssapi_mechanisms(char *host);
+char *ssh_gssapi_client_mechanisms(char *host);
+gss_OID ssh_gssapi_client_id_kex(Gssctxt *ctx, char *name);
+int  ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len);
+void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len);
+void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid);
+void ssh_gssapi_supported_oids(gss_OID_set *oidset);
+ssh_gssapi_mech *ssh_gssapi_get_ctype(Gssctxt *ctxt);
+
+OM_uint32 ssh_gssapi_import_name(Gssctxt *ctx, const char *host);
+OM_uint32 ssh_gssapi_acquire_cred(Gssctxt *ctx);
+OM_uint32 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds,
+                             gss_buffer_desc *recv_tok, 
+                             gss_buffer_desc *send_tok, OM_uint32 *flags);
+OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx,
+                               gss_buffer_desc *recv_tok,
+                               gss_buffer_desc *send_tok,
+                               OM_uint32 *flags);
+OM_uint32 ssh_gssapi_getclient(Gssctxt *ctx,
+                               ssh_gssapi_mech **mech,
+                               gss_buffer_desc *name,
+                               gss_cred_id_t *creds);
+void ssh_gssapi_error(Gssctxt *ctx);
+char *ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min);
+void ssh_gssapi_build_ctx(Gssctxt **ctx);
+void ssh_gssapi_delete_ctx(Gssctxt **ctx);
+OM_uint32 ssh_gssapi_server_ctx(Gssctxt **ctx,gss_OID oid);
+
+int ssh_gssapi_check_mechanism(gss_OID oid, char *host);
+
+/* In the server */
+gss_OID ssh_gssapi_server_id_kex(char *name);
+int ssh_gssapi_userok(char *name);
+int ssh_gssapi_localname(char **name);
+void ssh_gssapi_server(Kex *kex, Buffer *client_kexinit, 
+                      Buffer *server_kexinit);
+
+OM_uint32 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *buffer, 
+                                       gss_buffer_desc *hash);
+
+void ssh_gssapi_do_child(char ***envp, u_int *envsizep);                 
+void ssh_gssapi_cleanup_creds(void *ignored);
+void ssh_gssapi_storecreds();
+char *ssh_gssapi_server_mechanisms();
+#endif /* GSSAPI */
+
+#endif /* _SSH_GSS_H */
index 642b34b9e49417563e75e119b3cfc38f625e72a2..4f6933a2fc69d085abefc5685767b6096deeb080 100644 (file)
@@ -48,6 +48,10 @@ RCSID("$OpenBSD: sshconnect2.c,v 1.114 2003/04/01 10:22:21 markus Exp $");
 #include "msg.h"
 #include "pathnames.h"
 
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
 /* import */
 extern char *client_version_string;
 extern char *server_version_string;
@@ -77,10 +81,26 @@ void
 ssh_kex2(char *host, struct sockaddr *hostaddr)
 {
        Kex *kex;
+#ifdef GSSAPI
+       char *orig, *gss;
+       int len;
+#endif
 
        xxx_host = host;
        xxx_hostaddr = hostaddr;
 
+#ifdef GSSAPI
+       /* Add the GSSAPI mechanisms currently supported on this client to
+        * the key exchange algorithm proposal */
+       orig = myproposal[PROPOSAL_KEX_ALGS];
+       gss = ssh_gssapi_client_mechanisms(get_canonical_hostname(1));  
+       if (gss) {
+          len = strlen(orig)+strlen(gss)+2;
+          myproposal[PROPOSAL_KEX_ALGS]=xmalloc(len);
+          snprintf(myproposal[PROPOSAL_KEX_ALGS],len,"%s,%s",gss,orig);
+       }
+#endif
+
        if (options.ciphers == (char *)-1) {
                log("No valid ciphers for protocol version 2 given, using defaults.");
                options.ciphers = NULL;
@@ -108,14 +128,29 @@ ssh_kex2(char *host, struct sockaddr *hostaddr)
                myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
                    options.hostkeyalgorithms;
 
+#ifdef GSSAPI
+        /* If we've got GSSAPI algorithms, then we also support the
+         * 'null' hostkey, as a last resort */
+        if (gss) {
+                orig=myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
+                len = strlen(orig)+sizeof(",null");
+                myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]=xmalloc(len);
+                snprintf(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],len,"%s,null",orig);
+        }
+#endif
        /* start key exchange */
        kex = kex_setup(myproposal);
        kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
        kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
+#ifdef GSSAPI
+       kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
+#endif
        kex->client_version_string=client_version_string;
        kex->server_version_string=server_version_string;
        kex->verify_host_key=&verify_host_key_callback;
-
+#ifdef GSSAPI
+       kex->options.gss_deleg_creds=options.gss_deleg_creds;
+#endif
        xxx_kex = kex;
 
        dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
@@ -160,6 +195,8 @@ struct Authctxt {
        Sensitive *sensitive;
        /* kbd-interactive */
        int info_req_seen;
+       /* generic */
+       void *methoddata;
 };
 struct Authmethod {
        char    *name;          /* string to compare against server's list */
@@ -182,6 +219,16 @@ int        userauth_passwd(Authctxt *);
 int    userauth_kbdint(Authctxt *);
 int    userauth_hostbased(Authctxt *);
 
+#ifdef GSSAPI
+int    userauth_external(Authctxt *authctxt);
+int    userauth_gssapi(Authctxt *authctxt);
+void   input_gssapi_response(int type, u_int32_t, void *);
+void   input_gssapi_token(int type, u_int32_t, void *);
+void   input_gssapi_hash(int type, u_int32_t, void *);
+void   input_gssapi_error(int, u_int32_t, void *);
+void   input_gssapi_errtok(int, u_int32_t, void *);
+#endif
+
 void   userauth(Authctxt *, char *);
 
 static int sign_and_send_pubkey(Authctxt *, Key *, sign_cb_fn *);
@@ -192,6 +239,16 @@ static Authmethod *authmethod_lookup(const char *name);
 static char *authmethods_get(void);
 
 Authmethod authmethods[] = {
+#ifdef GSSAPI
+       {"external-keyx",
+               userauth_external,
+               &options.gss_authentication,
+               NULL},
+       {"gssapi",
+               userauth_gssapi,
+               &options.gss_authentication,
+               NULL},
+#endif
        {"hostbased",
                userauth_hostbased,
                &options.hostbased_authentication,
@@ -256,6 +313,7 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host,
        authctxt.success = 0;
        authctxt.method = authmethod_lookup("none");
        authctxt.authlist = NULL;
+       authctxt.methoddata = NULL;
        authctxt.sensitive = sensitive;
        authctxt.info_req_seen = 0;
        if (authctxt.method == NULL)
@@ -278,6 +336,11 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host,
 void
 userauth(Authctxt *authctxt, char *authlist)
 {
+       if (authctxt->methoddata!=NULL) {
+               xfree(authctxt->methoddata);
+               authctxt->methoddata=NULL;
+       }
+           
        if (authlist == NULL) {
                authlist = authctxt->authlist;
        } else {
@@ -327,6 +390,8 @@ input_userauth_success(int type, u_int32_t seq, void *ctxt)
                fatal("input_userauth_success: no authentication context");
        if (authctxt->authlist)
                xfree(authctxt->authlist);
+       if (authctxt->methoddata)
+               xfree(authctxt->methoddata);
        clear_auth_state(authctxt);
        authctxt->success = 1;                  /* break out */
 }
@@ -428,6 +493,263 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt)
 
 }
 
+#ifdef GSSAPI
+int 
+userauth_gssapi(Authctxt *authctxt)
+{
+       Gssctxt *gssctxt = NULL;
+       static gss_OID_set supported = NULL;
+       static int mech=0;
+       OM_uint32 min;
+       int ok=0;
+
+       /* Things work better if we send one mechanism at a time, rather
+        * than them all at once. This means that if we fail at some point
+        * in the middle of a negotiation, we can come back and try something
+        * different. */
+
+       if (datafellows & SSH_OLD_GSSAPI) return 0;
+       
+       /* Before we offer a mechanism, check that we can support it. Don't
+        * bother trying to get credentials - as the standard fallback will
+        * deal with that kind of failure.
+        */
+
+       if (supported==NULL) gss_indicate_mechs(&min, &supported);
+       
+       while (mech<supported->count && !ok) {
+               if (gssctxt) ssh_gssapi_delete_ctx(&gssctxt);
+               ssh_gssapi_build_ctx(&gssctxt);
+               ssh_gssapi_set_oid(gssctxt,&supported->elements[mech]);
+
+               /* The DER encoding below only works for lengths<128,
+                * so check this here 
+                */
+               if (supported->elements[mech].length<128 &&
+                   !GSS_ERROR(ssh_gssapi_import_name(gssctxt,
+                                                     authctxt->host))) {
+                       ok = 1; /* Mechanism works */
+               } else {
+                       mech++;
+               }
+       }
+       
+       if (!ok) return 0;
+       
+       authctxt->methoddata=(void *)gssctxt;
+               
+       packet_start(SSH2_MSG_USERAUTH_REQUEST);
+       packet_put_cstring(authctxt->server_user);
+       packet_put_cstring(authctxt->service);
+        packet_put_cstring(authctxt->method->name);
+       
+       packet_put_int(1);
+
+       /* The newest gsskeyex draft stipulates that OIDs should
+        * be DER encoded, so we need to add the object type and
+        * length information back on */
+       if (datafellows & SSH_BUG_GSSAPI_BER) {
+               packet_put_string(supported->elements[mech].elements,
+                                 supported->elements[mech].length);
+       } else {
+               packet_put_int((supported->elements[mech].length)+2);
+               packet_put_char(0x06);
+               packet_put_char(supported->elements[mech].length);
+               packet_put_raw(supported->elements[mech].elements,
+                              supported->elements[mech].length);
+       }
+
+        packet_send();
+        packet_write_wait();
+
+        dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,&input_gssapi_response);
+        dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,&input_gssapi_token);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR,&input_gssapi_error);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,&input_gssapi_errtok);
+       
+       mech++; /* Move along to next candidate */
+
+        return 1;
+}
+
+void
+input_gssapi_response(int type, u_int32_t plen, void *ctxt) 
+{
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       OM_uint32 status,ms;
+       int oidlen;
+       char *oidv;
+       gss_buffer_desc send_tok;
+       
+       if (authctxt == NULL)
+               fatal("input_gssapi_response: no authentication context");
+       gssctxt = authctxt->methoddata;
+       
+       /* Setup our OID */
+       oidv=packet_get_string(&oidlen);
+       
+       if (datafellows & SSH_BUG_GSSAPI_BER) {
+               if (!ssh_gssapi_check_oid(gssctxt,oidv,oidlen)) {
+                       fatal("Server returned different OID than expected");
+               }
+               ssh_gssapi_set_oid_data(gssctxt,oidv,oidlen);
+       } else {
+               if(oidv[0]!=0x06 || oidv[1]!=oidlen-2) {
+                       debug("Badly encoded mechanism OID received");
+                       clear_auth_state(authctxt);
+                       userauth(authctxt,NULL);
+                       return;
+               }
+               if (!ssh_gssapi_check_oid(gssctxt,oidv+2,oidlen-2)) {
+                       fatal("Server returned different OID than expected");
+               }
+               ssh_gssapi_set_oid_data(gssctxt,oidv+2,oidlen-2);
+       }
+               
+       packet_check_eom();
+       
+       status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+                                    GSS_C_NO_BUFFER, &send_tok, 
+                                    NULL);
+       if (GSS_ERROR(status)) {
+               if (send_tok.length>0) {
+                       packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+                       packet_put_string(send_tok.value,send_tok.length);
+                       packet_send();
+                       packet_write_wait();
+               }
+               /* Start again with next method on list */
+               debug("Trying to start again");
+               clear_auth_state(authctxt);
+               userauth(authctxt,NULL);
+               return;
+       }
+
+       /* We must have data to send */                                 
+       packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+       packet_put_string(send_tok.value,send_tok.length);
+       packet_send();
+       packet_write_wait();
+       gss_release_buffer(&ms, &send_tok);
+}
+
+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 status;
+       u_int slen;
+       
+       if (authctxt == NULL)
+               fatal("input_gssapi_response: no authentication context");
+       gssctxt = authctxt->methoddata;
+       
+       recv_tok.value=packet_get_string(&slen);
+       recv_tok.length=slen;   /* safe typecast */
+
+       status=ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+                                  &recv_tok, &send_tok, NULL);
+
+       packet_check_eom();
+       
+       if (GSS_ERROR(status)) {
+               if (send_tok.length>0) {
+                       packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+                       packet_put_string(send_tok.value,send_tok.length);
+                       packet_send();
+                       packet_write_wait();
+               }
+               /* Start again with the next method in the list */
+               clear_auth_state(authctxt);
+               userauth(authctxt,NULL);
+               return;
+       }
+       
+       if (send_tok.length>0) {
+               packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+               packet_put_string(send_tok.value,send_tok.length);
+               packet_send();
+               packet_write_wait();
+       }
+       
+       if (status == GSS_S_COMPLETE) {
+               /* If that succeeded, send a exchange complete message */
+               packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE);
+               packet_send();
+               packet_write_wait();
+       }
+}
+
+void
+input_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
+{
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       gss_buffer_desc send_tok,recv_tok;
+       OM_uint32 status;
+       
+       if (authctxt == NULL)
+               fatal("input_gssapi_response: no authentication context");
+       gssctxt = authctxt->methoddata;
+       
+       recv_tok.value=packet_get_string(&recv_tok.length);
+
+       /* Stick it into GSSAPI and see what it says */
+       status=ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+                                  &recv_tok, &send_tok, NULL);
+
+       packet_check_eom();
+       
+       /* We can't send a packet to the server */
+
+       /* The draft says that we should wait for the server to fail 
+        * before starting the next authentication. So, we clear the
+        * state, but don't do anything else */
+       clear_auth_state(authctxt);
+       return;
+}
+
+void
+input_gssapi_error(int type, u_int32_t plen, void *ctxt)
+{
+       OM_uint32 maj,min;
+       char *msg;
+       char *lang;
+       
+       maj=packet_get_int();
+       min=packet_get_int();
+       msg=packet_get_string(NULL);
+       lang=packet_get_string(NULL);
+
+       packet_check_eom();
+       
+       fprintf(stderr, "Server GSSAPI Error:\n%s\n", msg);
+       xfree(msg);
+       xfree(lang);
+}
+
+int
+userauth_external(Authctxt *authctxt)
+{
+        static int attempt =0;
+        
+        if (attempt++ >= 1)
+               return 0;
+                                
+        debug2("userauth_external");
+        packet_start(SSH2_MSG_USERAUTH_REQUEST);
+        packet_put_cstring(authctxt->server_user);
+        packet_put_cstring(authctxt->service);
+        packet_put_cstring(authctxt->method->name);
+        packet_send();
+        packet_write_wait();
+        return 1;
+}                                                                                                
+#endif /* GSSAPI */
+
 int
 userauth_none(Authctxt *authctxt)
 {
@@ -438,6 +760,7 @@ userauth_none(Authctxt *authctxt)
        packet_put_cstring(authctxt->method->name);
        packet_send();
        return 1;
+
 }
 
 int
@@ -544,7 +867,12 @@ clear_auth_state(Authctxt *authctxt)
 {
        /* XXX clear authentication state */
        dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, NULL);
-
+#ifdef GSSAPI
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR,NULL);
+#endif
+       
        if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) {
                debug3("clear_auth_state: key_free %p", authctxt->last_key);
                key_free(authctxt->last_key);
index 0f2b2a3ce597496363d99d3b2d873be10981bced..2e0fee561ac2cea2c202f9e192f3a2ca91b0d607 100644 (file)
@@ -85,6 +85,10 @@ RCSID("$OpenBSD: sshd.c,v 1.263 2003/02/16 17:09:57 markus Exp $");
 #include "monitor_wrap.h"
 #include "monitor_fdpass.h"
 
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
 #ifdef LIBWRAP
 #include <tcpd.h>
 #include <syslog.h>
@@ -1013,10 +1017,13 @@ main(int ac, char **av)
                log("Disabling protocol version 1. Could not load host key");
                options.protocol &= ~SSH_PROTO_1;
        }
+#ifndef GSSAPI
+       /* The GSSAPI key exchange can run without a host key */
        if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
                log("Disabling protocol version 2. Could not load host key");
                options.protocol &= ~SSH_PROTO_2;
        }
+#endif
        if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
                log("sshd: no hostkeys available -- exiting.");
                exit(1);
@@ -1816,10 +1823,52 @@ do_ssh2_kex(void)
        }
        myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types();
 
+#ifdef GSSAPI
+       { 
+       char *orig;
+       char *gss = NULL;
+       char *newstr = NULL;
+               orig = myproposal[PROPOSAL_KEX_ALGS];
+
+       /* If we don't have a host key, then all of the algorithms
+        * currently in myproposal are useless */
+       if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])==0)
+               orig= NULL;
+               
+        if (options.gss_keyex)
+               gss = ssh_gssapi_server_mechanisms();
+        else
+               gss = NULL;
+        
+       if (gss && orig) {
+               int len = strlen(orig) + strlen(gss) +2;
+               newstr=xmalloc(len);
+               snprintf(newstr,len,"%s,%s",gss,orig);
+       } else if (gss) {
+               newstr=gss;
+       } else if (orig) {
+               newstr=orig;
+       }
+        /* If we've got GSSAPI mechanisms, then we've also got the 'null'
+          host key algorithm, but we're not allowed to advertise it, unless
+          its the only host key algorithm we're supporting */
+       if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) {
+               myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]="null";
+       }
+       if (newstr)
+               myproposal[PROPOSAL_KEX_ALGS]=newstr;
+       else
+               fatal("No supported key exchange algorithms");
+        }
+#endif
+
        /* start key exchange */
        kex = kex_setup(myproposal);
        kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
        kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
+#ifdef GSSAPI
+        kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
+#endif         
        kex->server = 1;
        kex->client_version_string=client_version_string;
        kex->server_version_string=server_version_string;
index 6f38a260aa4e075b5d74bb9de4366f86b2a068b0..93124e53496fda3860e25779957286514636f2bd 100644 (file)
@@ -253,6 +253,26 @@ keys are used for version 1 and
 or
 .Dq rsa
 are used for version 2 of the SSH protocol.
+.It Cm GssapiAuthentication
+Specifies whether authentication based on GSSAPI may be used, either using
+the result of a successful key exchange, or using GSSAPI user
+authentication.
+The default is 
+.Dq yes .
+Note that this option applies to protocol version 2 only.
+.It Cm GssapiKeyExchange
+Specifies whether key exchange based on GSSAPI may be used. When using
+GSSAPI key exchange the server need not have a host key.
+The default is
+.Dq yes .
+Note that this option applies to protocol version 2 only.
+.It Cm GssapiUseSessionCredCache
+Specifies whether a unique credentials cache name should be generated per
+session for storing delegated credentials.
+The default is
+.Dq yes .
+Note that this option applies to protocol version 2 only.
+
 .It Cm IgnoreRhosts
 Specifies that
 .Pa .rhosts
This page took 0.23595 seconds and 5 git commands to generate.