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:
275
unix/uxgss.c
275
unix/uxgss.c
@ -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 */
|
||||
|
Reference in New Issue
Block a user