aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Rabson <dfr@FreeBSD.org>2008-08-06 14:02:05 +0000
committerDoug Rabson <dfr@FreeBSD.org>2008-08-06 14:02:05 +0000
commit8f55a568f69c5b2fadaa020a452c2043a7dcf65f (patch)
treea928d209076dec713f636439ec8dc5be13863460
parent503765e486a9c33dc1875451a7c94e761a8d8a8d (diff)
downloadsrc-8f55a568f69c5b2fadaa020a452c2043a7dcf65f.tar.gz
src-8f55a568f69c5b2fadaa020a452c2043a7dcf65f.zip
Add an implementation of the RPCSEC_GSS authentication protocol for RPC. This
is based on an old implementation from the University of Michigan with lots of changes and fixes by me and the addition of a Solaris-compatible API. Sponsored by: Isilon Systems Reviewed by: alfred
Notes
Notes: svn path=/head/; revision=181344
-rw-r--r--gnu/usr.bin/groff/tmac/mdoc.local1
-rw-r--r--include/gssapi/gssapi.h9
-rw-r--r--include/rpc/Makefile2
-rw-r--r--include/rpc/auth.h18
-rw-r--r--include/rpc/rpcsec_gss.h179
-rw-r--r--include/rpc/svc.h43
-rw-r--r--kerberos5/lib/libgssapi_krb5/Makefile1
-rw-r--r--kerberos5/lib/libgssapi_krb5/pname_to_uid.c59
-rw-r--r--lib/Makefile4
-rw-r--r--lib/libc/rpc/Makefile.inc5
-rw-r--r--lib/libc/rpc/Symbol.map4
-rw-r--r--lib/libc/rpc/clnt_dg.c112
-rw-r--r--lib/libc/rpc/clnt_perror.c9
-rw-r--r--lib/libc/rpc/clnt_vc.c49
-rw-r--r--lib/libc/rpc/rpcsec_gss_stub.c48
-rw-r--r--lib/libc/rpc/svc.c58
-rw-r--r--lib/libc/rpc/svc_auth.c24
-rw-r--r--lib/libc/rpc/svc_dg.c34
-rw-r--r--lib/libc/rpc/svc_raw.c38
-rw-r--r--lib/libc/rpc/svc_vc.c45
-rw-r--r--lib/libc/xdr/xdr_rec.c25
-rw-r--r--lib/libgssapi/Makefile1
-rw-r--r--lib/libgssapi/Symbol.map7
-rw-r--r--lib/libgssapi/gss_mech_switch.c1
-rw-r--r--lib/libgssapi/gss_pname_to_uid.c69
-rw-r--r--lib/libgssapi/gss_utils.c12
-rw-r--r--lib/libgssapi/mech_switch.h8
-rw-r--r--lib/libgssapi/utils.h1
-rw-r--r--lib/librpcsec_gss/Makefile39
-rw-r--r--lib/librpcsec_gss/Symbol.map28
-rw-r--r--lib/librpcsec_gss/rpc_gss_get_error.358
-rw-r--r--lib/librpcsec_gss/rpc_gss_get_mech_info.368
-rw-r--r--lib/librpcsec_gss/rpc_gss_get_mechanisms.355
-rw-r--r--lib/librpcsec_gss/rpc_gss_get_principal_name.382
-rw-r--r--lib/librpcsec_gss/rpc_gss_get_versions.364
-rw-r--r--lib/librpcsec_gss/rpc_gss_getcred.385
-rw-r--r--lib/librpcsec_gss/rpc_gss_is_installed.365
-rw-r--r--lib/librpcsec_gss/rpc_gss_max_data_length.364
-rw-r--r--lib/librpcsec_gss/rpc_gss_mech_to_oid.368
-rw-r--r--lib/librpcsec_gss/rpc_gss_oid_to_mech.368
-rw-r--r--lib/librpcsec_gss/rpc_gss_qop_to_num.370
-rw-r--r--lib/librpcsec_gss/rpc_gss_seccreate.3112
-rw-r--r--lib/librpcsec_gss/rpc_gss_set_callback.3115
-rw-r--r--lib/librpcsec_gss/rpc_gss_set_defaults.370
-rw-r--r--lib/librpcsec_gss/rpc_gss_set_svc_name.387
-rw-r--r--lib/librpcsec_gss/rpc_gss_svc_max_data_length.364
-rw-r--r--lib/librpcsec_gss/rpcsec_gss.3230
-rw-r--r--lib/librpcsec_gss/rpcsec_gss.c722
-rw-r--r--lib/librpcsec_gss/rpcsec_gss_conf.c417
-rw-r--r--lib/librpcsec_gss/rpcsec_gss_int.h95
-rw-r--r--lib/librpcsec_gss/rpcsec_gss_misc.c49
-rw-r--r--lib/librpcsec_gss/rpcsec_gss_prot.c288
-rw-r--r--lib/librpcsec_gss/svc_rpcsec_gss.c1214
53 files changed, 5048 insertions, 95 deletions
diff --git a/gnu/usr.bin/groff/tmac/mdoc.local b/gnu/usr.bin/groff/tmac/mdoc.local
index fe59dbf6c875..46941fce555b 100644
--- a/gnu/usr.bin/groff/tmac/mdoc.local
+++ b/gnu/usr.bin/groff/tmac/mdoc.local
@@ -53,6 +53,7 @@
.ds doc-str-Lb-libmemstat Kernel Memory Allocator Statistics Library (libmemstat, \-lmemstat)
.ds doc-str-Lb-libnetgraph Netgraph User Library (libnetgraph, \-lnetgraph)
.ds doc-str-Lb-libpmc Performance Monitoring Counters Interface Library (libpmc, \-lpmc)
+.ds doc-str-Lb-librpcsec_gss RPC GSS-API Authentication Library (librpcsec_gss, \-lrpcsec_gss)
.ds doc-str-Lb-librpcsvc RPC Service Library (librpcsvc, \-lrpcsvc)
.ds doc-str-Lb-libsdp Bluetooth Service Discovery Protocol User Library (libsdp, \-lsdp)
.ds doc-str-Lb-libthr 1:1 Threading Library (libthr, \-lthr)
diff --git a/include/gssapi/gssapi.h b/include/gssapi/gssapi.h
index 30f6876bcc7d..16a588e1456d 100644
--- a/include/gssapi/gssapi.h
+++ b/include/gssapi/gssapi.h
@@ -837,6 +837,15 @@ OM_uint32 gss_pseudo_random
gss_buffer_t /* buffer for result */
);
+#ifdef _UID_T_DECLARED
+OM_uint32 gss_pname_to_uid
+ (OM_uint32 *, /* minor status */
+ const gss_name_t pname, /* principal name */
+ const gss_OID mech, /* mechanism to query */
+ uid_t *uidp /* pointer to UID for result */
+ );
+#endif
+
__END_DECLS
#endif /* _GSSAPI_GSSAPI_H_ */
diff --git a/include/rpc/Makefile b/include/rpc/Makefile
index ee78fcf00e17..40d4c47b3a07 100644
--- a/include/rpc/Makefile
+++ b/include/rpc/Makefile
@@ -11,7 +11,7 @@ XFILES= rpcb_prot.x
HFILES= auth.h auth_unix.h clnt.h clnt_soc.h clnt_stat.h \
nettype.h pmap_clnt.h pmap_prot.h pmap_rmt.h raw.h \
- rpc.h rpc_msg.h rpcb_clnt.h rpcent.h rpc_com.h \
+ rpc.h rpc_msg.h rpcb_clnt.h rpcent.h rpc_com.h rpcsec_gss.h \
svc.h svc_auth.h svc_soc.h svc_dg.h xdr.h
# Secure RPC
diff --git a/include/rpc/auth.h b/include/rpc/auth.h
index fd48421de8a0..d39ad1205379 100644
--- a/include/rpc/auth.h
+++ b/include/rpc/auth.h
@@ -132,7 +132,7 @@ enum auth_stat {
* failed locally
*/
AUTH_INVALIDRESP=6, /* bogus response verifier */
- AUTH_FAILED=7 /* some unknown reason */
+ AUTH_FAILED=7, /* some unknown reason */
#ifdef KERBEROS
/*
* kerberos errors
@@ -142,8 +142,14 @@ enum auth_stat {
AUTH_TIMEEXPIRE = 9, /* time of credential expired */
AUTH_TKT_FILE = 10, /* something wrong with ticket file */
AUTH_DECODE = 11, /* can't decode authenticator */
- AUTH_NET_ADDR = 12 /* wrong net address in ticket */
+ AUTH_NET_ADDR = 12, /* wrong net address in ticket */
#endif /* KERBEROS */
+ /*
+ * RPCSEC_GSS errors
+ */
+ RPCSEC_GSS_CREDPROBLEM = 13,
+ RPCSEC_GSS_CTXPROBLEM = 14,
+ RPCSEC_GSS_NODISPATCH = 0x8000000
};
union des_block {
@@ -352,5 +358,13 @@ __END_DECLS
#define AUTH_DH 3 /* for Diffie-Hellman mechanism */
#define AUTH_DES AUTH_DH /* for backward compatibility */
#define AUTH_KERB 4 /* kerberos style */
+#define RPCSEC_GSS 6 /* RPCSEC_GSS */
+
+/*
+ * Pseudo auth flavors for RPCSEC_GSS.
+ */
+#define RPCSEC_GSS_KRB5 390003
+#define RPCSEC_GSS_KRB5I 390004
+#define RPCSEC_GSS_KRB5P 390005
#endif /* !_RPC_AUTH_H */
diff --git a/include/rpc/rpcsec_gss.h b/include/rpc/rpcsec_gss.h
new file mode 100644
index 000000000000..ccd346fd4564
--- /dev/null
+++ b/include/rpc/rpcsec_gss.h
@@ -0,0 +1,179 @@
+/*-
+ * Copyright (c) 2008 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _RPCSEC_GSS_H
+#define _RPCSEC_GSS_H
+
+#include <gssapi/gssapi.h>
+
+#ifndef MAX_GSS_MECH
+#define MAX_GSS_MECH 64
+#endif
+
+/*
+ * Define the types of security service required for rpc_gss_seccreate().
+ */
+typedef enum {
+ rpc_gss_svc_default = 0,
+ rpc_gss_svc_none = 1,
+ rpc_gss_svc_integrity = 2,
+ rpc_gss_svc_privacy = 3
+} rpc_gss_service_t;
+
+/*
+ * Structure containing options for rpc_gss_seccreate().
+ */
+typedef struct {
+ int req_flags; /* GSS request bits */
+ int time_req; /* requested credential lifetime */
+ gss_cred_id_t my_cred; /* GSS credential */
+ gss_channel_bindings_t input_channel_bindings;
+} rpc_gss_options_req_t;
+
+/*
+ * Structure containing options returned by rpc_gss_seccreate().
+ */
+typedef struct {
+ int major_status;
+ int minor_status;
+ u_int rpcsec_version;
+ int ret_flags;
+ int time_req;
+ gss_ctx_id_t gss_context;
+ char actual_mechanism[MAX_GSS_MECH];
+} rpc_gss_options_ret_t;
+
+/*
+ * Client principal type. Used as an argument to
+ * rpc_gss_get_principal_name(). Also referenced by the
+ * rpc_gss_rawcred_t structure.
+ */
+typedef struct {
+ int len;
+ char name[1];
+} *rpc_gss_principal_t;
+
+/*
+ * Structure for raw credentials used by rpc_gss_getcred() and
+ * rpc_gss_set_callback().
+ */
+typedef struct {
+ u_int version; /* RPC version number */
+ const char *mechanism; /* security mechanism */
+ const char *qop; /* quality of protection */
+ rpc_gss_principal_t client_principal; /* client name */
+ const char *svc_principal; /* server name */
+ rpc_gss_service_t service; /* service type */
+} rpc_gss_rawcred_t;
+
+/*
+ * Unix credentials derived from raw credentials. Returned by
+ * rpc_gss_getcred().
+ */
+typedef struct {
+ uid_t uid; /* user ID */
+ gid_t gid; /* group ID */
+ short gidlen;
+ gid_t *gidlist; /* list of groups */
+} rpc_gss_ucred_t;
+
+/*
+ * Structure used to enforce a particular QOP and service.
+ */
+typedef struct {
+ bool_t locked;
+ rpc_gss_rawcred_t *raw_cred;
+} rpc_gss_lock_t;
+
+/*
+ * Callback structure used by rpc_gss_set_callback().
+ */
+typedef struct {
+ u_int program; /* RPC program number */
+ u_int version; /* RPC version number */
+ /* user defined callback */
+ bool_t (*callback)(struct svc_req *req,
+ gss_cred_id_t deleg,
+ gss_ctx_id_t gss_context,
+ rpc_gss_lock_t *lock,
+ void **cookie);
+} rpc_gss_callback_t;
+
+/*
+ * Structure used to return error information by rpc_gss_get_error()
+ */
+typedef struct {
+ int rpc_gss_error;
+ int system_error; /* same as errno */
+} rpc_gss_error_t;
+
+/*
+ * Values for rpc_gss_error
+ */
+#define RPC_GSS_ER_SUCCESS 0 /* no error */
+#define RPC_GSS_ER_SYSTEMERROR 1 /* system error */
+
+__BEGIN_DECLS
+
+AUTH *rpc_gss_seccreate(CLIENT *clnt, const char *principal,
+ const char *mechanism, rpc_gss_service_t service, const char *qop,
+ rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret);
+bool_t rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service,
+ const char *qop);
+int rpc_gss_max_data_length(AUTH *handle, int max_tp_unit_len);
+void rpc_gss_get_error(rpc_gss_error_t *error);
+
+bool_t rpc_gss_mech_to_oid(const char *mech, gss_OID *oid_ret);
+bool_t rpc_gss_oid_to_mech(gss_OID oid, const char **mech_ret);
+bool_t rpc_gss_qop_to_num(const char *qop, const char *mech, u_int *num_ret);
+const char **rpc_gss_get_mechanisms(void);
+const char **rpc_gss_get_mech_info(const char *mech, rpc_gss_service_t *service);
+bool_t rpc_gss_get_versions(u_int *vers_hi, u_int *vers_lo);
+bool_t rpc_gss_is_installed(const char *mech);
+
+bool_t rpc_gss_set_svc_name(const char *principal, const char *mechanism,
+ u_int req_time, u_int program, u_int version);
+bool_t rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
+ rpc_gss_ucred_t **ucred, void **cookie);
+bool_t rpc_gss_set_callback(rpc_gss_callback_t *cb);
+bool_t rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
+ const char *mech, const char *name, const char *node, const char *domain);
+int rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len);
+
+/*
+ * Internal interface from the RPC implementation.
+ */
+bool_t __rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen,
+ XDR* xdrs, xdrproc_t xdr_args, void *args_ptr);
+bool_t __rpc_gss_unwrap(AUTH *auth, XDR* xdrs, xdrproc_t xdr_args,
+ void *args_ptr);
+bool_t __rpc_gss_set_error(int rpc_gss_error, int system_error);
+
+__END_DECLS
+
+#endif /* !_RPCSEC_GSS_H */
diff --git a/include/rpc/svc.h b/include/rpc/svc.h
index 7ecae0c68e8f..8466ef8b22af 100644
--- a/include/rpc/svc.h
+++ b/include/rpc/svc.h
@@ -127,6 +127,27 @@ typedef struct __rpc_svcxprt {
} SVCXPRT;
/*
+ * Interface to server-side authentication flavors.
+ */
+typedef struct __rpc_svcauth {
+ struct svc_auth_ops {
+ int (*svc_ah_wrap)(struct __rpc_svcauth *, XDR *,
+ xdrproc_t, caddr_t);
+ int (*svc_ah_unwrap)(struct __rpc_svcauth *, XDR *,
+ xdrproc_t, caddr_t);
+ } *svc_ah_ops;
+ void *svc_ah_private;
+} SVCAUTH;
+
+/*
+ * Server transport extensions (accessed via xp_p3).
+ */
+typedef struct __rpc_svcxprt_ext {
+ int xp_flags; /* versquiet */
+ SVCAUTH xp_auth; /* interface to auth methods */
+} SVCXPRT_EXT;
+
+/*
* Service request
*/
struct svc_req {
@@ -184,6 +205,20 @@ struct svc_req {
#define SVC_CONTROL(xprt, rq, in) \
(*(xprt)->xp_ops2->xp_control)((xprt), (rq), (in))
+#define SVC_EXT(xprt) \
+ ((SVCXPRT_EXT *) xprt->xp_p3)
+
+#define SVC_AUTH(xprt) \
+ (SVC_EXT(xprt)->xp_auth)
+
+/*
+ * Operations defined on an SVCAUTH handle
+ */
+#define SVCAUTH_WRAP(auth, xdrs, xfunc, xwhere) \
+ ((auth)->svc_ah_ops->svc_ah_wrap(auth, xdrs, xfunc, xwhere))
+#define SVCAUTH_UNWRAP(auth, xdrs, xfunc, xwhere) \
+ ((auth)->svc_ah_ops->svc_ah_unwrap(auth, xdrs, xfunc, xwhere))
+
/*
* Service registration
*
@@ -298,6 +333,12 @@ extern int svc_fds;
#endif /* def FD_SETSIZE */
/*
+ * A set of null auth methods used by any authentication protocols
+ * that don't need to inspect or modify the message body.
+ */
+extern SVCAUTH _svc_auth_null;
+
+/*
* a small program implemented by the svc_rpc implementation itself;
* also see clnt.h for protocol numbers.
*/
@@ -306,6 +347,8 @@ extern void rpctest_service(void);
__END_DECLS
__BEGIN_DECLS
+extern SVCXPRT *svc_xprt_alloc(void);
+extern void svc_xprt_free(SVCXPRT *);
extern void svc_getreq(int);
extern void svc_getreqset(fd_set *);
extern void svc_getreq_common(int);
diff --git a/kerberos5/lib/libgssapi_krb5/Makefile b/kerberos5/lib/libgssapi_krb5/Makefile
index b866d1b3bb71..b2cbe51f4e2f 100644
--- a/kerberos5/lib/libgssapi_krb5/Makefile
+++ b/kerberos5/lib/libgssapi_krb5/Makefile
@@ -46,6 +46,7 @@ SRCS= 8003.c \
inquire_mechs_for_name.c \
inquire_names_for_mech.c \
inquire_sec_context_by_oid.c \
+ pname_to_uid.c \
prefix.c \
prf.c \
process_context_token.c \
diff --git a/kerberos5/lib/libgssapi_krb5/pname_to_uid.c b/kerberos5/lib/libgssapi_krb5/pname_to_uid.c
new file mode 100644
index 000000000000..52220f67cfcb
--- /dev/null
+++ b/kerberos5/lib/libgssapi_krb5/pname_to_uid.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* $FreeBSD$ */
+
+#include <pwd.h>
+
+#include "krb5/gsskrb5_locl.h"
+
+OM_uint32
+_gsskrb5_pname_to_uid(OM_uint32 *minor_status, const gss_name_t pname,
+ const gss_OID mech, uid_t *uidp)
+{
+ krb5_context context;
+ krb5_const_principal name = (krb5_const_principal) pname;
+ krb5_error_code kret;
+ char lname[MAXLOGNAME + 1], buf[128];
+ struct passwd pwd, *pw;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ kret = krb5_aname_to_localname(context, name, sizeof(lname), lname);
+ if (kret) {
+ *minor_status = kret;
+ return (GSS_S_FAILURE);
+ }
+
+ *minor_status = 0;
+ getpwnam_r(lname, &pwd, buf, sizeof(buf), &pw);
+ if (pw) {
+ *uidp = pw->pw_uid;
+ return (GSS_S_COMPLETE);
+ } else {
+ return (GSS_S_FAILURE);
+ }
+}
diff --git a/lib/Makefile b/lib/Makefile
index 76e28fe69da9..fd1ea0b3bddd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -22,6 +22,7 @@
# libtacplus must be built before libpam.
# libutil must be built before libpam.
# libypclnt must be built before libpam.
+# libgssapi must be built before librpcsec_gss
#
# Otherwise, the SUBDIR list should be in alphabetical order.
@@ -31,7 +32,7 @@ SUBDIR= ${_csu} libc libbsm libcom_err libcrypt libelf libkvm msun libmd \
libbegemot ${_libbluetooth} libbsnmp libbz2 \
libcalendar libcam libcompat libdevinfo libdevstat libdisk \
libdwarf libedit libexpat libfetch libftpio libgeom ${_libgpib} \
- ${_libgssapi} libipsec \
+ ${_libgssapi} ${_librpcsec_gss} libipsec \
${_libipx} libkiconv libmagic libmemstat ${_libmilter} ${_libmp} \
${_libncp} ${_libngatm} libopie libpam libpcap \
libpmc libproc librt ${_libsdp} ${_libsm} ${_libsmb} \
@@ -62,6 +63,7 @@ _libsdp= libsdp
.if ${MK_GSSAPI} != "no"
_libgssapi= libgssapi
+_librpcsec_gss= librpcsec_gss
.endif
.if ${MK_IPX} != "no"
diff --git a/lib/libc/rpc/Makefile.inc b/lib/libc/rpc/Makefile.inc
index dceecc7a4c80..db0b8ad3e785 100644
--- a/lib/libc/rpc/Makefile.inc
+++ b/lib/libc/rpc/Makefile.inc
@@ -8,8 +8,9 @@ SRCS+= auth_none.c auth_unix.c authunix_prot.c bindresvport.c clnt_bcast.c \
getrpcport.c mt_misc.c pmap_clnt.c pmap_getmaps.c pmap_getport.c \
pmap_prot.c pmap_prot2.c pmap_rmt.c rpc_prot.c rpc_commondata.c \
rpc_callmsg.c rpc_generic.c rpc_soc.c rpcb_clnt.c rpcb_prot.c \
- rpcb_st_xdr.c svc.c svc_auth.c svc_dg.c svc_auth_unix.c svc_generic.c \
- svc_raw.c svc_run.c svc_simple.c svc_vc.c
+ rpcb_st_xdr.c rpcsec_gss_stub.c svc.c svc_auth.c svc_dg.c \
+ svc_auth_unix.c svc_generic.c svc_raw.c svc_run.c svc_simple.c \
+ svc_vc.c
# Secure-RPC
SRCS+= auth_time.c auth_des.c authdes_prot.c des_crypt.c des_soft.c \
diff --git a/lib/libc/rpc/Symbol.map b/lib/libc/rpc/Symbol.map
index e3ac6a608f7a..ccf0bfa79b66 100644
--- a/lib/libc/rpc/Symbol.map
+++ b/lib/libc/rpc/Symbol.map
@@ -244,4 +244,8 @@ FBSDprivate_1.0 {
* Remove this hack if rpcinfo stops building with it.
*/
__svc_clean_idle;
+ __rpc_gss_unwrap;
+ __rpc_gss_unwrap_stub;
+ __rpc_gss_wrap;
+ __rpc_gss_wrap_stub;
};
diff --git a/lib/libc/rpc/clnt_dg.c b/lib/libc/rpc/clnt_dg.c
index 27296abf7a9f..301415527706 100644
--- a/lib/libc/rpc/clnt_dg.c
+++ b/lib/libc/rpc/clnt_dg.c
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
@@ -113,6 +114,8 @@ static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
/* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */
+#define MCALL_MSG_SIZE 24
+
/*
* Private data kept per client handle
*/
@@ -127,6 +130,7 @@ struct cu_data {
XDR cu_outxdrs;
u_int cu_xdrpos;
u_int cu_sendsz; /* send size */
+ char cu_outhdr[MCALL_MSG_SIZE];
char *cu_outbuf;
u_int cu_recvsz; /* recv size */
int cu_async;
@@ -253,13 +257,16 @@ clnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz)
call_msg.rm_xid = __RPC_GETXID(&now);
call_msg.rm_call.cb_prog = program;
call_msg.rm_call.cb_vers = version;
- xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
- if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
+ xdrmem_create(&(cu->cu_outxdrs), cu->cu_outhdr, MCALL_MSG_SIZE,
+ XDR_ENCODE);
+ if (! xdr_callhdr(&cu->cu_outxdrs, &call_msg)) {
rpc_createerr.cf_stat = RPC_CANTENCODEARGS; /* XXX */
rpc_createerr.cf_error.re_errno = 0;
goto err2;
}
cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
+ XDR_DESTROY(&cu->cu_outxdrs);
+ xdrmem_create(&cu->cu_outxdrs, cu->cu_outbuf, sendsz, XDR_ENCODE);
/* XXX fvdl - do we still want this? */
#if 0
@@ -312,6 +319,7 @@ clnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
XDR reply_xdrs;
bool_t ok;
int nrefreshes = 2; /* number of times to refresh cred */
+ int nretries = 0; /* number of times we retransmitted */
struct timeval timeout;
struct timeval retransmit_time;
struct timeval next_sendtime, starttime, time_waited, tv;
@@ -375,25 +383,37 @@ clnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
kin_len = 1;
call_again:
- xdrs = &(cu->cu_outxdrs);
- if (cu->cu_async == TRUE && xargs == NULL)
- goto get_reply;
- xdrs->x_op = XDR_ENCODE;
- XDR_SETPOS(xdrs, cu->cu_xdrpos);
/*
* the transaction is the first thing in the out buffer
* XXX Yes, and it's in network byte order, so we should to
* be careful when we increment it, shouldn't we.
*/
- xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf));
+ xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr));
xid++;
- *(u_int32_t *)(void *)(cu->cu_outbuf) = htonl(xid);
-
- if ((! XDR_PUTINT32(xdrs, &proc)) ||
- (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
- (! (*xargs)(xdrs, argsp))) {
- cu->cu_error.re_status = RPC_CANTENCODEARGS;
- goto out;
+ *(u_int32_t *)(void *)(cu->cu_outhdr) = htonl(xid);
+call_again_same_xid:
+ xdrs = &(cu->cu_outxdrs);
+ if (cu->cu_async == TRUE && xargs == NULL)
+ goto get_reply;
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, 0);
+
+ if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
+ if ((! XDR_PUTBYTES(xdrs, cu->cu_outhdr, cu->cu_xdrpos)) ||
+ (! XDR_PUTINT32(xdrs, &proc)) ||
+ (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
+ (! (*xargs)(xdrs, argsp))) {
+ cu->cu_error.re_status = RPC_CANTENCODEARGS;
+ goto out;
+ }
+ } else {
+ *(uint32_t *) &cu->cu_outhdr[cu->cu_xdrpos] = htonl(proc);
+ if (!__rpc_gss_wrap(cl->cl_auth, cu->cu_outhdr,
+ cu->cu_xdrpos + sizeof(uint32_t),
+ xdrs, xargs, argsp)) {
+ cu->cu_error.re_status = RPC_CANTENCODEARGS;
+ goto out;
+ }
}
outlen = (size_t)XDR_GETPOS(xdrs);
@@ -420,8 +440,13 @@ get_reply:
* (We assume that this is actually only executed once.)
*/
reply_msg.acpted_rply.ar_verf = _null_auth;
- reply_msg.acpted_rply.ar_results.where = resultsp;
- reply_msg.acpted_rply.ar_results.proc = xresults;
+ if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
+ reply_msg.acpted_rply.ar_results.where = resultsp;
+ reply_msg.acpted_rply.ar_results.proc = xresults;
+ } else {
+ reply_msg.acpted_rply.ar_results.where = NULL;
+ reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
+ }
for (;;) {
/* Decide how long to wait. */
@@ -483,7 +508,17 @@ get_reply:
&retransmit_time);
timeradd(&next_sendtime, &retransmit_time,
&next_sendtime);
- goto send_again;
+ nretries++;
+
+ /*
+ * When retransmitting a RPCSEC_GSS message,
+ * we must use a new sequence number (handled
+ * by __rpc_gss_wrap above).
+ */
+ if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS)
+ goto send_again;
+ else
+ goto call_again_same_xid;
}
}
inlen = (socklen_t)recvlen;
@@ -505,8 +540,37 @@ get_reply:
if (cu->cu_error.re_status == RPC_SUCCESS) {
if (! AUTH_VALIDATE(cl->cl_auth,
&reply_msg.acpted_rply.ar_verf)) {
+ if (nretries &&
+ cl->cl_auth->ah_cred.oa_flavor
+ == RPCSEC_GSS)
+ /*
+ * If we retransmitted, its
+ * possible that we will
+ * receive a reply for one of
+ * the earlier transmissions
+ * (which will use an older
+ * RPCSEC_GSS sequence
+ * number). In this case, just
+ * go back and listen for a
+ * new reply. We could keep a
+ * record of all the seq
+ * numbers we have transmitted
+ * so far so that we could
+ * accept a reply for any of
+ * them here.
+ */
+ goto get_reply;
cu->cu_error.re_status = RPC_AUTHERROR;
cu->cu_error.re_why = AUTH_INVALIDRESP;
+ } else {
+ if (cl->cl_auth->ah_cred.oa_flavor
+ == RPCSEC_GSS) {
+ if (!__rpc_gss_unwrap(cl->cl_auth,
+ &reply_xdrs, xresults,
+ resultsp))
+ cu->cu_error.re_status =
+ RPC_CANTDECODERES;
+ }
}
if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
xdrs->x_op = XDR_FREE;
@@ -670,12 +734,12 @@ clnt_dg_control(cl, request, info)
* This will get the xid of the PREVIOUS call
*/
*(u_int32_t *)info =
- ntohl(*(u_int32_t *)(void *)cu->cu_outbuf);
+ ntohl(*(u_int32_t *)(void *)cu->cu_outhdr);
break;
case CLSET_XID:
/* This will set the xid of the NEXT call */
- *(u_int32_t *)(void *)cu->cu_outbuf =
+ *(u_int32_t *)(void *)cu->cu_outhdr =
htonl(*(u_int32_t *)info - 1);
/* decrement by 1 as clnt_dg_call() increments once */
break;
@@ -688,12 +752,12 @@ clnt_dg_control(cl, request, info)
* call_struct is changed
*/
*(u_int32_t *)info =
- ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
+ ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr +
4 * BYTES_PER_XDR_UNIT));
break;
case CLSET_VERS:
- *(u_int32_t *)(void *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
+ *(u_int32_t *)(void *)(cu->cu_outhdr + 4 * BYTES_PER_XDR_UNIT)
= htonl(*(u_int32_t *)info);
break;
@@ -705,12 +769,12 @@ clnt_dg_control(cl, request, info)
* call_struct is changed
*/
*(u_int32_t *)info =
- ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
+ ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr +
3 * BYTES_PER_XDR_UNIT));
break;
case CLSET_PROG:
- *(u_int32_t *)(void *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
+ *(u_int32_t *)(void *)(cu->cu_outhdr + 3 * BYTES_PER_XDR_UNIT)
= htonl(*(u_int32_t *)info);
break;
case CLSET_ASYNC:
diff --git a/lib/libc/rpc/clnt_perror.c b/lib/libc/rpc/clnt_perror.c
index 4b6d6b1e6e0f..efe9043c29ab 100644
--- a/lib/libc/rpc/clnt_perror.c
+++ b/lib/libc/rpc/clnt_perror.c
@@ -309,7 +309,14 @@ static const char *const auth_errlist[] = {
"Server rejected verifier", /* 4 - AUTH_REJECTEDVERF */
"Client credential too weak", /* 5 - AUTH_TOOWEAK */
"Invalid server verifier", /* 6 - AUTH_INVALIDRESP */
- "Failed (unspecified error)" /* 7 - AUTH_FAILED */
+ "Failed (unspecified error)", /* 7 - AUTH_FAILED */
+ "Kerberos generic error", /* 8 - AUTH_KERB_GENERIC*/
+ "Kerberos credential expired", /* 9 - AUTH_TIMEEXPIRE */
+ "Bad kerberos ticket file", /* 10 - AUTH_TKT_FILE */
+ "Can't decode kerberos authenticator", /* 11 - AUTH_DECODE */
+ "Address wrong in kerberos ticket", /* 12 - AUTH_NET_ADDR */
+ "GSS-API crediential problem", /* 13 - RPCSEC_GSS_CREDPROBLEM */
+ "GSS-API context problem" /* 14 - RPCSEC_GSS_CTXPROBLEM */
};
static char *
diff --git a/lib/libc/rpc/clnt_vc.c b/lib/libc/rpc/clnt_vc.c
index 414eb0bcaa68..07eff46d4c4f 100644
--- a/lib/libc/rpc/clnt_vc.c
+++ b/lib/libc/rpc/clnt_vc.c
@@ -77,6 +77,7 @@ __FBSDID("$FreeBSD$");
#include <signal.h>
#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
#include "un-namespace.h"
#include "rpc_com.h"
#include "mt_misc.h"
@@ -285,6 +286,7 @@ clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
}
ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
XDR_DESTROY(&(ct->ct_xdrs));
+ assert(ct->ct_mpos + sizeof(uint32_t) <= MCALL_MSG_SIZE);
/*
* Create a client handle which uses xdrrec for serialization
@@ -331,6 +333,7 @@ clnt_vc_call(cl, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
int refreshes = 2;
sigset_t mask, newmask;
int rpc_lock_value;
+ bool_t reply_stat;
assert(cl != NULL);
@@ -360,15 +363,28 @@ call_again:
ct->ct_error.re_status = RPC_SUCCESS;
x_id = ntohl(--(*msg_x_id));
- if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) ||
- (! XDR_PUTINT32(xdrs, &proc)) ||
- (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
- (! (*xdr_args)(xdrs, args_ptr))) {
- if (ct->ct_error.re_status == RPC_SUCCESS)
- ct->ct_error.re_status = RPC_CANTENCODEARGS;
- (void)xdrrec_endofrecord(xdrs, TRUE);
- release_fd_lock(ct->ct_fd, mask);
- return (ct->ct_error.re_status);
+ if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
+ if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) ||
+ (! XDR_PUTINT32(xdrs, &proc)) ||
+ (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
+ (! (*xdr_args)(xdrs, args_ptr))) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+ release_fd_lock(ct->ct_fd, mask);
+ return (ct->ct_error.re_status);
+ }
+ } else {
+ *(uint32_t *) &ct->ct_u.ct_mcallc[ct->ct_mpos] = htonl(proc);
+ if (! __rpc_gss_wrap(cl->cl_auth, ct->ct_u.ct_mcallc,
+ ct->ct_mpos + sizeof(uint32_t),
+ xdrs, xdr_args, args_ptr)) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+ release_fd_lock(ct->ct_fd, mask);
+ return (ct->ct_error.re_status);
+ }
}
if (! xdrrec_endofrecord(xdrs, shipnow)) {
release_fd_lock(ct->ct_fd, mask);
@@ -419,9 +435,18 @@ call_again:
&reply_msg.acpted_rply.ar_verf)) {
ct->ct_error.re_status = RPC_AUTHERROR;
ct->ct_error.re_why = AUTH_INVALIDRESP;
- } else if (! (*xdr_results)(xdrs, results_ptr)) {
- if (ct->ct_error.re_status == RPC_SUCCESS)
- ct->ct_error.re_status = RPC_CANTDECODERES;
+ } else {
+ if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
+ reply_stat = (*xdr_results)(xdrs, results_ptr);
+ } else {
+ reply_stat = __rpc_gss_unwrap(cl->cl_auth,
+ xdrs, xdr_results, results_ptr);
+ }
+ if (! reply_stat) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status =
+ RPC_CANTDECODERES;
+ }
}
/* free verifier ... */
if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
diff --git a/lib/libc/rpc/rpcsec_gss_stub.c b/lib/libc/rpc/rpcsec_gss_stub.c
new file mode 100644
index 000000000000..55eb1065c0b0
--- /dev/null
+++ b/lib/libc/rpc/rpcsec_gss_stub.c
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2006 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+
+bool_t
+__rpc_gss_wrap_stub(AUTH *auth, void *header, size_t headerlen, XDR* xdrs,
+ xdrproc_t xdr_args, void *args_ptr)
+{
+
+ return (FALSE);
+}
+
+bool_t
+__rpc_gss_unwrap_stub(AUTH *auth, XDR* xdrs, xdrproc_t xdr_args, void *args_ptr)
+{
+
+ return (FALSE);
+}
+
+__weak_reference(__rpc_gss_wrap_stub, __rpc_gss_wrap);
+__weak_reference(__rpc_gss_unwrap_stub, __rpc_gss_unwrap);
diff --git a/lib/libc/rpc/svc.c b/lib/libc/rpc/svc.c
index b924bdec7847..f5654b1023ef 100644
--- a/lib/libc/rpc/svc.c
+++ b/lib/libc/rpc/svc.c
@@ -67,7 +67,7 @@ __FBSDID("$FreeBSD$");
#define RQCRED_SIZE 400 /* this size is excessive */
#define SVC_VERSQUIET 0x0001 /* keep quiet about vers mismatch */
-#define version_keepquiet(xp) ((u_long)(xp)->xp_p3 & SVC_VERSQUIET)
+#define version_keepquiet(xp) (SVC_EXT(xp)->xp_flags & SVC_VERSQUIET)
#define max(a, b) (a > b ? a : b)
@@ -452,20 +452,16 @@ void
__svc_versquiet_on(xprt)
SVCXPRT *xprt;
{
- u_long tmp;
- tmp = ((u_long) xprt->xp_p3) | SVC_VERSQUIET;
- xprt->xp_p3 = tmp;
+ SVC_EXT(xprt)->xp_flags |= SVC_VERSQUIET;
}
void
__svc_versquiet_off(xprt)
SVCXPRT *xprt;
{
- u_long tmp;
- tmp = ((u_long) xprt->xp_p3) & ~SVC_VERSQUIET;
- xprt->xp_p3 = tmp;
+ SVC_EXT(xprt)->xp_flags &= ~SVC_VERSQUIET;
}
void
@@ -479,7 +475,8 @@ int
__svc_versquiet_get(xprt)
SVCXPRT *xprt;
{
- return ((int) xprt->xp_p3) & SVC_VERSQUIET;
+
+ return (SVC_EXT(xprt)->xp_flags & SVC_VERSQUIET);
}
#endif
@@ -555,6 +552,39 @@ svcerr_progvers(xprt, low_vers, high_vers)
SVC_REPLY(xprt, &rply);
}
+/*
+ * Allocate a new server transport structure. All fields are
+ * initialized to zero and xp_p3 is initialized to point at an
+ * extension structure to hold various flags and authentication
+ * parameters.
+ */
+SVCXPRT *
+svc_xprt_alloc()
+{
+ SVCXPRT *xprt;
+ SVCXPRT_EXT *ext;
+
+ xprt = mem_alloc(sizeof(SVCXPRT));
+ memset(xprt, 0, sizeof(SVCXPRT));
+ ext = mem_alloc(sizeof(SVCXPRT_EXT));
+ memset(ext, 0, sizeof(SVCXPRT_EXT));
+ xprt->xp_p3 = ext;
+
+ return (xprt);
+}
+
+/*
+ * Free a server transport structure.
+ */
+void
+svc_xprt_free(xprt)
+ SVCXPRT *xprt;
+{
+
+ mem_free(xprt->xp_p3, sizeof(SVCXPRT_EXT));
+ mem_free(xprt, sizeof(SVCXPRT));
+}
+
/* ******************* SERVER INPUT STUFF ******************* */
/*
@@ -643,7 +673,15 @@ svc_getreq_common(fd)
r.rq_cred = msg.rm_call.cb_cred;
/* first authenticate the message */
if ((why = _authenticate(&r, &msg)) != AUTH_OK) {
- svcerr_auth(xprt, why);
+ /*
+ * RPCSEC_GSS uses this return code
+ * for requests that form part of its
+ * context establishment protocol and
+ * should not be dispatched to the
+ * application.
+ */
+ if (why != RPCSEC_GSS_NODISPATCH)
+ svcerr_auth(xprt, why);
goto call_done;
}
/* now match message with a registered service*/
@@ -670,7 +708,7 @@ svc_getreq_common(fd)
if (prog_found)
svcerr_progvers(xprt, low_vers, high_vers);
else
- svcerr_noprog(xprt);
+ svcerr_noprog(xprt);
/* Fall through to ... */
}
/*
diff --git a/lib/libc/rpc/svc_auth.c b/lib/libc/rpc/svc_auth.c
index eb1a5f2f4a93..4e27ccd29615 100644
--- a/lib/libc/rpc/svc_auth.c
+++ b/lib/libc/rpc/svc_auth.c
@@ -75,6 +75,8 @@ struct authsvc {
};
static struct authsvc *Auths = NULL;
+static struct svc_auth_ops svc_auth_null_ops;
+
/*
* The call rpc message, msg has been obtained from the wire. The msg contains
* the raw form of credentials and verifiers. authenticate returns AUTH_OK
@@ -105,6 +107,8 @@ _authenticate(rqst, msg)
/* VARIABLES PROTECTED BY authsvc_lock: asp, Auths */
rqst->rq_cred = msg->rm_call.cb_cred;
+ SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_null_ops;
+ SVC_AUTH(rqst->rq_xprt).svc_ah_private = NULL;
rqst->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor;
rqst->rq_xprt->xp_verf.oa_length = 0;
cred_flavor = rqst->rq_cred.oa_flavor;
@@ -143,6 +147,26 @@ _authenticate(rqst, msg)
return (AUTH_REJECTEDCRED);
}
+/*
+ * A set of null auth methods used by any authentication protocols
+ * that don't need to inspect or modify the message body.
+ */
+static bool_t
+svcauth_null_wrap(auth, xdrs, xdr_func, xdr_ptr)
+ SVCAUTH *auth;
+ XDR *xdrs;
+ xdrproc_t xdr_func;
+ caddr_t xdr_ptr;
+{
+
+ return (xdr_func(xdrs, xdr_ptr));
+}
+
+static struct svc_auth_ops svc_auth_null_ops = {
+ svcauth_null_wrap,
+ svcauth_null_wrap,
+};
+
/*ARGSUSED*/
enum auth_stat
_svcauth_null(rqst, msg)
diff --git a/lib/libc/rpc/svc_dg.c b/lib/libc/rpc/svc_dg.c
index 1c602bc57ed2..51facd255979 100644
--- a/lib/libc/rpc/svc_dg.c
+++ b/lib/libc/rpc/svc_dg.c
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <rpc/rpc.h>
#include <rpc/svc_dg.h>
+#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
@@ -125,10 +126,9 @@ svc_dg_create(fd, sendsize, recvsize)
return (NULL);
}
- xprt = mem_alloc(sizeof (SVCXPRT));
+ xprt = svc_xprt_alloc();
if (xprt == NULL)
goto freedata;
- memset(xprt, 0, sizeof (SVCXPRT));
su = mem_alloc(sizeof (*su));
if (su == NULL)
@@ -160,7 +160,7 @@ freedata:
if (xprt) {
if (su)
(void) mem_free(su, sizeof (*su));
- (void) mem_free(xprt, sizeof (SVCXPRT));
+ svc_xprt_free(xprt);
}
return (NULL);
}
@@ -230,13 +230,28 @@ svc_dg_reply(xprt, msg)
{
struct svc_dg_data *su = su_data(xprt);
XDR *xdrs = &(su->su_xdrs);
- bool_t stat = FALSE;
+ bool_t stat = TRUE;
size_t slen;
+ xdrproc_t xdr_proc;
+ caddr_t xdr_where;
xdrs->x_op = XDR_ENCODE;
XDR_SETPOS(xdrs, 0);
msg->rm_xid = su->su_xid;
- if (xdr_replymsg(xdrs, msg)) {
+ if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
+ msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
+ xdr_proc = msg->acpted_rply.ar_results.proc;
+ xdr_where = msg->acpted_rply.ar_results.where;
+ msg->acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
+ msg->acpted_rply.ar_results.where = NULL;
+
+ if (!xdr_replymsg(xdrs, msg) ||
+ !SVCAUTH_WRAP(&SVC_AUTH(xprt), xdrs, xdr_proc, xdr_where))
+ stat = FALSE;
+ } else {
+ stat = xdr_replymsg(xdrs, msg);
+ }
+ if (stat) {
slen = XDR_GETPOS(xdrs);
if (_sendto(xprt->xp_fd, rpc_buffer(xprt), slen, 0,
(struct sockaddr *)xprt->xp_rtaddr.buf,
@@ -255,7 +270,12 @@ svc_dg_getargs(xprt, xdr_args, args_ptr)
xdrproc_t xdr_args;
void *args_ptr;
{
- return (*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr);
+ struct svc_dg_data *su;
+
+ assert(xprt != NULL);
+ su = su_data(xprt);
+ return (SVCAUTH_UNWRAP(&SVC_AUTH(xprt),
+ &su->su_xdrs, xdr_args, args_ptr));
}
static bool_t
@@ -288,7 +308,7 @@ svc_dg_destroy(xprt)
(void) mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen);
if (xprt->xp_tp)
(void) free(xprt->xp_tp);
- (void) mem_free(xprt, sizeof (SVCXPRT));
+ svc_xprt_free(xprt);
}
static bool_t
diff --git a/lib/libc/rpc/svc_raw.c b/lib/libc/rpc/svc_raw.c
index 32d1ff765b23..7492046a8888 100644
--- a/lib/libc/rpc/svc_raw.c
+++ b/lib/libc/rpc/svc_raw.c
@@ -66,7 +66,7 @@ __FBSDID("$FreeBSD$");
*/
static struct svc_raw_private {
char *raw_buf; /* should be shared with the cl handle */
- SVCXPRT server;
+ SVCXPRT *server;
XDR xdr_stream;
char verf_body[MAX_AUTH_BYTES];
} *svc_raw_private;
@@ -99,17 +99,17 @@ svc_raw_create()
if (__rpc_rawcombuf == NULL)
__rpc_rawcombuf = calloc(UDPMSGSIZE, sizeof (char));
srp->raw_buf = __rpc_rawcombuf; /* Share it with the client */
+ srp->server = svc_xprt_alloc();
svc_raw_private = srp;
}
- srp->server.xp_fd = FD_SETSIZE;
- srp->server.xp_port = 0;
- srp->server.xp_p3 = NULL;
- svc_raw_ops(&srp->server);
- srp->server.xp_verf.oa_base = srp->verf_body;
+ srp->server->xp_fd = FD_SETSIZE;
+ srp->server->xp_port = 0;
+ svc_raw_ops(srp->server);
+ srp->server->xp_verf.oa_base = srp->verf_body;
xdrmem_create(&srp->xdr_stream, srp->raw_buf, UDPMSGSIZE, XDR_DECODE);
- xprt_register(&srp->server);
+ xprt_register(srp->server);
mutex_unlock(&svcraw_lock);
- return (&srp->server);
+ return (srp->server);
}
/*ARGSUSED*/
@@ -154,6 +154,9 @@ svc_raw_reply(xprt, msg)
{
struct svc_raw_private *srp;
XDR *xdrs;
+ bool_t stat;
+ xdrproc_t xdr_proc;
+ caddr_t xdr_where;
mutex_lock(&svcraw_lock);
srp = svc_raw_private;
@@ -166,7 +169,20 @@ svc_raw_reply(xprt, msg)
xdrs = &srp->xdr_stream;
xdrs->x_op = XDR_ENCODE;
(void) XDR_SETPOS(xdrs, 0);
- if (! xdr_replymsg(xdrs, msg)) {
+ if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
+ msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
+ xdr_proc = msg->acpted_rply.ar_results.proc;
+ xdr_where = msg->acpted_rply.ar_results.where;
+ msg->acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
+ msg->acpted_rply.ar_results.where = NULL;
+
+ if (!xdr_replymsg(xdrs, msg) ||
+ !SVCAUTH_WRAP(&SVC_AUTH(xprt), xdrs, xdr_proc, xdr_where))
+ stat = FALSE;
+ } else {
+ stat = xdr_replymsg(xdrs, msg);
+ }
+ if (!stat) {
return (FALSE);
}
(void) XDR_GETPOS(xdrs); /* called just for overhead */
@@ -189,7 +205,9 @@ svc_raw_getargs(xprt, xdr_args, args_ptr)
return (FALSE);
}
mutex_unlock(&svcraw_lock);
- return (*xdr_args)(&srp->xdr_stream, args_ptr);
+
+ return (SVCAUTH_UNWRAP(&SVC_AUTH(xprt), &srp->xdr_stream,
+ xdr_args, args_ptr));
}
/*ARGSUSED*/
diff --git a/lib/libc/rpc/svc_vc.c b/lib/libc/rpc/svc_vc.c
index 6370aadaa6e7..3fd8b7e2ddb4 100644
--- a/lib/libc/rpc/svc_vc.c
+++ b/lib/libc/rpc/svc_vc.c
@@ -146,15 +146,12 @@ svc_vc_create(fd, sendsize, recvsize)
r->sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsize);
r->recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsize);
r->maxrec = __svc_maxrec;
- xprt = mem_alloc(sizeof(SVCXPRT));
+ xprt = svc_xprt_alloc();
if (xprt == NULL) {
warnx("svc_vc_create: out of memory");
goto cleanup_svc_vc_create;
}
- xprt->xp_tp = NULL;
xprt->xp_p1 = r;
- xprt->xp_p2 = NULL;
- xprt->xp_p3 = NULL;
xprt->xp_verf = _null_auth;
svc_vc_rendezvous_ops(xprt);
xprt->xp_port = (u_short)-1; /* It is the rendezvouser */
@@ -259,16 +256,15 @@ makefd_xprt(fd, sendsize, recvsize)
assert(fd != -1);
- xprt = mem_alloc(sizeof(SVCXPRT));
+ xprt = svc_xprt_alloc();
if (xprt == NULL) {
warnx("svc_vc: makefd_xprt: out of memory");
goto done;
}
- memset(xprt, 0, sizeof *xprt);
cd = mem_alloc(sizeof(struct cf_conn));
if (cd == NULL) {
warnx("svc_tcp: makefd_xprt: out of memory");
- mem_free(xprt, sizeof(SVCXPRT));
+ svc_xprt_free(xprt);
xprt = NULL;
goto done;
}
@@ -417,7 +413,7 @@ __svc_vc_dodestroy(xprt)
free(xprt->xp_tp);
if (xprt->xp_netid)
free(xprt->xp_netid);
- mem_free(xprt, sizeof(SVCXPRT));
+ svc_xprt_free(xprt);
}
/*ARGSUSED*/
@@ -623,11 +619,12 @@ svc_vc_getargs(xprt, xdr_args, args_ptr)
xdrproc_t xdr_args;
void *args_ptr;
{
+ struct cf_conn *cd;
assert(xprt != NULL);
- /* args_ptr may be NULL */
- return ((*xdr_args)(&(((struct cf_conn *)(xprt->xp_p1))->xdrs),
- args_ptr));
+ cd = (struct cf_conn *)(xprt->xp_p1);
+ return (SVCAUTH_UNWRAP(&SVC_AUTH(xprt),
+ &cd->xdrs, xdr_args, args_ptr));
}
static bool_t
@@ -655,6 +652,9 @@ svc_vc_reply(xprt, msg)
struct cf_conn *cd;
XDR *xdrs;
bool_t rstat;
+ xdrproc_t xdr_proc;
+ caddr_t xdr_where;
+ u_int pos;
assert(xprt != NULL);
assert(msg != NULL);
@@ -664,8 +664,27 @@ svc_vc_reply(xprt, msg)
xdrs->x_op = XDR_ENCODE;
msg->rm_xid = cd->x_id;
- rstat = xdr_replymsg(xdrs, msg);
- (void)xdrrec_endofrecord(xdrs, TRUE);
+ rstat = TRUE;
+ if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
+ msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
+ xdr_proc = msg->acpted_rply.ar_results.proc;
+ xdr_where = msg->acpted_rply.ar_results.where;
+ msg->acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
+ msg->acpted_rply.ar_results.where = NULL;
+
+ pos = XDR_GETPOS(xdrs);
+ if (!xdr_replymsg(xdrs, msg) ||
+ !SVCAUTH_WRAP(&SVC_AUTH(xprt), xdrs, xdr_proc, xdr_where)) {
+ XDR_SETPOS(xdrs, pos);
+ rstat = FALSE;
+ }
+ } else {
+ rstat = xdr_replymsg(xdrs, msg);
+ }
+
+ if (rstat)
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+
return (rstat);
}
diff --git a/lib/libc/xdr/xdr_rec.c b/lib/libc/xdr/xdr_rec.c
index 34238ed01321..dc4aa18f14bf 100644
--- a/lib/libc/xdr/xdr_rec.c
+++ b/lib/libc/xdr/xdr_rec.c
@@ -338,21 +338,22 @@ xdrrec_getpos(xdrs)
off_t pos;
pos = lseek((int)(u_long)rstrm->tcp_handle, (off_t)0, 1);
- if (pos != -1)
- switch (xdrs->x_op) {
+ if (pos == -1)
+ pos = 0;
+ switch (xdrs->x_op) {
- case XDR_ENCODE:
- pos += rstrm->out_finger - rstrm->out_base;
- break;
+ case XDR_ENCODE:
+ pos += rstrm->out_finger - rstrm->out_base;
+ break;
- case XDR_DECODE:
- pos -= rstrm->in_boundry - rstrm->in_finger;
- break;
+ case XDR_DECODE:
+ pos -= rstrm->in_boundry - rstrm->in_finger;
+ break;
- default:
- pos = (off_t) -1;
- break;
- }
+ default:
+ pos = (off_t) -1;
+ break;
+ }
return ((u_int) pos);
}
diff --git a/lib/libgssapi/Makefile b/lib/libgssapi/Makefile
index 5d4fef39e3ec..63d441c34b93 100644
--- a/lib/libgssapi/Makefile
+++ b/lib/libgssapi/Makefile
@@ -40,6 +40,7 @@ SRCS+= gss_inquire_sec_context_by_oid.c
SRCS+= gss_mech_switch.c
SRCS+= gss_names.c
SRCS+= gss_oid_to_str.c
+SRCS+= gss_pname_to_uid.c
SRCS+= gss_process_context_token.c
SRCS+= gss_pseudo_random.c
SRCS+= gss_release_buffer.c
diff --git a/lib/libgssapi/Symbol.map b/lib/libgssapi/Symbol.map
index d2746da3653d..6df8f0c4716d 100644
--- a/lib/libgssapi/Symbol.map
+++ b/lib/libgssapi/Symbol.map
@@ -47,6 +47,7 @@ FBSD_1.1 {
gss_inquire_sec_context_by_oid;
gss_oid_equal;
gss_oid_to_str;
+ gss_pname_to_uid;
gss_process_context_token;
gss_pseudo_random;
gss_release_buffer;
@@ -67,3 +68,9 @@ FBSD_1.1 {
gss_wrap;
gss_wrap_size_limit;
};
+
+FBSDprivate_1.0 {
+ _gss_copy_oid;
+ _gss_copy_buffer;
+ _gss_free_oid;
+};
diff --git a/lib/libgssapi/gss_mech_switch.c b/lib/libgssapi/gss_mech_switch.c
index f5808c5e411b..feb88f1a68c8 100644
--- a/lib/libgssapi/gss_mech_switch.c
+++ b/lib/libgssapi/gss_mech_switch.c
@@ -285,6 +285,7 @@ _gss_load_mech(void)
OPTSYM(set_sec_context_option);
OPTSYM(set_cred_option);
OPTSYM(pseudo_random);
+ OPTSYM(pname_to_uid);
SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link);
count++;
diff --git a/lib/libgssapi/gss_pname_to_uid.c b/lib/libgssapi/gss_pname_to_uid.c
new file mode 100644
index 000000000000..eb560b9e4248
--- /dev/null
+++ b/lib/libgssapi/gss_pname_to_uid.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* $FreeBSD$ */
+
+#include <unistd.h>
+#include <gssapi/gssapi.h>
+
+#include "mech_switch.h"
+#include "name.h"
+#include "utils.h"
+
+OM_uint32
+gss_pname_to_uid(OM_uint32 *minor_status, const gss_name_t pname,
+ const gss_OID mech, uid_t *uidp)
+{
+ struct _gss_name *name = (struct _gss_name *) pname;
+ struct _gss_mech_switch *m;
+ struct _gss_mechanism_name *mn;
+ OM_uint32 major_status;
+
+ *minor_status = 0;
+
+ if (pname == GSS_C_NO_NAME)
+ return (GSS_S_BAD_NAME);
+
+ m = _gss_find_mech_switch(mech);
+ if (!m)
+ return (GSS_S_BAD_MECH);
+
+ if (m->gm_pname_to_uid == NULL)
+ return (GSS_S_UNAVAILABLE);
+
+ major_status = _gss_find_mn(minor_status, name, mech, &mn);
+ if (major_status != GSS_S_COMPLETE) {
+ _gss_mg_error(m, major_status, *minor_status);
+ return (major_status);
+ }
+
+ major_status = (*m->gm_pname_to_uid)(minor_status, mn->gmn_name,
+ mech, uidp);
+ if (major_status != GSS_S_COMPLETE)
+ _gss_mg_error(m, major_status, *minor_status);
+
+ return (major_status);
+}
diff --git a/lib/libgssapi/gss_utils.c b/lib/libgssapi/gss_utils.c
index ec582a9bd1a4..992908bea949 100644
--- a/lib/libgssapi/gss_utils.c
+++ b/lib/libgssapi/gss_utils.c
@@ -66,6 +66,18 @@ _gss_copy_oid(OM_uint32 *minor_status,
return (GSS_S_COMPLETE);
}
+OM_uint32
+_gss_free_oid(OM_uint32 *minor_status, gss_OID oid)
+{
+
+ *minor_status = 0;
+ if (oid->elements) {
+ free(oid->elements);
+ oid->elements = NULL;
+ oid->length = 0;
+ }
+ return (GSS_S_COMPLETE);
+}
OM_uint32
_gss_copy_buffer(OM_uint32 *minor_status,
diff --git a/lib/libgssapi/mech_switch.h b/lib/libgssapi/mech_switch.h
index 876adcda9e64..99e254e73b6f 100644
--- a/lib/libgssapi/mech_switch.h
+++ b/lib/libgssapi/mech_switch.h
@@ -304,6 +304,13 @@ typedef OM_uint32 _gss_pseudo_random
gss_buffer_t /* PRF output */
);
+typedef OM_uint32 _gss_pname_to_uid
+ (OM_uint32 *, /* minor status */
+ gss_name_t pname, /* principal name */
+ gss_OID mech, /* mechanism to query */
+ uid_t *uidp /* pointer to UID for result */
+ );
+
struct _gss_mech_switch {
SLIST_ENTRY(_gss_mech_switch) gm_link;
const char *gm_name_prefix;
@@ -343,6 +350,7 @@ struct _gss_mech_switch {
_gss_set_sec_context_option *gm_set_sec_context_option;
_gss_set_cred_option *gm_set_cred_option;
_gss_pseudo_random *gm_pseudo_random;
+ _gss_pname_to_uid *gm_pname_to_uid;
};
SLIST_HEAD(_gss_mech_switch_list, _gss_mech_switch);
extern struct _gss_mech_switch_list _gss_mechs;
diff --git a/lib/libgssapi/utils.h b/lib/libgssapi/utils.h
index a54cb5089c03..347b58358f3a 100644
--- a/lib/libgssapi/utils.h
+++ b/lib/libgssapi/utils.h
@@ -30,5 +30,6 @@
do { (buffer)->value = NULL; (buffer)->length = 0; } while(0)
extern int _gss_oid_equal(const gss_OID, const gss_OID);
extern OM_uint32 _gss_copy_oid(OM_uint32 *, const gss_OID, gss_OID);
+extern OM_uint32 _gss_free_oid(OM_uint32 *, gss_OID);
extern OM_uint32 _gss_copy_buffer(OM_uint32 *minor_status,
const gss_buffer_t from_buf, gss_buffer_t to_buf);
diff --git a/lib/librpcsec_gss/Makefile b/lib/librpcsec_gss/Makefile
new file mode 100644
index 000000000000..90f7bd7210d9
--- /dev/null
+++ b/lib/librpcsec_gss/Makefile
@@ -0,0 +1,39 @@
+# $FreeBSD$
+
+LIB= rpcsec_gss
+SHLIB_MAJOR= 1
+WARNS?= 6
+SRCS+= rpcsec_gss.c rpcsec_gss_prot.c rpcsec_gss_conf.c rpcsec_gss_misc.c \
+ svc_rpcsec_gss.c
+
+DPADD+= ${LIBGSSAPI}
+LDADD+= -lgssapi
+
+VERSION_DEF= ${.CURDIR}/../libc/Versions.def
+SYMBOL_MAPS= ${.CURDIR}/Symbol.map
+
+CFLAGS+= -I${.CURDIR}/../../include
+CFLAGS+= -I${.CURDIR}/../../libc_rpc
+NO_PROFILE=
+
+MAN= rpcsec_gss.3
+MAN+= rpc_gss_seccreate.3
+MAN+= rpc_gss_set_defaults.3
+MAN+= rpc_gss_max_data_length.3
+MAN+= rpc_gss_get_error.3
+
+MAN+= rpc_gss_mech_to_oid.3
+MAN+= rpc_gss_oid_to_mech.3
+MAN+= rpc_gss_qop_to_num.3
+MAN+= rpc_gss_get_mechanisms.3
+MAN+= rpc_gss_get_mech_info.3
+MAN+= rpc_gss_get_versions.3
+MAN+= rpc_gss_is_installed.3
+
+MAN+= rpc_gss_set_svc_name.3
+MAN+= rpc_gss_getcred.3
+MAN+= rpc_gss_set_callback.3
+MAN+= rpc_gss_get_principal_name.3
+MAN+= rpc_gss_svc_max_data_length.3
+
+.include <bsd.lib.mk>
diff --git a/lib/librpcsec_gss/Symbol.map b/lib/librpcsec_gss/Symbol.map
new file mode 100644
index 000000000000..7f69c98e8af4
--- /dev/null
+++ b/lib/librpcsec_gss/Symbol.map
@@ -0,0 +1,28 @@
+/*
+ * $FreeBSD$
+ */
+FBSD_1.1 {
+ rpc_gss_seccreate;
+ rpc_gss_set_defaults;
+ rpc_gss_max_data_length;
+ rpc_gss_get_error;
+ rpc_gss_mesh_to_oid;
+ rpc_gss_oid_to_mech;
+ rpc_gss_qop_to_num;
+ rpc_gss_get_mechanisms;
+ rpc_gss_get_mech_info;
+ rpc_gss_get_versions;
+ rpc_gss_is_installed;
+ rpc_gss_set_svc_name;
+ rpc_gss_getcred;
+ rpc_gss_set_callback;
+ rpc_gss_get_principal_name;
+ rpc_gss_svc_max_data_length;
+};
+
+FBSDprivate_1.0 {
+ __rpc_gss_unwrap;
+ __rpc_gss_unwrap_stub;
+ __rpc_gss_wrap;
+ __rpc_gss_wrap_stub;
+};
diff --git a/lib/librpcsec_gss/rpc_gss_get_error.3 b/lib/librpcsec_gss/rpc_gss_get_error.3
new file mode 100644
index 000000000000..e108766322f9
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_get_error.3
@@ -0,0 +1,58 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_GET_ERROR 3
+.Os
+.Sh NAME
+.Nm rpc_gss_get_error
+.Nd "Get error details"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft void
+.Fn rpc_gss_get_error "rpc_gss_error_t *error"
+.Sh DESCRIPTION
+Get details of the last RPCSEC_GSS error.
+.Sh PARAMETERS
+.Bl -tag
+.It error
+A pointer to a structure where the error details will be returned
+.El
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_get_mech_info.3 b/lib/librpcsec_gss/rpc_gss_get_mech_info.3
new file mode 100644
index 000000000000..a7f7d6b4d46b
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_get_mech_info.3
@@ -0,0 +1,68 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_GET_MECH_INFO 3
+.Os
+.Sh NAME
+.Nm rpc_gss_get_mech_info
+.Nd "Get extra information about a security mechanism"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft const char **
+.Fn rpc_gss_get_mech_info "const char *mech" "rpc_gss_service_t *service"
+.Sh DESCRIPTION
+This function looks up a mechanism by name by reading the file
+/etc/gss/mech and queries it for its capabilities.
+.Sh PARAMETERS
+.Bl -tag
+.It mech
+The mechanism to search for
+.It service
+If the mechanism is found, the maximum supported service type is
+returned in
+.Fa *service
+.El
+.Sh RETURN VALUES
+If the mechanism is found,
+a list of the supported qualities of protection is returned,
+otherwise
+.Dv NULL .
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_get_mechanisms.3 b/lib/librpcsec_gss/rpc_gss_get_mechanisms.3
new file mode 100644
index 000000000000..4b57ac6fe58a
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_get_mechanisms.3
@@ -0,0 +1,55 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_GET_MECHANISMS 3
+.Os
+.Sh NAME
+.Nm rpc_gss_get_mechanisms
+.Nd "Get installed mechanisms"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft const char **
+.Fn rpc_gss_get_mechanisms "void"
+.Sh DESCRIPTION
+Return a
+.Dv NULL
+terminated list of installed security mechanisms.
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_get_principal_name.3 b/lib/librpcsec_gss/rpc_gss_get_principal_name.3
new file mode 100644
index 000000000000..6f5721259968
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_get_principal_name.3
@@ -0,0 +1,82 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_GET_PRINCIPAL_NAME 3
+.Os
+.Sh NAME
+.Nm rpc_gss_get_principal_name
+.Nd "Get a principal name"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft bool_t
+.Fo rpc_gss_get_principal_name
+.Fa "rpc_gss_principal_t *principal"
+.Fa "const char *mech"
+.Fa "const char *name"
+.Fa "const char *node"
+.Fa "const char *domain"
+.Fc
+.Sh DESCRIPTION
+This function can be used to generate a client principal name from
+various strings.
+.Sh PARAMETERS
+.Bl -tag
+.It principal
+If the principal is created successfully,
+.Fa *principal
+will be set to point at the new principal in GSS-API exported name form
+.It mech
+The name of the mechanism for this principal
+.It name
+The name part of the principal
+.It node
+If non-null, the hostname or instance part of the principal
+.It domain
+If non-null, the domain or realm part of the principal
+.El
+.Sh RETURN VALUES
+Returns
+.Dv TRUE
+if the principal was created or
+.Dv FALSE
+otherwise
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr gss_export_name 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_get_versions.3 b/lib/librpcsec_gss/rpc_gss_get_versions.3
new file mode 100644
index 000000000000..f82406677be0
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_get_versions.3
@@ -0,0 +1,64 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_GET_VERSIONS 3
+.Os
+.Sh NAME
+.Nm rpc_gss_get_versions
+.Nd "Get supported protocol version"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft bool_t
+.Fn rpc_gss_get_versions "u_int *vers_hi" "u_int *vers_lo"
+.Sh DESCRIPTION
+Return the highest and lowest supported versions of the RPCSEC_GSS protocol.
+.Sh PARAMETERS
+.Bl -tag
+.It vers_hi
+The value of
+.Fa *vers_hi
+is set to the highest suppored protocol version
+.It vers_lo
+The value of
+.Fa *vers_lo
+is set to the lowest suppored protocol version
+.El
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_getcred.3 b/lib/librpcsec_gss/rpc_gss_getcred.3
new file mode 100644
index 000000000000..2ede33e6ceb7
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_getcred.3
@@ -0,0 +1,85 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_GETCRED 3
+.Os
+.Sh NAME
+.Nm rpc_gss_getcred
+.Nd "Get authorization information for an RPC request"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft AUTH *
+.Fo rpc_gss_getcred
+.Fa "struct svc_req *req"
+.Fa "rpc_gss_rawcred_t **rcred"
+.Fa "rpc_gss_ucred_t **ucred"
+.Fa "void **cookie"
+.Fc
+.Sh DESCRIPTION
+This function returns the RPCSEC_GSS authenticated credentials
+associated with an RPC request.
+.Sh PARAMETERS
+.Bl -tag
+.It req
+The RPC request to query
+.It rcred
+If non-null,
+.Fa *rcred
+is set to point at the raw credentials for this request
+.It ucred
+.It rcred
+If non-null,
+.Fa *ucred
+is set to point at the corresponding unix credentials
+.It cookie
+If non-null,
+.Fa *cookie
+is set to the cookie value returned by a callback function registered with
+.Fn rpc_gss_set_callback
+.El
+.Sh RETURN VALUES
+Returns
+.Dv TRUE
+if successful,
+.Dv FALSE
+otherwise.
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpc_gss_set_callback 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_is_installed.3 b/lib/librpcsec_gss/rpc_gss_is_installed.3
new file mode 100644
index 000000000000..2859ed2e691b
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_is_installed.3
@@ -0,0 +1,65 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_IS_INSTALLED 3
+.Os
+.Sh NAME
+.Nm rpc_gss_is_installed
+.Nd "Query for the presence os a security mechanism"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft bool_t
+.Fn rpc_gss_is_installed "const char *mech"
+.Sh DESCRIPTION
+This function looks up a mechanism by name by reading the file
+/etc/gss/mech.
+.Sh PARAMETERS
+.Bl -tag
+.It mech
+The mechanism to search for
+.El
+.Sh RETURN VALUES
+Returns
+.Dv TRUE
+if the mechanism is installed,
+.Dv FALSE
+otherwise.
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_max_data_length.3 b/lib/librpcsec_gss/rpc_gss_max_data_length.3
new file mode 100644
index 000000000000..895711921052
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_max_data_length.3
@@ -0,0 +1,64 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_MAX_DATA_LENGTH 3
+.Os
+.Sh NAME
+.Nm rpc_gss_max_data_length
+.Nd "calculate maximum data size"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft int
+.Fn rpc_gss_max_data_length "AUTH *auth" "int max_tp_unit_len"
+.Sh DESCRIPTION
+Calculate the maximum message size that will fit into a packet of size
+.Fa max_tp_unit_len ,
+given the current service and QoP setting.
+.Sh PARAMETERS
+.Bl -tag
+.It auth
+A handle to a RPCSEC_GSS security ccontext
+.It max_tp_unit_len
+Maximum packet size of the underlying transport protocol
+.El
+.Sh RETURN VALUES
+The maximum message size that can be encoded
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_mech_to_oid.3 b/lib/librpcsec_gss/rpc_gss_mech_to_oid.3
new file mode 100644
index 000000000000..c1f9f2a32f88
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_mech_to_oid.3
@@ -0,0 +1,68 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_MECH_TO_OID 3
+.Os
+.Sh NAME
+.Nm rpc_gss_mech_to_oid
+.Nd "Convert a mechanism name to a GSS-API oid"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft bool_t
+.Fn rpc_gss_mech_to_oid "const char *mech" "gss_OID *oid_ret"
+.Sh DESCRIPTION
+This function looks up a mechanism by name by reading the file
+/etc/gss/mech.
+.Sh PARAMETERS
+.Bl -tag
+.It mech
+The mechanism name to search for
+.It oid_ret
+If the mechanism is found, the corresponding GSS-API oid is returned
+in
+.Fa *oid_ret
+.El
+.Sh RETURN VALUES
+If the mechanism is found,
+.Dv TRUE
+is returned, otherwise
+.Dv FALSE .
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_oid_to_mech.3 b/lib/librpcsec_gss/rpc_gss_oid_to_mech.3
new file mode 100644
index 000000000000..6183f26076e4
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_oid_to_mech.3
@@ -0,0 +1,68 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_OID_TO_MECH 3
+.Os
+.Sh NAME
+.Nm rpc_gss_oid_to_mech
+.Nd "Convert a mechanism name to a GSS-API oid"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft bool_t
+.Fn rpc_gss_oid_to_mech "gss_OID oid" "const char **mech_ret"
+.Sh DESCRIPTION
+This function looks up a mechanism by oid by reading the file
+/etc/gss/mech.
+.Sh PARAMETERS
+.Bl -tag
+.It oid
+The mechanism oid to search for
+.It mech_ret
+If the mechanism is found, the corresponding mechanism name is returned
+in
+.Fa *mech_ret
+.El
+.Sh RETURN VALUES
+If the mechanism is found,
+.Dv TRUE
+is returned, otherwise
+.Dv FALSE .
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_qop_to_num.3 b/lib/librpcsec_gss/rpc_gss_qop_to_num.3
new file mode 100644
index 000000000000..e58d553e149a
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_qop_to_num.3
@@ -0,0 +1,70 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_QOP_TO_NUM 3
+.Os
+.Sh NAME
+.Nm rpc_gss_qop_to_num
+.Nd "Convert a quality of protection name to number"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft bool_t
+.Fn rpc_gss_qop_to_num "const char *qop" "const char *mech" "u_int *num_ret"
+.Sh DESCRIPTION
+This function looks up a quality of protection by name by reading the file
+/etc/gss/qop.
+.Sh PARAMETERS
+.Bl -tag
+.It qop
+The quality of protection to search for
+.It mech
+The mechanism name to search for
+.It number_ret
+If the quality of protection is found, the corresponding number is
+returned in
+.Fa *num_ret
+.El
+.Sh RETURN VALUES
+If the value is found,
+.Dv TRUE
+is returned, otherwise
+.Dv FALSE .
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_seccreate.3 b/lib/librpcsec_gss/rpc_gss_seccreate.3
new file mode 100644
index 000000000000..0f7dfabb4d8b
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_seccreate.3
@@ -0,0 +1,112 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_SECCREATE 3
+.Os
+.Sh NAME
+.Nm rpc_gss_seccreate
+.Nd "create a security context using the RPCSEC_GSS protocol"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft AUTH *
+.Fo rpc_gss_seccreate
+.Fa "CLIENT *clnt"
+.Fa "const char *principal"
+.Fa "const char *mechanism"
+.Fa "rpc_gss_service_t service"
+.Fa "const char *qop"
+.Fa "rpc_gss_options_req_t *options_req"
+.Fa "rpc_gss_options_ret_t *options_ret"
+.Fc
+.Sh DESCRIPTION
+This function is used to establish a security context between an
+application and a remote peer using the RPSEC_GSS protocol.
+.Sh PARAMETERS
+.Bl -tag
+.It clnt
+An RPC handle which is connected to the remote peer
+.It principal
+The name of the service principal on the remote peer.
+For instance, a principal such as
+.Qq nfs@server.example.com
+might be used by an application which needs to contact an NFS server
+.It mechanism
+The desired mechanism for this security context.
+The value of mechanism should be the name of one of the security
+mechanisms listed in /etc/gss/mech.
+.It service
+Type of service requested.
+.Bl -tag
+.It rpc_gss_svc_default
+The default - typically the same as
+.Dv rpc_gss_svc_none .
+.It rpc_gss_svc_none
+RPC headers only are integrity protected by a checksum.
+.It rpc_gss_svc_integrity
+RPC headers and data are integrity protected by a checksum.
+.It rpc_gss_svc_privacy
+RPC headers are integrity protected by a checksum and data is encrypted.
+.El
+.It qop
+Desired quality of protection or NULL for the default.
+Available values are lised in /etc/gss/qop
+.It options_req
+Extra security context options to be passed to the underlying GSS-API
+mechanism.
+Pass
+.Dv NULL
+to supply default values.
+.It options_ret
+Various values returned by the underlying GSS-API mechanism.
+Pass
+.Dv NULL
+if these values are not required.
+.El
+.Sh RETURN VALUES
+If the security context was created successfully, a pointer to an
+.Vt AUTH
+structure that represents the context is returned.
+To use this security context for subsequent RPC calls, set
+.Va clnt->cl_auth
+to this value.
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr mech 5 ,
+.Xr qop 5 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_set_callback.3 b/lib/librpcsec_gss/rpc_gss_set_callback.3
new file mode 100644
index 000000000000..0ad861f89df7
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_set_callback.3
@@ -0,0 +1,115 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_SET_CALLBACK 3
+.Os
+.Sh NAME
+.Nm rpc_gss_set_callback
+.Nd "Register a security context creation callback"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft bool_t
+.Fo (*callback)
+.Fa "struct svc_req *req"
+.Fa "gss_cred_id_t deleg"
+.Fa "gss_ctx_id_t gss_context"
+.Fa "rpc_gss_lock_t *lock"
+.Fa "void **cookie"
+.Fc
+.Ft bool_t
+.Fn rpc_gss_set_callback "rpc_gss_callback_t *cb"
+.Sh DESCRIPTION
+Register a function which will be called when new security contexts
+are created on a server.
+This function will be called on the first RPC request which uses that
+context and has the opportunity of rejecting the request (for instance
+after matching the request credentials to an access control list).
+To accept the new security context, the callback should return
+.Dv TRUE ,
+otherwise
+.Dv FALSE .
+If the callback accepts a context, it becomes responsible for the
+lifetime of the delegated client credentials (if any).
+.Pp
+It is also possible to 'lock' the values of service and quality of
+protection used by the context.
+If a context is locked, any subsequent requests which use different
+values for service and quality of protection will be rejected.
+.Sh PARAMETERS
+.Bl -tag
+.It cb
+A structure containing the RPC program and version for this callback
+and a function which will be called when new contexts are created for
+ths given RPC program and version
+.It req
+The RPC request using the new context
+.It deleg
+GSS-APi delegated credentials (if any)
+.It gss_context
+The GSS-API context
+.It lock
+A structure used to enforce a particular QOP and service. Set
+.Fa lock->locked
+to
+.Dv TRUE
+to lock the service and QOP values
+.It cookie
+The callback function may set
+.Fa *cookie
+to any pointer sized value.
+This value can be accessed during the lifetime of the context via
+.Fn rpc_gss_getcred .
+.El
+.Sh RETURN VALUES
+Returns
+.Dv TRUE
+if the callback was registered successfully or
+.Dv FALSE
+otherwise
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpc_gss_getcred 3
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
+.Sh BUGS
+There is no mechanism for informing a server when a security context
+has been deleted.
+This makes it difficult to allocate resources (e.g. to return via the
+callback's
+.Fa cookie
+argument).
diff --git a/lib/librpcsec_gss/rpc_gss_set_defaults.3 b/lib/librpcsec_gss/rpc_gss_set_defaults.3
new file mode 100644
index 000000000000..241cf9ecf647
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_set_defaults.3
@@ -0,0 +1,70 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_SET_DEFAULTS 3
+.Os
+.Sh NAME
+.Nm rpc_gss_set_defaults
+.Nd "set service and quality of protection"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft bool_t
+.Fo rpc_gss_set_defaults
+.Fa "AUTH *auth"
+.Fa "rpc_gss_service_t service"
+.Fa "const char *qop"
+.Fc
+.Sh DESCRIPTION
+Set the service and quality of protection to be used for RPC requests.
+The new values apply for the rest of the lifetime of the context
+(unless changed again with this function).
+.Sh PARAMETERS
+.Bl -tag
+.It service
+The service type to use for subsequent RPC requests
+.It qop
+The quality of protection to use or NULL for the default
+.El
+.Sh RETURN VALUES
+Returns
+.Dv TRUE
+if the values were set
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_set_svc_name.3 b/lib/librpcsec_gss/rpc_gss_set_svc_name.3
new file mode 100644
index 000000000000..9be8fdffae89
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_set_svc_name.3
@@ -0,0 +1,87 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_SET_SVC_NAME 3
+.Os
+.Sh NAME
+.Nm rpc_gss_set_svc_name
+.Nd "Associate a GSS-API service principal with an RPC service"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft bool_t
+.Fo rpc_gss_set_svc_name
+.Fa "const char *principal"
+.Fa "const char *mechanism"
+.Fa "u_int req_time"
+.Fa "u_int program"
+.Fa "u_int version"
+.Fc
+.Sh DESCRIPTION
+This function registers a service principal which will be used to
+authenticate RPCSEC_GSS security contexts for a given RPC program and
+version.
+.Sh PARAMETERS
+.Bl -tag
+.It principal
+A string representing the service principal in the form
+.Qq service@hostname
+.It mechanim
+The name of the security mechanism
+.It req_time
+The time in seconds that the service credentials should remain
+valid.
+See
+.Xr gss_acquire_cred 3
+for more details.
+principal.
+.It program
+RPC program number for this service
+.It version
+RPC program version for this service
+.El
+.Sh RETURN VALUES
+Returns
+.Dv TRUE
+if the service principal was registered or
+.Dv FALSE
+otherwise.
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr gss_acquire_cred 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpc_gss_svc_max_data_length.3 b/lib/librpcsec_gss/rpc_gss_svc_max_data_length.3
new file mode 100644
index 000000000000..2445f7553f60
--- /dev/null
+++ b/lib/librpcsec_gss/rpc_gss_svc_max_data_length.3
@@ -0,0 +1,64 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_SVC_MAX_DATA_LENGTH 3
+.Os
+.Sh NAME
+.Nm rpc_gss_svc_max_data_length
+.Nd "calculate maximum data size"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Ft int
+.Fn rpc_gss_svc_max_data_length "struct svc_req *req" "int max_tp_unit_len"
+.Sh DESCRIPTION
+Calculate the maximum message size that will fit into a packet of size
+.Fa max_tp_unit_len ,
+given the current service and QoP setting.
+.Sh PARAMETERS
+.Bl -tag
+.It req
+An RPC request
+.It max_tp_unit_len
+Maximum packet size of the underlying transport protocol
+.El
+.Sh RETURN VALUES
+The maximum message size that can be encoded
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpcsec_gss.3 b/lib/librpcsec_gss/rpcsec_gss.3
new file mode 100644
index 000000000000..793f0129d390
--- /dev/null
+++ b/lib/librpcsec_gss/rpcsec_gss.3
@@ -0,0 +1,230 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 4, 2008
+.Dt RPC_GSS_SECCREATE 3
+.Os
+.Sh NAME
+.Nm RPCSEC_GSS
+.Nd "GSS-API based authentication for RPC"
+.Sh LIBRARY
+.Lb librpcsec_gss
+.Sh SYNOPSIS
+.In rpc/rpcsec_gss.h
+.Sh DESCRIPTION
+.Nm
+is a security mechanism for the RPC protocol.
+It uses the Generic Security Service API (GSS-API) to establish a
+security context between a client and a server and to ensure that all
+subsequent communication between client and server are properly
+authenticated.
+Optionally, extra protection can be applied to the connection.
+The integrity service uses checksums to ensure that all data sent by
+a peer is recieved without modification.
+The privacy service uses encryption to ensure that no third party can
+access the data for a connection.
+.Pp
+To use this system, an application must first use
+.Fn rpc_gss_seccreate
+to establish a security context.
+.Sh DATA STRUCTURES
+Data structures used by
+.Nm
+appear below.
+.Bl -tag -width "MMMM"
+.It Vt rpc_gss_service_t
+This type defines the types of security service required for
+.Fn rpc_gss_seccreate .
+.Bd -literal
+typedef enum {
+ rpc_gss_svc_default = 0,
+ rpc_gss_svc_none = 1,
+ rpc_gss_svc_integrity = 2,
+ rpc_gss_svc_privacy = 3
+} rpc_gss_service_t;
+.Ed
+.It Vt rpc_gss_options_ret_t
+This structure contains various optional values which are used while
+creating a security contect.
+.Bd -literal
+typedef struct {
+ int req_flags; /* GSS request bits */
+ int time_req; /* requested lifetime */
+ gss_cred_id_t my_cred; /* GSS credential */
+ gss_channel_bindings_t input_channel_bindings;
+} rpc_gss_options_req_t;
+.Ed
+.It Vt rpc_gss_options_ret_t
+Various details of the created security context are returned using
+this structure.
+.Bd -literal
+typedef struct {
+ int major_status;
+ int minor_status;
+ u_int rpcsec_version;
+ int ret_flags;
+ int time_req;
+ gss_ctx_id_t gss_context;
+ char actual_mechanism[MAX_GSS_MECH];
+} rpc_gss_options_ret_t;
+.Ed
+.It Vt rpc_gss_principal_t
+This type is used to refer to an client principal which is represented
+in GSS-API exported name form
+(see
+.Xr gss_export_name 3
+for more details).
+Names in this format may be stored in access control lists or compared
+with other names in exported name form.
+This structure is returned by
+.Fn rpc_gss_get_principal_name
+and is also referenced by the
+.Vt rpc_gss_rawcred_t
+structure.
+.Bd -literal
+typedef struct {
+ int len;
+ char name[1];
+} *rpc_gss_principal_t;
+.Ed
+.It Vt rpc_gss_rawcred_t
+This structure is used to access the raw credentions associated with a
+security context.
+.Bd -literal
+typedef struct {
+ u_int version; /* RPC version number */
+ const char *mechanism; /* security mechanism */
+ const char *qop; /* quality of protection */
+ rpc_gss_principal_t client_principal; /* client name */
+ const char *svc_principal; /* server name */
+ rpc_gss_service_t service; /* service type */
+} rpc_gss_rawcred_t;
+.Ed
+.It Vt rpc_gss_ucred_t
+Unix credentials which are derived form the raw credentials,
+accessed via
+.Fn rpc_gss_getcred .
+.Bd -literal
+typedef struct {
+ uid_t uid; /* user ID */
+ gid_t gid; /* group ID */
+ short gidlen;
+ gid_t *gidlist; /* list of groups */
+} rpc_gss_ucred_t;
+.Ed
+.It Vt rpc_gss_lock_t
+Structure used to enforce a particular QOP and service.
+.Bd -literal
+typedef struct {
+ bool_t locked;
+ rpc_gss_rawcred_t *raw_cred;
+} rpc_gss_lock_t;
+.Ed
+.It Vt rpc_gss_callback_t
+Callback structure used by
+.Fn rpc_gss_set_callback .
+.Bd -literal
+typedef struct {
+ u_int program; /* RPC program number */
+ u_int version; /* RPC version number */
+ /* user defined callback */
+ bool_t (*callback)(struct svc_req *req,
+ gss_cred_id_t deleg,
+ gss_ctx_id_t gss_context,
+ rpc_gss_lock_t *lock,
+ void **cookie);
+} rpc_gss_callback_t;
+.Ed
+.It Vt rpc_gss_error_t
+Structure used to return error information by
+.Fn rpc_gss_get_error .
+.Bd -literal
+typedef struct {
+ int rpc_gss_error;
+ int system_error; /* same as errno */
+} rpc_gss_error_t;
+
+/*
+ * Values for rpc_gss_error
+ */
+#define RPC_GSS_ER_SUCCESS 0 /* no error */
+#define RPC_GSS_ER_SYSTEMERROR 1 /* system error */
+.Ed
+.Sh INDEX
+.Bl -tag -width "MMMM"
+.It Xr rpc_gss_seccreate 3
+Create a new security context
+.It Xr rpc_gss_set_defaults 3
+Set service and quality of protection for a context
+.It Xr rpc_gss_max_data_length 3
+Calculate maximum client message sizes.
+.It Xr rpc_gss_get_error 3
+Get details of the last error
+.It Xr rpc_gss_mech_to_oid 3
+Convert a mechanism name to the corresponding GSS-API oid.
+.It Xr rpc_gss_oid_to_mech 3
+Convert a GSS-API oid to a mechanism name
+.It Xr rpc_gss_qop_to_num 3
+Convert a quality of protection name to the corresponding number
+.It Xr rpc_gss_get_mechanisms 3
+Get a list of security mechanisms.
+.It Xr rpc_gss_get_mech_info 3
+Return extra information about a security mechanism
+.It Xr rpc_gss_get_versions 3
+Return the maximum and minimum supported versions of the
+.Nm
+protocol
+.It Xr rpc_gss_is_installed 3
+Query for the presence of a particular security mechanism
+.It Xr rpc_gss_set_svc_name 3
+Set the name of a service principal which matches a given RPC program
+plus version pair
+.It Xr rpc_gss_getcred 3
+Get credential details for the security context of an RPC request
+.It Xr rpc_gss_set_callback 3
+Install a callback routine which is called on the server when new
+security contexts are created
+.It Xr rpc_gss_get_principal_name 3
+Create a client principal name from various strings
+.It Xr rpc_gss_svc_max_data_length 3
+Calculate maximum server message sizes.
+.El
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr gssapi 3 ,
+.Xr gss_export_name 3 ,
+.Xr mech 5 ,
+.Xr qop 5 ,
+.Xr rpcset_gss 3
+.Sh HISTORY
+The
+.Nm
+manual page example first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/lib/librpcsec_gss/rpcsec_gss.c b/lib/librpcsec_gss/rpcsec_gss.c
new file mode 100644
index 000000000000..60208810e4aa
--- /dev/null
+++ b/lib/librpcsec_gss/rpcsec_gss.c
@@ -0,0 +1,722 @@
+/*-
+ * Copyright (c) 2008 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ auth_gss.c
+
+ RPCSEC_GSS client routines.
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+#include "rpcsec_gss_int.h"
+
+static void rpc_gss_nextverf(AUTH*);
+static bool_t rpc_gss_marshal(AUTH *, XDR *);
+static bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
+static bool_t rpc_gss_refresh(AUTH *, void *);
+static bool_t rpc_gss_validate(AUTH *, struct opaque_auth *);
+static void rpc_gss_destroy(AUTH *);
+static void rpc_gss_destroy_context(AUTH *, bool_t);
+
+static struct auth_ops rpc_gss_ops = {
+ rpc_gss_nextverf,
+ rpc_gss_marshal,
+ rpc_gss_validate,
+ rpc_gss_refresh,
+ rpc_gss_destroy
+};
+
+enum rpcsec_gss_state {
+ RPCSEC_GSS_START,
+ RPCSEC_GSS_CONTEXT,
+ RPCSEC_GSS_ESTABLISHED
+};
+
+struct rpc_gss_data {
+ rpc_gss_options_req_t gd_options; /* GSS context options */
+ enum rpcsec_gss_state gd_state; /* connection state */
+ gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE
+ * NULL RPC verfier to
+ * process at end of
+ * context negotiation */
+ CLIENT *gd_clnt; /* client handle */
+ gss_name_t gd_name; /* service name */
+ gss_OID gd_mech; /* mechanism to use */
+ gss_qop_t gd_qop; /* quality of protection */
+ gss_ctx_id_t gd_ctx; /* context id */
+ struct rpc_gss_cred gd_cred; /* client credentials */
+ u_int gd_win; /* sequence window */
+};
+
+#define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
+
+static struct timeval AUTH_TIMEOUT = { 25, 0 };
+
+AUTH *
+rpc_gss_seccreate(CLIENT *clnt, const char *principal,
+ const char *mechanism, rpc_gss_service_t service, const char *qop,
+ rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
+{
+ AUTH *auth, *save_auth;
+ rpc_gss_options_ret_t options;
+ gss_OID oid;
+ u_int qop_num;
+ struct rpc_gss_data *gd;
+ OM_uint32 maj_stat = 0, min_stat = 0;
+ gss_buffer_desc principal_desc;
+
+ /*
+ * Bail out now if we don't know this mechanism.
+ */
+ if (!rpc_gss_mech_to_oid(mechanism, &oid))
+ return (NULL);
+
+ if (qop) {
+ if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
+ return (NULL);
+ } else {
+ qop_num = GSS_C_QOP_DEFAULT;
+ }
+
+ /*
+ * If the caller doesn't want the options, point at local
+ * storage to simplify the code below.
+ */
+ if (!options_ret)
+ options_ret = &options;
+
+ /*
+ * Default service is integrity.
+ */
+ if (service == rpc_gss_svc_default)
+ service = rpc_gss_svc_integrity;
+
+ memset(options_ret, 0, sizeof(*options_ret));
+
+ log_debug("in rpc_gss_seccreate()");
+
+ memset(&rpc_createerr, 0, sizeof(rpc_createerr));
+
+ auth = mem_alloc(sizeof(*auth));
+ if (auth == NULL) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = ENOMEM;
+ return (NULL);
+ }
+ gd = mem_alloc(sizeof(*gd));
+ if (gd == NULL) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = ENOMEM;
+ free(auth);
+ return (NULL);
+ }
+
+ auth->ah_ops = &rpc_gss_ops;
+ auth->ah_private = (caddr_t) gd;
+ auth->ah_cred.oa_flavor = RPCSEC_GSS;
+
+ principal_desc.value = (void *)(intptr_t) principal;
+ principal_desc.length = strlen(principal);
+ maj_stat = gss_import_name(&min_stat, &principal_desc,
+ GSS_C_NT_HOSTBASED_SERVICE, &gd->gd_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ options_ret->major_status = maj_stat;
+ options_ret->minor_status = min_stat;
+ goto bad;
+ }
+
+ if (options_req) {
+ gd->gd_options = *options_req;
+ } else {
+ gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
+ gd->gd_options.time_req = 0;
+ gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
+ gd->gd_options.input_channel_bindings = NULL;
+ }
+ gd->gd_clnt = clnt;
+ gd->gd_ctx = GSS_C_NO_CONTEXT;
+ gd->gd_mech = oid;
+ gd->gd_qop = qop_num;
+
+ gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
+ gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
+ gd->gd_cred.gc_seq = 0;
+ gd->gd_cred.gc_svc = service;
+
+ save_auth = clnt->cl_auth;
+
+ clnt->cl_auth = auth;
+ if (!rpc_gss_init(auth, options_ret)) {
+ clnt->cl_auth = save_auth;
+ goto bad;
+ }
+
+ clnt->cl_auth = save_auth;
+
+ return (auth);
+
+ bad:
+ AUTH_DESTROY(auth);
+ return (NULL);
+}
+
+bool_t
+rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
+{
+ struct rpc_gss_data *gd;
+ u_int qop_num;
+ const char *mechanism;
+
+ gd = AUTH_PRIVATE(auth);
+ if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
+ return (FALSE);
+ }
+
+ if (qop) {
+ if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
+ return (FALSE);
+ }
+ } else {
+ qop_num = GSS_C_QOP_DEFAULT;
+ }
+
+ gd->gd_cred.gc_svc = service;
+ gd->gd_qop = qop_num;
+ return (TRUE);
+}
+
+static void
+rpc_gss_nextverf(__unused AUTH *auth)
+{
+
+ /* not used */
+}
+
+static bool_t
+rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs)
+{
+
+ /* not used */
+ return (FALSE);
+}
+
+static bool_t
+rpc_gss_validate(AUTH *auth, struct opaque_auth *verf)
+{
+ struct rpc_gss_data *gd;
+ gss_qop_t qop_state;
+ uint32_t num;
+ gss_buffer_desc signbuf, checksum;
+ OM_uint32 maj_stat, min_stat;
+
+ log_debug("in rpc_gss_validate()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
+ /*
+ * Save the on the wire verifier to validate last INIT
+ * phase packet after decode if the major status is
+ * GSS_S_COMPLETE.
+ */
+ if (gd->gd_verf.value)
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &gd->gd_verf);
+ gd->gd_verf.value = mem_alloc(verf->oa_length);
+ if (gd->gd_verf.value == NULL) {
+ fprintf(stderr, "gss_validate: out of memory\n");
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
+ return (FALSE);
+ }
+ memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
+ gd->gd_verf.length = verf->oa_length;
+ return (TRUE);
+ }
+
+ num = htonl(gd->gd_cred.gc_seq);
+ signbuf.value = &num;
+ signbuf.length = sizeof(num);
+
+ checksum.value = verf->oa_base;
+ checksum.length = verf->oa_length;
+
+ maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &signbuf,
+ &checksum, &qop_state);
+ if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) {
+ log_status("gss_verify_mic", gd->gd_mech, maj_stat, min_stat);
+ if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
+ rpc_gss_destroy_context(auth, TRUE);
+ }
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+static bool_t
+rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
+{
+ struct rpc_gss_data *gd;
+ struct rpc_gss_init_res gr;
+ gss_buffer_desc *recv_tokenp, recv_token, send_token;
+ OM_uint32 maj_stat, min_stat, call_stat;
+ const char *mech;
+
+ log_debug("in rpc_gss_refresh()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ if (gd->gd_state != RPCSEC_GSS_START)
+ return (TRUE);
+
+ /* GSS context establishment loop. */
+ gd->gd_state = RPCSEC_GSS_CONTEXT;
+ gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
+ gd->gd_cred.gc_seq = 0;
+
+ memset(&recv_token, 0, sizeof(recv_token));
+ memset(&gr, 0, sizeof(gr));
+ recv_tokenp = GSS_C_NO_BUFFER;
+
+ for (;;) {
+ maj_stat = gss_init_sec_context(&min_stat,
+ gd->gd_options.my_cred,
+ &gd->gd_ctx,
+ gd->gd_name,
+ gd->gd_mech,
+ gd->gd_options.req_flags,
+ gd->gd_options.time_req,
+ gd->gd_options.input_channel_bindings,
+ recv_tokenp,
+ &gd->gd_mech, /* used mech */
+ &send_token,
+ &options_ret->ret_flags,
+ &options_ret->time_req);
+
+ /*
+ * Free the token which we got from the server (if
+ * any). Remember that this was allocated by XDR, not
+ * GSS-API.
+ */
+ if (recv_tokenp != GSS_C_NO_BUFFER) {
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &recv_token);
+ recv_tokenp = GSS_C_NO_BUFFER;
+ }
+ if (maj_stat != GSS_S_COMPLETE &&
+ maj_stat != GSS_S_CONTINUE_NEEDED) {
+ log_status("gss_init_sec_context", gd->gd_mech,
+ maj_stat, min_stat);
+ options_ret->major_status = maj_stat;
+ options_ret->minor_status = min_stat;
+ break;
+ }
+ if (send_token.length != 0) {
+ memset(&gr, 0, sizeof(gr));
+
+ call_stat = clnt_call(gd->gd_clnt, NULLPROC,
+ (xdrproc_t)xdr_gss_buffer_desc,
+ &send_token,
+ (xdrproc_t)xdr_rpc_gss_init_res,
+ (caddr_t)&gr, AUTH_TIMEOUT);
+
+ gss_release_buffer(&min_stat, &send_token);
+
+ if (call_stat != RPC_SUCCESS)
+ break;
+
+ if (gr.gr_major != GSS_S_COMPLETE &&
+ gr.gr_major != GSS_S_CONTINUE_NEEDED) {
+ log_status("server reply", gd->gd_mech,
+ gr.gr_major, gr.gr_minor);
+ options_ret->major_status = gr.gr_major;
+ options_ret->minor_status = gr.gr_minor;
+ break;
+ }
+
+ /*
+ * Save the server's gr_handle value, freeing
+ * what we have already (remember that this
+ * was allocated by XDR, not GSS-API).
+ */
+ if (gr.gr_handle.length != 0) {
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &gd->gd_cred.gc_handle);
+ gd->gd_cred.gc_handle = gr.gr_handle;
+ }
+
+ /*
+ * Save the server's token as well.
+ */
+ if (gr.gr_token.length != 0) {
+ recv_token = gr.gr_token;
+ recv_tokenp = &recv_token;
+ }
+
+ /*
+ * Since we have copied out all the bits of gr
+ * which XDR allocated for us, we don't need
+ * to free it.
+ */
+ gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
+ }
+
+ if (maj_stat == GSS_S_COMPLETE) {
+ gss_buffer_desc bufin;
+ u_int seq, qop_state = 0;
+
+ /*
+ * gss header verifier,
+ * usually checked in gss_validate
+ */
+ seq = htonl(gr.gr_win);
+ bufin.value = (unsigned char *)&seq;
+ bufin.length = sizeof(seq);
+
+ maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
+ &bufin, &gd->gd_verf, &qop_state);
+
+ if (maj_stat != GSS_S_COMPLETE ||
+ qop_state != gd->gd_qop) {
+ log_status("gss_verify_mic", gd->gd_mech,
+ maj_stat, min_stat);
+ if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
+ rpc_gss_destroy_context(auth, TRUE);
+ }
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
+ EPERM);
+ options_ret->major_status = maj_stat;
+ options_ret->minor_status = min_stat;
+ return (FALSE);
+ }
+
+ options_ret->major_status = GSS_S_COMPLETE;
+ options_ret->minor_status = 0;
+ options_ret->rpcsec_version = gd->gd_cred.gc_version;
+ options_ret->gss_context = gd->gd_ctx;
+ if (rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
+ strlcpy(options_ret->actual_mechanism,
+ mech,
+ sizeof(options_ret->actual_mechanism));
+ }
+
+ gd->gd_state = RPCSEC_GSS_ESTABLISHED;
+ gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
+ gd->gd_cred.gc_seq = 0;
+ gd->gd_win = gr.gr_win;
+ break;
+ }
+ }
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &gd->gd_verf);
+
+ /* End context negotiation loop. */
+ if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
+ rpc_createerr.cf_stat = RPC_AUTHERROR;
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+static bool_t
+rpc_gss_refresh(AUTH *auth, void *msg)
+{
+ struct rpc_msg *reply = (struct rpc_msg *) msg;
+ rpc_gss_options_ret_t options;
+
+ /*
+ * If the error was RPCSEC_GSS_CREDPROBLEM of
+ * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
+ * other errors are fatal.
+ */
+ if (reply->rm_reply.rp_stat == MSG_DENIED
+ && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
+ && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
+ || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
+ rpc_gss_destroy_context(auth, FALSE);
+ memset(&options, 0, sizeof(options));
+ return (rpc_gss_init(auth, &options));
+ }
+
+ return (FALSE);
+}
+
+static void
+rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
+{
+ struct rpc_gss_data *gd;
+ OM_uint32 min_stat;
+
+ log_debug("in rpc_gss_destroy_context()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ if (gd->gd_state == RPCSEC_GSS_ESTABLISHED && send_destroy) {
+ gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
+ clnt_call(gd->gd_clnt, NULLPROC,
+ (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
+ }
+
+ /*
+ * Free the context token. Remember that this was
+ * allocated by XDR, not GSS-API.
+ */
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &gd->gd_cred.gc_handle);
+ gd->gd_cred.gc_handle.length = 0;
+
+ if (gd->gd_ctx != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
+
+ gd->gd_state = RPCSEC_GSS_START;
+}
+
+static void
+rpc_gss_destroy(AUTH *auth)
+{
+ struct rpc_gss_data *gd;
+ OM_uint32 min_stat;
+
+ log_debug("in rpc_gss_destroy()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ rpc_gss_destroy_context(auth, TRUE);
+
+ if (gd->gd_name != GSS_C_NO_NAME)
+ gss_release_name(&min_stat, &gd->gd_name);
+ if (gd->gd_verf.value)
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &gd->gd_verf);
+
+ mem_free(gd, sizeof(*gd));
+ mem_free(auth, sizeof(*auth));
+}
+
+bool_t
+__rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen,
+ XDR* xdrs, xdrproc_t xdr_args, void *args_ptr)
+{
+ XDR tmpxdrs;
+ char credbuf[MAX_AUTH_BYTES];
+ char tmpheader[MAX_AUTH_BYTES];
+ struct opaque_auth creds, verf;
+ struct rpc_gss_data *gd;
+ gss_buffer_desc rpcbuf, checksum;
+ OM_uint32 maj_stat, min_stat;
+ bool_t xdr_stat;
+
+ log_debug("in rpc_gss_wrap()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ if (gd->gd_state == RPCSEC_GSS_ESTABLISHED)
+ gd->gd_cred.gc_seq++;
+
+ /*
+ * We need to encode our creds and then put the header and
+ * creds together in a buffer so that we can create a checksum
+ * for the verf.
+ */
+ xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
+ if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gd_cred)) {
+ XDR_DESTROY(&tmpxdrs);
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
+ return (FALSE);
+ }
+ creds.oa_flavor = RPCSEC_GSS;
+ creds.oa_base = credbuf;
+ creds.oa_length = XDR_GETPOS(&tmpxdrs);
+ XDR_DESTROY(&tmpxdrs);
+
+ xdrmem_create(&tmpxdrs, tmpheader, sizeof(tmpheader), XDR_ENCODE);
+ if (!XDR_PUTBYTES(&tmpxdrs, header, headerlen) ||
+ !xdr_opaque_auth(&tmpxdrs, &creds)) {
+ XDR_DESTROY(&tmpxdrs);
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
+ return (FALSE);
+ }
+ headerlen = XDR_GETPOS(&tmpxdrs);
+ XDR_DESTROY(&tmpxdrs);
+
+ if (!XDR_PUTBYTES(xdrs, tmpheader, headerlen)) {
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
+ return (FALSE);
+ }
+
+ if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
+ gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
+ if (!xdr_opaque_auth(xdrs, &_null_auth)) {
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
+ return (FALSE);
+ }
+ } else {
+ /*
+ * Checksum serialized RPC header, up to and including
+ * credential.
+ */
+ rpcbuf.length = headerlen;
+ rpcbuf.value = tmpheader;
+
+ maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
+ &rpcbuf, &checksum);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ log_status("gss_get_mic", gd->gd_mech,
+ maj_stat, min_stat);
+ if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
+ rpc_gss_destroy_context(auth, TRUE);
+ }
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
+ return (FALSE);
+ }
+
+ verf.oa_flavor = RPCSEC_GSS;
+ verf.oa_base = checksum.value;
+ verf.oa_length = checksum.length;
+
+ xdr_stat = xdr_opaque_auth(xdrs, &verf);
+ gss_release_buffer(&min_stat, &checksum);
+ if (!xdr_stat) {
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
+ return (FALSE);
+ }
+ }
+
+ if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
+ gd->gd_cred.gc_svc == rpc_gss_svc_none) {
+ return (xdr_args(xdrs, args_ptr));
+ }
+ return (xdr_rpc_gss_wrap_data(xdrs, xdr_args, args_ptr,
+ gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
+ gd->gd_cred.gc_seq));
+}
+
+bool_t
+__rpc_gss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, void *xdr_ptr)
+{
+ struct rpc_gss_data *gd;
+
+ log_debug("in rpc_gss_unwrap()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
+ gd->gd_cred.gc_svc == rpc_gss_svc_none) {
+ return (xdr_func(xdrs, xdr_ptr));
+ }
+ return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
+ gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
+ gd->gd_cred.gc_seq));
+}
+
+int
+rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
+{
+ struct rpc_gss_data *gd;
+ int want_conf;
+ OM_uint32 max;
+ OM_uint32 maj_stat, min_stat;
+ int result;
+
+ gd = AUTH_PRIVATE(auth);
+
+ switch (gd->gd_cred.gc_svc) {
+ case rpc_gss_svc_none:
+ return (max_tp_unit_len);
+ break;
+
+ case rpc_gss_svc_default:
+ case rpc_gss_svc_integrity:
+ want_conf = FALSE;
+ break;
+
+ case rpc_gss_svc_privacy:
+ want_conf = TRUE;
+ break;
+
+ default:
+ return (0);
+ }
+
+ maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
+ gd->gd_qop, max_tp_unit_len, &max);
+
+ if (maj_stat == GSS_S_COMPLETE) {
+ result = (int) max;
+ if (result < 0)
+ result = 0;
+ return (result);
+ } else {
+ log_status("gss_wrap_size_limit", gd->gd_mech,
+ maj_stat, min_stat);
+ return (0);
+ }
+}
diff --git a/lib/librpcsec_gss/rpcsec_gss_conf.c b/lib/librpcsec_gss/rpcsec_gss_conf.c
new file mode 100644
index 000000000000..4a0349be1522
--- /dev/null
+++ b/lib/librpcsec_gss/rpcsec_gss_conf.c
@@ -0,0 +1,417 @@
+/*-
+ * Copyright (c) 2008 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/queue.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+
+#include "rpcsec_gss_int.h"
+
+#ifndef _PATH_GSS_MECH
+#define _PATH_GSS_MECH "/etc/gss/mech"
+#endif
+
+#ifndef _PATH_GSS_QOP
+#define _PATH_GSS_QOP "/etc/gss/qop"
+#endif
+
+struct mech_info {
+ SLIST_ENTRY(mech_info) link;
+ char *name;
+ gss_OID_desc oid;
+ const char **qops;
+ char *lib;
+ char *kobj;
+};
+SLIST_HEAD(mech_info_list, mech_info);
+
+static struct mech_info_list mechs = SLIST_HEAD_INITIALIZER(&mechs);
+static const char **mech_names;
+
+struct qop_info {
+ SLIST_ENTRY(qop_info) link;
+ char *name;
+ char* mech;
+ u_int qop;
+};
+SLIST_HEAD(qop_info_list, qop_info);
+
+static struct qop_info_list qops = SLIST_HEAD_INITIALIZER(&qops);
+
+static int
+_rpc_gss_string_to_oid(const char* s, gss_OID oid)
+{
+ int number_count, i, j;
+ int byte_count;
+ const char *p, *q;
+ char *res;
+
+ /*
+ * First figure out how many numbers in the oid, then
+ * calculate the compiled oid size.
+ */
+ number_count = 0;
+ for (p = s; p; p = q) {
+ q = strchr(p, '.');
+ if (q) q = q + 1;
+ number_count++;
+ }
+
+ /*
+ * The first two numbers are in the first byte and each
+ * subsequent number is encoded in a variable byte sequence.
+ */
+ if (number_count < 2)
+ return (EINVAL);
+
+ /*
+ * We do this in two passes. The first pass, we just figure
+ * out the size. Second time around, we actually encode the
+ * number.
+ */
+ res = 0;
+ for (i = 0; i < 2; i++) {
+ byte_count = 0;
+ for (p = s, j = 0; p; p = q, j++) {
+ u_int number = 0;
+
+ /*
+ * Find the end of this number.
+ */
+ q = strchr(p, '.');
+ if (q) q = q + 1;
+
+ /*
+ * Read the number of of the string. Don't
+ * bother with anything except base ten.
+ */
+ while (*p && *p != '.') {
+ number = 10 * number + (*p - '0');
+ p++;
+ }
+
+ /*
+ * Encode the number. The first two numbers
+ * are packed into the first byte. Subsequent
+ * numbers are encoded in bytes seven bits at
+ * a time with the last byte having the high
+ * bit set.
+ */
+ if (j == 0) {
+ if (res)
+ *res = number * 40;
+ } else if (j == 1) {
+ if (res) {
+ *res += number;
+ res++;
+ }
+ byte_count++;
+ } else if (j >= 2) {
+ /*
+ * The number is encoded in seven bit chunks.
+ */
+ u_int t;
+ int bytes;
+
+ bytes = 0;
+ for (t = number; t; t >>= 7)
+ bytes++;
+ if (bytes == 0) bytes = 1;
+ while (bytes) {
+ if (res) {
+ int bit = 7*(bytes-1);
+
+ *res = (number >> bit) & 0x7f;
+ if (bytes != 1)
+ *res |= 0x80;
+ res++;
+ }
+ byte_count++;
+ bytes--;
+ }
+ }
+ }
+ if (!res) {
+ res = malloc(byte_count);
+ if (!res)
+ return (ENOMEM);
+ oid->length = byte_count;
+ oid->elements = res;
+ }
+ }
+
+ return (0);
+}
+
+static void
+_rpc_gss_load_mech(void)
+{
+ FILE *fp;
+ char buf[256];
+ char *p;
+ char *name, *oid, *lib, *kobj;
+ struct mech_info *info;
+ int count;
+ const char **pp;
+
+ if (SLIST_FIRST(&mechs))
+ return;
+
+ fp = fopen(_PATH_GSS_MECH, "r");
+ if (!fp)
+ return;
+
+ count = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (*buf == '#')
+ continue;
+ p = buf;
+ name = strsep(&p, "\t\n ");
+ if (p) while (isspace(*p)) p++;
+ oid = strsep(&p, "\t\n ");
+ if (p) while (isspace(*p)) p++;
+ lib = strsep(&p, "\t\n ");
+ if (p) while (isspace(*p)) p++;
+ kobj = strsep(&p, "\t\n ");
+ if (!name || !oid || !lib || !kobj)
+ continue;
+
+ info = malloc(sizeof(struct mech_info));
+ if (!info)
+ break;
+ if (_rpc_gss_string_to_oid(oid, &info->oid)) {
+ free(info);
+ continue;
+ }
+ info->name = strdup(name);
+ info->qops = NULL;
+ info->lib = strdup(lib);
+ info->kobj = strdup(kobj);
+ SLIST_INSERT_HEAD(&mechs, info, link);
+ count++;
+ }
+ fclose(fp);
+
+ mech_names = malloc((count + 1) * sizeof(char*));
+ pp = mech_names;
+ SLIST_FOREACH(info, &mechs, link) {
+ *pp++ = info->name;
+ }
+ *pp = NULL;
+}
+
+static void
+_rpc_gss_load_qop(void)
+{
+ FILE *fp;
+ char buf[256];
+ char *p;
+ char *name, *num, *mech;
+ struct mech_info *minfo;
+ struct qop_info *info;
+ int count;
+ const char **mech_qops;
+ const char **pp;
+
+ if (SLIST_FIRST(&qops))
+ return;
+
+ fp = fopen(_PATH_GSS_QOP, "r");
+ if (!fp)
+ return;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (*buf == '#')
+ continue;
+ p = buf;
+ name = strsep(&p, "\t\n ");
+ if (p) while (isspace(*p)) p++;
+ num = strsep(&p, "\t\n ");
+ if (p) while (isspace(*p)) p++;
+ mech = strsep(&p, "\t\n ");
+ if (!name || !num || !mech)
+ continue;
+
+ info = malloc(sizeof(struct qop_info));
+ if (!info)
+ break;
+ info->name = strdup(name);
+ info->qop = strtoul(name, 0, 0);
+ info->mech = strdup(mech);
+ SLIST_INSERT_HEAD(&qops, info, link);
+ }
+ fclose(fp);
+
+ /*
+ * Compile lists of qops for each mechanism.
+ */
+ SLIST_FOREACH(minfo, &mechs, link) {
+ count = 0;
+ SLIST_FOREACH(info, &qops, link) {
+ if (strcmp(info->mech, minfo->name) == 0)
+ count++;
+ }
+ mech_qops = malloc((count + 1) * sizeof(char*));
+ pp = mech_qops;
+ SLIST_FOREACH(info, &qops, link) {
+ if (strcmp(info->mech, minfo->name) == 0)
+ *pp++ = info->name;
+ }
+ *pp = NULL;
+ minfo->qops = mech_qops;
+ }
+}
+
+bool_t
+rpc_gss_mech_to_oid(const char *mech, gss_OID *oid_ret)
+{
+ struct mech_info *info;
+
+ _rpc_gss_load_mech();
+ SLIST_FOREACH(info, &mechs, link) {
+ if (!strcmp(info->name, mech)) {
+ *oid_ret = &info->oid;
+ return (TRUE);
+ }
+ }
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT);
+ return (FALSE);
+}
+
+bool_t
+rpc_gss_oid_to_mech(gss_OID oid, const char **mech_ret)
+{
+ struct mech_info *info;
+
+ _rpc_gss_load_mech();
+ SLIST_FOREACH(info, &mechs, link) {
+ if (oid->length == info->oid.length
+ && !memcmp(oid->elements, info->oid.elements,
+ oid->length)) {
+ *mech_ret = info->name;
+ return (TRUE);
+ }
+ }
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT);
+ return (FALSE);
+}
+
+bool_t
+rpc_gss_qop_to_num(const char *qop, const char *mech, u_int *num_ret)
+{
+ struct qop_info *info;
+
+ _rpc_gss_load_qop();
+ SLIST_FOREACH(info, &qops, link) {
+ if (strcmp(info->name, qop) == 0
+ && strcmp(info->mech, mech) == 0) {
+ *num_ret = info->qop;
+ return (TRUE);
+ }
+ }
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT);
+ return (FALSE);
+}
+
+const char *
+_rpc_gss_num_to_qop(const char *mech, u_int num)
+{
+ struct qop_info *info;
+
+ if (num == GSS_C_QOP_DEFAULT)
+ return "default";
+
+ _rpc_gss_load_qop();
+ SLIST_FOREACH(info, &qops, link) {
+ if (info->qop == num && strcmp(info->mech, mech) == 0) {
+ return (info->name);
+ }
+ }
+ return (NULL);
+}
+
+const char **
+rpc_gss_get_mechanisms(void)
+{
+
+ _rpc_gss_load_mech();
+ return (mech_names);
+}
+
+const char **
+rpc_gss_get_mech_info(const char *mech, rpc_gss_service_t *service)
+{
+ struct mech_info *info;
+
+ _rpc_gss_load_mech();
+ _rpc_gss_load_qop();
+ SLIST_FOREACH(info, &mechs, link) {
+ if (!strcmp(mech, info->name)) {
+ /*
+ * I'm not sure what to do with service
+ * here. The Solaris manpages are not clear on
+ * the subject and the OpenSolaris code just
+ * sets it to rpc_gss_svc_privacy
+ * unconditionally with a comment noting that
+ * it is bogus.
+ */
+ *service = rpc_gss_svc_privacy;
+ return info->qops;
+ }
+ }
+
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT);
+ return (NULL);
+}
+
+bool_t
+rpc_gss_get_versions(u_int *vers_hi, u_int *vers_lo)
+{
+
+ *vers_hi = 1;
+ *vers_lo = 1;
+ return (TRUE);
+}
+
+bool_t
+rpc_gss_is_installed(const char *mech)
+{
+ struct mech_info *info;
+
+ _rpc_gss_load_mech();
+ SLIST_FOREACH(info, &mechs, link)
+ if (!strcmp(mech, info->name))
+ return (TRUE);
+ return (FALSE);
+}
+
diff --git a/lib/librpcsec_gss/rpcsec_gss_int.h b/lib/librpcsec_gss/rpcsec_gss_int.h
new file mode 100644
index 000000000000..d74191dde937
--- /dev/null
+++ b/lib/librpcsec_gss/rpcsec_gss_int.h
@@ -0,0 +1,95 @@
+/*
+ rpcsec_gss.h
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ $Id: auth_gss.h,v 1.12 2001/04/30 19:44:47 andros Exp $
+*/
+/* $FreeBSD$ */
+
+#ifndef _RPCSEC_GSS_INT_H
+#define _RPCSEC_GSS_INT_H
+
+/* RPCSEC_GSS control procedures. */
+typedef enum {
+ RPCSEC_GSS_DATA = 0,
+ RPCSEC_GSS_INIT = 1,
+ RPCSEC_GSS_CONTINUE_INIT = 2,
+ RPCSEC_GSS_DESTROY = 3
+} rpc_gss_proc_t;
+
+#define RPCSEC_GSS_VERSION 1
+
+/* Credentials. */
+struct rpc_gss_cred {
+ u_int gc_version; /* version */
+ rpc_gss_proc_t gc_proc; /* control procedure */
+ u_int gc_seq; /* sequence number */
+ rpc_gss_service_t gc_svc; /* service */
+ gss_buffer_desc gc_handle; /* handle to server-side context */
+};
+
+/* Context creation response. */
+struct rpc_gss_init_res {
+ gss_buffer_desc gr_handle; /* handle to server-side context */
+ u_int gr_major; /* major status */
+ u_int gr_minor; /* minor status */
+ u_int gr_win; /* sequence window */
+ gss_buffer_desc gr_token; /* token */
+};
+
+/* Maximum sequence number value. */
+#define MAXSEQ 0x80000000
+
+/* Prototypes. */
+__BEGIN_DECLS
+
+bool_t xdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *p);
+bool_t xdr_rpc_gss_cred(XDR *xdrs, struct rpc_gss_cred *p);
+bool_t xdr_rpc_gss_init_res(XDR *xdrs, struct rpc_gss_init_res *p);
+bool_t xdr_rpc_gss_wrap_data(XDR *xdrs, xdrproc_t xdr_func,
+ caddr_t xdr_ptr, gss_ctx_id_t ctx, gss_qop_t qop, rpc_gss_service_t svc,
+ u_int seq);
+bool_t xdr_rpc_gss_unwrap_data(XDR *xdrs, xdrproc_t xdr_func,
+ caddr_t xdr_ptr, gss_ctx_id_t ctx, gss_qop_t qop, rpc_gss_service_t svc,
+ u_int seq);
+const char *_rpc_gss_num_to_qop(const char *mech, u_int num);
+void _rpc_gss_set_error(int rpc_gss_error, int system_error);
+
+void log_debug(const char *fmt, ...);
+void log_status(const char *m, gss_OID mech, OM_uint32 major,
+ OM_uint32 minor);
+void log_hexdump(const u_char *buf, int len, int offset);
+
+__END_DECLS
+
+#endif /* !_RPCSEC_GSS_INT_H */
diff --git a/lib/librpcsec_gss/rpcsec_gss_misc.c b/lib/librpcsec_gss/rpcsec_gss_misc.c
new file mode 100644
index 000000000000..e9d09dee2206
--- /dev/null
+++ b/lib/librpcsec_gss/rpcsec_gss_misc.c
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2008 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+
+#include "rpcsec_gss_int.h"
+
+static rpc_gss_error_t _rpc_gss_error;
+
+void
+_rpc_gss_set_error(int rpc_gss_error, int system_error)
+{
+
+ _rpc_gss_error.rpc_gss_error = rpc_gss_error;
+ _rpc_gss_error.system_error = system_error;
+}
+
+void
+rpc_gss_get_error(rpc_gss_error_t *error)
+{
+
+ *error = _rpc_gss_error;
+}
diff --git a/lib/librpcsec_gss/rpcsec_gss_prot.c b/lib/librpcsec_gss/rpcsec_gss_prot.c
new file mode 100644
index 000000000000..930bb04ec5eb
--- /dev/null
+++ b/lib/librpcsec_gss/rpcsec_gss_prot.c
@@ -0,0 +1,288 @@
+/*
+ rpcsec_gss_prot.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ $Id: authgss_prot.c,v 1.18 2000/09/01 04:14:03 dugsong Exp $
+*/
+/* $FreeBSD$ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+#include "rpcsec_gss_int.h"
+
+#define MAX_GSS_SIZE 10240 /* XXX */
+
+bool_t
+xdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *p)
+{
+ char *val;
+ u_int len;
+ bool_t ret;
+
+ val = p->value;
+ len = p->length;
+ ret = xdr_bytes(xdrs, &val, &len, MAX_GSS_SIZE);
+ p->value = val;
+ p->length = len;
+
+ return (ret);
+}
+
+bool_t
+xdr_rpc_gss_cred(XDR *xdrs, struct rpc_gss_cred *p)
+{
+ enum_t proc, svc;
+ bool_t ret;
+
+ proc = p->gc_proc;
+ svc = p->gc_svc;
+ ret = (xdr_u_int(xdrs, &p->gc_version) &&
+ xdr_enum(xdrs, &proc) &&
+ xdr_u_int(xdrs, &p->gc_seq) &&
+ xdr_enum(xdrs, &svc) &&
+ xdr_gss_buffer_desc(xdrs, &p->gc_handle));
+ p->gc_proc = proc;
+ p->gc_svc = svc;
+
+ return (ret);
+}
+
+bool_t
+xdr_rpc_gss_init_res(XDR *xdrs, struct rpc_gss_init_res *p)
+{
+
+ return (xdr_gss_buffer_desc(xdrs, &p->gr_handle) &&
+ xdr_u_int(xdrs, &p->gr_major) &&
+ xdr_u_int(xdrs, &p->gr_minor) &&
+ xdr_u_int(xdrs, &p->gr_win) &&
+ xdr_gss_buffer_desc(xdrs, &p->gr_token));
+}
+
+bool_t
+xdr_rpc_gss_wrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr,
+ gss_ctx_id_t ctx, gss_qop_t qop,
+ rpc_gss_service_t svc, u_int seq)
+{
+ gss_buffer_desc databuf, wrapbuf;
+ OM_uint32 maj_stat, min_stat;
+ int start, end, conf_state;
+ bool_t xdr_stat;
+
+ /* Skip databody length. */
+ start = XDR_GETPOS(xdrs);
+ XDR_SETPOS(xdrs, start + 4);
+
+ /* Marshal rpc_gss_data_t (sequence number + arguments). */
+ if (!xdr_u_int(xdrs, &seq) || !xdr_func(xdrs, xdr_ptr))
+ return (FALSE);
+ end = XDR_GETPOS(xdrs);
+
+ /* Set databuf to marshalled rpc_gss_data_t. */
+ databuf.length = end - start - 4;
+ XDR_SETPOS(xdrs, start + 4);
+ databuf.value = XDR_INLINE(xdrs, databuf.length);
+
+ xdr_stat = FALSE;
+
+ if (svc == rpc_gss_svc_integrity) {
+ /* Marshal databody_integ length. */
+ XDR_SETPOS(xdrs, start);
+ if (!xdr_u_int(xdrs, &databuf.length))
+ return (FALSE);
+
+ /* Checksum rpc_gss_data_t. */
+ maj_stat = gss_get_mic(&min_stat, ctx, qop,
+ &databuf, &wrapbuf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ log_debug("gss_get_mic failed");
+ return (FALSE);
+ }
+ /* Marshal checksum. */
+ XDR_SETPOS(xdrs, end);
+ xdr_stat = xdr_gss_buffer_desc(xdrs, &wrapbuf);
+ gss_release_buffer(&min_stat, &wrapbuf);
+ }
+ else if (svc == rpc_gss_svc_privacy) {
+ /* Encrypt rpc_gss_data_t. */
+ maj_stat = gss_wrap(&min_stat, ctx, TRUE, qop, &databuf,
+ &conf_state, &wrapbuf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ log_status("gss_wrap", NULL, maj_stat, min_stat);
+ return (FALSE);
+ }
+ /* Marshal databody_priv. */
+ XDR_SETPOS(xdrs, start);
+ xdr_stat = xdr_gss_buffer_desc(xdrs, &wrapbuf);
+ gss_release_buffer(&min_stat, &wrapbuf);
+ }
+ return (xdr_stat);
+}
+
+bool_t
+xdr_rpc_gss_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr,
+ gss_ctx_id_t ctx, gss_qop_t qop,
+ rpc_gss_service_t svc, u_int seq)
+{
+ XDR tmpxdrs;
+ gss_buffer_desc databuf, wrapbuf;
+ OM_uint32 maj_stat, min_stat;
+ u_int seq_num, conf_state, qop_state;
+ bool_t xdr_stat;
+
+ if (xdr_func == (xdrproc_t) xdr_void || xdr_ptr == NULL)
+ return (TRUE);
+
+ memset(&databuf, 0, sizeof(databuf));
+ memset(&wrapbuf, 0, sizeof(wrapbuf));
+
+ if (svc == rpc_gss_svc_integrity) {
+ /* Decode databody_integ. */
+ if (!xdr_gss_buffer_desc(xdrs, &databuf)) {
+ log_debug("xdr decode databody_integ failed");
+ return (FALSE);
+ }
+ /* Decode checksum. */
+ if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) {
+ mem_free(databuf.value, databuf.length);
+ log_debug("xdr decode checksum failed");
+ return (FALSE);
+ }
+ /* Verify checksum and QOP. */
+ maj_stat = gss_verify_mic(&min_stat, ctx, &databuf,
+ &wrapbuf, &qop_state);
+ mem_free(wrapbuf.value, wrapbuf.length);
+
+ if (maj_stat != GSS_S_COMPLETE || qop_state != qop) {
+ mem_free(databuf.value, databuf.length);
+ log_status("gss_verify_mic", NULL, maj_stat, min_stat);
+ return (FALSE);
+ }
+ } else if (svc == rpc_gss_svc_privacy) {
+ /* Decode databody_priv. */
+ if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) {
+ log_debug("xdr decode databody_priv failed");
+ return (FALSE);
+ }
+ /* Decrypt databody. */
+ maj_stat = gss_unwrap(&min_stat, ctx, &wrapbuf, &databuf,
+ &conf_state, &qop_state);
+
+ mem_free(wrapbuf.value, wrapbuf.length);
+
+ /* Verify encryption and QOP. */
+ if (maj_stat != GSS_S_COMPLETE || qop_state != qop ||
+ conf_state != TRUE) {
+ gss_release_buffer(&min_stat, &databuf);
+ log_status("gss_unwrap", NULL, maj_stat, min_stat);
+ return (FALSE);
+ }
+ }
+ /* Decode rpc_gss_data_t (sequence number + arguments). */
+ xdrmem_create(&tmpxdrs, databuf.value, databuf.length, XDR_DECODE);
+ xdr_stat = (xdr_u_int(&tmpxdrs, &seq_num) &&
+ xdr_func(&tmpxdrs, xdr_ptr));
+ XDR_DESTROY(&tmpxdrs);
+
+ /*
+ * Integrity service allocates databuf via XDR so free it the
+ * same way.
+ */
+ if (svc == rpc_gss_svc_integrity) {
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &databuf);
+ } else {
+ gss_release_buffer(&min_stat, &databuf);
+ }
+
+ /* Verify sequence number. */
+ if (xdr_stat == TRUE && seq_num != seq) {
+ log_debug("wrong sequence number in databody");
+ return (FALSE);
+ }
+ return (xdr_stat);
+}
+
+#ifdef DEBUG
+#include <ctype.h>
+
+void
+log_debug(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "rpcsec_gss: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+void
+log_status(const char *m, gss_OID mech, OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+ OM_uint32 min;
+ gss_buffer_desc msg;
+ int msg_ctx = 0;
+
+ fprintf(stderr, "rpcsec_gss: %s: ", m);
+
+ gss_display_status(&min, maj_stat, GSS_C_GSS_CODE, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ fprintf(stderr, "%s - ", (char *)msg.value);
+ gss_release_buffer(&min, &msg);
+
+ gss_display_status(&min, min_stat, GSS_C_MECH_CODE, mech,
+ &msg_ctx, &msg);
+ fprintf(stderr, "%s\n", (char *)msg.value);
+ gss_release_buffer(&min, &msg);
+}
+
+#else
+
+void
+log_debug(__unused const char *fmt, ...)
+{
+}
+
+void
+log_status(__unused const char *m, __unused gss_OID mech,
+ __unused OM_uint32 maj_stat, __unused OM_uint32 min_stat)
+{
+}
+
+#endif
+
+
diff --git a/lib/librpcsec_gss/svc_rpcsec_gss.c b/lib/librpcsec_gss/svc_rpcsec_gss.c
new file mode 100644
index 000000000000..7eff62aeafe3
--- /dev/null
+++ b/lib/librpcsec_gss/svc_rpcsec_gss.c
@@ -0,0 +1,1214 @@
+/*-
+ * Copyright (c) 2008 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ svc_rpcsec_gss.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/queue.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+#include "rpcsec_gss_int.h"
+
+static bool_t svc_rpc_gss_initialised = FALSE;
+
+static bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
+static bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
+static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
+
+static struct svc_auth_ops svc_auth_gss_ops = {
+ svc_rpc_gss_wrap,
+ svc_rpc_gss_unwrap,
+};
+
+struct svc_rpc_gss_callback {
+ SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
+ rpc_gss_callback_t cb_callback;
+};
+static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
+ svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(&svc_rpc_gss_callbacks);
+
+struct svc_rpc_gss_svc_name {
+ SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
+ char *sn_principal;
+ gss_OID sn_mech;
+ u_int sn_req_time;
+ gss_cred_id_t sn_cred;
+ u_int sn_program;
+ u_int sn_version;
+};
+static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
+ svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(&svc_rpc_gss_svc_names);
+
+enum svc_rpc_gss_client_state {
+ CLIENT_NEW, /* still authenticating */
+ CLIENT_ESTABLISHED, /* context established */
+ CLIENT_STALE /* garbage to collect */
+};
+
+#define SVC_RPC_GSS_SEQWINDOW 128
+
+struct svc_rpc_gss_client {
+ TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
+ TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
+ uint32_t cl_id;
+ time_t cl_expiration; /* when to gc */
+ enum svc_rpc_gss_client_state cl_state; /* client state */
+ bool_t cl_locked; /* fixed service+qop */
+ gss_ctx_id_t cl_ctx; /* context id */
+ gss_cred_id_t cl_creds; /* delegated creds */
+ gss_name_t cl_cname; /* client name */
+ struct svc_rpc_gss_svc_name *cl_sname; /* server name used */
+ rpc_gss_rawcred_t cl_rawcred; /* raw credentials */
+ rpc_gss_ucred_t cl_ucred; /* unix-style credentials */
+ bool_t cl_done_callback; /* TRUE after call */
+ void *cl_cookie; /* user cookie from callback */
+ gid_t cl_gid_storage[NGRPS];
+ gss_OID cl_mech; /* mechanism */
+ gss_qop_t cl_qop; /* quality of protection */
+ u_int cl_seq; /* current sequence number */
+ u_int cl_win; /* sequence window size */
+ u_int cl_seqlast; /* sequence window origin */
+ uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
+ gss_buffer_desc cl_verf; /* buffer for verf checksum */
+};
+TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
+
+#define CLIENT_HASH_SIZE 256
+#define CLIENT_MAX 128
+struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
+struct svc_rpc_gss_client_list svc_rpc_gss_clients;
+static size_t svc_rpc_gss_client_count;
+static uint32_t svc_rpc_gss_next_clientid = 1;
+
+#ifdef __GNUC__
+static void svc_rpc_gss_init(void) __attribute__ ((constructor));
+#endif
+
+static void
+svc_rpc_gss_init(void)
+{
+ int i;
+
+ if (!svc_rpc_gss_initialised) {
+ for (i = 0; i < CLIENT_HASH_SIZE; i++)
+ TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
+ TAILQ_INIT(&svc_rpc_gss_clients);
+ svc_auth_reg(RPCSEC_GSS, svc_rpc_gss);
+ svc_rpc_gss_initialised = TRUE;
+ }
+}
+
+bool_t
+rpc_gss_set_callback(rpc_gss_callback_t *cb)
+{
+ struct svc_rpc_gss_callback *scb;
+
+ scb = malloc(sizeof(struct svc_rpc_gss_callback));
+ if (!scb) {
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
+ return (FALSE);
+ }
+ scb->cb_callback = *cb;
+ SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
+
+ return (TRUE);
+}
+
+bool_t
+rpc_gss_set_svc_name(const char *principal, const char *mechanism,
+ u_int req_time, u_int program, u_int version)
+{
+ OM_uint32 maj_stat, min_stat;
+ struct svc_rpc_gss_svc_name *sname;
+ gss_buffer_desc namebuf;
+ gss_name_t name;
+ gss_OID mech_oid;
+ gss_OID_set_desc oid_set;
+ gss_cred_id_t cred;
+
+ svc_rpc_gss_init();
+
+ if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
+ return (FALSE);
+ oid_set.count = 1;
+ oid_set.elements = mech_oid;
+
+ namebuf.value = (void *)(intptr_t) principal;
+ namebuf.length = strlen(principal);
+
+ maj_stat = gss_import_name(&min_stat, &namebuf,
+ GSS_C_NT_HOSTBASED_SERVICE, &name);
+ if (maj_stat != GSS_S_COMPLETE)
+ return (FALSE);
+
+ maj_stat = gss_acquire_cred(&min_stat, name,
+ req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE)
+ return (FALSE);
+
+ gss_release_name(&min_stat, &name);
+
+ sname = malloc(sizeof(struct svc_rpc_gss_svc_name));
+ if (!sname)
+ return (FALSE);
+ sname->sn_principal = strdup(principal);
+ sname->sn_mech = mech_oid;
+ sname->sn_req_time = req_time;
+ sname->sn_cred = cred;
+ sname->sn_program = program;
+ sname->sn_version = version;
+ SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
+
+ return (TRUE);
+}
+
+bool_t
+rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
+ const char *mech, const char *name, const char *node, const char *domain)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_OID mech_oid;
+ size_t namelen;
+ gss_buffer_desc buf;
+ gss_name_t gss_name, gss_mech_name;
+ rpc_gss_principal_t result;
+
+ svc_rpc_gss_init();
+
+ if (!rpc_gss_mech_to_oid(mech, &mech_oid))
+ return (FALSE);
+
+ /*
+ * Construct a gss_buffer containing the full name formatted
+ * as "name/node@domain" where node and domain are optional.
+ */
+ namelen = strlen(name);
+ if (node) {
+ namelen += strlen(node) + 1;
+ }
+ if (domain) {
+ namelen += strlen(domain) + 1;
+ }
+
+ buf.value = malloc(namelen);
+ buf.length = namelen;
+ strcpy((char *) buf.value, name);
+ if (node) {
+ strcat((char *) buf.value, "/");
+ strcat((char *) buf.value, node);
+ }
+ if (domain) {
+ strcat((char *) buf.value, "@");
+ strcat((char *) buf.value, domain);
+ }
+
+ /*
+ * Convert that to a gss_name_t and then convert that to a
+ * mechanism name in the selected mechanism.
+ */
+ maj_stat = gss_import_name(&min_stat, &buf,
+ GSS_C_NT_USER_NAME, &gss_name);
+ free(buf.value);
+ if (maj_stat != GSS_S_COMPLETE) {
+ log_status("gss_import_name", mech_oid, maj_stat, min_stat);
+ return (FALSE);
+ }
+ maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
+ &gss_mech_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ log_status("gss_canonicalize_name", mech_oid, maj_stat,
+ min_stat);
+ gss_release_name(&min_stat, &gss_name);
+ return (FALSE);
+ }
+ gss_release_name(&min_stat, &gss_name);
+
+ /*
+ * Export the mechanism name and use that to construct the
+ * rpc_gss_principal_t result.
+ */
+ maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ log_status("gss_export_name", mech_oid, maj_stat, min_stat);
+ gss_release_name(&min_stat, &gss_mech_name);
+ return (FALSE);
+ }
+ gss_release_name(&min_stat, &gss_mech_name);
+
+ result = malloc(sizeof(int) + buf.length);
+ if (!result) {
+ gss_release_buffer(&min_stat, &buf);
+ return (FALSE);
+ }
+ result->len = buf.length;
+ memcpy(result->name, buf.value, buf.length);
+ gss_release_buffer(&min_stat, &buf);
+
+ *principal = result;
+ return (TRUE);
+}
+
+bool_t
+rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
+ rpc_gss_ucred_t **ucred, void **cookie)
+{
+ struct svc_rpc_gss_client *client;
+
+ if (req->rq_cred.oa_flavor != RPCSEC_GSS)
+ return (FALSE);
+
+ client = req->rq_clntcred;
+ if (rcred)
+ *rcred = &client->cl_rawcred;
+ if (ucred)
+ *ucred = &client->cl_ucred;
+ if (cookie)
+ *cookie = client->cl_cookie;
+ return (TRUE);
+}
+
+int
+rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
+{
+ struct svc_rpc_gss_client *client = req->rq_clntcred;
+ int want_conf;
+ OM_uint32 max;
+ OM_uint32 maj_stat, min_stat;
+ int result;
+
+ switch (client->cl_rawcred.service) {
+ case rpc_gss_svc_none:
+ return (max_tp_unit_len);
+ break;
+
+ case rpc_gss_svc_default:
+ case rpc_gss_svc_integrity:
+ want_conf = FALSE;
+ break;
+
+ case rpc_gss_svc_privacy:
+ want_conf = TRUE;
+ break;
+
+ default:
+ return (0);
+ }
+
+ maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
+ client->cl_qop, max_tp_unit_len, &max);
+
+ if (maj_stat == GSS_S_COMPLETE) {
+ result = (int) max;
+ if (result < 0)
+ result = 0;
+ return (result);
+ } else {
+ log_status("gss_wrap_size_limit", client->cl_mech,
+ maj_stat, min_stat);
+ return (0);
+ }
+}
+
+static struct svc_rpc_gss_client *
+svc_rpc_gss_find_client(uint32_t clientid)
+{
+ struct svc_rpc_gss_client *client;
+ struct svc_rpc_gss_client_list *list;
+
+
+ log_debug("in svc_rpc_gss_find_client(%d)", clientid);
+
+ list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE];
+ TAILQ_FOREACH(client, list, cl_link) {
+ if (client->cl_id == clientid) {
+ /*
+ * Move this client to the front of the LRU
+ * list.
+ */
+ TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
+ TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
+ cl_alllink);
+ return client;
+ }
+ }
+
+ return (NULL);
+}
+
+static struct svc_rpc_gss_client *
+svc_rpc_gss_create_client(void)
+{
+ struct svc_rpc_gss_client *client;
+ struct svc_rpc_gss_client_list *list;
+
+ log_debug("in svc_rpc_gss_create_client()");
+
+ client = mem_alloc(sizeof(struct svc_rpc_gss_client));
+ memset(client, 0, sizeof(struct svc_rpc_gss_client));
+ client->cl_id = svc_rpc_gss_next_clientid++;
+ list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
+ TAILQ_INSERT_HEAD(list, client, cl_link);
+ TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
+
+ /*
+ * Start the client off with a short expiration time. We will
+ * try to get a saner value from the client creds later.
+ */
+ client->cl_state = CLIENT_NEW;
+ client->cl_locked = FALSE;
+ client->cl_expiration = time(0) + 5*60;
+ svc_rpc_gss_client_count++;
+
+ return (client);
+}
+
+static void
+svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
+{
+ struct svc_rpc_gss_client_list *list;
+ OM_uint32 min_stat;
+
+ log_debug("in svc_rpc_gss_destroy_client()");
+
+ if (client->cl_ctx)
+ gss_delete_sec_context(&min_stat,
+ &client->cl_ctx, GSS_C_NO_BUFFER);
+
+ if (client->cl_cname)
+ gss_release_name(&min_stat, &client->cl_cname);
+
+ if (client->cl_rawcred.client_principal)
+ free(client->cl_rawcred.client_principal);
+
+ if (client->cl_verf.value)
+ gss_release_buffer(&min_stat, &client->cl_verf);
+
+ list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
+ TAILQ_REMOVE(list, client, cl_link);
+ TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
+ svc_rpc_gss_client_count--;
+ mem_free(client, sizeof(*client));
+}
+
+static void
+svc_rpc_gss_timeout_clients(void)
+{
+ struct svc_rpc_gss_client *client;
+ struct svc_rpc_gss_client *nclient;
+ time_t now = time(0);
+
+ log_debug("in svc_rpc_gss_timeout_clients()");
+ /*
+ * First enforce the max client limit. We keep
+ * svc_rpc_gss_clients in LRU order.
+ */
+ while (svc_rpc_gss_client_count > CLIENT_MAX)
+ svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
+ svc_rpc_gss_client_list));
+ TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
+ if (client->cl_state == CLIENT_STALE
+ || now > client->cl_expiration) {
+ log_debug("expiring client %p", client);
+ svc_rpc_gss_destroy_client(client);
+ }
+ }
+}
+
+#ifdef DEBUG
+/*
+ * OID<->string routines. These are uuuuugly.
+ */
+static OM_uint32
+gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
+{
+ char numstr[128];
+ unsigned long number;
+ int numshift;
+ size_t string_length;
+ size_t i;
+ unsigned char *cp;
+ char *bp;
+
+ /* Decoded according to krb5/gssapi_krb5.c */
+
+ /* First determine the size of the string */
+ string_length = 0;
+ number = 0;
+ numshift = 0;
+ cp = (unsigned char *) oid->elements;
+ number = (unsigned long) cp[0];
+ sprintf(numstr, "%ld ", number/40);
+ string_length += strlen(numstr);
+ sprintf(numstr, "%ld ", number%40);
+ string_length += strlen(numstr);
+ for (i=1; i<oid->length; i++) {
+ if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
+ number = (number << 7) | (cp[i] & 0x7f);
+ numshift += 7;
+ }
+ else {
+ *minor_status = 0;
+ return(GSS_S_FAILURE);
+ }
+ if ((cp[i] & 0x80) == 0) {
+ sprintf(numstr, "%ld ", number);
+ string_length += strlen(numstr);
+ number = 0;
+ numshift = 0;
+ }
+ }
+ /*
+ * If we get here, we've calculated the length of "n n n ... n ". Add 4
+ * here for "{ " and "}\0".
+ */
+ string_length += 4;
+ if ((bp = (char *) malloc(string_length))) {
+ strcpy(bp, "{ ");
+ number = (unsigned long) cp[0];
+ sprintf(numstr, "%ld ", number/40);
+ strcat(bp, numstr);
+ sprintf(numstr, "%ld ", number%40);
+ strcat(bp, numstr);
+ number = 0;
+ cp = (unsigned char *) oid->elements;
+ for (i=1; i<oid->length; i++) {
+ number = (number << 7) | (cp[i] & 0x7f);
+ if ((cp[i] & 0x80) == 0) {
+ sprintf(numstr, "%ld ", number);
+ strcat(bp, numstr);
+ number = 0;
+ }
+ }
+ strcat(bp, "}");
+ oid_str->length = strlen(bp)+1;
+ oid_str->value = (void *) bp;
+ *minor_status = 0;
+ return(GSS_S_COMPLETE);
+ }
+ *minor_status = 0;
+ return(GSS_S_FAILURE);
+}
+#endif
+
+static void
+svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
+ const gss_name_t name)
+{
+ OM_uint32 maj_stat, min_stat;
+ char buf[128];
+ uid_t uid;
+ struct passwd pwd, *pw;
+ rpc_gss_ucred_t *uc = &client->cl_ucred;
+
+ uc->uid = 65534;
+ uc->gid = 65534;
+ uc->gidlen = 0;
+ uc->gidlist = client->cl_gid_storage;
+
+ maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
+ if (maj_stat != GSS_S_COMPLETE)
+ return;
+
+ getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
+ if (pw) {
+ int len = NGRPS;
+ uc->uid = pw->pw_uid;
+ uc->gid = pw->pw_gid;
+ uc->gidlist = client->cl_gid_storage;
+ getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
+ uc->gidlen = len;
+ }
+}
+
+static bool_t
+svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
+ struct svc_req *rqst,
+ struct rpc_gss_init_res *gr,
+ struct rpc_gss_cred *gc)
+{
+ gss_buffer_desc recv_tok;
+ gss_OID mech;
+ OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
+ OM_uint32 cred_lifetime;
+ struct svc_rpc_gss_svc_name *sname;
+
+ log_debug("in svc_rpc_gss_accept_context()");
+
+ /* Deserialize arguments. */
+ memset(&recv_tok, 0, sizeof(recv_tok));
+
+ if (!svc_getargs(rqst->rq_xprt,
+ (xdrproc_t) xdr_gss_buffer_desc,
+ (caddr_t) &recv_tok)) {
+ client->cl_state = CLIENT_STALE;
+ return (FALSE);
+ }
+
+ /*
+ * First time round, try all the server names we have until
+ * one matches. Afterwards, stick with that one.
+ */
+ if (!client->cl_sname) {
+ SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
+ if (sname->sn_program == rqst->rq_prog
+ && sname->sn_version == rqst->rq_vers) {
+ gr->gr_major = gss_accept_sec_context(
+ &gr->gr_minor,
+ &client->cl_ctx,
+ sname->sn_cred,
+ &recv_tok,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client->cl_cname,
+ &mech,
+ &gr->gr_token,
+ &ret_flags,
+ &cred_lifetime,
+ &client->cl_creds);
+ if (gr->gr_major == GSS_S_COMPLETE
+ || gr->gr_major == GSS_S_CONTINUE_NEEDED) {
+ client->cl_sname = sname;
+ break;
+ }
+ }
+ }
+ } else {
+ gr->gr_major = gss_accept_sec_context(
+ &gr->gr_minor,
+ &client->cl_ctx,
+ client->cl_sname->sn_cred,
+ &recv_tok,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client->cl_cname,
+ &mech,
+ &gr->gr_token,
+ &ret_flags,
+ &cred_lifetime,
+ NULL);
+ }
+
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
+
+ /*
+ * If we get an error from gss_accept_sec_context, send the
+ * reply anyway so that the client gets a chance to see what
+ * is wrong.
+ */
+ if (gr->gr_major != GSS_S_COMPLETE &&
+ gr->gr_major != GSS_S_CONTINUE_NEEDED) {
+ log_status("accept_sec_context", client->cl_mech,
+ gr->gr_major, gr->gr_minor);
+ client->cl_state = CLIENT_STALE;
+ return (FALSE);
+ }
+
+ gr->gr_handle.value = &client->cl_id;
+ gr->gr_handle.length = sizeof(uint32_t);
+ gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
+
+ /* Save client info. */
+ client->cl_mech = mech;
+ client->cl_qop = GSS_C_QOP_DEFAULT;
+ client->cl_seq = gc->gc_seq;
+ client->cl_win = gr->gr_win;
+ client->cl_done_callback = FALSE;
+
+ if (gr->gr_major == GSS_S_COMPLETE) {
+ gss_buffer_desc export_name;
+
+ /*
+ * Change client expiration time to be near when the
+ * client creds expire (or 24 hours if we can't figure
+ * that out).
+ */
+ if (cred_lifetime == GSS_C_INDEFINITE)
+ cred_lifetime = time(0) + 24*60*60;
+
+ client->cl_expiration = time(0) + cred_lifetime;
+
+ /*
+ * Fill in cred details in the rawcred structure.
+ */
+ client->cl_rawcred.version = RPCSEC_GSS_VERSION;
+ rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
+ maj_stat = gss_export_name(&min_stat, client->cl_cname,
+ &export_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ log_status("gss_export_name", client->cl_mech,
+ maj_stat, min_stat);
+ return (FALSE);
+ }
+ client->cl_rawcred.client_principal =
+ malloc(sizeof(*client->cl_rawcred.client_principal)
+ + export_name.length);
+ client->cl_rawcred.client_principal->len = export_name.length;
+ memcpy(client->cl_rawcred.client_principal->name,
+ export_name.value, export_name.length);
+ gss_release_buffer(&min_stat, &export_name);
+ client->cl_rawcred.svc_principal =
+ client->cl_sname->sn_principal;
+ client->cl_rawcred.service = gc->gc_svc;
+
+ /*
+ * Use gss_pname_to_uid to map to unix creds. For
+ * kerberos5, this uses krb5_aname_to_localname.
+ */
+ svc_rpc_gss_build_ucred(client, client->cl_cname);
+
+#ifdef DEBUG
+ {
+ gss_buffer_desc mechname;
+
+ gss_oid_to_str(&min_stat, mech, &mechname);
+
+ log_debug("accepted context for %s with "
+ "<mech %.*s, qop %d, svc %d>",
+ client->cl_rawcred.client_principal->name,
+ mechname.length, (char *)mechname.value,
+ client->cl_qop, client->rawcred.service);
+
+ gss_release_buffer(&min_stat, &mechname);
+ }
+#endif /* DEBUG */
+ }
+ return (TRUE);
+}
+
+static bool_t
+svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
+ gss_qop_t *qop)
+{
+ struct opaque_auth *oa;
+ gss_buffer_desc rpcbuf, checksum;
+ OM_uint32 maj_stat, min_stat;
+ gss_qop_t qop_state;
+ u_char rpchdr[128];
+ int32_t *buf;
+
+ log_debug("in svc_rpc_gss_validate()");
+
+ memset(rpchdr, 0, sizeof(rpchdr));
+
+ /* Reconstruct RPC header for signing (from xdr_callmsg). */
+ buf = (int32_t *)rpchdr;
+ IXDR_PUT_LONG(buf, msg->rm_xid);
+ IXDR_PUT_ENUM(buf, msg->rm_direction);
+ IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
+ IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
+ IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
+ IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
+ oa = &msg->rm_call.cb_cred;
+ IXDR_PUT_ENUM(buf, oa->oa_flavor);
+ IXDR_PUT_LONG(buf, oa->oa_length);
+ if (oa->oa_length) {
+ memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
+ buf += RNDUP(oa->oa_length) / sizeof(int32_t);
+ }
+ rpcbuf.value = rpchdr;
+ rpcbuf.length = (u_char *)buf - rpchdr;
+
+ checksum.value = msg->rm_call.cb_verf.oa_base;
+ checksum.length = msg->rm_call.cb_verf.oa_length;
+
+ maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
+ &qop_state);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ log_status("gss_verify_mic", client->cl_mech,
+ maj_stat, min_stat);
+ client->cl_state = CLIENT_STALE;
+ return (FALSE);
+ }
+ *qop = qop_state;
+ return (TRUE);
+}
+
+static bool_t
+svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
+ struct svc_req *rqst, u_int seq)
+{
+ gss_buffer_desc signbuf;
+ OM_uint32 maj_stat, min_stat;
+ uint32_t nseq;
+
+ log_debug("in svc_rpc_gss_nextverf()");
+
+ nseq = htonl(seq);
+ signbuf.value = &nseq;
+ signbuf.length = sizeof(nseq);
+
+ if (client->cl_verf.value)
+ gss_release_buffer(&min_stat, &client->cl_verf);
+
+ maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
+ &signbuf, &client->cl_verf);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
+ client->cl_state = CLIENT_STALE;
+ return (FALSE);
+ }
+ rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
+ rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
+ rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
+
+ return (TRUE);
+}
+
+static bool_t
+svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
+{
+ struct svc_rpc_gss_callback *scb;
+ rpc_gss_lock_t lock;
+ void *cookie;
+ bool_t cb_res;
+ bool_t result;
+
+ /*
+ * See if we have a callback for this guy.
+ */
+ result = TRUE;
+ SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
+ if (scb->cb_callback.program == rqst->rq_prog
+ && scb->cb_callback.version == rqst->rq_vers) {
+ /*
+ * This one matches. Call the callback and see
+ * if it wants to veto or something.
+ */
+ lock.locked = FALSE;
+ lock.raw_cred = &client->cl_rawcred;
+ cb_res = scb->cb_callback.callback(rqst,
+ client->cl_creds,
+ client->cl_ctx,
+ &lock,
+ &cookie);
+
+ if (!cb_res) {
+ client->cl_state = CLIENT_STALE;
+ result = FALSE;
+ break;
+ }
+
+ /*
+ * The callback accepted the connection - it
+ * is responsible for freeing client->cl_creds
+ * now.
+ */
+ client->cl_creds = GSS_C_NO_CREDENTIAL;
+ client->cl_locked = lock.locked;
+ client->cl_cookie = cookie;
+ return (TRUE);
+ }
+ }
+
+ /*
+ * Either no callback exists for this program/version or one
+ * of the callbacks rejected the connection. We just need to
+ * clean up the delegated client creds, if any.
+ */
+ if (client->cl_creds) {
+ OM_uint32 min_ver;
+ gss_release_cred(&min_ver, &client->cl_creds);
+ }
+ return (result);
+}
+
+static bool_t
+svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
+{
+ u_int32_t offset;
+ int word, bit;
+
+ if (seq < client->cl_seqlast) {
+ /*
+ * The request sequence number is less than
+ * the largest we have seen so far. If it is
+ * outside the window or if we have seen a
+ * request with this sequence before, silently
+ * discard it.
+ */
+ offset = client->cl_seqlast - seq;
+ if (offset >= client->cl_win)
+ return (FALSE);
+ word = offset / 32;
+ bit = offset % 32;
+ if (client->cl_seqmask[word] & (1 << bit))
+ return (FALSE);
+ client->cl_seqmask[word] |= (1 << bit);
+ }
+
+ return (TRUE);
+}
+
+static void
+svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
+{
+ int offset, i;
+ uint32_t carry, newcarry;
+
+ if (seq > client->cl_seqlast) {
+ /*
+ * This request has a sequence number greater
+ * than any we have seen so far. Advance the
+ * seq window and set bit zero of the window
+ * (which corresponds to the new sequence
+ * number)
+ */
+ offset = seq - client->cl_seqlast;
+ while (offset > 32) {
+ for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
+ i > 0; i--) {
+ client->cl_seqmask[i] = client->cl_seqmask[i-1];
+ }
+ client->cl_seqmask[0] = 0;
+ offset -= 32;
+ }
+ carry = 0;
+ for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
+ newcarry = client->cl_seqmask[i] >> (32 - offset);
+ client->cl_seqmask[i] =
+ (client->cl_seqmask[i] << offset) | carry;
+ carry = newcarry;
+ }
+ client->cl_seqmask[0] |= 1;
+ client->cl_seqlast = seq;
+ }
+}
+
+enum auth_stat
+svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
+
+{
+ OM_uint32 min_stat;
+ XDR xdrs;
+ struct svc_rpc_gss_client *client;
+ struct rpc_gss_cred gc;
+ struct rpc_gss_init_res gr;
+ gss_qop_t qop;
+ int call_stat;
+ enum auth_stat result;
+
+ log_debug("in svc_rpc_gss()");
+
+ /* Garbage collect old clients. */
+ svc_rpc_gss_timeout_clients();
+
+ /* Initialize reply. */
+ rqst->rq_xprt->xp_verf = _null_auth;
+
+ /* Deserialize client credentials. */
+ if (rqst->rq_cred.oa_length <= 0)
+ return (AUTH_BADCRED);
+
+ memset(&gc, 0, sizeof(gc));
+
+ xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
+ rqst->rq_cred.oa_length, XDR_DECODE);
+
+ if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
+ XDR_DESTROY(&xdrs);
+ return (AUTH_BADCRED);
+ }
+ XDR_DESTROY(&xdrs);
+
+ /* Check version. */
+ if (gc.gc_version != RPCSEC_GSS_VERSION) {
+ result = AUTH_BADCRED;
+ goto out;
+ }
+
+ /* Check the proc and find the client (or create it) */
+ if (gc.gc_proc == RPCSEC_GSS_INIT) {
+ client = svc_rpc_gss_create_client();
+ } else {
+ if (gc.gc_handle.length != sizeof(uint32_t)) {
+ result = AUTH_BADCRED;
+ goto out;
+ }
+ uint32_t *p = gc.gc_handle.value;
+ client = svc_rpc_gss_find_client(*p);
+ if (!client) {
+ /*
+ * Can't find the client - we may have
+ * destroyed it - tell the other side to
+ * re-authenticate.
+ */
+ result = RPCSEC_GSS_CREDPROBLEM;
+ goto out;
+ }
+ }
+ rqst->rq_clntcred = client;
+
+ /*
+ * The service and sequence number must be ignored for
+ * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
+ */
+ if (gc.gc_proc != RPCSEC_GSS_INIT
+ && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
+ /*
+ * Check for sequence number overflow.
+ */
+ if (gc.gc_seq >= MAXSEQ) {
+ result = RPCSEC_GSS_CTXPROBLEM;
+ goto out;
+ }
+ client->cl_seq = gc.gc_seq;
+
+ /*
+ * Check for valid service.
+ */
+ if (gc.gc_svc != rpc_gss_svc_none &&
+ gc.gc_svc != rpc_gss_svc_integrity &&
+ gc.gc_svc != rpc_gss_svc_privacy) {
+ result = AUTH_BADCRED;
+ goto out;
+ }
+ }
+
+ /* Handle RPCSEC_GSS control procedure. */
+ switch (gc.gc_proc) {
+
+ case RPCSEC_GSS_INIT:
+ case RPCSEC_GSS_CONTINUE_INIT:
+ if (rqst->rq_proc != NULLPROC) {
+ result = AUTH_REJECTEDCRED;
+ break;
+ }
+
+ memset(&gr, 0, sizeof(gr));
+ if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
+ result = AUTH_REJECTEDCRED;
+ break;
+ }
+
+ if (gr.gr_major == GSS_S_COMPLETE) {
+ if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
+ result = AUTH_REJECTEDCRED;
+ break;
+ }
+ } else {
+ rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
+ rqst->rq_xprt->xp_verf.oa_length = 0;
+ }
+
+ call_stat = svc_sendreply(rqst->rq_xprt,
+ (xdrproc_t) xdr_rpc_gss_init_res,
+ (caddr_t) &gr);
+
+ gss_release_buffer(&min_stat, &gr.gr_token);
+
+ if (!call_stat) {
+ result = AUTH_FAILED;
+ break;
+ }
+
+ if (gr.gr_major == GSS_S_COMPLETE)
+ client->cl_state = CLIENT_ESTABLISHED;
+
+ result = RPCSEC_GSS_NODISPATCH;
+ break;
+
+ case RPCSEC_GSS_DATA:
+ case RPCSEC_GSS_DESTROY:
+ if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
+ result = RPCSEC_GSS_NODISPATCH;
+ break;
+ }
+
+ if (!svc_rpc_gss_validate(client, msg, &qop)) {
+ result = RPCSEC_GSS_CREDPROBLEM;
+ break;
+ }
+
+ if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
+ result = RPCSEC_GSS_CTXPROBLEM;
+ break;
+ }
+
+ svc_rpc_gss_update_seq(client, gc.gc_seq);
+
+ /*
+ * Change the SVCAUTH ops on the transport to point at
+ * our own code so that we can unwrap the arguments
+ * and wrap the result. The caller will re-set this on
+ * every request to point to a set of null wrap/unwrap
+ * methods.
+ */
+ SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
+ SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
+
+ if (gc.gc_proc == RPCSEC_GSS_DATA) {
+ /*
+ * We might be ready to do a callback to the server to
+ * see if it wants to accept/reject the connection.
+ */
+ if (!client->cl_done_callback) {
+ client->cl_done_callback = TRUE;
+ client->cl_qop = qop;
+ client->cl_rawcred.qop = _rpc_gss_num_to_qop(
+ client->cl_rawcred.mechanism, qop);
+ if (!svc_rpc_gss_callback(client, rqst)) {
+ result = AUTH_REJECTEDCRED;
+ break;
+ }
+ }
+
+ /*
+ * If the server has locked this client to a
+ * particular service+qop pair, enforce that
+ * restriction now.
+ */
+ if (client->cl_locked) {
+ if (client->cl_rawcred.service != gc.gc_svc) {
+ result = AUTH_FAILED;
+ break;
+ } else if (client->cl_qop != qop) {
+ result = AUTH_BADVERF;
+ break;
+ }
+ }
+
+ /*
+ * If the qop changed, look up the new qop
+ * name for rawcred.
+ */
+ if (client->cl_qop != qop) {
+ client->cl_qop = qop;
+ client->cl_rawcred.qop = _rpc_gss_num_to_qop(
+ client->cl_rawcred.mechanism, qop);
+ }
+
+ /*
+ * Make sure we use the right service value
+ * for unwrap/wrap.
+ */
+ client->cl_rawcred.service = gc.gc_svc;
+
+ result = AUTH_OK;
+ } else {
+ if (rqst->rq_proc != NULLPROC) {
+ result = AUTH_REJECTEDCRED;
+ break;
+ }
+
+ call_stat = svc_sendreply(rqst->rq_xprt,
+ (xdrproc_t) xdr_void, (caddr_t) NULL);
+
+ if (!call_stat) {
+ result = AUTH_FAILED;
+ break;
+ }
+
+ svc_rpc_gss_destroy_client(client);
+
+ result = RPCSEC_GSS_NODISPATCH;
+ break;
+ }
+ break;
+
+ default:
+ result = AUTH_BADCRED;
+ break;
+ }
+out:
+ xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
+ return (result);
+}
+
+bool_t
+svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
+{
+ struct svc_rpc_gss_client *client;
+
+ log_debug("in svc_rpc_gss_wrap()");
+
+ client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
+ if (client->cl_state != CLIENT_ESTABLISHED
+ || client->cl_rawcred.service == rpc_gss_svc_none) {
+ return xdr_func(xdrs, xdr_ptr);
+ }
+ return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
+ client->cl_ctx, client->cl_qop,
+ client->cl_rawcred.service, client->cl_seq));
+}
+
+bool_t
+svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
+{
+ struct svc_rpc_gss_client *client;
+
+ log_debug("in svc_rpc_gss_unwrap()");
+
+ client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
+ if (client->cl_state != CLIENT_ESTABLISHED
+ || client->cl_rawcred.service == rpc_gss_svc_none) {
+ return xdr_func(xdrs, xdr_ptr);
+ }
+ return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
+ client->cl_ctx, client->cl_qop,
+ client->cl_rawcred.service, client->cl_seq));
+}