X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/c677cad690637a1ceaf6e039dcdbd3a267ad3c86..HEAD:/unix/uxgss.c diff --git a/unix/uxgss.c b/unix/uxgss.c index db653f3c..2a9e1296 100644 --- a/unix/uxgss.c +++ b/unix/uxgss.c @@ -1,197 +1,169 @@ -#include -#include #include "putty.h" +#ifndef NO_GSSAPI +#include "pgssapi.h" #include "sshgss.h" -#include "misc.h" +#include "sshgssc.h" + +/* Unix code to set up the GSSAPI library list. */ + +#if !defined NO_LIBDL && !defined NO_GSSAPI + +const int ngsslibs = 4; +const char *const gsslibnames[4] = { + "libgssapi (Heimdal)", + "libgssapi_krb5 (MIT Kerberos)", + "libgss (Sun)", + "User-specified GSSAPI library", +}; +const struct keyvalwhere gsslibkeywords[] = { + { "libgssapi", 0, -1, -1 }, + { "libgssapi_krb5", 1, -1, -1 }, + { "libgss", 2, -1, -1 }, + { "custom", 3, -1, -1 }, +}; + +/* + * Run-time binding against a choice of GSSAPI implementations. We + * try loading several libraries, and produce an entry in + * ssh_gss_libraries[] for each one. + */ + +static void gss_init(struct ssh_gss_library *lib, void *dlhandle, + int id, const char *msg) +{ + lib->id = id; + lib->gsslogmsg = msg; + lib->handle = dlhandle; -#ifndef NO_GSSAPI +#define BIND_GSS_FN(name) \ + lib->u.gssapi.name = (t_gss_##name) dlsym(dlhandle, "gss_" #name) -static gss_OID_desc putty_gss_mech_krb5_desc = - { 9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; -static gss_OID const putty_gss_mech_krb5 = &putty_gss_mech_krb5_desc; + BIND_GSS_FN(delete_sec_context); + BIND_GSS_FN(display_status); + BIND_GSS_FN(get_mic); + BIND_GSS_FN(import_name); + BIND_GSS_FN(init_sec_context); + BIND_GSS_FN(release_buffer); + BIND_GSS_FN(release_cred); + BIND_GSS_FN(release_name); -typedef struct uxSsh_gss_ctx { - OM_uint32 maj_stat; - OM_uint32 min_stat; - gss_ctx_id_t ctx; -} uxSsh_gss_ctx; +#undef BIND_GSS_FN -int ssh_gss_init(void) -{ - /* On Windows this tries to load the SSPI library functions. On - Unix we assume we have GSSAPI at runtime if we were linked with - it at compile time */ - return 1; + ssh_gssapi_bind_fns(lib); } -Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech) +/* Dynamically load gssapi libs. */ +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { - /* Copy constant into mech */ - mech->length = putty_gss_mech_krb5->length; - mech->value = putty_gss_mech_krb5->elements; - - return SSH_GSS_OK; + void *gsslib; + char *gsspath; + struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); + + list->libraries = snewn(4, struct ssh_gss_library); + list->nlibraries = 0; + + /* Heimdal's GSSAPI Library */ + if ((gsslib = dlopen("libgssapi.so.2", RTLD_LAZY)) != NULL) + gss_init(&list->libraries[list->nlibraries++], gsslib, + 0, "Using GSSAPI from libgssapi.so.2"); + + /* MIT Kerberos's GSSAPI Library */ + if ((gsslib = dlopen("libgssapi_krb5.so.2", RTLD_LAZY)) != NULL) + gss_init(&list->libraries[list->nlibraries++], gsslib, + 1, "Using GSSAPI from libgssapi_krb5.so.2"); + + /* Sun's GSSAPI Library */ + if ((gsslib = dlopen("libgss.so.1", RTLD_LAZY)) != NULL) + gss_init(&list->libraries[list->nlibraries++], gsslib, + 2, "Using GSSAPI from libgss.so.1"); + + /* User-specified GSSAPI library */ + gsspath = conf_get_filename(conf, CONF_ssh_gss_custom)->path; + if (*gsspath && (gsslib = dlopen(gsspath, RTLD_LAZY)) != NULL) + gss_init(&list->libraries[list->nlibraries++], gsslib, + 3, dupprintf("Using GSSAPI from user-specified" + " library '%s'", gsspath)); + + return list; } -Ssh_gss_stat ssh_gss_import_name(char *host, - Ssh_gss_name *srv_name) +void ssh_gss_cleanup(struct ssh_gss_liblist *list) { - OM_uint32 min_stat,maj_stat; - gss_buffer_desc host_buf; - char *pStr; - - pStr = dupcat("host@", host, NULL); - - host_buf.value = pStr; - host_buf.length = strlen(pStr); - - maj_stat = gss_import_name(&min_stat, &host_buf, - GSS_C_NT_HOSTBASED_SERVICE, - (gss_name_t *)srv_name); - /* Release buffer */ - sfree(pStr); - if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; - return SSH_GSS_FAILURE; + int i; + + /* + * dlopen and dlclose are defined to employ reference counting + * in the case where the same library is repeatedly dlopened, so + * even in a multiple-sessions-per-process context it's safe to + * naively dlclose everything here without worrying about + * destroying it under the feet of another SSH instance still + * using it. + */ + for (i = 0; i < list->nlibraries; i++) { + dlclose(list->libraries[i].handle); + if (list->libraries[i].id == 3) { + /* The 'custom' id involves a dynamically allocated message. + * Note that we must cast away the 'const' to free it. */ + sfree((char *)list->libraries[i].gsslogmsg); + } + } + sfree(list->libraries); + sfree(list); } -Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx) -{ - uxSsh_gss_ctx *uxctx = snew(uxSsh_gss_ctx); +#elif !defined NO_GSSAPI - uxctx->maj_stat = uxctx->min_stat = GSS_S_COMPLETE; - uxctx->ctx = GSS_C_NO_CONTEXT; - *ctx = (Ssh_gss_ctx) uxctx; +const int ngsslibs = 1; +const char *const gsslibnames[1] = { + "static", +}; +const struct keyvalwhere gsslibkeywords[] = { + { "static", 0, -1, -1 }, +}; - return SSH_GSS_OK; -} +/* + * Link-time binding against GSSAPI. Here we just construct a single + * library structure containing pointers to the functions we linked + * against. + */ -Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx, - Ssh_gss_name srv_name, - int to_deleg, - Ssh_gss_buf *recv_tok, - Ssh_gss_buf *send_tok) -{ - uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx*) *ctx; - OM_uint32 ret_flags; - - if (to_deleg) to_deleg = GSS_C_DELEG_FLAG; - uxctx->maj_stat = gss_init_sec_context(&uxctx->min_stat, - GSS_C_NO_CREDENTIAL, - &uxctx->ctx, - (gss_name_t) srv_name, - (gss_OID) putty_gss_mech_krb5, - GSS_C_MUTUAL_FLAG | - GSS_C_INTEG_FLAG | to_deleg, - 0, - GSS_C_NO_CHANNEL_BINDINGS, - recv_tok, - NULL, /* ignore mech type */ - send_tok, - &ret_flags, - NULL); /* ignore time_rec */ - - if (uxctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE; - if (uxctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED; - return SSH_GSS_FAILURE; -} +#include -Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf) +/* Dynamically load gssapi libs. */ +struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { - uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) ctx; - OM_uint32 lmin,lmax; - OM_uint32 ccc; - gss_buffer_desc msg_maj=GSS_C_EMPTY_BUFFER; - gss_buffer_desc msg_min=GSS_C_EMPTY_BUFFER; + struct ssh_gss_liblist *list = snew(struct ssh_gss_liblist); - /* Return empty buffer in case of failure */ - SSH_GSS_CLEAR_BUF(buf); + list->libraries = snew(struct ssh_gss_library); + list->nlibraries = 1; - /* get first mesg from GSS */ - ccc=0; - lmax=gss_display_status(&lmin,uxctx->maj_stat,GSS_C_GSS_CODE,(gss_OID) putty_gss_mech_krb5,&ccc,&msg_maj); + list->libraries[0].gsslogmsg = "Using statically linked GSSAPI"; - if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE; +#define BIND_GSS_FN(name) \ + list->libraries[0].u.gssapi.name = (t_gss_##name) gss_##name - /* get first mesg from Kerberos */ - ccc=0; - lmax=gss_display_status(&lmin,uxctx->min_stat,GSS_C_MECH_CODE,(gss_OID) putty_gss_mech_krb5,&ccc,&msg_min); + BIND_GSS_FN(delete_sec_context); + BIND_GSS_FN(display_status); + BIND_GSS_FN(get_mic); + BIND_GSS_FN(import_name); + BIND_GSS_FN(init_sec_context); + BIND_GSS_FN(release_buffer); + BIND_GSS_FN(release_cred); + BIND_GSS_FN(release_name); - if (lmax != GSS_S_COMPLETE) { - gss_release_buffer(&lmin, &msg_maj); - return SSH_GSS_FAILURE; - } +#undef BIND_GSS_FN - /* copy data into buffer */ - buf->length = msg_maj.length + msg_min.length + 1; - buf->value = snewn(buf->length + 1, char); - - /* copy mem */ - memcpy((char *)buf->value, msg_maj.value, msg_maj.length); - ((char *)buf->value)[msg_maj.length] = ' '; - memcpy((char *)buf->value + msg_maj.length + 1, msg_min.value, msg_min.length); - ((char *)buf->value)[buf->length] = 0; - /* free mem & exit */ - gss_release_buffer(&lmin, &msg_maj); - gss_release_buffer(&lmin, &msg_min); - return SSH_GSS_OK; -} + ssh_gssapi_bind_fns(&list->libraries[0]); -Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok) -{ - OM_uint32 min_stat,maj_stat; - maj_stat = gss_release_buffer(&min_stat, send_tok); - - if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; - return SSH_GSS_FAILURE; + return list; } -Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx) +void ssh_gss_cleanup(struct ssh_gss_liblist *list) { - uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) *ctx; - OM_uint32 min_stat; - OM_uint32 maj_stat=GSS_S_COMPLETE; - - if (uxctx == NULL) return SSH_GSS_FAILURE; - if (uxctx->ctx != GSS_C_NO_CONTEXT) - maj_stat = gss_delete_sec_context(&min_stat,&uxctx->ctx,GSS_C_NO_BUFFER); - sfree(uxctx); - - if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; - return SSH_GSS_FAILURE; + sfree(list->libraries); + sfree(list); } +#endif /* NO_LIBDL */ -Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name) -{ - OM_uint32 min_stat,maj_stat; - maj_stat = gss_release_name(&min_stat, (gss_name_t *) srv_name); - - if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; - return SSH_GSS_FAILURE; -} - -Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf, - Ssh_gss_buf *hash) -{ - uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) ctx; - if (uxctx == NULL) return SSH_GSS_FAILURE; - return gss_get_mic(&(uxctx->min_stat), uxctx->ctx, 0, buf, hash); -} - -Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash) -{ - /* On Unix this is the same freeing process as ssh_gss_free_tok. */ - return ssh_gss_free_tok(hash); -} - -#else - -/* Dummy function so this source file defines something if NO_GSSAPI - is defined. */ - -int ssh_gss_init(void) -{ - return 1; -} - -#endif +#endif /* NO_GSSAPI */