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