/* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* GSSAPI SASL plugin * Leif Johansson * Rob Siemborski (SASL v2 Conversion) * $Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $ */ /* * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any other legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifdef HAVE_GSSAPI_H #include #else #include #endif #ifdef WIN32 # include # ifndef R_OK # define R_OK 04 # endif /* we also need io.h for access() prototype */ # include #else # include # include # include # include # include #endif /* WIN32 */ #include #include #include #include #include #include "plugin_common.h" #ifdef HAVE_UNISTD_H #include #endif #include #ifdef WIN32 /* This must be after sasl.h */ # include "saslgssapi.h" #endif /* WIN32 */ /***************************** Common Section *****************************/ #ifndef _SUN_SDK_ static const char plugin_id[] = "$Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $"; #endif /* !_SUN_SDK_ */ static const char * GSSAPI_BLANK_STRING = ""; #ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE extern gss_OID gss_nt_service_name; #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name #endif #ifdef _SUN_SDK_ static int get_oid(const sasl_utils_t *utils, gss_OID *oid); #ifdef GSSAPI_PROTECT DEFINE_STATIC_MUTEX(global_mutex); #endif /* GSSAPI_PROTECT */ #endif /* _SUN_SDK_ */ /* GSSAPI SASL Mechanism by Leif Johansson * inspired by the kerberos mechanism and the gssapi_server and * gssapi_client from the heimdal distribution by Assar Westerlund * and Johan Danielsson . * See the configure.in file for details on dependencies. * Heimdal can be obtained from http://www.pdc.kth.se/heimdal * * Important contributions from Sam Hartman . */ typedef struct context { int state; gss_ctx_id_t gss_ctx; gss_name_t client_name; gss_name_t server_name; gss_cred_id_t server_creds; sasl_ssf_t limitssf, requiressf; /* application defined bounds, for the server */ #ifdef _SUN_SDK_ gss_cred_id_t client_creds; gss_OID mech_oid; int use_authid; #endif /* _SUN_SDK_ */ const sasl_utils_t *utils; /* layers buffering */ char *buffer; #ifdef _SUN_SDK_ unsigned bufsize; #else int bufsize; #endif /* _SUN_SDK_ */ char sizebuf[4]; #ifdef _SUN_SDK_ unsigned cursize; unsigned size; #else int cursize; int size; #endif /* _SUN_SDK_ */ unsigned needsize; char *encode_buf; /* For encoding/decoding mem management */ char *decode_buf; char *decode_once_buf; unsigned encode_buf_len; unsigned decode_buf_len; unsigned decode_once_buf_len; buffer_info_t *enc_in_buf; char *out_buf; /* per-step mem management */ unsigned out_buf_len; char *authid; /* hold the authid between steps - server */ const char *user; /* hold the userid between steps - client */ #ifdef _SUN_SDK_ const char *client_authid; #endif /* _SUN_SDK_ */ #ifdef _INTEGRATED_SOLARIS_ void *h; #endif /* _INTEGRATED_SOLARIS_ */ } context_t; enum { SASL_GSSAPI_STATE_AUTHNEG = 1, SASL_GSSAPI_STATE_SSFCAP = 2, SASL_GSSAPI_STATE_SSFREQ = 3, SASL_GSSAPI_STATE_AUTHENTICATED = 4 }; #ifdef _SUN_SDK_ /* sasl_gss_log only logs gss_display_status() error string */ #define sasl_gss_log(x,y,z) sasl_gss_seterror_(text,y,z,1) #define sasl_gss_seterror(x,y,z) sasl_gss_seterror_(text,y,z,0) static void sasl_gss_seterror_(const context_t *text, OM_uint32 maj, OM_uint32 min, int logonly) #else static void sasl_gss_seterror(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min) #endif /* _SUN_SDK_ */ { OM_uint32 maj_stat, min_stat; gss_buffer_desc msg; OM_uint32 msg_ctx; int ret; char *out = NULL; #ifdef _SUN_SDK_ unsigned len, curlen = 0; const sasl_utils_t *utils = text->utils; char *prefix = dgettext(TEXT_DOMAIN, "GSSAPI Error: "); #else size_t len, curlen = 0; const char prefix[] = "GSSAPI Error: "; #endif /* _SUN_SDK_ */ if(!utils) return; len = sizeof(prefix); ret = _plug_buf_alloc(utils, &out, &curlen, 256); if(ret != SASL_OK) return; strcpy(out, prefix); msg_ctx = 0; while (1) { maj_stat = gss_display_status(&min_stat, maj, #ifdef _SUN_SDK_ GSS_C_GSS_CODE, text->mech_oid, #else GSS_C_GSS_CODE, GSS_C_NULL_OID, #endif /* _SUN_SDK_ */ &msg_ctx, &msg); if(GSS_ERROR(maj_stat)) { #ifdef _SUN_SDK_ if (logonly) { utils->log(text->utils->conn, SASL_LOG_FAIL, "GSSAPI Failure: (could not get major error message)"); } else { #endif /* _SUN_SDK_ */ #ifdef _INTEGRATED_SOLARIS_ utils->seterror(utils->conn, 0, gettext("GSSAPI Failure " "(could not get major error message)")); #ifdef _SUN_SDK_ } #endif /* _SUN_SDK_ */ #else utils->seterror(utils->conn, 0, "GSSAPI Failure " "(could not get major error message)"); #ifdef _SUN_SDK_ } #endif /* _SUN_SDK_ */ #endif /* _INTEGRATED_SOLARIS_ */ utils->free(out); return; } len += len + msg.length; ret = _plug_buf_alloc(utils, &out, &curlen, len); if(ret != SASL_OK) { utils->free(out); return; } strcat(out, msg.value); gss_release_buffer(&min_stat, &msg); if (!msg_ctx) break; } /* Now get the minor status */ len += 2; ret = _plug_buf_alloc(utils, &out, &curlen, len); if(ret != SASL_OK) { utils->free(out); return; } strcat(out, " ("); msg_ctx = 0; while (1) { maj_stat = gss_display_status(&min_stat, min, #ifdef _SUN_SDK_ GSS_C_MECH_CODE, text->mech_oid, #else GSS_C_MECH_CODE, GSS_C_NULL_OID, #endif /* _SUN_SDK_ */ &msg_ctx, &msg); if(GSS_ERROR(maj_stat)) { #ifdef _SUN_SDK_ if (logonly) { utils->log(text->utils->conn, SASL_LOG_FAIL, "GSSAPI Failure: (could not get minor error message)"); } else { #endif /* _SUN_SDK_ */ #ifdef _INTEGRATED_SOLARIS_ utils->seterror(utils->conn, 0, gettext("GSSAPI Failure " "(could not get minor error message)")); #ifdef _SUN_SDK_ } #endif /* _SUN_SDK_ */ #else utils->seterror(utils->conn, 0, "GSSAPI Failure " "(could not get minor error message)"); #ifdef _SUN_SDK_ } #endif /* _SUN_SDK_ */ #endif /* _INTEGRATED_SOLARIS_ */ utils->free(out); return; } len += len + msg.length; ret = _plug_buf_alloc(utils, &out, &curlen, len); if(ret != SASL_OK) { utils->free(out); return; } strcat(out, msg.value); gss_release_buffer(&min_stat, &msg); if (!msg_ctx) break; } len += 1; ret = _plug_buf_alloc(utils, &out, &curlen, len); if(ret != SASL_OK) { utils->free(out); return; } strcat(out, ")"); #ifdef _SUN_SDK_ if (logonly) { utils->log(text->utils->conn, SASL_LOG_FAIL, out); } else { utils->seterror(utils->conn, 0, out); } #else utils->seterror(utils->conn, 0, out); #endif /* _SUN_SDK_ */ utils->free(out); } static int sasl_gss_encode(void *context, const struct iovec *invec, unsigned numiov, const char **output, unsigned *outputlen, int privacy) { context_t *text = (context_t *)context; OM_uint32 maj_stat, min_stat; gss_buffer_t input_token, output_token; gss_buffer_desc real_input_token, real_output_token; int ret; struct buffer_info *inblob, bufinfo; if(!output) return SASL_BADPARAM; if(numiov > 1) { ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf); if(ret != SASL_OK) return ret; inblob = text->enc_in_buf; } else { bufinfo.data = invec[0].iov_base; bufinfo.curlen = invec[0].iov_len; inblob = &bufinfo; } if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) return SASL_NOTDONE; input_token = &real_input_token; real_input_token.value = inblob->data; real_input_token.length = inblob->curlen; output_token = &real_output_token; output_token->value = NULL; output_token->length = 0; #if defined _SUN_SDK_ && defined GSSAPI_PROTECT if (LOCK_MUTEX(&global_mutex) < 0) return (SASL_FAIL); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ maj_stat = gss_wrap (&min_stat, text->gss_ctx, privacy, GSS_C_QOP_DEFAULT, input_token, NULL, output_token); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); if (output_token->value) gss_release_buffer(&min_stat, output_token); #if defined _SUN_SDK_ && defined GSSAPI_PROTECT UNLOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ return SASL_FAIL; } if (output_token->value && output) { int len; ret = _plug_buf_alloc(text->utils, &(text->encode_buf), &(text->encode_buf_len), output_token->length + 4); if (ret != SASL_OK) { gss_release_buffer(&min_stat, output_token); #if defined _SUN_SDK_ && defined GSSAPI_PROTECT UNLOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ return ret; } len = htonl(output_token->length); memcpy(text->encode_buf, &len, 4); memcpy(text->encode_buf + 4, output_token->value, output_token->length); } if (outputlen) { *outputlen = output_token->length + 4; } *output = text->encode_buf; if (output_token->value) gss_release_buffer(&min_stat, output_token); #if defined _SUN_SDK_ && defined GSSAPI_PROTECT UNLOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ return SASL_OK; } static int gssapi_privacy_encode(void *context, const struct iovec *invec, unsigned numiov, const char **output, unsigned *outputlen) { return sasl_gss_encode(context,invec,numiov,output,outputlen,1); } static int gssapi_integrity_encode(void *context, const struct iovec *invec, unsigned numiov, const char **output, unsigned *outputlen) { return sasl_gss_encode(context,invec,numiov,output,outputlen,0); } #define myMIN(a,b) (((a) < (b)) ? (a) : (b)) static int gssapi_decode_once(void *context, const char **input, unsigned *inputlen, char **output, unsigned *outputlen) { context_t *text = (context_t *) context; OM_uint32 maj_stat, min_stat; gss_buffer_t input_token, output_token; gss_buffer_desc real_input_token, real_output_token; int result; unsigned diff; if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) { #ifdef _INTEGRATED_SOLARIS_ SETERROR(text->utils, gettext("GSSAPI Failure")); #else SETERROR(text->utils, "GSSAPI Failure"); #endif /* _INTEGRATED_SOLARIS_ */ return SASL_NOTDONE; } /* first we need to extract a packet */ if (text->needsize > 0) { /* how long is it? */ int tocopy = myMIN(text->needsize, *inputlen); memcpy(text->sizebuf + 4 - text->needsize, *input, tocopy); text->needsize -= tocopy; *input += tocopy; *inputlen -= tocopy; if (text->needsize == 0) { /* got the entire size */ memcpy(&text->size, text->sizebuf, 4); text->size = ntohl(text->size); text->cursize = 0; #ifdef _SUN_SDK_ if (text->size > 0xFFFFFF) { text->utils->log(text->utils->conn, SASL_LOG_ERR, "Illegal size in sasl_gss_decode_once"); #else if (text->size > 0xFFFFFF || text->size <= 0) { SETERROR(text->utils, "Illegal size in sasl_gss_decode_once"); #endif /* _SUN_SDK_ */ return SASL_FAIL; } if (text->bufsize < text->size + 5) { result = _plug_buf_alloc(text->utils, &text->buffer, &(text->bufsize), text->size+5); if(result != SASL_OK) return result; } } if (*inputlen == 0) { /* need more data ! */ *outputlen = 0; *output = NULL; return SASL_OK; } } diff = text->size - text->cursize; if (*inputlen < diff) { /* ok, let's queue it up; not enough data */ memcpy(text->buffer + text->cursize, *input, *inputlen); text->cursize += *inputlen; *inputlen = 0; *outputlen = 0; *output = NULL; return SASL_OK; } else { memcpy(text->buffer + text->cursize, *input, diff); *input += diff; *inputlen -= diff; } input_token = &real_input_token; real_input_token.value = text->buffer; real_input_token.length = text->size; output_token = &real_output_token; output_token->value = NULL; output_token->length = 0; #if defined _SUN_SDK_ && defined GSSAPI_PROTECT if (LOCK_MUTEX(&global_mutex) < 0) return (SASL_FAIL); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ maj_stat = gss_unwrap (&min_stat, text->gss_ctx, input_token, output_token, NULL, NULL); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); if (output_token->value) gss_release_buffer(&min_stat, output_token); #if defined _SUN_SDK_ && defined GSSAPI_PROTECT UNLOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ return SASL_FAIL; } if (outputlen) *outputlen = output_token->length; if (output_token->value) { if (output) { result = _plug_buf_alloc(text->utils, &text->decode_once_buf, &text->decode_once_buf_len, *outputlen); if(result != SASL_OK) { gss_release_buffer(&min_stat, output_token); #if defined _SUN_SDK_ && defined GSSAPI_PROTECT UNLOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ return result; } *output = text->decode_once_buf; memcpy(*output, output_token->value, *outputlen); } gss_release_buffer(&min_stat, output_token); } #if defined _SUN_SDK_ && defined GSSAPI_PROTECT UNLOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ /* reset for the next packet */ #ifndef _SUN_SDK_ text->size = -1; #endif /* !_SUN_SDK_ */ text->needsize = 4; return SASL_OK; } static int gssapi_decode(void *context, const char *input, unsigned inputlen, const char **output, unsigned *outputlen) { context_t *text = (context_t *) context; int ret; ret = _plug_decode(text->utils, context, input, inputlen, &text->decode_buf, &text->decode_buf_len, outputlen, gssapi_decode_once); *output = text->decode_buf; return ret; } static context_t *gss_new_context(const sasl_utils_t *utils) { context_t *ret; ret = utils->malloc(sizeof(context_t)); if(!ret) return NULL; memset(ret,0,sizeof(context_t)); ret->utils = utils; #ifdef _SUN_SDK_ ret->gss_ctx = GSS_C_NO_CONTEXT; ret->client_name = GSS_C_NO_NAME; ret->server_name = GSS_C_NO_NAME; ret->server_creds = GSS_C_NO_CREDENTIAL; ret->client_creds = GSS_C_NO_CREDENTIAL; if (get_oid(utils, &ret->mech_oid) != SASL_OK) { utils->free(ret); return (NULL); } #endif /* _SUN_SDK_ */ ret->needsize = 4; return ret; } static void sasl_gss_free_context_contents(context_t *text) { OM_uint32 maj_stat, min_stat; if (!text) return; if (text->gss_ctx != GSS_C_NO_CONTEXT) { maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER); text->gss_ctx = GSS_C_NO_CONTEXT; } if (text->client_name != GSS_C_NO_NAME) { maj_stat = gss_release_name(&min_stat,&text->client_name); text->client_name = GSS_C_NO_NAME; } if (text->server_name != GSS_C_NO_NAME) { maj_stat = gss_release_name(&min_stat,&text->server_name); text->server_name = GSS_C_NO_NAME; } if ( text->server_creds != GSS_C_NO_CREDENTIAL) { maj_stat = gss_release_cred(&min_stat, &text->server_creds); text->server_creds = GSS_C_NO_CREDENTIAL; } #ifdef _SUN_SDK_ if ( text->client_creds != GSS_C_NO_CREDENTIAL) { maj_stat = gss_release_cred(&min_stat, &text->client_creds); text->client_creds = GSS_C_NO_CREDENTIAL; } /* * Note that the oid returned by rpc_gss_mech_to_oid should not * be released */ #endif /* _SUN_SDK_ */ if (text->out_buf) { text->utils->free(text->out_buf); text->out_buf = NULL; } if (text->encode_buf) { text->utils->free(text->encode_buf); text->encode_buf = NULL; } if (text->decode_buf) { text->utils->free(text->decode_buf); text->decode_buf = NULL; } if (text->decode_once_buf) { text->utils->free(text->decode_once_buf); text->decode_once_buf = NULL; } if (text->enc_in_buf) { if(text->enc_in_buf->data) text->utils->free(text->enc_in_buf->data); text->utils->free(text->enc_in_buf); text->enc_in_buf = NULL; } if (text->buffer) { text->utils->free(text->buffer); text->buffer = NULL; } if (text->authid) { /* works for both client and server */ text->utils->free(text->authid); text->authid = NULL; } } #ifdef _SUN_SDK_ #ifdef HAVE_RPC_GSS_MECH_TO_OID #include #endif /* HAVE_RPC_GSS_MECH_TO_OID */ static int get_oid(const sasl_utils_t *utils, gss_OID *oid) { #ifdef HAVE_RPC_GSS_MECH_TO_OID static gss_OID_desc kerb_v5 = {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"}; /* 1.2.840.113554.1.2.2 */ *oid = &kerb_v5; #endif /* HAVE_RPC_GSS_MECH_TO_OID */ return (SASL_OK); } static int add_mech_to_set(context_t *text, gss_OID_set *desired_mechs) { OM_uint32 maj_stat, min_stat; maj_stat = gss_create_empty_oid_set(&min_stat, desired_mechs); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); return SASL_FAIL; } maj_stat = gss_add_oid_set_member(&min_stat, text->mech_oid, desired_mechs); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); (void) gss_release_oid_set(&min_stat, desired_mechs); return SASL_FAIL; } return SASL_OK; } #endif /* _SUN_SDK_ */ static void gssapi_common_mech_dispose(void *conn_context, const sasl_utils_t *utils) { #ifdef _SUN_SDK_ if (conn_context == NULL) return; #ifdef _INTEGRATED_SOLARIS_ convert_prompt(utils, &((context_t *)conn_context)->h, NULL); #endif /* _INTEGRATED_SOLARIS_ */ #endif /* _SUN_SDK_ */ #if defined _SUN_SDK_ && defined GSSAPI_PROTECT (void) LOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ sasl_gss_free_context_contents((context_t *)(conn_context)); #if defined _SUN_SDK_ && defined GSSAPI_PROTECT UNLOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ utils->free(conn_context); } /***************************** Server Section *****************************/ static int gssapi_server_mech_new(void *glob_context __attribute__((unused)), sasl_server_params_t *params, const char *challenge __attribute__((unused)), unsigned challen __attribute__((unused)), void **conn_context) { context_t *text; #if defined _SUN_SDK_ && defined GSSAPI_PROTECT if (LOCK_MUTEX(&global_mutex) < 0) return (SASL_FAIL); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ text = gss_new_context(params->utils); #if defined _SUN_SDK_ && defined GSSAPI_PROTECT UNLOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ if (text == NULL) { #ifndef _SUN_SDK_ MEMERROR(params->utils); #endif /* !_SUN_SDK_ */ return SASL_NOMEM; } text->gss_ctx = GSS_C_NO_CONTEXT; text->client_name = GSS_C_NO_NAME; text->server_name = GSS_C_NO_NAME; text->server_creds = GSS_C_NO_CREDENTIAL; text->state = SASL_GSSAPI_STATE_AUTHNEG; *conn_context = text; return SASL_OK; } static int gssapi_server_mech_step(void *conn_context, sasl_server_params_t *params, const char *clientin, unsigned clientinlen, const char **serverout, unsigned *serveroutlen, sasl_out_params_t *oparams) { context_t *text = (context_t *)conn_context; gss_buffer_t input_token, output_token; gss_buffer_desc real_input_token, real_output_token; OM_uint32 maj_stat, min_stat; #ifdef _SUN_SDK_ OM_uint32 max_input_size; gss_OID_set desired_mechs = GSS_C_NULL_OID_SET; #endif /* _SUN_SDK_ */ gss_buffer_desc name_token; int ret; input_token = &real_input_token; output_token = &real_output_token; output_token->value = NULL; output_token->length = 0; input_token->value = NULL; input_token->length = 0; if(!serverout) { PARAMERROR(text->utils); return SASL_BADPARAM; } *serverout = NULL; *serveroutlen = 0; switch (text->state) { case SASL_GSSAPI_STATE_AUTHNEG: if (text->server_name == GSS_C_NO_NAME) { /* only once */ name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN); 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; } #ifdef _SUN_SDK_ snprintf(name_token.value, name_token.length + 1, "%s@%s", params->service, params->serverFQDN); #else sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN); #endif /* _SUN_SDK_ */ maj_stat = gss_import_name (&min_stat, &name_token, GSS_C_NT_HOSTBASED_SERVICE, &text->server_name); params->utils->free(name_token.value); name_token.value = NULL; if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); return SASL_FAIL; } if ( text->server_creds != GSS_C_NO_CREDENTIAL) { maj_stat = gss_release_cred(&min_stat, &text->server_creds); text->server_creds = GSS_C_NO_CREDENTIAL; } #ifdef _SUN_SDK_ if (text->mech_oid != GSS_C_NULL_OID) { ret = add_mech_to_set(text, &desired_mechs); if (ret != SASL_OK) return (ret); } #endif /* _SUN_SDK_ */ maj_stat = gss_acquire_cred(&min_stat, text->server_name, GSS_C_INDEFINITE, #ifdef _SUN_SDK_ desired_mechs, #else GSS_C_NO_OID_SET, #endif /* _SUN_SDK_ */ GSS_C_ACCEPT, &text->server_creds, NULL, NULL); #ifdef _SUN_SDK_ if (desired_mechs != GSS_C_NULL_OID_SET) { OM_uint32 min_stat2; (void) gss_release_oid_set(&min_stat2, &desired_mechs); } #endif /* _SUN_SDK_ */ if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); return SASL_FAIL; } } if (clientinlen) { real_input_token.value = (void *)clientin; real_input_token.length = clientinlen; } maj_stat = gss_accept_sec_context(&min_stat, &(text->gss_ctx), text->server_creds, input_token, GSS_C_NO_CHANNEL_BINDINGS, &text->client_name, NULL, output_token, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) { #ifdef _SUN_SDK_ /* log the local error info, set a more generic error */ sasl_gss_log(text->utils, maj_stat, min_stat); text->utils->seterror(text->utils->conn, SASL_NOLOG, gettext("GSSAPI Failure: accept security context error")); if (output_token->value) { gss_release_buffer(&min_stat, output_token); } #else if (output_token->value) { gss_release_buffer(&min_stat, output_token); } text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Failure: gss_accept_sec_context"); text->utils->log(NULL, SASL_LOG_DEBUG, "GSSAPI Failure: gss_accept_sec_context"); #endif /* _SUN_SDK_ */ sasl_gss_free_context_contents(text); return SASL_BADAUTH; } if (serveroutlen) *serveroutlen = output_token->length; 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_release_buffer(&min_stat, output_token); return ret; } memcpy(text->out_buf, output_token->value, *serveroutlen); *serverout = text->out_buf; } gss_release_buffer(&min_stat, output_token); } else { /* No output token, send an empty string */ *serverout = GSSAPI_BLANK_STRING; #ifndef _SUN_SDK_ serveroutlen = 0; #endif /* !_SUN_SDK_ */ } if (maj_stat == GSS_S_COMPLETE) { /* Switch to ssf negotiation */ text->state = SASL_GSSAPI_STATE_SSFCAP; } return SASL_CONTINUE; case SASL_GSSAPI_STATE_SSFCAP: { unsigned char sasldata[4]; gss_buffer_desc name_token; #ifndef _SUN_SDK_ gss_buffer_desc name_without_realm; gss_name_t without = NULL; int equal; #endif /* !_SUN_SDK_ */ name_token.value = NULL; #ifndef _SUN_SDK_ name_without_realm.value = NULL; #endif /* !_SUN_SDK_ */ /* We ignore whatever the client sent us at this stage */ maj_stat = gss_display_name (&min_stat, text->client_name, &name_token, NULL); if (GSS_ERROR(maj_stat)) { #ifndef _SUN_SDK_ if (name_without_realm.value) params->utils->free(name_without_realm.value); #endif /* !_SUN_SDK_ */ if (name_token.value) gss_release_buffer(&min_stat, &name_token); #ifndef _SUN_SDK_ if (without) gss_release_name(&min_stat, &without); #endif /* !_SUN_SDK_ */ #ifdef _INTEGRATED_SOLARIS_ SETERROR(text->utils, gettext("GSSAPI Failure")); #else SETERROR(text->utils, "GSSAPI Failure"); #endif /* _INTEGRATED_SOLARIS_ */ sasl_gss_free_context_contents(text); return SASL_BADAUTH; } #ifndef _SUN_SDK_ /* 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 = malloc(strlen(name_token.value)+1); if (name_without_realm.value == NULL) { MEMERROR(text->utils); return SASL_NOMEM; } strcpy(name_without_realm.value, name_token.value); /* cut off string at '@' */ (strchr(name_without_realm.value,'@'))[0] = '\0'; name_without_realm.length = strlen( (char *) name_without_realm.value ); maj_stat = gss_import_name (&min_stat, &name_without_realm, /* Solaris 8/9 gss_import_name doesn't accept GSS_C_NULL_OID here, 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); if (GSS_ERROR(maj_stat)) { params->utils->free(name_without_realm.value); if (name_token.value) gss_release_buffer(&min_stat, &name_token); if (without) gss_release_name(&min_stat, &without); SETERROR(text->utils, "GSSAPI Failure"); sasl_gss_free_context_contents(text); return SASL_BADAUTH; } maj_stat = gss_compare_name(&min_stat, text->client_name, without, &equal); if (GSS_ERROR(maj_stat)) { params->utils->free(name_without_realm.value); if (name_token.value) gss_release_buffer(&min_stat, &name_token); if (without) gss_release_name(&min_stat, &without); SETERROR(text->utils, "GSSAPI Failure"); sasl_gss_free_context_contents(text); return SASL_BADAUTH; } gss_release_name(&min_stat,&without); } else { equal = 0; } if (equal) { text->authid = strdup(name_without_realm.value); if (text->authid == NULL) { MEMERROR(params->utils); return SASL_NOMEM; } } else { text->authid = strdup(name_token.value); if (text->authid == NULL) { MEMERROR(params->utils); return SASL_NOMEM; } } #else { ret = _plug_strdup(params->utils, name_token.value, &text->authid, NULL); } #endif /* _SUN_SDK_ */ if (name_token.value) gss_release_buffer(&min_stat, &name_token); #ifdef _SUN_SDK_ if (ret != SASL_OK) return (ret); #else if (name_without_realm.value) params->utils->free(name_without_realm.value); #endif /* _SUN_SDK_ */ /* 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 (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; } sasldata[0] = 0; if(text->requiressf != 0 && !params->props.maxbufsize) { #ifdef _SUN_SDK_ params->utils->log(params->utils->conn, SASL_LOG_ERR, "GSSAPI needs a security layer but one is forbidden"); #else params->utils->seterror(params->utils->conn, 0, "GSSAPI needs a security layer but one is forbidden"); #endif /* _SUN_SDK_ */ return SASL_TOOWEAK; } if (text->requiressf == 0) { sasldata[0] |= 1; /* authentication */ } if (text->requiressf <= 1 && text->limitssf >= 1 && params->props.maxbufsize) { sasldata[0] |= 2; } if (text->requiressf <= 56 && text->limitssf >= 56 && params->props.maxbufsize) { sasldata[0] |= 4; } real_input_token.value = (void *)sasldata; real_input_token.length = 4; maj_stat = gss_wrap(&min_stat, text->gss_ctx, 0, /* Just integrity checking here */ GSS_C_QOP_DEFAULT, input_token, NULL, output_token); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); if (output_token->value) gss_release_buffer(&min_stat, output_token); sasl_gss_free_context_contents(text); return SASL_FAIL; } if (serveroutlen) *serveroutlen = output_token->length; 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_release_buffer(&min_stat, output_token); return ret; } memcpy(text->out_buf, output_token->value, *serveroutlen); *serverout = text->out_buf; } gss_release_buffer(&min_stat, output_token); } /* Wait for ssf request and authid */ text->state = SASL_GSSAPI_STATE_SSFREQ; return SASL_CONTINUE; } case SASL_GSSAPI_STATE_SSFREQ: { int layerchoice; real_input_token.value = (void *)clientin; real_input_token.length = clientinlen; maj_stat = gss_unwrap(&min_stat, text->gss_ctx, input_token, output_token, NULL, NULL); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); return SASL_FAIL; } layerchoice = (int)(((char *)(output_token->value))[0]); if (layerchoice == 1 && text->requiressf == 0) { /* no encryption */ oparams->encode = NULL; oparams->decode = NULL; oparams->mech_ssf = 0; } else if (layerchoice == 2 && text->requiressf <= 1 && text->limitssf >= 1) { /* integrity */ oparams->encode=&gssapi_integrity_encode; oparams->decode=&gssapi_decode; oparams->mech_ssf=1; } else if (layerchoice == 4 && text->requiressf <= 56 && text->limitssf >= 56) { /* privacy */ oparams->encode = &gssapi_privacy_encode; oparams->decode = &gssapi_decode; oparams->mech_ssf = 56; } else { /* not a supported encryption layer */ #ifdef _SUN_SDK_ text->utils->log(text->utils->conn, SASL_LOG_ERR, "protocol violation: client requested invalid layer"); #else SETERROR(text->utils, "protocol violation: client requested invalid layer"); #endif /* _SUN_SDK_ */ /* Mark that we attempted negotiation */ oparams->mech_ssf = 2; if (output_token->value) gss_release_buffer(&min_stat, output_token); sasl_gss_free_context_contents(text); return SASL_FAIL; } if (output_token->length > 4) { int ret; ret = params->canon_user(params->utils->conn, ((char *) output_token->value) + 4, (output_token->length - 4) * sizeof(char), SASL_CU_AUTHZID, oparams); if (ret != SASL_OK) { sasl_gss_free_context_contents(text); return ret; } ret = params->canon_user(params->utils->conn, text->authid, 0, /* strlen(text->authid) */ SASL_CU_AUTHID, 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, oparams); if (ret != SASL_OK) { sasl_gss_free_context_contents(text); return ret; } } else { #ifdef _SUN_SDK_ text->utils->log(text->utils->conn, SASL_LOG_ERR, "token too short"); #else SETERROR(text->utils, "token too short"); #endif /* _SUN_SDK_ */ gss_release_buffer(&min_stat, output_token); sasl_gss_free_context_contents(text); return SASL_FAIL; } /* No matter what, set the rest of the oparams */ oparams->maxoutbuf = (((unsigned char *) output_token->value)[1] << 16) | (((unsigned char *) output_token->value)[2] << 8) | (((unsigned char *) output_token->value)[3] << 0); #ifdef _SUN_SDK_ if (oparams->mech_ssf) { oparams->maxoutbuf -= 4; /* Allow for 4 byte tag */ maj_stat = gss_wrap_size_limit(&min_stat, text->gss_ctx, oparams->mech_ssf > 1, GSS_C_QOP_DEFAULT, oparams->maxoutbuf, &max_input_size); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); (void) gss_release_buffer(&min_stat, output_token); sasl_gss_free_context_contents(text); return (SASL_FAIL); } /* * gss_wrap_size_limit will return very big sizes for * small input values */ if (max_input_size < oparams->maxoutbuf) oparams->maxoutbuf = max_input_size; else { oparams->maxoutbuf = 0; } } #else if (oparams->mech_ssf) { /* xxx this is probably too big */ oparams->maxoutbuf -= 50; } #endif /* _SUN_SDK_ */ gss_release_buffer(&min_stat, output_token); text->state = SASL_GSSAPI_STATE_AUTHENTICATED; oparams->doneflag = 1; return SASL_OK; } default: #ifdef _SUN_SDK_ params->utils->log(text->utils->conn, SASL_LOG_ERR, "Invalid GSSAPI server step %d", text->state); #else params->utils->log(NULL, SASL_LOG_ERR, "Invalid GSSAPI server step %d\n", text->state); #endif /* _SUN_SDK_ */ return SASL_FAIL; } #ifndef _SUN_SDK_ return SASL_FAIL; /* should never get here */ #endif /* !_SUN_SDK_ */ } #if defined _SUN_SDK_ && defined GSSAPI_PROTECT static int _gssapi_server_mech_step(void *conn_context, sasl_server_params_t *params, const char *clientin, unsigned clientinlen, const char **serverout, unsigned *serveroutlen, sasl_out_params_t *oparams) { int ret; if (LOCK_MUTEX(&global_mutex) < 0) return (SASL_FAIL); ret = gssapi_server_mech_step(conn_context, params, clientin, clientinlen, serverout, serveroutlen, oparams); UNLOCK_MUTEX(&global_mutex); return (ret); } #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ static sasl_server_plug_t gssapi_server_plugins[] = { { "GSSAPI", /* mech_name */ 56, /* max_ssf */ SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE | SASL_SEC_NOANONYMOUS | SASL_SEC_MUTUAL_AUTH, /* security_flags */ SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_ALLOWS_PROXY, /* features */ NULL, /* glob_context */ &gssapi_server_mech_new, /* mech_new */ #if defined _SUN_SDK_ && defined GSSAPI_PROTECT &_gssapi_server_mech_step, /* mech_step */ #else &gssapi_server_mech_step, /* mech_step */ #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ &gssapi_common_mech_dispose, /* mech_dispose */ NULL, /* mech_free */ NULL, /* setpass */ NULL, /* user_query */ NULL, /* idle */ NULL, /* mech_avail */ NULL /* spare */ } }; int gssapiv2_server_plug_init( #ifndef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY const sasl_utils_t *utils __attribute__((unused)), #else const sasl_utils_t *utils, #endif int maxversion, int *out_version, sasl_server_plug_t **pluglist, int *plugcount) { #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY const char *keytab = NULL; char keytab_path[1024]; unsigned int rl; #endif if (maxversion < SASL_SERVER_PLUG_VERSION) { return SASL_BADVERS; } #ifndef _SUN_SDK_ #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY /* unfortunately, we don't check for readability of keytab if it's the standard one, since we don't know where it is */ /* FIXME: This code is broken */ utils->getopt(utils->getopt_context, "GSSAPI", "keytab", &keytab, &rl); if (keytab != NULL) { if (access(keytab, R_OK) != 0) { utils->log(NULL, SASL_LOG_ERR, "Could not find keytab file: %s: %m", keytab, errno); return SASL_FAIL; } if(strlen(keytab) > 1024) { utils->log(NULL, SASL_LOG_ERR, "path to keytab is > 1024 characters"); return SASL_BUFOVER; } strncpy(keytab_path, keytab, 1024); gsskrb5_register_acceptor_identity(keytab_path); } #endif #endif /* !_SUN_SDK_ */ #ifdef _INTEGRATED_SOLARIS_ /* * Let libsasl know that we are a "Sun" plugin so that privacy * and integrity will be allowed. */ REG_PLUG("GSSAPI", gssapi_server_plugins); #endif /* _INTEGRATED_SOLARIS_ */ *out_version = SASL_SERVER_PLUG_VERSION; *pluglist = gssapi_server_plugins; *plugcount = 1; return SASL_OK; } /***************************** Client Section *****************************/ static int gssapi_client_mech_new(void *glob_context __attribute__((unused)), sasl_client_params_t *params, void **conn_context) { context_t *text; #ifdef _SUN_SDK_ const char *use_authid = NULL; #endif /* _SUN_SDK_ */ /* holds state are in */ #if defined _SUN_SDK_ && defined GSSAPI_PROTECT if (LOCK_MUTEX(&global_mutex) < 0) return (SASL_FAIL); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ text = gss_new_context(params->utils); #if defined _SUN_SDK_ && defined GSSAPI_PROTECT UNLOCK_MUTEX(&global_mutex); #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ if (text == NULL) { #ifndef _SUN_SDK_ MEMERROR(params->utils); #endif /* !_SUN_SDK_ */ return SASL_NOMEM; } text->state = SASL_GSSAPI_STATE_AUTHNEG; text->gss_ctx = GSS_C_NO_CONTEXT; text->client_name = GSS_C_NO_NAME; text->server_creds = GSS_C_NO_CREDENTIAL; #ifdef _SUN_SDK_ params->utils->getopt(params->utils->getopt_context, "GSSAPI", "use_authid", &use_authid, NULL); text->use_authid = (use_authid != NULL) && (*use_authid == 'y' || *use_authid == 'Y' || *use_authid == '1'); #endif /* _SUN_SDK_ */ *conn_context = text; return SASL_OK; } static int gssapi_client_mech_step(void *conn_context, sasl_client_params_t *params, const char *serverin, unsigned serverinlen, sasl_interact_t **prompt_need, const char **clientout, unsigned *clientoutlen, sasl_out_params_t *oparams) { context_t *text = (context_t *)conn_context; gss_buffer_t input_token, output_token; gss_buffer_desc real_input_token, real_output_token; OM_uint32 maj_stat, min_stat; #ifdef _SUN_SDK_ OM_uint32 max_input_size; #endif /* _SUN_SDK_ */ gss_buffer_desc name_token; int ret; OM_uint32 req_flags, out_req_flags; input_token = &real_input_token; output_token = &real_output_token; output_token->value = NULL; input_token->value = NULL; input_token->length = 0; *clientout = NULL; *clientoutlen = 0; switch (text->state) { case SASL_GSSAPI_STATE_AUTHNEG: /* try to get the userid */ #ifdef _SUN_SDK_ if (text->user == NULL || (text->use_authid && text->client_authid == NULL)) { int auth_result = SASL_OK; int user_result = SASL_OK; if (text->use_authid && text->client_authid == NULL) { auth_result = _plug_get_authid(params->utils, &text->client_authid, prompt_need); if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) { sasl_gss_free_context_contents(text); return auth_result; } } if (text->user == NULL) { user_result = _plug_get_userid(params->utils, &text->user, prompt_need); if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) { sasl_gss_free_context_contents(text); return user_result; } } #else if (text->user == NULL) { int user_result = SASL_OK; user_result = _plug_get_userid(params->utils, &text->user, prompt_need); if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) { sasl_gss_free_context_contents(text); return user_result; } #endif /* _SUN_SDK_ */ /* free prompts we got */ if (prompt_need && *prompt_need) { params->utils->free(*prompt_need); *prompt_need = NULL; } /* if there are prompts not filled in */ #ifdef _SUN_SDK_ if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT)) { /* make the prompt list */ #ifdef _INTEGRATED_SOLARIS_ int result = _plug_make_prompts(params->utils, &text->h, prompt_need, user_result == SASL_INTERACT ? convert_prompt(params->utils, &text->h, gettext("Please enter your authorization name")) : NULL, NULL, auth_result == SASL_INTERACT ? convert_prompt(params->utils, &text->h, gettext("Please enter your authentication name")) : NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); #else int result = _plug_make_prompts(params->utils, prompt_need, user_result == SASL_INTERACT ? "Please enter your authorization name" : NULL, NULL, auth_result == SASL_INTERACT ? "Please enter your authentication name" : NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); #endif /* _INTEGRATED_SOLARIS_ */ if (result != SASL_OK) return result; return SASL_INTERACT; } #else if (user_result == SASL_INTERACT) { /* make the prompt list */ int result = _plug_make_prompts(params->utils, prompt_need, user_result == SASL_INTERACT ? "Please enter your authorization name" : NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (result != SASL_OK) return result; return SASL_INTERACT; } #endif /* _SUN_SDK_ */ } if (text->server_name == GSS_C_NO_NAME) { /* only once */ name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN); name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char)); if (name_token.value == NULL) { sasl_gss_free_context_contents(text); return SASL_NOMEM; } if (params->serverFQDN == NULL || strlen(params->serverFQDN) == 0) { #ifdef _SUN_SDK_ text->utils->log(text->utils->conn, SASL_LOG_ERR, "GSSAPI Failure: no serverFQDN"); #else SETERROR(text->utils, "GSSAPI Failure: no serverFQDN"); #endif /* _SUN_SDK_ */ return SASL_FAIL; } #ifdef _SUN_SDK_ snprintf(name_token.value, name_token.length + 1, "%s@%s", params->service, params->serverFQDN); #else sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN); #endif /* _SUN_SDK_ */ maj_stat = gss_import_name (&min_stat, &name_token, GSS_C_NT_HOSTBASED_SERVICE, &text->server_name); params->utils->free(name_token.value); name_token.value = NULL; if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); return SASL_FAIL; } } if (serverinlen == 0) input_token = GSS_C_NO_BUFFER; if (serverinlen) { real_input_token.value = (void *)serverin; real_input_token.length = serverinlen; } else if (text->gss_ctx != GSS_C_NO_CONTEXT ) { /* This can't happen under GSSAPI: we have a non-null context * and no input from the server. However, thanks to Imap, * which discards our first output, this happens all the time. * Throw away the context and try again. */ maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER); text->gss_ctx = GSS_C_NO_CONTEXT; } /* Setup req_flags properly */ req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; if(params->props.max_ssf > params->external_ssf) { /* We are requesting a security layer */ req_flags |= GSS_C_INTEG_FLAG; if(params->props.max_ssf - params->external_ssf > 56) { /* We want to try for privacy */ req_flags |= GSS_C_CONF_FLAG; } } #ifdef _SUN_SDK_ if (text->use_authid && text->client_creds == GSS_C_NO_CREDENTIAL) { gss_OID_set desired_mechs = GSS_C_NULL_OID_SET; gss_buffer_desc name_token; name_token.length = strlen(text->client_authid); name_token.value = (char *)text->client_authid; maj_stat = gss_import_name (&min_stat, &name_token, #ifdef HAVE_GSS_C_NT_USER_NAME GSS_C_NT_USER_NAME, #else GSS_C_NULL_OID, #endif &text->client_name); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); return SASL_FAIL; } if (text->mech_oid != GSS_C_NULL_OID) { ret = add_mech_to_set(text, &desired_mechs); if (ret != SASL_OK) return (ret); } maj_stat = gss_acquire_cred(&min_stat, text->client_name, GSS_C_INDEFINITE, desired_mechs, GSS_C_INITIATE, &text->client_creds, NULL, NULL); if (desired_mechs != GSS_C_NULL_OID_SET) { OM_uint32 min_stat2; (void) gss_release_oid_set(&min_stat2, &desired_mechs); } if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); return SASL_FAIL; } } #endif /* _SUN_SDK_ */ maj_stat = gss_init_sec_context(&min_stat, #ifdef _SUN_SDK_ text->client_creds, #else GSS_C_NO_CREDENTIAL, #endif /* _SUN_SDK_ */ &text->gss_ctx, text->server_name, #ifdef _SUN_SDK_ text->mech_oid, #else GSS_C_NO_OID, #endif /* _SUN_SDK_ */ req_flags, 0, GSS_C_NO_CHANNEL_BINDINGS, input_token, NULL, output_token, &out_req_flags, NULL); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); if (output_token->value) gss_release_buffer(&min_stat, output_token); sasl_gss_free_context_contents(text); return SASL_FAIL; } *clientoutlen = output_token->length; if (output_token->value) { if (clientout) { ret = _plug_buf_alloc(text->utils, &(text->out_buf), &(text->out_buf_len), *clientoutlen); if(ret != SASL_OK) { gss_release_buffer(&min_stat, output_token); return ret; } memcpy(text->out_buf, output_token->value, *clientoutlen); *clientout = text->out_buf; } gss_release_buffer(&min_stat, output_token); } if (maj_stat == GSS_S_COMPLETE) { maj_stat = gss_inquire_context(&min_stat, text->gss_ctx, &text->client_name, NULL, /* targ_name */ NULL, /* lifetime */ NULL, /* mech */ NULL, /* flags */ NULL, /* local init */ NULL); /* open */ if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); return SASL_FAIL; } name_token.length = 0; maj_stat = gss_display_name(&min_stat, text->client_name, &name_token, NULL); if (GSS_ERROR(maj_stat)) { if (name_token.value) gss_release_buffer(&min_stat, &name_token); #ifdef _INTEGRATED_SOLARIS_ SETERROR(text->utils, gettext("GSSAPI Failure")); #else SETERROR(text->utils, "GSSAPI Failure"); #endif /* _INTEGRATED_SOLARIS_ */ sasl_gss_free_context_contents(text); return SASL_FAIL; } if (text->user && text->user[0]) { ret = params->canon_user(params->utils->conn, text->user, 0, SASL_CU_AUTHZID, oparams); if (ret == SASL_OK) ret = params->canon_user(params->utils->conn, name_token.value, 0, SASL_CU_AUTHID, oparams); } else { ret = params->canon_user(params->utils->conn, name_token.value, 0, SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); } gss_release_buffer(&min_stat, &name_token); if (ret != SASL_OK) return ret; /* Switch to ssf negotiation */ text->state = SASL_GSSAPI_STATE_SSFCAP; } return SASL_CONTINUE; case SASL_GSSAPI_STATE_SSFCAP: { sasl_security_properties_t *secprops = &(params->props); unsigned int alen, external = params->external_ssf; sasl_ssf_t need, allowed; char serverhas, mychoice; real_input_token.value = (void *) serverin; real_input_token.length = serverinlen; maj_stat = gss_unwrap(&min_stat, text->gss_ctx, input_token, output_token, NULL, NULL); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); sasl_gss_free_context_contents(text); if (output_token->value) gss_release_buffer(&min_stat, output_token); return SASL_FAIL; } /* taken from kerberos.c */ if (secprops->min_ssf > (56 + external)) { return SASL_TOOWEAK; } else if (secprops->min_ssf > secprops->max_ssf) { return SASL_BADPARAM; } /* need bits of layer -- sasl_ssf_t is unsigned so be careful */ if (secprops->max_ssf >= external) { allowed = secprops->max_ssf - external; } else { allowed = 0; } if (secprops->min_ssf >= external) { need = secprops->min_ssf - external; } else { /* good to go */ need = 0; } /* bit mask of server support */ serverhas = ((char *)output_token->value)[0]; /* if client didn't set use strongest layer available */ if (allowed >= 56 && need <= 56 && (serverhas & 4)) { /* encryption */ oparams->encode = &gssapi_privacy_encode; oparams->decode = &gssapi_decode; oparams->mech_ssf = 56; mychoice = 4; } else if (allowed >= 1 && need <= 1 && (serverhas & 2)) { /* integrity */ oparams->encode = &gssapi_integrity_encode; oparams->decode = &gssapi_decode; oparams->mech_ssf = 1; mychoice = 2; #ifdef _SUN_SDK_ } else if (need == 0 && (serverhas & 1)) { #else } else if (need <= 0 && (serverhas & 1)) { #endif /* _SUN_SDK_ */ /* no layer */ oparams->encode = NULL; oparams->decode = NULL; oparams->mech_ssf = 0; mychoice = 1; } else { /* there's no appropriate layering for us! */ sasl_gss_free_context_contents(text); return SASL_TOOWEAK; } oparams->maxoutbuf = (((unsigned char *) output_token->value)[1] << 16) | (((unsigned char *) output_token->value)[2] << 8) | (((unsigned char *) output_token->value)[3] << 0); #ifdef _SUN_SDK_ if (oparams->mech_ssf > 0) { oparams->maxoutbuf -= 4; /* Space for 4 byte length header */ maj_stat = gss_wrap_size_limit(&min_stat, text->gss_ctx, oparams->mech_ssf > 1, GSS_C_QOP_DEFAULT, oparams->maxoutbuf, &max_input_size); if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); (void) gss_release_buffer(&min_stat, output_token); sasl_gss_free_context_contents(text); return (SASL_FAIL); } /* * This is a workaround for a Solaris bug where * gss_wrap_size_limit may return very big sizes for * small input values */ if (max_input_size < oparams->maxoutbuf) oparams->maxoutbuf = max_input_size; else { oparams->maxoutbuf = 0; } } #else if(oparams->mech_ssf) { /* xxx probably too large */ oparams->maxoutbuf -= 50; } #endif /* _SUN_SDK_ */ gss_release_buffer(&min_stat, output_token); /* oparams->user is always set, due to canon_user requirements. * Make sure the client actually requested it though, by checking * if our context was set. */ if (text->user && text->user[0]) alen = strlen(oparams->user); else alen = 0; input_token->length = 4 + alen; input_token->value = (char *)params->utils->malloc((input_token->length + 1)*sizeof(char)); if (input_token->value == NULL) { sasl_gss_free_context_contents(text); return SASL_NOMEM; } if (alen) memcpy((char *)input_token->value+4,oparams->user,alen); /* build up our security properties token */ if (params->props.maxbufsize > 0xFFFFFF) { /* make sure maxbufsize isn't too large */ /* maxbufsize = 0xFFFFFF */ ((unsigned char *)input_token->value)[1] = 0xFF; ((unsigned char *)input_token->value)[2] = 0xFF; ((unsigned char *)input_token->value)[3] = 0xFF; } else { ((unsigned char *)input_token->value)[1] = (params->props.maxbufsize >> 16) & 0xFF; ((unsigned char *)input_token->value)[2] = (params->props.maxbufsize >> 8) & 0xFF; ((unsigned char *)input_token->value)[3] = (params->props.maxbufsize >> 0) & 0xFF; } ((unsigned char *)input_token->value)[0] = mychoice; maj_stat = gss_wrap (&min_stat, text->gss_ctx, 0, /* Just integrity checking here */ GSS_C_QOP_DEFAULT, input_token, NULL, output_token); params->utils->free(input_token->value); input_token->value = NULL; if (GSS_ERROR(maj_stat)) { sasl_gss_seterror(text->utils, maj_stat, min_stat); if (output_token->value) gss_release_buffer(&min_stat, output_token); sasl_gss_free_context_contents(text); return SASL_FAIL; } if (clientoutlen) *clientoutlen = output_token->length; if (output_token->value) { if (clientout) { ret = _plug_buf_alloc(text->utils, &(text->out_buf), &(text->out_buf_len), *clientoutlen); if (ret != SASL_OK) { gss_release_buffer(&min_stat, output_token); return ret; } memcpy(text->out_buf, output_token->value, *clientoutlen); *clientout = text->out_buf; } gss_release_buffer(&min_stat, output_token); } text->state = SASL_GSSAPI_STATE_AUTHENTICATED; oparams->doneflag = 1; return SASL_OK; } default: #ifdef _SUN_SDK_ params->utils->log(params->utils->conn, SASL_LOG_ERR, "Invalid GSSAPI client step %d", text->state); #else params->utils->log(NULL, SASL_LOG_ERR, "Invalid GSSAPI client step %d\n", text->state); #endif /* _SUN_SDK_ */ return SASL_FAIL; } #ifndef _SUN_SDK_ return SASL_FAIL; /* should never get here */ #endif /* !_SUN_SDK_ */ } #ifdef _SUN_SDK_ static const unsigned long gssapi_required_prompts[] = { #else static const long gssapi_required_prompts[] = { #endif /* _SUN_SDK_ */ SASL_CB_LIST_END }; #if defined _SUN_SDK_ && defined GSSAPI_PROTECT static int _gssapi_client_mech_step(void *conn_context, sasl_client_params_t *params, const char *serverin, unsigned serverinlen, sasl_interact_t **prompt_need, const char **clientout, unsigned *clientoutlen, sasl_out_params_t *oparams) { int ret; if (LOCK_MUTEX(&global_mutex) < 0) return (SASL_FAIL); ret = gssapi_client_mech_step(conn_context, params, serverin, serverinlen, prompt_need, clientout, clientoutlen, oparams); UNLOCK_MUTEX(&global_mutex); return (ret); } #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ static sasl_client_plug_t gssapi_client_plugins[] = { { "GSSAPI", /* mech_name */ 56, /* max_ssf */ SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE | SASL_SEC_NOANONYMOUS | SASL_SEC_MUTUAL_AUTH, /* security_flags */ SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_ALLOWS_PROXY, /* features */ gssapi_required_prompts, /* required_prompts */ NULL, /* glob_context */ &gssapi_client_mech_new, /* mech_new */ #if defined _SUN_SDK_ && defined GSSAPI_PROTECT &_gssapi_client_mech_step, /* mech_step */ #else &gssapi_client_mech_step, /* mech_step */ #endif /* _SUN_SDK_ && GSSAPI_PROTECT */ &gssapi_common_mech_dispose, /* mech_dispose */ NULL, /* mech_free */ NULL, /* idle */ NULL, /* spare */ NULL /* spare */ } }; int gssapiv2_client_plug_init(const sasl_utils_t *utils __attribute__((unused)), int maxversion, int *out_version, sasl_client_plug_t **pluglist, int *plugcount) { if (maxversion < SASL_CLIENT_PLUG_VERSION) { SETERROR(utils, "Version mismatch in GSSAPI"); return SASL_BADVERS; } #ifdef _INTEGRATED_SOLARIS_ /* * Let libsasl know that we are a "Sun" plugin so that privacy * and integrity will be allowed. */ REG_PLUG("GSSAPI", gssapi_client_plugins); #endif /* _INTEGRATED_SOLARIS_ */ *out_version = SASL_CLIENT_PLUG_VERSION; *pluglist = gssapi_client_plugins; *plugcount = 1; return SASL_OK; }