summaryrefslogtreecommitdiff
path: root/usr/src/lib/sasl_plugins/gssapi/gssapi.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/sasl_plugins/gssapi/gssapi.c
downloadillumos-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.c2206
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;
+}