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 \
- kexgss.o kexgssc.o gss-genr.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
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 \
- kexgsss.o auth2-gss.o gss-serv.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
#endif /* HAVE_PAM_GETENVLIST */
}
+void free_pam_environment(char **env)
+{
+ int i;
+
+ if (env != NULL) {
+ for (i = 0; env[i] != NULL; i++)
+ xfree(env[i]);
+ }
+}
+
/* 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.
return(ret);
}
-void free_pam_environment(char **env)
-{
- int i;
-
- if (env != NULL) {
- for (i = 0; env[i] != NULL; i++)
- xfree(env[i]);
- }
-}
-
/* Print any messages that have been generated during authentication */
/* or account checking to stderr */
void print_pam_messages(void)
/* import */
extern ServerOptions options;
-extern Authmethod method_gssapi;
-
-
#ifdef GSSAPI
#ifdef GSI
#include "globus_gss_assist.h"
#endif
+extern Authmethod method_gssapi;
+
int userauth_gssapi(Authctxt *authctxt);
void
/*
- * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved.
+ * 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
#include "ssh-gss.h"
extern ServerOptions options;
-extern unsigned char ssh1_key_digest[16];
+unsigned char ssh1_key_digest[16];
static int
userauth_external(Authctxt *authctxt)
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);
int present;
OM_uint32 ms;
u_int len;
+ char * doid = NULL;
if (!authctxt->valid || authctxt->user == NULL)
return 0;
ssh_gssapi_supported_oids(&supported);
do {
- if (oid.elements)
- xfree(oid.elements);
- oid.elements = packet_get_string(&len);
- oid.length = len;
- gss_test_oid_set_member(&ms, &oid, supported, &present);
mechs--;
+
+ if (doid)
+ xfree(doid);
+
+ debug("Trying to get OID string");
+ doid = packet_get_string(&len);
+ debug("Got string");
+
+ if (datafellows & SSH_BUG_GSSAPI_BER) {
+ oid.elements = doid;
+ oid.length = len;
+ } else {
+ if (doid[0]!=0x06 || doid[1]!=len-2) {
+ debug("Badly encoded mechanism OID received");
+ oid.elements=NULL;
+ } else {
+ oid.elements = doid + 2;
+ oid.length = len - 2;
+ gss_test_oid_set_member(&ms, &oid, supported, &present);
+ }
+ }
+ if (oid.elements) {
+ gss_test_oid_set_member(&ms, &oid, supported, &present);
+ }
} while (mechs>0 && !present);
if (!present) {
- xfree(oid.elements);
+ xfree(doid);
return(0);
}
- if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,&oid))))
+ 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 */
- if (!compat20)
- packet_start(SSH_SMSG_AUTH_GSSAPI_RESPONSE);
- else
- packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
- packet_put_string(oid.elements,oid.length);
+ if (!compat20) {
+
+ packet_start(SSH_SMSG_AUTH_GSSAPI_RESPONSE);
+ packet_put_string(oid.elements,oid.length);
+
+ } else {
+
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
+
+ if (datafellows & SSH_BUG_GSSAPI_BER) {
+ packet_put_string(oid.elements,oid.length);
+ } else {
+ packet_put_string(doid,len);
+ }
+
+ } /* !compat20 */
+
packet_send();
packet_write_wait();
- xfree(oid.elements);
+ xfree(doid);
if (!compat20)
dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN,
&input_gssapi_token);
else
- dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
- &input_gssapi_token);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
+ &input_gssapi_token);
authctxt->postponed = 1;
return 0;
gssctxt=authctxt->methoddata;
recv_tok.value=packet_get_string(&len);
- recv_tok.length=len; /* int vs. size_t */
+ recv_tok.length=len; /* int vs. size_t */
maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
- &send_tok, NULL));
+ &send_tok, NULL));
packet_check_eom();
-
+
if (send_tok.length != 0) {
- /* Send a packet back to the client */
+ /* Send a packet back to the client, even if there has
+ * been an error, as this may contain mechanism specific
+ * error information */
if (!compat20)
packet_start(SSH_MSG_AUTH_GSSAPI_TOKEN);
else
packet_write_wait();
gss_release_buffer(&min_status, &send_tok);
}
-
+
if (GSS_ERROR(maj_status)) {
- /* Failure <sniff> */
- if (gssctxt) { /* may be NULL under privsep */
- ssh_gssapi_send_error(gssctxt->oid,maj_status,min_status);
- } else {
- ssh_gssapi_send_error(GSS_C_NO_OID,maj_status,min_status);
- }
+ ssh_gssapi_userauth_error(gssctxt);
authctxt->postponed = 0;
dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL);
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
userauth_finish(authctxt, 0, "gssapi");
}
-
+
if (maj_status == GSS_S_COMPLETE) {
dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL);
dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
}
gssctxt=authctxt->methoddata;
-
+
/* ssh1 needs to exchange the hash of the keys */
if (!compat20) {
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,
debug2("input_userauth_request: try method %s", method);
authenticated = m->userauth(authctxt);
}
-
userauth_finish(authctxt, authenticated, method);
xfree(service);
{ "OpenSSH_2.9p*", SSH_OLD_GSSAPI },
{ "OpenSSH_2.*,"
"OpenSSH_3.0*,"
- "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_BUG_GSS_EMPTYUSER},
+ "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_BUG_GSS_EMPTYUSER|
+ SSH_BUG_GSSAPI_BER},
+ { "OpenSSH_3.2*,"
+ "OpenSSH_3.3*,"
+ "OpenSSH_3.4*,"
+ "OpenSSH_3.5*,"
+ "OpenSSH_3.6.1*", SSH_BUG_GSSAPI_BER},
{ "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
{ "OpenSSH*", 0 },
{ "*MindTerm*", 0 },
#define SSH_BUG_K5USER 0x00400000
#define SSH_BUG_PROBE 0x00800000
#define SSH_BUG_FIRSTKEX 0x01000000
-#define SSH_OLD_GSSAPI 0x10000000
-#define SSH_BUG_GSS_EMPTYUSER 0x20000000
+#define SSH_OLD_GSSAPI 0x02000000
+#define SSH_BUG_GSSAPI_BER 0x04000000
+#define SSH_BUG_GSS_EMPTYUSER 0x10000000
void enable_compat13(void);
void enable_compat20(void);
/*
- * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved. *
+ * 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:
#include "ssh-gss.h"
-/* Assorted globals for tracking the clients identity once they've
- * authenticated */
-
-gss_buffer_desc gssapi_client_name = {0,NULL}; /* Name of our client */
-gss_cred_id_t gssapi_client_creds = GSS_C_NO_CREDENTIAL; /* Their credentials */
-enum ssh_gss_id gssapi_client_type = GSS_LAST_ENTRY;
-
-unsigned char ssh1_key_digest[16]; /* used for ssh1 gssapi */
-
-/* The mechanism name used in the list below is defined in the internet
- * draft as the Base 64 encoding of the MD5 hash of the ASN.1 DER encoding
- * of the underlying GSSAPI mechanism's OID.
- *
- * Also from the draft, before considering adding SPNEGO, bear in mind that
- * "mechanisms ... MUST NOT use SPNEGO as the underlying GSSAPI mechanism"
- */
-
-/* These must be in the same order as ssh_gss_id, in ssh-gss.h */
-
-ssh_gssapi_mech supported_mechs[]= {
-#ifdef KRB5
- /* Official OID - 1.2.850.113554.1.2.2 */
- {"Se3H81ismmOC3OE+FwYCiQ==","Kerberos",
- {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}},
-#endif
-#ifdef GSI
- /* gssapi_ssleay 1.3.6.1.4.1.3536.1.1 */
- {"N3+k7/4wGxHyuP8Yxi4RhA==",
- "GSI",
- {9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"}
- },
-#endif /* GSI */
- {NULL,NULL,{0,0}}
-};
-
-char gssprefix[]=KEX_GSS_SHA1;
+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.
*
- * 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)
+ * 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.
*
- * 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.
+ * 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_mechanisms(char *host) {
+ssh_gssapi_client_mechanisms(char *host) {
gss_OID_set supported;
- OM_uint32 maj_status, min_status;
+ OM_uint32 min_status;
Buffer buf;
int i = 0;
- int present;
- int mech_count=0;
- char * mechs;
- Gssctxt * ctx = NULL;
-
+ 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);
+ 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);
-
- do {
- if ((maj_status=gss_test_oid_set_member(&min_status,
- &supported_mechs[i].oid,
- supported,
- &present))) {
- present=0;
- }
- if (present) {
- if (!GSS_ERROR(ssh_gssapi_client_ctx(&ctx,
- &supported_mechs[i].oid,
- host))) {
- /* Append gss_group1_sha1_x to our list */
- if (++mech_count > 1) {
- buffer_append(&buf, ",", 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++;
}
- buffer_append(&buf, gssprefix,
- strlen(gssprefix));
- 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, gssprefix,
- 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);
+
+ /* 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++;
}
- } while (supported_mechs[++i].name != NULL);
+ }
+ gss_enc2oid[oidpos].oid=NULL;
+ gss_enc2oid[oidpos].encoded=NULL;
buffer_put_char(&buf,'\0');
buffer_get(&buf,mechs,buffer_len(&buf));
buffer_free(&buf);
if (strlen(mechs)==0)
- return(NULL);
+ return(NULL);
else
- return(mechs);
+ 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) {
ssh_gssapi_set_oid_data(ctx,oid->elements,oid->length);
}
-/* Find out which GSS type (out of the list we define in ssh-gss.h) a
- * particular connection is using
- */
-enum ssh_gss_id ssh_gssapi_get_ctype(Gssctxt *ctxt) {
- enum ssh_gss_id 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 i;
- i++;
- }
- return(GSS_LAST_ENTRY);
-}
-
-/* Set the GSS context's OID to the oid indicated by the given key exchange
- * name. */
-gss_OID ssh_gssapi_id_kex(Gssctxt *ctx, char *name) {
- enum ssh_gss_id i=0;
-
- if (strncmp(name, gssprefix, strlen(gssprefix)-1) !=0) {
- return(NULL);
- }
-
- name+=strlen(gssprefix); /* 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);
-
- if (ctx) ssh_gssapi_set_oid(ctx,&supported_mechs[i].oid);
-
- debug("using GSSAPI mechanism %s (%s%s)", supported_mechs[i].name,
- gssprefix, supported_mechs[i].enc_name);
+/* All this effort to report an error ... */
- return &supported_mechs[i].oid;
+void
+ssh_gssapi_error(Gssctxt *ctxt) {
+
+ debug(ssh_gssapi_last_error(ctxt,NULL,NULL));
}
-
-/* All this effort to report an error ... */
-void
-ssh_gssapi_error(gss_OID mech, OM_uint32 major_status,
- OM_uint32 minor_status) {
- OM_uint32 lmaj, lmin;
- gss_buffer_desc msg = {0,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 {
- lmaj = gss_display_status(&lmin, major_status,
- GSS_C_GSS_CODE,
- mech, &ctx, &msg);
- if (lmaj == GSS_S_COMPLETE) {
- debug((char *)msg.value);
- (void) gss_release_buffer(&lmin, &msg);
- }
- } while (ctx!=0);
+ gss_display_status(&lmin, ctxt->major,
+ GSS_C_GSS_CODE, ctxt->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 {
- lmaj = gss_display_status(&lmin, minor_status,
- GSS_C_MECH_CODE,
- mech, &ctx, &msg);
- if (lmaj == GSS_S_COMPLETE) {
- debug((char *)msg.value);
- (void) gss_release_buffer(&lmin, &msg);
- }
+ gss_display_status(&lmin, ctxt->minor,
+ GSS_C_MECH_CODE, ctxt->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
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;
ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
gss_buffer_desc* send_tok, OM_uint32 *flags)
{
- OM_uint32 maj_status, min_status;
int deleg_flag = 0;
if (deleg_creds) {
debug("Delegating credentials");
}
- maj_status=gss_init_sec_context(&min_status,
+ ctx->major=gss_init_sec_context(&ctx->minor,
GSS_C_NO_CREDENTIAL, /* def. cred */
&ctx->context,
ctx->name,
send_tok,
flags,
NULL);
- ctx->status=maj_status;
- if (GSS_ERROR(maj_status)) {
- ssh_gssapi_error(ctx->oid,maj_status,min_status);
+ if (GSS_ERROR(ctx->major)) {
+ ssh_gssapi_error(ctx);
}
- return(maj_status);
+ 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 = {0,NULL};
- OM_uint32 maj_status, min_status;
+ gss_buffer_desc gssbuf;
char *xhost;
/* Make a copy of the host name, in case it was returned by a
- * previous call to gethostbyname(). */
+ * previous call to gethostbyname(). */
xhost = xstrdup(host);
- /* Make sure we have the FQHN. Some GSSAPI implementations don't do
- * this for us themselves */
+ /* Make sure we have the FQDN. Some GSSAPI implementations don't do
+ * this for us themselves */
resolve_localhost(&xhost);
-
+
gssbuf.length = sizeof("host@")+strlen(xhost);
gssbuf.value = xmalloc(gssbuf.length);
return(-1);
}
snprintf(gssbuf.value,gssbuf.length,"host@%s",xhost);
- if ((maj_status=gss_import_name(&min_status,
+ if ((ctx->major=gss_import_name(&ctx->minor,
&gssbuf,
GSS_C_NT_HOSTBASED_SERVICE,
&ctx->name))) {
- ssh_gssapi_error(ctx->oid, maj_status,min_status);
+ ssh_gssapi_error(ctx);
}
xfree(xhost);
xfree(gssbuf.value);
- return(maj_status);
+ return(ctx->major);
}
/* Acquire credentials for a server running on the current host.
/* Returns a GSSAPI error code */
OM_uint32
ssh_gssapi_acquire_cred(Gssctxt *ctx) {
- OM_uint32 maj_status, min_status;
+ OM_uint32 status;
char lname[MAXHOSTNAMELEN];
gss_OID_set oidset;
- gss_create_empty_oid_set(&min_status,&oidset);
- gss_add_oid_set_member(&min_status,ctx->oid,&oidset);
+ gss_create_empty_oid_set(&status,&oidset);
+ gss_add_oid_set_member(&status,ctx->oid,&oidset);
if (gethostname(lname, MAXHOSTNAMELEN)) {
return(-1);
}
- if ((maj_status=ssh_gssapi_import_name(ctx,lname))) {
- return(maj_status);
+ if (GSS_ERROR(ssh_gssapi_import_name(ctx,lname))) {
+ return(ctx->major);
}
- if ((maj_status=gss_acquire_cred(&min_status,
- ctx->name,
+
+ if ((ctx->major=gss_acquire_cred(&ctx->minor,
+ ctx->name,
0,
oidset,
GSS_C_ACCEPT,
&ctx->creds,
NULL,
NULL))) {
- ssh_gssapi_error(ctx->oid,maj_status,min_status);
+ ssh_gssapi_error(ctx);
}
- gss_release_oid_set(&min_status, &oidset);
- return(maj_status);
-}
-
-/* Extract the client details from a given context. This can only reliably
- * be called once for a context */
-
-OM_uint32
-ssh_gssapi_getclient(Gssctxt *ctx, enum ssh_gss_id *type,
- gss_buffer_desc *name, gss_cred_id_t *creds) {
-
- OM_uint32 maj_status,min_status;
-
- *type=ssh_gssapi_get_ctype(ctx);
- if ((maj_status=gss_display_name(&min_status,ctx->client,name,NULL))) {
- ssh_gssapi_error(ctx->oid,maj_status,min_status);
- }
-
- /* 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(maj_status);
+ gss_release_oid_set(&status, &oidset);
+ return(ctx->major);
}
OM_uint32
ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *buffer, gss_buffer_desc *hash) {
- OM_uint32 maj_status,min_status;
/* ssh1 needs to exchange the hash of the keys */
/* will us this hash to return it */
if (!compat20) {
- if ((maj_status=gss_wrap(&min_status,ctx->context,
- 0,
- GSS_C_QOP_DEFAULT,
- buffer,
- NULL,
- hash)))
- ssh_gssapi_error(ctx->oid,maj_status,min_status);
+ if ((ctx->major=gss_wrap(&ctx->minor,ctx->context,
+ 0,
+ GSS_C_QOP_DEFAULT,
+ buffer,
+ NULL,
+ hash)))
+ ssh_gssapi_error(ctx);
}
else
- if ((maj_status=gss_get_mic(&min_status,ctx->context,
+ if ((ctx->major=gss_get_mic(&ctx->minor,ctx->context,
GSS_C_QOP_DEFAULT, buffer, hash))) {
- ssh_gssapi_error(ctx->oid,maj_status,min_status);
+ ssh_gssapi_error(ctx);
}
- return(maj_status);
+ return(ctx->major);
}
-OM_uint32
-ssh_gssapi_client_ctx(Gssctxt **ctx,gss_OID oid, char *host) {
- gss_buffer_desc token = {0,NULL};
- OM_uint32 major,minor;
-
+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);
- ssh_gssapi_import_name(*ctx,host);
- major=ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, NULL);
+ 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);
- return(major);
+ ssh_gssapi_delete_ctx(&ctx);
+ return(!GSS_ERROR(major));
}
#endif /* GSSAPI */
/*
- * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved.
+ * 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
#ifdef GSSAPI
#include "ssh.h"
-#include "ssh1.h"
#include "ssh2.h"
#include "xmalloc.h"
#include "buffer.h"
#include "dispatch.h"
#include "servconf.h"
#include "compat.h"
-#include "misc.h"
#include "monitor_wrap.h"
#include "ssh-gss.h"
extern u_char *session_id2;
extern int session_id2_len;
-typedef struct ssh_gssapi_cred_cache {
- char *filename;
- char *envvar;
- char *envval;
- void *data;
-} ssh_gssapi_cred_cache;
+static ssh_gssapi_client gssapi_client =
+ { {0,NULL}, GSS_C_NO_CREDENTIAL, NULL, {NULL,NULL,NULL}};
-static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL};
-
-/*
- * Environment variables pointing to delegated credentials
- */
-static char *delegation_env[] = {
- "X509_USER_PROXY", /* GSSAPI/SSLeay */
- "KRB5CCNAME", /* Krb5 and possibly SSLeay */
- NULL
-};
-
-static void gssapi_unsetenv(const char *var);
+ssh_gssapi_mech gssapi_null_mech
+ = {NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
#ifdef KRB5
-
-#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 */
-
-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
- */
-
-int
-ssh_gssapi_krb5_userok(char *name) {
- krb5_principal princ;
- int retval;
-
- if (ssh_gssapi_krb5_init() == 0)
- return 0;
-
- if ((retval=krb5_parse_name(krb_context, gssapi_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 *)gssapi_client_name.value);
- }
- else
- retval = 0;
-
- krb5_free_principal(krb_context, princ);
- return retval;
-}
-
-int
-ssh_gssapi_krb5_localname(char **user)
-{
- krb5_principal princ;
-
- if (ssh_gssapi_krb5_init() == 0)
- return 0;
-
- if (krb5_parse_name(krb_context, gssapi_client_name.value, &princ)) {
- return(0);
- }
- *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.
- */
-
-OM_uint32
-ssh_gssapi_krb5_storecreds(gss_buffer_t export_buffer) {
- 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;
- gss_cred_id_t krb5_cred_handle;
-
-
- if (gssapi_client_creds==NULL) {
- debug("No credentials stored");
- return GSS_S_NO_CRED;
- }
-
- if (ssh_gssapi_krb5_init() == 0)
- return GSS_S_FAILURE;
-
- 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 GSS_S_FAILURE;
- }
- if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
- log("fchmod(): %.100s", strerror(errno));
- close(tmpfd);
- return GSS_S_FAILURE;
- }
- } 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 GSS_S_FAILURE;
- }
- }
-
- 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 GSS_S_FAILURE;
- }
-
- if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value,
- &princ))) {
- log("krb5_parse_name(): %.100s",
- krb5_get_err_text(krb_context,problem));
- krb5_cc_destroy(krb_context,ccache);
- return GSS_S_FAILURE;
- }
-
- 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 GSS_S_FAILURE;
- }
-
- krb5_free_principal(krb_context,princ);
-
-#ifdef MECHGLUE
- krb5_cred_handle =
- __gss_get_mechanism_cred(gssapi_client_creds,
- &(supported_mechs[GSS_KERBEROS].oid));
-#else
- krb5_cred_handle = gssapi_client_creds;
+extern ssh_gssapi_mech gssapi_kerberos_mech;
+extern ssh_gssapi_mech gssapi_kerberos_mech_old;
#endif
-
- if ((maj_status = gss_krb5_copy_ccache(&min_status,
- krb5_cred_handle,
- ccache))) {
- log("gss_krb5_copy_ccache() failed");
- ssh_gssapi_error(&supported_mechs[GSS_KERBEROS].oid,
- maj_status,min_status);
- krb5_cc_destroy(krb_context,ccache);
- return GSS_S_FAILURE;
- }
-
- krb5_cc_close(krb_context,ccache);
-
- export_buffer->length = strlen("KRB5CCNAME")+strlen(name)+1;
- export_buffer->value = xmalloc(export_buffer->length+1);
- sprintf(export_buffer->value, "%s=%s", "KRB5CCNAME", name);
-
- return GSS_S_COMPLETE;
-}
-
-#endif /* KRB5 */
-
#ifdef GSI
-#include <globus_gss_assist.h>
-
-/*
- * 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.
- */
-int
-ssh_gssapi_gsi_userok(char *name)
-{
- int authorized = 0;
-
-#ifdef GLOBUS_GSI_GSS_ASSIST_MODULE
- if (globus_module_activate(GLOBUS_GSI_GSS_ASSIST_MODULE) != 0) {
- return 0;
- }
+extern ssh_gssapi_mech gssapi_gsi_mech;
+extern ssh_gssapi_mech gssapi_gsi_mech_old;
#endif
- /* globus_gss_assist_userok() returns 0 on success */
- authorized = (globus_gss_assist_userok(gssapi_client_name.value,
- name) == 0);
-
- log("GSI user %s is%s authorized as target user %s",
- (char *) gssapi_client_name.value, (authorized ? "" : " not"), name);
-
- return authorized;
-}
-
-/*
- * Return the local username associated with the GSI credentials.
- */
-int
-ssh_gssapi_gsi_localname(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(gssapi_client_name.value, user) == 0);
-}
-
-/*
- * Handle setting up child environment for GSI.
- *
- * Make sure that this is called _after_ we've setuid to the user.
- */
-OM_uint32
-ssh_gssapi_gsi_storecreds(gss_buffer_t export_buffer)
-{
- OM_uint32 major_status;
- OM_uint32 minor_status;
-
- if (gssapi_client_creds != NULL)
- {
- char *creds_env = NULL;
-
- /*
- * This is the current hack with the GSI gssapi library to
- * export credentials to disk.
- */
-
- debug("Exporting delegated credentials");
-
- minor_status = 0xdee0; /* Magic value */
- major_status =
- gss_inquire_cred(&minor_status,
- gssapi_client_creds,
- (gss_name_t *) &creds_env,
- NULL,
- NULL,
- NULL);
-
- if ((major_status == GSS_S_COMPLETE) &&
- (minor_status == 0xdee1) &&
- (creds_env != NULL))
- {
- char *value;
-
- /*
- * String is of the form:
- * X509_USER_DELEG_PROXY=filename
- * so we parse out the filename
- * and then set X509_USER_PROXY
- * to point at it.
- */
- value = strchr(creds_env, '=');
-
- if (value != NULL)
- {
- *value = '\0';
- value++;
- export_buffer->length=
- strlen("X509_USER_PROXY")+strlen(value)+1;
- export_buffer->value =
- xmalloc(export_buffer->length+1);
- sprintf(export_buffer->value, "%s=%s",
- "X509_USER_PROXY", value);
-
- return GSS_S_COMPLETE;
- }
- else
- {
- log("Failed to parse delegated credentials string '%s'",
- creds_env);
- }
- }
- else
- {
- log("Failed to export delegated credentials (error %u)",
- (unsigned int)major_status);
- }
- }
- return 0;
-}
-
-#endif /* GSI */
-
-void
-ssh_gssapi_cleanup_creds(void *ignored)
-{
- /* OM_uint32 min_stat; */
-
- if (gssapi_cred_store.filename!=NULL) {
- /* Unlink probably isn't sufficient */
- debug("removing gssapi cred file \"%s\"",gssapi_cred_store.filename);
- unlink(gssapi_cred_store.filename);
- }
- /* DK ??
- if (gssapi_client_creds != GSS_C_NO_CREDENTIAL)
- gss_release_cred(&min_stat, &gssapi_client_creds);
- */
-}
-
-OM_uint32
-ssh_gssapi_export_cred(OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
- const gss_OID desired_mech,
- OM_uint32 option_req,
- gss_buffer_t export_buffer)
-{
- OM_uint32 maj_stat = GSS_S_FAILURE;
-
- if (option_req != 1) return GSS_S_UNAVAILABLE;
- if (desired_mech != NULL) return GSS_S_BAD_MECH;
-
- switch (gssapi_client_type) {
+ssh_gssapi_mech* supported_mechs[]= {
#ifdef KRB5
- case GSS_KERBEROS:
- maj_stat = ssh_gssapi_krb5_storecreds(export_buffer);
- break;
+ &gssapi_kerberos_mech,
+ &gssapi_kerberos_mech_old, /* Support for legacy clients */
#endif
#ifdef GSI
- case GSS_GSI:
- maj_stat = ssh_gssapi_gsi_storecreds(export_buffer);
- break;
-#endif /* GSI */
- case GSS_LAST_ENTRY:
- /* GSSAPI not used in this authentication */
- debug("No GSSAPI credentials stored");
- break;
- default:
- log("ssh_gssapi_do_child: Unknown mechanism");
-
- }
-
- if (GSS_ERROR(maj_stat)) {
- *minor_status = GSS_S_FAILURE;
- }
- return maj_stat;
-}
-
-void
-ssh_gssapi_storecreds()
-{
- OM_uint32 maj_stat, min_stat;
- gss_buffer_desc export_cred = GSS_C_EMPTY_BUFFER;
- char *p;
-
- if (gssapi_client_creds == GSS_C_NO_CREDENTIAL)
- return;
-
-#ifdef HAVE_GSSAPI_EXT
- maj_stat = gss_export_cred(&min_stat, gssapi_client_creds,
- GSS_C_NO_OID, 1, &export_cred);
- if (GSS_ERROR(maj_stat) && maj_stat != GSS_S_UNAVAILABLE) {
- ssh_gssapi_error(GSS_C_NO_OID, maj_stat, min_stat);
- return;
- }
-#endif
-
- /* If gss_export_cred() is not available, use old methods */
- if (export_cred.length == 0) {
- ssh_gssapi_export_cred(&min_stat, gssapi_client_creds,
- GSS_C_NO_OID, 1, &export_cred);
- if (GSS_ERROR(maj_stat)) {
- ssh_gssapi_error(GSS_C_NO_OID, maj_stat, min_stat);
- }
- }
-
- p = strchr((char *) export_cred.value, '=');
- if (p == NULL) {
- log("Failed to parse exported credentials string '%.100s'",
- (char *)export_cred.value);
- gss_release_buffer(&min_stat, &export_cred);
- return;
- }
- *p++ = '\0';
-#ifdef GSI
- if (strcmp((char *)export_cred.value,"X509_USER_DELEG_PROXY") == 0)
- gssapi_cred_store.envvar = strdup("X509_USER_PROXY");
- else
+ &gssapi_gsi_mech,
+ &gssapi_gsi_mech_old, /* Support for legacy clients */
#endif
- gssapi_cred_store.envvar = strdup((char *)export_cred.value);
- gssapi_cred_store.envval = strdup(p);
-#ifdef USE_PAM
- do_pam_putenv(gssapi_cred_store.envvar, gssapi_cred_store.envval);
-#endif
- if (strncmp(p, "FILE:", 5) == 0) {
- p += 5;
- }
- if (access(p, R_OK) == 0) {
- gssapi_cred_store.filename = strdup(p);
- }
- gss_release_buffer(&min_stat, &export_cred);
-
- if (options.gss_cleanup_creds) {
- fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
- }
-}
+ &gssapi_null_mech,
+};
-/* This allows GSSAPI methods to do things to the childs environment based
- * on the passed authentication process and credentials.
+/* Return a list of the gss-group1-sha1-x mechanisms supported by this
+ * program.
*
- * Question: If we didn't use userauth_external for some reason, should we
- * still delegate credentials?
- */
-void
-ssh_gssapi_do_child(char ***envp, u_int *envsizep)
-{
-
- if (gssapi_cred_store.envvar!=NULL &&
- gssapi_cred_store.envval!=NULL) {
-
- debug("Setting %s to %s", gssapi_cred_store.envvar,
- gssapi_cred_store.envval);
- child_set_env(envp, envsizep, gssapi_cred_store.envvar,
- gssapi_cred_store.envval);
- }
-
- switch(gssapi_client_type) {
-#ifdef KRB5
- case GSS_KERBEROS: break;
-#endif
-#ifdef GSI
- case GSS_GSI: break;
-#endif
- case GSS_LAST_ENTRY:
- debug("No GSSAPI credentials stored");
- break;
- default:
- log("ssh_gssapi_do_child: Unknown mechanism");
- }
-}
-
-int
-ssh_gssapi_userok(char *user)
-{
- if (gssapi_client_name.length==0 ||
- gssapi_client_name.value==NULL) {
- debug("No suitable client data");
- return 0;
- }
- switch (gssapi_client_type) {
-#ifdef KRB5
- case GSS_KERBEROS:
- return(ssh_gssapi_krb5_userok(user));
- break; /* Not reached */
-#endif
-#ifdef GSI
- case GSS_GSI:
- return(ssh_gssapi_gsi_userok(user));
- break; /* Not reached */
-#endif /* GSI */
- case GSS_LAST_ENTRY:
- debug("Client not GSSAPI");
- break;
- default:
- debug("Unknown client authentication type");
- }
- return(0);
-}
-
-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);;
- }
- switch (gssapi_client_type) {
-#ifdef KRB5
- case GSS_KERBEROS:
- return(ssh_gssapi_krb5_localname(user));
- break; /* Not reached */
-#endif
-#ifdef GSI
- case GSS_GSI:
- return(ssh_gssapi_gsi_localname(user));
- break; /* Not reached */
-#endif /* GSI */
- case GSS_LAST_ENTRY:
- debug("Client not GSSAPI");
- break;
- default:
- debug("Unknown client authentication type");
- }
- return(0);
-}
-
-/*
- * Clean our environment on startup. This means removing any environment
- * strings that might inadvertantly been in root's environment and
- * could cause serious security problems if we think we set them.
- */
-void
-ssh_gssapi_clean_env(void)
-{
- char *envstr;
- int envstr_index;
-
-
- for (envstr_index = 0;
- (envstr = delegation_env[envstr_index]) != NULL;
- envstr_index++) {
-
- if (getenv(envstr)) {
- debug("Clearing environment variable %s", envstr);
- gssapi_unsetenv(envstr);
- }
- }
-}
-
-/*
- * Wrapper around unsetenv.
+ * 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.
*/
-static void
-gssapi_unsetenv(const char *var)
-{
-#ifdef HAVE_UNSETENV
- unsetenv(var);
-
-#else /* !HAVE_UNSETENV */
- extern char **environ;
- char **p1 = environ; /* New array list */
- char **p2 = environ; /* Current array list */
- int len = strlen(var);
-
- /*
- * Walk through current environ array (p2) copying each pointer
- * to new environ array (p1) unless the pointer is to the item
- * we want to delete. Copy happens in place.
- */
- while (*p2) {
- if ((strncmp(*p2, var, len) == 0) &&
- ((*p2)[len] == '=')) {
- /*
- * *p2 points at item to be deleted, just skip over it
- */
- p2++;
- } else {
- /*
- * *p2 points at item we want to save, so copy it
- */
- *p1 = *p2;
- p1++;
- p2++;
- }
- }
-
- /* And make sure new array is NULL terminated */
- *p1 = NULL;
-#endif /* HAVE_UNSETENV */
-}
-
+
char *
-ssh_server_gssapi_mechanisms() {
+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;
- int mech_count=0;
char * mechs;
- Gssctxt * ctx = NULL;
if (datafellows & SSH_OLD_GSSAPI) return NULL;
- PRIVSEP(gss_indicate_mechs(&min_status, &supported));
+ ssh_gssapi_supported_oids(&supported);
- buffer_init(&buf);
+ buffer_init(&buf);
- do {
+ while(supported_mechs[i]->name != NULL) {
if ((maj_status=gss_test_oid_set_member(&min_status,
- &supported_mechs[i].oid,
+ &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 (++mech_count > 1) {
- buffer_append(&buf, ",", 1);
- }
- buffer_append(&buf, gssprefix,
- strlen(gssprefix));
- 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, gssprefix,
- supported_mechs[i].enc_name);
- } else {
- debug("no credentials for GSSAPI mechanism %s",
- supported_mechs[i].name);
- }
- } else {
+ 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);
+ supported_mechs[i]->name);
}
- } while (supported_mechs[++i].name != NULL);
+ ssh_gssapi_delete_ctx(&ctx);
+ i++;
+ }
buffer_put_char(&buf,'\0');
return(mechs);
}
-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));
-}
-
void ssh_gssapi_supported_oids(gss_OID_set *oidset) {
- enum ssh_gss_id i =0;
+ 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) {
+ while (supported_mechs[i]->name!=NULL) {
if ((maj_status=gss_test_oid_set_member(&min_status,
- &supported_mechs[i].oid,
+ &supported_mechs[i]->oid,
supported,
&present))) {
present=0;
}
if (present) {
gss_add_oid_set_member(&min_status,
- &supported_mechs[i].oid,
+ &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
+ */
+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;
+}
+
+/* Set the GSS context's OID to the oid indicated by the given key exchange
+ * name. */
+
+gss_OID
+ssh_gssapi_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 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);
+
+ if (ctx) ssh_gssapi_set_oid(ctx,&supported_mechs[i]->oid);
+
+ 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 arround accept_sec_context
* Requires that the context contains:
* oid
OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx,gss_buffer_desc *recv_tok,
gss_buffer_desc *send_tok, OM_uint32 *flags)
{
- OM_uint32 maj_status, min_status;
+ OM_uint32 status;
gss_OID mech;
- maj_status=gss_accept_sec_context(&min_status,
+ ctx->major=gss_accept_sec_context(&ctx->minor,
&ctx->context,
ctx->creds,
recv_tok,
flags,
NULL,
&ctx->client_creds);
- if (GSS_ERROR(maj_status)) {
- ssh_gssapi_send_error(ctx->oid,maj_status,min_status);
+ if (GSS_ERROR(ctx->major)) {
+ ssh_gssapi_error(ctx);
}
if (ctx->client_creds) {
}
/* FIXME: We should check that the me
- * the one that we asked for (in ctx->oid) */
+ * the one that we asked for (in ctx->oid) */
- ctx->status=maj_status;
+ status=ctx->major;
/* Now, if we're complete and we have the right flags, then
- * we flag the user as also having been authenticated
- */
+ * we flag the user as also having been authenticated
+ */
if (((flags==NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
(*flags & GSS_C_INTEG_FLAG))) &&
- (maj_status == GSS_S_COMPLETE)) {
- if (ssh_gssapi_getclient(ctx,&gssapi_client_type,
- &gssapi_client_name,
- &gssapi_client_creds))
+ (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");
}
- return(maj_status);
+ /* 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 */
+
+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);
}
void
-ssh_gssapi_send_error(gss_OID mech, OM_uint32 major_status,
- OM_uint32 minor_status) {
- OM_uint32 lmaj, lmin;
- gss_buffer_desc msg = {0,NULL};
- OM_uint32 ctx;
-
- ctx = 0;
- /* The GSSAPI error */
- do {
- lmaj = PRIVSEP(gss_display_status(&lmin, major_status,
- GSS_C_GSS_CODE,
- mech,
- &ctx, &msg));
- if (lmaj == GSS_S_COMPLETE) {
- debug((char *)msg.value);
- packet_send_debug((char *)msg.value);
- (void) gss_release_buffer(&lmin, &msg);
- }
- } while (ctx!=0);
-
- /* The mechanism specific error */
- do {
- lmaj = PRIVSEP(gss_display_status(&lmin, minor_status,
- GSS_C_MECH_CODE,
- mech,
- &ctx, &msg));
- if (lmaj == GSS_S_COMPLETE) {
- debug((char *)msg.value);
- packet_send_debug((char *)msg.value);
- (void) gss_release_buffer(&lmin, &msg);
- }
- } while (ctx!=0);
+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);
+ }
+}
+
+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");
+ }
}
-#endif /* GSSAPI */
+/* This allows GSSAPI methods to do things to the childs environment based
+ * on the passed authentication process and credentials.
+ *
+ * Question: If we didn't use userauth_external for some reason, should we
+ * still delegate credentials?
+ */
+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);
+ }
+}
+
+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);
+}
+
+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
} 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) {
+ } else if (strncmp(k->name, KEX_GSS_SHA1, sizeof(KEX_GSS_SHA1)-1) == 0) {
k->kex_type = KEX_GSS_GRP1_SHA1;
#endif
} else
Buffer peer;
int done;
int flags;
- char *host;
+ char *host;
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 *);
- struct KexOptions options;
void (*kex[KEX_MAX])(Kex *);
};
void kexdh_server(Kex *);
void kexgex_client(Kex *);
void kexgex_server(Kex *);
-void kexgex_client(Kex *);
-void kexgex_server(Kex *);
#ifdef GSSAPI
-void kexgss_client(Kex *);
-void kexgss_server(Kex *);
+void kexgss_client(Kex *);
+void kexgss_server(Kex *);
#endif
u_char *
u_char *
kexgex_hash(char *, char *, char *, int, char *, int, u_char *, int,
int, int, int, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *);
-#ifdef GSSAPI
-u_char *
-kex_gssapi_hash(char *, char *, char *, int, char *, int, u_char *, int,
- BIGNUM *, BIGNUM *, BIGNUM *);
-#endif
#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
void dump_digest(char *, u_char *, int);
+++ /dev/null
-/*
- * Copyright (c) 2001,2002 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 "buffer.h"
-#include "bufaux.h"
-#include "ssh2.h"
-#include "kex.h"
-
-/* This is now the same as the DH hash ... */
-
-u_char *
-kex_gssapi_hash(
- char *client_version_string,
- char *server_version_string,
- char *ckexinit, int ckexinitlen,
- char *skexinit, int skexinitlen,
- u_char *serverhostkeyblob, int sbloblen,
- BIGNUM *client_dh_pub,
- BIGNUM *server_dh_pub,
- BIGNUM *shared_secret)
-{
- Buffer b;
- static u_char digest[EVP_MAX_MD_SIZE];
- EVP_MD *evp_md = EVP_sha1();
- EVP_MD_CTX md;
-
- buffer_init(&b);
- buffer_put_string(&b, client_version_string, strlen(client_version_string));
- buffer_put_string(&b, server_version_string, strlen(server_version_string));
-
- /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
- buffer_put_int(&b, ckexinitlen+1);
- buffer_put_char(&b, SSH2_MSG_KEXINIT);
- buffer_append(&b, ckexinit, ckexinitlen);
- buffer_put_int(&b, skexinitlen+1);
- buffer_put_char(&b, SSH2_MSG_KEXINIT);
- buffer_append(&b, skexinit, skexinitlen);
-
- buffer_put_string(&b, serverhostkeyblob, sbloblen);
- buffer_put_bignum2(&b, client_dh_pub);
- buffer_put_bignum2(&b, server_dh_pub);
- buffer_put_bignum2(&b, shared_secret);
-
-#ifdef DEBUG_KEX
- buffer_dump(&b);
-#endif
- EVP_DigestInit(&md, evp_md);
- EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
- EVP_DigestFinal(&md, digest, NULL);
-
- buffer_free(&b);
-
-#ifdef DEBUG_KEX
- dump_digest("hash", digest, evp_md->md_size);
-#endif
- return digest;
-}
-
-#endif /* GSSAPI */
#include "dh.h"
#include "ssh2.h"
#include "ssh-gss.h"
-#include "monitor_wrap.h"
#include "canohost.h"
unsigned char *kbuf;
unsigned char *hash;
unsigned char *serverhostkey;
+ char *msg;
+ char *lang;
int type = 0;
int first = 1;
- int slen = 0;
+ int slen = 0, strlen;
/* Initialise our GSSAPI world */
ssh_gssapi_build_ctx(&ctxt);
- if (ssh_gssapi_id_kex(ctxt,kex->name)==NULL) {
+ 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))) {
}
token_ptr = GSS_C_NO_BUFFER;
-
+
do {
debug("Calling gss_init_sec_context");
debug("Received GSSAPI_CONTINUE");
if (maj_status == GSS_S_COMPLETE)
fatal("GSSAPI Continue received from server when complete");
- recv_tok.value=packet_get_string(&slen);
- recv_tok.length=slen; /* int vs. size_t */
+ recv_tok.value=packet_get_string(&strlen);
+ recv_tok.length=strlen; /* 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(&slen);
- msg_tok.length=slen; /* int vs. size_t */
+ msg_tok.value=packet_get_string(&strlen);
+ msg_tok.length=strlen; /* int vs. size_t */
/* Is there a token included? */
if (packet_get_char()) {
recv_tok.value=
- packet_get_string(&slen);
- recv_tok.length=slen; /* int/size_t */
+ packet_get_string(&strlen);
+ recv_tok.length=strlen; /*int/size_t*/
/* If we're already complete - protocol error */
if (maj_status == GSS_S_COMPLETE)
packet_disconnect("Protocol error: received token when 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);
+ fatal(msg);
default:
packet_disconnect("Protocol error: didn't expect packet type %d",
type);
memset(kbuf, 0, klen);
xfree(kbuf);
- slen=0;
- hash = kex_gssapi_hash(
+ /* 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),
/*
- * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved.
+ * 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
#include "ssh2.h"
#include "ssh-gss.h"
#include "monitor_wrap.h"
-#include "canohost.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.*/
+ * 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;
BIGNUM *shared_secret = NULL;
BIGNUM *dh_client_pub = NULL;
int type =0;
- gss_OID oid;
u_int slen;
+ gss_OID oid;
/* Initialise GSSAPI */
debug2("%s: Acquiring credentials",__func__);
- if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,oid))))
- packet_disconnect("Unable to acquire credentials for the server");
-
+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,oid)))) {
+ kex_gss_send_error(ctxt);
+ packet_disconnect("Unable to acquire credentials for the server");
+ }
+
do {
debug("Wait SSH2_MSG_GSSAPI_INIT");
type = packet_read();
dh_client_pub = BN_new();
if (dh_client_pub == NULL)
- fatal("dh_client_pub == NULL");
+ packet_disconnect("dh_client_pub == NULL");
packet_get_bignum2(dh_client_pub);
/* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
}
maj_status=PRIVSEP(ssh_gssapi_accept_ctx(ctxt,&recv_tok,
- &send_tok, &ret_flags));
+ &send_tok, &ret_flags));
gss_release_buffer(&min_status,&recv_tok);
-
+
#ifdef GSS_C_GLOBUS_LIMITED_PROXY_FLAG
if (ret_flags & GSS_C_GLOBUS_LIMITED_PROXY_FLAG) {
packet_disconnect("Limited proxy is not allowed in gssapi key exchange.");
}
#endif
-
- if (maj_status & GSS_S_CONTINUE_NEEDED) {
+
+ if ((maj_status & GSS_S_CONTINUE_NEEDED) ||
+ (GSS_ERROR(maj_status) && send_tok.length>0)) {
debug("Sending GSSAPI_CONTINUE");
packet_start(SSH2_MSG_KEXGSS_CONTINUE);
packet_put_string(send_tok.value,send_tok.length);
} while (maj_status & GSS_S_CONTINUE_NEEDED);
if (GSS_ERROR(maj_status)) {
- ssh_gssapi_send_error(oid,maj_status,min_status);
+ kex_gss_send_error(ctxt);
packet_disconnect("gssapi key exchange handshake failed");
}
-
+
debug("gss_complete");
- if (!(ret_flags & GSS_C_MUTUAL_FLAG)) {
- ssh_gssapi_send_error(oid,maj_status,min_status);
- packet_disconnect("gssapi mutual authentication failed");
- }
+ if (!(ret_flags & GSS_C_MUTUAL_FLAG))
+ packet_disconnect("gssapi_mutual authentication failed");
- if (!(ret_flags & GSS_C_INTEG_FLAG)) {
- ssh_gssapi_send_error(oid,maj_status,min_status);
+ if (!(ret_flags & GSS_C_INTEG_FLAG))
packet_disconnect("gssapi channel integrity not established");
- }
-
+
dh = dh_new_group1();
dh_gen_key(dh, kex->we_need * 8);
memset(kbuf, 0, klen);
xfree(kbuf);
- hash = kex_gssapi_hash(
+ /* 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),
gssbuf.length = 20; /* Hashlen appears to always be 20 */
if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) {
- if (ctxt) { /* may be NULL under privsep */
- ssh_gssapi_send_error(ctxt->oid,maj_status,min_status);
- } else {
- ssh_gssapi_send_error(GSS_C_NO_OID,maj_status,min_status);
- }
- packet_disconnect("Couldn't get MIC");
+ kex_gss_send_error(ctxt);
+ fatal("Couldn't get MIC");
}
packet_start(SSH2_MSG_KEXGSS_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 */
+ * using privsep */
ssh_gssapi_delete_ctx(&ctxt);
DH_free(dh);
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 */
use Convert::ASN1 qw(:tag);
use Digest::MD5 qw(md5);
use MIME::Base64;
+use Data::Dumper;
$oid=shift;
-$encoded=encode_object_id($oid);
+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);
-shift @entries; # Get rid of the NULL
print "DER representation: ";
foreach $entry (@entries) {
int mm_answer_pam_start(int, Buffer *);
#endif
+#ifdef KRB4
+int mm_answer_krb4(int, Buffer *);
+#endif
+#ifdef KRB5
+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_localname(int, Buffer *);
int mm_answer_gss_sign(int, Buffer *);
int mm_answer_gss_indicate_mechs(int, Buffer *);
-int mm_answer_gss_display_status(int, Buffer *);
+int mm_answer_gss_error(int, Buffer *);
#endif
#ifdef GSI
int mm_answer_gsi_gridmap(int, Buffer *);
#endif
-#ifdef KRB4
-int mm_answer_krb4(int, Buffer *);
-#endif
-#ifdef KRB5
-int mm_answer_krb5(int, Buffer *);
-#endif
-
static Authctxt *authctxt;
static BIGNUM *ssh1_challenge = NULL; /* used for ssh1 rsa auth */
{MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
{MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
{MONITOR_REQ_GSSMECHS, MON_ISAUTH, mm_answer_gss_indicate_mechs},
- {MONITOR_REQ_GSSSTAT, MON_ISAUTH, mm_answer_gss_display_status},
{MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
{MONITOR_REQ_GSSLOCALNAME, MON_AUTH, mm_answer_gss_localname},
+ {MONITOR_REQ_GSSERR, MON_ISAUTH | MON_ONCE, mm_answer_gss_error},
#endif
{MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed},
{MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify},
{MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
{MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
{MONITOR_REQ_GSSMECHS, 0, mm_answer_gss_indicate_mechs},
- {MONITOR_REQ_GSSSTAT, 0, mm_answer_gss_display_status},
+ {MONITOR_REQ_GSSERR, 0, mm_answer_gss_error},
#endif
{MONITOR_REQ_MODULI, 0, mm_answer_moduli},
{MONITOR_REQ_SIGN, 0, mm_answer_sign},
{MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
{MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
{MONITOR_REQ_GSSMECHS, MON_ISAUTH, mm_answer_gss_indicate_mechs},
- {MONITOR_REQ_GSSSTAT, MON_ISAUTH, mm_answer_gss_display_status},
#endif
#ifdef GSI
{MONITOR_REQ_GSIGRIDMAP, MON_PERMIT, mm_answer_gsi_gridmap},
{MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
{MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
{MONITOR_REQ_GSSMECHS, 0, mm_answer_gss_indicate_mechs},
- {MONITOR_REQ_GSSSTAT, 0, mm_answer_gss_display_status},
#endif
{MONITOR_REQ_PTY, MON_ONCE, mm_answer_pty},
{MONITOR_REQ_PTYCLEANUP, MON_ONCE, mm_answer_pty_cleanup},
monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS, 1);
- monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTAT, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
#endif
} else {
mon_dispatch = mon_dispatch_proto15;
#ifdef GSSAPI
monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS, 1);
- monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTAT, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
#endif
#ifdef GSI
monitor_permit(mon_dispatch, MONITOR_REQ_GSIGRIDMAP, 1);
monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
}
#ifdef GSSAPI
- monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTAT, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
#endif
if (!no_pty_flag) {
monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
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);
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_localname(int socket, Buffer *m) {
char *name;
}
int
-mm_answer_gss_display_status(int socket, Buffer *m) {
- OM_uint32 major,minor,status_value,message_context;
- int status_type;
- gss_OID_desc mech_type_desc;
- gss_OID mech_type;
- gss_buffer_desc status_string;
- u_int length;
-
- status_value = buffer_get_int(m);
- status_type = buffer_get_int(m);
- mech_type_desc.elements = buffer_get_string(m, &length);
- mech_type_desc.length = length;
- if (length != 0) {
- mech_type = &mech_type_desc;
- } else if (gsscontext) {
- mech_type = gsscontext->oid;
- } else {
- mech_type = GSS_C_NO_OID;
- }
- message_context = buffer_get_int(m);
-
- major=gss_display_status(&minor, status_value, status_type, mech_type,
- &message_context, &status_string);
+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, message_context);
- buffer_put_string(m, status_string.value, status_string.length);
-
- mm_request_send(socket,MONITOR_ANS_GSSSTAT,m);
+ buffer_put_int(m,major);
+ buffer_put_int(m,minor);
+ buffer_put_cstring(m,msg);
- if (mech_type_desc.elements) {
- xfree(mech_type_desc.elements);
- }
+ mm_request_send(socket,MONITOR_ANS_GSSERR,m);
- return 0;
+ xfree(msg);
+
+ return(0);
}
#endif /* GSSAPI */
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_GSSMECHS,MONITOR_ANS_GSSMECHS,
- MONITOR_REQ_GSSSTAT,MONITOR_ANS_GSSSTAT,
MONITOR_REQ_GSSUSEROK,MONITOR_ANS_GSSUSEROK,
MONITOR_REQ_GSSLOCALNAME,MONITOR_ANS_GSSLOCALNAME,
MONITOR_REQ_GSIGRIDMAP,MONITOR_ANS_GSIGRIDMAP,
+ MONITOR_REQ_GSSERR,MONITOR_ANS_GSSERR,
+#endif
MONITOR_REQ_KEYALLOWED, MONITOR_ANS_KEYALLOWED,
MONITOR_REQ_KEYVERIFY, MONITOR_ANS_KEYVERIFY,
MONITOR_REQ_KEYEXPORT,
return (success);
}
+
+#ifdef KRB4
+int
+mm_auth_krb4(Authctxt *authctxt, void *_auth, char **client, void *_reply)
+{
+ KTEXT auth, reply;
+ Buffer m;
+ u_int rlen;
+ int success = 0;
+ char *p;
+
+ debug3("%s entering", __func__);
+ auth = _auth;
+ reply = _reply;
+
+ buffer_init(&m);
+ buffer_put_string(&m, auth->dat, auth->length);
+
+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KRB4, &m);
+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KRB4, &m);
+
+ success = buffer_get_int(&m);
+ if (success) {
+ *client = buffer_get_string(&m, NULL);
+ p = buffer_get_string(&m, &rlen);
+ if (rlen >= MAX_KTXT_LEN)
+ fatal("%s: reply from monitor too large", __func__);
+ reply->length = rlen;
+ memcpy(reply->dat, p, rlen);
+ memset(p, 0, rlen);
+ xfree(p);
+ }
+ buffer_free(&m);
+ return (success);
+}
+#endif
+
+#ifdef KRB5
+int
+mm_auth_krb5(void *ctx, void *argp, char **userp, void *resp)
+{
+ krb5_data *tkt, *reply;
+ Buffer m;
+ int success;
+
+ debug3("%s entering", __func__);
+ tkt = (krb5_data *) argp;
+ reply = (krb5_data *) resp;
+
+ buffer_init(&m);
+ buffer_put_string(&m, tkt->data, tkt->length);
+
+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KRB5, &m);
+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KRB5, &m);
+
+ success = buffer_get_int(&m);
+ if (success) {
+ u_int len;
+
+ *userp = buffer_get_string(&m, NULL);
+ reply->data = buffer_get_string(&m, &len);
+ reply->length = len;
+ } else {
+ memset(reply, 0, sizeof(*reply));
+ *userp = NULL;
+ }
+
+ buffer_free(&m);
+ return (success);
+}
+#endif
#ifdef GSSAPI
OM_uint32
mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) {
buffer_put_string(&m,oid->elements,oid->length);
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, &m);
-
- debug3("%s: waiting for MONITOR_ANS_GSSSETUP",__func__);
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);
- debug3("%s: waiting for MONITOR_ANS_GSSSTEP", __func__);
+ 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);
}
buffer_init(&m);
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m);
- debug3("%s: waiting for MONITOR_ANS_GSSUSEROK", __func__);
mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK,
&m);
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);
buffer_put_string(&m, data->value, data->length);
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
-
- debug3("%s: waiting for MONITOR_ANS_GSSSIGN",__func__);
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)
{
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSMECHS, &m);
- debug3("%s: waiting for MONITOR_ANS_GSSMECHS",__func__);
mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSMECHS,
&m);
major=buffer_get_int(&m);
lmajor=gss_add_oid_set_member(&lminor, &member_oid, mech_set);
}
- return(major);
-}
-
-OM_uint32
-mm_gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value,
- int status_type, const gss_OID mech_type,
- OM_uint32 *message_context, gss_buffer_t status_string)
-{
- Buffer m;
- OM_uint32 major;
-
- buffer_init(&m);
-
- buffer_put_int(&m, status_value);
- buffer_put_int(&m, status_type);
- if (mech_type) {
- buffer_put_string(&m, mech_type->elements, mech_type->length);
- } else {
- buffer_put_string(&m, "", 0);
- }
- if (message_context) {
- buffer_put_int(&m, *message_context);
- } else {
- buffer_put_int(&m, 0);
- }
-
- mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTAT, &m);
-
- debug3("%s: waiting for MONITOR_ANS_GSSMECHS",__func__);
- mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTAT,
- &m);
+ buffer_free(&m);
- if (message_context) {
- *message_context = buffer_get_int(&m);
- } else {
- buffer_get_int(&m);
- }
- status_string->value = buffer_get_string(&m, &status_string->length);
-
- return major;
+ return(major);
}
#endif /* GSSAPI */
buffer_put_cstring(&m, subject_name);
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSIGRIDMAP, &m);
- debug3("%s: waiting for MONITOR_ANS_GSIGRIDMAP", __func__);
mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSIGRIDMAP,
&m);
}
#endif /* GSI */
-
-#ifdef KRB4
-int
-mm_auth_krb4(Authctxt *authctxt, void *_auth, char **client, void *_reply)
-{
- KTEXT auth, reply;
- Buffer m;
- u_int rlen;
- int success = 0;
- char *p;
-
- debug3("%s entering", __func__);
- auth = _auth;
- reply = _reply;
-
- buffer_init(&m);
- buffer_put_string(&m, auth->dat, auth->length);
-
- mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KRB4, &m);
- mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KRB4, &m);
-
- success = buffer_get_int(&m);
- if (success) {
- *client = buffer_get_string(&m, NULL);
- p = buffer_get_string(&m, &rlen);
- if (rlen >= MAX_KTXT_LEN)
- fatal("%s: reply from monitor too large", __func__);
- reply->length = rlen;
- memcpy(reply->dat, p, rlen);
- memset(p, 0, rlen);
- xfree(p);
- }
- buffer_free(&m);
- return (success);
-}
-#endif
-
-#ifdef KRB5
-int
-mm_auth_krb5(void *ctx, void *argp, char **userp, void *resp)
-{
- krb5_data *tkt, *reply;
- Buffer m;
- int success;
-
- debug3("%s entering", __func__);
- tkt = (krb5_data *) argp;
- reply = (krb5_data *) resp;
-
- buffer_init(&m);
- buffer_put_string(&m, tkt->data, tkt->length);
-
- mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KRB5, &m);
- mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KRB5, &m);
-
- success = buffer_get_int(&m);
- if (success) {
- u_int len;
-
- *userp = buffer_get_string(&m, NULL);
- reply->data = buffer_get_string(&m, &len);
- reply->length = len;
- } else {
- memset(reply, 0, sizeof(*reply));
- *userp = NULL;
- }
-
- buffer_free(&m);
- return (success);
-}
-#endif
int mm_ssh_gssapi_localname(char **user);
OM_uint32 mm_gss_indicate_mechs(OM_uint32 *minor_status,
gss_OID_set *mech_set);
-OM_uint32 mm_gss_display_status(OM_uint32 *minor_status,
- OM_uint32 status_value,
- int status_type, const gss_OID mech_type,
- OM_uint32 *message_context,
- gss_buffer_t status_string);
+char *mm_ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min);
#endif
#ifdef GSI
/*
- * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved.
+ * 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
#define KEX_GSS_SHA1 "gss-group1-sha1-"
-enum ssh_gss_id {
-#ifdef KRB5
- GSS_KERBEROS,
-#endif
-#ifdef GSI
- GSS_GSI,
-#endif /* GSI */
- GSS_LAST_ENTRY
-};
+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_gss_mech_struct {
+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 status; /* both */
+ OM_uint32 major; /* both */
+ OM_uint32 minor; /* both */
gss_ctx_id_t context; /* both */
gss_name_t name; /* both */
gss_OID oid; /* both */
gss_cred_id_t client_creds; /* server */
} Gssctxt;
-extern ssh_gssapi_mech supported_mechs[];
-extern char gssprefix[];
-extern gss_buffer_desc gssapi_client_name;
-extern gss_cred_id_t gssapi_client_creds;
-extern enum ssh_gss_id gssapi_client_type;
+extern ssh_gssapi_mech *supported_mechs[];
char *ssh_gssapi_mechanisms(char *host);
-char *ssh_server_gssapi_mechanisms();
-gss_OID ssh_gssapi_id_kex(Gssctxt *ctx, char *name);
+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);
-enum ssh_gss_id ssh_gssapi_get_ctype(Gssctxt *ctxt);
+ssh_gssapi_mech *ssh_gssapi_get_ctype(Gssctxt *ctxt);
+gss_OID ssh_gssapi_id_kex(Gssctxt *ctx, char *name);
OM_uint32 ssh_gssapi_import_name(Gssctxt *ctx, const char *host);
OM_uint32 ssh_gssapi_acquire_cred(Gssctxt *ctx);
gss_buffer_desc *send_tok,
OM_uint32 *flags);
OM_uint32 ssh_gssapi_getclient(Gssctxt *ctx,
- enum ssh_gss_id *type,
+ ssh_gssapi_mech **mech,
gss_buffer_desc *name,
gss_cred_id_t *creds);
-void ssh_gssapi_error(gss_OID mech,
- OM_uint32 major_status, OM_uint32 minor_status);
-void ssh_gssapi_send_error(gss_OID mech,
- OM_uint32 major_status,OM_uint32 minor_status);
+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_client_ctx(Gssctxt **ctx,gss_OID oid,char *host);
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(Gssctxt *ctx, char *name);
int ssh_gssapi_userok(char *name);
-int ssh_gssapi_localname(char **lname);
+int ssh_gssapi_localname(char **user);
void ssh_gssapi_server(Kex *kex, Buffer *client_kexinit,
Buffer *server_kexinit);
void ssh_gssapi_do_child(char ***envp, u_int *envsizep);
void ssh_gssapi_cleanup_creds(void *ignored);
void ssh_gssapi_storecreds();
-void ssh_gssapi_clean_env();
+char *ssh_gssapi_server_mechanisms();
#ifdef GSI
int gsi_gridmap(char *subject_name, char **mapped_name);
c->c_kex = kex_setup(myproposal);
c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
-#ifdef GSSAPI
- c->c_kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
-#endif
c->c_kex->verify_host_key = hostjump;
if (!(j = setjmp(kexjmp))) {
#ifdef GSSAPI
#ifdef GSI
+static gss_OID_desc gsioid={9, "\x2B\x06\x01\x04\x01\x9B\x50\x01\x01"};
char * get_gsi_name()
{
- OM_uint32 maj_stat;
- OM_uint32 min_stat;
gss_name_t pname = GSS_C_NO_NAME;
gss_buffer_desc tmpname;
gss_buffer_t tmpnamed = &tmpname;
- char *retname;
+ char *retname=NULL;
gss_OID_set oidset;
gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
+ Gssctxt *ctx = NULL;
- gss_create_empty_oid_set(&min_stat,&oidset);
- gss_add_oid_set_member(&min_stat,&supported_mechs[GSS_GSI].oid,&oidset);
- maj_stat = gss_acquire_cred(&min_stat,
- GSS_C_NO_NAME,
- GSS_C_INDEFINITE,
- oidset,
- GSS_C_INITIATE,
- &gss_cred,
- NULL,
- NULL);
-
- if (maj_stat != GSS_S_COMPLETE) {
- goto error;
+ ssh_gssapi_build_ctx(&ctx);
+
+ gss_create_empty_oid_set(&ctx->minor,&oidset);
+ gss_add_oid_set_member(&ctx->minor,&gsioid,&oidset);
+ ssh_gssapi_set_oid(ctx,&gsioid);
+ ctx->major = gss_acquire_cred(&ctx->minor,
+ GSS_C_NO_NAME,
+ GSS_C_INDEFINITE,
+ oidset,
+ GSS_C_INITIATE,
+ &gss_cred,
+ NULL,
+ NULL);
+
+ if (ctx->major != GSS_S_COMPLETE) {
+ goto cleanup;
}
debug("calling gss_inquire_cred");
- maj_stat = gss_inquire_cred(&min_stat,
- gss_cred,
- &pname,
- NULL,
- NULL,
- NULL);
- if (maj_stat != GSS_S_COMPLETE) {
- goto error;
+ ctx->major = gss_inquire_cred(&ctx->minor,
+ gss_cred,
+ &pname,
+ NULL,
+ NULL,
+ NULL);
+ if (ctx->major != GSS_S_COMPLETE) {
+ goto cleanup;
}
- maj_stat = gss_display_name(&min_stat,
- pname,
- tmpnamed,
- NULL);
- if (maj_stat != GSS_S_COMPLETE) {
- goto error;
+ ctx->major = gss_display_name(&ctx->minor,
+ pname,
+ tmpnamed,
+ NULL);
+ if (ctx->major != GSS_S_COMPLETE) {
+ goto cleanup;
}
debug("gss_display_name finsished");
- retname = (char *)malloc(tmpname.length + 1);
- if (!retname) {
- goto error;
- }
+ retname = xmalloc(tmpname.length + 1);
memcpy(retname, tmpname.value, tmpname.length);
retname[tmpname.length] = '\0';
- gss_release_name(&min_stat, &pname);
- gss_release_buffer(&min_stat, tmpnamed);
+ gss_release_name(&ctx->minor, &pname);
+ gss_release_buffer(&ctx->minor, tmpnamed);
+ cleanup:
+ if (!retname) {
+ debug("Failed to set GSI username from credentials");
+ ssh_gssapi_error(ctx);
+ }
+ if (ctx) ssh_gssapi_delete_ctx(&ctx);
return retname;
-
- error:
- debug("Failed to set GSI username from credentials");
- ssh_gssapi_error(&supported_mechs[GSS_GSI].oid, maj_stat, min_stat);
- return NULL;
}
#endif /* GSI */
gss_OID name_type;
gss_OID_set gss_mechs, my_mechs;
int my_mech_num, i, present;
- OM_uint32 maj_stat;
- OM_uint32 min_stat;
int ret_stat = 0; /* 1 == success */
OM_uint32 req_flags = 0;
OM_uint32 ret_flags;
unsigned int slen;
Gssctxt *ctx = NULL;
+ ssh_gssapi_build_ctx(&ctx);
+
xhost = xstrdup(get_canonical_hostname(1));
resolve_localhost(&xhost);
name_tok.value = service_name;
name_tok.length = strlen(service_name) + 1;
- maj_stat = gss_import_name(&min_stat, &name_tok,
- name_type, &target_name);
+ ctx->major = gss_import_name(&ctx->minor, &name_tok,
+ name_type, &target_name);
free(service_name);
service_name = NULL;
- if (maj_stat != GSS_S_COMPLETE) {
- ssh_gssapi_error(GSS_C_NO_OID, maj_stat, min_stat);
+ if (ctx->major != GSS_S_COMPLETE) {
+ ssh_gssapi_error(ctx);
goto cleanup;
}
- maj_stat = gss_indicate_mechs(&min_stat, &gss_mechs);
+ ctx->major = gss_indicate_mechs(&ctx->minor, &gss_mechs);
- if (maj_stat != GSS_S_COMPLETE) {
- ssh_gssapi_error(GSS_C_NO_OID, maj_stat, min_stat);
+ if (ctx->major != GSS_S_COMPLETE) {
+ ssh_gssapi_error(ctx);
goto cleanup;
}
/* The GSSAPI supports the mechs in gss_mechs, but which ones do
we have credentials for? We only get one try, so we don't want
to propose a mechanism we know is going to fail. */
- maj_stat = gss_create_empty_oid_set(&min_stat, &my_mechs);
- for (i=0; supported_mechs[i].name != NULL; i++) {
- maj_stat = gss_test_oid_set_member(&min_stat, &supported_mechs[i].oid,
- gss_mechs, &present);
- if (present) {
- if (!GSS_ERROR(ssh_gssapi_client_ctx(&ctx, &supported_mechs[i].oid,
- host))) {
- maj_stat = gss_add_oid_set_member(&min_stat,
- &supported_mechs[i].oid,
- &my_mechs);
- debug("GSSAPI mechanism %s supported", supported_mechs[i].name);
- } else {
- debug("no credentials for GSSAPI mechanism %s",
- supported_mechs[i].name);
- }
- } else {
- debug("GSSAPI mechanism %s not supported", supported_mechs[i].name);
+ ctx->major = gss_create_empty_oid_set(&ctx->minor, &my_mechs);
+ for (i=0; i<gss_mechs->count; i++) {
+ if (ssh_gssapi_check_mechanism(&(gss_mechs->elements[i]), host)) {
+ ctx->major = gss_add_oid_set_member(&ctx->minor,
+ &(gss_mechs->elements[i]),
+ &my_mechs);
}
}
#ifdef GSI
/* Send GSI before Kerberos, because if GSI fails, we can always fall
back and try regular Kerberos authentication with our Kerberos cred. */
- maj_stat = gss_test_oid_set_member(&min_stat, &supported_mechs[GSS_GSI].oid,
- my_mechs, &present);
+ ctx->major = gss_test_oid_set_member(&ctx->minor, &gsioid,
+ my_mechs, &present);
if (present) {
- packet_put_string(supported_mechs[GSS_GSI].oid.elements,
- supported_mechs[GSS_GSI].oid.length);
+ packet_put_string(gsioid.elements,gsioid.length);
}
#endif
for (my_mech_num = 0; my_mech_num < my_mechs->count; my_mech_num++) {
#ifdef GSI
/* Skip GSI. We already sent it above. */
if ((my_mechs->elements[my_mech_num].length ==
- supported_mechs[GSS_GSI].oid.length) &&
+ gsioid.length) &&
memcmp(my_mechs->elements[my_mech_num].elements,
- supported_mechs[GSS_GSI].oid.elements,
+ gsioid.elements,
my_mechs->elements[my_mech_num].length) == 0) {
continue;
}
mech_oid.length = slen; /* safe typecast */
packet_get_all();
+ ssh_gssapi_set_oid(ctx, &mech_oid);
+
/*
* Perform the context-establishement loop.
*
gss_context = GSS_C_NO_CONTEXT;
do {
- maj_stat =
- gss_init_sec_context(&min_stat,
+ ctx->major =
+ gss_init_sec_context(&ctx->minor,
GSS_C_NO_CREDENTIAL,
&gss_context,
target_name,
- &mech_oid,
+ ctx->oid,
req_flags,
0,
NULL, /* no channel bindings */
NULL); /* ignore time_rec */
if (token_ptr != GSS_C_NO_BUFFER)
- (void) gss_release_buffer(&min_stat, &recv_tok);
+ (void) gss_release_buffer(&ctx->minor, &recv_tok);
- if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
- ssh_gssapi_error(&mech_oid, maj_stat, min_stat);
+ if (ctx->major != GSS_S_COMPLETE && ctx->major != GSS_S_CONTINUE_NEEDED) {
+ ssh_gssapi_error(ctx);
/* Send an abort message */
packet_start(SSH_MSG_AUTH_GSSAPI_ABORT);
packet_send();
packet_write_wait();
- (void) gss_release_buffer(&min_stat, &send_tok);
+ (void) gss_release_buffer(&ctx->minor, &send_tok);
}
- if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+ if (ctx->major == GSS_S_CONTINUE_NEEDED) {
debug("Continue needed. Reading response...");
packet_get_all();
token_ptr = &recv_tok;
}
- } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+ } while (ctx->major == GSS_S_CONTINUE_NEEDED);
/* Success */
ret_stat = 1;
wrapped_buf.length=slen; /* safe typecast */
packet_get_all();
- maj_stat = gss_unwrap(&min_stat,
+ ctx->major = gss_unwrap(&ctx->minor,
gss_context,
&wrapped_buf,
&unwrapped_buf,
&conf_state,
&qop_state);
- if (maj_stat != GSS_S_COMPLETE) {
- ssh_gssapi_error(&mech_oid, maj_stat, min_stat);
+ if (ctx->major != GSS_S_COMPLETE) {
+ ssh_gssapi_error(ctx);
packet_disconnect("Verification of SSHD keys through GSSAPI-secured channel failed: "
"Unwrapping of hash failed.");
}
debug("Verified SSHD keys through GSSAPI-secured channel.");
- gss_release_buffer(&min_stat, &unwrapped_buf);
+ gss_release_buffer(&ctx->minor, &unwrapped_buf);
} else {
packet_disconnect("Protocol error during GSSAPI authentication:"
cleanup:
if (target_name != NULL)
- (void) gss_release_name(&min_stat, &target_name);
+ (void) gss_release_name(&ctx->minor, &target_name);
+ if (ctx)
+ ssh_gssapi_delete_ctx(&ctx);
return ret_stat;
}
orig = myproposal[PROPOSAL_KEX_ALGS];
canonhost = xstrdup(get_canonical_hostname(1));
resolve_localhost(&canonhost);
- gss = ssh_gssapi_mechanisms(canonhost);
+ gss = ssh_gssapi_client_mechanisms(canonhost);
xfree(canonhost);
canonhost=NULL;
if (gss) {
/* If we've got GSSAPI algorithms, then we also support the
* 'null' hostkey, as a last resort */
if (options.gss_keyex && 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);
+ 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->server_version_string=server_version_string;
kex->verify_host_key=&verify_host_key_callback;
kex->host=host;
-
#ifdef GSSAPI
kex->options.gss_deleg_creds=options.gss_deleg_creds;
#endif
-
xxx_kex = kex;
dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
void input_gssapi_response(int type, u_int32_t plen, void *ctxt);
void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
void input_gssapi_hash(int type, u_int32_t plen, void *ctxt);
+void input_gssapi_error(int, u_int32_t, void *);
#endif
void userauth(Authctxt *, char *);
int
userauth_gssapi(Authctxt *authctxt)
{
- Gssctxt *gssctxt;
+ 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;
- /* Try each mechanism in turn. Give up if we've tried all
- supported mechanisms.
- */
- if (mech==GSS_LAST_ENTRY) return 0;
-
- /* Initialise as much of our context as we can, so failures can be
- * trapped before sending any packets.
- */
- ssh_gssapi_build_ctx(&gssctxt);
+ /* 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 (ssh_gssapi_import_name(gssctxt,get_canonical_hostname(1))) {
- return(0);
+ 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);
-#ifdef GSI
- if(options.implicit && !(datafellows & SSH_BUG_GSS_EMPTYUSER)) {
- packet_put_cstring("");
- } else {
-#endif
- packet_put_cstring(authctxt->server_user);
-#ifdef GSI
- }
-#endif
+ packet_put_cstring(authctxt->server_user);
packet_put_cstring(authctxt->service);
packet_put_cstring(authctxt->method->name);
+
+ packet_put_int(1);
- /* FIXME: This assumes that our current GSSAPI implementation
- * supports all of the mechanisms listed in supported_mechs.
- * This may not be the case - we should use something along
- * the lines of the code in gss_genr to remove the ones that
- * aren't supported */
+ /* 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);
+ }
- /* Try one GSSAPI mechanism at a time. */
- packet_put_int(1);
- packet_put_string(supported_mechs[mech].oid.elements,
- supported_mechs[mech].oid.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);
- mech++; /* Move to next mechanism for next time. */
+ mech++; /* Move along to next candidate */
return 1;
}
/* Setup our OID */
oidv=packet_get_string(&oidlen);
- ssh_gssapi_set_oid_data(gssctxt,oidv,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,
if (GSS_ERROR(status)) {
/* Start again with next method on list */
debug("Trying to start again");
+ clear_auth_state(authctxt);
userauth(authctxt,NULL);
return;
}
if (GSS_ERROR(status)) {
/* Start again with the next method in the list */
+ clear_auth_state(authctxt);
userauth(authctxt,NULL);
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)
{
{
/* 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);
if (test_flag)
exit(0);
-#ifdef GSSAPI
- ssh_gssapi_clean_env();
-#endif /* GSSAPI */
-
/*
* Clear out any supplemental groups we may have inherited. This
* prevents inadvertent creation of files with bad modes (in the
Buffer buf;
unsigned char *data;
unsigned int data_len;
- extern unsigned char ssh1_key_digest[16]; /* in gss-genr.c */
+ extern unsigned char ssh1_key_digest[16]; /* in auth2-gss.c */
debug("Calculating MD5 hash of server and host keys...");
orig= NULL;
if (options.gss_keyex)
- gss = ssh_server_gssapi_mechanisms();
+ gss = ssh_gssapi_server_mechanisms();
else
gss = NULL;