summaryrefslogtreecommitdiff
path: root/usr/src/lib/rpcsec_gss
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/rpcsec_gss
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/rpcsec_gss')
-rw-r--r--usr/src/lib/rpcsec_gss/Makefile98
-rw-r--r--usr/src/lib/rpcsec_gss/Makefile.com64
-rw-r--r--usr/src/lib/rpcsec_gss/amd64/Makefile39
-rw-r--r--usr/src/lib/rpcsec_gss/i386/Makefile37
-rw-r--r--usr/src/lib/rpcsec_gss/mapfile-vers78
-rw-r--r--usr/src/lib/rpcsec_gss/rpcsec_gss.c945
-rw-r--r--usr/src/lib/rpcsec_gss/rpcsec_gss_misc.c346
-rw-r--r--usr/src/lib/rpcsec_gss/rpcsec_gss_utils.c336
-rw-r--r--usr/src/lib/rpcsec_gss/sparc/Makefile38
-rw-r--r--usr/src/lib/rpcsec_gss/sparcv9/Makefile40
-rw-r--r--usr/src/lib/rpcsec_gss/svc_rpcsec_gss.c1698
11 files changed, 3719 insertions, 0 deletions
diff --git a/usr/src/lib/rpcsec_gss/Makefile b/usr/src/lib/rpcsec_gss/Makefile
new file mode 100644
index 0000000000..da7a5e7056
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/Makefile
@@ -0,0 +1,98 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1995,1997, by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+include ../../Makefile.master
+
+SUBDIRS= $(MACH) $(BUILD64) $(MACH64)
+
+# include library definitions
+include ../Makefile.lib
+
+#override INS.liblink
+INS.liblink= -$(RM) $@; $(SYMLINK) $(LIBLINKPATH)$(LIBLINKS)$(VERS) $@
+
+HDRS=
+
+CHECKHDRS= $(HDRS:%.h=%.check)
+
+#install rule
+$(ROOTDIRS)/%: %
+ $(INS.file)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+LIBRARY= rpcsec.a
+TEXT_DOMAIN= SUNW_OST_NETRPC
+XGETFLAGS= -a
+POFILE= $(LIBRARY:.a=.po)
+POFILES= generic.po
+
+.KEEP_STATE:
+
+all: .WAIT $(SUBDIRS)
+
+lint: .WAIT $(SUBDIRS)
+
+install: all .WAIT $(SUBDIRS)
+
+install_h:
+
+clean clobber: $(SUBDIRS)
+
+check: $(CHECKHDRS)
+
+# include library targets
+include ../Makefile.targ
+
+$(MACH) $(MACH64): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg: $(MSGDOMAIN) $(POFILE)
+ $(RM) $(MSGDOMAIN)/$(POFILE)
+ $(CP) $(POFILE) $(MSGDOMAIN)
+
+$(POFILE): .WAIT $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+$(POFILES):
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]`
+ $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@
+ $(RM) messages.po
+
+catalog:
+
+$(MSGDOMAIN):
+ $(INS.dir)
+
+FRC:
diff --git a/usr/src/lib/rpcsec_gss/Makefile.com b/usr/src/lib/rpcsec_gss/Makefile.com
new file mode 100644
index 0000000000..a7a3531ed5
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/Makefile.com
@@ -0,0 +1,64 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY= rpcsec.a
+VERS = .1
+
+OBJECTS=rpcsec_gss.o rpcsec_gss_misc.o rpcsec_gss_utils.o svc_rpcsec_gss.o
+
+# include library definitions
+include ../../Makefile.lib
+
+MAPFILE= ../mapfile-vers
+SRCS= $(OBJECTS:%.o=../%.c)
+
+CPPFLAGS += -D_REENTRANT -I$(SRC)/uts/common/gssapi/include \
+ -I$(SRC)/uts/common
+CFLAGS += $(XFFLAG)
+
+LINTSRC= $(LINTLIB:%.ln=%)
+
+LIBS = $(DYNLIB)
+
+LDLIBS += -lgss -lnsl -lc
+DYNFLAGS += -M$(MAPFILE)
+
+.KEEP_STATE:
+
+lint: lintcheck
+
+# include library targets
+include ../../Makefile.targ
+
+# librpcsec build rules
+
+pics/%.o: ../%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+$(DYNLIB) : $(MAPFILE)
diff --git a/usr/src/lib/rpcsec_gss/amd64/Makefile b/usr/src/lib/rpcsec_gss/amd64/Makefile
new file mode 100644
index 0000000000..c70cf69700
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/amd64/Makefile
@@ -0,0 +1,39 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+LIBS= $(DYNLIB)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
+
diff --git a/usr/src/lib/rpcsec_gss/i386/Makefile b/usr/src/lib/rpcsec_gss/i386/Makefile
new file mode 100644
index 0000000000..6feae2497f
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/i386/Makefile
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1997, by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/rpcsec_gss/i386/Makefile
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
+
diff --git a/usr/src/lib/rpcsec_gss/mapfile-vers b/usr/src/lib/rpcsec_gss/mapfile-vers
new file mode 100644
index 0000000000..03dc20ed9b
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/mapfile-vers
@@ -0,0 +1,78 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Generic interface definition for usr/src/lib/rpcsec_gss.
+#
+# For information regarding the establishment of versioned definitions see:
+# The Linker and Libraries Manual (version 2.5 or greater)
+# This is part of the Developers Guide in the Answerbook. Specifically refer
+# to Chapter 2 under section "Defining Additional Symbols" through section
+# "Reducing Symbol Scope", and Chapter 5 "Versioning".
+#
+# For specific OSNET rules for the modification (evolution) of these version
+# definitions see:
+# Policy for Shared Library Version Names and Interface Definitions
+
+
+# Note: Even though the SUNW_1.2 version now contains no symbols
+# beyond what was present at Solaris 2.6, the SUNW_1.2 version MUST be
+# present. This is because applications built on 2.6 Beta
+# (when it did contain symbols explicitly) may depend on it.
+#
+SUNW_1.2 { # This empty version MUST BE preserved for 2.6Beta apps
+ global:
+ SUNW_1.2;
+} SUNW_1.1;
+
+SUNW_1.1 {
+ global:
+ SUNW_1.1;
+};
+
+SUNWprivate_1.1 {
+ global:
+ __rpc_gss_seccreate;
+ __rpc_gss_set_defaults;
+ __rpc_gss_wrap;
+ __rpc_gss_unwrap;
+ __rpc_gss_max_data_length;
+ __rpc_gss_get_error;
+ __rpc_gss_mech_to_oid;
+ __rpc_gss_qop_to_num;
+ __rpc_gss_get_principal_name;
+ __rpc_gss_get_mechanisms;
+ __rpc_gss_get_mech_info;
+ __rpc_gss_get_versions;
+ __rpc_gss_is_installed;
+ __rpc_gss_set_callback;
+ __rpc_gss_getcred;
+ __svcrpcsec_gss;
+ __rpc_gss_set_svc_name;
+ __rpc_gss_svc_max_data_length;
+
+ local:
+ *;
+};
diff --git a/usr/src/lib/rpcsec_gss/rpcsec_gss.c b/usr/src/lib/rpcsec_gss/rpcsec_gss.c
new file mode 100644
index 0000000000..c71840304d
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/rpcsec_gss.c
@@ -0,0 +1,945 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1986-1995, 1997, 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header:
+ * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi.c,v
+ * 1.14 1995/03/22 22:07:55 jik Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <pthread.h>
+#include <thread.h>
+#include <syslog.h>
+#include <gssapi/gssapi.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_defs.h>
+
+static void rpc_gss_nextverf();
+static bool_t rpc_gss_marshall();
+static bool_t rpc_gss_validate();
+static bool_t rpc_gss_refresh();
+static void rpc_gss_destroy();
+static void rpc_gss_destroy_pvt();
+static bool_t rpc_gss_seccreate_pvt();
+static bool_t validate_seqwin();
+
+/*
+ * Globals that should have header files but don't.
+ */
+extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *);
+extern int _thr_main(void);
+extern int _thr_getspecific(thread_key_t key, void **valuep);
+typedef void (*PFrV) (void *);
+extern int _thr_keycreate(thread_key_t *pkey, PFrV destructor);
+extern int _thr_setspecific(unsigned int key, void *value);
+
+
+static struct auth_ops rpc_gss_ops = {
+ rpc_gss_nextverf,
+ rpc_gss_marshall,
+ rpc_gss_validate,
+ rpc_gss_refresh,
+ rpc_gss_destroy
+};
+
+/*
+ * Private data for RPCSEC_GSS.
+ */
+typedef struct _rpc_gss_data {
+ bool_t established; /* TRUE when established */
+ CLIENT *clnt; /* associated client handle */
+ uint_t version; /* RPCSEC version */
+ gss_ctx_id_t context; /* GSS context id */
+ gss_buffer_desc ctx_handle; /* RPCSEC context handle */
+ uint_t seq_num; /* last sequence number rcvd */
+ gss_cred_id_t my_cred; /* GSS credentials */
+ OM_uint32 qop; /* requested QOP */
+ rpc_gss_service_t service; /* requested service */
+ uint_t gss_proc; /* GSS control procedure */
+ gss_name_t target_name; /* target server */
+ int req_flags; /* GSS request bits */
+ gss_OID mech_type; /* GSS mechanism */
+ OM_uint32 time_req; /* requested cred lifetime */
+ bool_t invalid; /* can't use this any more */
+ OM_uint32 seq_window; /* server sequence window */
+ struct opaque_auth *verifier; /* rpc reply verifier saved for */
+ /* validating the sequence window */
+ gss_channel_bindings_t icb;
+} rpc_gss_data;
+#define AUTH_PRIVATE(auth) ((rpc_gss_data *)auth->ah_private)
+
+/*
+ * Create a context.
+ */
+AUTH *
+__rpc_gss_seccreate(clnt, server_name, mech, service, qop, options_req,
+ options_ret)
+ CLIENT *clnt; /* associated client handle */
+ char *server_name; /* target server */
+ char *mech; /* security mechanism */
+ rpc_gss_service_t service; /* security service */
+ char *qop; /* requested QOP */
+ rpc_gss_options_req_t *options_req; /* requested options */
+ rpc_gss_options_ret_t *options_ret; /* returned options */
+{
+ OM_uint32 gssstat;
+ OM_uint32 minor_stat;
+ gss_name_t target_name;
+ gss_OID mech_type;
+ OM_uint32 ret_flags;
+ OM_uint32 time_rec;
+ gss_buffer_desc input_name;
+ AUTH *auth = NULL;
+ rpc_gss_data *ap = NULL;
+ OM_uint32 qop_num;
+ rpc_gss_error_t error;
+ void __rpc_gss_get_error();
+
+ /*
+ * convert ascii strings to GSS values
+ */
+ if (!__rpc_gss_qop_to_num(qop, mech, &qop_num)) {
+ __rpc_gss_get_error(&error);
+ return (NULL);
+ }
+
+ if (!__rpc_gss_mech_to_oid(mech, &mech_type)) {
+ __rpc_gss_get_error(&error);
+ return (NULL);
+ }
+
+ /*
+ * convert name to GSS internal type
+ */
+ input_name.value = server_name;
+ input_name.length = strlen(server_name);
+ gssstat = gss_import_name(&minor_stat, &input_name,
+ (gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
+ &target_name);
+ if (gssstat != GSS_S_COMPLETE) {
+ rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
+ rpc_gss_err.system_error = ENOMEM;
+ return (NULL);
+ }
+
+ /*
+ * Create AUTH handle. Save the necessary interface information
+ * so that the client can refresh the handle later if needed.
+ */
+ if ((auth = (AUTH *) malloc(sizeof (*auth))) != NULL)
+ ap = (rpc_gss_data *) malloc(sizeof (*ap));
+ if (auth == NULL || ap == NULL) {
+ rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
+ rpc_gss_err.system_error = ENOMEM;
+ if (auth != NULL)
+ free((char *)auth);
+ (void) gss_release_name(&minor_stat, &target_name);
+ return (NULL);
+ }
+
+ memset((char *)ap, 0, sizeof (*ap));
+ ap->clnt = clnt;
+ ap->version = RPCSEC_GSS_VERSION;
+ if (options_req != NULL) {
+ ap->my_cred = options_req->my_cred;
+ ap->req_flags = options_req->req_flags;
+ ap->time_req = options_req->time_req;
+ ap->icb = options_req->input_channel_bindings;
+ } else {
+ ap->my_cred = GSS_C_NO_CREDENTIAL;
+ ap->req_flags = GSS_C_MUTUAL_FLAG;
+ ap->time_req = 0;
+ ap->icb = NULL;
+ }
+ if ((ap->service = service) == rpc_gss_svc_default)
+ ap->service = rpc_gss_svc_integrity;
+ ap->qop = qop_num;
+ ap->target_name = target_name;
+ ap->mech_type = mech_type;
+
+ /*
+ * Now invoke the real interface that sets up the context from
+ * the information stashed away in the private data.
+ */
+ if (!rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap,
+ &mech_type, &ret_flags, &time_rec)) {
+ if (ap->target_name)
+ (void) gss_release_name(&minor_stat, &ap->target_name);
+ free((char *)ap);
+ free((char *)auth);
+ return (NULL);
+ }
+
+ /*
+ * Make sure that the requested service is supported. In all
+ * cases, integrity service must be available.
+ */
+ if ((ap->service == rpc_gss_svc_privacy &&
+ !(ret_flags & GSS_C_CONF_FLAG)) ||
+ !(ret_flags & GSS_C_INTEG_FLAG)) {
+ rpc_gss_destroy(auth);
+ rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
+ rpc_gss_err.system_error = EPROTONOSUPPORT;
+ return (NULL);
+ }
+
+ /*
+ * return option values if requested
+ */
+ if (options_ret != NULL) {
+ char *s;
+
+ options_ret->major_status = gssstat;
+ options_ret->minor_status = minor_stat;
+ options_ret->rpcsec_version = ap->version;
+ options_ret->ret_flags = ret_flags;
+ options_ret->time_ret = time_rec;
+ options_ret->gss_context = ap->context;
+ if ((s = __rpc_gss_oid_to_mech(mech_type)) != NULL)
+ strcpy(options_ret->actual_mechanism, s);
+ else
+ options_ret->actual_mechanism[0] = '\0';
+ }
+ return (auth);
+}
+
+/*
+ * Private interface to create a context. This is the interface
+ * that's invoked when the context has to be refreshed.
+ */
+static bool_t
+rpc_gss_seccreate_pvt(gssstat, minor_stat, auth, ap, actual_mech_type,
+ ret_flags, time_rec)
+ OM_uint32 *gssstat;
+ OM_uint32 *minor_stat;
+ AUTH *auth;
+ rpc_gss_data *ap;
+ gss_OID *actual_mech_type;
+ OM_uint32 *ret_flags;
+ OM_uint32 *time_rec;
+{
+ CLIENT *clnt = ap->clnt;
+ AUTH *save_auth;
+ enum clnt_stat callstat;
+ rpc_gss_init_arg call_arg;
+ rpc_gss_init_res call_res;
+ gss_buffer_desc *input_token_p, input_token;
+ bool_t free_results = FALSE;
+
+ /*
+ * initialize error
+ */
+ memset(&rpc_createerr, 0, sizeof (rpc_createerr));
+
+ /*
+ * (re)initialize AUTH handle and private data.
+ */
+ memset((char *)auth, 0, sizeof (*auth));
+ auth->ah_ops = &rpc_gss_ops;
+ auth->ah_private = (caddr_t)ap;
+ auth->ah_cred.oa_flavor = RPCSEC_GSS;
+
+ ap->established = FALSE;
+ ap->ctx_handle.length = 0;
+ ap->ctx_handle.value = NULL;
+ ap->context = GSS_C_NO_CONTEXT;
+ ap->seq_num = 0;
+ ap->gss_proc = RPCSEC_GSS_INIT;
+
+ /*
+ * should not change clnt->cl_auth at this time, so save
+ * old handle
+ */
+ save_auth = clnt->cl_auth;
+ clnt->cl_auth = auth;
+
+ /*
+ * set state for starting context setup
+ */
+ input_token_p = GSS_C_NO_BUFFER;
+
+next_token:
+ *gssstat = gss_init_sec_context(minor_stat,
+ ap->my_cred,
+ &ap->context,
+ ap->target_name,
+ ap->mech_type,
+ ap->req_flags,
+ ap->time_req,
+ NULL,
+ input_token_p,
+ actual_mech_type,
+ &call_arg,
+ ret_flags,
+ time_rec);
+
+ if (input_token_p != GSS_C_NO_BUFFER) {
+ OM_uint32 minor_stat2;
+
+ (void) gss_release_buffer(&minor_stat2, input_token_p);
+ input_token_p = GSS_C_NO_BUFFER;
+ }
+
+ if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) {
+
+ goto cleanup;
+ }
+
+ /*
+ * if we got a token, pass it on
+ */
+ if (call_arg.length != 0) {
+ struct timeval timeout = {30, 0};
+
+ memset((char *)&call_res, 0, sizeof (call_res));
+ callstat = clnt_call(clnt, NULLPROC,
+ __xdr_rpc_gss_init_arg, (caddr_t)&call_arg,
+ __xdr_rpc_gss_init_res, (caddr_t)&call_res,
+ timeout);
+ (void) gss_release_buffer(minor_stat, &call_arg);
+
+ if (callstat != RPC_SUCCESS) {
+ goto cleanup;
+ }
+ /*
+ * we have results - note that these need to be freed
+ */
+ free_results = TRUE;
+
+ if (call_res.gss_major != GSS_S_COMPLETE &&
+ call_res.gss_major != GSS_S_CONTINUE_NEEDED)
+ goto cleanup;
+
+ ap->gss_proc = RPCSEC_GSS_CONTINUE_INIT;
+
+ /*
+ * check for ctx_handle
+ */
+ if (ap->ctx_handle.length == 0) {
+ if (call_res.ctx_handle.length == 0)
+ goto cleanup;
+ GSS_DUP_BUFFER(ap->ctx_handle,
+ call_res.ctx_handle);
+ } else if (!GSS_BUFFERS_EQUAL(ap->ctx_handle,
+ call_res.ctx_handle))
+ goto cleanup;
+
+ /*
+ * check for token
+ */
+ if (call_res.token.length != 0) {
+ if (*gssstat == GSS_S_COMPLETE)
+ goto cleanup;
+ GSS_DUP_BUFFER(input_token, call_res.token);
+ input_token_p = &input_token;
+
+ } else if (*gssstat != GSS_S_COMPLETE)
+ goto cleanup;
+
+ /* save the sequence window value; validate later */
+ ap->seq_window = call_res.seq_window;
+ xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
+ free_results = FALSE;
+ }
+
+ /*
+ * results were okay.. continue if necessary
+ */
+ if (*gssstat == GSS_S_CONTINUE_NEEDED)
+ goto next_token;
+
+ /*
+ * Validate the sequence window - RFC 2203 section 5.2.3.1
+ */
+ if (!validate_seqwin(ap)) {
+ goto cleanup;
+ }
+
+ /*
+ * Done! Security context creation is successful.
+ * Ready for exchanging data.
+ */
+ ap->established = TRUE;
+ ap->seq_num = 1;
+ ap->gss_proc = RPCSEC_GSS_DATA;
+ ap->invalid = FALSE;
+
+ clnt->cl_auth = save_auth; /* restore cl_auth */
+ return (TRUE);
+
+cleanup:
+ if (ap->context != GSS_C_NO_CONTEXT)
+ rpc_gss_destroy_pvt(auth);
+ if (free_results)
+ xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
+ clnt->cl_auth = save_auth; /* restore cl_auth */
+
+/*
+ * if (rpc_createerr.cf_stat == 0)
+ * rpc_createerr.cf_stat = RPC_AUTHERROR;
+ */
+ if (rpc_createerr.cf_stat == 0) {
+ rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
+ rpc_gss_err.system_error = RPC_AUTHERROR;
+ }
+
+ return (FALSE);
+}
+
+/*
+ * Set service defaults.
+ */
+bool_t
+__rpc_gss_set_defaults(auth, service, qop)
+ AUTH *auth;
+ rpc_gss_service_t service;
+ char *qop;
+{
+ /*LINTED*/
+ rpc_gss_data *ap = AUTH_PRIVATE(auth);
+ char *mech;
+ OM_uint32 qop_num;
+
+ switch (service) {
+ case rpc_gss_svc_integrity:
+ case rpc_gss_svc_privacy:
+ case rpc_gss_svc_none:
+ break;
+ case rpc_gss_svc_default:
+ service = rpc_gss_svc_integrity;
+ break;
+ default:
+ return (FALSE);
+ }
+
+ if ((mech = __rpc_gss_oid_to_mech(ap->mech_type)) == NULL)
+ return (FALSE);
+
+ if (!__rpc_gss_qop_to_num(qop, mech, &qop_num))
+ return (FALSE);
+
+ ap->qop = qop_num;
+ ap->service = service;
+ return (TRUE);
+}
+
+/*
+ * Marshall credentials.
+ */
+static bool_t
+marshall_creds(ap, xdrs)
+ rpc_gss_data *ap;
+ XDR *xdrs;
+{
+ rpc_gss_creds ag_creds;
+ char cred_buf[MAX_AUTH_BYTES];
+ struct opaque_auth creds;
+ XDR cred_xdrs;
+
+ ag_creds.version = ap->version;
+ ag_creds.gss_proc = ap->gss_proc;
+ ag_creds.seq_num = ap->seq_num;
+ ag_creds.service = ap->service;
+
+ /*
+ * If context has not been set up yet, use NULL handle.
+ */
+ if (ap->ctx_handle.length > 0)
+ ag_creds.ctx_handle = ap->ctx_handle;
+ else {
+ ag_creds.ctx_handle.length = 0;
+ ag_creds.ctx_handle.value = NULL;
+ }
+
+ xdrmem_create(&cred_xdrs, (caddr_t)cred_buf, MAX_AUTH_BYTES,
+ XDR_ENCODE);
+ if (!__xdr_rpc_gss_creds(&cred_xdrs, &ag_creds)) {
+ XDR_DESTROY(&cred_xdrs);
+ return (FALSE);
+ }
+
+ creds.oa_flavor = RPCSEC_GSS;
+ creds.oa_base = cred_buf;
+ creds.oa_length = xdr_getpos(&cred_xdrs);
+ XDR_DESTROY(&cred_xdrs);
+
+ if (!xdr_opaque_auth(xdrs, &creds))
+ return (FALSE);
+
+ return (TRUE);
+}
+
+/*
+ * Marshall verifier. The verifier is the checksum of the RPC header
+ * up to and including the credential field. The XDR handle that's
+ * passed in has the header up to and including the credential field
+ * encoded. A pointer to the transmit buffer is also passed in.
+ */
+static bool_t
+marshall_verf(ap, xdrs, buf)
+ rpc_gss_data *ap;
+ XDR *xdrs; /* send XDR */
+ char *buf; /* pointer of send buffer */
+{
+ struct opaque_auth verf;
+ OM_uint32 major, minor;
+ gss_buffer_desc in_buf, out_buf;
+ bool_t ret = FALSE;
+
+ /*
+ * If context is not established yet, use NULL verifier.
+ */
+ if (!ap->established) {
+ verf.oa_flavor = AUTH_NONE;
+ verf.oa_base = NULL;
+ verf.oa_length = 0;
+ return (xdr_opaque_auth(xdrs, &verf));
+ }
+
+ verf.oa_flavor = RPCSEC_GSS;
+ in_buf.length = xdr_getpos(xdrs);
+ in_buf.value = buf;
+ if ((major = gss_sign(&minor, ap->context, ap->qop, &in_buf,
+ &out_buf)) != GSS_S_COMPLETE) {
+ if (major == GSS_S_CONTEXT_EXPIRED) {
+ ap->invalid = TRUE;
+ }
+ return (FALSE);
+ }
+ verf.oa_base = out_buf.value;
+ verf.oa_length = out_buf.length;
+ ret = xdr_opaque_auth(xdrs, &verf);
+ (void) gss_release_buffer(&minor, &out_buf);
+
+ return (ret);
+}
+
+/*
+ * Function: rpc_gss_nextverf. Not used.
+ */
+static void
+rpc_gss_nextverf()
+{
+}
+
+/*
+ * Function: rpc_gss_marshall - not used.
+ */
+static bool_t
+rpc_gss_marshall(auth, xdrs)
+ AUTH *auth;
+ XDR *xdrs;
+{
+ if (!xdr_opaque_auth(xdrs, &auth->ah_cred) ||
+ !xdr_opaque_auth(xdrs, &auth->ah_verf))
+ return (FALSE);
+ return (TRUE);
+}
+
+/*
+ * Validate sequence window upon a successful RPCSEC_GSS INIT session.
+ * The sequence window sent back by the server should be verifiable by
+ * the verifier which is a checksum of the sequence window.
+ */
+static bool_t
+validate_seqwin(rpc_gss_data *ap)
+{
+ uint_t seq_win_net;
+ OM_uint32 major = 0, minor = 0;
+ gss_buffer_desc msg_buf, tok_buf;
+ int qop_state = 0;
+
+ seq_win_net = (uint_t)htonl(ap->seq_window);
+ msg_buf.length = sizeof (seq_win_net);
+ msg_buf.value = (char *)&seq_win_net;
+ tok_buf.length = ap->verifier->oa_length;
+ tok_buf.value = ap->verifier->oa_base;
+ major = gss_verify(&minor, ap->context, &msg_buf, &tok_buf, &qop_state);
+ if (major != GSS_S_COMPLETE)
+ return (FALSE);
+ return (TRUE);
+}
+
+/*
+ * Validate RPC response verifier from server. The response verifier
+ * is the checksum of the request sequence number.
+ */
+static bool_t
+rpc_gss_validate(auth, verf)
+ AUTH *auth;
+ struct opaque_auth *verf;
+{
+ /*LINTED*/
+ rpc_gss_data *ap = AUTH_PRIVATE(auth);
+ uint_t seq_num_net;
+ OM_uint32 major, minor;
+ gss_buffer_desc msg_buf, tok_buf;
+ int qop_state;
+
+ /*
+ * If context is not established yet, save the verifier for
+ * validating the sequence window later at the end of context
+ * creation session.
+ */
+ if (!ap->established) {
+ if (ap->verifier == NULL) {
+ ap->verifier = malloc(sizeof (struct opaque_auth));
+ memset(ap->verifier, 0, sizeof (struct opaque_auth));
+ if (verf->oa_length > 0)
+ ap->verifier->oa_base = malloc(verf->oa_length);
+ } else {
+ if (ap->verifier->oa_length > 0)
+ free(ap->verifier->oa_base);
+ if (verf->oa_length > 0)
+ ap->verifier->oa_base = malloc(verf->oa_length);
+ }
+ ap->verifier->oa_length = verf->oa_length;
+ bcopy(verf->oa_base, ap->verifier->oa_base, verf->oa_length);
+ return (TRUE);
+ }
+
+ seq_num_net = (uint_t)htonl(ap->seq_num);
+ msg_buf.length = sizeof (seq_num_net);
+ msg_buf.value = (char *)&seq_num_net;
+ tok_buf.length = verf->oa_length;
+ tok_buf.value = verf->oa_base;
+ major = gss_verify(&minor, ap->context, &msg_buf, &tok_buf, &qop_state);
+ if (major != GSS_S_COMPLETE)
+ return (FALSE);
+ return (TRUE);
+}
+
+/*
+ * Refresh client context. This is necessary sometimes because the
+ * server will ocassionally destroy contexts based on LRU method, or
+ * because of expired credentials.
+ */
+static bool_t
+rpc_gss_refresh(auth, msg)
+ AUTH *auth;
+ struct rpc_msg *msg;
+{
+ /*LINTED*/
+ rpc_gss_data *ap = AUTH_PRIVATE(auth);
+ OM_uint32 gssstat, minor_stat;
+
+ /*
+ * The context needs to be recreated only when the error status
+ * returned from the server is one of the following:
+ * RPCSEC_GSS_NOCRED and RPCSEC_GSS_FAILED
+ * The existing context should not be destroyed unless the above
+ * error status codes are received or if the context has not
+ * been set up.
+ */
+
+ if (msg->rjcted_rply.rj_why == RPCSEC_GSS_NOCRED ||
+ msg->rjcted_rply.rj_why == RPCSEC_GSS_FAILED ||
+ !ap->established) {
+ /*
+ * Destroy the context if necessary. Use the same memory
+ * for the new context since we've already passed a pointer
+ * to it to the user.
+ */
+ if (ap->context != GSS_C_NO_CONTEXT) {
+ (void) gss_delete_sec_context(&minor_stat, &ap->context,
+ NULL);
+ ap->context = GSS_C_NO_CONTEXT;
+ }
+ if (ap->ctx_handle.length != 0) {
+ (void) gss_release_buffer(&minor_stat,
+ &ap->ctx_handle);
+ ap->ctx_handle.length = 0;
+ ap->ctx_handle.value = NULL;
+ }
+
+ /*
+ * If the context was not already established, don't try to
+ * recreate it.
+ */
+ if (!ap->established) {
+ ap->invalid = TRUE;
+ return (FALSE);
+ }
+
+ /*
+ * Recreate context.
+ */
+ if (rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap,
+ (gss_OID *)0, (OM_uint32 *)0, (OM_uint32 *)0))
+ return (TRUE);
+ else {
+ ap->invalid = TRUE;
+ return (FALSE);
+ }
+ }
+ return (FALSE);
+}
+
+/*
+ * Destroy a context.
+ */
+static void
+rpc_gss_destroy(auth)
+ AUTH *auth;
+{
+ /*LINTED*/
+ rpc_gss_data *ap = AUTH_PRIVATE(auth);
+
+ rpc_gss_destroy_pvt(auth);
+ free((char *)ap);
+ free(auth);
+}
+
+/*
+ * Private interface to destroy a context without freeing up
+ * the memory used by it. We need to do this when a refresh
+ * fails, for example, so the user will still have a handle.
+ */
+static void
+rpc_gss_destroy_pvt(auth)
+ AUTH *auth;
+{
+ struct timeval timeout;
+ OM_uint32 minor_stat;
+ /*LINTED*/
+ rpc_gss_data *ap = AUTH_PRIVATE(auth);
+
+ /*
+ * If we have a server context id, inform server that we are
+ * destroying the context.
+ */
+ if (ap->ctx_handle.length != 0) {
+ ap->gss_proc = RPCSEC_GSS_DESTROY;
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ (void) clnt_call(ap->clnt, NULLPROC, xdr_void, NULL,
+ xdr_void, NULL, timeout);
+
+ (void) gss_release_buffer(&minor_stat, &ap->ctx_handle);
+ ap->ctx_handle.length = 0;
+ ap->ctx_handle.value = NULL;
+ }
+
+ /*
+ * Destroy local GSS context.
+ */
+ if (ap->context != GSS_C_NO_CONTEXT) {
+ (void) gss_delete_sec_context(&minor_stat, &ap->context, NULL);
+ ap->context = GSS_C_NO_CONTEXT;
+ }
+
+ /*
+ * Looks like we need to release default credentials if we use it.
+ * Non-default creds need to be released by user.
+ */
+ if (ap->my_cred == GSS_C_NO_CREDENTIAL)
+ (void) gss_release_cred(&minor_stat, &ap->my_cred);
+
+ /*
+ * Release any internal name structures.
+ */
+ if (ap->target_name != NULL) {
+ (void) gss_release_name(&minor_stat, &ap->target_name);
+ ap->target_name = NULL;
+ }
+
+ /*
+ * Free the verifier saved for sequence window checking.
+ */
+ if (ap->verifier != NULL) {
+ if (ap->verifier->oa_length > 0)
+ free(ap->verifier->oa_base);
+ free(ap->verifier);
+ ap->verifier = NULL;
+ }
+}
+
+/*
+ * Wrap client side data. The encoded header is passed in through
+ * buf and buflen. The header is up to but not including the
+ * credential field.
+ */
+bool_t
+__rpc_gss_wrap(auth, buf, buflen, out_xdrs, xdr_func, xdr_ptr)
+ AUTH *auth;
+ char *buf; /* encoded header */
+ uint_t buflen; /* encoded header length */
+ XDR *out_xdrs;
+ bool_t (*xdr_func)();
+ caddr_t xdr_ptr;
+{
+ /*LINTED*/
+ rpc_gss_data *ap = AUTH_PRIVATE(auth);
+ XDR xdrs;
+ char tmp_buf[512];
+
+
+ /*
+ * Reject an invalid context.
+ */
+ if (ap->invalid)
+ return (FALSE);
+
+ /*
+ * If context is established, bump up sequence number.
+ */
+ if (ap->established)
+ ap->seq_num++;
+
+ /*
+ * Create the header in a temporary XDR context and buffer
+ * before putting it out.
+ */
+ xdrmem_create(&xdrs, tmp_buf, sizeof (tmp_buf), XDR_ENCODE);
+ if (!XDR_PUTBYTES(&xdrs, buf, buflen))
+ return (FALSE);
+
+ /*
+ * create cred field
+ */
+ if (!marshall_creds(ap, &xdrs))
+ return (FALSE);
+
+ /*
+ * create verifier
+ */
+ if (!marshall_verf(ap, &xdrs, tmp_buf))
+ return (FALSE);
+
+ /*
+ * write out header and destroy temp structures
+ */
+ if (!XDR_PUTBYTES(out_xdrs, tmp_buf, XDR_GETPOS(&xdrs)))
+ return (FALSE);
+ XDR_DESTROY(&xdrs);
+
+ /*
+ * If context is not established, or if neither integrity
+ * nor privacy is used, just XDR encode data.
+ */
+ if (!ap->established || ap->service == rpc_gss_svc_none)
+ return ((*xdr_func)(out_xdrs, xdr_ptr));
+
+ return (__rpc_gss_wrap_data(ap->service, ap->qop, ap->context,
+ ap->seq_num, out_xdrs, xdr_func, xdr_ptr));
+}
+
+/*
+ * Unwrap received data.
+ */
+bool_t
+__rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr)
+ AUTH *auth;
+ XDR *in_xdrs;
+ bool_t (*xdr_func)();
+ caddr_t xdr_ptr;
+{
+ /*LINTED*/
+ rpc_gss_data *ap = AUTH_PRIVATE(auth);
+
+ /*
+ * If context is not established, of if neither integrity
+ * nor privacy is used, just XDR encode data.
+ */
+ if (!ap->established || ap->service == rpc_gss_svc_none)
+ return ((*xdr_func)(in_xdrs, xdr_ptr));
+
+ return (__rpc_gss_unwrap_data(ap->service,
+ ap->context,
+ ap->seq_num,
+ ap->qop,
+ in_xdrs, xdr_func, xdr_ptr));
+}
+
+int
+__rpc_gss_max_data_length(auth, max_tp_unit_len)
+ AUTH *auth;
+ int max_tp_unit_len;
+{
+ /*LINTED*/
+ rpc_gss_data *ap = AUTH_PRIVATE(auth);
+
+ if (!ap->established || max_tp_unit_len <= 0)
+ return (0);
+
+ return (__find_max_data_length(ap->service,
+ ap->context,
+ ap->qop,
+ max_tp_unit_len));
+}
+
+#undef rpc_gss_err
+
+rpc_gss_error_t rpc_gss_err;
+static mutex_t rge_lock; /* protects TSD key creation */
+
+rpc_gss_error_t *
+__rpc_gss_err()
+{
+ static thread_key_t rpc_gss_err_key = 0;
+ rpc_gss_error_t *tsd = 0;
+
+ if (_thr_main())
+ return (&rpc_gss_err);
+ if (_thr_getspecific(rpc_gss_err_key, (void **) &tsd) != 0) {
+ mutex_lock(&rge_lock);
+ if (_thr_keycreate(&rpc_gss_err_key, free) != 0) {
+ mutex_unlock(&rge_lock);
+ return (&rpc_gss_err);
+ }
+ mutex_unlock(&rge_lock);
+ }
+ if (!tsd) {
+ tsd = (rpc_gss_error_t *)
+ calloc(1, sizeof (rpc_gss_error_t));
+ if (_thr_setspecific(rpc_gss_err_key, (void *) tsd) != 0) {
+ if (tsd)
+ free(tsd);
+ return (&rpc_gss_err);
+ }
+ memset(tsd, 0, sizeof (rpc_gss_error_t));
+ return (tsd);
+ }
+ return (tsd);
+}
+
+void
+__rpc_gss_get_error(error)
+ rpc_gss_error_t *error;
+{
+
+ error->rpc_gss_error = rpc_gss_err.rpc_gss_error;
+ error->system_error = rpc_gss_err.system_error;
+}
diff --git a/usr/src/lib/rpcsec_gss/rpcsec_gss_misc.c b/usr/src/lib/rpcsec_gss/rpcsec_gss_misc.c
new file mode 100644
index 0000000000..e9bf848365
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/rpcsec_gss_misc.c
@@ -0,0 +1,346 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1986-1995, 1997, 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header:
+ * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi_misc.c,v
+ * 1.10 1994/10/27 12:39:23 jik Exp $
+ */
+
+#include <stdlib.h>
+#include <gssapi/gssapi.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_defs.h>
+
+/*
+ * Miscellaneous XDR routines.
+ */
+bool_t
+__xdr_gss_buf(xdrs, buf)
+ XDR *xdrs;
+ gss_buffer_t buf;
+{
+ u_int cast_len, bound_len;
+
+ /*
+ * We go through this contortion because size_t is a now a ulong,
+ * GSS-API uses ulongs.
+ */
+
+ if (xdrs->x_op != XDR_DECODE) {
+ bound_len = cast_len = (u_int) buf->length;
+ } else {
+ bound_len = (u_int)-1;
+ }
+
+ if (xdr_bytes(xdrs, (char **)&buf->value, &cast_len,
+ bound_len) == TRUE) {
+ if (xdrs->x_op == XDR_DECODE)
+ buf->length = cast_len;
+
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+bool_t
+__xdr_rpc_gss_creds(xdrs, creds)
+ XDR *xdrs;
+ rpc_gss_creds *creds;
+{
+ if (!xdr_u_int(xdrs, &creds->version) ||
+ !xdr_u_int(xdrs, &creds->gss_proc) ||
+ !xdr_u_int(xdrs, &creds->seq_num) ||
+ !xdr_u_int(xdrs, (u_int *)&creds->service) ||
+ !__xdr_gss_buf(xdrs, &creds->ctx_handle))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+__xdr_rpc_gss_init_arg(xdrs, init_arg)
+ XDR *xdrs;
+ rpc_gss_init_arg *init_arg;
+{
+ if (!__xdr_gss_buf(xdrs, init_arg))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+__xdr_rpc_gss_init_res(xdrs, init_res)
+ XDR *xdrs;
+ rpc_gss_init_res *init_res;
+{
+ if (!__xdr_gss_buf(xdrs, &init_res->ctx_handle) ||
+ !xdr_u_int(xdrs, (u_int *)&init_res->gss_major) ||
+ !xdr_u_int(xdrs, (u_int *)&init_res->gss_minor) ||
+ !xdr_u_int(xdrs, (u_int *)&init_res->seq_window) ||
+ !__xdr_gss_buf(xdrs, &init_res->token))
+ return (FALSE);
+ return (TRUE);
+}
+
+/*
+ * Generic routine to wrap data used by client and server sides.
+ */
+bool_t
+__rpc_gss_wrap_data(service, qop, context, seq_num, out_xdrs, xdr_func,
+ xdr_ptr)
+ OM_uint32 qop;
+ rpc_gss_service_t service;
+ gss_ctx_id_t context;
+ u_int seq_num;
+ XDR *out_xdrs;
+ bool_t (*xdr_func)();
+ caddr_t xdr_ptr;
+{
+ OM_uint32 minor;
+ gss_buffer_desc in_buf, out_buf;
+ XDR temp_xdrs;
+ bool_t conf_state;
+ bool_t ret = FALSE;
+ u_int bufsiz;
+ char *buf;
+
+ /*
+ * Create a temporary XDR/buffer to hold the data to be wrapped.
+ */
+ out_buf.length = 0;
+ bufsiz = xdr_sizeof(xdr_func, xdr_ptr) +
+ xdr_sizeof(xdr_u_int, &seq_num);
+ if ((buf = (char *)malloc(bufsiz)) == NULL) {
+ fprintf(stderr, dgettext(TEXT_DOMAIN, "malloc failed in "
+ "__rpc_gss_wrap_data\n"));
+ return (FALSE);
+ }
+ xdrmem_create(&temp_xdrs, buf, bufsiz, XDR_ENCODE);
+
+ /*
+ * serialize the sequence number into tmp memory
+ */
+ if (!xdr_u_int(&temp_xdrs, &seq_num))
+ goto fail;
+
+ /*
+ * serialize the arguments into tmp memory
+ */
+ if (!(*xdr_func)(&temp_xdrs, xdr_ptr))
+ goto fail;
+
+ /*
+ * Data to be wrapped goes in in_buf. If privacy is used,
+ * out_buf will have wrapped data (in_buf will no longer be
+ * needed). If integrity is used, out_buf will have checksum
+ * which will follow the data in in_buf.
+ */
+ in_buf.length = xdr_getpos(&temp_xdrs);
+ in_buf.value = temp_xdrs.x_base;
+
+ switch (service) {
+ case rpc_gss_svc_privacy:
+ if (gss_seal(&minor, context, TRUE, qop, &in_buf,
+ &conf_state, &out_buf) != GSS_S_COMPLETE)
+ goto fail;
+ in_buf.length = 0; /* in_buf not needed */
+ if (!conf_state)
+ goto fail;
+ break;
+ case rpc_gss_svc_integrity:
+ if (gss_sign(&minor, context, qop, &in_buf,
+ &out_buf) != GSS_S_COMPLETE)
+ goto fail;
+ break;
+ default:
+ goto fail;
+ }
+
+ /*
+ * write out in_buf and out_buf as needed
+ */
+ if (in_buf.length != 0) {
+ if (!__xdr_gss_buf(out_xdrs, &in_buf))
+ goto fail;
+ }
+
+ if (!__xdr_gss_buf(out_xdrs, &out_buf))
+ goto fail;
+ ret = TRUE;
+fail:
+ XDR_DESTROY(&temp_xdrs);
+ if (buf)
+ (void) free(buf);
+ if (out_buf.length != 0)
+ (void) gss_release_buffer(&minor, &out_buf);
+ return (ret);
+}
+
+/*
+ * Generic routine to unwrap data used by client and server sides.
+ */
+bool_t
+__rpc_gss_unwrap_data(service, context, seq_num, qop_check, in_xdrs, xdr_func,
+ xdr_ptr)
+ rpc_gss_service_t service;
+ gss_ctx_id_t context;
+ u_int seq_num;
+ OM_uint32 qop_check;
+ XDR *in_xdrs;
+ bool_t (*xdr_func)();
+ caddr_t xdr_ptr;
+{
+ gss_buffer_desc in_buf, out_buf;
+ XDR temp_xdrs;
+ u_int seq_num2;
+ bool_t conf;
+ OM_uint32 major = GSS_S_COMPLETE, minor = 0;
+ int qop;
+
+ in_buf.value = NULL;
+ out_buf.value = NULL;
+
+ /*
+ * Pull out wrapped data. For privacy service, this is the
+ * encrypted data. For integrity service, this is the data
+ * followed by a checksum.
+ */
+ if (!__xdr_gss_buf(in_xdrs, &in_buf))
+ return (FALSE);
+
+ if (service == rpc_gss_svc_privacy) {
+ major = gss_unseal(&minor, context, &in_buf, &out_buf, &conf,
+ &qop);
+ free(in_buf.value);
+ if (major != GSS_S_COMPLETE)
+ return (FALSE);
+ /*
+ * Keep the returned token (unencrypted data) in in_buf.
+ */
+ in_buf.length = out_buf.length;
+ in_buf.value = out_buf.value;
+
+ /*
+ * If privacy was not used, or if QOP is not what we are
+ * expecting, fail.
+ */
+ if (!conf || qop != qop_check)
+ goto fail;
+
+ } else if (service == rpc_gss_svc_integrity) {
+ if (!__xdr_gss_buf(in_xdrs, &out_buf))
+ return (FALSE);
+ major = gss_verify(&minor, context, &in_buf, &out_buf, &qop);
+ free(out_buf.value);
+ if (major != GSS_S_COMPLETE) {
+ free(in_buf.value);
+ return (FALSE);
+ }
+
+ /*
+ * If QOP is not what we are expecting, fail.
+ */
+ if (qop != qop_check)
+ goto fail;
+ }
+
+ xdrmem_create(&temp_xdrs, in_buf.value, in_buf.length, XDR_DECODE);
+
+ /*
+ * The data consists of the sequence number followed by the
+ * arguments. Make sure sequence number is what we are
+ * expecting (i.e., the value in the header).
+ */
+ if (!xdr_u_int(&temp_xdrs, &seq_num2))
+ goto fail;
+ if (seq_num2 != seq_num)
+ goto fail;
+
+ /*
+ * Deserialize the arguments into xdr_ptr, and release in_buf.
+ */
+ if (!(*xdr_func)(&temp_xdrs, xdr_ptr))
+ goto fail;
+
+ if (service == rpc_gss_svc_privacy)
+ (void) gss_release_buffer(&minor, &in_buf);
+ else
+ free(in_buf.value);
+ XDR_DESTROY(&temp_xdrs);
+ return (TRUE);
+fail:
+ XDR_DESTROY(&temp_xdrs);
+ if (service == rpc_gss_svc_privacy)
+ (void) gss_release_buffer(&minor, &in_buf);
+ else
+ free(in_buf.value);
+ return (FALSE);
+}
+
+/*ARGSUSED*/
+int
+__find_max_data_length(service, context, qop, max_tp_unit_len)
+ rpc_gss_service_t service;
+ gss_ctx_id_t context;
+ OM_uint32 qop;
+ int max_tp_unit_len;
+{
+ int conf;
+ OM_uint32 maj_stat = GSS_S_COMPLETE, min_stat = 0;
+ OM_uint32 max_input_size;
+ int ret_val = 0;
+
+ if (service == rpc_gss_svc_integrity || service == rpc_gss_svc_default)
+ conf = 0;
+ else if (service == rpc_gss_svc_privacy)
+ conf = 1;
+ else if (service == rpc_gss_svc_none)
+ return (max_tp_unit_len);
+
+ maj_stat = gss_wrap_size_limit(&min_stat,
+ context, conf, qop,
+ max_tp_unit_len, &max_input_size);
+
+ /*
+ * max_input_size may result in negative value
+ */
+ if (maj_stat == GSS_S_COMPLETE) {
+ if ((int)max_input_size <= 0)
+ ret_val = 0;
+ else
+ ret_val = (int)(max_input_size);
+ } else {
+ fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "gss_wrap_size_limit failed in "
+ "__find_max_data_length\n"));
+ }
+
+ return (ret_val);
+}
diff --git a/usr/src/lib/rpcsec_gss/rpcsec_gss_utils.c b/usr/src/lib/rpcsec_gss/rpcsec_gss_utils.c
new file mode 100644
index 0000000000..7b7e1e2f75
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/rpcsec_gss_utils.c
@@ -0,0 +1,336 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1986-1995,1997, by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <strings.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_defs.h>
+#include <rpc/trace.h>
+
+#define SVC_INTEGRITY "integrity"
+#define SVC_PRIVACY "privacy"
+#define SVC_NONE "none"
+#define SVC_DEFAULT "default"
+
+#define MCALL_MSG_SIZE 24
+/*
+ * Private data kept per client handle
+ */
+struct cu_data {
+ int cu_fd; /* connections fd */
+ bool_t cu_closeit; /* opened by library */
+ struct netbuf cu_raddr; /* remote address */
+ struct timeval cu_wait; /* retransmit interval */
+ struct timeval cu_total; /* total time for the call */
+ struct rpc_err cu_error;
+ struct t_unitdata *cu_tr_data;
+ XDR cu_outxdrs;
+ char *cu_outbuf_start;
+ char cu_outbuf[MCALL_MSG_SIZE];
+ u_int cu_xdrpos;
+ u_int cu_sendsz; /* send size */
+ u_int cu_recvsz; /* recv size */
+ struct pollfd pfdp;
+ char cu_inbuf[1];
+};
+
+/*
+ * Internal utility routines.
+ */
+bool_t
+__rpc_gss_mech_to_oid(mech, oid)
+ char *mech;
+ rpc_gss_OID *oid;
+{
+
+ if (__gss_mech_to_oid(mech, (gss_OID*)oid) != GSS_S_COMPLETE)
+ return (FALSE);
+
+ return (TRUE);
+}
+
+char *
+__rpc_gss_oid_to_mech(oid)
+ rpc_gss_OID oid;
+{
+
+ return ((char *)__gss_oid_to_mech((const gss_OID)oid));
+}
+
+
+bool_t
+__rpc_gss_qop_to_num(qop, mech, num)
+ char *qop;
+ char *mech;
+ OM_uint32 *num;
+{
+
+ if (__gss_qop_to_num(qop, mech, num) != GSS_S_COMPLETE)
+ return (FALSE);
+ return (TRUE);
+}
+
+char *
+__rpc_gss_num_to_qop(mech, num)
+ char *mech;
+ OM_uint32 num;
+{
+ char *qop;
+
+ if (__gss_num_to_qop(mech, num, &qop) != GSS_S_COMPLETE)
+ return (NULL);
+ return (qop);
+}
+
+bool_t
+__rpc_gss_svc_to_num(svc, num)
+ char *svc;
+ rpc_gss_service_t *num;
+{
+ if (strcasecmp(svc, SVC_INTEGRITY) == 0)
+ *num = rpc_gss_svc_integrity;
+ else if (strcasecmp(svc, SVC_PRIVACY) == 0)
+ *num = rpc_gss_svc_privacy;
+ else if (strcasecmp(svc, SVC_NONE) == 0)
+ *num = rpc_gss_svc_none;
+ else if (strcasecmp(svc, SVC_DEFAULT) == 0)
+ *num = rpc_gss_svc_default;
+ else
+ return (FALSE);
+ return (TRUE);
+}
+
+char *
+__rpc_gss_num_to_svc(num)
+ rpc_gss_service_t num;
+{
+ switch (num) {
+ case rpc_gss_svc_integrity:
+ return (strdup(SVC_INTEGRITY));
+ case rpc_gss_svc_privacy:
+ return (strdup(SVC_PRIVACY));
+ case rpc_gss_svc_none:
+ return (strdup(SVC_NONE));
+ case rpc_gss_svc_default:
+ return (strdup(SVC_DEFAULT));
+ default:
+ return (NULL);
+ }
+}
+
+/*
+ * Given the user name, node, and security domain, get the mechanism
+ * specific principal name (for the user name) in exported form.
+ */
+bool_t
+__rpc_gss_get_principal_name(principal, mech, user, node, secdomain)
+ rpc_gss_principal_t *principal;
+ char *mech;
+ char *user;
+ char *node;
+ char *secdomain;
+{
+ gss_name_t gss_name, gss_canon_name;
+ gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
+ char user_name[256], *s;
+ gss_OID mech_oid;
+ int nlen = 0, slen = 0, plen;
+ OM_uint32 major, minor;
+
+ *principal = NULL;
+ if (user == NULL || strlen(user) == 0)
+ return (FALSE);
+
+ if (!__rpc_gss_mech_to_oid(mech, (rpc_gss_OID *) &mech_oid)) {
+ syslog(LOG_ERR, "rpc_gss_get_principal_name: can't get"
+ "mech oid");
+ return (FALSE);
+ }
+
+ if (secdomain != NULL)
+ slen = strlen(secdomain);
+
+ if (node != NULL)
+ nlen = strlen(node);
+
+ strcpy(user_name, user);
+ if (nlen > 0) {
+ strcat(user_name, "/");
+ strcat(user_name, node);
+ }
+
+ if (slen > 0) {
+ strcat(user_name, "@");
+ strcat(user_name, secdomain);
+ }
+
+ name_buf.value = user_name;
+ name_buf.length = strlen(user_name);
+
+ /*
+ * Convert a text string to a GSSAPI Internal name.
+ */
+ if ((major = gss_import_name(&minor, &name_buf,
+ (gss_OID) GSS_C_NT_USER_NAME, &gss_name)) != GSS_S_COMPLETE) {
+ syslog(LOG_ERR, "rpc_gss_get_principal_name: import name"
+ "failed 0x%x", major);
+ return (FALSE);
+ }
+
+ /*
+ * Convert the GSSAPI Internal name to a MN - Mechanism Name
+ */
+ if ((major = gss_canonicalize_name(&minor, gss_name, mech_oid,
+ &gss_canon_name)) != GSS_S_COMPLETE) {
+ syslog(LOG_ERR, "rpc_gss_get_principal_name: canonicalize name"
+ "failed 0x%x", major);
+ gss_release_name(&minor, &gss_name);
+ return (FALSE);
+ }
+ gss_release_name(&minor, &gss_name);
+
+ /*
+ * Convert the MN Internal name to an exported flat name, so
+ * it is suitable for binary comparison.
+ */
+ if ((major = gss_export_name(&minor, gss_canon_name, &name_buf)) !=
+ GSS_S_COMPLETE) {
+ syslog(LOG_ERR, "rpc_gss_get_principal_name: export name"
+ "failed %x", major);
+ gss_release_name(&minor, &gss_canon_name);
+ return (FALSE);
+ }
+ gss_release_name(&minor, &gss_canon_name);
+
+ /*
+ * Put the exported name into rpc_gss_principal_t structure.
+ */
+ plen = RNDUP(name_buf.length) + sizeof (int);
+ (*principal) = (rpc_gss_principal_t)malloc(plen);
+ if ((*principal) == NULL) {
+ gss_release_buffer(&minor, &name_buf);
+ return (FALSE);
+ }
+ bzero((caddr_t)(*principal), plen);
+ (*principal)->len = RNDUP(name_buf.length);
+ s = (*principal)->name;
+ memcpy(s, name_buf.value, name_buf.length);
+ gss_release_buffer(&minor, &name_buf);
+
+ return (TRUE);
+}
+
+/*
+ * Return supported mechanisms.
+ */
+char **
+__rpc_gss_get_mechanisms()
+{
+ static char *mech_list[MAX_MECH_OID_PAIRS+1];
+
+ *mech_list = NULL;
+ __gss_get_mechanisms(mech_list, MAX_MECH_OID_PAIRS+1);
+ return (mech_list);
+}
+
+/*
+ * For a given mechanism, return information about it.
+ */
+/*
+ * static char *krb5_qop_list[] = {Q_DEFAULT, NULL};
+ */
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+/* Don't know how to get the service type for a given mech. */
+/* "service" should NOT be there! */
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*!!!!!!!!!!! */
+
+char **
+__rpc_gss_get_mech_info(mech, service)
+ char *mech;
+ rpc_gss_service_t *service;
+{
+ char **l;
+
+ l = calloc(MAX_QOPS_PER_MECH + 1, sizeof (char *));
+ if (l == NULL)
+ return (NULL);
+
+ if (__gss_get_mech_info(mech, l) != GSS_S_COMPLETE) {
+ free(l);
+ return (NULL);
+ }
+ /* !!!!!!!!!!!!!!!! */
+ *service = rpc_gss_svc_privacy; /* What service type? */
+ /* !!!!!!!!!!!!!!!! */
+ return (l);
+}
+
+/*
+ * Returns highest and lowest versions of RPCSEC_GSS flavor supported.
+ */
+bool_t
+__rpc_gss_get_versions(vers_hi, vers_lo)
+ u_int *vers_hi;
+ u_int *vers_lo;
+{
+ *vers_hi = RPCSEC_GSS_VERSION;
+ *vers_lo = RPCSEC_GSS_VERSION;
+ return (TRUE);
+}
+
+/*
+ * Check if a mechanism is installed.
+ */
+bool_t
+__rpc_gss_is_installed(mech)
+ char *mech;
+{
+ char **l;
+
+ if (mech == NULL)
+ return (FALSE);
+
+ if ((l = __rpc_gss_get_mechanisms()) == NULL)
+ return (FALSE);
+
+ while (*l != NULL) {
+ if (strcmp(*l, mech) == 0)
+ return (TRUE);
+ l++;
+ }
+ return (FALSE);
+}
diff --git a/usr/src/lib/rpcsec_gss/sparc/Makefile b/usr/src/lib/rpcsec_gss/sparc/Makefile
new file mode 100644
index 0000000000..3d1000e3e6
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/sparc/Makefile
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1997-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/rpcsec_gss/sparc/Makefile
+
+include ../Makefile.com
+
+LIBS = $(DYNLIB)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/rpcsec_gss/sparcv9/Makefile b/usr/src/lib/rpcsec_gss/sparcv9/Makefile
new file mode 100644
index 0000000000..2a9866a38c
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/sparcv9/Makefile
@@ -0,0 +1,40 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1997, by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/rpcsec_gss/sparcv9/Makefile
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+LIBS= $(DYNLIB)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
+
diff --git a/usr/src/lib/rpcsec_gss/svc_rpcsec_gss.c b/usr/src/lib/rpcsec_gss/svc_rpcsec_gss.c
new file mode 100644
index 0000000000..38fd1efd81
--- /dev/null
+++ b/usr/src/lib/rpcsec_gss/svc_rpcsec_gss.c
@@ -0,0 +1,1698 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1995-2003 Sun Microsystems, Inc.
+ * All rights reserved. Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id: svc_auth_gssapi.c,v 1.19 1994/10/27 12:38:51 jik Exp $
+ */
+
+/*
+ * Server side handling of RPCSEC_GSS flavor.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_defs.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <syslog.h>
+
+/*
+ * Sequence window definitions.
+ */
+#define SEQ_ARR_SIZE 4
+#define SEQ_WIN (SEQ_ARR_SIZE*32)
+#define SEQ_HI_BIT 0x80000000
+#define SEQ_LO_BIT 1
+#define DIV_BY_32 5
+#define SEQ_MASK 0x1f
+#define SEQ_MAX 0x80000000
+
+
+/* cache retransmit data */
+typedef struct _retrans_entry {
+ uint32_t xid;
+ rpc_gss_init_res result;
+ struct _retrans_entry *next, *prev;
+} retrans_entry;
+
+/*
+ * Server side RPCSEC_GSS context information.
+ */
+typedef struct _svc_rpc_gss_data {
+ struct _svc_rpc_gss_data *next, *prev;
+ struct _svc_rpc_gss_data *lru_next, *lru_prev;
+ bool_t established;
+ gss_ctx_id_t context;
+ gss_name_t client_name;
+ gss_cred_id_t server_creds;
+ uint_t expiration;
+ uint_t seq_num;
+ uint_t seq_bits[SEQ_ARR_SIZE];
+ uint_t key;
+ OM_uint32 qop;
+ bool_t done_docallback;
+ bool_t locked;
+ rpc_gss_rawcred_t raw_cred;
+ rpc_gss_ucred_t u_cred;
+ bool_t u_cred_set;
+ void *cookie;
+ gss_cred_id_t deleg;
+ mutex_t clm;
+ int ref_cnt;
+ bool_t stale;
+ time_t time_secs_set;
+ retrans_entry *retrans_data;
+} svc_rpc_gss_data;
+
+/*
+ * Data structures used for LRU based context management.
+ */
+#define HASHMOD 256
+#define HASHMASK 255
+
+static svc_rpc_gss_data *clients[HASHMOD];
+static svc_rpc_gss_data *lru_first, *lru_last;
+static int num_gss_contexts = 0;
+static int max_gss_contexts = 128;
+static int sweep_interval = 10;
+static int last_swept = 0;
+static uint_t max_lifetime = GSS_C_INDEFINITE;
+static int init_lifetime = 300;
+static uint_t gid_timeout = 43200; /* 43200 secs = 12 hours */
+
+/*
+ * lock used with context/lru variables
+ */
+static mutex_t ctx_mutex = DEFAULTMUTEX;
+
+/*
+ * server credential management data and structures
+ */
+typedef struct svc_creds_list_s {
+ struct svc_creds_list_s *next;
+ gss_cred_id_t cred;
+ gss_name_t name;
+ rpcprog_t program;
+ rpcvers_t version;
+ gss_OID_set oid_set;
+ OM_uint32 req_time;
+ char *server_name;
+ mutex_t refresh_mutex;
+} svc_creds_list_t;
+
+
+static svc_creds_list_t *svc_creds_list;
+static int svc_creds_count = 0;
+
+/*
+ * lock used with server credential variables list
+ *
+ * server cred list locking guidelines:
+ * - Writer's lock holder has exclusive access to the list
+ * - Reader's lock holder(s) must also lock (refresh_mutex) each node
+ * before accessing that node's elements (ie. cred)
+ */
+static rwlock_t cred_lock = DEFAULTRWLOCK;
+
+/*
+ * server callback list
+ */
+typedef struct cblist_s {
+ struct cblist_s *next;
+ rpc_gss_callback_t cb;
+} cblist_t;
+
+cblist_t *cblist = NULL;
+
+/*
+ * lock used with callback variables
+ */
+static mutex_t cb_mutex = DEFAULTMUTEX;
+
+/*
+ * forward declarations
+ */
+static bool_t svc_rpc_gss_wrap();
+static bool_t svc_rpc_gss_unwrap();
+static svc_rpc_gss_data *create_client();
+static svc_rpc_gss_data *get_client();
+static svc_rpc_gss_data *find_client();
+static void destroy_client();
+static void sweep_clients();
+static void drop_lru_client();
+static void insert_client();
+static bool_t check_verf();
+static bool_t rpc_gss_refresh_svc_cred();
+static bool_t set_response_verf();
+static void retrans_add(svc_rpc_gss_data *, uint32_t,
+ rpc_gss_init_res *);
+static void retrans_del(struct _svc_rpc_gss_data *);
+
+
+/*
+ * server side wrap/unwrap routines
+ */
+struct svc_auth_ops svc_rpc_gss_ops = {
+ svc_rpc_gss_wrap,
+ svc_rpc_gss_unwrap,
+};
+
+/*
+ * Fetch server side authentication structure.
+ */
+extern SVCAUTH *__svc_get_svcauth();
+
+/*
+ * Cleanup routine for destroying context, called after service
+ * procedure is executed, for MT safeness.
+ */
+extern void *__svc_set_proc_cleanup_cb();
+static void (*old_cleanup_cb)() = NULL;
+static bool_t cleanup_cb_set = FALSE;
+
+static void
+ctx_cleanup(xprt)
+ SVCXPRT *xprt;
+{
+ svc_rpc_gss_data *cl;
+ SVCAUTH *svcauth;
+
+ if (old_cleanup_cb != NULL)
+ (*old_cleanup_cb)(xprt);
+
+ /*
+ * First check if current context needs to be cleaned up.
+ */
+ svcauth = __svc_get_svcauth(xprt);
+ /*LINTED*/
+ if ((cl = (svc_rpc_gss_data *)svcauth->svc_ah_private) != NULL) {
+ mutex_lock(&cl->clm);
+ if (--cl->ref_cnt == 0 && cl->stale) {
+ mutex_unlock(&cl->clm);
+ mutex_lock(&ctx_mutex);
+ destroy_client(cl);
+ mutex_unlock(&ctx_mutex);
+ } else
+ mutex_unlock(&cl->clm);
+ }
+
+ /*
+ * Check for other expired contexts.
+ */
+ if ((time(0) - last_swept) > sweep_interval) {
+ mutex_lock(&ctx_mutex);
+ /*
+ * Check again, in case some other thread got in.
+ */
+ if ((time(0) - last_swept) > sweep_interval)
+ sweep_clients();
+ mutex_unlock(&ctx_mutex);
+ }
+}
+
+/*
+ * Set server parameters.
+ */
+void
+__rpc_gss_set_server_parms(init_cred_lifetime, max_cred_lifetime, cache_size)
+ int init_cred_lifetime;
+ int max_cred_lifetime;
+ int cache_size;
+{
+ /*
+ * Ignore parameters unless greater than zero.
+ */
+ mutex_lock(&ctx_mutex);
+ if (cache_size > 0)
+ max_gss_contexts = cache_size;
+ if (max_cred_lifetime > 0)
+ max_lifetime = (uint_t)max_cred_lifetime;
+ if (init_cred_lifetime > 0)
+ init_lifetime = init_cred_lifetime;
+ mutex_unlock(&ctx_mutex);
+}
+
+/*
+ * Shift the array arr of length arrlen right by nbits bits.
+ */
+static void
+shift_bits(arr, arrlen, nbits)
+ uint_t *arr;
+ int arrlen;
+ int nbits;
+{
+ int i, j;
+ uint_t lo, hi;
+
+ /*
+ * If the number of bits to be shifted exceeds SEQ_WIN, just
+ * zero out the array.
+ */
+ if (nbits < SEQ_WIN) {
+ for (i = 0; i < nbits; i++) {
+ hi = 0;
+ for (j = 0; j < arrlen; j++) {
+ lo = arr[j] & SEQ_LO_BIT;
+ arr[j] >>= 1;
+ if (hi)
+ arr[j] |= SEQ_HI_BIT;
+ hi = lo;
+ }
+ }
+ } else {
+ for (j = 0; j < arrlen; j++)
+ arr[j] = 0;
+ }
+}
+
+/*
+ * Check that the received sequence number seq_num is valid.
+ */
+static bool_t
+check_seq(cl, seq_num, kill_context)
+ svc_rpc_gss_data *cl;
+ uint_t seq_num;
+ bool_t *kill_context;
+{
+ int i, j;
+ uint_t bit;
+
+ /*
+ * If it exceeds the maximum, kill context.
+ */
+ if (seq_num >= SEQ_MAX) {
+ *kill_context = TRUE;
+ return (FALSE);
+ }
+
+ /*
+ * If greater than the last seen sequence number, just shift
+ * the sequence window so that it starts at the new sequence
+ * number and extends downwards by SEQ_WIN.
+ */
+ if (seq_num > cl->seq_num) {
+ shift_bits(cl->seq_bits, SEQ_ARR_SIZE, seq_num - cl->seq_num);
+ cl->seq_bits[0] |= SEQ_HI_BIT;
+ cl->seq_num = seq_num;
+ return (TRUE);
+ }
+
+ /*
+ * If it is outside the sequence window, return failure.
+ */
+ i = cl->seq_num - seq_num;
+ if (i >= SEQ_WIN)
+ return (FALSE);
+
+ /*
+ * If within sequence window, set the bit corresponding to it
+ * if not already seen; if already seen, return failure.
+ */
+ j = SEQ_MASK - (i & SEQ_MASK);
+ bit = j > 0 ? (1 << j) : 1;
+ i >>= DIV_BY_32;
+ if (cl->seq_bits[i] & bit)
+ return (FALSE);
+ cl->seq_bits[i] |= bit;
+ return (TRUE);
+}
+
+/*
+ * Convert a name in gss exported type to rpc_gss_principal_t type.
+ */
+static bool_t
+__rpc_gss_make_principal(principal, name)
+ rpc_gss_principal_t *principal;
+ gss_buffer_desc *name;
+{
+ int plen;
+ char *s;
+
+ plen = RNDUP(name->length) + sizeof (int);
+ (*principal) = (rpc_gss_principal_t)malloc(plen);
+ if ((*principal) == NULL)
+ return (FALSE);
+ bzero((caddr_t)(*principal), plen);
+ (*principal)->len = RNDUP(name->length);
+ s = (*principal)->name;
+ memcpy(s, name->value, name->length);
+ return (TRUE);
+}
+
+/*
+ * Convert a name in internal form to the exported type.
+ */
+static bool_t
+set_client_principal(g_name, r_name)
+ gss_name_t g_name;
+ rpc_gss_principal_t *r_name;
+{
+ gss_buffer_desc name;
+ OM_uint32 major, minor;
+ bool_t ret = FALSE;
+
+ major = gss_export_name(&minor, g_name, &name);
+ if (major != GSS_S_COMPLETE)
+ return (FALSE);
+ ret = __rpc_gss_make_principal(r_name, &name);
+ (void) gss_release_buffer(&minor, &name);
+ return (ret);
+}
+
+/*
+ * Set server callback.
+ */
+bool_t
+__rpc_gss_set_callback(cb)
+ rpc_gss_callback_t *cb;
+{
+ cblist_t *cbl;
+
+ if (cb->callback == NULL)
+ return (FALSE);
+ if ((cbl = (cblist_t *)malloc(sizeof (*cbl))) == NULL)
+ return (FALSE);
+ cbl->cb = *cb;
+ mutex_lock(&cb_mutex);
+ cbl->next = cblist;
+ cblist = cbl;
+ mutex_unlock(&cb_mutex);
+ return (TRUE);
+}
+
+/*
+ * Locate callback (if specified) and call server. Release any
+ * delegated credentials unless passed to server and the server
+ * accepts the context. If a callback is not specified, accept
+ * the incoming context.
+ */
+static bool_t
+do_callback(req, client_data)
+ struct svc_req *req;
+ svc_rpc_gss_data *client_data;
+{
+ cblist_t *cbl;
+ bool_t ret = TRUE, found = FALSE;
+ rpc_gss_lock_t lock;
+ OM_uint32 minor;
+
+ mutex_lock(&cb_mutex);
+ for (cbl = cblist; cbl != NULL; cbl = cbl->next) {
+ if (req->rq_prog != cbl->cb.program ||
+ req->rq_vers != cbl->cb.version)
+ continue;
+ found = TRUE;
+ lock.locked = FALSE;
+ lock.raw_cred = &client_data->raw_cred;
+ ret = (*cbl->cb.callback)(req, client_data->deleg,
+ client_data->context, &lock, &client_data->cookie);
+ if (ret) {
+ client_data->locked = lock.locked;
+ client_data->deleg = GSS_C_NO_CREDENTIAL;
+ }
+ break;
+ }
+ if (!found) {
+ if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
+ (void) gss_release_cred(&minor, &client_data->deleg);
+ client_data->deleg = GSS_C_NO_CREDENTIAL;
+ }
+ }
+ mutex_unlock(&cb_mutex);
+ return (ret);
+}
+
+/*
+ * Return caller credentials.
+ */
+bool_t
+__rpc_gss_getcred(req, rcred, ucred, cookie)
+ struct svc_req *req;
+ rpc_gss_rawcred_t **rcred;
+ rpc_gss_ucred_t **ucred;
+ void **cookie;
+{
+ SVCAUTH *svcauth;
+ svc_rpc_gss_data *client_data;
+ svc_rpc_gss_parms_t *gss_parms;
+ gss_OID oid;
+ OM_uint32 status;
+ int len = 0;
+ struct timeval now;
+
+ svcauth = __svc_get_svcauth(req->rq_xprt);
+ /*LINTED*/
+ client_data = (svc_rpc_gss_data *)svcauth->svc_ah_private;
+ gss_parms = &svcauth->svc_gss_parms;
+
+ mutex_lock(&client_data->clm);
+
+ if (rcred != NULL) {
+ svcauth->raw_cred = client_data->raw_cred;
+ svcauth->raw_cred.service = gss_parms->service;
+ svcauth->raw_cred.qop = __rpc_gss_num_to_qop(
+ svcauth->raw_cred.mechanism, gss_parms->qop_rcvd);
+ *rcred = &svcauth->raw_cred;
+ }
+ if (ucred != NULL) {
+ if (!client_data->u_cred_set) {
+ /*
+ * Double check making sure ucred is not set
+ * after acquiring the lock.
+ */
+ if (!client_data->u_cred_set) {
+ if (!__rpc_gss_mech_to_oid(
+ (*rcred)->mechanism, &oid)) {
+ fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "mech_to_oid failed in getcred.\n"));
+ *ucred = NULL;
+ } else {
+ status = gsscred_name_to_unix_cred(
+ client_data->client_name, oid,
+ &client_data->u_cred.uid,
+ &client_data->u_cred.gid,
+ &client_data->u_cred.gidlist,
+ &len);
+ if (status == GSS_S_COMPLETE) {
+ client_data->u_cred_set = TRUE;
+ client_data->u_cred.gidlen =
+ (short)len;
+ gettimeofday(&now,
+ (struct timezone *)NULL);
+ client_data->time_secs_set =
+ now.tv_sec;
+ *ucred = &client_data->u_cred;
+ } else
+ *ucred = NULL;
+ }
+ }
+ } else {
+ /*
+ * gid's already set;
+ * check if they have expired.
+ */
+ gettimeofday(&now, (struct timezone *)NULL);
+ if ((now.tv_sec - client_data->time_secs_set)
+ > gid_timeout) {
+ /* Refresh gid's */
+ status = gss_get_group_info(
+ client_data->u_cred.uid,
+ &client_data->u_cred.gid,
+ &client_data->u_cred.gidlist,
+ &len);
+ if (status == GSS_S_COMPLETE) {
+ client_data->u_cred.gidlen =
+ (short)len;
+ gettimeofday(&now,
+ (struct timezone *)NULL);
+ client_data->time_secs_set = now.tv_sec;
+ *ucred = &client_data->u_cred;
+ } else {
+ client_data->u_cred_set = FALSE;
+ *ucred = NULL;
+ }
+ }
+ else
+ *ucred = &client_data->u_cred;
+ }
+ }
+ if (cookie != NULL)
+ *cookie = client_data->cookie;
+
+ mutex_unlock(&client_data->clm);
+
+ return (TRUE);
+}
+
+/*
+ * Server side authentication for RPCSEC_GSS.
+ */
+
+enum auth_stat
+__svcrpcsec_gss(rqst, msg, no_dispatch)
+ struct svc_req *rqst;
+ struct rpc_msg *msg;
+ bool_t *no_dispatch;
+{
+ XDR xdrs;
+ rpc_gss_creds creds;
+ rpc_gss_init_arg call_arg;
+ rpc_gss_init_res call_res, *retrans_result;
+ gss_buffer_desc output_token;
+ OM_uint32 gssstat, minor_stat, time_rec, ret_flags;
+ struct opaque_auth *cred;
+ svc_rpc_gss_data *client_data;
+ int ret;
+ svc_creds_list_t *sc;
+ SVCAUTH *svcauth;
+ svc_rpc_gss_parms_t *gss_parms;
+ gss_OID mech_type = GSS_C_NULL_OID;
+
+ /*
+ * Initialize response verifier to NULL verifier. If
+ * necessary, this will be changed later.
+ */
+ rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NONE;
+ rqst->rq_xprt->xp_verf.oa_base = NULL;
+ rqst->rq_xprt->xp_verf.oa_length = 0;
+ /*
+ * Need to null out results to start with.
+ */
+ memset((char *)&call_res, 0, sizeof (call_res));
+
+ /*
+ * Pull out and check credential and verifier.
+ */
+ cred = &msg->rm_call.cb_cred;
+ if (cred->oa_length == 0) {
+ ret = AUTH_BADCRED;
+ goto error;
+ }
+
+ xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE);
+
+ memset((char *)&creds, 0, sizeof (creds));
+ if (!__xdr_rpc_gss_creds(&xdrs, &creds)) {
+ XDR_DESTROY(&xdrs);
+ ret = AUTH_BADCRED;
+ goto error;
+ }
+ XDR_DESTROY(&xdrs);
+
+ /*
+ * If this is a control message and proc is GSSAPI_INIT, then
+ * create a client handle for this client. Otherwise, look up
+ * the existing handle.
+ */
+ if (creds.gss_proc == RPCSEC_GSS_INIT) {
+ if (creds.ctx_handle.length != 0) {
+ ret = AUTH_BADCRED;
+ goto error;
+ }
+ if ((client_data = create_client()) == NULL) {
+ ret = AUTH_FAILED;
+ goto error;
+ }
+ } else {
+ /*
+ * Only verify values for service parameter when proc
+ * not RPCSEC_GSS_INIT or RPCSEC_GSS_CONTINUE_INIT.
+ * RFC2203 says contents for sequence and service args
+ * are undefined for creation procs.
+ *
+ * Note: only need to check for *CONTINUE_INIT here because
+ * if() clause already checked for RPCSEC_GSS_INIT
+ */
+ if (creds.gss_proc != RPCSEC_GSS_CONTINUE_INIT) {
+ switch (creds.service) {
+ case rpc_gss_svc_none:
+ case rpc_gss_svc_integrity:
+ case rpc_gss_svc_privacy:
+ break;
+ default:
+ ret = AUTH_BADCRED;
+ goto error;
+ }
+ }
+ if (creds.ctx_handle.length == 0) {
+ ret = AUTH_BADCRED;
+ goto error;
+ }
+ if ((client_data = get_client(&creds.ctx_handle)) == NULL) {
+ ret = RPCSEC_GSS_NOCRED;
+ goto error;
+ }
+ }
+
+ /*
+ * lock the client data until it's safe; if it's already stale,
+ * no more processing is possible
+ */
+ mutex_lock(&client_data->clm);
+ if (client_data->stale) {
+ ret = RPCSEC_GSS_NOCRED;
+ goto error2;
+ }
+
+ /*
+ * Any response we send will use ctx_handle, so set it now;
+ * also set seq_window since this won't change.
+ */
+ call_res.ctx_handle.length = sizeof (client_data->key);
+ call_res.ctx_handle.value = (char *)&client_data->key;
+ call_res.seq_window = SEQ_WIN;
+
+ /*
+ * Set the appropriate wrap/unwrap routine for RPCSEC_GSS.
+ */
+ svcauth = __svc_get_svcauth(rqst->rq_xprt);
+ svcauth->svc_ah_ops = svc_rpc_gss_ops;
+ svcauth->svc_ah_private = (caddr_t)client_data;
+
+ /*
+ * Keep copy of parameters we'll need for response, for the
+ * sake of reentrancy (we don't want to look in the context
+ * data because when we are sending a response, another
+ * request may have come in.
+ */
+ gss_parms = &svcauth->svc_gss_parms;
+ gss_parms->established = client_data->established;
+ gss_parms->service = creds.service;
+ gss_parms->qop_rcvd = (uint_t)client_data->qop;
+ gss_parms->context = (void *)client_data->context;
+ gss_parms->seq_num = creds.seq_num;
+
+ if (!client_data->established) {
+ if (creds.gss_proc == RPCSEC_GSS_DATA) {
+ ret = RPCSEC_GSS_FAILED;
+ client_data->stale = TRUE;
+ goto error2;
+ }
+
+ /*
+ * If the context is not established, then only GSSAPI_INIT
+ * and _CONTINUE requests are valid.
+ */
+ if (creds.gss_proc != RPCSEC_GSS_INIT && creds.gss_proc !=
+ RPCSEC_GSS_CONTINUE_INIT) {
+ ret = RPCSEC_GSS_FAILED;
+ client_data->stale = TRUE;
+ goto error2;
+ }
+
+ /*
+ * call is for us, deserialize arguments
+ */
+ memset(&call_arg, 0, sizeof (call_arg));
+ if (!svc_getargs(rqst->rq_xprt, __xdr_rpc_gss_init_arg,
+ (caddr_t)&call_arg)) {
+ ret = RPCSEC_GSS_FAILED;
+ client_data->stale = TRUE;
+ goto error2;
+ }
+
+ gssstat = GSS_S_FAILURE;
+ minor_stat = 0;
+ rw_rdlock(&cred_lock);
+ /*
+ * set next sc to point to the server cred
+ * if the client_data contains server_creds
+ */
+ for (sc = svc_creds_list; sc != NULL; sc = sc->next) {
+ if (rqst->rq_prog != sc->program ||
+ rqst->rq_vers != sc->version)
+ continue;
+
+ mutex_lock(&sc->refresh_mutex);
+ gssstat = gss_accept_sec_context(&minor_stat,
+ &client_data->context,
+ sc->cred,
+ &call_arg,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client_data->client_name,
+ &mech_type,
+ &output_token,
+ &ret_flags,
+ &time_rec,
+ NULL);
+
+ if (gssstat == GSS_S_CREDENTIALS_EXPIRED) {
+ if (rpc_gss_refresh_svc_cred(sc)) {
+ gssstat = gss_accept_sec_context(
+ &minor_stat,
+ &client_data->context,
+ sc->cred,
+ &call_arg,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client_data->client_name,
+ &mech_type,
+ &output_token,
+ &ret_flags,
+ &time_rec,
+ NULL);
+ mutex_unlock(&sc->refresh_mutex);
+
+ } else {
+ mutex_unlock(&sc->refresh_mutex);
+ gssstat = GSS_S_NO_CRED;
+ break;
+ }
+
+ } else
+ mutex_unlock(&sc->refresh_mutex);
+
+ if (gssstat == GSS_S_COMPLETE) {
+ /*
+ * Server_creds was right - set it. Also
+ * set the raw and unix credentials at this
+ * point. This saves a lot of computation
+ * later when credentials are retrieved.
+ */
+ /*
+ * XXX server_creds will prob be stale
+ * after rpc_gss_refresh_svc_cred(), but
+ * it appears not to ever be referenced
+ * anyways.
+ */
+ mutex_lock(&sc->refresh_mutex);
+ client_data->server_creds = sc->cred;
+ client_data->raw_cred.version = creds.version;
+ client_data->raw_cred.service = creds.service;
+ client_data->raw_cred.svc_principal =
+ sc->server_name;
+ mutex_unlock(&sc->refresh_mutex);
+
+ if ((client_data->raw_cred.mechanism
+ = __rpc_gss_oid_to_mech(mech_type))
+ == NULL) {
+ gssstat = GSS_S_FAILURE;
+ (void) gss_release_buffer(&minor_stat,
+ &output_token);
+ } else if (!set_client_principal(client_data->
+ client_name, &client_data->
+ raw_cred.client_principal)) {
+ gssstat = GSS_S_FAILURE;
+ (void) gss_release_buffer(&minor_stat,
+ &output_token);
+ }
+ break;
+ }
+
+ if (gssstat == GSS_S_CONTINUE_NEEDED) {
+ /*
+ * XXX server_creds will prob be stale
+ * after rpc_gss_refresh_svc_cred(), but
+ * it appears not to ever be referenced
+ * anyways.
+ */
+ mutex_lock(&sc->refresh_mutex);
+ client_data->server_creds = sc->cred;
+ mutex_unlock(&sc->refresh_mutex);
+ break;
+ }
+
+ }
+ rw_unlock(&cred_lock);
+
+ call_res.gss_major = gssstat;
+ call_res.gss_minor = minor_stat;
+
+ xdr_free(__xdr_rpc_gss_init_arg, (caddr_t)&call_arg);
+
+ if (gssstat != GSS_S_COMPLETE &&
+ gssstat != GSS_S_CONTINUE_NEEDED) {
+ /*
+ * We have a failure - send response and delete
+ * the context. Don't dispatch. Set ctx_handle
+ * to NULL and seq_window to 0.
+ */
+ call_res.ctx_handle.length = 0;
+ call_res.ctx_handle.value = NULL;
+ call_res.seq_window = 0;
+
+ svc_sendreply(rqst->rq_xprt, __xdr_rpc_gss_init_res,
+ (caddr_t)&call_res);
+ *no_dispatch = TRUE;
+ ret = AUTH_OK;
+ client_data->stale = TRUE;
+ goto error2;
+ }
+
+ /*
+ * This step succeeded. Send a response, along with
+ * a token if there's one. Don't dispatch.
+ */
+ if (output_token.length != 0) {
+ GSS_COPY_BUFFER(call_res.token, output_token);
+ }
+
+ /*
+ * set response verifier: checksum of SEQ_WIN
+ */
+ if (gssstat == GSS_S_COMPLETE) {
+ if (!set_response_verf(rqst, msg, client_data,
+ (uint_t)SEQ_WIN)) {
+ ret = RPCSEC_GSS_FAILED;
+ client_data->stale = TRUE;
+ (void) gss_release_buffer(&minor_stat,
+ &output_token);
+ goto error2;
+ }
+ }
+
+ svc_sendreply(rqst->rq_xprt, __xdr_rpc_gss_init_res,
+ (caddr_t)&call_res);
+ /*
+ * Cache last response in case it is lost and the client
+ * retries on an established context.
+ */
+ (void) retrans_add(client_data, msg->rm_xid, &call_res);
+ *no_dispatch = TRUE;
+ (void) gss_release_buffer(&minor_stat, &output_token);
+
+ /*
+ * If appropriate, set established to TRUE *after* sending
+ * response (otherwise, the client will receive the final
+ * token encrypted)
+ */
+ if (gssstat == GSS_S_COMPLETE) {
+ /*
+ * Context is established. Set expiry time for
+ * context (the minimum of time_rec and max_lifetime).
+ */
+ client_data->seq_num = 1;
+ if (time_rec == GSS_C_INDEFINITE) {
+ if (max_lifetime != GSS_C_INDEFINITE)
+ client_data->expiration =
+ max_lifetime + time(0);
+ else
+ client_data->expiration =
+ GSS_C_INDEFINITE;
+ } else if (max_lifetime == GSS_C_INDEFINITE ||
+ max_lifetime > time_rec)
+ client_data->expiration = time_rec + time(0);
+ else
+ client_data->expiration = max_lifetime +
+ time(0);
+ client_data->established = TRUE;
+ }
+
+ } else {
+ if ((creds.gss_proc != RPCSEC_GSS_DATA) &&
+ (creds.gss_proc != RPCSEC_GSS_DESTROY)) {
+
+ switch (creds.gss_proc) {
+
+ case RPCSEC_GSS_CONTINUE_INIT:
+ /*
+ * This is an established context. Continue to
+ * satisfy retried continue init requests out of
+ * the retransmit cache. Throw away any that don't
+ * have a matching xid or the cach is empty.
+ * Delete the retransmit cache once the client sends
+ * a data request.
+ */
+ if (client_data->retrans_data &&
+ (client_data->retrans_data->xid == msg->rm_xid)) {
+
+ retrans_result = &client_data->retrans_data->result;
+ if (set_response_verf(rqst, msg, client_data,
+ (uint_t)retrans_result->seq_window)) {
+
+ gss_parms->established = FALSE;
+ svc_sendreply(rqst->rq_xprt,
+ __xdr_rpc_gss_init_res,
+ (caddr_t)retrans_result);
+ *no_dispatch = TRUE;
+ goto success;
+ }
+ }
+ /* fall thru to default */
+
+ default:
+ syslog(LOG_ERR, "_svcrpcsec_gss: non-data request "
+ "on an established context");
+ ret = AUTH_FAILED;
+ goto error2;
+ }
+ }
+
+ /*
+ * Once the context is established and there is no more
+ * retransmission of last continue init request, it is safe
+ * to delete the retransmit cache entry.
+ */
+ if (client_data->retrans_data)
+ retrans_del(client_data);
+
+ /*
+ * Context is already established. Check verifier, and
+ * note parameters we will need for response in gss_parms.
+ */
+ if (!check_verf(msg, client_data->context,
+ &gss_parms->qop_rcvd)) {
+ ret = RPCSEC_GSS_NOCRED;
+ goto error2;
+ }
+ /*
+ * Check and invoke callback if necessary.
+ */
+ if (!client_data->done_docallback) {
+ client_data->done_docallback = TRUE;
+ client_data->qop = gss_parms->qop_rcvd;
+ client_data->raw_cred.qop = __rpc_gss_num_to_qop(
+ client_data->raw_cred.mechanism,
+ gss_parms->qop_rcvd);
+ client_data->raw_cred.service = creds.service;
+ if (!do_callback(rqst, client_data)) {
+ ret = AUTH_FAILED;
+ client_data->stale = TRUE;
+ goto error2;
+ }
+ }
+
+ /*
+ * If the context was locked, make sure that the client
+ * has not changed QOP.
+ */
+ if (client_data->locked &&
+ gss_parms->qop_rcvd != client_data->qop) {
+ ret = AUTH_BADVERF;
+ goto error2;
+ }
+
+ /*
+ * Validate sequence number.
+ */
+ if (!check_seq(client_data, creds.seq_num,
+ &client_data->stale)) {
+ if (client_data->stale)
+ ret = RPCSEC_GSS_FAILED;
+ else {
+ /*
+ * Operational error, drop packet silently.
+ * The client will recover after timing out,
+ * assuming this is a client error and not
+ * a relpay attack. Don't dispatch.
+ */
+ ret = AUTH_OK;
+ *no_dispatch = TRUE;
+ }
+ goto error2;
+ }
+
+ /*
+ * set response verifier
+ */
+ if (!set_response_verf(rqst, msg, client_data, creds.seq_num)) {
+ ret = RPCSEC_GSS_FAILED;
+ client_data->stale = TRUE;
+ goto error2;
+ }
+
+ /*
+ * If this is a control message RPCSEC_GSS_DESTROY, process
+ * the call; otherwise, return AUTH_OK so it will be
+ * dispatched to the application server.
+ */
+ if (creds.gss_proc == RPCSEC_GSS_DESTROY) {
+ svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
+ *no_dispatch = TRUE;
+ client_data->stale = TRUE;
+
+ } else {
+ /*
+ * This should be an RPCSEC_GSS_DATA request.
+ * If context is locked, make sure that the client
+ * has not changed the security service.
+ */
+ if (client_data->locked &&
+ client_data->raw_cred.service != creds.service) {
+ ret = AUTH_FAILED;
+ goto error2;
+ }
+
+ /*
+ * Set client credentials to raw credential
+ * structure in context. This is okay, since
+ * this will not change during the lifetime of
+ * the context (so it's MT safe).
+ */
+ rqst->rq_clntcred = (char *)&client_data->raw_cred;
+ }
+ }
+
+success:
+ /*
+ * Success.
+ */
+ if (creds.ctx_handle.length != 0)
+ xdr_free(__xdr_rpc_gss_creds, (caddr_t)&creds);
+ mutex_unlock(&client_data->clm);
+ return (AUTH_OK);
+error2:
+ mutex_unlock(&client_data->clm);
+error:
+ /*
+ * Failure.
+ */
+ if (creds.ctx_handle.length != 0)
+ xdr_free(__xdr_rpc_gss_creds, (caddr_t)&creds);
+ return (ret);
+}
+
+/*
+ * Check verifier. The verifier is the checksum of the RPC header
+ * upto and including the credentials field.
+ */
+static bool_t
+check_verf(msg, context, qop_state)
+ struct rpc_msg *msg;
+ gss_ctx_id_t context;
+ int *qop_state;
+{
+ int *buf, *tmp;
+ int hdr[32];
+ struct opaque_auth *oa;
+ int len;
+ gss_buffer_desc msg_buf;
+ gss_buffer_desc tok_buf;
+ OM_uint32 gssstat, minor_stat;
+
+ /*
+ * We have to reconstruct the RPC header from the previously
+ * parsed information, since we haven't kept the header intact.
+ */
+ buf = hdr;
+ IXDR_PUT_U_INT32(buf, msg->rm_xid);
+ IXDR_PUT_ENUM(buf, msg->rm_direction);
+ IXDR_PUT_U_INT32(buf, msg->rm_call.cb_rpcvers);
+ IXDR_PUT_U_INT32(buf, msg->rm_call.cb_prog);
+ IXDR_PUT_U_INT32(buf, msg->rm_call.cb_vers);
+ IXDR_PUT_U_INT32(buf, msg->rm_call.cb_proc);
+ oa = &msg->rm_call.cb_cred;
+ IXDR_PUT_ENUM(buf, oa->oa_flavor);
+ IXDR_PUT_U_INT32(buf, oa->oa_length);
+ if (oa->oa_length) {
+ len = RNDUP(oa->oa_length);
+ tmp = buf;
+ buf += len / sizeof (int);
+ *(buf - 1) = 0;
+ (void) memcpy((caddr_t)tmp, oa->oa_base, oa->oa_length);
+ }
+ len = ((char *)buf) - (char *)hdr;
+ msg_buf.length = len;
+ msg_buf.value = (char *)hdr;
+ oa = &msg->rm_call.cb_verf;
+ tok_buf.length = oa->oa_length;
+ tok_buf.value = oa->oa_base;
+
+ gssstat = gss_verify(&minor_stat, context, &msg_buf, &tok_buf,
+ qop_state);
+ if (gssstat != GSS_S_COMPLETE)
+ return (FALSE);
+ return (TRUE);
+}
+
+/*
+ * Set response verifier. This is the checksum of the given number.
+ * (e.g. sequence number or sequence window)
+ */
+static bool_t
+set_response_verf(rqst, msg, cl, num)
+ struct svc_req *rqst;
+ struct rpc_msg *msg;
+ svc_rpc_gss_data *cl;
+ uint_t num;
+{
+ OM_uint32 minor;
+ gss_buffer_desc in_buf, out_buf;
+ uint_t num_net;
+
+ num_net = (uint_t)htonl(num);
+ in_buf.length = sizeof (num);
+ in_buf.value = (char *)&num_net;
+ if (gss_sign(&minor, cl->context, cl->qop, &in_buf,
+ &out_buf) != GSS_S_COMPLETE)
+ return (FALSE);
+ rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
+ rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
+ rqst->rq_xprt->xp_verf.oa_length = out_buf.length;
+ memcpy(rqst->rq_xprt->xp_verf.oa_base, out_buf.value,
+ out_buf.length);
+ (void) gss_release_buffer(&minor, &out_buf);
+ return (TRUE);
+}
+
+/*
+ * Create client context.
+ */
+static svc_rpc_gss_data *
+create_client()
+{
+ svc_rpc_gss_data *client_data;
+ static uint_t key = 1;
+
+ client_data = (svc_rpc_gss_data *) malloc(sizeof (*client_data));
+ if (client_data == NULL)
+ return (NULL);
+ memset((char *)client_data, 0, sizeof (*client_data));
+
+ /*
+ * set up client data structure
+ */
+ client_data->established = FALSE;
+ client_data->locked = FALSE;
+ client_data->u_cred_set = FALSE;
+ client_data->context = GSS_C_NO_CONTEXT;
+ client_data->expiration = init_lifetime + time(0);
+ client_data->ref_cnt = 1;
+ client_data->qop = GSS_C_QOP_DEFAULT;
+ client_data->done_docallback = FALSE;
+ client_data->stale = FALSE;
+ client_data->time_secs_set = 0;
+ client_data->retrans_data = NULL;
+ mutex_init(&client_data->clm, USYNC_THREAD, NULL);
+ /*
+ * Check totals. If we've hit the limit, we destroy a context
+ * based on LRU method.
+ */
+ mutex_lock(&ctx_mutex);
+ if (num_gss_contexts >= max_gss_contexts) {
+ /*
+ * now try on LRU basis
+ */
+ drop_lru_client();
+ if (num_gss_contexts >= max_gss_contexts) {
+ mutex_unlock(&ctx_mutex);
+ free((char *)client_data);
+ return (NULL);
+ }
+ }
+
+ /*
+ * The client context handle is a 32-bit key (unsigned int).
+ * The key is incremented until there is no duplicate for it.
+ */
+ for (;;) {
+ client_data->key = key++;
+ if (find_client(client_data->key) == NULL) {
+ insert_client(client_data);
+ /*
+ * Set cleanup callback if we haven't.
+ */
+ if (!cleanup_cb_set) {
+ old_cleanup_cb =
+ (void (*)()) __svc_set_proc_cleanup_cb(
+ (void *)ctx_cleanup);
+ cleanup_cb_set = TRUE;
+ }
+ mutex_unlock(&ctx_mutex);
+ return (client_data);
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Insert client context into hash list and LRU list.
+ */
+static void
+insert_client(client_data)
+ svc_rpc_gss_data *client_data;
+{
+ svc_rpc_gss_data *cl;
+ int index = (client_data->key & HASHMASK);
+
+ client_data->prev = NULL;
+ cl = clients[index];
+ if ((client_data->next = cl) != NULL)
+ cl->prev = client_data;
+ clients[index] = client_data;
+
+ client_data->lru_prev = NULL;
+ if ((client_data->lru_next = lru_first) != NULL)
+ lru_first->lru_prev = client_data;
+ else
+ lru_last = client_data;
+ lru_first = client_data;
+
+ num_gss_contexts++;
+}
+
+/*
+ * Fetch a client, given the client context handle. Move it to the
+ * top of the LRU list since this is the most recently used context.
+ */
+static svc_rpc_gss_data *
+get_client(ctx_handle)
+ gss_buffer_t ctx_handle;
+{
+ uint_t key = *(uint_t *)ctx_handle->value;
+ svc_rpc_gss_data *cl;
+
+ mutex_lock(&ctx_mutex);
+ if ((cl = find_client(key)) != NULL) {
+ mutex_lock(&cl->clm);
+ if (cl->stale) {
+ mutex_unlock(&cl->clm);
+ mutex_unlock(&ctx_mutex);
+ return (NULL);
+ }
+ cl->ref_cnt++;
+ mutex_unlock(&cl->clm);
+ if (cl != lru_first) {
+ cl->lru_prev->lru_next = cl->lru_next;
+ if (cl->lru_next != NULL)
+ cl->lru_next->lru_prev = cl->lru_prev;
+ else
+ lru_last = cl->lru_prev;
+ cl->lru_prev = NULL;
+ cl->lru_next = lru_first;
+ lru_first->lru_prev = cl;
+ lru_first = cl;
+ }
+ }
+ mutex_unlock(&ctx_mutex);
+ return (cl);
+}
+
+/*
+ * Given the client context handle, find the context corresponding to it.
+ * Don't change its LRU state since it may not be used.
+ */
+static svc_rpc_gss_data *
+find_client(key)
+ uint_t key;
+{
+ int index = (key & HASHMASK);
+ svc_rpc_gss_data *cl;
+
+ for (cl = clients[index]; cl != NULL; cl = cl->next) {
+ if (cl->key == key)
+ break;
+ }
+ return (cl);
+}
+
+/*
+ * Destroy a client context.
+ */
+static void
+destroy_client(client_data)
+ svc_rpc_gss_data *client_data;
+{
+ OM_uint32 minor;
+ int index = (client_data->key & HASHMASK);
+
+ /*
+ * remove from hash list
+ */
+ if (client_data->prev == NULL)
+ clients[index] = client_data->next;
+ else
+ client_data->prev->next = client_data->next;
+ if (client_data->next != NULL)
+ client_data->next->prev = client_data->prev;
+
+ /*
+ * remove from LRU list
+ */
+ if (client_data->lru_prev == NULL)
+ lru_first = client_data->lru_next;
+ else
+ client_data->lru_prev->lru_next = client_data->lru_next;
+ if (client_data->lru_next != NULL)
+ client_data->lru_next->lru_prev = client_data->lru_prev;
+ else
+ lru_last = client_data->lru_prev;
+
+ /*
+ * If there is a GSS context, clean up GSS state.
+ */
+ if (client_data->context != GSS_C_NO_CONTEXT) {
+ (void) gss_delete_sec_context(&minor, &client_data->context,
+ NULL);
+ if (client_data->client_name)
+ (void) gss_release_name(&minor, &client_data->client_name);
+ if (client_data->raw_cred.client_principal)
+ free((char *)client_data->raw_cred.client_principal);
+ if (client_data->u_cred.gidlist != NULL)
+ free((char *)client_data->u_cred.gidlist);
+ if (client_data->deleg != GSS_C_NO_CREDENTIAL)
+ (void) gss_release_cred(&minor, &client_data->deleg);
+ }
+
+ if (client_data->retrans_data != NULL)
+ retrans_del(client_data);
+
+ free(client_data);
+ num_gss_contexts--;
+}
+
+/*
+ * Check for expired client contexts.
+ */
+static void
+sweep_clients()
+{
+ svc_rpc_gss_data *cl, *next;
+ int index;
+
+ for (index = 0; index < HASHMOD; index++) {
+ cl = clients[index];
+ while (cl) {
+ next = cl->next;
+ mutex_lock(&cl->clm);
+ if ((cl->expiration != GSS_C_INDEFINITE &&
+ cl->expiration <= time(0)) || cl->stale) {
+ cl->stale = TRUE;
+ if (cl->ref_cnt == 0) {
+ mutex_unlock(&cl->clm);
+ destroy_client(cl);
+ } else
+ mutex_unlock(&cl->clm);
+ } else
+ mutex_unlock(&cl->clm);
+ cl = next;
+ }
+ }
+ last_swept = time(0);
+}
+
+/*
+ * Drop the least recently used client context, if possible.
+ */
+static void
+drop_lru_client()
+{
+ mutex_lock(&lru_last->clm);
+ lru_last->stale = TRUE;
+ mutex_unlock(&lru_last->clm);
+ if (lru_last->ref_cnt == 0)
+ destroy_client(lru_last);
+ else
+ sweep_clients();
+}
+
+/*
+ * find service credentials
+ * return cred if found,
+ * other wise, NULL
+ */
+
+svc_creds_list_t *
+find_svc_cred(char *service_name, uint_t program, uint_t version) {
+
+ svc_creds_list_t *sc;
+
+ if (!svc_creds_list)
+ return (NULL);
+
+ for (sc = svc_creds_list; sc != NULL; sc = sc->next) {
+ if (program != sc->program || version != sc->version)
+ continue;
+
+ if (strcmp(service_name, sc->server_name) != 0)
+ continue;
+ return (sc);
+ }
+ return (NULL);
+}
+
+/*
+ * Set the server principal name.
+ */
+bool_t
+__rpc_gss_set_svc_name(server_name, mech, req_time, program, version)
+ char *server_name;
+ char *mech;
+ OM_uint32 req_time;
+ uint_t program;
+ uint_t version;
+{
+ gss_name_t name;
+ svc_creds_list_t *svc_cred;
+ gss_OID mechanism;
+ gss_OID_set_desc oid_set_desc;
+ gss_OID_set oid_set;
+ OM_uint32 ret_time;
+ OM_uint32 major, minor;
+ gss_buffer_desc name_buf;
+
+ if (!__rpc_gss_mech_to_oid(mech, &mechanism)) {
+ return (FALSE);
+ }
+
+ name_buf.value = server_name;
+ name_buf.length = strlen(server_name);
+ major = gss_import_name(&minor, &name_buf,
+ (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &name);
+ if (major != GSS_S_COMPLETE) {
+ return (FALSE);
+ }
+
+ /* Check if there is already an entry in the svc_creds_list. */
+ rw_wrlock(&cred_lock);
+ if (svc_cred = find_svc_cred(server_name, program, version)) {
+
+ major = gss_add_cred(&minor, svc_cred->cred, name,
+ mechanism, GSS_C_ACCEPT,
+ 0, req_time, NULL,
+ &oid_set, NULL,
+ &ret_time);
+ (void) gss_release_name(&minor, &name);
+ if (major == GSS_S_COMPLETE) {
+ /*
+ * Successfully added the mech to the cred handle
+ * free the existing oid_set in svc_cred
+ */
+ gss_release_oid_set(&minor, &svc_cred->oid_set);
+ svc_cred->oid_set = oid_set;
+ rw_unlock(&cred_lock);
+ return (TRUE);
+ } else if (major == GSS_S_DUPLICATE_ELEMENT) {
+ rw_unlock(&cred_lock);
+ return (TRUE);
+ } else if (major == GSS_S_CREDENTIALS_EXPIRED) {
+ if (rpc_gss_refresh_svc_cred(svc_cred)) {
+ rw_unlock(&cred_lock);
+ return (TRUE);
+ } else {
+ rw_unlock(&cred_lock);
+ return (FALSE);
+ }
+ } else {
+ rw_unlock(&cred_lock);
+ return (FALSE);
+ }
+ } else {
+ svc_cred = (svc_creds_list_t *)malloc(sizeof (*svc_cred));
+ if (svc_cred == NULL) {
+ (void) gss_release_name(&minor, &name);
+ rw_unlock(&cred_lock);
+ return (FALSE);
+ }
+ oid_set_desc.count = 1;
+ oid_set_desc.elements = mechanism;
+ major = gss_acquire_cred(&minor, name, req_time,
+ &oid_set_desc,
+ GSS_C_ACCEPT,
+ &svc_cred->cred,
+ &oid_set, &ret_time);
+
+ if (major != GSS_S_COMPLETE) {
+ (void) gss_release_name(&minor, &name);
+ free(svc_cred);
+ rw_unlock(&cred_lock);
+ return (FALSE);
+ }
+
+ svc_cred->name = name;
+ svc_cred->program = program;
+ svc_cred->version = version;
+ svc_cred->req_time = req_time;
+ svc_cred->oid_set = oid_set;
+ svc_cred->server_name = strdup(server_name);
+ if (svc_cred->server_name == NULL) {
+ (void) gss_release_name(&minor, &name);
+ free((char *)svc_cred);
+ rw_unlock(&cred_lock);
+ return (FALSE);
+ }
+ mutex_init(&svc_cred->refresh_mutex, USYNC_THREAD, NULL);
+
+ svc_cred->next = svc_creds_list;
+ svc_creds_list = svc_cred;
+ svc_creds_count++;
+ rw_unlock(&cred_lock);
+
+ return (TRUE);
+ }
+}
+/*
+ * Refresh server credentials.
+ */
+static bool_t
+rpc_gss_refresh_svc_cred(svc_cred)
+ svc_creds_list_t *svc_cred;
+{
+ OM_uint32 major, minor;
+ gss_OID_set oid_set;
+ OM_uint32 ret_time;
+
+ (void) gss_release_cred(&minor, &svc_cred->cred);
+ svc_cred->cred = GSS_C_NO_CREDENTIAL;
+ major = gss_acquire_cred(&minor, svc_cred->name, svc_cred->req_time,
+ svc_cred->oid_set, GSS_C_ACCEPT, &svc_cred->cred, &oid_set,
+ &ret_time);
+ if (major != GSS_S_COMPLETE) {
+ return (FALSE);
+ }
+ gss_release_oid_set(&minor, &svc_cred->oid_set);
+ svc_cred->oid_set = oid_set;
+ return (TRUE);
+}
+
+/*
+ * Encrypt the serialized arguments from xdr_func applied to xdr_ptr
+ * and write the result to xdrs.
+ */
+static bool_t
+svc_rpc_gss_wrap(auth, out_xdrs, xdr_func, xdr_ptr)
+ SVCAUTH *auth;
+ XDR *out_xdrs;
+ bool_t (*xdr_func)();
+ caddr_t xdr_ptr;
+{
+ svc_rpc_gss_parms_t *gss_parms = &auth->svc_gss_parms;
+
+ /*
+ * If context is not established, or if neither integrity nor
+ * privacy service is used, don't wrap - just XDR encode.
+ * Otherwise, wrap data using service and QOP parameters.
+ */
+ if (!gss_parms->established ||
+ gss_parms->service == rpc_gss_svc_none)
+ return ((*xdr_func)(out_xdrs, xdr_ptr));
+
+ return (__rpc_gss_wrap_data(gss_parms->service,
+ (OM_uint32)gss_parms->qop_rcvd,
+ (gss_ctx_id_t)gss_parms->context,
+ gss_parms->seq_num,
+ out_xdrs, xdr_func, xdr_ptr));
+}
+
+/*
+ * Decrypt the serialized arguments and XDR decode them.
+ */
+static bool_t
+svc_rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr)
+ SVCAUTH *auth;
+ XDR *in_xdrs;
+ bool_t (*xdr_func)();
+ caddr_t xdr_ptr;
+{
+ svc_rpc_gss_parms_t *gss_parms = &auth->svc_gss_parms;
+
+ /*
+ * If context is not established, or if neither integrity nor
+ * privacy service is used, don't unwrap - just XDR decode.
+ * Otherwise, unwrap data.
+ */
+ if (!gss_parms->established ||
+ gss_parms->service == rpc_gss_svc_none)
+ return ((*xdr_func)(in_xdrs, xdr_ptr));
+
+ return (__rpc_gss_unwrap_data(gss_parms->service,
+ (gss_ctx_id_t)gss_parms->context,
+ gss_parms->seq_num,
+ gss_parms->qop_rcvd,
+ in_xdrs, xdr_func, xdr_ptr));
+}
+
+int
+__rpc_gss_svc_max_data_length(req, max_tp_unit_len)
+ struct svc_req *req;
+ int max_tp_unit_len;
+{
+ SVCAUTH *svcauth;
+ svc_rpc_gss_parms_t *gss_parms;
+
+ svcauth = __svc_get_svcauth(req->rq_xprt);
+ gss_parms = &svcauth->svc_gss_parms;
+
+ if (!gss_parms->established || max_tp_unit_len <= 0)
+ return (0);
+
+ return (__find_max_data_length(gss_parms->service,
+ (gss_ctx_id_t)gss_parms->context,
+ gss_parms->qop_rcvd, max_tp_unit_len));
+}
+
+/*
+ * Add retransmit entry to the context cache entry for a new xid.
+ * If there is already an entry, delete it before adding the new one.
+ */
+static void retrans_add(client, xid, result)
+ svc_rpc_gss_data *client;
+ uint32_t xid;
+ rpc_gss_init_res *result;
+{
+ retrans_entry *rdata;
+
+ if (client->retrans_data && client->retrans_data->xid == xid)
+ return;
+
+ rdata = (retrans_entry *) malloc(sizeof (*rdata));
+ if (rdata == NULL)
+ return;
+
+ rdata->xid = xid;
+ rdata->result = *result;
+
+ if (result->token.length != 0) {
+ GSS_DUP_BUFFER(rdata->result.token, result->token);
+ }
+
+ if (client->retrans_data)
+ retrans_del(client);
+
+ client->retrans_data = rdata;
+}
+
+/*
+ * Delete the retransmit data from the context cache entry.
+ */
+static void retrans_del(client)
+ svc_rpc_gss_data *client;
+{
+ retrans_entry *rdata;
+ OM_uint32 minor_stat;
+
+ if (client->retrans_data == NULL)
+ return;
+
+ rdata = client->retrans_data;
+ if (rdata->result.token.length != 0) {
+ (void) gss_release_buffer(&minor_stat, &rdata->result.token);
+ }
+
+ free((caddr_t)rdata);
+ client->retrans_data = NULL;
+}