gssapi.c   gssapi.c 
/* GSSAPI SASL plugin /* GSSAPI SASL plugin
* Leif Johansson * Leif Johansson
* Rob Siemborski (SASL v2 Conversion) * Rob Siemborski (SASL v2 Conversion)
* $Id: gssapi.c,v 1.112 2011/04/19 09:19:18 mel Exp $ * $Id: gssapi.c,v 1.115 2011/11/21 15:12:35 mel Exp $
*/ */
/* /*
* Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved . * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved .
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
* are met: * are met:
* *
* 1. Redistributions of source code must retain the above copyright * 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
skipping to change at line 82 skipping to change at line 82
#include <saslutil.h> #include <saslutil.h>
#include <saslplug.h> #include <saslplug.h>
#include "plugin_common.h" #include "plugin_common.h"
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
#include <errno.h> #include <errno.h>
#include <assert.h>
/***************************** Common Section *************************** **/ /***************************** Common Section *************************** **/
static const char plugin_id[] = "$Id: gssapi.c,v 1.112 2011/04/19 09:19:18 mel Exp $"; static const char plugin_id[] = "$Id: gssapi.c,v 1.115 2011/11/21 15:12:35 mel Exp $";
static const char * GSSAPI_BLANK_STRING = ""; static const char * GSSAPI_BLANK_STRING = "";
static gss_OID_desc gss_spnego_oid = { 6, (void *) "\x2b\x06\x01\x05\x05\x0
2" };
#if !defined(HAVE_GSS_C_NT_HOSTBASED_SERVICE) && !defined(GSS_C_NT_HOSTBASE D_SERVICE) #if !defined(HAVE_GSS_C_NT_HOSTBASED_SERVICE) && !defined(GSS_C_NT_HOSTBASE D_SERVICE)
extern gss_OID gss_nt_service_name; extern gss_OID gss_nt_service_name;
#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
#endif #endif
#ifdef WANT_KERBEROS5_3DES #ifdef WANT_KERBEROS5_3DES
/* Check if CyberSafe flag is defined */ /* Check if CyberSafe flag is defined */
#ifdef CSF_GSS_C_DES3_FLAG #ifdef CSF_GSS_C_DES3_FLAG
#define K5_MAX_SSF 112 #define K5_MAX_SSF 112
#endif #endif
skipping to change at line 145 skipping to change at line 148
static void *gss_mutex = NULL; static void *gss_mutex = NULL;
#else #else
#define GSS_LOCK_MUTEX(utils) #define GSS_LOCK_MUTEX(utils)
#define GSS_UNLOCK_MUTEX(utils) #define GSS_UNLOCK_MUTEX(utils)
#endif #endif
typedef struct context { typedef struct context {
int state; int state;
gss_OID mech_type; /* GSS-SPNEGO or GSSAPI */
int http_mode; /* use RFC 4559 compatible protocol? */
gss_ctx_id_t gss_ctx; gss_ctx_id_t gss_ctx;
gss_name_t client_name; gss_name_t client_name;
gss_name_t server_name; gss_name_t server_name;
gss_cred_id_t server_creds; gss_cred_id_t server_creds;
gss_cred_id_t client_creds; gss_cred_id_t client_creds;
sasl_ssf_t limitssf, requiressf; /* application defined bounds, for the sasl_ssf_t limitssf, requiressf; /* application defined bounds, for the
server */ server */
unsigned char qop; /* as allowed by GSSAPI */ unsigned char qop; /* as allowed by GSSAPI */
skipping to change at line 373 skipping to change at line 379
sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_seterror(text->utils, maj_stat, min_stat);
if (output_token->value) { if (output_token->value) {
GSS_LOCK_MUTEX(text->utils); GSS_LOCK_MUTEX(text->utils);
gss_release_buffer(&min_stat, output_token); gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(text->utils); GSS_UNLOCK_MUTEX(text->utils);
} }
return SASL_FAIL; return SASL_FAIL;
} }
if (output_token->value && output) { if (output_token->value && output) {
unsigned char * p = (unsigned char *) text->encode_buf; unsigned char * p;
ret = _plug_buf_alloc(text->utils, ret = _plug_buf_alloc(text->utils,
&(text->encode_buf), &(text->encode_buf),
&(text->encode_buf_len), &(text->encode_buf_len),
output_token->length + 4); output_token->length + 4);
if (ret != SASL_OK) { if (ret != SASL_OK) {
GSS_LOCK_MUTEX(text->utils); GSS_LOCK_MUTEX(text->utils);
gss_release_buffer(&min_stat, output_token); gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(text->utils); GSS_UNLOCK_MUTEX(text->utils);
return ret; return ret;
} }
p = (unsigned char *) text->encode_buf;
p[0] = (output_token->length>>24) & 0xFF; p[0] = (output_token->length>>24) & 0xFF;
p[1] = (output_token->length>>16) & 0xFF; p[1] = (output_token->length>>16) & 0xFF;
p[2] = (output_token->length>>8) & 0xFF; p[2] = (output_token->length>>8) & 0xFF;
p[3] = output_token->length & 0xFF; p[3] = output_token->length & 0xFF;
memcpy(text->encode_buf + 4, output_token->value, output_token->leng th); memcpy(text->encode_buf + 4, output_token->value, output_token->leng th);
} }
if (outputlen) { if (outputlen) {
*outputlen = output_token->length + 4; *outputlen = output_token->length + 4;
skipping to change at line 639 skipping to change at line 647
return SASL_NOMEM; return SASL_NOMEM;
} }
text->gss_ctx = GSS_C_NO_CONTEXT; text->gss_ctx = GSS_C_NO_CONTEXT;
text->client_name = GSS_C_NO_NAME; text->client_name = GSS_C_NO_NAME;
text->server_name = GSS_C_NO_NAME; text->server_name = GSS_C_NO_NAME;
text->server_creds = GSS_C_NO_CREDENTIAL; text->server_creds = GSS_C_NO_CREDENTIAL;
text->client_creds = GSS_C_NO_CREDENTIAL; text->client_creds = GSS_C_NO_CREDENTIAL;
text->state = SASL_GSSAPI_STATE_AUTHNEG; text->state = SASL_GSSAPI_STATE_AUTHNEG;
text->http_mode = (params->flags & SASL_NEED_HTTP);
*conn_context = text; *conn_context = text;
return SASL_OK; return SASL_OK;
} }
static int static int
gssapi_server_mech_step(void *conn_context, gssapi_server_mech_authneg(context_t *text,
sasl_server_params_t *params, sasl_server_params_t *params,
const char *clientin, const char *clientin,
unsigned clientinlen, unsigned clientinlen,
const char **serverout, const char **serverout,
unsigned *serveroutlen, unsigned *serveroutlen,
sasl_out_params_t *oparams) sasl_out_params_t *oparams __attribute__((unused)
))
{ {
context_t *text = (context_t *)conn_context;
gss_buffer_t input_token, output_token; gss_buffer_t input_token, output_token;
gss_buffer_desc real_input_token, real_output_token; gss_buffer_desc real_input_token, real_output_token;
OM_uint32 maj_stat = 0, min_stat = 0; OM_uint32 maj_stat = 0, min_stat = 0;
OM_uint32 max_input;
gss_buffer_desc name_token; gss_buffer_desc name_token;
int ret, out_flags = 0 ; int ret, equal = 0;
gss_cred_id_t server_creds = params->gss_creds; unsigned out_flags = 0;
gss_cred_id_t server_creds = (gss_cred_id_t) params->gss_creds;
gss_buffer_desc name_without_realm;
gss_name_t client_name_MN = NULL, without = NULL;
gss_OID mech_type;
input_token = &real_input_token; input_token = &real_input_token;
output_token = &real_output_token; output_token = &real_output_token;
output_token->value = NULL; output_token->length = 0; output_token->value = NULL; output_token->length = 0;
input_token->value = NULL; input_token->length = 0; input_token->value = NULL; input_token->length = 0;
if(!serverout) { if (text->server_name == GSS_C_NO_NAME) { /* only once */
PARAMERROR(text->utils); if (params->serverFQDN == NULL
return SASL_BADPARAM; || strlen(params->serverFQDN) == 0) {
} SETERROR(text->utils, "GSSAPI Failure: no serverFQDN");
sasl_gss_free_context_contents(text);
*serverout = NULL; return SASL_FAIL;
*serveroutlen = 0; }
name_token.length = strlen(params->service) + 1 + strlen(params->ser
verFQDN);
name_token.value = (char *)params->utils->malloc((name_token.length
+ 1) * sizeof(char));
if (name_token.value == NULL) {
MEMERROR(text->utils);
sasl_gss_free_context_contents(text);
return SASL_NOMEM;
}
sprintf(name_token.value,"%s@%s", params->service, params->serverFQD
N);
if (text == NULL) { GSS_LOCK_MUTEX(params->utils);
return SASL_BADPROT; maj_stat = gss_import_name (&min_stat,
} &name_token,
GSS_C_NT_HOSTBASED_SERVICE,
&text->server_name);
GSS_UNLOCK_MUTEX(params->utils);
switch (text->state) { params->utils->free(name_token.value);
name_token.value = NULL;
case SASL_GSSAPI_STATE_AUTHNEG: if (GSS_ERROR(maj_stat)) {
if (text->server_name == GSS_C_NO_NAME) { /* only once */ sasl_gss_seterror(text->utils, maj_stat, min_stat);
if (params->serverFQDN == NULL sasl_gss_free_context_contents(text);
|| strlen(params->serverFQDN) == 0) { return SASL_FAIL;
SETERROR(text->utils, "GSSAPI Failure: no serverFQDN"); }
sasl_gss_free_context_contents(text);
return SASL_FAIL;
}
name_token.length = strlen(params->service) + 1 + strlen(params-
>serverFQDN);
name_token.value = (char *)params->utils->malloc((name_token.len
gth + 1) * sizeof(char));
if (name_token.value == NULL) {
MEMERROR(text->utils);
sasl_gss_free_context_contents(text);
return SASL_NOMEM;
}
sprintf(name_token.value,"%s@%s", params->service, params->serve
rFQDN);
if ( text->server_creds != GSS_C_NO_CREDENTIAL) {
GSS_LOCK_MUTEX(params->utils); GSS_LOCK_MUTEX(params->utils);
maj_stat = gss_import_name (&min_stat, maj_stat = gss_release_cred(&min_stat, &text->server_creds);
&name_token,
GSS_C_NT_HOSTBASED_SERVICE,
&text->server_name);
GSS_UNLOCK_MUTEX(params->utils); GSS_UNLOCK_MUTEX(params->utils);
text->server_creds = GSS_C_NO_CREDENTIAL;
}
params->utils->free(name_token.value); /* If caller didn't provide creds already */
name_token.value = NULL; if ( server_creds == GSS_C_NO_CREDENTIAL) {
GSS_LOCK_MUTEX(params->utils);
maj_stat = gss_acquire_cred(&min_stat,
text->server_name,
GSS_C_INDEFINITE,
GSS_C_NO_OID_SET,
GSS_C_ACCEPT,
&text->server_creds,
NULL,
NULL);
GSS_UNLOCK_MUTEX(params->utils);
if (GSS_ERROR(maj_stat)) { if (GSS_ERROR(maj_stat)) {
sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_seterror(text->utils, maj_stat, min_stat);
sasl_gss_free_context_contents(text); sasl_gss_free_context_contents(text);
return SASL_FAIL; return SASL_FAIL;
} }
server_creds = text->server_creds;
}
}
if ( text->server_creds != GSS_C_NO_CREDENTIAL) { if (clientinlen) {
GSS_LOCK_MUTEX(params->utils); real_input_token.value = (void *)clientin;
maj_stat = gss_release_cred(&min_stat, &text->server_creds); real_input_token.length = clientinlen;
GSS_UNLOCK_MUTEX(params->utils); }
text->server_creds = GSS_C_NO_CREDENTIAL;
}
/* If caller didn't provide creds already */
if ( server_creds == GSS_C_NO_CREDENTIAL) {
GSS_LOCK_MUTEX(params->utils);
maj_stat = gss_acquire_cred(&min_stat,
text->server_name,
GSS_C_INDEFINITE,
GSS_C_NO_OID_SET,
GSS_C_ACCEPT,
&text->server_creds,
NULL,
NULL);
GSS_UNLOCK_MUTEX(params->utils);
if (GSS_ERROR(maj_stat)) { GSS_LOCK_MUTEX(params->utils);
sasl_gss_seterror(text->utils, maj_stat, min_stat); maj_stat =
sasl_gss_free_context_contents(text); gss_accept_sec_context(&min_stat,
return SASL_FAIL; &(text->gss_ctx),
} server_creds,
server_creds = text->server_creds; input_token,
} GSS_C_NO_CHANNEL_BINDINGS,
} &text->client_name,
&mech_type,
output_token,
&out_flags,
NULL, /* context validity period */
&(text->client_creds));
GSS_UNLOCK_MUTEX(params->utils);
if (clientinlen) { if (GSS_ERROR(maj_stat)) {
real_input_token.value = (void *)clientin; sasl_gss_log(text->utils, maj_stat, min_stat);
real_input_token.length = clientinlen; text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Failure
: gss_accept_sec_context");
if (output_token->value) {
GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(params->utils);
} }
sasl_gss_free_context_contents(text);
return SASL_BADAUTH;
}
GSS_LOCK_MUTEX(params->utils); if (serveroutlen) {
maj_stat = *serveroutlen = output_token->length;
gss_accept_sec_context(&min_stat, }
&(text->gss_ctx), if (output_token->value) {
server_creds, if (serverout) {
input_token, ret = _plug_buf_alloc(text->utils, &(text->out_buf),
GSS_C_NO_CHANNEL_BINDINGS, &(text->out_buf_len), *serveroutlen);
&text->client_name, if(ret != SASL_OK) {
NULL, /* resulting mech_name */
output_token,
&out_flags,
NULL, /* context validity period *
/
&(text->client_creds));
GSS_UNLOCK_MUTEX(params->utils);
if (GSS_ERROR(maj_stat)) {
sasl_gss_log(text->utils, maj_stat, min_stat);
text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Fai
lure: gss_accept_sec_context");
if (output_token->value) {
GSS_LOCK_MUTEX(params->utils); GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, output_token); gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(params->utils); GSS_UNLOCK_MUTEX(params->utils);
return ret;
} }
sasl_gss_free_context_contents(text); memcpy(text->out_buf, output_token->value, *serveroutlen);
return SASL_BADAUTH; *serverout = text->out_buf;
} }
/* When GSS_Accept_sec_context returns GSS_S_COMPLETE, the server GSS_LOCK_MUTEX(params->utils);
examines the context to ensure that it provides a level of protec gss_release_buffer(&min_stat, output_token);
tion GSS_UNLOCK_MUTEX(params->utils);
permitted by the server's security policy. In particular, if the } else {
integ_avail flag is not set in the context, then no security laye /* No output token, send an empty string */
r *serverout = GSSAPI_BLANK_STRING;
can be offered or accepted. If the conf_avail flag is not set in *serveroutlen = 0;
the }
context, then no security layer with confidentiality can be offer
ed if (maj_stat == GSS_S_CONTINUE_NEEDED) {
/* Context isn't complete */
return SASL_CONTINUE;
}
assert(maj_stat == GSS_S_COMPLETE);
/* When GSS_Accept_sec_context returns GSS_S_COMPLETE, the server
examines the context to ensure that it provides a level of protectio
n
permitted by the server's security policy. In particular, if the
integ_avail flag is not set in the context, then no security layer
can be offered or accepted. If the conf_avail flag is not set in th
e
context, then no security layer with confidentiality can be offered
or accepted. */
if ((out_flags & GSS_C_INTEG_FLAG) == 0) {
/* if the integ_avail flag is not set in the context,
then no security layer can be offered or accepted. */
text->qop = LAYER_NONE;
} else if ((out_flags & GSS_C_CONF_FLAG) == 0) {
/* If the conf_avail flag is not set in the context,
then no security layer with confidentiality can be offered
or accepted. */ or accepted. */
if ((out_flags & GSS_C_INTEG_FLAG) == 0) { text->qop = LAYER_NONE | LAYER_INTEGRITY;
/* if the integ_avail flag is not set in the context, } else {
then no security layer can be offered or accepted. */ text->qop = LAYER_NONE | LAYER_INTEGRITY | LAYER_CONFIDENTIALITY;
text->qop = LAYER_NONE; }
} else if ((out_flags & GSS_C_CONF_FLAG) == 0) {
/* If the conf_avail flag is not set in the context,
then no security layer with confidentiality can be offered
or accepted. */
text->qop = LAYER_NONE | LAYER_INTEGRITY;
} else {
text->qop = LAYER_NONE | LAYER_INTEGRITY | LAYER_CONFIDENTIALITY
;
}
if ((params->props.security_flags & SASL_SEC_PASS_CREDENTIALS) && if ((params->props.security_flags & SASL_SEC_PASS_CREDENTIALS) &&
(!(out_flags & GSS_C_DELEG_FLAG) || (!(out_flags & GSS_C_DELEG_FLAG) ||
text->client_creds == GSS_C_NO_CREDENTIAL) ) text->client_creds == GSS_C_NO_CREDENTIAL) )
{ {
text->utils->seterror(text->utils->conn, SASL_LOG_WARN, text->utils->seterror(text->utils->conn, SASL_LOG_WARN,
"GSSAPI warning: no credentials were passe d"); "GSSAPI warning: no credentials were passe d");
/* continue with authentication */ /* continue with authentication */
} }
if (serveroutlen) GSS_LOCK_MUTEX(params->utils);
*serveroutlen = output_token->length; maj_stat = gss_canonicalize_name(&min_stat,
if (output_token->value) { text->client_name,
if (serverout) { mech_type,
ret = _plug_buf_alloc(text->utils, &(text->out_buf), &client_name_MN);
&(text->out_buf_len), *serveroutlen); GSS_UNLOCK_MUTEX(params->utils);
if(ret != SASL_OK) {
GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(params->utils);
return ret;
}
memcpy(text->out_buf, output_token->value, *serveroutlen);
*serverout = text->out_buf;
}
GSS_LOCK_MUTEX(params->utils); if (GSS_ERROR(maj_stat)) {
gss_release_buffer(&min_stat, output_token); SETERROR(text->utils, "GSSAPI Failure: gss_canonicalize_name");
GSS_UNLOCK_MUTEX(params->utils); sasl_gss_free_context_contents(text);
} else { return SASL_BADAUTH;
/* No output token, send an empty string */ }
*serverout = GSSAPI_BLANK_STRING;
*serveroutlen = 0;
}
if (maj_stat == GSS_S_COMPLETE) { name_token.value = NULL;
/* Switch to ssf negotiation */ name_without_realm.value = NULL;
text->state = SASL_GSSAPI_STATE_SSFCAP;
GSS_LOCK_MUTEX(params->utils);
maj_stat = gss_display_name (&min_stat,
client_name_MN,
&name_token,
NULL);
GSS_UNLOCK_MUTEX(params->utils);
if (*serveroutlen != 0) { if (GSS_ERROR(maj_stat)) {
return SASL_CONTINUE; SETERROR(text->utils, "GSSAPI Failure: gss_display_name");
} sasl_gss_free_context_contents(text);
ret = SASL_BADAUTH;
goto cleanup;
}
/* If the id contains a realm get the identifier for the user
without the realm and see if it's the same id (i.e.
tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just want
to return the id (i.e. just "tmartin" */
if (strchr((char *) name_token.value, (int) '@') != NULL) {
/* NOTE: libc malloc, as it is freed below by a gssapi internal
* function! */
name_without_realm.value = params->utils->malloc(strlen(name_token.v
alue)+1);
if (name_without_realm.value == NULL) {
MEMERROR(text->utils);
ret = SASL_NOMEM;
goto cleanup;
}
/* Pretend that we just got an empty response from the client */ strcpy(name_without_realm.value, name_token.value);
clientinlen = 0;
/* fall through */ /* cut off string at '@' */
} else { (strchr(name_without_realm.value,'@'))[0] = '\0';
return SASL_CONTINUE;
}
case SASL_GSSAPI_STATE_SSFCAP: { name_without_realm.length = strlen( (char *) name_without_realm.valu
unsigned char sasldata[4]; e );
gss_buffer_desc name_token;
gss_buffer_desc name_without_realm;
gss_name_t without = NULL;
int equal;
name_token.value = NULL; GSS_LOCK_MUTEX(params->utils);
name_without_realm.value = NULL; maj_stat = gss_import_name (&min_stat,
&name_without_realm,
/* Solaris 8/9 gss_import_name doesn't accept GSS_C_NULL_OID her
e,
so use GSS_C_NT_USER_NAME instead if available. */
#ifdef HAVE_GSS_C_NT_USER_NAME
GSS_C_NT_USER_NAME,
#else
GSS_C_NULL_OID,
#endif
&without);
GSS_UNLOCK_MUTEX(params->utils);
if (clientinlen != 0) { if (GSS_ERROR(maj_stat)) {
SETERROR(text->utils, "GSSAPI server is not expecting data at th SETERROR(text->utils, "GSSAPI Failure: gss_import_name");
is stage");
sasl_gss_free_context_contents(text); sasl_gss_free_context_contents(text);
return SASL_BADAUTH; ret = SASL_BADAUTH;
goto cleanup;
} }
GSS_LOCK_MUTEX(params->utils); GSS_LOCK_MUTEX(params->utils);
maj_stat = gss_display_name (&min_stat, maj_stat = gss_compare_name(&min_stat,
text->client_name, client_name_MN,
&name_token, without,
NULL); &equal);
GSS_UNLOCK_MUTEX(params->utils); GSS_UNLOCK_MUTEX(params->utils);
if (GSS_ERROR(maj_stat)) { if (GSS_ERROR(maj_stat)) {
SETERROR(text->utils, "GSSAPI Failure"); SETERROR(text->utils, "GSSAPI Failure: gss_compare_name");
sasl_gss_free_context_contents(text); sasl_gss_free_context_contents(text);
return SASL_BADAUTH; ret = SASL_BADAUTH;
goto cleanup;
} }
/* If the id contains a realm get the identifier for the user } else {
without the realm and see if it's the same id (i.e. equal = 0;
tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just wa }
nt
to return the id (i.e. just "tmartin" */
if (strchr((char *) name_token.value, (int) '@') != NULL) {
/* NOTE: libc malloc, as it is freed below by a gssapi internal
* function! */
name_without_realm.value = params->utils->malloc(strlen(name_tok
en.value)+1);
if (name_without_realm.value == NULL) {
if (name_token.value) {
GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, &name_token);
GSS_UNLOCK_MUTEX(params->utils);
}
MEMERROR(text->utils);
return SASL_NOMEM;
}
strcpy(name_without_realm.value, name_token.value);
/* cut off string at '@' */ if (equal) {
(strchr(name_without_realm.value,'@'))[0] = '\0'; text->authid = strdup(name_without_realm.value);
} else {
text->authid = strdup(name_token.value);
}
name_without_realm.length = strlen( (char *) name_without_realm. if (text->authid == NULL) {
value ); MEMERROR(params->utils);
ret = SASL_NOMEM;
goto cleanup;
}
GSS_LOCK_MUTEX(params->utils); if (text->http_mode) {
maj_stat = gss_import_name (&min_stat, /* HTTP doesn't do any ssf negotiation */
&name_without_realm, text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
/* Solaris 8/9 gss_import_name doesn't accept GSS_C_NULL_OID her ret = SASL_OK;
e, }
so use GSS_C_NT_USER_NAME instead if available. */ else {
#ifdef HAVE_GSS_C_NT_USER_NAME /* Switch to ssf negotiation */
GSS_C_NT_USER_NAME, text->state = SASL_GSSAPI_STATE_SSFCAP;
#else ret = SASL_CONTINUE;
GSS_C_NULL_OID, }
#endif
&without);
GSS_UNLOCK_MUTEX(params->utils);
if (GSS_ERROR(maj_stat)) { cleanup:
params->utils->free(name_without_realm.value); if (client_name_MN) {
if (name_token.value) { GSS_LOCK_MUTEX(params->utils);
GSS_LOCK_MUTEX(params->utils); gss_release_name(&min_stat, &client_name_MN);
gss_release_buffer(&min_stat, &name_token); GSS_UNLOCK_MUTEX(params->utils);
GSS_UNLOCK_MUTEX(params->utils); }
} if (name_token.value) {
SETERROR(text->utils, "GSSAPI Failure"); GSS_LOCK_MUTEX(params->utils);
sasl_gss_free_context_contents(text); gss_release_buffer(&min_stat, &name_token);
return SASL_BADAUTH; GSS_UNLOCK_MUTEX(params->utils);
} }
if (name_without_realm.value) {
params->utils->free(name_without_realm.value);
}
if (without) {
GSS_LOCK_MUTEX(params->utils);
gss_release_name(&min_stat, &without);
GSS_UNLOCK_MUTEX(params->utils);
}
GSS_LOCK_MUTEX(params->utils); return ret;
maj_stat = gss_compare_name(&min_stat, }
text->client_name,
without,
&equal);
GSS_UNLOCK_MUTEX(params->utils);
if (GSS_ERROR(maj_stat)) { static int
params->utils->free(name_without_realm.value); gssapi_server_mech_ssfcap(context_t *text,
if (name_token.value) { sasl_server_params_t *params,
GSS_LOCK_MUTEX(params->utils); const char *clientin __attribute__((unused)),
gss_release_buffer(&min_stat, &name_token); unsigned clientinlen,
GSS_UNLOCK_MUTEX(params->utils); const char **serverout,
} unsigned *serveroutlen,
if (without) { sasl_out_params_t *oparams __attribute__((unused))
GSS_LOCK_MUTEX(params->utils); )
gss_release_name(&min_stat, &without); {
GSS_UNLOCK_MUTEX(params->utils); gss_buffer_t input_token, output_token;
} gss_buffer_desc real_input_token, real_output_token;
SETERROR(text->utils, "GSSAPI Failure"); OM_uint32 maj_stat = 0, min_stat = 0;
sasl_gss_free_context_contents(text); unsigned char sasldata[4];
return SASL_BADAUTH; int ret;
}
GSS_LOCK_MUTEX(params->utils); input_token = &real_input_token;
gss_release_name(&min_stat,&without); output_token = &real_output_token;
GSS_UNLOCK_MUTEX(params->utils); output_token->value = NULL; output_token->length = 0;
} else { if (clientinlen != 0) {
equal = 0; SETERROR(text->utils, "GSSAPI server is not expecting data at this s
} tage");
sasl_gss_free_context_contents(text);
return SASL_BADAUTH;
}
if (equal) { /* we have to decide what sort of encryption/integrity/etc.,
text->authid = strdup(name_without_realm.value); we support */
if (params->props.max_ssf < params->external_ssf) {
text->limitssf = 0;
} else {
text->limitssf = params->props.max_ssf - params->external_ssf;
}
if (params->props.min_ssf < params->external_ssf) {
text->requiressf = 0;
} else {
text->requiressf = params->props.min_ssf - params->external_ssf;
}
if (text->authid == NULL) { /* build up our security properties token */
MEMERROR(params->utils); if (text->requiressf != 0 &&
return SASL_NOMEM; (text->qop & (LAYER_INTEGRITY|LAYER_CONFIDENTIALITY))) {
} if (params->props.maxbufsize > 0xFFFFFF) {
/* make sure maxbufsize isn't too large */
/* maxbufsize = 0xFFFFFF */
sasldata[1] = sasldata[2] = sasldata[3] = 0xFF;
} else { } else {
text->authid = strdup(name_token.value); sasldata[1] = (params->props.maxbufsize >> 16) & 0xFF;
sasldata[2] = (params->props.maxbufsize >> 8) & 0xFF;
if (text->authid == NULL) { sasldata[3] = (params->props.maxbufsize >> 0) & 0xFF;
MEMERROR(params->utils);
return SASL_NOMEM;
}
} }
} else {
/* From RFC 4752: "The client verifies that the server maximum buffe
r is 0
if the server does not advertise support for any security layer."
*/
sasldata[1] = sasldata[2] = sasldata[3] = 0;
}
sasldata[0] = 0;
if(text->requiressf != 0 && !params->props.maxbufsize) {
params->utils->seterror(params->utils->conn, 0,
"GSSAPI needs a security layer but one is fo
rbidden");
return SASL_TOOWEAK;
}
if (text->requiressf == 0) {
sasldata[0] |= LAYER_NONE; /* authentication */
}
if ((text->qop & LAYER_INTEGRITY) &&
text->requiressf <= 1 &&
text->limitssf >= 1 &&
params->props.maxbufsize) {
sasldata[0] |= LAYER_INTEGRITY;
}
if ((text->qop & LAYER_CONFIDENTIALITY) &&
text->requiressf <= K5_MAX_SSF &&
text->limitssf >= K5_MAX_SSF &&
params->props.maxbufsize) {
sasldata[0] |= LAYER_CONFIDENTIALITY;
}
/* Remember what we want and can offer */
text->qop = sasldata[0];
real_input_token.value = (void *)sasldata;
real_input_token.length = 4;
GSS_LOCK_MUTEX(params->utils);
maj_stat = gss_wrap(&min_stat,
text->gss_ctx,
0, /* Just integrity checking here */
GSS_C_QOP_DEFAULT,
input_token,
NULL,
output_token);
GSS_UNLOCK_MUTEX(params->utils);
if (name_token.value) { if (GSS_ERROR(maj_stat)) {
sasl_gss_seterror(text->utils, maj_stat, min_stat);
if (output_token->value) {
GSS_LOCK_MUTEX(params->utils); GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, &name_token); gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(params->utils); GSS_UNLOCK_MUTEX(params->utils);
} }
if (name_without_realm.value) { sasl_gss_free_context_contents(text);
params->utils->free(name_without_realm.value); return SASL_FAIL;
} }
/* we have to decide what sort of encryption/integrity/etc.,
we support */
if (params->props.max_ssf < params->external_ssf) {
text->limitssf = 0;
} else {
text->limitssf = params->props.max_ssf - params->external_ssf;
}
if (params->props.min_ssf < params->external_ssf) {
text->requiressf = 0;
} else {
text->requiressf = params->props.min_ssf - params->external_ssf;
}
/* build up our security properties token */
if (text->requiressf != 0 &&
(text->qop & (LAYER_INTEGRITY|LAYER_CONFIDENTIALITY))) {
if (params->props.maxbufsize > 0xFFFFFF) {
/* make sure maxbufsize isn't too large */
/* maxbufsize = 0xFFFFFF */
sasldata[1] = sasldata[2] = sasldata[3] = 0xFF;
} else {
sasldata[1] = (params->props.maxbufsize >> 16) & 0xFF;
sasldata[2] = (params->props.maxbufsize >> 8) & 0xFF;
sasldata[3] = (params->props.maxbufsize >> 0) & 0xFF;
}
} else {
/* From RFC 4752: "The client verifies that the server maximum b
uffer is 0
if the server does not advertise support for any security lay
er." */
sasldata[1] = sasldata[2] = sasldata[3] = 0;
}
sasldata[0] = 0;
if(text->requiressf != 0 && !params->props.maxbufsize) {
params->utils->seterror(params->utils->conn, 0,
"GSSAPI needs a security layer but one i
s forbidden");
return SASL_TOOWEAK;
}
if (text->requiressf == 0) {
sasldata[0] |= LAYER_NONE; /* authentication */
}
if ((text->qop & LAYER_INTEGRITY) &&
text->requiressf <= 1 &&
text->limitssf >= 1 &&
params->props.maxbufsize) {
sasldata[0] |= LAYER_INTEGRITY;
}
if ((text->qop & LAYER_CONFIDENTIALITY) &&
text->requiressf <= K5_MAX_SSF &&
text->limitssf >= K5_MAX_SSF &&
params->props.maxbufsize) {
sasldata[0] |= LAYER_CONFIDENTIALITY;
}
real_input_token.value = (void *)sasldata;
real_input_token.length = 4;
GSS_LOCK_MUTEX(params->utils);
maj_stat = gss_wrap(&min_stat,
text->gss_ctx,
0, /* Just integrity checking here */
GSS_C_QOP_DEFAULT,
input_token,
NULL,
output_token);
GSS_UNLOCK_MUTEX(params->utils);
if (GSS_ERROR(maj_stat)) { if (serveroutlen)
sasl_gss_seterror(text->utils, maj_stat, min_stat); *serveroutlen = output_token->length;
if (output_token->value) { if (output_token->value) {
if (serverout) {
ret = _plug_buf_alloc(text->utils, &(text->out_buf),
&(text->out_buf_len), *serveroutlen);
if(ret != SASL_OK) {
GSS_LOCK_MUTEX(params->utils); GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, output_token); gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(params->utils); GSS_UNLOCK_MUTEX(params->utils);
return ret;
} }
sasl_gss_free_context_contents(text); memcpy(text->out_buf, output_token->value, *serveroutlen);
return SASL_FAIL; *serverout = text->out_buf;
} }
if (serveroutlen) GSS_LOCK_MUTEX(params->utils);
*serveroutlen = output_token->length; gss_release_buffer(&min_stat, output_token);
if (output_token->value) { GSS_UNLOCK_MUTEX(params->utils);
if (serverout) { }
ret = _plug_buf_alloc(text->utils, &(text->out_buf),
&(text->out_buf_len), *serveroutlen);
if(ret != SASL_OK) {
GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(params->utils);
return ret;
}
memcpy(text->out_buf, output_token->value, *serveroutlen);
*serverout = text->out_buf;
}
GSS_LOCK_MUTEX(params->utils); /* Wait for ssf request and authid */
gss_release_buffer(&min_stat, output_token); text->state = SASL_GSSAPI_STATE_SSFREQ;
GSS_UNLOCK_MUTEX(params->utils);
}
/* Remember what we want and can offer */ return SASL_CONTINUE;
text->qop = sasldata[0]; }
/* Wait for ssf request and authid */ static int
text->state = SASL_GSSAPI_STATE_SSFREQ; gssapi_server_mech_ssfreq(context_t *text,
sasl_server_params_t *params,
const char *clientin,
unsigned clientinlen,
const char **serverout __attribute__((unused)),
unsigned *serveroutlen __attribute__((unused)),
sasl_out_params_t *oparams)
{
gss_buffer_t input_token, output_token;
gss_buffer_desc real_input_token, real_output_token;
OM_uint32 maj_stat = 0, min_stat = 0;
OM_uint32 max_input;
int layerchoice;
return SASL_CONTINUE; input_token = &real_input_token;
} output_token = &real_output_token;
output_token->value = NULL; output_token->length = 0;
case SASL_GSSAPI_STATE_SSFREQ: { real_input_token.value = (void *)clientin;
int layerchoice; real_input_token.length = clientinlen;
real_input_token.value = (void *)clientin; GSS_LOCK_MUTEX(params->utils);
real_input_token.length = clientinlen; maj_stat = gss_unwrap(&min_stat,
text->gss_ctx,
input_token,
output_token,
NULL,
NULL);
GSS_UNLOCK_MUTEX(params->utils);
if (GSS_ERROR(maj_stat)) {
sasl_gss_seterror(text->utils, maj_stat, min_stat);
sasl_gss_free_context_contents(text);
return SASL_FAIL;
}
if (output_token->length < 4) {
SETERROR(text->utils,
"token too short");
GSS_LOCK_MUTEX(params->utils); GSS_LOCK_MUTEX(params->utils);
maj_stat = gss_unwrap(&min_stat, gss_release_buffer(&min_stat, output_token);
text->gss_ctx,
input_token,
output_token,
NULL,
NULL);
GSS_UNLOCK_MUTEX(params->utils); GSS_UNLOCK_MUTEX(params->utils);
sasl_gss_free_context_contents(text);
return SASL_FAIL;
}
if (GSS_ERROR(maj_stat)) { layerchoice = (int)(((char *)(output_token->value))[0]);
sasl_gss_seterror(text->utils, maj_stat, min_stat); if (layerchoice == LAYER_NONE &&
sasl_gss_free_context_contents(text); (text->qop & LAYER_NONE)) { /* no encryption */
return SASL_FAIL; oparams->encode = NULL;
} oparams->decode = NULL;
oparams->mech_ssf = 0;
if (output_token->length < 4) { } else if (layerchoice == LAYER_INTEGRITY &&
SETERROR(text->utils, (text->qop & LAYER_INTEGRITY)) { /* integrity */
"token too short"); oparams->encode = &gssapi_integrity_encode;
oparams->decode = &gssapi_decode;
oparams->mech_ssf = 1;
} else if ((layerchoice == LAYER_CONFIDENTIALITY ||
/* For compatibility with broken clients setting both bits *
/
layerchoice == (LAYER_CONFIDENTIALITY|LAYER_INTEGRITY)) &&
(text->qop & LAYER_CONFIDENTIALITY)) { /* privacy */
oparams->encode = &gssapi_privacy_encode;
oparams->decode = &gssapi_decode;
/* FIX ME: Need to extract the proper value here */
oparams->mech_ssf = K5_MAX_SSF;
} else {
/* not a supported encryption layer */
SETERROR(text->utils,
"protocol violation: client requested invalid layer");
/* Mark that we attempted negotiation */
oparams->mech_ssf = 2;
if (output_token->value) {
GSS_LOCK_MUTEX(params->utils); GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, output_token); gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(params->utils); GSS_UNLOCK_MUTEX(params->utils);
sasl_gss_free_context_contents(text);
return SASL_FAIL;
}
layerchoice = (int)(((char *)(output_token->value))[0]);
if (layerchoice == LAYER_NONE &&
(text->qop & LAYER_NONE)) { /* no encryption */
oparams->encode = NULL;
oparams->decode = NULL;
oparams->mech_ssf = 0;
} else if (layerchoice == LAYER_INTEGRITY &&
(text->qop & LAYER_INTEGRITY)) { /* integrity */
oparams->encode = &gssapi_integrity_encode;
oparams->decode = &gssapi_decode;
oparams->mech_ssf = 1;
} else if ((layerchoice == LAYER_CONFIDENTIALITY ||
/* For compatibility with broken clients setting both bi
ts */
layerchoice == (LAYER_CONFIDENTIALITY|LAYER_INTEGRITY))
&&
(text->qop & LAYER_CONFIDENTIALITY)) { /* privacy */
oparams->encode = &gssapi_privacy_encode;
oparams->decode = &gssapi_decode;
/* FIX ME: Need to extract the proper value here */
oparams->mech_ssf = K5_MAX_SSF;
} else {
/* not a supported encryption layer */
SETERROR(text->utils,
"protocol violation: client requested invalid layer");
/* Mark that we attempted negotiation */
oparams->mech_ssf = 2;
if (output_token->value) {
GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(params->utils);
}
sasl_gss_free_context_contents(text);
return SASL_FAIL;
} }
sasl_gss_free_context_contents(text);
return SASL_FAIL;
}
if (output_token->length > 4) { if (output_token->length > 4) {
int ret; int ret;
ret = params->canon_user(params->utils->conn, ret = params->canon_user(params->utils->conn,
((char *) output_token->value) + 4, ((char *) output_token->value) + 4,
(output_token->length - 4) * sizeof(cha (output_token->length - 4) * sizeof(char),
r), SASL_CU_AUTHZID, oparams);
SASL_CU_AUTHZID, oparams);
if (ret != SASL_OK) { if (ret != SASL_OK) {
sasl_gss_free_context_contents(text); sasl_gss_free_context_contents(text);
return ret; return ret;
} }
}
ret = params->canon_user(params->utils->conn, /* No matter what, set the rest of the oparams */
text->authid,
0, /* strlen(text->authid) */
SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VER
IFIED, oparams);
if (ret != SASL_OK) {
sasl_gss_free_context_contents(text);
return ret;
}
} else /* if (output_token->length == 4) */ {
/* null authzid */
int ret;
ret = params->canon_user(params->utils->conn,
text->authid,
0, /* strlen(text->authid) */
SASL_CU_AUTHZID | SASL_CU_AUTHID | SASL
_CU_EXTERNALLY_VERIFIED,
oparams);
if (ret != SASL_OK) { oparams->maxoutbuf =
sasl_gss_free_context_contents(text); (((unsigned char *) output_token->value)[1] << 16) |
return ret; (((unsigned char *) output_token->value)[2] << 8) |
} (((unsigned char *) output_token->value)[3] << 0);
if (oparams->mech_ssf) {
maj_stat = gss_wrap_size_limit( &min_stat,
text->gss_ctx,
1,
GSS_C_QOP_DEFAULT,
(OM_uint32) oparams->maxoutbuf,
&max_input);
if(max_input > oparams->maxoutbuf) {
/* Heimdal appears to get this wrong */
oparams->maxoutbuf -= (max_input - oparams->maxoutbuf);
} else {
/* This code is actually correct */
oparams->maxoutbuf = max_input;
} }
}
/* No matter what, set the rest of the oparams */ GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, output_token);
GSS_UNLOCK_MUTEX(params->utils);
if (text->client_creds != GSS_C_NO_CREDENTIAL) { text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
oparams->client_creds = &text->client_creds;
}
else {
oparams->client_creds = NULL;
}
oparams->maxoutbuf = /* used by layers */
(((unsigned char *) output_token->value)[1] << 16) | _plug_decode_init(&text->decode_context,
(((unsigned char *) output_token->value)[2] << 8) | text->utils,
(((unsigned char *) output_token->value)[3] << 0); (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
params->props.maxbufsize);
if (oparams->mech_ssf) { return SASL_OK;
maj_stat = gss_wrap_size_limit( &min_stat, }
text->gss_ctx,
1,
GSS_C_QOP_DEFAULT,
(OM_uint32) oparams->maxoutbuf,
&max_input);
if(max_input > oparams->maxoutbuf) { static int
/* Heimdal appears to get this wrong */ gssapi_server_mech_step(void *conn_context,
oparams->maxoutbuf -= (max_input - oparams->maxoutbuf); sasl_server_params_t *params,
} else { const char *clientin,
/* This code is actually correct */ unsigned clientinlen,
oparams->maxoutbuf = max_input; const char **serverout,
} unsigned *serveroutlen,
} sasl_out_params_t *oparams)
{
context_t *text = (context_t *) conn_context;
int ret;
GSS_LOCK_MUTEX(params->utils); if (!serverout) {
gss_release_buffer(&min_stat, output_token); PARAMERROR(text->utils);
GSS_UNLOCK_MUTEX(params->utils); return SASL_BADPARAM;
}
text->state = SASL_GSSAPI_STATE_AUTHENTICATED; *serverout = NULL;
*serveroutlen = 0;
/* used by layers */ if (text == NULL) return SASL_BADPROT;
_plug_decode_init(&text->decode_context,
text->utils,
(params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
params->props.maxbufsize);
oparams->doneflag = 1; params->utils->log(NULL, SASL_LOG_DEBUG,
"GSSAPI server step %d\n", text->state);
return SASL_OK; switch (text->state) {
}
case SASL_GSSAPI_STATE_AUTHNEG:
ret = gssapi_server_mech_authneg(text, params, clientin, clientinlen
,
serverout, serveroutlen, oparams);
if (ret != SASL_CONTINUE || *serveroutlen) break;
/* Pretend that we just got an empty response from the client */
clientinlen = 0;
/* fall through */
case SASL_GSSAPI_STATE_SSFCAP:
ret = gssapi_server_mech_ssfcap(text, params, clientin, clientinlen,
serverout, serveroutlen, oparams);
break;
case SASL_GSSAPI_STATE_SSFREQ:
ret = gssapi_server_mech_ssfreq(text, params, clientin, clientinlen,
serverout, serveroutlen, oparams);
break;
default: default:
params->utils->log(NULL, SASL_LOG_ERR, params->utils->log(NULL, SASL_LOG_ERR,
"Invalid GSSAPI server step %d\n", text->state); "Invalid GSSAPI server step %d\n", text->state);
return SASL_FAIL; return SASL_FAIL;
} }
return SASL_FAIL; /* should never get here */ if (ret == SASL_OK) {
ret = params->canon_user(params->utils->conn,
text->authid,
0, /* strlen(text->authid) */
(oparams->user ? 0 : SASL_CU_AUTHZID)
| SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VERIF
IED,
oparams);
if (ret != SASL_OK) {
sasl_gss_free_context_contents(text);
return ret;
}
if (text->client_creds != GSS_C_NO_CREDENTIAL) {
oparams->client_creds = &text->client_creds;
}
else {
oparams->client_creds = NULL;
}
oparams->doneflag = 1;
}
return ret;
} }
static sasl_server_plug_t gssapi_server_plugins[] = static sasl_server_plug_t gssapi_server_plugins[] =
{ {
{ {
"GSSAPI", /* mech_name */ "GSSAPI", /* mech_name */
K5_MAX_SSF, /* max_ssf */ K5_MAX_SSF, /* max_ssf */
SASL_SEC_NOPLAINTEXT SASL_SEC_NOPLAINTEXT
| SASL_SEC_NOACTIVE | SASL_SEC_NOACTIVE
| SASL_SEC_NOANONYMOUS | SASL_SEC_NOANONYMOUS
skipping to change at line 1272 skipping to change at line 1339
&gssapi_server_mech_new, /* mech_new */ &gssapi_server_mech_new, /* mech_new */
&gssapi_server_mech_step, /* mech_step */ &gssapi_server_mech_step, /* mech_step */
&gssapi_common_mech_dispose, /* mech_dispose */ &gssapi_common_mech_dispose, /* mech_dispose */
&gssapi_common_mech_free, /* mech_free */ &gssapi_common_mech_free, /* mech_free */
NULL, /* setpass */ NULL, /* setpass */
NULL, /* user_query */ NULL, /* user_query */
NULL, /* idle */ NULL, /* idle */
NULL, /* mech_avail */ NULL, /* mech_avail */
NULL /* spare */ NULL /* spare */
} }
#ifdef HAVE_GSS_SPNEGO
,{
"GSS-SPNEGO", /* mech_name */
K5_MAX_SSF, /* max_ssf */
SASL_SEC_NOPLAINTEXT
| SASL_SEC_NOACTIVE
| SASL_SEC_NOANONYMOUS
| SASL_SEC_MUTUAL_AUTH /* security_flags */
| SASL_SEC_PASS_CREDENTIALS,
SASL_FEAT_WANT_CLIENT_FIRST
| SASL_FEAT_ALLOWS_PROXY
| SASL_FEAT_DONTUSE_USERPASSWD
| SASL_FEAT_SUPPORTS_HTTP, /* features */
NULL, /* glob_context */
&gssapi_server_mech_new, /* mech_new */
&gssapi_server_mech_step, /* mech_step */
&gssapi_common_mech_dispose, /* mech_dispose */
&gssapi_common_mech_free, /* mech_free */
NULL, /* setpass */
NULL, /* user_query */
NULL, /* idle */
NULL, /* mech_avail */
NULL /* spare */
}
#endif
}; };
int gssapiv2_server_plug_init( int gssapiv2_server_plug_init(
#ifndef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY #ifndef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
const sasl_utils_t *utils __attribute__((unused)), const sasl_utils_t *utils __attribute__((unused)),
#else #else
const sasl_utils_t *utils, const sasl_utils_t *utils,
#endif #endif
int maxversion, int maxversion,
int *out_version, int *out_version,
skipping to change at line 1324 skipping to change at line 1416
} }
strncpy(keytab_path, keytab, 1024); strncpy(keytab_path, keytab, 1024);
gsskrb5_register_acceptor_identity(keytab_path); gsskrb5_register_acceptor_identity(keytab_path);
} }
#endif #endif
*out_version = SASL_SERVER_PLUG_VERSION; *out_version = SASL_SERVER_PLUG_VERSION;
*pluglist = gssapi_server_plugins; *pluglist = gssapi_server_plugins;
#ifdef HAVE_GSS_SPNEGO
*plugcount = 2;
#else
*plugcount = 1; *plugcount = 1;
#endif
#ifdef GSS_USE_MUTEXES #ifdef GSS_USE_MUTEXES
if (!gss_mutex) { if (!gss_mutex) {
gss_mutex = utils->mutex_alloc(); gss_mutex = utils->mutex_alloc();
if (!gss_mutex) { if (!gss_mutex) {
return SASL_FAIL; return SASL_FAIL;
} }
} }
#endif #endif
return SASL_OK; return SASL_OK;
} }
/***************************** Client Section *************************** **/ /***************************** Client Section *************************** **/
static int gssapi_client_mech_new(void *glob_context __attribute__((unused) ), static int gssapi_client_mech_new(void *glob_context,
sasl_client_params_t *params, sasl_client_params_t *params,
void **conn_context) void **conn_context)
{ {
context_t *text; context_t *text;
/* holds state are in */ /* holds state are in */
text = sasl_gss_new_context(params->utils); text = sasl_gss_new_context(params->utils);
if (text == NULL) { if (text == NULL) {
MEMERROR(params->utils); MEMERROR(params->utils);
return SASL_NOMEM; return SASL_NOMEM;
} }
text->state = SASL_GSSAPI_STATE_AUTHNEG; text->state = SASL_GSSAPI_STATE_AUTHNEG;
text->mech_type = (gss_OID) glob_context;
text->gss_ctx = GSS_C_NO_CONTEXT; text->gss_ctx = GSS_C_NO_CONTEXT;
text->client_name = GSS_C_NO_NAME; text->client_name = GSS_C_NO_NAME;
text->server_creds = GSS_C_NO_CREDENTIAL; text->server_creds = GSS_C_NO_CREDENTIAL;
text->client_creds = GSS_C_NO_CREDENTIAL; text->client_creds = GSS_C_NO_CREDENTIAL;
text->http_mode = (params->flags & SASL_NEED_HTTP);
*conn_context = text; *conn_context = text;
return SASL_OK; return SASL_OK;
} }
static int gssapi_client_mech_step(void *conn_context, static int gssapi_client_mech_step(void *conn_context,
sasl_client_params_t *params, sasl_client_params_t *params,
const char *serverin, const char *serverin,
unsigned serverinlen, unsigned serverinlen,
sasl_interact_t **prompt_need, sasl_interact_t **prompt_need,
skipping to change at line 1391 skipping to change at line 1490
input_token = &real_input_token; input_token = &real_input_token;
output_token = &real_output_token; output_token = &real_output_token;
output_token->value = NULL; output_token->value = NULL;
input_token->value = NULL; input_token->value = NULL;
input_token->length = 0; input_token->length = 0;
gss_cred_id_t client_creds = (gss_cred_id_t)params->gss_creds; gss_cred_id_t client_creds = (gss_cred_id_t)params->gss_creds;
*clientout = NULL; *clientout = NULL;
*clientoutlen = 0; *clientoutlen = 0;
params->utils->log(NULL, SASL_LOG_DEBUG,
"GSSAPI client step %d", text->state);
switch (text->state) { switch (text->state) {
case SASL_GSSAPI_STATE_AUTHNEG: case SASL_GSSAPI_STATE_AUTHNEG:
/* try to get the userid */ /* try to get the userid */
if (text->user == NULL) { if (text->user == NULL) {
int user_result = SASL_OK; int user_result = SASL_OK;
user_result = _plug_get_userid(params->utils, &text->user, user_result = _plug_get_userid(params->utils, &text->user,
prompt_need); prompt_need);
skipping to change at line 1503 skipping to change at line 1605
if (params->props.security_flags & SASL_SEC_PASS_CREDENTIALS) { if (params->props.security_flags & SASL_SEC_PASS_CREDENTIALS) {
req_flags = req_flags | GSS_C_DELEG_FLAG; req_flags = req_flags | GSS_C_DELEG_FLAG;
} }
GSS_LOCK_MUTEX(params->utils); GSS_LOCK_MUTEX(params->utils);
maj_stat = gss_init_sec_context(&min_stat, maj_stat = gss_init_sec_context(&min_stat,
client_creds, /* GSS_C_NO_CREDENTIAL */ client_creds, /* GSS_C_NO_CREDENTIAL */
&text->gss_ctx, &text->gss_ctx,
text->server_name, text->server_name,
GSS_C_NO_OID, text->mech_type,
req_flags, req_flags,
0, 0,
GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_CHANNEL_BINDINGS,
input_token, input_token,
NULL, NULL,
output_token, output_token,
&out_req_flags, &out_req_flags,
NULL); NULL);
GSS_UNLOCK_MUTEX(params->utils); GSS_UNLOCK_MUTEX(params->utils);
skipping to change at line 1623 skipping to change at line 1725
name_token.value, 0, name_token.value, 0,
SASL_CU_AUTHID | SASL_CU_AUTHZID, SASL_CU_AUTHID | SASL_CU_AUTHZID,
oparams); oparams);
} }
GSS_LOCK_MUTEX(params->utils); GSS_LOCK_MUTEX(params->utils);
gss_release_buffer(&min_stat, &name_token); gss_release_buffer(&min_stat, &name_token);
GSS_UNLOCK_MUTEX(params->utils); GSS_UNLOCK_MUTEX(params->utils);
if (ret != SASL_OK) return ret; if (ret != SASL_OK) return ret;
if (text->http_mode) {
/* HTTP doesn't do any ssf negotiation */
text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
oparams->doneflag = 1;
return SASL_OK;
}
/* Switch to ssf negotiation */ /* Switch to ssf negotiation */
text->state = SASL_GSSAPI_STATE_SSFCAP; text->state = SASL_GSSAPI_STATE_SSFCAP;
} }
return SASL_CONTINUE; return SASL_CONTINUE;
case SASL_GSSAPI_STATE_SSFCAP: { case SASL_GSSAPI_STATE_SSFCAP: {
sasl_security_properties_t *secprops = &(params->props); sasl_security_properties_t *secprops = &(params->props);
unsigned int alen, external = params->external_ssf; unsigned int alen, external = params->external_ssf;
sasl_ssf_t need, allowed; sasl_ssf_t need, allowed;
skipping to change at line 1729 skipping to change at line 1838
} }
} else if ((text->qop & LAYER_INTEGRITY) && } else if ((text->qop & LAYER_INTEGRITY) &&
allowed >= 1 && allowed >= 1 &&
need <= 1 && need <= 1 &&
(serverhas & LAYER_INTEGRITY)) { (serverhas & LAYER_INTEGRITY)) {
/* integrity */ /* integrity */
oparams->encode = &gssapi_integrity_encode; oparams->encode = &gssapi_integrity_encode;
oparams->decode = &gssapi_decode; oparams->decode = &gssapi_decode;
oparams->mech_ssf = 1; oparams->mech_ssf = 1;
mychoice = LAYER_INTEGRITY; mychoice = LAYER_INTEGRITY;
} else if (need <= 0 && (serverhas & LAYER_NONE)) { } else if ((text->qop & LAYER_NONE) &&
need <= 0 && (serverhas & LAYER_NONE)) {
/* no layer */ /* no layer */
oparams->encode = NULL; oparams->encode = NULL;
oparams->decode = NULL; oparams->decode = NULL;
oparams->mech_ssf = 0; oparams->mech_ssf = 0;
mychoice = LAYER_NONE; mychoice = LAYER_NONE;
} else { } else {
/* there's no appropriate layering for us! */ /* there's no appropriate layering for us! */
sasl_gss_free_context_contents(text); sasl_gss_free_context_contents(text);
return SASL_TOOWEAK; return SASL_TOOWEAK;
} }
skipping to change at line 1881 skipping to change at line 1991
default: default:
params->utils->log(NULL, SASL_LOG_ERR, params->utils->log(NULL, SASL_LOG_ERR,
"Invalid GSSAPI client step %d\n", text->state); "Invalid GSSAPI client step %d\n", text->state);
return SASL_FAIL; return SASL_FAIL;
} }
return SASL_FAIL; /* should never get here */ return SASL_FAIL; /* should never get here */
} }
static const long gssapi_required_prompts[] = { static const unsigned long gssapi_required_prompts[] = {
SASL_CB_LIST_END SASL_CB_LIST_END
}; };
static sasl_client_plug_t gssapi_client_plugins[] = static sasl_client_plug_t gssapi_client_plugins[] =
{ {
{ {
"GSSAPI", /* mech_name */ "GSSAPI", /* mech_name */
K5_MAX_SSF, /* max_ssf */ K5_MAX_SSF, /* max_ssf */
SASL_SEC_NOPLAINTEXT SASL_SEC_NOPLAINTEXT
| SASL_SEC_NOACTIVE | SASL_SEC_NOACTIVE
| SASL_SEC_NOANONYMOUS | SASL_SEC_NOANONYMOUS
| SASL_SEC_MUTUAL_AUTH | SASL_SEC_MUTUAL_AUTH
| SASL_SEC_PASS_CREDENTIALS, /* security_flags */ | SASL_SEC_PASS_CREDENTIALS, /* security_flags */
SASL_FEAT_NEEDSERVERFQDN SASL_FEAT_NEEDSERVERFQDN
| SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_WANT_CLIENT_FIRST
| SASL_FEAT_ALLOWS_PROXY, /* features */ | SASL_FEAT_ALLOWS_PROXY, /* features */
gssapi_required_prompts, /* required_prompts */ gssapi_required_prompts, /* required_prompts */
NULL, /* glob_context */ GSS_C_NO_OID, /* glob_context */
&gssapi_client_mech_new, /* mech_new */ &gssapi_client_mech_new, /* mech_new */
&gssapi_client_mech_step, /* mech_step */ &gssapi_client_mech_step, /* mech_step */
&gssapi_common_mech_dispose, /* mech_dispose */ &gssapi_common_mech_dispose, /* mech_dispose */
&gssapi_common_mech_free, /* mech_free */ &gssapi_common_mech_free, /* mech_free */
NULL, /* idle */ NULL, /* idle */
NULL, /* spare */ NULL, /* spare */
NULL /* spare */ NULL /* spare */
} }
#ifdef HAVE_GSS_SPNEGO
,{
"GSS-SPNEGO", /* mech_name */
K5_MAX_SSF, /* max_ssf */
SASL_SEC_NOPLAINTEXT
| SASL_SEC_NOACTIVE
| SASL_SEC_NOANONYMOUS
| SASL_SEC_MUTUAL_AUTH
| SASL_SEC_PASS_CREDENTIALS, /* security_flags */
SASL_FEAT_NEEDSERVERFQDN
| SASL_FEAT_WANT_CLIENT_FIRST
| SASL_FEAT_ALLOWS_PROXY
| SASL_FEAT_SUPPORTS_HTTP, /* features */
gssapi_required_prompts, /* required_prompts */
&gss_spnego_oid, /* glob_context */
&gssapi_client_mech_new, /* mech_new */
&gssapi_client_mech_step, /* mech_step */
&gssapi_common_mech_dispose, /* mech_dispose */
&gssapi_common_mech_free, /* mech_free */
NULL, /* idle */
NULL, /* spare */
NULL /* spare */
}
#endif
}; };
int gssapiv2_client_plug_init(const sasl_utils_t *utils __attribute__((unus ed)), int gssapiv2_client_plug_init(const sasl_utils_t *utils __attribute__((unus ed)),
int maxversion, int maxversion,
int *out_version, int *out_version,
sasl_client_plug_t **pluglist, sasl_client_plug_t **pluglist,
int *plugcount) int *plugcount)
{ {
if (maxversion < SASL_CLIENT_PLUG_VERSION) { if (maxversion < SASL_CLIENT_PLUG_VERSION) {
SETERROR(utils, "Version mismatch in GSSAPI"); SETERROR(utils, "Version mismatch in GSSAPI");
return SASL_BADVERS; return SASL_BADVERS;
} }
*out_version = SASL_CLIENT_PLUG_VERSION; *out_version = SASL_CLIENT_PLUG_VERSION;
*pluglist = gssapi_client_plugins; *pluglist = gssapi_client_plugins;
#ifdef HAVE_GSS_SPNEGO
*plugcount = 2;
#else
*plugcount = 1; *plugcount = 1;
#endif
#ifdef GSS_USE_MUTEXES #ifdef GSS_USE_MUTEXES
if(!gss_mutex) { if(!gss_mutex) {
gss_mutex = utils->mutex_alloc(); gss_mutex = utils->mutex_alloc();
if(!gss_mutex) { if(!gss_mutex) {
return SASL_FAIL; return SASL_FAIL;
} }
} }
#endif #endif
 End of changes. 108 change blocks. 
511 lines changed or deleted 645 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/