X-Git-Url: http://andersk.mit.edu/gitweb/gssapi-openssh.git/blobdiff_plain/905081a434bc720834aa516f11ad6c20c3405034..HEAD:/openssh/gss-genr.c diff --git a/openssh/gss-genr.c b/openssh/gss-genr.c index 087690c..dff0921 100644 --- a/openssh/gss-genr.c +++ b/openssh/gss-genr.c @@ -1,5 +1,8 @@ +/* $OpenBSD: gss-genr.c,v 1.20 2009/06/22 05:39:28 dtucker Exp $ */ + /* - * Copyright (c) 2001 Simon Wilkinson. All rights reserved. * + * Copyright (c) 2001-2009 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: @@ -25,469 +28,536 @@ #ifdef GSSAPI -#include "ssh.h" -#include "ssh2.h" +#include +#include + +#include +#include +#include + #include "xmalloc.h" #include "buffer.h" -#include "bufaux.h" -#include "packet.h" -#include "compat.h" -#include +#include "log.h" +#include "canohost.h" +#include "ssh2.h" #include "cipher.h" +#include "key.h" #include "kex.h" -#include "log.h" -#include "compat.h" - -#include +#include #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; +extern u_char *session_id2; +extern u_int session_id2_len; -/* 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" +typedef struct { + char *encoded; + gss_OID oid; +} ssh_gss_kex_mapping; + +/* + * XXX - It would be nice to find a more elegant way of handling the + * XXX passing of the key exchange context to the userauth routines */ -/* These must be in the same order as ssh_gss_id, in ssh-gss.h */ +Gssctxt *gss_kex_context = NULL; -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; - -/* 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) +static ssh_gss_kex_mapping *gss_enc2oid = NULL; + +int +ssh_gssapi_oid_table_ok() { + return (gss_enc2oid != NULL); +} + +/* + * Return a list of the gss-group1-sha1 mechanisms supported by this program * - * 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. + * We test mechanisms to ensure that we can use them, to avoid starting + * a key exchange with a bad mechanism */ - -char * -ssh_gssapi_mechanisms(int server,char *host) { - gss_OID_set supported; - OM_uint32 maj_status, min_status; - Buffer buf; - int i = 0; - int present; - char * mechs; - Gssctxt ctx; - gss_buffer_desc token; - - if (datafellows & SSH_OLD_GSSAPI) return NULL; - - gss_indicate_mechs(&min_status, &supported); - - buffer_init(&buf); - do { - if ((maj_status=gss_test_oid_set_member(&min_status, - &supported_mechs[i].oid, - supported, - &present))) { - present=0; +char * +ssh_gssapi_client_mechanisms(const char *host, const char *client) { + gss_OID_set gss_supported; + OM_uint32 min_status; + + if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) + return NULL; + + return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, + host, client)); +} + +char * +ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, + const char *host, const char *client) { + Buffer buf; + size_t i; + int oidpos, enclen; + char *mechs, *encoded; + u_char digest[EVP_MAX_MD_SIZE]; + char deroid[2]; + const EVP_MD *evp_md = EVP_md5(); + EVP_MD_CTX md; + + if (gss_enc2oid != NULL) { + for (i = 0; gss_enc2oid[i].encoded != NULL; i++) + xfree(gss_enc2oid[i].encoded); + xfree(gss_enc2oid); + } + + gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * + (gss_supported->count + 1)); + + buffer_init(&buf); + + oidpos = 0; + for (i = 0; i < gss_supported->count; i++) { + if (gss_supported->elements[i].length < 128 && + (*check)(NULL, &(gss_supported->elements[i]), host, client)) { + + deroid[0] = SSH_GSS_OIDTYPE; + deroid[1] = gss_supported->elements[i].length; + + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, deroid, 2); + EVP_DigestUpdate(&md, + gss_supported->elements[i].elements, + gss_supported->elements[i].length); + EVP_DigestFinal(&md, digest, NULL); + + 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_GEX_SHA1_ID, + sizeof(KEX_GSS_GEX_SHA1_ID) - 1); + buffer_append(&buf, encoded, enclen); + buffer_put_char(&buf, ','); + buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, + sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); + buffer_append(&buf, encoded, enclen); + buffer_put_char(&buf, ','); + buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, + sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); + buffer_append(&buf, encoded, enclen); + + gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); + gss_enc2oid[oidpos].encoded = encoded; + oidpos++; } - if (present) { - ssh_gssapi_build_ctx(&ctx); - ssh_gssapi_set_oid(&ctx,&supported_mechs[i].oid); - if (server) { - if (ssh_gssapi_acquire_cred(&ctx)) { - ssh_gssapi_delete_ctx(&ctx); - continue; - } - } - else { /* client */ - if (ssh_gssapi_import_name(&ctx,host)) - continue; - maj_status=ssh_gssapi_init_ctx(&ctx, 0, - GSS_C_NO_BUFFER, - &token, - NULL); - ssh_gssapi_delete_ctx(&ctx); - if (GSS_ERROR(maj_status)) { - continue; - } - } - - /* Append gss_group1_sha1_x to our list */ - buffer_append(&buf, gssprefix, - strlen(gssprefix)); - buffer_append(&buf, supported_mechs[i].enc_name, - strlen(supported_mechs[i].enc_name)); - } - } while (supported_mechs[++i].name != NULL); - - buffer_put_char(&buf,'\0'); - - mechs=xmalloc(buffer_len(&buf)); - buffer_get(&buf,mechs,buffer_len(&buf)); + } + 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); + + if (strlen(mechs) == 0) { + xfree(mechs); + mechs = NULL; + } + + return (mechs); } -void ssh_gssapi_supported_oids(gss_OID_set *oidset) { - enum ssh_gss_id i =0; - OM_uint32 maj_status,min_status; - int present; - gss_OID_set supported; +gss_OID +ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { + int i = 0; - gss_create_empty_oid_set(&min_status,oidset); - gss_indicate_mechs(&min_status, &supported); - - while (supported_mechs[i].name!=NULL) { - if ((maj_status=gss_test_oid_set_member(&min_status, - &supported_mechs[i].oid, - supported, - &present))) { - present=0; - } - if (present) { - gss_add_oid_set_member(&min_status, - &supported_mechs[i].oid, - oidset); - } - i++; + switch (kex_type) { + case KEX_GSS_GRP1_SHA1: + if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) + return GSS_C_NO_OID; + name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; + break; + case KEX_GSS_GRP14_SHA1: + if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) + return GSS_C_NO_OID; + name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; + break; + case KEX_GSS_GEX_SHA1: + if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) + return GSS_C_NO_OID; + name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; + break; + default: + return GSS_C_NO_OID; } -} -/* Set the contexts OID from a data stream */ -void ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) { - if (ctx->oid != GSS_C_NO_OID) { - xfree(ctx->oid->elements); - xfree(ctx->oid); - } - ctx->oid=xmalloc(sizeof(gss_OID_desc)); - ctx->oid->length=len; - ctx->oid->elements=xmalloc(len); - memcpy(ctx->oid->elements,data,len); + while (gss_enc2oid[i].encoded != NULL && + strcmp(name, gss_enc2oid[i].encoded) != 0) + i++; + + if (gss_enc2oid[i].oid != NULL && ctx != NULL) + ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); + + return gss_enc2oid[i].oid; } -/* Set the contexts OID */ -void ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) { - ssh_gssapi_set_oid_data(ctx,oid->elements,oid->length); +/* 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); } -/* 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 && - supported_mechs[i].oid.length != ctxt->oid->length && - (memcmp(supported_mechs[i].oid.elements, - ctxt->oid->elements,ctxt->oid->length) !=0)) { - i++; +/* Set the contexts OID from a data stream */ +void +ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) +{ + if (ctx->oid != GSS_C_NO_OID) { + xfree(ctx->oid->elements); + xfree(ctx->oid); } - return(i); + ctx->oid = xmalloc(sizeof(gss_OID_desc)); + ctx->oid->length = len; + ctx->oid->elements = xmalloc(len); + memcpy(ctx->oid->elements, data, len); } -/* Set the GSS context's OID to the oid indicated by the given key exchange - * name. */ -int ssh_gssapi_id_kex(Gssctxt *ctx, char *name) { - enum ssh_gss_id i=0; - - if (strncmp(name, gssprefix, strlen(gssprefix)-1) !=0) { - return(1); - } - - 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 (1); - - ssh_gssapi_set_oid(ctx,&supported_mechs[i].oid); - - return 0; +/* Set the contexts OID */ +void +ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) +{ + ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length); } - /* All this effort to report an error ... */ void -ssh_gssapi_error(OM_uint32 major_status,OM_uint32 minor_status) { - OM_uint32 lmaj, lmin; - gss_buffer_desc msg; - OM_uint32 ctx; - - ctx = 0; +ssh_gssapi_error(Gssctxt *ctxt) +{ + char *s; + + s = ssh_gssapi_last_error(ctxt, NULL, NULL); + debug("%s", s); + xfree(s); +} + +char * +ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, + OM_uint32 *minor_status) +{ + OM_uint32 lmin; + gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; + 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, - GSS_C_NULL_OID, - &ctx, &msg); - if (lmaj == GSS_S_COMPLETE) { - debug((char *)msg.value); - (void) gss_release_buffer(&lmin, &msg); - } - } while (ctx!=0); - - /* The mechanism specific error */ - do { - lmaj = gss_display_status(&lmin, minor_status, - GSS_C_MECH_CODE, - GSS_C_NULL_OID, - &ctx, &msg); - if (lmaj == GSS_S_COMPLETE) { - debug((char *)msg.value); - (void) gss_release_buffer(&lmin, &msg); - } - } while (ctx!=0); + do { + 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 { + 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 +/* + * Initialise our GSSAPI context. We use this opaque structure to contain all * of the data which both the client and server need to persist across * {accept,init}_sec_context calls, so that when we do it from the userauth * stuff life is a little easier */ void -ssh_gssapi_build_ctx(Gssctxt *ctx) +ssh_gssapi_build_ctx(Gssctxt **ctx) { - ctx->context=GSS_C_NO_CONTEXT; - ctx->name=GSS_C_NO_NAME; - ctx->oid=GSS_C_NO_OID; - ctx->creds=GSS_C_NO_CREDENTIAL; - ctx->client=GSS_C_NO_NAME; - ctx->client_creds=GSS_C_NO_CREDENTIAL; + *ctx = xcalloc(1, sizeof (Gssctxt)); + (*ctx)->context = GSS_C_NO_CONTEXT; + (*ctx)->name = GSS_C_NO_NAME; + (*ctx)->oid = GSS_C_NO_OID; + (*ctx)->creds = GSS_C_NO_CREDENTIAL; + (*ctx)->client = GSS_C_NO_NAME; + (*ctx)->client_creds = GSS_C_NO_CREDENTIAL; } /* Delete our context, providing it has been built correctly */ void -ssh_gssapi_delete_ctx(Gssctxt *ctx) +ssh_gssapi_delete_ctx(Gssctxt **ctx) { +#if !defined(MECHGLUE) OM_uint32 ms; - - if (ctx->context != GSS_C_NO_CONTEXT) - gss_delete_sec_context(&ms,&ctx->context,GSS_C_NO_BUFFER); - if (ctx->name != GSS_C_NO_NAME) - gss_release_name(&ms,&ctx->name); - if (ctx->oid != GSS_C_NO_OID) { - xfree(ctx->oid->elements); - xfree(ctx->oid); - ctx->oid = GSS_C_NO_OID; +#endif + + if ((*ctx) == NULL) + return; +#if !defined(MECHGLUE) /* mechglue has some memory management issues */ + if ((*ctx)->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER); + if ((*ctx)->name != GSS_C_NO_NAME) + gss_release_name(&ms, &(*ctx)->name); + if ((*ctx)->oid != GSS_C_NO_OID) { + xfree((*ctx)->oid->elements); + xfree((*ctx)->oid); + (*ctx)->oid = GSS_C_NO_OID; } - if (ctx->creds != GSS_C_NO_CREDENTIAL) - gss_release_cred(&ms,&ctx->creds); - if (ctx->client != GSS_C_NO_NAME) - gss_release_name(&ms,&ctx->client); - if (ctx->client_creds != GSS_C_NO_CREDENTIAL) - gss_release_cred(&ms,&ctx->client_creds); + if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&ms, &(*ctx)->creds); + if ((*ctx)->client != GSS_C_NO_NAME) + gss_release_name(&ms, &(*ctx)->client); + if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&ms, &(*ctx)->client_creds); +#endif + + xfree(*ctx); + *ctx = NULL; } -/* Wrapper to init_sec_context +/* + * Wrapper to init_sec_context * Requires that the context contains: * oid - * server name (from ssh_gssapi_import_name) + * server name (from ssh_gssapi_import_name) */ -OM_uint32 +OM_uint32 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, - gss_buffer_desc* send_tok, OM_uint32 *flags) + gss_buffer_desc* send_tok, OM_uint32 *flags) { - OM_uint32 maj_status, min_status; int deleg_flag = 0; - + if (deleg_creds) { - deleg_flag=GSS_C_DELEG_FLAG; + deleg_flag = GSS_C_DELEG_FLAG; debug("Delegating credentials"); } - - maj_status=gss_init_sec_context(&min_status, - GSS_C_NO_CREDENTIAL, /* def. cred */ - &ctx->context, - ctx->name, - ctx->oid, - GSS_C_MUTUAL_FLAG | - GSS_C_INTEG_FLAG | - deleg_flag, - 0, /* default lifetime */ - NULL, /* no channel bindings */ - recv_tok, - NULL, - send_tok, - flags, - NULL); - ctx->status=maj_status; - if (GSS_ERROR(maj_status)) { - ssh_gssapi_error(maj_status,min_status); - } - return(maj_status); -} -/* Wrapper arround accept_sec_context - * Requires that the context contains: - * oid - * credentials (from ssh_gssapi_acquire_cred) - */ -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; - gss_OID mech; - - maj_status=gss_accept_sec_context(&min_status, - &ctx->context, - ctx->creds, - recv_tok, - GSS_C_NO_CHANNEL_BINDINGS, - &ctx->client, - &mech, - send_tok, - flags, - NULL, - &ctx->client_creds); - if (GSS_ERROR(maj_status)) { - ssh_gssapi_error(maj_status,min_status); - } - - if (ctx->client_creds) { - debug("Received some client credentials"); - } else { - debug("Got no client credentials"); - } + ctx->major = gss_init_sec_context(&ctx->minor, + ctx->client_creds, &ctx->context, ctx->name, ctx->oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, + 0, NULL, recv_tok, NULL, send_tok, flags, NULL); - /* FIXME: We should check that the mechanism thats being used is - * the one that we asked for (in ctx->oid) */ + if (GSS_ERROR(ctx->major)) + ssh_gssapi_error(ctx); - ctx->status=maj_status; - - return(maj_status); + return (ctx->major); } /* Create a service name for the given host */ OM_uint32 -ssh_gssapi_import_name(Gssctxt *ctx,char *host) { +ssh_gssapi_import_name(Gssctxt *ctx, const char *host) +{ gss_buffer_desc gssbuf; - OM_uint32 maj_status, min_status; - struct hostent *hostinfo = NULL; char *xhost; - + char *val; + /* Make a copy of the host name, in case it was returned by a * previous call to gethostbyname(). */ xhost = xstrdup(host); /* Make sure we have the FQDN. Some GSSAPI implementations don't do * this for us themselves */ + resolve_localhost(&xhost); - hostinfo = gethostbyname(xhost); - - if ((hostinfo == NULL) || (hostinfo->h_name == NULL)) { - debug("Unable to get FQDN for \"%s\"", xhost); - } else { - xfree(xhost); - xhost = hostinfo->h_name; - } - - gssbuf.length = sizeof("host@")+strlen(xhost); - - gssbuf.value = xmalloc(gssbuf.length); - if (gssbuf.value == NULL) { - return(-1); - } - snprintf(gssbuf.value,gssbuf.length,"host@%s",xhost); - if ((maj_status=gss_import_name(&min_status, - &gssbuf, - GSS_C_NT_HOSTBASED_SERVICE, - &ctx->name))) { - ssh_gssapi_error(maj_status,min_status); - } - + xasprintf(&val, "host@%s", xhost); + gssbuf.value = val; + gssbuf.length = strlen(gssbuf.value); + + if ((ctx->major = gss_import_name(&ctx->minor, + &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name))) + 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. - * Requires that the context structure contains a valid OID - */ OM_uint32 -ssh_gssapi_acquire_cred(Gssctxt *ctx) { - OM_uint32 maj_status, min_status; - char lname[MAXHOSTNAMELEN]; +ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) +{ + gss_buffer_desc gssbuf; + gss_name_t gssname; + OM_uint32 status; gss_OID_set oidset; - - gss_create_empty_oid_set(&min_status,&oidset); - gss_add_oid_set_member(&min_status,ctx->oid,&oidset); - - if (gethostname(lname, MAXHOSTNAMELEN)) { - return(-1); - } - if ((maj_status=ssh_gssapi_import_name(ctx,lname))) { - return(maj_status); - } - if ((maj_status=gss_acquire_cred(&min_status, - ctx->name, - 0, - oidset, - GSS_C_ACCEPT, - &ctx->creds, - NULL, - NULL))) { - ssh_gssapi_error(maj_status,min_status); - } - - gss_release_oid_set(&min_status, &oidset); - return(maj_status); + gssbuf.value = (void *) name; + gssbuf.length = strlen(gssbuf.value); + + gss_create_empty_oid_set(&status, &oidset); + gss_add_oid_set_member(&status, ctx->oid, &oidset); + + ctx->major = gss_import_name(&ctx->minor, &gssbuf, + GSS_C_NT_USER_NAME, &gssname); + + if (!ctx->major) + ctx->major = gss_acquire_cred(&ctx->minor, + gssname, 0, oidset, GSS_C_INITIATE, + &ctx->client_creds, NULL, NULL); + + gss_release_name(&status, &gssname); + gss_release_oid_set(&status, &oidset); + + if (ctx->major) + ssh_gssapi_error(ctx); + + return(ctx->major); +} + +OM_uint32 +ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) +{ + if (ctx == NULL) + return -1; + + if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash))) + ssh_gssapi_error(ctx); + + return (ctx->major); +} + +/* Priviledged when used by server */ +OM_uint32 +ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) +{ + if (ctx == NULL) + return -1; + + ctx->major = gss_verify_mic(&ctx->minor, ctx->context, + gssbuf, gssmic, NULL); + + return (ctx->major); } -/* Extract the client details from a given context. This can only reliably - * be called once for a context */ +void +ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, + const char *context) +{ + buffer_init(b); + buffer_put_string(b, session_id2, session_id2_len); + buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(b, user); + buffer_put_cstring(b, service); + buffer_put_cstring(b, context); +} -OM_uint32 -ssh_gssapi_getclient(Gssctxt *ctx, enum ssh_gss_id *type, - gss_buffer_desc *name, gss_cred_id_t *creds) { +int +ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, + const char *client) +{ + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; + Gssctxt *intctx = NULL; + + if (ctx == NULL) + ctx = &intctx; + + /* RFC 4462 says we MUST NOT do SPNEGO */ + if (oid->length == spnego_oid.length && + (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) + return 0; /* false */ + + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + major = ssh_gssapi_import_name(*ctx, host); + + if (!GSS_ERROR(major) && client) + major = ssh_gssapi_client_identity(*ctx, client); + + if (!GSS_ERROR(major)) { + major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + NULL); + gss_release_buffer(&minor, &token); + if ((*ctx)->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor, &(*ctx)->context, + GSS_C_NO_BUFFER); + } - OM_uint32 maj_status,min_status; + if (GSS_ERROR(major) || intctx != NULL) + ssh_gssapi_delete_ctx(ctx); + + return (!GSS_ERROR(major)); +} + +int +ssh_gssapi_credentials_updated(Gssctxt *ctxt) { + static gss_name_t saved_name = GSS_C_NO_NAME; + static OM_uint32 saved_lifetime = 0; + static gss_OID saved_mech = GSS_C_NO_OID; + static gss_name_t name; + static OM_uint32 last_call = 0; + OM_uint32 lifetime, now, major, minor; + int equal; - *type=ssh_gssapi_get_ctype(ctx); - if ((maj_status=gss_display_name(&min_status,ctx->client,name,NULL))) { - ssh_gssapi_error(maj_status,min_status); + now = time(NULL); + + if (ctxt) { + debug("Rekey has happened - updating saved versions"); + + if (saved_name != GSS_C_NO_NAME) + gss_release_name(&minor, &saved_name); + + major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, + &saved_name, &saved_lifetime, NULL, NULL); + + if (!GSS_ERROR(major)) { + saved_mech = ctxt->oid; + saved_lifetime+= now; + } else { + /* Handle the error */ + } + return 0; } + + if (now - last_call < 10) + return 0; + + last_call = now; + + if (saved_mech == GSS_C_NO_OID) + return 0; - /* 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); + major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, + &name, &lifetime, NULL, NULL); + if (major == GSS_S_CREDENTIALS_EXPIRED) + return 0; + else if (GSS_ERROR(major)) + return 0; + + major = gss_compare_name(&minor, saved_name, name, &equal); + gss_release_name(&minor, &name); + if (GSS_ERROR(major)) + return 0; + + if (equal && (saved_lifetime < lifetime + now - 10)) + return 1; + + return 0; } - + #endif /* GSSAPI */