![]()
|
diff -u -udbNpr Makefile.in Makefile.in
--- Makefile.in 2004-02-17 19:35:11.000000000 -0800
+++ Makefile.in 2004-10-26 14:45:50.000000000 -0700
@@ -70,7 +70,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o b
atomicio.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 dns.o \
- entropy.o scard-opensc.o gss-genr.o
+ entropy.o scard-opensc.o gss-genr.o kexgssc.o
SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
sshconnect.o sshconnect1.o sshconnect2.o
@@ -85,6 +85,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw
kexdhs.o kexgexs.o \
auth-krb5.o \
auth2-gss.o gss-serv.o gss-serv-krb5.o \
+ kexgsss.o \
loginrec.o auth-pam.o auth-shadow.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
diff -u -udbNpr auth-krb5.c auth-krb5.c
--- auth-krb5.c 2004-04-16 05:47:55.000000000 -0700
+++ auth-krb5.c 2004-10-26 14:45:50.000000000 -0700
@@ -42,6 +42,10 @@ RCSID("$OpenBSD: auth-krb5.c,v 1.15 2003
#ifdef KRB5
#include <krb5.h>
+#ifndef HEIMDAL
+#include <com_err.h>
+#endif
+
extern ServerOptions options;
static int
@@ -68,7 +72,6 @@ auth_krb5_password(Authctxt *authctxt, c
krb5_creds creds;
krb5_principal server;
char ccname[40];
- int tmpfd;
#endif
krb5_error_code problem;
krb5_ccache ccache = NULL;
@@ -145,8 +148,14 @@ auth_krb5_password(Authctxt *authctxt, c
goto out;
}
+#ifdef USE_CCAPI
+ snprintf(ccname,sizeof(ccname),"API:krb5cc_%d",geteuid());
+#else
snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
+ {
+ int tmpfd;
+
if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
logit("mkstemp(): %.100s", strerror(errno));
problem = errno;
@@ -160,6 +169,8 @@ auth_krb5_password(Authctxt *authctxt, c
goto out;
}
close(tmpfd);
+ }
+#endif
problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache);
if (problem)
@@ -180,8 +191,11 @@ auth_krb5_password(Authctxt *authctxt, c
len = strlen(authctxt->krb5_ticket_file) + 6;
authctxt->krb5_ccname = xmalloc(len);
- snprintf(authctxt->krb5_ccname, len, "FILE:%s",
- authctxt->krb5_ticket_file);
+#ifdef USE_CCAPI
+ snprintf(authctxt->krb5_ccname, len, "API:%s", authctxt->krb5_ticket_file);
+#else
+ snprintf(authctxt->krb5_ccname, len, "FILE:%s", authctxt->krb5_ticket_file);
+#endif
out:
restore_uid();
diff -u -udbNpr auth.c auth.c
--- auth.c 2004-10-26 14:40:11.000000000 -0700
+++ auth.c 2004-10-26 14:45:50.000000000 -0700
@@ -256,7 +256,7 @@ auth_log(Authctxt *authctxt, int authent
authmsg,
method,
authctxt->valid ? "" : "illegal user ",
- authctxt->user,
+ (authctxt->user[0]) ? authctxt->user : "<implicit>",
get_remote_ipaddr(),
get_remote_port(),
info);
diff -u -udbNpr auth.h auth.h
--- auth.h 2004-04-16 05:47:55.000000000 -0700
+++ auth.h 2004-10-26 14:45:50.000000000 -0700
@@ -52,6 +52,8 @@ struct Authctxt {
int valid; /* user exists and is allowed to login */
int attempt;
int failures;
+ int server_caused_failure;
+ Authmethod *method;
int force_pwchange;
char *user; /* username sent by the client */
char *service;
diff -u -udbNpr auth2-gss.c auth2-gss.c
--- auth2-gss.c 2003-11-21 04:56:47.000000000 -0800
+++ auth2-gss.c 2004-10-26 14:45:50.000000000 -0700
@@ -63,6 +63,7 @@ userauth_gssapi(Authctxt *authctxt)
u_int len;
char *doid = NULL;
+
if (!authctxt->valid || authctxt->user == NULL)
return (0);
@@ -98,11 +99,13 @@ userauth_gssapi(Authctxt *authctxt)
if (!present) {
xfree(doid);
+ authctxt->server_caused_failure = 1;
return (0);
}
if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &oid)))) {
xfree(doid);
+ authctxt->server_caused_failure = 1;
return (0);
}
@@ -132,6 +135,8 @@ input_gssapi_token(int type, u_int32_t p
gss_buffer_desc recv_tok;
OM_uint32 maj_status, min_status, flags;
u_int len;
+ int old_gssapi_mechanism = !strcmp(authctxt->method->name, "gssapi");
+
if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
fatal("No authentication or GSSAPI context");
@@ -154,8 +159,9 @@ input_gssapi_token(int type, u_int32_t p
packet_send();
}
authctxt->postponed = 0;
+ authctxt->server_caused_failure = 0;
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
- userauth_finish(authctxt, 0, "gssapi-with-mic");
+ userauth_finish(authctxt, 0, authctxt->method->name);
} else {
if (send_tok.length != 0) {
packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
@@ -164,7 +170,7 @@ input_gssapi_token(int type, u_int32_t p
}
if (maj_status == GSS_S_COMPLETE) {
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
- if (flags & GSS_C_INTEG_FLAG)
+ if (!old_gssapi_mechanism &&flags & GSS_C_INTEG_FLAG)
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC,
&input_gssapi_mic);
else
@@ -239,11 +245,12 @@ input_gssapi_exchange_complete(int type,
authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
authctxt->postponed = 0;
+ authctxt->server_caused_failure = 0;
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
- userauth_finish(authctxt, authenticated, "gssapi-with-mic");
+ userauth_finish(authctxt, authenticated, authctxt->method->name);
}
static void
@@ -292,4 +299,10 @@ Authmethod method_gssapi = {
&options.gss_authentication
};
+Authmethod method_gssapi_nomic = {
+ "gssapi",
+ userauth_gssapi,
+ &options.gss_nomic_authentication
+};
+
#endif /* GSSAPI */
diff -u -udbNpr auth2.c auth2.c
--- auth2.c 2004-10-26 14:40:11.000000000 -0700
+++ auth2.c 2004-10-26 14:48:35.000000000 -0700
@@ -54,14 +54,16 @@ extern Authmethod method_kbdint;
extern Authmethod method_hostbased;
#ifdef GSSAPI
extern Authmethod method_gssapi;
+extern Authmethod method_gssapi_nomic;
#endif
Authmethod *authmethods[] = {
&method_none,
- &method_pubkey,
#ifdef GSSAPI
&method_gssapi,
#endif
+ &method_pubkey,
+ &method_gssapi_nomic,
&method_passwd,
&method_kbdint,
&method_hostbased,
@@ -190,11 +192,13 @@ input_userauth_request(int type, u_int32
#endif
authctxt->postponed = 0;
+ authctxt->server_caused_failure = 0;
/* try to authenticate user */
m = authmethod_lookup(method);
if (m != NULL) {
debug2("input_userauth_request: try method %s", method);
+ authctxt->method = m;
authenticated = m->userauth(authctxt);
}
userauth_finish(authctxt, authenticated, method);
@@ -250,12 +254,15 @@ userauth_finish(Authctxt *authctxt, int
/* now we can break out */
authctxt->success = 1;
} else {
+ /* Do not count server configuration problems against the client */
+ if (!authctxt->server_caused_failure) {
if (authctxt->failures++ > AUTH_FAIL_MAX) {
#if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM)
PRIVSEP(solaris_audit_maxtrys());
#endif /* BSM */
packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
}
+ }
#if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM)
PRIVSEP(solaris_audit_bad_pw("authorization"));
#endif /* BSM */
diff -u -udbNpr auth2.c.rej auth2.c.rej
--- auth2.c.rej 1969-12-31 16:00:00.000000000 -0800
+++ auth2.c.rej 2004-10-26 14:45:50.000000000 -0700
@@ -0,0 +1,22 @@
+***************
+*** 246,253 ****
+ /* now we can break out */
+ authctxt->success = 1;
+ } else {
+- if (authctxt->failures++ > AUTH_FAIL_MAX)
+- packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
+ methods = authmethods_get();
+ packet_start(SSH2_MSG_USERAUTH_FAILURE);
+ packet_put_cstring(methods);
+--- 250,260 ----
+ /* now we can break out */
+ authctxt->success = 1;
+ } else {
++ /* Do not count server configuration problems against the client */
++ if (!authctxt->server_caused_failure) {
++ if (authctxt->failures++ > AUTH_FAIL_MAX)
++ packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
++ }
+ methods = authmethods_get();
+ packet_start(SSH2_MSG_USERAUTH_FAILURE);
+ packet_put_cstring(methods);
diff -u -udbNpr compat.c compat.c
--- compat.c 2003-11-03 01:09:03.000000000 -0800
+++ compat.c 2004-10-26 14:45:50.000000000 -0700
@@ -79,7 +79,11 @@ compat_datafellows(const char *version)
{ "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
{ "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 },
diff -u -udbNpr compat.h compat.h
--- compat.h 2003-11-03 01:09:03.000000000 -0800
+++ compat.h 2004-10-26 14:45:50.000000000 -0700
@@ -55,6 +55,7 @@
#define SSH_BUG_EXTEOF 0x00200000
#define SSH_BUG_PROBE 0x00400000
#define SSH_BUG_FIRSTKEX 0x00800000
+#define SSH_BUG_GSSAPI_BER 0x01000000
void enable_compat13(void);
void enable_compat20(void);
diff -u -udbNpr config.h.in config.h.in
--- config.h.in 2004-10-26 14:44:19.000000000 -0700
+++ config.h.in 2004-10-26 14:45:50.000000000 -0700
@@ -353,6 +353,12 @@
/* getaddrinfo is broken (if present) */
#undef BROKEN_GETADDRINFO
+/* platform uses an in-memory credentials cache */
+#undef USE_CCAPI
+
+/* platform has a Security Authorization Session API */
+#undef USE_SECURITY_SESSION_API
+
/* updwtmpx is broken (if present) */
#undef BROKEN_UPDWTMPX
diff -u -udbNpr configure.ac configure.ac
--- configure.ac 2004-10-26 14:40:11.000000000 -0700
+++ configure.ac 2004-10-26 14:45:50.000000000 -0700
@@ -168,6 +168,28 @@ main() { if (NSVersionOfRunTimeLibrary("
AC_DEFINE(BROKEN_SETREUID)
AC_DEFINE(BROKEN_SETREGID)
AC_DEFINE_UNQUOTED(BIND_8_COMPAT, 1)
+ AC_MSG_CHECKING(if we have the Security Authorization Session API)
+ AC_TRY_COMPILE([#include <Security/AuthSession.h>],
+ [SessionCreate(0, 0);],
+ [ac_cv_use_security_session_api="yes"
+ AC_DEFINE(USE_SECURITY_SESSION_API)
+ LIBS="$LIBS -framework Security"
+ AC_MSG_RESULT(yes)],
+ [ac_cv_use_security_session_api="no"
+ AC_MSG_RESULT(no)])
+ AC_MSG_CHECKING(if we have an in-memory credentials cache)
+ AC_TRY_COMPILE(
+ [#include <Kerberos/Kerberos.h>],
+ [cc_context_t c;
+ (void) cc_initialize (&c, 0, NULL, NULL);],
+ [AC_DEFINE(USE_CCAPI)
+ LIBS="$LIBS -framework Security"
+ AC_MSG_RESULT(yes)
+ if test "x$ac_cv_use_security_session_api" = "xno"; then
+ AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***)
+ fi],
+ [AC_MSG_RESULT(no)]
+ )
;;
*-*-hpux10.26)
if test -z "$GCC"; then
diff -u -udbNpr gss-genr.c gss-genr.c
--- gss-genr.c 2003-11-21 04:56:47.000000000 -0800
+++ gss-genr.c 2004-10-26 14:45:50.000000000 -0700
@@ -28,15 +28,179 @@
#ifdef GSSAPI
+#include "ssh.h"
#include "xmalloc.h"
#include "bufaux.h"
+#include "buffer.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 "ssh2.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(const 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;
+
+
+ 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;
+}
+
extern u_char *session_id2;
extern u_int session_id2_len;
@@ -269,6 +433,22 @@ ssh_gssapi_buildmic(Buffer *b, const cha
buffer_put_cstring(b, context);
}
+
+int
+ssh_gssapi_check_mechanism(gss_OID oid, const 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, (char *) 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));
+}
OM_uint32
ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) {
if (*ctx)
diff -u -udbNpr gss-serv-krb5.c gss-serv-krb5.c
--- gss-serv-krb5.c 2004-04-06 21:16:11.000000000 -0700
+++ gss-serv-krb5.c 2004-10-26 14:45:50.000000000 -0700
@@ -41,9 +41,10 @@ extern ServerOptions options;
#ifdef HEIMDAL
# include <krb5.h>
#else
-# ifdef HAVE_GSSAPI_KRB5
+# include <com_err.h>
+# ifdef HAVE_GSSAPI_KRB5_h
# include <gssapi_krb5.h>
-# elif HAVE_GSSAPI_GSSAPI_KRB5
+# elif HAVE_GSSAPI_GSSAPI_KRB5_H
# include <gssapi/gssapi_krb5.h>
# endif
#endif
@@ -132,9 +133,11 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
}
#else
{
- int tmpfd;
char ccname[40];
+#ifndef USE_CCAPI
+ int tmpfd;
+
snprintf(ccname, sizeof(ccname),
"FILE:/tmp/krb5cc_%d_XXXXXX", geteuid());
@@ -150,6 +153,10 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
return;
}
close(tmpfd);
+#else
+ snprintf(ccname, sizeof(ccname), "API:krb5cc_%d", geteuid());
+ debug ("Using ccache '%s'", ccname);
+#endif
if ((problem = krb5_cc_resolve(krb_context, ccname, &ccache))) {
logit("krb5_cc_resolve(): %.100s",
krb5_get_err_text(krb_context, problem));
@@ -183,11 +190,20 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
return;
}
- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache));
+ {
+ const char *new_ccname = krb5_cc_get_name(krb_context, ccache);
+
client->store.envvar = "KRB5CCNAME";
- len = strlen(client->store.filename) + 6;
+ len = strlen(new_ccname) + 6;
client->store.envval = xmalloc(len);
- snprintf(client->store.envval, len, "FILE:%s", client->store.filename);
+#ifdef USE_CCAPI
+ snprintf(client->store.envval, len, "API:%s", new_ccname);
+ client->store.filename = NULL; /* don't unlink -- not a file */
+#else
+ snprintf(client->store.envval, len, "FILE:%s", new_ccname);
+ client->store.filename = xstrdup(new_ccname);
+#endif
+ }
#ifdef USE_PAM
if (options.use_pam)
diff -u -udbNpr gss-serv.c gss-serv.c
--- gss-serv.c 2003-11-17 03:18:22.000000000 -0800
+++ gss-serv.c 2004-10-26 14:45:50.000000000 -0700
@@ -42,6 +42,8 @@
#include "ssh-gss.h"
extern ServerOptions options;
+extern u_char *session_id2;
+extern int session_id2_len;
static ssh_gssapi_client gssapi_client =
{ GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
@@ -61,6 +63,84 @@ ssh_gssapi_mech* supported_mechs[]= {
&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;
+
+
+ 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)
@@ -84,6 +164,32 @@ ssh_gssapi_supported_oids(gss_OID_set *o
}
}
+/* 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:
@@ -208,7 +314,6 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g
if (client->mech == NULL)
return GSS_S_FAILURE;
-
if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
&client->displayname, NULL))) {
ssh_gssapi_error(ctx);
diff -u -udbNpr kex.c kex.c
--- kex.c 2003-11-21 04:48:55.000000000 -0800
+++ kex.c 2004-10-26 14:45:50.000000000 -0700
@@ -42,6 +42,10 @@ RCSID("$OpenBSD: kex.c,v 1.56 2003/11/21
#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 *s
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);
}
diff -u -udbNpr kex.h kex.h
--- kex.h 2003-02-23 17:03:03.000000000 -0800
+++ kex.h 2004-10-26 14:45:50.000000000 -0700
@@ -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 -u -udbNpr kexgssc.c kexgssc.c
--- kexgssc.c 1969-12-31 16:00:00.000000000 -0800
+++ kexgssc.c 2004-10-26 14:45:50.000000000 -0700
@@ -0,0 +1,255 @@
+/*
+ * 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 = NULL;
+ 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. */
+
+ read_loop: type = packet_read();
+ switch (type) {
+ case SSH2_MSG_KEXGSS_HOSTKEY:
+ debug("Received KEXGSS_HOSTKEY");
+ if (serverhostkey)
+ fatal("Server host key received more than once");
+ serverhostkey=packet_get_string(&slen);
+ goto read_loop;
+ 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);
+ debug("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 -u -udbNpr kexgsss.c kexgsss.c
--- kexgsss.c 1969-12-31 16:00:00.000000000 -0800
+++ kexgsss.c 2004-10-26 14:45:50.000000000 -0700
@@ -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 */
diff -u -udbNpr key.c key.c
--- key.c 2003-11-17 02:18:23.000000000 -0800
+++ key.c 2004-10-26 14:45:50.000000000 -0700
@@ -650,6 +650,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;
diff -u -udbNpr key.h key.h
--- key.h 2003-11-17 02:18:23.000000000 -0800
+++ key.h 2004-10-26 14:45:50.000000000 -0700
@@ -34,6 +34,7 @@ enum types {
KEY_RSA1,
KEY_RSA,
KEY_DSA,
+ KEY_NULL,
KEY_UNSPEC
};
enum fp_type {
diff -u -udbNpr monitor.c monitor.c
--- monitor.c 2004-10-26 14:40:11.000000000 -0700
+++ monitor.c 2004-10-26 14:45:50.000000000 -0700
@@ -141,6 +141,9 @@ 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_checkmic(int, Buffer *);
+int mm_answer_gss_sign(int, Buffer *);
+int mm_answer_gss_error(int, Buffer *);
+int mm_answer_gss_indicate_mechs(int, Buffer *);
#endif
#if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM)
@@ -205,6 +208,9 @@ struct mon_table mon_dispatch_proto20[]
#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},
{MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
#endif
@@ -217,6 +223,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},
@@ -313,6 +326,12 @@ monitor_child_preauth(Authctxt *_authctx
/* 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;
@@ -386,6 +405,12 @@ monitor_child_postauth(struct monitor *p
monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
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);
@@ -1608,6 +1633,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);
@@ -1799,6 +1827,84 @@ monitor_reinit(struct monitor *mon)
}
#ifdef GSSAPI
+
+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);
+}
+
+#endif /* GSSAPI */
+
+#ifdef GSSAPI
int
mm_answer_gss_setup_ctx(int socket, Buffer *m)
{
@@ -1850,6 +1956,7 @@ mm_answer_gss_accept_ctx(int socket, Buf
monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
}
return (0);
}
diff -u -udbNpr monitor.h monitor.h
--- monitor.h 2004-10-26 14:40:11.000000000 -0700
+++ monitor.h 2004-10-26 14:45:50.000000000 -0700
@@ -56,6 +56,9 @@ enum monitor_reqtype {
MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP,
MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK,
MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC,
+ MONITOR_REQ_GSSSIGN,MONITOR_ANS_GSSSIGN,
+ MONITOR_REQ_GSSERR,MONITOR_ANS_GSSERR,
+ MONITOR_REQ_GSSMECHS,MONITOR_ANS_GSSMECHS,
MONITOR_REQ_PAM_START,
MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT,
MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX,
diff -u -udbNpr monitor_wrap.c monitor_wrap.c
--- monitor_wrap.c 2004-10-26 14:40:11.000000000 -0700
+++ monitor_wrap.c 2004-10-26 14:45:50.000000000 -0700
@@ -1175,6 +1175,79 @@ mm_ssh_gssapi_userok(char *user)
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);
+}
+
#endif /* GSSAPI */
#if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM)
diff -u -udbNpr monitor_wrap.h monitor_wrap.h
--- monitor_wrap.h 2004-10-26 14:40:11.000000000 -0700
+++ monitor_wrap.h 2004-10-26 14:45:50.000000000 -0700
@@ -63,6 +63,10 @@ OM_uint32 mm_ssh_gssapi_accept_ctx(Gssct
gss_buffer_desc *recv, gss_buffer_desc *send, OM_uint32 *flags);
int mm_ssh_gssapi_userok(char *user);
OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *ctxt, gss_buffer_desc *buffer,
+ gss_buffer_desc *hash);
+char *mm_ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min);
+
#endif
#ifdef USE_PAM
@@ -74,6 +78,7 @@ int mm_sshpam_respond(void *, u_int, cha
void mm_sshpam_free_ctx(void *);
#endif
+
struct Session;
void mm_terminate(void);
int mm_pty_allocate(int *, int *, char *, int);
diff -u -udbNpr readconf.c readconf.c
--- readconf.c 2004-03-08 04:12:36.000000000 -0800
+++ readconf.c 2004-10-26 14:45:50.000000000 -0700
@@ -910,9 +910,9 @@ fill_default_options(Options * options)
if (options->challenge_response_authentication == -1)
options->challenge_response_authentication = 1;
if (options->gss_authentication == -1)
- options->gss_authentication = 0;
+ options->gss_authentication = 1;
if (options->gss_deleg_creds == -1)
- options->gss_deleg_creds = 0;
+ options->gss_deleg_creds = 1;
if (options->password_authentication == -1)
options->password_authentication = 1;
if (options->kbd_interactive_authentication == -1)
diff -u -udbNpr servconf.c servconf.c
--- servconf.c 2004-01-23 03:03:10.000000000 -0800
+++ servconf.c 2004-10-26 14:45:50.000000000 -0700
@@ -74,6 +74,9 @@ initialize_server_options(ServerOptions
options->kerberos_ticket_cleanup = -1;
options->kerberos_get_afs_token = -1;
options->gss_authentication=-1;
+ options->gss_nomic_authentication = -1;
+ options->gss_keyex = -1;
+ options->gss_use_session_ccache = -1;
options->gss_cleanup_creds = -1;
options->password_authentication = -1;
options->kbd_interactive_authentication = -1;
@@ -111,7 +114,7 @@ fill_default_server_options(ServerOption
{
/* Portable-specific options */
if (options->use_pam == -1)
- options->use_pam = 0;
+ options->use_pam = 1;
/* Standard Options */
if (options->protocol == SSH_PROTO_UNKNOWN)
@@ -185,7 +188,13 @@ fill_default_server_options(ServerOption
if (options->kerberos_get_afs_token == -1)
options->kerberos_get_afs_token = 0;
if (options->gss_authentication == -1)
- options->gss_authentication = 0;
+ options->gss_authentication = 1;
+ if (options->gss_nomic_authentication == -1)
+ options->gss_nomic_authentication = options->gss_authentication;
+ 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;
if (options->password_authentication == -1)
@@ -266,7 +275,8 @@ typedef enum {
sBanner, sUseDNS, sHostbasedAuthentication,
sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
- sGssAuthentication, sGssCleanupCreds,
+ sGssAuthentication, sGssKeyEx, sGssNoMICAuthentication,
+ sGssUseSessionCredCache, sGssCleanupCreds,
sUsePrivilegeSeparation,
sDeprecated, sUnsupported
} ServerOpCodes;
@@ -320,9 +330,14 @@ static struct {
{ "afstokenpassing", sUnsupported },
#ifdef GSSAPI
{ "gssapiauthentication", sGssAuthentication },
+ {"gssapinomicauthentication", sGssNoMICAuthentication},
+ { "gssapikeyexchange", sGssKeyEx },
+ { "gssusesessionccache", sGssUseSessionCredCache },
+ { "gssapiusesessioncredcache", sGssUseSessionCredCache },
{ "gssapicleanupcredentials", sGssCleanupCreds },
#else
{ "gssapiauthentication", sUnsupported },
+ {"gssapinomicauthentication", sUnsupported },
{ "gssapicleanupcredentials", sUnsupported },
#endif
{ "passwordauthentication", sPasswordAuthentication },
@@ -648,6 +663,18 @@ parse_flag:
intptr = &options->gss_authentication;
goto parse_flag;
+ case sGssNoMICAuthentication:
+ intptr = &options->gss_nomic_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;
diff -u -udbNpr servconf.h servconf.h
--- servconf.h 2003-12-30 16:37:34.000000000 -0800
+++ servconf.h 2004-10-26 14:45:50.000000000 -0700
@@ -83,6 +83,10 @@ typedef struct {
int kerberos_get_afs_token; /* If true, try to get AFS token if
* authenticated with Kerberos. */
int gss_authentication; /* If true, permit GSSAPI authentication */
+ int gss_nomic_authentication; /* Add option for old gssapi mechanism*/
+ 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 */
int password_authentication; /* If true, permit password
* authentication. */
diff -u -udbNpr session.c session.c
--- session.c 2004-10-26 14:40:11.000000000 -0700
+++ session.c 2004-10-26 14:45:50.000000000 -0700
@@ -398,6 +398,12 @@ do_exec_no_pty(Session *s, const char *c
session_proctitle(s);
+#if defined(GSSAPI)
+ temporarily_use_uid(s->pw);
+ ssh_gssapi_storecreds();
+ restore_uid();
+#endif
+
#if defined(USE_PAM)
if (options.use_pam && !use_privsep)
do_pam_setcred(1);
@@ -529,6 +535,12 @@ do_exec_pty(Session *s, const char *comm
ptyfd = s->ptyfd;
ttyfd = s->ttyfd;
+#if defined(GSSAPI)
+ temporarily_use_uid(s->pw);
+ ssh_gssapi_storecreds();
+ restore_uid();
+#endif
+
#if defined(USE_PAM)
if (options.use_pam) {
do_pam_set_tty(s->tty);
@@ -2221,6 +2233,9 @@ static void
do_authenticated2(Authctxt *authctxt)
{
server_loop2(authctxt);
+#if defined(GSSAPI)
+ ssh_gssapi_cleanup_creds();
+#endif
}
void
diff -u -udbNpr ssh-gss.h ssh-gss.h
--- ssh-gss.h 2004-02-23 15:37:33.000000000 -0800
+++ ssh-gss.h 2004-10-26 14:45:50.000000000 -0700
@@ -28,6 +28,7 @@
#ifdef GSSAPI
+#include "kex.h"
#include "buffer.h"
#ifdef HAVE_GSSAPI_H
@@ -53,6 +54,11 @@
#endif /* KRB5 */
/* draft-ietf-secsh-gsskeyex-06 */
+#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
@@ -60,6 +66,7 @@
#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65
#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66
+#define KEX_GSS_SHA1 "gss-group1-sha1-"
#define SSH_GSS_OIDTYPE 0x06
typedef struct {
@@ -100,6 +107,9 @@ typedef struct {
extern ssh_gssapi_mech *supported_mechs[];
+char *ssh_gssapi_mechanisms(char *host);
+char *ssh_gssapi_client_mechanisms(const 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);
@@ -117,16 +127,22 @@ 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);
+int ssh_gssapi_check_mechanism(gss_OID oid, const char *host);
OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
OM_uint32 ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid);
void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *);
/* In the server */
+gss_OID ssh_gssapi_server_id_kex(char *name);
int ssh_gssapi_userok(char *name);
+void ssh_gssapi_server(Kex *kex, Buffer *client_kexinit,
+ Buffer *server_kexinit);
+
OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
void ssh_gssapi_do_child(char ***envp, u_int *envsizep);
void ssh_gssapi_cleanup_creds(void);
void ssh_gssapi_storecreds(void);
+char *ssh_gssapi_server_mechanisms(void);
#endif /* GSSAPI */
diff -u -udbNpr sshconnect2.c sshconnect2.c
--- sshconnect2.c 2004-03-08 04:12:36.000000000 -0800
+++ sshconnect2.c 2004-10-26 14:45:50.000000000 -0700
@@ -83,10 +83,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) {
logit("No valid ciphers for protocol version 2 given, using defaults.");
options.ciphers = NULL;
@@ -114,6 +130,16 @@ ssh_kex2(char *host, struct sockaddr *ho
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
if (options.rekey_limit)
packet_set_rekey_limit(options.rekey_limit);
@@ -121,10 +147,15 @@ ssh_kex2(char *host, struct sockaddr *ho
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);
@@ -226,6 +257,10 @@ Authmethod authmethods[] = {
userauth_gssapi,
&options.gss_authentication,
NULL},
+ {"gssapi",
+ userauth_gssapi,
+ &options.gss_authentication,
+ NULL},
#endif
{"hostbased",
userauth_hostbased,
@@ -484,6 +519,7 @@ userauth_gssapi(Authctxt *authctxt)
static int mech = 0;
OM_uint32 min;
int ok = 0;
+ int old_gssapi_method;
/* Try one GSSAPI method at a time, rather than sending them all at
* once. */
@@ -517,13 +553,24 @@ userauth_gssapi(Authctxt *authctxt)
packet_put_cstring(authctxt->service);
packet_put_cstring(authctxt->method->name);
- packet_put_int(1);
+ old_gssapi_method = !strcmp( authctxt->method->name, "gssapi");
+
+ /* Version of Debian ssh-krb5 prior to 3.8.1p1-1 don't expect
+ * tagged OIDs. As such we include both tagged and untagged oids for the old gssapi method.
+ * We only include tagged oids for the new gssapi-with-mic method.
+ */
+ packet_put_int(old_gssapi_method?2:1);
packet_put_int((gss_supported->elements[mech].length) + 2);
packet_put_char(SSH_GSS_OIDTYPE);
packet_put_char(gss_supported->elements[mech].length);
packet_put_raw(gss_supported->elements[mech].elements,
gss_supported->elements[mech].length);
+ if (old_gssapi_method) {
+ packet_put_int((gss_supported->elements[mech].length) );
+ packet_put_raw(gss_supported->elements[mech].elements,
+ gss_supported->elements[mech].length);
+ }
packet_send();
@@ -563,7 +610,9 @@ process_gssapi_token(void *ctxt, gss_buf
if (status == GSS_S_COMPLETE) {
/* send either complete or MIC, depending on mechanism */
- if (!(flags & GSS_C_INTEG_FLAG)) {
+ int old_gssapi_method = !strcmp(authctxt->method->name, "gssapi");
+
+ if (old_gssapi_method || !(flags & GSS_C_INTEG_FLAG)) {
packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE);
packet_send();
} else {
@@ -596,30 +645,35 @@ input_gssapi_response(int type, u_int32_
Authctxt *authctxt = ctxt;
Gssctxt *gssctxt;
int oidlen;
- char *oidv;
+ char *oidv, *oidv_free;
if (authctxt == NULL)
fatal("input_gssapi_response: no authentication context");
gssctxt = authctxt->methoddata;
/* Setup our OID */
- oidv = packet_get_string(&oidlen);
+ oidv = oidv_free = packet_get_string(&oidlen);
if (oidlen <= 2 ||
oidv[0] != SSH_GSS_OIDTYPE ||
oidv[1] != oidlen - 2) {
- xfree(oidv);
debug("Badly encoded mechanism OID received");
+ if (!(oidlen >= 2)) {
+ xfree(oidv_free);
userauth(authctxt, NULL);
return;
}
+ } else {
+ oidlen -= 2;
+ oidv += 2;
+ }
- if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2))
+ if (!ssh_gssapi_check_oid(gssctxt, oidv , oidlen))
fatal("Server returned different OID than expected");
packet_check_eom();
- xfree(oidv);
+ xfree(oidv_free);
if (GSS_ERROR(process_gssapi_token(ctxt, GSS_C_NO_BUFFER))) {
/* Start again with next method on list */
diff -u -udbNpr sshd.c sshd.c
--- sshd.c 2004-10-26 14:40:11.000000000 -0700
+++ sshd.c 2004-10-26 14:45:50.000000000 -0700
@@ -85,6 +85,14 @@ RCSID("$OpenBSD: sshd.c,v 1.290 2004/03/
#include "monitor_wrap.h"
#include "monitor_fdpass.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+
+#ifdef USE_SECURITY_SESSION_API
+#include <Security/AuthSession.h>
+#endif
+
#ifdef LIBWRAP
#include <tcpd.h>
#include <syslog.h>
@@ -994,10 +1002,13 @@ main(int ac, char **av)
logit("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) {
logit("Disabling protocol version 2. Could not load host key");
options.protocol &= ~SSH_PROTO_2;
}
+#endif
if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
logit("sshd: no hostkeys available -- exiting.");
exit(1);
@@ -1447,6 +1458,62 @@ main(int ac, char **av)
/* Log the connection. */
verbose("Connection from %.500s port %d", remote_ip, remote_port);
+#ifdef USE_SECURITY_SESSION_API
+ /*
+ * Create a new security session for use by the new user login if
+ * the current session is the root session or we are not launched
+ * by inetd (eg: debugging mode or server mode). We do not
+ * necessarily need to create a session if we are launched from
+ * inetd because Panther xinetd will create a session for us.
+ *
+ * The only case where this logic will fail is if there is an
+ * inetd running in a non-root session which is not creating
+ * new sessions for us. Then all the users will end up in the
+ * same session (bad).
+ *
+ * When the client exits, the session will be destroyed for us
+ * automatically.
+ *
+ * We must create the session before any credentials are stored
+ * (including AFS pags, which happens a few lines below).
+ */
+ {
+ OSStatus err = 0;
+ SecuritySessionId sid = 0;
+ SessionAttributeBits sattrs = 0;
+
+ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
+ if (err) {
+ error("SessionGetInfo() failed with error %.8X",
+ (unsigned) err);
+ } else {
+ debug("Current Session ID is %.8X / Session Attributes are %.8X",
+ (unsigned) sid, (unsigned) sattrs);
+ }
+
+ if (inetd_flag && !(sattrs & sessionIsRoot)) {
+ debug("Running in inetd mode in a non-root session... "
+ "assuming inetd created the session for us.");
+ } else {
+ debug("Creating new security session...");
+ err = SessionCreate(0, sessionHasTTY | sessionIsRemote);
+ if (err) {
+ error("SessionCreate() failed with error %.8X",
+ (unsigned) err);
+ }
+
+ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs);
+ if (err) {
+ error("SessionGetInfo() failed with error %.8X",
+ (unsigned) err);
+ } else {
+ debug("New Session ID is %.8X / Session Attributes are %.8X",
+ (unsigned) sid, (unsigned) sattrs);
+ }
+ }
+ }
+#endif
+
/*
* We don\'t want to listen forever unless the other side
* successfully authenticates itself. So we set up an alarm which is
@@ -1779,10 +1846,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;
diff -u -udbNpr sshd_config.5 sshd_config.5
--- sshd_config.5 2004-04-13 20:04:36.000000000 -0700
+++ sshd_config.5 2004-10-26 14:45:50.000000000 -0700
@@ -265,6 +265,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
syntax highlighted by Code2HTML, v. 0.9.1 |