diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/sasl_plugins/gssapi/gssapi.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/sasl_plugins/gssapi/gssapi.c')
-rw-r--r-- | usr/src/lib/sasl_plugins/gssapi/gssapi.c | 2206 |
1 files changed, 2206 insertions, 0 deletions
diff --git a/usr/src/lib/sasl_plugins/gssapi/gssapi.c b/usr/src/lib/sasl_plugins/gssapi/gssapi.c new file mode 100644 index 0000000000..302d029380 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/gssapi.c @@ -0,0 +1,2206 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* 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 <config.h> + +#ifdef HAVE_GSSAPI_H +#include <gssapi.h> +#else +#include <gssapi/gssapi.h> +#endif + +#ifdef WIN32 +# include <winsock.h> + +# ifndef R_OK +# define R_OK 04 +# endif +/* we also need io.h for access() prototype */ +# include <io.h> +#else +# include <sys/param.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# include <netdb.h> +#endif /* WIN32 */ +#include <fcntl.h> +#include <stdio.h> +#include <sasl.h> +#include <saslutil.h> +#include <saslplug.h> + +#include "plugin_common.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <errno.h> + +#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 <leifj@matematik.su.se> + * inspired by the kerberos mechanism and the gssapi_server and + * gssapi_client from the heimdal distribution by Assar Westerlund + * <assar@sics.se> and Johan Danielsson <joda@pdc.kth.se>. + * 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 <hartmans@fundsxpress.com>. + */ + +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 <rpc/rpcsec_gss.h> +#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_ */ + + /* EXPORT DELETE START */ + /* CRYPT DELETE START */ +#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_ */ + /* CRYPT DELETE END */ + /* EXPORT DELETE END */ + + *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; + } + + /* EXPORT DELETE START */ + /* CRYPT DELETE START */ +#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_ */ + /* CRYPT DELETE END */ + /* EXPORT DELETE END */ + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = gssapi_client_plugins; + *plugcount = 1; + + return SASL_OK; +} |