1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-06-30 19:12:48 -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

@ -17,18 +17,22 @@ else
fi
AC_SUBST(PUTTYCFLAGS)
AC_ARG_WITH(gssapi,
[ --without-gssapi disable GSS-API support])
AC_ARG_WITH([gssapi],
[AS_HELP_STRING([--without-gssapi],
[disable GSSAPI support])],
[],
[with_gssapi=yes])
WITH_GSSAPI=
AS_IF([test "x$with_gssapi" != xno],
[AC_DEFINE([WITH_GSSAPI], [1], [Define if building with GSSAPI support.])])
AC_CHECK_HEADERS([utmpx.h sys/select.h],,,[
#include <sys/types.h>
#include <utmp.h>])
if test "$with_gssapi" != "no"; then
AC_CHECK_HEADERS([gssapi/gssapi.h])
fi
# Look for both GTK 1 and GTK 2.
AM_PATH_GTK([1.2.0], [gtk=1], [gtk=none])
# AM_PATH_GTK([1.2.0], [gtk=1], [gtk=none])
AM_PATH_GTK_2_0([2.0.0], [gtk=2], [])
if test "$gtk" = "none"; then
all_targets="all-cli"
@ -47,9 +51,17 @@ fi
AC_SUBST([all_targets])
AC_SEARCH_LIBS([socket], [xnet])
if test "$with_gssapi" != "no"; then
AC_SEARCH_LIBS([gss_init_sec_context], [gssapi gssapi_krb5 gss])
fi
AS_IF([test "x$with_gssapi" != xno],
[AC_SEARCH_LIBS(
[dlopen],[dl],
[],
[AC_DEFINE([NO_LIBDL], [1], [Define if we could not find libdl.])
AC_CHECK_HEADERS([gssapi/gssapi.h])
AC_SEARCH_LIBS(
[gss_init_sec_context],[gssapi gssapi_krb5 gss],
[],
[AC_DEFINE([NO_GSSAPI_LIB], [1], [Define if we could not find a gssapi library])])])])
AC_CHECK_LIB(X11, XOpenDisplay)
@ -84,7 +96,12 @@ AH_BOTTOM([
#ifndef HAVE_PANGO_FONT_MAP_LIST_FAMILIES
# define PANGO_PRE_1POINT6
#endif
#ifndef HAVE_GSSAPI_GSSAPI_H
#if !defined(WITH_GSSAPI)
# define NO_GSSAPI
#endif
#if !defined(NO_GSSAPI) && defined(NO_LIBDL)
# if !defined(HAVE_GSSAPI_GSSAPI_H) || defined(NO_GSSAPI_LIB)
# define NO_GSSAPI
# endif
#endif
])

View File

@ -6,6 +6,10 @@
#endif
#include <stdio.h> /* for FILENAME_MAX */
#include <stdint.h> /* C99 int types */
#ifndef NO_LIBDL
#include <dlfcn.h> /* Dynamic library loading */
#endif /* NO_LIBDL */
#include "charset.h"
struct Filename {
@ -24,6 +28,9 @@ typedef int OSSocket;
extern Backend pty_backend;
typedef uint32_t uint32; /* C99: uint32_t defined in stdint.h */
#define PUTTY_UINT32_DEFINED
/*
* Under GTK, we send MA_CLICK _and_ MA_2CLK, or MA_CLICK _and_
* MA_3CLK, when a button is pressed for the second or third time.
@ -60,16 +67,6 @@ extern long tickcount_offset;
#define WCHAR wchar_t
#define BYTE unsigned char
#ifndef NO_GSSAPI
/*
* GSS-API stuff
*/
#include <gssapi/gssapi.h>
typedef gss_buffer_desc Ssh_gss_buf;
#define SSH_GSS_EMPTY_BUF GSS_C_EMPTY_BUFFER
typedef gss_name_t Ssh_gss_name;
#endif
/*
* Unix-specific global flag
*

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 */

View File

@ -220,7 +220,7 @@ void close_settings_w(void *handle)
* FIXME: the above comment is a bit out of date. Did it happen?
*/
struct keyval {
struct skeyval {
const char *key;
const char *value;
};
@ -229,15 +229,15 @@ static tree234 *xrmtree = NULL;
int keycmp(void *av, void *bv)
{
struct keyval *a = (struct keyval *)av;
struct keyval *b = (struct keyval *)bv;
struct skeyval *a = (struct skeyval *)av;
struct skeyval *b = (struct skeyval *)bv;
return strcmp(a->key, b->key);
}
void provide_xrm_string(char *string)
{
char *p, *q, *key;
struct keyval *xrms, *ret;
struct skeyval *xrms, *ret;
p = q = strchr(string, ':');
if (!q) {
@ -248,7 +248,7 @@ void provide_xrm_string(char *string)
q++;
while (p > string && p[-1] != '.' && p[-1] != '*')
p--;
xrms = snew(struct keyval);
xrms = snew(struct skeyval);
key = snewn(q-p, char);
memcpy(key, p, q-p);
key[q-p-1] = '\0';
@ -270,7 +270,7 @@ void provide_xrm_string(char *string)
const char *get_setting(const char *key)
{
struct keyval tmp, *ret;
struct skeyval tmp, *ret;
tmp.key = key;
if (xrmtree) {
ret = find234(xrmtree, &tmp, NULL);
@ -297,14 +297,14 @@ void *open_settings_r(const char *sessionname)
while ( (line = fgetline(fp)) ) {
char *value = strchr(line, '=');
struct keyval *kv;
struct skeyval *kv;
if (!value)
continue;
*value++ = '\0';
value[strcspn(value, "\r\n")] = '\0'; /* trim trailing NL */
kv = snew(struct keyval);
kv = snew(struct skeyval);
kv->key = dupstr(line);
kv->value = dupstr(value);
add234(ret, kv);
@ -321,7 +321,7 @@ char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)
{
tree234 *tree = (tree234 *)handle;
const char *val;
struct keyval tmp, *kv;
struct skeyval tmp, *kv;
tmp.key = key;
if (tree != NULL &&
@ -344,7 +344,7 @@ int read_setting_i(void *handle, const char *key, int defvalue)
{
tree234 *tree = (tree234 *)handle;
const char *val;
struct keyval tmp, *kv;
struct skeyval tmp, *kv;
tmp.key = key;
if (tree != NULL &&
@ -416,7 +416,7 @@ void write_setting_filename(void *handle, const char *name, Filename result)
void close_settings_r(void *handle)
{
tree234 *tree = (tree234 *)handle;
struct keyval *kv;
struct skeyval *kv;
if (!tree)
return;