1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-07-03 04:22:47 -05:00

Patch from Alejandro Sedeno, somewhat modified by me, which

reorganises the GSSAPI support so that it handles alternative
implementations of the GSS-API. In particular, this means PuTTY can
now talk to MIT Kerberos for Windows instead of being limited to
SSPI. I don't know for sure whether further tweaking will be needed
(to the UI, most likely, or to automatic selection of credentials),
but testing reports suggest it's now at least worth committing to
trunk to get it more widely tested.

[originally from svn r8952]
This commit is contained in:
Simon Tatham
2010-05-19 18:22:17 +00:00
parent f2b737cdd6
commit 99fffd6ed3
21 changed files with 1148 additions and 303 deletions

View File

@ -1,197 +1,118 @@
#include "putty.h"
#ifndef NO_GSSAPI
#include <string.h>
#include <gssapi/gssapi.h>
#include "pgssapi.h"
#include "sshgss.h"
#include "misc.h"
#include "sshgssc.h"
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;
/* Unix code to set up the GSSAPI library list. */
typedef struct uxSsh_gss_ctx {
OM_uint32 maj_stat;
OM_uint32 min_stat;
gss_ctx_id_t ctx;
} uxSsh_gss_ctx;
struct ssh_gss_library ssh_gss_libraries[3];
int n_ssh_gss_libraries = 0;
static int initialised = FALSE;
int ssh_gss_init(void)
const int ngsslibs = 3;
const char *const gsslibnames[3] = {
"libgssapi (Heimdal)",
"libgssapi_krb5 (MIT Kerberos)",
"libgss (Sun)",
};
const struct keyval gsslibkeywords[] = {
{ "libgssapi", 0 },
{ "libgssapi_krb5", 1 },
{ "libgss", 2 },
};
#ifndef NO_LIBDL
/*
* 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)
{
/* 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;
lib->id = id;
lib->gsslogmsg = msg;
#define BIND_GSS_FN(name) \
lib->u.gssapi.name = (t_gss_##name) dlsym(dlhandle, "gss_" #name)
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);
#undef BIND_GSS_FN
ssh_gssapi_bind_fns(lib);
}
Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech)
/* Dynamically load gssapi libs. */
void ssh_gss_init(void)
{
/* Copy constant into mech */
mech->length = putty_gss_mech_krb5->length;
mech->value = putty_gss_mech_krb5->elements;
void *gsslib;
return SSH_GSS_OK;
if (initialised) return;
initialised = TRUE;
/* Heimdal's GSSAPI Library */
if ((gsslib = dlopen("libgssapi.so.2", RTLD_LAZY)) != NULL)
gss_init(&ssh_gss_libraries[n_ssh_gss_libraries++], 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(&ssh_gss_libraries[n_ssh_gss_libraries++], gsslib,
1, "Using GSSAPI from libgssapi_krb5.so.2");
/* Sun's GSSAPI Library */
if ((gsslib = dlopen("libgss.so.1", RTLD_LAZY)) != NULL)
gss_init(&ssh_gss_libraries[n_ssh_gss_libraries++], gsslib,
2, "Using GSSAPI from libgss.so.1");
}
Ssh_gss_stat ssh_gss_import_name(char *host,
Ssh_gss_name *srv_name)
#else /* NO_LIBDL */
/*
* Link-time binding against GSSAPI. Here we just construct a single
* library structure containing pointers to the functions we linked
* against.
*/
#include <gssapi/gssapi.h>
/* Dynamically load gssapi libs. */
void ssh_gss_init(void)
{
OM_uint32 min_stat,maj_stat;
gss_buffer_desc host_buf;
char *pStr;
if (initialised) return;
initialised = TRUE;
pStr = dupcat("host@", host, NULL);
n_ssh_gss_libraries = 1;
ssh_gss_libraries[0].gsslogmsg = "Using statically linked GSSAPI";
host_buf.value = pStr;
host_buf.length = strlen(pStr);
#define BIND_GSS_FN(name) \
ssh_gss_libraries[0].u.gssapi.name = (t_gss_##name) gss_##name
maj_stat = gss_import_name(&min_stat, &host_buf,
GSS_C_NT_HOSTBASED_SERVICE, srv_name);
/* Release buffer */
sfree(pStr);
if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
return SSH_GSS_FAILURE;
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);
#undef BIND_GSS_FN
ssh_gssapi_bind_fns(&ssh_gss_libraries[0]);
}
Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx)
{
uxSsh_gss_ctx *uxctx = snew(uxSsh_gss_ctx);
#endif /* NO_LIBDL */
uxctx->maj_stat = uxctx->min_stat = GSS_S_COMPLETE;
uxctx->ctx = GSS_C_NO_CONTEXT;
*ctx = (Ssh_gss_ctx) uxctx;
return SSH_GSS_OK;
}
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,
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;
}
Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
{
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;
/* Return empty buffer in case of failure */
SSH_GSS_CLEAR_BUF(buf);
/* 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);
if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE;
/* 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);
if (lmax != GSS_S_COMPLETE) {
gss_release_buffer(&lmin, &msg_maj);
return SSH_GSS_FAILURE;
}
/* 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_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;
}
Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
{
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;
}
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, 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 */