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.92 2004/07/21 14:39:06 rjs3 Exp $ | * $Id: gssapi.c,v 1.112 2011/04/19 09:19:18 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 85 | skipping to change at line 85 | |||
#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> | |||
/***************************** Common Section *************************** **/ | /***************************** Common Section *************************** **/ | |||
static const char plugin_id[] = "$Id: gssapi.c,v 1.92 2004/07/21 14:39:06 r js3 Exp $"; | static const char plugin_id[] = "$Id: gssapi.c,v 1.112 2011/04/19 09:19:18 mel Exp $"; | |||
static const char * GSSAPI_BLANK_STRING = ""; | static const char * GSSAPI_BLANK_STRING = ""; | |||
#ifndef HAVE_GSS_C_NT_HOSTBASED_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 153 | skipping to change at line 153 | |||
int state; | int state; | |||
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 */ | ||||
const sasl_utils_t *utils; | const sasl_utils_t *utils; | |||
/* layers buffering */ | /* layers buffering */ | |||
decode_context_t decode_context; | decode_context_t decode_context; | |||
char *encode_buf; /* For encoding/decoding mem managemen t */ | char *encode_buf; /* For encoding/decoding mem managemen t */ | |||
char *decode_buf; | char *decode_buf; | |||
char *decode_once_buf; | char *decode_once_buf; | |||
unsigned encode_buf_len; | unsigned encode_buf_len; | |||
unsigned decode_buf_len; | unsigned decode_buf_len; | |||
skipping to change at line 180 | skipping to change at line 182 | |||
const char *user; /* hold the userid between steps - client */ | const char *user; /* hold the userid between steps - client */ | |||
} context_t; | } context_t; | |||
enum { | enum { | |||
SASL_GSSAPI_STATE_AUTHNEG = 1, | SASL_GSSAPI_STATE_AUTHNEG = 1, | |||
SASL_GSSAPI_STATE_SSFCAP = 2, | SASL_GSSAPI_STATE_SSFCAP = 2, | |||
SASL_GSSAPI_STATE_SSFREQ = 3, | SASL_GSSAPI_STATE_SSFREQ = 3, | |||
SASL_GSSAPI_STATE_AUTHENTICATED = 4 | SASL_GSSAPI_STATE_AUTHENTICATED = 4 | |||
}; | }; | |||
#define LAYER_CONFIDENTIALITY 4 | ||||
#define LAYER_INTEGRITY 2 | ||||
#define LAYER_NONE 1 | ||||
/* sasl_gss_log: only logs status string returned from gss_display_status() */ | /* sasl_gss_log: only logs status string returned from gss_display_status() */ | |||
#define sasl_gss_log(x,y,z) sasl_gss_seterror_(x,y,z,1) | #define sasl_gss_log(x,y,z) sasl_gss_seterror_(x,y,z,1) | |||
#define sasl_gss_seterror(x,y,z) sasl_gss_seterror_(x,y,z,0) | #define sasl_gss_seterror(x,y,z) sasl_gss_seterror_(x,y,z,0) | |||
static int | static int | |||
sasl_gss_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min, | sasl_gss_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min, | |||
int logonly) | int logonly) | |||
{ | { | |||
OM_uint32 maj_stat, min_stat; | OM_uint32 maj_stat, min_stat; | |||
gss_buffer_desc msg; | gss_buffer_desc msg; | |||
OM_uint32 msg_ctx; | OM_uint32 msg_ctx; | |||
int ret; | int ret; | |||
char *out = NULL; | char *out = NULL; | |||
size_t len, curlen = 0; | size_t len, curlen = 0; | |||
const char prefix[] = "GSSAPI Error: "; | const char prefix[] = "GSSAPI Error: "; | |||
if (!utils) return SASL_OK; | ||||
len = sizeof(prefix); | len = sizeof(prefix); | |||
ret = _plug_buf_alloc(utils, &out, &curlen, 256); | ret = _plug_buf_alloc(utils, &out, &curlen, 256); | |||
if(ret != SASL_OK) return SASL_OK; | if (ret != SASL_OK) return SASL_NOMEM; | |||
strcpy(out, prefix); | strcpy(out, prefix); | |||
msg_ctx = 0; | msg_ctx = 0; | |||
while (1) { | while (1) { | |||
GSS_LOCK_MUTEX(utils); | GSS_LOCK_MUTEX(utils); | |||
maj_stat = gss_display_status(&min_stat, maj, | maj_stat = gss_display_status(&min_stat, maj, | |||
GSS_C_GSS_CODE, GSS_C_NULL_OID, | GSS_C_GSS_CODE, GSS_C_NULL_OID, | |||
&msg_ctx, &msg); | &msg_ctx, &msg); | |||
GSS_UNLOCK_MUTEX(utils); | GSS_UNLOCK_MUTEX(utils); | |||
skipping to change at line 228 | skipping to change at line 236 | |||
} | } | |||
utils->free(out); | utils->free(out); | |||
return SASL_OK; | return SASL_OK; | |||
} | } | |||
len += len + msg.length; | len += len + msg.length; | |||
ret = _plug_buf_alloc(utils, &out, &curlen, len); | ret = _plug_buf_alloc(utils, &out, &curlen, len); | |||
if(ret != SASL_OK) { | if(ret != SASL_OK) { | |||
utils->free(out); | utils->free(out); | |||
return SASL_OK; | return SASL_NOMEM; | |||
} | } | |||
strcat(out, msg.value); | strcat(out, msg.value); | |||
GSS_LOCK_MUTEX(utils); | GSS_LOCK_MUTEX(utils); | |||
gss_release_buffer(&min_stat, &msg); | gss_release_buffer(&min_stat, &msg); | |||
GSS_UNLOCK_MUTEX(utils); | GSS_UNLOCK_MUTEX(utils); | |||
if (!msg_ctx) | if (!msg_ctx) | |||
break; | break; | |||
skipping to change at line 321 | skipping to change at line 329 | |||
sasl_gss_encode(void *context, const struct iovec *invec, unsigned numiov, | sasl_gss_encode(void *context, const struct iovec *invec, unsigned numiov, | |||
const char **output, unsigned *outputlen, int privacy) | const char **output, unsigned *outputlen, int privacy) | |||
{ | { | |||
context_t *text = (context_t *)context; | context_t *text = (context_t *)context; | |||
OM_uint32 maj_stat, min_stat; | OM_uint32 maj_stat, min_stat; | |||
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; | |||
int ret; | int ret; | |||
struct buffer_info *inblob, bufinfo; | struct buffer_info *inblob, bufinfo; | |||
if(!output) return SASL_BADPARAM; | if (!output) return SASL_BADPARAM; | |||
if(numiov > 1) { | if (numiov > 1) { | |||
ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_b uf); | ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_b uf); | |||
if(ret != SASL_OK) return ret; | if (ret != SASL_OK) return ret; | |||
inblob = text->enc_in_buf; | inblob = text->enc_in_buf; | |||
} else { | } else { | |||
bufinfo.data = invec[0].iov_base; | bufinfo.data = invec[0].iov_base; | |||
bufinfo.curlen = invec[0].iov_len; | bufinfo.curlen = invec[0].iov_len; | |||
inblob = &bufinfo; | inblob = &bufinfo; | |||
} | } | |||
if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) return SASL_NOTDONE ; | if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) return SASL_NOTDONE ; | |||
input_token = &real_input_token; | input_token = &real_input_token; | |||
skipping to change at line 354 | skipping to change at line 362 | |||
GSS_LOCK_MUTEX(text->utils); | GSS_LOCK_MUTEX(text->utils); | |||
maj_stat = gss_wrap (&min_stat, | maj_stat = gss_wrap (&min_stat, | |||
text->gss_ctx, | text->gss_ctx, | |||
privacy, | privacy, | |||
GSS_C_QOP_DEFAULT, | GSS_C_QOP_DEFAULT, | |||
input_token, | input_token, | |||
NULL, | NULL, | |||
output_token); | output_token); | |||
GSS_UNLOCK_MUTEX(text->utils); | GSS_UNLOCK_MUTEX(text->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); | 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) { | |||
int len; | unsigned char * p = (unsigned char *) text->encode_buf; | |||
ret = _plug_buf_alloc(text->utils, &(text->encode_buf), | ret = _plug_buf_alloc(text->utils, | |||
&(text->encode_buf_len), output_token->length | &(text->encode_buf), | |||
+ 4); | &(text->encode_buf_len), | |||
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; | |||
} | } | |||
len = htonl(output_token->length); | p[0] = (output_token->length>>24) & 0xFF; | |||
memcpy(text->encode_buf, &len, 4); | p[1] = (output_token->length>>16) & 0xFF; | |||
p[2] = (output_token->length>>8) & 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; | |||
} | } | |||
*output = text->encode_buf; | *output = text->encode_buf; | |||
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_OK; | return SASL_OK; | |||
} | } | |||
static int gssapi_privacy_encode(void *context, const struct iovec *invec, | static int gssapi_privacy_encode(void *context, const struct iovec *invec, | |||
unsigned numiov, const char **output, | unsigned numiov, const char **output, | |||
unsigned *outputlen) | unsigned *outputlen) | |||
{ | { | |||
return sasl_gss_encode(context,invec,numiov,output,outputlen,1); | return sasl_gss_encode(context,invec,numiov,output,outputlen,1); | |||
} | } | |||
static int gssapi_integrity_encode(void *context, const struct iovec *invec , | static int gssapi_integrity_encode(void *context, const struct iovec *invec , | |||
unsigned numiov, const char **output, | unsigned numiov, const char **output, | |||
unsigned *outputlen) | unsigned *outputlen) | |||
{ | { | |||
return sasl_gss_encode(context,invec,numiov,output,outputlen,0); | return sasl_gss_encode(context,invec,numiov,output,outputlen,0); | |||
} | } | |||
static int gssapi_decode_packet(void *context, | static int | |||
const char *input, unsigned inputlen, | gssapi_decode_packet(void *context, | |||
char **output, unsigned *outputlen) | const char *input, | |||
unsigned inputlen, | ||||
char **output, | ||||
unsigned *outputlen) | ||||
{ | { | |||
context_t *text = (context_t *) context; | context_t *text = (context_t *) context; | |||
OM_uint32 maj_stat, min_stat; | OM_uint32 maj_stat, min_stat; | |||
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; | |||
int result; | int result; | |||
if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) { | if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) { | |||
SETERROR(text->utils, "GSSAPI Failure"); | SETERROR(text->utils, "GSSAPI Failure"); | |||
return SASL_NOTDONE; | return SASL_NOTDONE; | |||
skipping to change at line 443 | skipping to change at line 459 | |||
GSS_LOCK_MUTEX(text->utils); | GSS_LOCK_MUTEX(text->utils); | |||
maj_stat = gss_unwrap (&min_stat, | maj_stat = gss_unwrap (&min_stat, | |||
text->gss_ctx, | text->gss_ctx, | |||
input_token, | input_token, | |||
output_token, | output_token, | |||
NULL, | NULL, | |||
NULL); | NULL); | |||
GSS_UNLOCK_MUTEX(text->utils); | GSS_UNLOCK_MUTEX(text->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); | 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 (outputlen) | if (outputlen) { | |||
*outputlen = output_token->length; | *outputlen = output_token->length; | |||
} | ||||
if (output_token->value) { | if (output_token->value) { | |||
if (output) { | if (output) { | |||
result = _plug_buf_alloc(text->utils, &text->decode_once_buf, | result = _plug_buf_alloc(text->utils, &text->decode_once_buf, | |||
&text->decode_once_buf_len, | &text->decode_once_buf_len, | |||
*outputlen); | *outputlen); | |||
if(result != SASL_OK) { | if (result != 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 result; | return result; | |||
} | } | |||
*output = text->decode_once_buf; | *output = text->decode_once_buf; | |||
memcpy(*output, output_token->value, *outputlen); | memcpy(*output, output_token->value, *outputlen); | |||
} | } | |||
GSS_LOCK_MUTEX(text->utils); | GSS_LOCK_MUTEX(text->utils); | |||
gss_release_buffer(&min_stat, output_token); | gss_release_buffer(&min_stat, output_token); | |||
skipping to change at line 644 | skipping to change at line 660 | |||
unsigned *serveroutlen, | unsigned *serveroutlen, | |||
sasl_out_params_t *oparams) | sasl_out_params_t *oparams) | |||
{ | { | |||
context_t *text = (context_t *)conn_context; | 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; | OM_uint32 max_input; | |||
gss_buffer_desc name_token; | gss_buffer_desc name_token; | |||
int ret, out_flags = 0 ; | int ret, out_flags = 0 ; | |||
gss_cred_id_t server_creds = params->gss_creds; | ||||
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(!serverout) { | |||
PARAMERROR(text->utils); | PARAMERROR(text->utils); | |||
return SASL_BADPARAM; | return SASL_BADPARAM; | |||
} | } | |||
*serverout = NULL; | *serverout = NULL; | |||
*serveroutlen = 0; | *serveroutlen = 0; | |||
if (text == NULL) { | ||||
return SASL_BADPROT; | ||||
} | ||||
switch (text->state) { | switch (text->state) { | |||
case SASL_GSSAPI_STATE_AUTHNEG: | case SASL_GSSAPI_STATE_AUTHNEG: | |||
if (text->server_name == GSS_C_NO_NAME) { /* only once */ | if (text->server_name == GSS_C_NO_NAME) { /* only once */ | |||
if (params->serverFQDN == NULL | ||||
|| strlen(params->serverFQDN) == 0) { | ||||
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.length = strlen(params->service) + 1 + strlen(params- >serverFQDN); | |||
name_token.value = (char *)params->utils->malloc((name_token.len gth + 1) * sizeof(char)); | name_token.value = (char *)params->utils->malloc((name_token.len gth + 1) * sizeof(char)); | |||
if (name_token.value == NULL) { | if (name_token.value == NULL) { | |||
MEMERROR(text->utils); | MEMERROR(text->utils); | |||
sasl_gss_free_context_contents(text); | sasl_gss_free_context_contents(text); | |||
return SASL_NOMEM; | return SASL_NOMEM; | |||
} | } | |||
sprintf(name_token.value,"%s@%s", params->service, params->serve rFQDN); | sprintf(name_token.value,"%s@%s", params->service, params->serve rFQDN); | |||
GSS_LOCK_MUTEX(params->utils); | GSS_LOCK_MUTEX(params->utils); | |||
skipping to change at line 694 | skipping to change at line 721 | |||
return SASL_FAIL; | return SASL_FAIL; | |||
} | } | |||
if ( text->server_creds != GSS_C_NO_CREDENTIAL) { | if ( text->server_creds != GSS_C_NO_CREDENTIAL) { | |||
GSS_LOCK_MUTEX(params->utils); | GSS_LOCK_MUTEX(params->utils); | |||
maj_stat = gss_release_cred(&min_stat, &text->server_creds); | maj_stat = gss_release_cred(&min_stat, &text->server_creds); | |||
GSS_UNLOCK_MUTEX(params->utils); | GSS_UNLOCK_MUTEX(params->utils); | |||
text->server_creds = GSS_C_NO_CREDENTIAL; | text->server_creds = GSS_C_NO_CREDENTIAL; | |||
} | } | |||
GSS_LOCK_MUTEX(params->utils); | /* If caller didn't provide creds already */ | |||
maj_stat = gss_acquire_cred(&min_stat, | if ( server_creds == GSS_C_NO_CREDENTIAL) { | |||
text->server_name, | GSS_LOCK_MUTEX(params->utils); | |||
GSS_C_INDEFINITE, | maj_stat = gss_acquire_cred(&min_stat, | |||
GSS_C_NO_OID_SET, | text->server_name, | |||
GSS_C_ACCEPT, | GSS_C_INDEFINITE, | |||
&text->server_creds, | GSS_C_NO_OID_SET, | |||
NULL, | GSS_C_ACCEPT, | |||
NULL); | &text->server_creds, | |||
GSS_UNLOCK_MUTEX(params->utils); | 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 (clientinlen) { | if (clientinlen) { | |||
real_input_token.value = (void *)clientin; | real_input_token.value = (void *)clientin; | |||
real_input_token.length = clientinlen; | real_input_token.length = clientinlen; | |||
} | } | |||
GSS_LOCK_MUTEX(params->utils); | GSS_LOCK_MUTEX(params->utils); | |||
maj_stat = | maj_stat = | |||
gss_accept_sec_context(&min_stat, | gss_accept_sec_context(&min_stat, | |||
&(text->gss_ctx), | &(text->gss_ctx), | |||
text->server_creds, | server_creds, | |||
input_token, | input_token, | |||
GSS_C_NO_CHANNEL_BINDINGS, | GSS_C_NO_CHANNEL_BINDINGS, | |||
&text->client_name, | &text->client_name, | |||
NULL, | NULL, /* resulting mech_name */ | |||
output_token, | output_token, | |||
&out_flags, | &out_flags, | |||
NULL, | NULL, /* context validity period * / | |||
&(text->client_creds)); | &(text->client_creds)); | |||
GSS_UNLOCK_MUTEX(params->utils); | GSS_UNLOCK_MUTEX(params->utils); | |||
if (GSS_ERROR(maj_stat)) { | if (GSS_ERROR(maj_stat)) { | |||
sasl_gss_log(text->utils, maj_stat, min_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"); | text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Fai lure: gss_accept_sec_context"); | |||
if (output_token->value) { | 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); | sasl_gss_free_context_contents(text); | |||
return SASL_BADAUTH; | return SASL_BADAUTH; | |||
} | } | |||
/* When GSS_Accept_sec_context returns GSS_S_COMPLETE, the server | ||||
examines the context to ensure that it provides a level of protec | ||||
tion | ||||
permitted by the server's security policy. In particular, if the | ||||
integ_avail flag is not set in the context, then no security laye | ||||
r | ||||
can be offered or accepted. If the conf_avail flag is not set in | ||||
the | ||||
context, then no security layer with confidentiality can be offer | ||||
ed | ||||
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. */ | ||||
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) | if (serveroutlen) | |||
skipping to change at line 775 | skipping to change at line 826 | |||
memcpy(text->out_buf, output_token->value, *serveroutlen); | memcpy(text->out_buf, output_token->value, *serveroutlen); | |||
*serverout = text->out_buf; | *serverout = text->out_buf; | |||
} | } | |||
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); | |||
} else { | } else { | |||
/* No output token, send an empty string */ | /* No output token, send an empty string */ | |||
*serverout = GSSAPI_BLANK_STRING; | *serverout = GSSAPI_BLANK_STRING; | |||
serveroutlen = 0; | *serveroutlen = 0; | |||
} | } | |||
if (maj_stat == GSS_S_COMPLETE) { | if (maj_stat == GSS_S_COMPLETE) { | |||
/* Switch to ssf negotiation */ | /* Switch to ssf negotiation */ | |||
text->state = SASL_GSSAPI_STATE_SSFCAP; | text->state = SASL_GSSAPI_STATE_SSFCAP; | |||
} | ||||
return SASL_CONTINUE; | if (*serveroutlen != 0) { | |||
return SASL_CONTINUE; | ||||
} | ||||
/* Pretend that we just got an empty response from the client */ | ||||
clientinlen = 0; | ||||
/* fall through */ | ||||
} else { | ||||
return SASL_CONTINUE; | ||||
} | ||||
case SASL_GSSAPI_STATE_SSFCAP: { | case SASL_GSSAPI_STATE_SSFCAP: { | |||
unsigned char sasldata[4]; | unsigned char sasldata[4]; | |||
gss_buffer_desc name_token; | gss_buffer_desc name_token; | |||
gss_buffer_desc name_without_realm; | gss_buffer_desc name_without_realm; | |||
gss_name_t without = NULL; | gss_name_t without = NULL; | |||
int equal; | int equal; | |||
name_token.value = NULL; | name_token.value = NULL; | |||
name_without_realm.value = NULL; | name_without_realm.value = NULL; | |||
/* We ignore whatever the client sent us at this stage */ | if (clientinlen != 0) { | |||
SETERROR(text->utils, "GSSAPI server is not expecting data at th | ||||
is stage"); | ||||
sasl_gss_free_context_contents(text); | ||||
return SASL_BADAUTH; | ||||
} | ||||
GSS_LOCK_MUTEX(params->utils); | GSS_LOCK_MUTEX(params->utils); | |||
maj_stat = gss_display_name (&min_stat, | maj_stat = gss_display_name (&min_stat, | |||
text->client_name, | text->client_name, | |||
&name_token, | &name_token, | |||
NULL); | NULL); | |||
GSS_UNLOCK_MUTEX(params->utils); | GSS_UNLOCK_MUTEX(params->utils); | |||
if (GSS_ERROR(maj_stat)) { | if (GSS_ERROR(maj_stat)) { | |||
if (without) { | ||||
GSS_LOCK_MUTEX(params->utils); | ||||
gss_release_name(&min_stat, &without); | ||||
GSS_UNLOCK_MUTEX(params->utils); | ||||
} | ||||
SETERROR(text->utils, "GSSAPI Failure"); | SETERROR(text->utils, "GSSAPI Failure"); | |||
sasl_gss_free_context_contents(text); | sasl_gss_free_context_contents(text); | |||
return SASL_BADAUTH; | return SASL_BADAUTH; | |||
} | } | |||
/* If the id contains a realm get the identifier for the user | /* 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. | 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 wa nt | tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just wa nt | |||
to return the id (i.e. just "tmartin" */ | to return the id (i.e. just "tmartin" */ | |||
if (strchr((char *) name_token.value, (int) '@') != NULL) { | if (strchr((char *) name_token.value, (int) '@') != NULL) { | |||
skipping to change at line 936 | skipping to change at line 995 | |||
} else { | } else { | |||
text->limitssf = params->props.max_ssf - params->external_ssf; | text->limitssf = params->props.max_ssf - params->external_ssf; | |||
} | } | |||
if (params->props.min_ssf < params->external_ssf) { | if (params->props.min_ssf < params->external_ssf) { | |||
text->requiressf = 0; | text->requiressf = 0; | |||
} else { | } else { | |||
text->requiressf = params->props.min_ssf - params->external_ssf; | text->requiressf = params->props.min_ssf - params->external_ssf; | |||
} | } | |||
/* build up our security properties token */ | /* build up our security properties token */ | |||
if (params->props.maxbufsize > 0xFFFFFF) { | if (text->requiressf != 0 && | |||
/* make sure maxbufsize isn't too large */ | (text->qop & (LAYER_INTEGRITY|LAYER_CONFIDENTIALITY))) { | |||
/* maxbufsize = 0xFFFFFF */ | if (params->props.maxbufsize > 0xFFFFFF) { | |||
sasldata[1] = sasldata[2] = sasldata[3] = 0xFF; | /* make sure maxbufsize isn't too large */ | |||
} else { | /* maxbufsize = 0xFFFFFF */ | |||
sasldata[1] = (params->props.maxbufsize >> 16) & 0xFF; | sasldata[1] = sasldata[2] = sasldata[3] = 0xFF; | |||
sasldata[2] = (params->props.maxbufsize >> 8) & 0xFF; | } else { | |||
sasldata[3] = (params->props.maxbufsize >> 0) & 0xFF; | 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; | sasldata[0] = 0; | |||
if(text->requiressf != 0 && !params->props.maxbufsize) { | if(text->requiressf != 0 && !params->props.maxbufsize) { | |||
params->utils->seterror(params->utils->conn, 0, | params->utils->seterror(params->utils->conn, 0, | |||
"GSSAPI needs a security layer but one i s forbidden"); | "GSSAPI needs a security layer but one i s forbidden"); | |||
return SASL_TOOWEAK; | return SASL_TOOWEAK; | |||
} | } | |||
if (text->requiressf == 0) { | if (text->requiressf == 0) { | |||
sasldata[0] |= 1; /* authentication */ | sasldata[0] |= LAYER_NONE; /* authentication */ | |||
} | } | |||
if (text->requiressf <= 1 && text->limitssf >= 1 | if ((text->qop & LAYER_INTEGRITY) && | |||
&& params->props.maxbufsize) { | text->requiressf <= 1 && | |||
sasldata[0] |= 2; | text->limitssf >= 1 && | |||
} | params->props.maxbufsize) { | |||
if (text->requiressf <= K5_MAX_SSF && text->limitssf >= K5_MAX_SSF | sasldata[0] |= LAYER_INTEGRITY; | |||
&& params->props.maxbufsize) { | } | |||
sasldata[0] |= 4; | 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.value = (void *)sasldata; | |||
real_input_token.length = 4; | real_input_token.length = 4; | |||
GSS_LOCK_MUTEX(params->utils); | GSS_LOCK_MUTEX(params->utils); | |||
maj_stat = gss_wrap(&min_stat, | maj_stat = gss_wrap(&min_stat, | |||
text->gss_ctx, | text->gss_ctx, | |||
0, /* Just integrity checking here */ | 0, /* Just integrity checking here */ | |||
GSS_C_QOP_DEFAULT, | GSS_C_QOP_DEFAULT, | |||
skipping to change at line 1009 | skipping to change at line 1080 | |||
} | } | |||
memcpy(text->out_buf, output_token->value, *serveroutlen); | memcpy(text->out_buf, output_token->value, *serveroutlen); | |||
*serverout = text->out_buf; | *serverout = text->out_buf; | |||
} | } | |||
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); | |||
} | } | |||
/* Remember what we want and can offer */ | ||||
text->qop = sasldata[0]; | ||||
/* Wait for ssf request and authid */ | /* Wait for ssf request and authid */ | |||
text->state = SASL_GSSAPI_STATE_SSFREQ; | text->state = SASL_GSSAPI_STATE_SSFREQ; | |||
return SASL_CONTINUE; | return SASL_CONTINUE; | |||
} | } | |||
case SASL_GSSAPI_STATE_SSFREQ: { | case SASL_GSSAPI_STATE_SSFREQ: { | |||
int layerchoice; | int layerchoice; | |||
real_input_token.value = (void *)clientin; | real_input_token.value = (void *)clientin; | |||
skipping to change at line 1036 | skipping to change at line 1110 | |||
NULL, | NULL, | |||
NULL); | NULL); | |||
GSS_UNLOCK_MUTEX(params->utils); | 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; | |||
} | } | |||
if (output_token->length < 4) { | ||||
SETERROR(text->utils, | ||||
"token too short"); | ||||
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; | ||||
} | ||||
layerchoice = (int)(((char *)(output_token->value))[0]); | layerchoice = (int)(((char *)(output_token->value))[0]); | |||
if (layerchoice == 1 && text->requiressf == 0) { /* no encryption */ | if (layerchoice == LAYER_NONE && | |||
(text->qop & LAYER_NONE)) { /* no encryption */ | ||||
oparams->encode = NULL; | oparams->encode = NULL; | |||
oparams->decode = NULL; | oparams->decode = NULL; | |||
oparams->mech_ssf = 0; | oparams->mech_ssf = 0; | |||
} else if (layerchoice == 2 && text->requiressf <= 1 && | } else if (layerchoice == LAYER_INTEGRITY && | |||
text->limitssf >= 1) { /* integrity */ | (text->qop & LAYER_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; | |||
} else if (layerchoice == 4 && text->requiressf <= K5_MAX_SSF && | } else if ((layerchoice == LAYER_CONFIDENTIALITY || | |||
text->limitssf >= K5_MAX_SSF) { /* privacy */ | /* 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->encode = &gssapi_privacy_encode; | |||
oparams->decode = &gssapi_decode; | oparams->decode = &gssapi_decode; | |||
/* FIX ME: Need to extract the proper value here */ | /* FIX ME: Need to extract the proper value here */ | |||
oparams->mech_ssf = K5_MAX_SSF; | oparams->mech_ssf = K5_MAX_SSF; | |||
} else { | } else { | |||
/* not a supported encryption layer */ | /* not a supported encryption layer */ | |||
SETERROR(text->utils, | SETERROR(text->utils, | |||
"protocol violation: client requested invalid layer"); | "protocol violation: client requested invalid layer"); | |||
/* Mark that we attempted negotiation */ | /* Mark that we attempted negotiation */ | |||
oparams->mech_ssf = 2; | oparams->mech_ssf = 2; | |||
skipping to change at line 1083 | skipping to change at line 1170 | |||
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, | ret = params->canon_user(params->utils->conn, | |||
text->authid, | text->authid, | |||
0, /* strlen(text->authid) */ | 0, /* strlen(text->authid) */ | |||
SASL_CU_AUTHID, oparams); | SASL_CU_AUTHID | SASL_CU_EXTERNALLY_VER IFIED, 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; | |||
} | } | |||
} else if(output_token->length == 4) { | } else /* if (output_token->length == 4) */ { | |||
/* null authzid */ | /* null authzid */ | |||
int ret; | int ret; | |||
ret = params->canon_user(params->utils->conn, | ret = params->canon_user(params->utils->conn, | |||
text->authid, | text->authid, | |||
0, /* strlen(text->authid) */ | 0, /* strlen(text->authid) */ | |||
SASL_CU_AUTHZID | SASL_CU_AUTHID, | SASL_CU_AUTHZID | SASL_CU_AUTHID | SASL _CU_EXTERNALLY_VERIFIED, | |||
oparams); | 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; | |||
} | } | |||
} else { | ||||
SETERROR(text->utils, | ||||
"token too short"); | ||||
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; | ||||
} | } | |||
/* No matter what, set the rest of the oparams */ | /* No matter what, set the rest of the oparams */ | |||
if (text->client_creds != GSS_C_NO_CREDENTIAL) { | if (text->client_creds != GSS_C_NO_CREDENTIAL) { | |||
oparams->client_creds = &text->client_creds; | oparams->client_creds = &text->client_creds; | |||
} | } | |||
else { | else { | |||
oparams->client_creds = NULL; | oparams->client_creds = NULL; | |||
} | } | |||
skipping to change at line 1150 | skipping to change at line 1229 | |||
} | } | |||
} | } | |||
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); | |||
text->state = SASL_GSSAPI_STATE_AUTHENTICATED; | text->state = SASL_GSSAPI_STATE_AUTHENTICATED; | |||
/* used by layers */ | /* used by layers */ | |||
_plug_decode_init(&text->decode_context, text->utils, | _plug_decode_init(&text->decode_context, | |||
text->utils, | ||||
(params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : | (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : | |||
params->props.maxbufsize); | params->props.maxbufsize); | |||
oparams->doneflag = 1; | oparams->doneflag = 1; | |||
return SASL_OK; | return SASL_OK; | |||
} | } | |||
default: | default: | |||
params->utils->log(NULL, SASL_LOG_ERR, | params->utils->log(NULL, SASL_LOG_ERR, | |||
skipping to change at line 1179 | skipping to change at line 1259 | |||
{ | { | |||
{ | { | |||
"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 /* security_flags */ | | SASL_SEC_MUTUAL_AUTH /* security_flags */ | |||
| SASL_SEC_PASS_CREDENTIALS, | | SASL_SEC_PASS_CREDENTIALS, | |||
SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_WANT_CLIENT_FIRST | |||
| SASL_FEAT_ALLOWS_PROXY, /* features */ | | SASL_FEAT_ALLOWS_PROXY | |||
| SASL_FEAT_DONTUSE_USERPASSWD, /* features */ | ||||
NULL, /* glob_context */ | NULL, /* glob_context */ | |||
&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 */ | |||
skipping to change at line 1305 | skipping to change at line 1386 | |||
OM_uint32 maj_stat = 0, min_stat = 0; | OM_uint32 maj_stat = 0, min_stat = 0; | |||
OM_uint32 max_input; | OM_uint32 max_input; | |||
gss_buffer_desc name_token; | gss_buffer_desc name_token; | |||
int ret; | int ret; | |||
OM_uint32 req_flags = 0, out_req_flags = 0; | OM_uint32 req_flags = 0, out_req_flags = 0; | |||
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; | ||||
*clientout = NULL; | *clientout = NULL; | |||
*clientoutlen = 0; | *clientoutlen = 0; | |||
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; | |||
skipping to change at line 1348 | skipping to change at line 1430 | |||
NULL, NULL, | NULL, NULL, | |||
NULL, NULL, NULL, | NULL, NULL, NULL, | |||
NULL, NULL, NULL); | NULL, NULL, NULL); | |||
if (result != SASL_OK) return result; | if (result != SASL_OK) return result; | |||
return SASL_INTERACT; | return SASL_INTERACT; | |||
} | } | |||
} | } | |||
if (text->server_name == GSS_C_NO_NAME) { /* only once */ | if (text->server_name == GSS_C_NO_NAME) { /* only once */ | |||
if (params->serverFQDN == NULL | ||||
|| strlen(params->serverFQDN) == 0) { | ||||
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.length = strlen(params->service) + 1 + strlen(params- >serverFQDN); | |||
name_token.value = (char *)params->utils->malloc((name_token.len gth + 1) * sizeof(char)); | name_token.value = (char *)params->utils->malloc((name_token.len gth + 1) * sizeof(char)); | |||
if (name_token.value == NULL) { | if (name_token.value == NULL) { | |||
sasl_gss_free_context_contents(text); | sasl_gss_free_context_contents(text); | |||
return SASL_NOMEM; | return SASL_NOMEM; | |||
} | } | |||
if (params->serverFQDN == NULL | ||||
|| strlen(params->serverFQDN) == 0) { | ||||
SETERROR(text->utils, "GSSAPI Failure: no serverFQDN"); | ||||
return SASL_FAIL; | ||||
} | ||||
sprintf(name_token.value,"%s@%s", params->service, params->serve rFQDN); | sprintf(name_token.value,"%s@%s", params->service, params->serve rFQDN); | |||
GSS_LOCK_MUTEX(params->utils); | GSS_LOCK_MUTEX(params->utils); | |||
maj_stat = gss_import_name (&min_stat, | maj_stat = gss_import_name (&min_stat, | |||
&name_token, | &name_token, | |||
GSS_C_NT_HOSTBASED_SERVICE, | GSS_C_NT_HOSTBASED_SERVICE, | |||
&text->server_name); | &text->server_name); | |||
GSS_UNLOCK_MUTEX(params->utils); | GSS_UNLOCK_MUTEX(params->utils); | |||
skipping to change at line 1398 | skipping to change at line 1481 | |||
* and no input from the server. However, thanks to Imap, | * and no input from the server. However, thanks to Imap, | |||
* which discards our first output, this happens all the time. | * which discards our first output, this happens all the time. | |||
* Throw away the context and try again. */ | * Throw away the context and try again. */ | |||
GSS_LOCK_MUTEX(params->utils); | GSS_LOCK_MUTEX(params->utils); | |||
maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_ C_NO_BUFFER); | maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_ C_NO_BUFFER); | |||
GSS_UNLOCK_MUTEX(params->utils); | GSS_UNLOCK_MUTEX(params->utils); | |||
text->gss_ctx = GSS_C_NO_CONTEXT; | text->gss_ctx = GSS_C_NO_CONTEXT; | |||
} | } | |||
/* Setup req_flags properly */ | /* Setup req_flags properly */ | |||
req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; | req_flags = GSS_C_INTEG_FLAG; | |||
if(params->props.max_ssf > params->external_ssf) { | if (params->props.max_ssf > params->external_ssf) { | |||
/* We are requesting a security layer */ | /* We are requesting a security layer */ | |||
req_flags |= GSS_C_INTEG_FLAG; | req_flags |= GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; | |||
/* Any SSF bigger than 1 is confidentiality. */ | /* Any SSF bigger than 1 is confidentiality. */ | |||
/* Let's check if the client of the API requires confidentiality , | /* Let's check if the client of the API requires confidentiality , | |||
and it wasn't already provided by an external layer */ | and it wasn't already provided by an external layer */ | |||
if(params->props.max_ssf - params->external_ssf > 1) { | if (params->props.max_ssf - params->external_ssf > 1) { | |||
/* We want to try for privacy */ | /* We want to try for privacy */ | |||
req_flags |= GSS_C_CONF_FLAG; | req_flags |= GSS_C_CONF_FLAG; | |||
} | } | |||
} | } | |||
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, | |||
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, | GSS_C_NO_OID, | |||
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, | |||
skipping to change at line 1441 | skipping to change at line 1525 | |||
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(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); | sasl_gss_free_context_contents(text); | |||
return SASL_FAIL; | return SASL_FAIL; | |||
} | } | |||
if ((out_req_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_req_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 ((out_req_flags & GSS_C_DELEG_FLAG) != (req_flags & GSS_C_DELEG_F LAG)) { | if ((out_req_flags & GSS_C_DELEG_FLAG) != (req_flags & GSS_C_DELEG_F LAG)) { | |||
text->utils->seterror(text->utils->conn, SASL_LOG_WARN, "GSSAPI warning: no credentials were passed"); | text->utils->seterror(text->utils->conn, SASL_LOG_WARN, "GSSAPI warning: no credentials were passed"); | |||
/* not a fatal error */ | /* not a fatal error */ | |||
} | } | |||
*clientoutlen = output_token->length; | *clientoutlen = output_token->length; | |||
if (output_token->value) { | if (output_token->value) { | |||
if (clientout) { | if (clientout) { | |||
ret = _plug_buf_alloc(text->utils, &(text->out_buf), | ret = _plug_buf_alloc(text->utils, &(text->out_buf), | |||
skipping to change at line 1561 | skipping to change at line 1658 | |||
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); | |||
if (output_token->value) { | 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 SASL_FAIL; | return SASL_FAIL; | |||
} | } | |||
if (output_token->length != 4) { | ||||
SETERROR(text->utils, | ||||
(output_token->length < 4) ? "token too short" : "token | ||||
too long"); | ||||
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; | ||||
} | ||||
/* taken from kerberos.c */ | /* taken from kerberos.c */ | |||
if (secprops->min_ssf > (K5_MAX_SSF + external)) { | if (secprops->min_ssf > (K5_MAX_SSF + external)) { | |||
return SASL_TOOWEAK; | return SASL_TOOWEAK; | |||
} else if (secprops->min_ssf > secprops->max_ssf) { | } else if (secprops->min_ssf > secprops->max_ssf) { | |||
return SASL_BADPARAM; | return SASL_BADPARAM; | |||
} | } | |||
/* need bits of layer -- sasl_ssf_t is unsigned so be careful */ | /* need bits of layer -- sasl_ssf_t is unsigned so be careful */ | |||
if (secprops->max_ssf >= external) { | if (secprops->max_ssf >= external) { | |||
allowed = secprops->max_ssf - external; | allowed = secprops->max_ssf - external; | |||
skipping to change at line 1584 | skipping to change at line 1691 | |||
if (secprops->min_ssf >= external) { | if (secprops->min_ssf >= external) { | |||
need = secprops->min_ssf - external; | need = secprops->min_ssf - external; | |||
} else { | } else { | |||
/* good to go */ | /* good to go */ | |||
need = 0; | need = 0; | |||
} | } | |||
/* bit mask of server support */ | /* bit mask of server support */ | |||
serverhas = ((char *)output_token->value)[0]; | serverhas = ((char *)output_token->value)[0]; | |||
/* if client didn't set use strongest layer available */ | /* use the strongest layer available */ | |||
if (allowed >= K5_MAX_SSF && need <= K5_MAX_SSF && (serverhas & 4)) | if ((text->qop & LAYER_CONFIDENTIALITY) && | |||
{ | allowed >= K5_MAX_SSF && | |||
need <= K5_MAX_SSF && | ||||
(serverhas & LAYER_CONFIDENTIALITY)) { | ||||
const char *ad_compat; | ||||
/* encryption */ | /* encryption */ | |||
oparams->encode = &gssapi_privacy_encode; | oparams->encode = &gssapi_privacy_encode; | |||
oparams->decode = &gssapi_decode; | oparams->decode = &gssapi_decode; | |||
/* FIX ME: Need to extract the proper value here */ | /* FIX ME: Need to extract the proper value here */ | |||
oparams->mech_ssf = K5_MAX_SSF; | oparams->mech_ssf = K5_MAX_SSF; | |||
mychoice = 4; | mychoice = LAYER_CONFIDENTIALITY; | |||
} else if (allowed >= 1 && need <= 1 && (serverhas & 2)) { | ||||
if (serverhas & LAYER_INTEGRITY) { | ||||
/* should we send an AD compatible choice of security layers | ||||
? */ | ||||
params->utils->getopt(params->utils->getopt_context, | ||||
"GSSAPI", | ||||
"ad_compat", | ||||
&ad_compat, | ||||
NULL); | ||||
if (ad_compat && | ||||
(ad_compat[0] == '1' || ad_compat[0] == 'y' || | ||||
(ad_compat[0] == 'o' && ad_compat[1] == 'n') || | ||||
ad_compat[0] == 't')) { | ||||
mychoice = LAYER_INTEGRITY|LAYER_CONFIDENTIALITY; | ||||
} | ||||
} | ||||
} else if ((text->qop & LAYER_INTEGRITY) && | ||||
allowed >= 1 && | ||||
need <= 1 && | ||||
(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 = 2; | mychoice = LAYER_INTEGRITY; | |||
} else if (need <= 0 && (serverhas & 1)) { | } else if (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 = 1; | 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; | |||
} | } | |||
oparams->maxoutbuf = | oparams->maxoutbuf = | |||
(((unsigned char *) output_token->value)[1] << 16) | | (((unsigned char *) output_token->value)[1] << 16) | | |||
(((unsigned char *) output_token->value)[2] << 8) | | (((unsigned char *) output_token->value)[2] << 8) | | |||
(((unsigned char *) output_token->value)[3] << 0); | (((unsigned char *) output_token->value)[3] << 0); | |||
if(oparams->mech_ssf) { | if (oparams->mech_ssf) { | |||
maj_stat = gss_wrap_size_limit( &min_stat, | maj_stat = gss_wrap_size_limit( &min_stat, | |||
text->gss_ctx, | text->gss_ctx, | |||
1, | 1, | |||
GSS_C_QOP_DEFAULT, | GSS_C_QOP_DEFAULT, | |||
(OM_uint32) oparams->maxoutbuf, | (OM_uint32) oparams->maxoutbuf, | |||
&max_input); | &max_input); | |||
if(max_input > oparams->maxoutbuf) { | if (max_input > oparams->maxoutbuf) { | |||
/* Heimdal appears to get this wrong */ | /* Heimdal appears to get this wrong */ | |||
oparams->maxoutbuf -= (max_input - oparams->maxoutbuf); | oparams->maxoutbuf -= (max_input - oparams->maxoutbuf); | |||
} else { | } else { | |||
/* This code is actually correct */ | /* This code is actually correct */ | |||
oparams->maxoutbuf = max_input; | oparams->maxoutbuf = max_input; | |||
} | } | |||
} | } | |||
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); | |||
/* oparams->user is always set, due to canon_user requirements. | /* oparams->user is always set, due to canon_user requirements. | |||
* Make sure the client actually requested it though, by checking | * Make sure the client actually requested it though, by checking | |||
* if our context was set. | * if our context was set. | |||
*/ | */ | |||
if (text->user && text->user[0]) | if (text->user && text->user[0]) { | |||
alen = strlen(oparams->user); | alen = strlen(oparams->user); | |||
else | } else { | |||
alen = 0; | alen = 0; | |||
} | ||||
input_token->length = 4 + alen; | input_token->length = 4 + alen; | |||
input_token->value = | input_token->value = | |||
(char *)params->utils->malloc((input_token->length + 1)*sizeof(c har)); | (char *)params->utils->malloc((input_token->length + 1)*sizeof(c har)); | |||
if (input_token->value == NULL) { | if (input_token->value == NULL) { | |||
sasl_gss_free_context_contents(text); | sasl_gss_free_context_contents(text); | |||
return SASL_NOMEM; | return SASL_NOMEM; | |||
} | } | |||
if (alen) | if (alen) | |||
memcpy((char *)input_token->value+4,oparams->user,alen); | memcpy((char *)input_token->value+4,oparams->user,alen); | |||
/* build up our security properties token */ | /* build up our security properties token */ | |||
if (params->props.maxbufsize > 0xFFFFFF) { | if (mychoice > 1) { | |||
/* make sure maxbufsize isn't too large */ | if (params->props.maxbufsize > 0xFFFFFF) { | |||
/* maxbufsize = 0xFFFFFF */ | /* make sure maxbufsize isn't too large */ | |||
((unsigned char *)input_token->value)[1] = 0xFF; | /* maxbufsize = 0xFFFFFF */ | |||
((unsigned char *)input_token->value)[2] = 0xFF; | ((unsigned char *)input_token->value)[1] = 0xFF; | |||
((unsigned char *)input_token->value)[3] = 0xFF; | ((unsigned char *)input_token->value)[2] = 0xFF; | |||
} else { | ((unsigned char *)input_token->value)[3] = 0xFF; | |||
((unsigned char *)input_token->value)[1] = | } else { | |||
(params->props.maxbufsize >> 16) & 0xFF; | ((unsigned char *)input_token->value)[1] = | |||
((unsigned char *)input_token->value)[2] = | (params->props.maxbufsize >> 16) & 0xFF; | |||
(params->props.maxbufsize >> 8) & 0xFF; | ((unsigned char *)input_token->value)[2] = | |||
((unsigned char *)input_token->value)[3] = | (params->props.maxbufsize >> 8) & 0xFF; | |||
(params->props.maxbufsize >> 0) & 0xFF; | ((unsigned char *)input_token->value)[3] = | |||
} | (params->props.maxbufsize >> 0) & 0xFF; | |||
} | ||||
} else { | ||||
((unsigned char *)input_token->value)[1] = 0; | ||||
((unsigned char *)input_token->value)[2] = 0; | ||||
((unsigned char *)input_token->value)[3] = 0; | ||||
} | ||||
((unsigned char *)input_token->value)[0] = mychoice; | ((unsigned char *)input_token->value)[0] = mychoice; | |||
GSS_LOCK_MUTEX(params->utils); | GSS_LOCK_MUTEX(params->utils); | |||
maj_stat = gss_wrap (&min_stat, | maj_stat = gss_wrap (&min_stat, | |||
text->gss_ctx, | text->gss_ctx, | |||
0, /* Just integrity checking here */ | 0, /* Just integrity checking here */ | |||
GSS_C_QOP_DEFAULT, | GSS_C_QOP_DEFAULT, | |||
input_token, | input_token, | |||
NULL, | NULL, | |||
output_token); | output_token); | |||
skipping to change at line 1697 | skipping to change at line 1835 | |||
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(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); | sasl_gss_free_context_contents(text); | |||
return SASL_FAIL; | return SASL_FAIL; | |||
} | } | |||
if (clientoutlen) | if (clientoutlen) { | |||
*clientoutlen = output_token->length; | *clientoutlen = output_token->length; | |||
} | ||||
if (output_token->value) { | if (output_token->value) { | |||
if (clientout) { | if (clientout) { | |||
ret = _plug_buf_alloc(text->utils, &(text->out_buf), | ret = _plug_buf_alloc(text->utils, | |||
&(text->out_buf_len), *clientoutlen); | &(text->out_buf), | |||
&(text->out_buf_len), | ||||
*clientoutlen); | ||||
if (ret != SASL_OK) { | 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; | return ret; | |||
} | } | |||
memcpy(text->out_buf, output_token->value, *clientoutlen); | memcpy(text->out_buf, output_token->value, *clientoutlen); | |||
*clientout = text->out_buf; | *clientout = text->out_buf; | |||
} | } | |||
End of changes. 74 change blocks. | ||||
141 lines changed or deleted | 293 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/ |