summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/gss/mech2
-rwxr-xr-xetc/rc.d/Makefile2
-rwxr-xr-xetc/rc.d/gssd18
-rwxr-xr-xetc/rc.d/nfsd2
-rw-r--r--include/rpc/xdr.h3
-rw-r--r--lib/libc/sys/Symbol.map1
-rw-r--r--lib/libc/xdr/Symbol.map6
-rw-r--r--lib/libc/xdr/xdr.c92
-rw-r--r--lib/librpcsec_gss/svc_rpcsec_gss.c43
-rw-r--r--sbin/mount_nfs/mount_nfs.c54
-rw-r--r--sys/compat/freebsd32/syscalls.master2
-rw-r--r--sys/conf/files123
-rw-r--r--sys/conf/options9
-rw-r--r--sys/fs/unionfs/union_vfsops.c2
-rw-r--r--sys/kern/syscalls.master2
-rw-r--r--sys/kern/vfs_export.c18
-rw-r--r--sys/kern/vfs_mount.c14
-rw-r--r--sys/kgssapi/gss_accept_sec_context.c138
-rw-r--r--sys/kgssapi/gss_acquire_cred.c105
-rw-r--r--sys/kgssapi/gss_add_oid_set_member.c76
-rw-r--r--sys/kgssapi/gss_canonicalize_name.c76
-rw-r--r--sys/kgssapi/gss_create_empty_oid_set.c55
-rw-r--r--sys/kgssapi/gss_delete_sec_context.c91
-rw-r--r--sys/kgssapi/gss_display_status.c79
-rw-r--r--sys/kgssapi/gss_export_name.c71
-rw-r--r--sys/kgssapi/gss_get_mic.c89
-rw-r--r--sys/kgssapi/gss_impl.c266
-rw-r--r--sys/kgssapi/gss_import_name.c79
-rw-r--r--sys/kgssapi/gss_init_sec_context.c135
-rw-r--r--sys/kgssapi/gss_names.c176
-rw-r--r--sys/kgssapi/gss_pname_to_uid.c122
-rw-r--r--sys/kgssapi/gss_release_buffer.c52
-rw-r--r--sys/kgssapi/gss_release_cred.c69
-rw-r--r--sys/kgssapi/gss_release_name.c74
-rw-r--r--sys/kgssapi/gss_release_oid_set.c52
-rw-r--r--sys/kgssapi/gss_set_cred_option.c77
-rw-r--r--sys/kgssapi/gss_test_oid_set_member.c54
-rw-r--r--sys/kgssapi/gss_unwrap.c97
-rw-r--r--sys/kgssapi/gss_verify_mic.c87
-rw-r--r--sys/kgssapi/gss_wrap.c96
-rw-r--r--sys/kgssapi/gss_wrap_size_limit.c56
-rw-r--r--sys/kgssapi/gssapi.h620
-rw-r--r--sys/kgssapi/gssapi_impl.h67
-rw-r--r--sys/kgssapi/gssd.x265
-rw-r--r--sys/kgssapi/gssd_prot.c244
-rw-r--r--sys/kgssapi/gsstest.c1141
-rw-r--r--sys/kgssapi/kgss_if.m95
-rw-r--r--sys/kgssapi/krb5/kcrypto.c266
-rw-r--r--sys/kgssapi/krb5/kcrypto.h154
-rw-r--r--sys/kgssapi/krb5/kcrypto_aes.c384
-rw-r--r--sys/kgssapi/krb5/kcrypto_arcfour.c220
-rw-r--r--sys/kgssapi/krb5/kcrypto_des.c262
-rw-r--r--sys/kgssapi/krb5/kcrypto_des3.c402
-rw-r--r--sys/kgssapi/krb5/krb5_mech.c2100
-rw-r--r--sys/modules/kgssapi/Makefile55
-rw-r--r--sys/modules/kgssapi_krb5/Makefile21
-rw-r--r--sys/modules/nfsclient/Makefile4
-rw-r--r--sys/modules/nfsserver/Makefile4
-rw-r--r--sys/nfsclient/nfs.h25
-rw-r--r--sys/nfsclient/nfs_krpc.c769
-rw-r--r--sys/nfsclient/nfs_socket.c4
-rw-r--r--sys/nfsclient/nfs_subs.c8
-rw-r--r--sys/nfsclient/nfs_vfsops.c61
-rw-r--r--sys/nfsclient/nfsmount.h34
-rw-r--r--sys/nfsserver/nfs.h90
-rw-r--r--sys/nfsserver/nfs_fha.c597
-rw-r--r--sys/nfsserver/nfs_fha.h28
-rw-r--r--sys/nfsserver/nfs_serv.c67
-rw-r--r--sys/nfsserver/nfs_srvcache.c4
-rw-r--r--sys/nfsserver/nfs_srvkrpc.c565
-rw-r--r--sys/nfsserver/nfs_srvsock.c5
-rw-r--r--sys/nfsserver/nfs_srvsubs.c65
-rw-r--r--sys/nfsserver/nfs_syscalls.c8
-rw-r--r--sys/nfsserver/nfsm_subs.h5
-rw-r--r--sys/nfsserver/nfsrvcache.h4
-rw-r--r--sys/nlm/nlm.h2
-rw-r--r--sys/nlm/nlm_advlock.c9
-rw-r--r--sys/nlm/nlm_prot_impl.c116
-rw-r--r--sys/nlm/nlm_prot_svc.c72
-rw-r--r--sys/rpc/auth.h45
-rw-r--r--sys/rpc/auth_none.c20
-rw-r--r--sys/rpc/auth_unix.c34
-rw-r--r--sys/rpc/clnt.h70
-rw-r--r--sys/rpc/clnt_dg.c310
-rw-r--r--sys/rpc/clnt_rc.c126
-rw-r--r--sys/rpc/clnt_vc.c239
-rw-r--r--sys/rpc/replay.c248
-rw-r--r--sys/rpc/replay.h85
-rw-r--r--sys/rpc/rpc_com.h1
-rw-r--r--sys/rpc/rpc_generic.c134
-rw-r--r--sys/rpc/rpc_msg.h2
-rw-r--r--sys/rpc/rpc_prot.c81
-rw-r--r--sys/rpc/rpcsec_gss.h189
-rw-r--r--sys/rpc/rpcsec_gss/rpcsec_gss.c1064
-rw-r--r--sys/rpc/rpcsec_gss/rpcsec_gss_conf.c163
-rw-r--r--sys/rpc/rpcsec_gss/rpcsec_gss_int.h94
-rw-r--r--sys/rpc/rpcsec_gss/rpcsec_gss_misc.c53
-rw-r--r--sys/rpc/rpcsec_gss/rpcsec_gss_prot.c359
-rw-r--r--sys/rpc/rpcsec_gss/svc_rpcsec_gss.c1485
-rw-r--r--sys/rpc/svc.c1048
-rw-r--r--sys/rpc/svc.h244
-rw-r--r--sys/rpc/svc_auth.c75
-rw-r--r--sys/rpc/svc_auth.h24
-rw-r--r--sys/rpc/svc_auth_unix.c3
-rw-r--r--sys/rpc/svc_dg.c157
-rw-r--r--sys/rpc/svc_generic.c93
-rw-r--r--sys/rpc/svc_vc.c247
-rw-r--r--sys/rpc/xdr.h2
-rw-r--r--sys/sys/mount.h24
-rw-r--r--sys/xdr/xdr_mbuf.c70
-rw-r--r--tools/regression/kgssapi/Makefile9
-rw-r--r--tools/regression/kgssapi/gsstest.c307
-rw-r--r--tools/regression/rpcsec_gss/Makefile9
-rw-r--r--tools/regression/rpcsec_gss/rpctest.c400
-rw-r--r--usr.sbin/Makefile5
-rw-r--r--usr.sbin/gssd/Makefile29
-rw-r--r--usr.sbin/gssd/gssd.870
-rw-r--r--usr.sbin/gssd/gssd.c610
-rw-r--r--usr.sbin/mountd/exports.522
-rw-r--r--usr.sbin/mountd/mountd.c92
-rw-r--r--usr.sbin/nfsd/nfsd.c115
121 files changed, 18973 insertions, 991 deletions
diff --git a/etc/gss/mech b/etc/gss/mech
index d2eaa4c..7cc82c7 100644
--- a/etc/gss/mech
+++ b/etc/gss/mech
@@ -1,6 +1,6 @@
# $FreeBSD$
#
# Name OID Library name Kernel module
-kerberosv5 1.2.840.113554.1.2.2 /usr/lib/libgssapi_krb5.so.10 -
+kerberosv5 1.2.840.113554.1.2.2 /usr/lib/libgssapi_krb5.so.10 kgssapi_krb5
spnego 1.3.6.1.5.5.2 /usr/lib/libgssapi_spnego.so.10 -
#ntlm 1.3.6.1.4.1.311.2.2.10 /usr/lib/libgssapi_ntlm.so.10 -
diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile
index 7d01004..d28fb8b 100755
--- a/etc/rc.d/Makefile
+++ b/etc/rc.d/Makefile
@@ -11,7 +11,7 @@ FILES= DAEMON FILESYSTEMS LOGIN NETWORKING SERVERS \
dmesg dumpon \
early.sh encswap \
fsck ftp-proxy ftpd \
- gbde geli geli2 \
+ gbde geli geli2 gssd \
hcsecd \
hostapd hostid hostname \
idmapd inetd initrandom \
diff --git a/etc/rc.d/gssd b/etc/rc.d/gssd
new file mode 100755
index 0000000..3788307
--- /dev/null
+++ b/etc/rc.d/gssd
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+# PROVIDE: gssd
+# REQUIRE: root
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="gssd"
+
+load_rc_config $name
+rcvar="gssd_enable"
+command="${gssd:-/usr/sbin/${name}}"
+eval ${name}_flags=\"${gssd_flags}\"
+run_rc_command "$1"
diff --git a/etc/rc.d/nfsd b/etc/rc.d/nfsd
index b60c5c8..84bda25 100755
--- a/etc/rc.d/nfsd
+++ b/etc/rc.d/nfsd
@@ -4,7 +4,7 @@
#
# PROVIDE: nfsd
-# REQUIRE: mountd
+# REQUIRE: mountd hostname gssd
# KEYWORD: nojail shutdown
. /etc/rc.subr
diff --git a/include/rpc/xdr.h b/include/rpc/xdr.h
index db29cc3..a02291b 100644
--- a/include/rpc/xdr.h
+++ b/include/rpc/xdr.h
@@ -294,10 +294,13 @@ extern bool_t xdr_short(XDR *, short *);
extern bool_t xdr_u_short(XDR *, u_short *);
extern bool_t xdr_int16_t(XDR *, int16_t *);
extern bool_t xdr_u_int16_t(XDR *, u_int16_t *);
+extern bool_t xdr_uint16_t(XDR *, u_int16_t *);
extern bool_t xdr_int32_t(XDR *, int32_t *);
extern bool_t xdr_u_int32_t(XDR *, u_int32_t *);
+extern bool_t xdr_uint32_t(XDR *, u_int32_t *);
extern bool_t xdr_int64_t(XDR *, int64_t *);
extern bool_t xdr_u_int64_t(XDR *, u_int64_t *);
+extern bool_t xdr_uint64_t(XDR *, u_int64_t *);
extern bool_t xdr_bool(XDR *, bool_t *);
extern bool_t xdr_enum(XDR *, enum_t *);
extern bool_t xdr_array(XDR *, char **, u_int *, u_int, u_int, xdrproc_t);
diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map
index 5217ab0..02ed73b 100644
--- a/lib/libc/sys/Symbol.map
+++ b/lib/libc/sys/Symbol.map
@@ -982,4 +982,5 @@ FBSDprivate_1.0 {
__sys_writev;
__error_unthreaded;
nlm_syscall;
+ gssd_syscall;
};
diff --git a/lib/libc/xdr/Symbol.map b/lib/libc/xdr/Symbol.map
index 4cceeb7..0739b7b 100644
--- a/lib/libc/xdr/Symbol.map
+++ b/lib/libc/xdr/Symbol.map
@@ -45,3 +45,9 @@ FBSD_1.0 {
/* xdr_sizeof; */ /* Why is xdr_sizeof.c not included in Makefileinc? */
xdrstdio_create;
};
+
+FBSD_1.1 {
+ xdr_uint16_t;
+ xdr_uint32_t;
+ xdr_uint64_t;
+};
diff --git a/lib/libc/xdr/xdr.c b/lib/libc/xdr/xdr.c
index c1e5eec..337cdc0 100644
--- a/lib/libc/xdr/xdr.c
+++ b/lib/libc/xdr/xdr.c
@@ -263,6 +263,36 @@ xdr_u_int32_t(xdrs, u_int32_p)
return (FALSE);
}
+/*
+ * XDR unsigned 32-bit integers
+ * same as xdr_int32_t - open coded to save a proc call!
+ */
+bool_t
+xdr_uint32_t(xdrs, u_int32_p)
+ XDR *xdrs;
+ uint32_t *u_int32_p;
+{
+ u_long l;
+
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ l = (u_long) *u_int32_p;
+ return (XDR_PUTLONG(xdrs, (long *)&l));
+
+ case XDR_DECODE:
+ if (!XDR_GETLONG(xdrs, (long *)&l)) {
+ return (FALSE);
+ }
+ *u_int32_p = (u_int32_t) l;
+ return (TRUE);
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+ /* NOTREACHED */
+ return (FALSE);
+}
/*
* XDR short integers
@@ -385,6 +415,36 @@ xdr_u_int16_t(xdrs, u_int16_p)
return (FALSE);
}
+/*
+ * XDR unsigned 16-bit integers
+ */
+bool_t
+xdr_uint16_t(xdrs, u_int16_p)
+ XDR *xdrs;
+ uint16_t *u_int16_p;
+{
+ u_long l;
+
+ switch (xdrs->x_op) {
+
+ case XDR_ENCODE:
+ l = (u_long) *u_int16_p;
+ return (XDR_PUTLONG(xdrs, (long *)&l));
+
+ case XDR_DECODE:
+ if (!XDR_GETLONG(xdrs, (long *)&l)) {
+ return (FALSE);
+ }
+ *u_int16_p = (u_int16_t) l;
+ return (TRUE);
+
+ case XDR_FREE:
+ return (TRUE);
+ }
+ /* NOTREACHED */
+ return (FALSE);
+}
+
/*
* XDR a char
@@ -806,6 +866,38 @@ xdr_u_int64_t(xdrs, ullp)
return (FALSE);
}
+/*
+ * XDR unsigned 64-bit integers
+ */
+bool_t
+xdr_uint64_t(xdrs, ullp)
+ XDR *xdrs;
+ uint64_t *ullp;
+{
+ u_long ul[2];
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ ul[0] = (u_long)(*ullp >> 32) & 0xffffffff;
+ ul[1] = (u_long)(*ullp) & 0xffffffff;
+ if (XDR_PUTLONG(xdrs, (long *)&ul[0]) == FALSE)
+ return (FALSE);
+ return (XDR_PUTLONG(xdrs, (long *)&ul[1]));
+ case XDR_DECODE:
+ if (XDR_GETLONG(xdrs, (long *)&ul[0]) == FALSE)
+ return (FALSE);
+ if (XDR_GETLONG(xdrs, (long *)&ul[1]) == FALSE)
+ return (FALSE);
+ *ullp = (u_int64_t)
+ (((u_int64_t)ul[0] << 32) | ((u_int64_t)ul[1]));
+ return (TRUE);
+ case XDR_FREE:
+ return (TRUE);
+ }
+ /* NOTREACHED */
+ return (FALSE);
+}
+
/*
* XDR hypers
diff --git a/lib/librpcsec_gss/svc_rpcsec_gss.c b/lib/librpcsec_gss/svc_rpcsec_gss.c
index acafa09..276126b 100644
--- a/lib/librpcsec_gss/svc_rpcsec_gss.c
+++ b/lib/librpcsec_gss/svc_rpcsec_gss.c
@@ -168,7 +168,7 @@ rpc_gss_set_callback(rpc_gss_callback_t *cb)
{
struct svc_rpc_gss_callback *scb;
- scb = malloc(sizeof(struct svc_rpc_gss_callback));
+ scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
if (!scb) {
_rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
return (FALSE);
@@ -255,7 +255,7 @@ rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
namelen += strlen(domain) + 1;
}
- buf.value = malloc(namelen);
+ buf.value = mem_alloc(namelen);
buf.length = namelen;
strcpy((char *) buf.value, name);
if (node) {
@@ -273,7 +273,7 @@ rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
*/
maj_stat = gss_import_name(&min_stat, &buf,
GSS_C_NT_USER_NAME, &gss_name);
- free(buf.value);
+ mem_free(buf.value, buf.length);
if (maj_stat != GSS_S_COMPLETE) {
log_status("gss_import_name", mech_oid, maj_stat, min_stat);
return (FALSE);
@@ -300,7 +300,7 @@ rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
}
gss_release_name(&min_stat, &gss_mech_name);
- result = malloc(sizeof(int) + buf.length);
+ result = mem_alloc(sizeof(int) + buf.length);
if (!result) {
gss_release_buffer(&min_stat, &buf);
return (FALSE);
@@ -443,7 +443,9 @@ svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
gss_release_name(&min_stat, &client->cl_cname);
if (client->cl_rawcred.client_principal)
- free(client->cl_rawcred.client_principal);
+ mem_free(client->cl_rawcred.client_principal,
+ sizeof(*client->cl_rawcred.client_principal)
+ + client->cl_rawcred.client_principal->len);
if (client->cl_verf.value)
gss_release_buffer(&min_stat, &client->cl_verf);
@@ -527,7 +529,7 @@ gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
* here for "{ " and "}\0".
*/
string_length += 4;
- if ((bp = (char *) malloc(string_length))) {
+ if ((bp = (char *) mem_alloc(string_length))) {
strcpy(bp, "{ ");
number = (unsigned long) cp[0];
sprintf(numstr, "%ld ", number/40);
@@ -634,8 +636,15 @@ svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
client->cl_sname = sname;
break;
}
+ client->cl_sname = sname;
+ break;
}
}
+ if (!sname) {
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &recv_tok);
+ return (FALSE);
+ }
} else {
gr->gr_major = gss_accept_sec_context(
&gr->gr_minor,
@@ -663,11 +672,11 @@ svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
log_status("accept_sec_context", client->cl_mech,
gr->gr_major, gr->gr_minor);
client->cl_state = CLIENT_STALE;
- return (FALSE);
+ return (TRUE);
}
gr->gr_handle.value = &client->cl_id;
- gr->gr_handle.length = sizeof(uint32_t);
+ gr->gr_handle.length = sizeof(client->cl_id);
gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
/* Save client info. */
@@ -703,7 +712,7 @@ svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
return (FALSE);
}
client->cl_rawcred.client_principal =
- malloc(sizeof(*client->cl_rawcred.client_principal)
+ mem_alloc(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,
@@ -718,6 +727,7 @@ svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
* kerberos5, this uses krb5_aname_to_localname.
*/
svc_rpc_gss_build_ucred(client, client->cl_cname);
+ gss_release_name(&min_stat, &client->cl_cname);
#ifdef DEBUG
{
@@ -892,13 +902,12 @@ svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
* discard it.
*/
offset = client->cl_seqlast - seq;
- if (offset >= client->cl_win)
+ if (offset >= SVC_RPC_GSS_SEQWINDOW)
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);
@@ -907,7 +916,7 @@ svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
static void
svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
{
- int offset, i;
+ int offset, i, word, bit;
uint32_t carry, newcarry;
if (seq > client->cl_seqlast) {
@@ -936,7 +945,13 @@ svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
}
client->cl_seqmask[0] |= 1;
client->cl_seqlast = seq;
+ } else {
+ offset = client->cl_seqlast - seq;
+ word = offset / 32;
+ bit = offset % 32;
+ client->cl_seqmask[word] |= (1 << bit);
}
+
}
enum auth_stat
@@ -983,6 +998,10 @@ svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
/* Check the proc and find the client (or create it) */
if (gc.gc_proc == RPCSEC_GSS_INIT) {
+ if (gc.gc_handle.length != 0) {
+ result = AUTH_BADCRED;
+ goto out;
+ }
client = svc_rpc_gss_create_client();
} else {
if (gc.gc_handle.length != sizeof(uint32_t)) {
diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c
index 71ac14e..fdfecc4 100644
--- a/sbin/mount_nfs/mount_nfs.c
+++ b/sbin/mount_nfs/mount_nfs.c
@@ -134,6 +134,7 @@ struct sockaddr *addr;
int addrlen = 0;
u_char *fh = NULL;
int fhsize = 0;
+int secflavor = -1;
enum mountmode {
ANY,
@@ -151,6 +152,8 @@ enum tryret {
};
int fallback_mount(struct iovec *iov, int iovlen, int mntflags);
+int sec_name_to_num(char *sec);
+char *sec_num_to_name(int num);
int getnfsargs(char *, struct iovec **iov, int *iovlen);
int getnfs4args(char *, struct iovec **iov, int *iovlen);
/* void set_rpc_maxgrouplist(int); */
@@ -308,6 +311,21 @@ main(int argc, char *argv[])
atoi(val));
if (portspec == NULL)
err(1, "asprintf");
+ } else if (strcmp(opt, "sec") == 0) {
+ /*
+ * Don't add this option to
+ * the iovec yet - we will
+ * negotiate which sec flavor
+ * to use with the remote
+ * mountd.
+ */
+ pass_flag_to_nmount=0;
+ secflavor = sec_name_to_num(val);
+ if (secflavor < 0) {
+ errx(1,
+ "illegal sec value -- %s",
+ val);
+ }
} else if (strcmp(opt, "retrycnt") == 0) {
pass_flag_to_nmount=0;
num = strtol(val, &p, 10);
@@ -635,6 +653,36 @@ fallback_mount(struct iovec *iov, int iovlen, int mntflags)
}
int
+sec_name_to_num(char *sec)
+{
+ if (!strcmp(sec, "krb5"))
+ return (RPCSEC_GSS_KRB5);
+ if (!strcmp(sec, "krb5i"))
+ return (RPCSEC_GSS_KRB5I);
+ if (!strcmp(sec, "krb5p"))
+ return (RPCSEC_GSS_KRB5P);
+ if (!strcmp(sec, "sys"))
+ return (AUTH_SYS);
+ return (-1);
+}
+
+char *
+sec_num_to_name(int flavor)
+{
+ switch (flavor) {
+ case RPCSEC_GSS_KRB5:
+ return ("krb5");
+ case RPCSEC_GSS_KRB5I:
+ return ("krb5i");
+ case RPCSEC_GSS_KRB5P:
+ return ("krb5p");
+ case AUTH_SYS:
+ return ("sys");
+ }
+ return (NULL);
+}
+
+int
getnfsargs(char *spec, struct iovec **iov, int *iovlen)
{
struct addrinfo hints, *ai_nfs, *ai;
@@ -904,6 +952,7 @@ nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr,
CLIENT *clp;
struct netconfig *nconf, *nconf_mnt;
const char *netid, *netid_mnt;
+ char *secname;
int doconnect, nfsvers, mntvers, sotype;
enum clnt_stat stat;
enum mountmode trymntmode;
@@ -1033,7 +1082,7 @@ tryagain:
&rpc_createerr.cf_error));
}
clp->cl_auth = authsys_create_default();
- nfhret.auth = -1;
+ nfhret.auth = secflavor;
nfhret.vers = mntvers;
stat = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec,
(xdrproc_t)xdr_fh, &nfhret,
@@ -1074,6 +1123,9 @@ tryagain:
build_iovec(iov, iovlen, "addr", addr, addrlen);
build_iovec(iov, iovlen, "fh", fh, fhsize);
+ secname = sec_num_to_name(nfhret.auth);
+ if (secname)
+ build_iovec(iov, iovlen, "sec", secname, (size_t)-1);
if (nfsvers == 3)
build_iovec(iov, iovlen, "nfsv3", NULL, 0);
diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master
index fa9cbd2..d4c1d2e 100644
--- a/sys/compat/freebsd32/syscalls.master
+++ b/sys/compat/freebsd32/syscalls.master
@@ -854,3 +854,5 @@
503 AUE_UNLINKAT NOPROTO { int unlinkat(int fd, char *path, \
int flag); }
504 AUE_POSIX_OPENPT NOPROTO { int posix_openpt(int flags); }
+; 505 is initialised by the kgssapi code, if present.
+505 AUE_NULL UNIMPL gssd_syscall
diff --git a/sys/conf/files b/sys/conf/files
index 073f737..af7f0b0 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -339,7 +339,7 @@ crypto/camellia/camellia.c optional crypto | ipsec
crypto/camellia/camellia-api.c optional crypto | ipsec
crypto/des/des_ecb.c optional crypto | ipsec | netsmb
crypto/des/des_setkey.c optional crypto | ipsec | netsmb
-crypto/rc4/rc4.c optional netgraph_mppc_encryption
+crypto/rc4/rc4.c optional netgraph_mppc_encryption | kgssapi
crypto/rijndael/rijndael-alg-fst.c optional crypto | geom_bde | \
ipsec | random | wlan_ccmp
crypto/rijndael/rijndael-api-fst.c optional geom_bde | random
@@ -1746,6 +1746,56 @@ kern/vfs_subr.c standard
kern/vfs_syscalls.c standard
kern/vfs_vnops.c standard
#
+# Kernel GSS-API
+#
+gssd.h optional kgssapi \
+ dependency "$S/kgssapi/gssd.x" \
+ compile-with "rpcgen -hM $S/kgssapi/gssd.x | grep -v pthread.h > gssd.h" \
+ no-obj no-implicit-rule before-depend local \
+ clean "gssd.h"
+gssd_xdr.c optional kgssapi \
+ dependency "$S/kgssapi/gssd.x gssd.h" \
+ compile-with "rpcgen -c $S/kgssapi/gssd.x -o gssd_xdr.c" \
+ no-implicit-rule before-depend local \
+ clean "gssd_xdr.c"
+gssd_clnt.c optional kgssapi \
+ dependency "$S/kgssapi/gssd.x gssd.h" \
+ compile-with "rpcgen -lM $S/kgssapi/gssd.x | grep -v string.h > gssd_clnt.c" \
+ no-implicit-rule before-depend local \
+ clean "gssd_clnt.c"
+kgssapi/gss_accept_sec_context.c optional kgssapi
+kgssapi/gss_add_oid_set_member.c optional kgssapi
+kgssapi/gss_acquire_cred.c optional kgssapi
+kgssapi/gss_canonicalize_name.c optional kgssapi
+kgssapi/gss_create_empty_oid_set.c optional kgssapi
+kgssapi/gss_delete_sec_context.c optional kgssapi
+kgssapi/gss_display_status.c optional kgssapi
+kgssapi/gss_export_name.c optional kgssapi
+kgssapi/gss_get_mic.c optional kgssapi
+kgssapi/gss_init_sec_context.c optional kgssapi
+kgssapi/gss_impl.c optional kgssapi
+kgssapi/gss_import_name.c optional kgssapi
+kgssapi/gss_names.c optional kgssapi
+kgssapi/gss_pname_to_uid.c optional kgssapi
+kgssapi/gss_release_buffer.c optional kgssapi
+kgssapi/gss_release_cred.c optional kgssapi
+kgssapi/gss_release_name.c optional kgssapi
+kgssapi/gss_release_oid_set.c optional kgssapi
+kgssapi/gss_set_cred_option.c optional kgssapi
+kgssapi/gss_test_oid_set_member.c optional kgssapi
+kgssapi/gss_unwrap.c optional kgssapi
+kgssapi/gss_verify_mic.c optional kgssapi
+kgssapi/gss_wrap.c optional kgssapi
+kgssapi/gss_wrap_size_limit.c optional kgssapi
+kgssapi/gssd_prot.c optional kgssapi
+kgssapi/krb5/krb5_mech.c optional kgssapi
+kgssapi/krb5/kcrypto.c optional kgssapi
+kgssapi/krb5/kcrypto_aes.c optional kgssapi
+kgssapi/krb5/kcrypto_arcfour.c optional kgssapi
+kgssapi/krb5/kcrypto_des.c optional kgssapi
+kgssapi/krb5/kcrypto_des3.c optional kgssapi
+kgssapi/kgss_if.m optional kgssapi
+kgssapi/gsstest.c optional kgssapi_debug
# These files in libkern/ are those needed by all architectures. Some
# of the files in libkern/ are only needed on some architectures, e.g.,
# libkern/divdi3.c is needed by i386 but not alpha. Also, some of these
@@ -2106,18 +2156,21 @@ nfsclient/krpc_subr.c optional bootp nfsclient
nfsclient/nfs_bio.c optional nfsclient
nfsclient/nfs_diskless.c optional nfsclient nfs_root
nfsclient/nfs_node.c optional nfsclient
-nfsclient/nfs_socket.c optional nfsclient
+nfsclient/nfs_socket.c optional nfsclient nfs_legacyrpc
+nfsclient/nfs_krpc.c optional nfsclient
nfsclient/nfs_subs.c optional nfsclient
nfsclient/nfs_nfsiod.c optional nfsclient
nfsclient/nfs_vfsops.c optional nfsclient
nfsclient/nfs_vnops.c optional nfsclient
nfsclient/nfs_lock.c optional nfsclient
+nfsserver/nfs_fha.c optional nfsserver
nfsserver/nfs_serv.c optional nfsserver
-nfsserver/nfs_srvsock.c optional nfsserver
-nfsserver/nfs_srvcache.c optional nfsserver
+nfsserver/nfs_srvkrpc.c optional nfsserver
+nfsserver/nfs_srvsock.c optional nfsserver nfs_legacyrpc
+nfsserver/nfs_srvcache.c optional nfsserver nfs_legacyrpc
nfsserver/nfs_srvsubs.c optional nfsserver
-nfsserver/nfs_syscalls.c optional nfsserver
-nlm/nlm_advlock.c optional nfslockd
+nfsserver/nfs_syscalls.c optional nfsserver nfs_legacyrpc
+nlm/nlm_advlock.c optional nfslockd nfsclient
nlm/nlm_prot_clnt.c optional nfslockd
nlm/nlm_prot_impl.c optional nfslockd
nlm/nlm_prot_server.c optional nfslockd
@@ -2143,27 +2196,33 @@ pci/intpm.c optional intpm pci
pci/ncr.c optional ncr pci
pci/nfsmb.c optional nfsmb pci
pci/viapm.c optional viapm pci
-rpc/auth_none.c optional krpc | nfslockd
-rpc/auth_unix.c optional krpc | nfslockd
-rpc/authunix_prot.c optional krpc | nfslockd
-rpc/clnt_dg.c optional krpc | nfslockd
-rpc/clnt_rc.c optional krpc | nfslockd
-rpc/clnt_vc.c optional krpc | nfslockd
-rpc/getnetconfig.c optional krpc | nfslockd
-rpc/inet_ntop.c optional krpc | nfslockd
-rpc/inet_pton.c optional krpc | nfslockd
-rpc/rpc_callmsg.c optional krpc | nfslockd
-rpc/rpc_generic.c optional krpc | nfslockd
-rpc/rpc_prot.c optional krpc | nfslockd
-rpc/rpcb_clnt.c optional krpc | nfslockd
-rpc/rpcb_prot.c optional krpc | nfslockd
+rpc/auth_none.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/auth_unix.c optional krpc | nfslockd | nfsclient
+rpc/authunix_prot.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/clnt_dg.c optional krpc | nfslockd | nfsclient
+rpc/clnt_rc.c optional krpc | nfslockd | nfsclient
+rpc/clnt_vc.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/getnetconfig.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/inet_ntop.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/inet_pton.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/replay.c optional krpc | nfslockd | nfsserver
+rpc/rpc_callmsg.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/rpc_generic.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/rpc_prot.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/rpcb_clnt.c optional krpc | nfslockd | nfsclient | nfsserver
+rpc/rpcb_prot.c optional krpc | nfslockd | nfsclient | nfsserver
rpc/rpcclnt.c optional nfsclient
-rpc/svc.c optional krpc | nfslockd
-rpc/svc_auth.c optional krpc | nfslockd
-rpc/svc_auth_unix.c optional krpc | nfslockd
-rpc/svc_dg.c optional krpc | nfslockd
-rpc/svc_generic.c optional krpc | nfslockd
-rpc/svc_vc.c optional krpc | nfslockd
+rpc/svc.c optional krpc | nfslockd | nfsserver
+rpc/svc_auth.c optional krpc | nfslockd | nfsserver
+rpc/svc_auth_unix.c optional krpc | nfslockd | nfsserver
+rpc/svc_dg.c optional krpc | nfslockd | nfsserver
+rpc/svc_generic.c optional krpc | nfslockd | nfsserver
+rpc/svc_vc.c optional krpc | nfslockd | nfsserver
+rpc/rpcsec_gss/rpcsec_gss.c optional krpc kgssapi | nfslockd kgssapi
+rpc/rpcsec_gss/rpcsec_gss_conf.c optional krpc kgssapi | nfslockd kgssapi
+rpc/rpcsec_gss/rpcsec_gss_misc.c optional krpc kgssapi | nfslockd kgssapi
+rpc/rpcsec_gss/rpcsec_gss_prot.c optional krpc kgssapi | nfslockd kgssapi
+rpc/rpcsec_gss/svc_rpcsec_gss.c optional krpc kgssapi | nfslockd kgssapi
security/audit/audit.c optional audit
security/audit/audit_arg.c optional audit
security/audit/audit_bsm.c optional audit
@@ -2251,12 +2310,12 @@ vm/vm_reserv.c standard
vm/vm_unix.c standard
vm/vm_zeroidle.c standard
vm/vnode_pager.c standard
-xdr/xdr.c optional krpc | nfslockd
-xdr/xdr_array.c optional krpc | nfslockd
-xdr/xdr_mbuf.c optional krpc | nfslockd
-xdr/xdr_mem.c optional krpc | nfslockd
-xdr/xdr_reference.c optional krpc | nfslockd
-xdr/xdr_sizeof.c optional krpc | nfslockd
+xdr/xdr.c optional krpc | nfslockd | nfsclient | nfsserver
+xdr/xdr_array.c optional krpc | nfslockd | nfsclient | nfsserver
+xdr/xdr_mbuf.c optional krpc | nfslockd | nfsclient | nfsserver
+xdr/xdr_mem.c optional krpc | nfslockd | nfsclient | nfsserver
+xdr/xdr_reference.c optional krpc | nfslockd | nfsclient | nfsserver
+xdr/xdr_sizeof.c optional krpc | nfslockd | nfsclient | nfsserver
#
gnu/fs/xfs/xfs_alloc.c optional xfs \
compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" \
diff --git a/sys/conf/options b/sys/conf/options
index 0823c82..f20547d 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -214,6 +214,10 @@ PSEUDOFS_TRACE opt_pseudofs.h
# Broken - ffs_snapshot() dependency from ufs_lookup() :-(
FFS opt_ffs_broken_fixme.h
+# In-kernel GSS-API
+KGSSAPI opt_kgssapi.h
+KGSSAPI_DEBUG opt_kgssapi.h
+
# These static filesystems have one slightly bogus static dependency in
# sys/i386/i386/autoconf.c. If any of these filesystems are
# statically compiled into the kernel, code for mounting them as root
@@ -222,6 +226,11 @@ NFSCLIENT opt_nfs.h
NFSSERVER opt_nfs.h
NFS4CLIENT opt_nfs.h
+# Use this option to compile both NFS client and server using the
+# legacy RPC implementation instead of the newer KRPC system (which
+# supports modern features such as RPCSEC_GSS
+NFS_LEGACYRPC opt_nfs.h
+
# filesystems and libiconv bridge
CD9660_ICONV opt_dontuse.h
MSDOSFS_ICONV opt_dontuse.h
diff --git a/sys/fs/unionfs/union_vfsops.c b/sys/fs/unionfs/union_vfsops.c
index 2e873fe..e0a3188 100644
--- a/sys/fs/unionfs/union_vfsops.c
+++ b/sys/fs/unionfs/union_vfsops.c
@@ -521,7 +521,7 @@ unionfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp)
static int
unionfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
- struct ucred **credanonp)
+ struct ucred **credanonp, int *numsecflavors, int **secflavors)
{
return (EOPNOTSUPP);
}
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
index 38adf73..924d750 100644
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -895,5 +895,7 @@
char *path2); }
503 AUE_UNLINKAT STD { int unlinkat(int fd, char *path, int flag); }
504 AUE_POSIX_OPENPT STD { int posix_openpt(int flags); }
+; 505 is initialised by the kgssapi code, if present.
+505 AUE_NULL NOSTD { int gssd_syscall(char *path); }
; Please copy any additions and changes to the following compatability tables:
; sys/compat/freebsd32/syscalls.master
diff --git a/sys/kern/vfs_export.c b/sys/kern/vfs_export.c
index 775ed26..223c2fe 100644
--- a/sys/kern/vfs_export.c
+++ b/sys/kern/vfs_export.c
@@ -68,6 +68,8 @@ struct netcred {
struct radix_node netc_rnodes[2];
int netc_exflags;
struct ucred netc_anon;
+ int netc_numsecflavors;
+ int netc_secflavors[MAXSECFLAVORS];
};
/*
@@ -120,6 +122,9 @@ vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
np->netc_anon.cr_ngroups = argp->ex_anon.cr_ngroups;
bcopy(argp->ex_anon.cr_groups, np->netc_anon.cr_groups,
sizeof(np->netc_anon.cr_groups));
+ np->netc_numsecflavors = argp->ex_numsecflavors;
+ bcopy(argp->ex_secflavors, np->netc_secflavors,
+ sizeof(np->netc_secflavors));
refcount_init(&np->netc_anon.cr_ref, 1);
MNT_ILOCK(mp);
mp->mnt_flag |= MNT_DEFEXPORTED;
@@ -203,6 +208,9 @@ vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
np->netc_anon.cr_ngroups = argp->ex_anon.cr_ngroups;
bcopy(argp->ex_anon.cr_groups, np->netc_anon.cr_groups,
sizeof(np->netc_anon.cr_groups));
+ np->netc_numsecflavors = argp->ex_numsecflavors;
+ bcopy(argp->ex_secflavors, np->netc_secflavors,
+ sizeof(np->netc_secflavors));
refcount_init(&np->netc_anon.cr_ref, 1);
return (0);
out:
@@ -253,6 +261,10 @@ vfs_export(struct mount *mp, struct export_args *argp)
struct netexport *nep;
int error;
+ if (argp->ex_numsecflavors < 0
+ || argp->ex_numsecflavors >= MAXSECFLAVORS)
+ return (EINVAL);
+
nep = mp->mnt_export;
error = 0;
lockmgr(&mp->mnt_explock, LK_EXCLUSIVE, NULL);
@@ -441,7 +453,7 @@ vfs_export_lookup(struct mount *mp, struct sockaddr *nam)
int
vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
- struct ucred **credanonp)
+ struct ucred **credanonp, int *numsecflavors, int **secflavors)
{
struct netcred *np;
@@ -452,6 +464,10 @@ vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
return (EACCES);
*extflagsp = np->netc_exflags;
*credanonp = &np->netc_anon;
+ if (numsecflavors)
+ *numsecflavors = np->netc_numsecflavors;
+ if (secflavors)
+ *secflavors = np->netc_secflavors;
return (0);
}
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 5a216c9..4285b4c 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -827,6 +827,7 @@ vfs_domount(
struct vnode *vp;
struct mount *mp;
struct vfsconf *vfsp;
+ struct oexport_args oexport;
struct export_args export;
int error, flag = 0;
struct vattr va;
@@ -1010,6 +1011,19 @@ vfs_domount(
if (vfs_copyopt(mp->mnt_optnew, "export", &export,
sizeof(export)) == 0)
error = vfs_export(mp, &export);
+ else if (vfs_copyopt(mp->mnt_optnew, "export", &oexport,
+ sizeof(oexport)) == 0) {
+ export.ex_flags = oexport.ex_flags;
+ export.ex_root = oexport.ex_root;
+ export.ex_anon = oexport.ex_anon;
+ export.ex_addr = oexport.ex_addr;
+ export.ex_addrlen = oexport.ex_addrlen;
+ export.ex_mask = oexport.ex_mask;
+ export.ex_masklen = oexport.ex_masklen;
+ export.ex_indexfile = oexport.ex_indexfile;
+ export.ex_numsecflavors = 0;
+ error = vfs_export(mp, &export);
+ }
}
if (!error) {
diff --git a/sys/kgssapi/gss_accept_sec_context.c b/sys/kgssapi/gss_accept_sec_context.c
new file mode 100644
index 0000000..59f2803
--- /dev/null
+++ b/sys/kgssapi/gss_accept_sec_context.c
@@ -0,0 +1,138 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+#include <rpc/rpc.h>
+
+#include "gssd.h"
+#include "kgss_if.h"
+
+OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ const gss_cred_id_t acceptor_cred_handle,
+ const gss_buffer_t input_token,
+ const gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ struct accept_sec_context_res res;
+ struct accept_sec_context_args args;
+ enum clnt_stat stat;
+ gss_ctx_id_t ctx = *context_handle;
+ gss_name_t name;
+ gss_cred_id_t cred;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ if (ctx)
+ args.ctx = ctx->handle;
+ else
+ args.ctx = 0;
+ if (acceptor_cred_handle)
+ args.cred = acceptor_cred_handle->handle;
+ else
+ args.cred = 0;
+ args.input_token = *input_token;
+ args.input_chan_bindings = input_chan_bindings;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_accept_sec_context_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE
+ && res.major_status != GSS_S_CONTINUE_NEEDED) {
+ *minor_status = res.minor_status;
+ xdr_free((xdrproc_t) xdr_accept_sec_context_res, &res);
+ return (res.major_status);
+ }
+
+ *minor_status = res.minor_status;
+
+ if (!ctx) {
+ ctx = kgss_create_context(res.mech_type);
+ if (!ctx) {
+ xdr_free((xdrproc_t) xdr_accept_sec_context_res, &res);
+ *minor_status = 0;
+ return (GSS_S_BAD_MECH);
+ }
+ }
+ *context_handle = ctx;
+
+ ctx->handle = res.ctx;
+ name = malloc(sizeof(struct _gss_name_t), M_GSSAPI, M_WAITOK);
+ name->handle = res.src_name;
+ if (src_name) {
+ *src_name = name;
+ } else {
+ OM_uint32 junk;
+ gss_release_name(&junk, &name);
+ }
+ if (mech_type)
+ *mech_type = KGSS_MECH_TYPE(ctx);
+ kgss_copy_buffer(&res.output_token, output_token);
+ if (ret_flags)
+ *ret_flags = res.ret_flags;
+ if (time_rec)
+ *time_rec = res.time_rec;
+ cred = malloc(sizeof(struct _gss_cred_id_t), M_GSSAPI, M_WAITOK);
+ cred->handle = res.delegated_cred_handle;
+ if (delegated_cred_handle) {
+ *delegated_cred_handle = cred;
+ } else {
+ OM_uint32 junk;
+ gss_release_cred(&junk, &cred);
+ }
+
+ xdr_free((xdrproc_t) xdr_accept_sec_context_res, &res);
+
+ /*
+ * If the context establishment is complete, export it from
+ * userland and hand the result (which includes key material
+ * etc.) to the kernel implementation.
+ */
+ if (res.major_status == GSS_S_COMPLETE)
+ res.major_status = kgss_transfer_context(ctx);
+
+ return (res.major_status);
+}
diff --git a/sys/kgssapi/gss_acquire_cred.c b/sys/kgssapi/gss_acquire_cred.c
new file mode 100644
index 0000000..e5fe821
--- /dev/null
+++ b/sys/kgssapi/gss_acquire_cred.c
@@ -0,0 +1,105 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "gssd.h"
+
+OM_uint32
+gss_acquire_cred(OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 major_status;
+ struct acquire_cred_res res;
+ struct acquire_cred_args args;
+ enum clnt_stat stat;
+ gss_cred_id_t cred;
+ int i;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ args.uid = curthread->td_ucred->cr_uid;
+ if (desired_name)
+ args.desired_name = desired_name->handle;
+ else
+ args.desired_name = 0;
+ args.time_req = time_req;
+ args.desired_mechs = desired_mechs;
+ args.cred_usage = cred_usage;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_acquire_cred_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE) {
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+
+ *minor_status = 0;
+ cred = malloc(sizeof(struct _gss_cred_id_t), M_GSSAPI, M_WAITOK);
+ cred->handle = res.output_cred;
+ *output_cred_handle = cred;
+ if (actual_mechs) {
+ major_status = gss_create_empty_oid_set(minor_status,
+ actual_mechs);
+ if (major_status)
+ return (major_status);
+ for (i = 0; i < res.actual_mechs->count; i++) {
+ major_status = gss_add_oid_set_member(minor_status,
+ &res.actual_mechs->elements[i], actual_mechs);
+ if (major_status)
+ return (major_status);
+ }
+ }
+ if (time_rec)
+ *time_rec = res.time_rec;
+
+ xdr_free((xdrproc_t) xdr_acquire_cred_res, &res);
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_add_oid_set_member.c b/sys/kgssapi/gss_add_oid_set_member.c
new file mode 100644
index 0000000..ecb8a85
--- /dev/null
+++ b/sys/kgssapi/gss_add_oid_set_member.c
@@ -0,0 +1,76 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+OM_uint32
+gss_add_oid_set_member(OM_uint32 *minor_status,
+ const gss_OID member_oid,
+ gss_OID_set *oid_set)
+{
+ OM_uint32 major_status;
+ gss_OID_set set = *oid_set;
+ gss_OID new_elements;
+ gss_OID new_oid;
+ int t;
+
+ *minor_status = 0;
+
+ major_status = gss_test_oid_set_member(minor_status,
+ member_oid, *oid_set, &t);
+ if (major_status)
+ return (major_status);
+ if (t)
+ return (GSS_S_COMPLETE);
+
+ new_elements = malloc((set->count + 1) * sizeof(gss_OID_desc),
+ M_GSSAPI, M_WAITOK);
+
+ new_oid = &new_elements[set->count];
+ new_oid->elements = malloc(member_oid->length, M_GSSAPI, M_WAITOK);
+ new_oid->length = member_oid->length;
+ memcpy(new_oid->elements, member_oid->elements, member_oid->length);
+
+ if (set->elements) {
+ memcpy(new_elements, set->elements,
+ set->count * sizeof(gss_OID_desc));
+ free(set->elements, M_GSSAPI);
+ }
+ set->elements = new_elements;
+ set->count++;
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_canonicalize_name.c b/sys/kgssapi/gss_canonicalize_name.c
new file mode 100644
index 0000000..bea3dd8
--- /dev/null
+++ b/sys/kgssapi/gss_canonicalize_name.c
@@ -0,0 +1,76 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "gssd.h"
+
+OM_uint32
+gss_canonicalize_name(OM_uint32 *minor_status,
+ gss_name_t input_name,
+ const gss_OID mech_type,
+ gss_name_t *output_name)
+{
+ struct canonicalize_name_res res;
+ struct canonicalize_name_args args;
+ enum clnt_stat stat;
+ gss_name_t name;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ args.input_name = input_name->handle;
+ args.mech_type = mech_type;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_canonicalize_name_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE) {
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+
+ name = malloc(sizeof(struct _gss_name_t), M_GSSAPI, M_WAITOK);
+ name->handle = res.output_name;
+ *minor_status = 0;
+ *output_name = name;
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_create_empty_oid_set.c b/sys/kgssapi/gss_create_empty_oid_set.c
new file mode 100644
index 0000000..dd9965c
--- /dev/null
+++ b/sys/kgssapi/gss_create_empty_oid_set.c
@@ -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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+OM_uint32
+gss_create_empty_oid_set(OM_uint32 *minor_status,
+ gss_OID_set *oid_set)
+{
+ gss_OID_set set;
+
+ *minor_status = 0;
+ *oid_set = GSS_C_NO_OID_SET;
+
+ set = malloc(sizeof(gss_OID_set_desc), M_GSSAPI, M_WAITOK);
+
+ set->count = 0;
+ set->elements = 0;
+ *oid_set = set;
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_delete_sec_context.c b/sys/kgssapi/gss_delete_sec_context.c
new file mode 100644
index 0000000..e1582a2
--- /dev/null
+++ b/sys/kgssapi/gss_delete_sec_context.c
@@ -0,0 +1,91 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "gssd.h"
+
+OM_uint32
+gss_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token)
+{
+ struct delete_sec_context_res res;
+ struct delete_sec_context_args args;
+ enum clnt_stat stat;
+ gss_ctx_id_t ctx;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ if (*context_handle) {
+ ctx = *context_handle;
+
+ /*
+ * If we are past the context establishment phase, let
+ * the in-kernel code do the delete, otherwise
+ * userland needs to deal with it.
+ */
+ if (ctx->handle) {
+ args.ctx = ctx->handle;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_delete_sec_context_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (output_token)
+ kgss_copy_buffer(&res.output_token,
+ output_token);
+ xdr_free((xdrproc_t) xdr_delete_sec_context_res, &res);
+
+ kgss_delete_context(ctx, NULL);
+ } else {
+ kgss_delete_context(ctx, output_token);
+ }
+ *context_handle = NULL;
+ } else {
+ if (output_token) {
+ output_token->length = 0;
+ output_token->value = NULL;
+ }
+ }
+
+ *minor_status = 0;
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_display_status.c b/sys/kgssapi/gss_display_status.c
new file mode 100644
index 0000000..0b5b79d
--- /dev/null
+++ b/sys/kgssapi/gss_display_status.c
@@ -0,0 +1,79 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "gssd.h"
+
+OM_uint32
+gss_display_status(OM_uint32 *minor_status,
+ OM_uint32 status_value,
+ int status_type,
+ const gss_OID mech_type,
+ OM_uint32 *message_context,
+ gss_buffer_t status_string) /* status_string */
+{
+ struct display_status_res res;
+ struct display_status_args args;
+ enum clnt_stat stat;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ args.status_value = status_value;
+ args.status_type = status_type;
+ args.mech_type = mech_type;
+ args.message_context = *message_context;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_display_status_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE) {
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+
+ *minor_status = 0;
+ *message_context = res.message_context;
+ kgss_copy_buffer(&res.status_string, status_string);
+ xdr_free((xdrproc_t) xdr_display_status_res, &res);
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_export_name.c b/sys/kgssapi/gss_export_name.c
new file mode 100644
index 0000000..63c1e8a
--- /dev/null
+++ b/sys/kgssapi/gss_export_name.c
@@ -0,0 +1,71 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "gssd.h"
+
+OM_uint32
+gss_export_name(OM_uint32 *minor_status, gss_name_t input_name,
+ gss_buffer_t exported_name)
+{
+ struct export_name_res res;
+ struct export_name_args args;
+ enum clnt_stat stat;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ args.input_name = input_name->handle;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_export_name_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE) {
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+
+ *minor_status = 0;
+ kgss_copy_buffer(&res.exported_name, exported_name);
+ xdr_free((xdrproc_t) xdr_export_name_res, &res);
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_get_mic.c b/sys/kgssapi/gss_get_mic.c
new file mode 100644
index 0000000..1e8dd52
--- /dev/null
+++ b/sys/kgssapi/gss_get_mic.c
@@ -0,0 +1,89 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kgss_if.h"
+
+OM_uint32
+gss_get_mic(OM_uint32 *minor_status,
+ const gss_ctx_id_t ctx,
+ gss_qop_t qop_req,
+ const gss_buffer_t message_buffer,
+ gss_buffer_t message_token)
+{
+ OM_uint32 maj_stat;
+ struct mbuf *m, *mic;
+
+ if (!ctx) {
+ *minor_status = 0;
+ return (GSS_S_NO_CONTEXT);
+ }
+
+ MGET(m, M_WAITOK, MT_DATA);
+ if (message_buffer->length > MLEN)
+ MCLGET(m, M_WAITOK);
+ m_append(m, message_buffer->length, message_buffer->value);
+
+ maj_stat = KGSS_GET_MIC(ctx, minor_status, qop_req, m, &mic);
+
+ m_freem(m);
+ if (maj_stat == GSS_S_COMPLETE) {
+ message_token->length = m_length(mic, NULL);
+ message_token->value = malloc(message_token->length,
+ M_GSSAPI, M_WAITOK);
+ m_copydata(mic, 0, message_token->length,
+ message_token->value);
+ m_freem(mic);
+ }
+
+ return (maj_stat);
+}
+
+OM_uint32
+gss_get_mic_mbuf(OM_uint32 *minor_status, const gss_ctx_id_t ctx,
+ gss_qop_t qop_req, struct mbuf *m, struct mbuf **micp)
+{
+
+ if (!ctx) {
+ *minor_status = 0;
+ return (GSS_S_NO_CONTEXT);
+ }
+
+ return (KGSS_GET_MIC(ctx, minor_status, qop_req, m, micp));
+}
+
diff --git a/sys/kgssapi/gss_impl.c b/sys/kgssapi/gss_impl.c
new file mode 100644
index 0000000..01d940a
--- /dev/null
+++ b/sys/kgssapi/gss_impl.c
@@ -0,0 +1,266 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/priv.h>
+#include <sys/syscall.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+
+#include "gssd.h"
+#include "kgss_if.h"
+
+MALLOC_DEFINE(M_GSSAPI, "GSS-API", "GSS-API");
+
+/*
+ * Syscall hooks
+ */
+static int gssd_syscall_offset = SYS_gssd_syscall;
+static struct sysent gssd_syscall_prev_sysent;
+MAKE_SYSENT(gssd_syscall);
+static bool_t gssd_syscall_registered = FALSE;
+
+struct kgss_mech_list kgss_mechs;
+CLIENT *kgss_gssd_handle;
+
+static void
+kgss_init(void *dummy)
+{
+ int error;
+
+ LIST_INIT(&kgss_mechs);
+ error = syscall_register(&gssd_syscall_offset, &gssd_syscall_sysent,
+ &gssd_syscall_prev_sysent);
+ if (error)
+ printf("Can't register GSSD syscall\n");
+ else
+ gssd_syscall_registered = TRUE;
+}
+SYSINIT(kgss_init, SI_SUB_LOCK, SI_ORDER_FIRST, kgss_init, NULL);
+
+static void
+kgss_uninit(void *dummy)
+{
+
+ if (gssd_syscall_registered)
+ syscall_deregister(&gssd_syscall_offset,
+ &gssd_syscall_prev_sysent);
+}
+SYSUNINIT(kgss_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, kgss_uninit, NULL);
+
+int
+gssd_syscall(struct thread *td, struct gssd_syscall_args *uap)
+{
+ struct sockaddr_un sun;
+ struct netconfig *nconf;
+ char path[MAXPATHLEN];
+ int error;
+
+ error = priv_check(td, PRIV_NFS_DAEMON);
+ if (error)
+ return (error);
+
+ if (kgss_gssd_handle)
+ CLNT_DESTROY(kgss_gssd_handle);
+
+ error = copyinstr(uap->path, path, sizeof(path), NULL);
+ if (error)
+ return (error);
+
+ sun.sun_family = AF_LOCAL;
+ strcpy(sun.sun_path, path);
+ sun.sun_len = SUN_LEN(&sun);
+
+ nconf = getnetconfigent("local");
+ kgss_gssd_handle = clnt_reconnect_create(nconf,
+ (struct sockaddr *) &sun, GSSD, GSSDVERS,
+ RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+
+ return (0);
+}
+
+int
+kgss_oid_equal(const gss_OID oid1, const gss_OID oid2)
+{
+
+ if (oid1 == oid2)
+ return (1);
+ if (!oid1 || !oid2)
+ return (0);
+ if (oid1->length != oid2->length)
+ return (0);
+ if (memcmp(oid1->elements, oid2->elements, oid1->length))
+ return (0);
+ return (1);
+}
+
+void
+kgss_install_mech(gss_OID mech_type, const char *name, struct kobj_class *cls)
+{
+ struct kgss_mech *km;
+
+ km = malloc(sizeof(struct kgss_mech), M_GSSAPI, M_WAITOK);
+ km->km_mech_type = mech_type;
+ km->km_mech_name = name;
+ km->km_class = cls;
+ LIST_INSERT_HEAD(&kgss_mechs, km, km_link);
+}
+
+void
+kgss_uninstall_mech(gss_OID mech_type)
+{
+ struct kgss_mech *km;
+
+ LIST_FOREACH(km, &kgss_mechs, km_link) {
+ if (kgss_oid_equal(km->km_mech_type, mech_type)) {
+ LIST_REMOVE(km, km_link);
+ free(km, M_GSSAPI);
+ return;
+ }
+ }
+}
+
+gss_OID
+kgss_find_mech_by_name(const char *name)
+{
+ struct kgss_mech *km;
+
+ LIST_FOREACH(km, &kgss_mechs, km_link) {
+ if (!strcmp(km->km_mech_name, name)) {
+ return (km->km_mech_type);
+ }
+ }
+ return (GSS_C_NO_OID);
+}
+
+const char *
+kgss_find_mech_by_oid(const gss_OID oid)
+{
+ struct kgss_mech *km;
+
+ LIST_FOREACH(km, &kgss_mechs, km_link) {
+ if (kgss_oid_equal(km->km_mech_type, oid)) {
+ return (km->km_mech_name);
+ }
+ }
+ return (NULL);
+}
+
+gss_ctx_id_t
+kgss_create_context(gss_OID mech_type)
+{
+ struct kgss_mech *km;
+ gss_ctx_id_t ctx;
+
+ LIST_FOREACH(km, &kgss_mechs, km_link) {
+ if (kgss_oid_equal(km->km_mech_type, mech_type))
+ break;
+ }
+ if (!km)
+ return (NULL);
+
+ ctx = (gss_ctx_id_t) kobj_create(km->km_class, M_GSSAPI, M_WAITOK);
+ KGSS_INIT(ctx);
+
+ return (ctx);
+}
+
+void
+kgss_delete_context(gss_ctx_id_t ctx, gss_buffer_t output_token)
+{
+
+ KGSS_DELETE(ctx, output_token);
+ kobj_delete((kobj_t) ctx, M_GSSAPI);
+}
+
+OM_uint32
+kgss_transfer_context(gss_ctx_id_t ctx)
+{
+ struct export_sec_context_res res;
+ struct export_sec_context_args args;
+ enum clnt_stat stat;
+ OM_uint32 maj_stat;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ args.ctx = ctx->handle;
+ bzero(&res, sizeof(res));
+ stat = gssd_export_sec_context_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ return (GSS_S_FAILURE);
+ }
+
+ maj_stat = KGSS_IMPORT(ctx, res.format, &res.interprocess_token);
+ ctx->handle = 0;
+
+ xdr_free((xdrproc_t) xdr_export_sec_context_res, &res);
+
+ return (maj_stat);
+}
+
+void
+kgss_copy_buffer(const gss_buffer_t from, gss_buffer_t to)
+{
+ to->length = from->length;
+ if (from->length) {
+ to->value = malloc(from->length, M_GSSAPI, M_WAITOK);
+ bcopy(from->value, to->value, from->length);
+ } else {
+ to->value = NULL;
+ }
+}
+
+/*
+ * Kernel module glue
+ */
+static int
+kgssapi_modevent(module_t mod, int type, void *data)
+{
+
+ return (0);
+}
+static moduledata_t kgssapi_mod = {
+ "kgssapi",
+ kgssapi_modevent,
+ NULL,
+};
+DECLARE_MODULE(kgssapi, kgssapi_mod, SI_SUB_VFS, SI_ORDER_ANY);
+MODULE_DEPEND(kgssapi, krpc, 1, 1, 1);
+MODULE_VERSION(kgssapi, 1);
diff --git a/sys/kgssapi/gss_import_name.c b/sys/kgssapi/gss_import_name.c
new file mode 100644
index 0000000..c8019c5
--- /dev/null
+++ b/sys/kgssapi/gss_import_name.c
@@ -0,0 +1,79 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "gssd.h"
+
+OM_uint32
+gss_import_name(OM_uint32 *minor_status,
+ const gss_buffer_t input_name_buffer,
+ const gss_OID input_name_type,
+ gss_name_t *output_name)
+{
+ struct import_name_res res;
+ struct import_name_args args;
+ enum clnt_stat stat;
+ gss_name_t name;
+
+ *minor_status = 0;
+ *output_name = GSS_C_NO_NAME;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ args.input_name_buffer = *input_name_buffer;
+ args.input_name_type = input_name_type;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_import_name_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE) {
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+
+ name = malloc(sizeof(struct _gss_name_t), M_GSSAPI, M_WAITOK);
+ name->handle = res.output_name;
+ *minor_status = 0;
+ *output_name = name;
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_init_sec_context.c b/sys/kgssapi/gss_init_sec_context.c
new file mode 100644
index 0000000..0b7cee3
--- /dev/null
+++ b/sys/kgssapi/gss_init_sec_context.c
@@ -0,0 +1,135 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+#include <rpc/rpc.h>
+
+#include "gssd.h"
+#include "kgss_if.h"
+
+OM_uint32
+gss_init_sec_context(OM_uint32 * minor_status,
+ const gss_cred_id_t initiator_cred_handle,
+ gss_ctx_id_t * context_handle,
+ const gss_name_t target_name,
+ const gss_OID input_mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec)
+{
+ struct init_sec_context_res res;
+ struct init_sec_context_args args;
+ enum clnt_stat stat;
+ gss_ctx_id_t ctx = *context_handle;
+
+ *minor_status = 0;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ args.uid = curthread->td_ucred->cr_uid;
+ if (initiator_cred_handle)
+ args.cred = initiator_cred_handle->handle;
+ else
+ args.cred = 0;
+ if (ctx)
+ args.ctx = ctx->handle;
+ else
+ args.ctx = 0;
+ args.name = target_name->handle;
+ args.mech_type = input_mech_type;
+ args.req_flags = req_flags;
+ args.time_req = time_req;
+ args.input_chan_bindings = input_chan_bindings;
+ if (input_token)
+ args.input_token = *input_token;
+ else {
+ args.input_token.length = 0;
+ args.input_token.value = NULL;
+ }
+
+ bzero(&res, sizeof(res));
+ stat = gssd_init_sec_context_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE
+ && res.major_status != GSS_S_CONTINUE_NEEDED) {
+ *minor_status = res.minor_status;
+ xdr_free((xdrproc_t) xdr_init_sec_context_res, &res);
+ return (res.major_status);
+ }
+
+ *minor_status = res.minor_status;
+
+ if (!ctx) {
+ ctx = kgss_create_context(res.actual_mech_type);
+ if (!ctx) {
+ xdr_free((xdrproc_t) xdr_init_sec_context_res, &res);
+ *minor_status = 0;
+ return (GSS_S_BAD_MECH);
+ }
+ }
+ *context_handle = ctx;
+ ctx->handle = res.ctx;
+ if (actual_mech_type)
+ *actual_mech_type = KGSS_MECH_TYPE(ctx);
+ kgss_copy_buffer(&res.output_token, output_token);
+ if (ret_flags)
+ *ret_flags = res.ret_flags;
+ if (time_rec)
+ *time_rec = res.time_rec;
+
+ xdr_free((xdrproc_t) xdr_init_sec_context_res, &res);
+
+ /*
+ * If the context establishment is complete, export it from
+ * userland and hand the result (which includes key material
+ * etc.) to the kernel implementation.
+ */
+ if (res.major_status == GSS_S_COMPLETE)
+ res.major_status = kgss_transfer_context(ctx);
+
+ return (res.major_status);
+}
diff --git a/sys/kgssapi/gss_names.c b/sys/kgssapi/gss_names.c
new file mode 100644
index 0000000..a83693c
--- /dev/null
+++ b/sys/kgssapi/gss_names.c
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 2005 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <kgssapi/gssapi.h>
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x01"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) user_name(1)}. The constant
+ * GSS_C_NT_USER_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+static gss_OID_desc GSS_C_NT_USER_NAME_storage =
+ {10, (void *)(uintptr_t)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"};
+gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_storage;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
+ * The constant GSS_C_NT_MACHINE_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+static gss_OID_desc GSS_C_NT_MACHINE_UID_NAME_storage =
+ {10, (void *)(uintptr_t)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
+gss_OID GSS_C_NT_MACHINE_UID_NAME = &GSS_C_NT_MACHINE_UID_NAME_storage;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x03"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
+ * The constant GSS_C_NT_STRING_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+static gss_OID_desc GSS_C_NT_STRING_UID_NAME_storage =
+ {10, (void *)(uintptr_t)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"};
+gss_OID GSS_C_NT_STRING_UID_NAME = &GSS_C_NT_STRING_UID_NAME_storage;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) org(3) dod(6) internet(1) security(5)
+ * nametypes(6) gss-host-based-services(2)). The constant
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
+ * to that gss_OID_desc. This is a deprecated OID value, and
+ * implementations wishing to support hostbased-service names
+ * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
+ * defined below, to identify such names;
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
+ * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
+ * parameter, but should not be emitted by GSS-API
+ * implementations
+ */
+static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_storage =
+ {6, (void *)(uintptr_t)"\x2b\x06\x01\x05\x06\x02"};
+gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = &GSS_C_NT_HOSTBASED_SERVICE_X_storage;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x04"}, corresponding to an
+ * object-identifier value of {iso(1) member-body(2)
+ * Unites States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) service_name(4)}. The constant
+ * GSS_C_NT_HOSTBASED_SERVICE should be initialized
+ * to point to that gss_OID_desc.
+ */
+static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_storage =
+ {10, (void *)(uintptr_t)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
+gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_storage;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\01\x05\x06\x03"},
+ * corresponding to an object identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 3(gss-anonymous-name)}. The constant
+ * and GSS_C_NT_ANONYMOUS should be initialized to point
+ * to that gss_OID_desc.
+ */
+static gss_OID_desc GSS_C_NT_ANONYMOUS_storage =
+ {6, (void *)(uintptr_t)"\x2b\x06\01\x05\x06\x03"};
+gss_OID GSS_C_NT_ANONYMOUS = &GSS_C_NT_ANONYMOUS_storage;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
+ * corresponding to an object-identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 4(gss-api-exported-name)}. The constant
+ * GSS_C_NT_EXPORT_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+static gss_OID_desc GSS_C_NT_EXPORT_NAME_storage =
+ {6, (void *)(uintptr_t)"\x2b\x06\x01\x05\x06\x04"};
+gss_OID GSS_C_NT_EXPORT_NAME = &GSS_C_NT_EXPORT_NAME_storage;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * krb5(2) krb5_name(1)}. The recommended symbolic name for this type
+ * is "GSS_KRB5_NT_PRINCIPAL_NAME".
+ */
+static gss_OID_desc GSS_KRB5_NT_PRINCIPAL_NAME_storage =
+ {10, (void *)(uintptr_t)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
+gss_OID GSS_KRB5_NT_PRINCIPAL_NAME = &GSS_KRB5_NT_PRINCIPAL_NAME_storage;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) user_name(1)}. The recommended symbolic name for this
+ * type is "GSS_KRB5_NT_USER_NAME".
+ */
+gss_OID GSS_KRB5_NT_USER_NAME = &GSS_C_NT_USER_NAME_storage;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) machine_uid_name(2)}. The recommended symbolic name for
+ * this type is "GSS_KRB5_NT_MACHINE_UID_NAME".
+ */
+gss_OID GSS_KRB5_NT_MACHINE_UID_NAME = &GSS_C_NT_MACHINE_UID_NAME_storage;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) string_uid_name(3)}. The recommended symbolic name for
+ * this type is "GSS_KRB5_NT_STRING_UID_NAME".
+ */
+gss_OID GSS_KRB5_NT_STRING_UID_NAME = &GSS_C_NT_STRING_UID_NAME_storage;
+
+
diff --git a/sys/kgssapi/gss_pname_to_uid.c b/sys/kgssapi/gss_pname_to_uid.c
new file mode 100644
index 0000000..b83fd73
--- /dev/null
+++ b/sys/kgssapi/gss_pname_to_uid.c
@@ -0,0 +1,122 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kgss_if.h"
+
+OM_uint32
+gss_pname_to_uid(OM_uint32 *minor_status, const gss_name_t pname,
+ const gss_OID mech, uid_t *uidp)
+{
+ struct pname_to_uid_res res;
+ struct pname_to_uid_args args;
+ enum clnt_stat stat;
+
+ *minor_status = 0;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ if (pname == GSS_C_NO_NAME)
+ return (GSS_S_BAD_NAME);
+
+ args.pname = pname->handle;
+ args.mech = mech;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_pname_to_uid_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE) {
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+
+ *uidp = res.uid;
+ return (GSS_S_COMPLETE);
+}
+
+OM_uint32
+gss_pname_to_unix_cred(OM_uint32 *minor_status, const gss_name_t pname,
+ const gss_OID mech, uid_t *uidp, gid_t *gidp,
+ int *numgroups, gid_t *groups)
+
+{
+ struct pname_to_uid_res res;
+ struct pname_to_uid_args args;
+ enum clnt_stat stat;
+ int i, n;
+
+ *minor_status = 0;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ if (pname == GSS_C_NO_NAME)
+ return (GSS_S_BAD_NAME);
+
+ args.pname = pname->handle;
+ args.mech = mech;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_pname_to_uid_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE) {
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+
+ *uidp = res.uid;
+ *gidp = res.gid;
+ n = res.gidlist.gidlist_len;
+ if (n > *numgroups)
+ n = *numgroups;
+ for (i = 0; i < n; i++)
+ groups[i] = res.gidlist.gidlist_val[i];
+ *numgroups = n;
+
+ xdr_free((xdrproc_t) xdr_pname_to_uid_res, &res);
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_release_buffer.c b/sys/kgssapi/gss_release_buffer.c
new file mode 100644
index 0000000..ea5efc9
--- /dev/null
+++ b/sys/kgssapi/gss_release_buffer.c
@@ -0,0 +1,52 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+OM_uint32
+gss_release_buffer(OM_uint32 *minor_status, gss_buffer_t buffer)
+{
+
+ *minor_status = 0;
+ if (buffer->value) {
+ free(buffer->value, M_GSSAPI);
+ }
+ buffer->length = 0;
+ buffer->value = NULL;
+
+ return (GSS_S_COMPLETE);
+}
+
diff --git a/sys/kgssapi/gss_release_cred.c b/sys/kgssapi/gss_release_cred.c
new file mode 100644
index 0000000..6c68496
--- /dev/null
+++ b/sys/kgssapi/gss_release_cred.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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "gssd.h"
+
+OM_uint32
+gss_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle)
+{
+ struct release_cred_res res;
+ struct release_cred_args args;
+ enum clnt_stat stat;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ if (*cred_handle) {
+ args.cred = (*cred_handle)->handle;
+ stat = gssd_release_cred_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ free((*cred_handle), M_GSSAPI);
+ *cred_handle = NULL;
+
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+
+ *minor_status = 0;
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_release_name.c b/sys/kgssapi/gss_release_name.c
new file mode 100644
index 0000000..6f27e74
--- /dev/null
+++ b/sys/kgssapi/gss_release_name.c
@@ -0,0 +1,74 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "gssd.h"
+
+OM_uint32
+gss_release_name(OM_uint32 *minor_status, gss_name_t *input_name)
+{
+ struct release_name_res res;
+ struct release_name_args args;
+ enum clnt_stat stat;
+ gss_name_t name;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ if (*input_name) {
+ name = *input_name;
+ args.input_name = name->handle;
+
+ stat = gssd_release_name_1(&args, &res, kgss_gssd_handle);
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ free(name, M_GSSAPI);
+ *input_name = NULL;
+
+ if (res.major_status != GSS_S_COMPLETE) {
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+ }
+
+ *minor_status = 0;
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_release_oid_set.c b/sys/kgssapi/gss_release_oid_set.c
new file mode 100644
index 0000000..34b802a
--- /dev/null
+++ b/sys/kgssapi/gss_release_oid_set.c
@@ -0,0 +1,52 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+OM_uint32
+gss_release_oid_set(OM_uint32 *minor_status,
+ gss_OID_set *set)
+{
+
+ *minor_status = 0;
+ if (set && *set) {
+ if ((*set)->elements)
+ free((*set)->elements, M_GSSAPI);
+ free(*set, M_GSSAPI);
+ *set = GSS_C_NO_OID_SET;
+ }
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_set_cred_option.c b/sys/kgssapi/gss_set_cred_option.c
new file mode 100644
index 0000000..ce781af
--- /dev/null
+++ b/sys/kgssapi/gss_set_cred_option.c
@@ -0,0 +1,77 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "gssd.h"
+
+OM_uint32
+gss_set_cred_option(OM_uint32 *minor_status,
+ gss_cred_id_t *cred,
+ const gss_OID option_name,
+ const gss_buffer_t option_value)
+{
+ struct set_cred_option_res res;
+ struct set_cred_option_args args;
+ enum clnt_stat stat;
+
+ *minor_status = 0;
+
+ if (!kgss_gssd_handle)
+ return (GSS_S_FAILURE);
+
+ if (cred)
+ args.cred = (*cred)->handle;
+ else
+ args.cred = 0;
+ args.option_name = option_name;
+ args.option_value = *option_value;
+
+ bzero(&res, sizeof(res));
+ stat = gssd_set_cred_option_1(&args, &res, kgss_gssd_handle);
+
+ if (stat != RPC_SUCCESS) {
+ *minor_status = stat;
+ return (GSS_S_FAILURE);
+ }
+
+ if (res.major_status != GSS_S_COMPLETE) {
+ *minor_status = res.minor_status;
+ return (res.major_status);
+ }
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_test_oid_set_member.c b/sys/kgssapi/gss_test_oid_set_member.c
new file mode 100644
index 0000000..9642478
--- /dev/null
+++ b/sys/kgssapi/gss_test_oid_set_member.c
@@ -0,0 +1,54 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+OM_uint32
+gss_test_oid_set_member(OM_uint32 *minor_status,
+ const gss_OID member,
+ const gss_OID_set set,
+ int *present)
+{
+ size_t i;
+
+ *present = 0;
+ for (i = 0; i < set->count; i++)
+ if (kgss_oid_equal(member, &set->elements[i]))
+ *present = 1;
+
+ *minor_status = 0;
+ return (GSS_S_COMPLETE);
+}
diff --git a/sys/kgssapi/gss_unwrap.c b/sys/kgssapi/gss_unwrap.c
new file mode 100644
index 0000000..3b6d614
--- /dev/null
+++ b/sys/kgssapi/gss_unwrap.c
@@ -0,0 +1,97 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kgss_if.h"
+
+OM_uint32
+gss_unwrap(OM_uint32 *minor_status,
+ const gss_ctx_id_t ctx,
+ const gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int *conf_state,
+ gss_qop_t *qop_state)
+{
+ OM_uint32 maj_stat;
+ struct mbuf *m;
+
+ if (!ctx) {
+ *minor_status = 0;
+ return (GSS_S_NO_CONTEXT);
+ }
+
+ MGET(m, M_WAITOK, MT_DATA);
+ if (input_message_buffer->length > MLEN)
+ MCLGET(m, M_WAITOK);
+ m_append(m, input_message_buffer->length, input_message_buffer->value);
+
+ maj_stat = KGSS_UNWRAP(ctx, minor_status, &m, conf_state, qop_state);
+
+ /*
+ * On success, m is the wrapped message, on failure, m is
+ * freed.
+ */
+ if (maj_stat == GSS_S_COMPLETE) {
+ output_message_buffer->length = m_length(m, NULL);
+ output_message_buffer->value =
+ malloc(output_message_buffer->length,
+ M_GSSAPI, M_WAITOK);
+ m_copydata(m, 0, output_message_buffer->length,
+ output_message_buffer->value);
+ m_freem(m);
+ }
+
+ return (maj_stat);
+}
+
+OM_uint32
+gss_unwrap_mbuf(OM_uint32 *minor_status,
+ const gss_ctx_id_t ctx,
+ struct mbuf **mp,
+ int *conf_state,
+ gss_qop_t *qop_state)
+{
+
+ if (!ctx) {
+ *minor_status = 0;
+ return (GSS_S_NO_CONTEXT);
+ }
+
+ return (KGSS_UNWRAP(ctx, minor_status, mp, conf_state, qop_state));
+}
+
diff --git a/sys/kgssapi/gss_verify_mic.c b/sys/kgssapi/gss_verify_mic.c
new file mode 100644
index 0000000..0a8e7c4
--- /dev/null
+++ b/sys/kgssapi/gss_verify_mic.c
@@ -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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kgss_if.h"
+
+OM_uint32
+gss_verify_mic(OM_uint32 *minor_status,
+ const gss_ctx_id_t ctx,
+ const gss_buffer_t message_buffer,
+ const gss_buffer_t token_buffer,
+ gss_qop_t *qop_state)
+{
+ OM_uint32 maj_stat;
+ struct mbuf *m, *mic;
+
+ if (!ctx) {
+ *minor_status = 0;
+ return (GSS_S_NO_CONTEXT);
+ }
+
+ MGET(m, M_WAITOK, MT_DATA);
+ if (message_buffer->length > MLEN)
+ MCLGET(m, M_WAITOK);
+ m_append(m, message_buffer->length, message_buffer->value);
+
+ MGET(mic, M_WAITOK, MT_DATA);
+ if (token_buffer->length > MLEN)
+ MCLGET(mic, M_WAITOK);
+ m_append(mic, token_buffer->length, token_buffer->value);
+
+ maj_stat = KGSS_VERIFY_MIC(ctx, minor_status, m, mic, qop_state);
+
+ m_freem(m);
+ m_freem(mic);
+
+ return (maj_stat);
+}
+
+OM_uint32
+gss_verify_mic_mbuf(OM_uint32 *minor_status, const gss_ctx_id_t ctx,
+ struct mbuf *m, struct mbuf *mic, gss_qop_t *qop_state)
+{
+
+ if (!ctx) {
+ *minor_status = 0;
+ return (GSS_S_NO_CONTEXT);
+ }
+
+ return (KGSS_VERIFY_MIC(ctx, minor_status, m, mic, qop_state));
+}
+
diff --git a/sys/kgssapi/gss_wrap.c b/sys/kgssapi/gss_wrap.c
new file mode 100644
index 0000000..99bf686
--- /dev/null
+++ b/sys/kgssapi/gss_wrap.c
@@ -0,0 +1,96 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kgss_if.h"
+
+OM_uint32
+gss_wrap(OM_uint32 *minor_status,
+ const gss_ctx_id_t ctx,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ const gss_buffer_t input_message_buffer,
+ int *conf_state,
+ gss_buffer_t output_message_buffer)
+{
+ OM_uint32 maj_stat;
+ struct mbuf *m;
+
+ if (!ctx) {
+ *minor_status = 0;
+ return (GSS_S_NO_CONTEXT);
+ }
+
+ MGET(m, M_WAITOK, MT_DATA);
+ if (input_message_buffer->length > MLEN)
+ MCLGET(m, M_WAITOK);
+ m_append(m, input_message_buffer->length, input_message_buffer->value);
+
+ maj_stat = KGSS_WRAP(ctx, minor_status, conf_req_flag, qop_req,
+ &m, conf_state);
+
+ /*
+ * On success, m is the wrapped message, on failure, m is
+ * freed.
+ */
+ if (maj_stat == GSS_S_COMPLETE) {
+ output_message_buffer->length = m_length(m, NULL);
+ output_message_buffer->value =
+ malloc(output_message_buffer->length,
+ M_GSSAPI, M_WAITOK);
+ m_copydata(m, 0, output_message_buffer->length,
+ output_message_buffer->value);
+ m_freem(m);
+ }
+
+ return (maj_stat);
+}
+
+OM_uint32
+gss_wrap_mbuf(OM_uint32 *minor_status, const gss_ctx_id_t ctx,
+ int conf_req_flag, gss_qop_t qop_req, struct mbuf **mp, int *conf_state)
+{
+
+ if (!ctx) {
+ *minor_status = 0;
+ return (GSS_S_NO_CONTEXT);
+ }
+
+ return (KGSS_WRAP(ctx, minor_status, conf_req_flag, qop_req,
+ mp, conf_state));
+}
diff --git a/sys/kgssapi/gss_wrap_size_limit.c b/sys/kgssapi/gss_wrap_size_limit.c
new file mode 100644
index 0000000..17bedb2
--- /dev/null
+++ b/sys/kgssapi/gss_wrap_size_limit.c
@@ -0,0 +1,56 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kgss_if.h"
+
+OM_uint32
+gss_wrap_size_limit(OM_uint32 *minor_status,
+ const gss_ctx_id_t ctx,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ OM_uint32 req_output_size,
+ OM_uint32 *max_input_size)
+{
+ if (!ctx) {
+ *minor_status = 0;
+ return (GSS_S_NO_CONTEXT);
+ }
+
+ return (KGSS_WRAP_SIZE_LIMIT(ctx, minor_status, conf_req_flag,
+ qop_req, req_output_size, max_input_size));
+}
diff --git a/sys/kgssapi/gssapi.h b/sys/kgssapi/gssapi.h
new file mode 100644
index 0000000..c8f86c6
--- /dev/null
+++ b/sys/kgssapi/gssapi.h
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) The Internet Society (2000). All Rights Reserved.
+ *
+ * This document and translations of it may be copied and furnished to
+ * others, and derivative works that comment on or otherwise explain it
+ * or assist in its implementation may be prepared, copied, published
+ * and distributed, in whole or in part, without restriction of any
+ * kind, provided that the above copyright notice and this paragraph are
+ * included on all such copies and derivative works. However, this
+ * document itself may not be modified in any way, such as by removing
+ * the copyright notice or references to the Internet Society or other
+ * Internet organizations, except as needed for the purpose of
+ * developing Internet standards in which case the procedures for
+ * copyrights defined in the Internet Standards process must be
+ * followed, or as required to translate it into languages other than
+ * English.
+ *
+ * The limited permissions granted above are perpetual and will not be
+ * revoked by the Internet Society or its successors or assigns.
+ *
+ * This document and the information contained herein is provided on an
+ * "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _KGSSAPI_GSSAPI_H_
+#define _KGSSAPI_GSSAPI_H_
+
+/*
+ * A cut-down version of the GSS-API for in-kernel use
+ */
+
+/*
+ * Now define the three implementation-dependent types.
+ */
+typedef struct _gss_ctx_id_t *gss_ctx_id_t;
+typedef struct _gss_cred_id_t *gss_cred_id_t;
+typedef struct _gss_name_t *gss_name_t;
+
+/*
+ * We can't use X/Open definitions, so roll our own.
+ */
+typedef uint32_t OM_uint32;
+typedef uint64_t OM_uint64;
+
+typedef struct gss_OID_desc_struct {
+ OM_uint32 length;
+ void *elements;
+} gss_OID_desc, *gss_OID;
+
+typedef struct gss_OID_set_desc_struct {
+ size_t count;
+ gss_OID elements;
+} gss_OID_set_desc, *gss_OID_set;
+
+typedef struct gss_buffer_desc_struct {
+ size_t length;
+ void *value;
+} gss_buffer_desc, *gss_buffer_t;
+
+typedef struct gss_channel_bindings_struct {
+ OM_uint32 initiator_addrtype;
+ gss_buffer_desc initiator_address;
+ OM_uint32 acceptor_addrtype;
+ gss_buffer_desc acceptor_address;
+ gss_buffer_desc application_data;
+} *gss_channel_bindings_t;
+
+/*
+ * For now, define a QOP-type as an OM_uint32
+ */
+typedef OM_uint32 gss_qop_t;
+
+typedef int gss_cred_usage_t;
+
+/*
+ * Flag bits for context-level services.
+ */
+#define GSS_C_DELEG_FLAG 1
+#define GSS_C_MUTUAL_FLAG 2
+#define GSS_C_REPLAY_FLAG 4
+#define GSS_C_SEQUENCE_FLAG 8
+#define GSS_C_CONF_FLAG 16
+#define GSS_C_INTEG_FLAG 32
+#define GSS_C_ANON_FLAG 64
+#define GSS_C_PROT_READY_FLAG 128
+#define GSS_C_TRANS_FLAG 256
+
+/*
+ * Credential usage options
+ */
+#define GSS_C_BOTH 0
+#define GSS_C_INITIATE 1
+#define GSS_C_ACCEPT 2
+
+/*
+ * Status code types for gss_display_status
+ */
+#define GSS_C_GSS_CODE 1
+#define GSS_C_MECH_CODE 2
+
+/*
+ * The constant definitions for channel-bindings address families
+ */
+#define GSS_C_AF_UNSPEC 0
+#define GSS_C_AF_LOCAL 1
+#define GSS_C_AF_INET 2
+#define GSS_C_AF_IMPLINK 3
+#define GSS_C_AF_PUP 4
+#define GSS_C_AF_CHAOS 5
+#define GSS_C_AF_NS 6
+#define GSS_C_AF_NBS 7
+#define GSS_C_AF_ECMA 8
+#define GSS_C_AF_DATAKIT 9
+#define GSS_C_AF_CCITT 10
+#define GSS_C_AF_SNA 11
+#define GSS_C_AF_DECnet 12
+#define GSS_C_AF_DLI 13
+#define GSS_C_AF_LAT 14
+#define GSS_C_AF_HYLINK 15
+#define GSS_C_AF_APPLETALK 16
+#define GSS_C_AF_BSC 17
+#define GSS_C_AF_DSS 18
+#define GSS_C_AF_OSI 19
+#define GSS_C_AF_X25 21
+#define GSS_C_AF_NULLADDR 255
+
+/*
+ * Various Null values
+ */
+#define GSS_C_NO_NAME ((gss_name_t) 0)
+#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
+#define GSS_C_NO_OID ((gss_OID) 0)
+#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
+#define GSS_C_EMPTY_BUFFER {0, NULL}
+
+/*
+ * Some alternate names for a couple of the above
+ * values. These are defined for V1 compatibility.
+ */
+#define GSS_C_NULL_OID GSS_C_NO_OID
+#define GSS_C_NULL_OID_SET GSS_C_NO_OID_SET
+
+/*
+ * Define the default Quality of Protection for per-message
+ * services. Note that an implementation that offers multiple
+ * levels of QOP may define GSS_C_QOP_DEFAULT to be either zero
+ * (as done here) to mean "default protection", or to a specific
+ * explicit QOP value. However, a value of 0 should always be
+ * interpreted by a GSS-API implementation as a request for the
+ * default protection level.
+ */
+#define GSS_C_QOP_DEFAULT 0
+
+/*
+ * Expiration time of 2^32-1 seconds means infinite lifetime for a
+ * credential or security context
+ */
+#define GSS_C_INDEFINITE 0xfffffffful
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x01"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) user_name(1)}. The constant
+ * GSS_C_NT_USER_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_USER_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
+ * The constant GSS_C_NT_MACHINE_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_MACHINE_UID_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x03"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
+ * The constant GSS_C_NT_STRING_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_STRING_UID_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) org(3) dod(6) internet(1) security(5)
+ * nametypes(6) gss-host-based-services(2)). The constant
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
+ * to that gss_OID_desc. This is a deprecated OID value, and
+ * implementations wishing to support hostbased-service names
+ * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
+ * defined below, to identify such names;
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
+ * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
+ * parameter, but should not be emitted by GSS-API
+ * implementations
+ */
+extern gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x04"}, corresponding to an
+ * object-identifier value of {iso(1) member-body(2)
+ * Unites States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) service_name(4)}. The constant
+ * GSS_C_NT_HOSTBASED_SERVICE should be initialized
+ * to point to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_HOSTBASED_SERVICE;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\01\x05\x06\x03"},
+ * corresponding to an object identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 3(gss-anonymous-name)}. The constant
+ * and GSS_C_NT_ANONYMOUS should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_ANONYMOUS;
+
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
+ * corresponding to an object-identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 4(gss-api-exported-name)}. The constant
+ * GSS_C_NT_EXPORT_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_EXPORT_NAME;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * krb5(2) krb5_name(1)}. The recommended symbolic name for this type
+ * is "GSS_KRB5_NT_PRINCIPAL_NAME".
+ */
+extern gss_OID GSS_KRB5_NT_PRINCIPAL_NAME;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) user_name(1)}. The recommended symbolic name for this
+ * type is "GSS_KRB5_NT_USER_NAME".
+ */
+extern gss_OID GSS_KRB5_NT_USER_NAME;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) machine_uid_name(2)}. The recommended symbolic name for
+ * this type is "GSS_KRB5_NT_MACHINE_UID_NAME".
+ */
+extern gss_OID GSS_KRB5_NT_MACHINE_UID_NAME;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) string_uid_name(3)}. The recommended symbolic name for
+ * this type is "GSS_KRB5_NT_STRING_UID_NAME".
+ */
+extern gss_OID GSS_KRB5_NT_STRING_UID_NAME;
+
+/* Major status codes */
+
+#define GSS_S_COMPLETE 0
+
+/*
+ * Some "helper" definitions to make the status code macros obvious.
+ */
+#define GSS_C_CALLING_ERROR_OFFSET 24
+#define GSS_C_ROUTINE_ERROR_OFFSET 16
+#define GSS_C_SUPPLEMENTARY_OFFSET 0
+#define GSS_C_CALLING_ERROR_MASK 0377ul
+#define GSS_C_ROUTINE_ERROR_MASK 0377ul
+#define GSS_C_SUPPLEMENTARY_MASK 0177777ul
+
+/*
+ * The macros that test status codes for error conditions.
+ * Note that the GSS_ERROR() macro has changed slightly from
+ * the V1 GSS-API so that it now evaluates its argument
+ * only once.
+ */
+#define GSS_CALLING_ERROR(x) \
+ (x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
+#define GSS_ROUTINE_ERROR(x) \
+ (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
+#define GSS_SUPPLEMENTARY_INFO(x) \
+ (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
+#define GSS_ERROR(x) \
+ (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
+ (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
+
+/*
+ * Now the actual status code definitions
+ */
+
+/*
+ * Calling errors:
+ */
+#define GSS_S_CALL_INACCESSIBLE_READ \
+(1ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_INACCESSIBLE_WRITE \
+(2ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_BAD_STRUCTURE \
+(3ul << GSS_C_CALLING_ERROR_OFFSET)
+
+/*
+ * Routine errors:
+ */
+#define GSS_S_BAD_MECH (1ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAME (2ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAMETYPE (3ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_BINDINGS (4ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_STATUS (5ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_SIG (6ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_MIC GSS_S_BAD_SIG
+#define GSS_S_NO_CRED (7ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CONTEXT (8ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_TOKEN (9ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_CREDENTIAL (10ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CREDENTIALS_EXPIRED (11ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CONTEXT_EXPIRED (12ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_FAILURE (13ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_QOP (14ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAUTHORIZED (15ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAVAILABLE (16ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DUPLICATE_ELEMENT (17ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NAME_NOT_MN (18ul << GSS_C_ROUTINE_ERROR_OFFSET)
+
+/*
+ * Supplementary info bits:
+ */
+#define GSS_S_CONTINUE_NEEDED \
+ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
+#define GSS_S_DUPLICATE_TOKEN \
+ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
+#define GSS_S_OLD_TOKEN \
+ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
+#define GSS_S_UNSEQ_TOKEN \
+ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
+#define GSS_S_GAP_TOKEN \
+ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
+
+__BEGIN_DECLS
+
+/*
+ * Finally, function prototypes for the GSS-API routines.
+ */
+OM_uint32 gss_acquire_cred
+ (OM_uint32 *, /* minor_status */
+ const gss_name_t, /* desired_name */
+ OM_uint32, /* time_req */
+ const gss_OID_set, /* desired_mechs */
+ gss_cred_usage_t, /* cred_usage */
+ gss_cred_id_t *, /* output_cred_handle */
+ gss_OID_set *, /* actual_mechs */
+ OM_uint32 * /* time_rec */
+ );
+
+OM_uint32 gss_release_cred
+ (OM_uint32 *, /* minor_status */
+ gss_cred_id_t * /* cred_handle */
+ );
+
+OM_uint32 gss_init_sec_context
+ (OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* initiator_cred_handle */
+ gss_ctx_id_t *, /* context_handle */
+ const gss_name_t, /* target_name */
+ const gss_OID, /* mech_type */
+ OM_uint32, /* req_flags */
+ OM_uint32, /* time_req */
+ const gss_channel_bindings_t,
+ /* input_chan_bindings */
+ const gss_buffer_t, /* input_token */
+ gss_OID *, /* actual_mech_type */
+ gss_buffer_t, /* output_token */
+ OM_uint32 *, /* ret_flags */
+ OM_uint32 * /* time_rec */
+ );
+
+OM_uint32 gss_accept_sec_context
+ (OM_uint32 *, /* minor_status */
+ gss_ctx_id_t *, /* context_handle */
+ const gss_cred_id_t, /* acceptor_cred_handle */
+ const gss_buffer_t, /* input_token_buffer */
+ const gss_channel_bindings_t,
+ /* input_chan_bindings */
+ gss_name_t *, /* src_name */
+ gss_OID *, /* mech_type */
+ gss_buffer_t, /* output_token */
+ OM_uint32 *, /* ret_flags */
+ OM_uint32 *, /* time_rec */
+ gss_cred_id_t * /* delegated_cred_handle */
+ );
+
+OM_uint32 gss_delete_sec_context
+ (OM_uint32 *, /* minor_status */
+ gss_ctx_id_t *, /* context_handle */
+ gss_buffer_t /* output_token */
+ );
+
+OM_uint32 gss_get_mic
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ gss_qop_t, /* qop_req */
+ const gss_buffer_t, /* message_buffer */
+ gss_buffer_t /* message_token */
+ );
+
+OM_uint32 gss_verify_mic
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ const gss_buffer_t, /* message_buffer */
+ const gss_buffer_t, /* token_buffer */
+ gss_qop_t * /* qop_state */
+ );
+
+OM_uint32 gss_wrap
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ int, /* conf_req_flag */
+ gss_qop_t, /* qop_req */
+ const gss_buffer_t, /* input_message_buffer */
+ int *, /* conf_state */
+ gss_buffer_t /* output_message_buffer */
+ );
+
+OM_uint32 gss_unwrap
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ const gss_buffer_t, /* input_message_buffer */
+ gss_buffer_t, /* output_message_buffer */
+ int *, /* conf_state */
+ gss_qop_t * /* qop_state */
+ );
+
+OM_uint32 gss_display_status
+ (OM_uint32 *, /* minor_status */
+ OM_uint32, /* status_value */
+ int, /* status_type */
+ const gss_OID, /* mech_type */
+ OM_uint32 *, /* message_context */
+ gss_buffer_t /* status_string */
+ );
+
+OM_uint32 gss_import_name
+ (OM_uint32 *, /* minor_status */
+ const gss_buffer_t, /* input_name_buffer */
+ const gss_OID, /* input_name_type */
+ gss_name_t * /* output_name */
+ );
+
+OM_uint32 gss_export_name
+ (OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ gss_buffer_t /* exported_name */
+ );
+
+OM_uint32 gss_release_name
+ (OM_uint32 *, /* minor_status */
+ gss_name_t * /* input_name */
+ );
+
+OM_uint32 gss_release_buffer
+ (OM_uint32 *, /* minor_status */
+ gss_buffer_t /* buffer */
+ );
+
+OM_uint32 gss_release_oid_set
+ (OM_uint32 *, /* minor_status */
+ gss_OID_set * /* set */
+ );
+
+OM_uint32 gss_wrap_size_limit (
+ OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ int, /* conf_req_flag */
+ gss_qop_t, /* qop_req */
+ OM_uint32, /* req_output_size */
+ OM_uint32 * /* max_input_size */
+ );
+
+OM_uint32 gss_create_empty_oid_set (
+ OM_uint32 *, /* minor_status */
+ gss_OID_set * /* oid_set */
+ );
+
+OM_uint32 gss_add_oid_set_member (
+ OM_uint32 *, /* minor_status */
+ const gss_OID, /* member_oid */
+ gss_OID_set * /* oid_set */
+ );
+
+OM_uint32 gss_test_oid_set_member (
+ OM_uint32 *, /* minor_status */
+ const gss_OID, /* member */
+ const gss_OID_set, /* set */
+ int * /* present */
+ );
+
+OM_uint32 gss_canonicalize_name (
+ OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ const gss_OID, /* mech_type */
+ gss_name_t * /* output_name */
+ );
+
+/*
+ * Other extensions and helper functions.
+ */
+
+OM_uint32 gss_set_cred_option
+ (OM_uint32 *, /* minor status */
+ gss_cred_id_t *, /* cred */
+ const gss_OID, /* option to set */
+ const gss_buffer_t /* option value */
+ );
+
+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 */
+ );
+
+/*
+ * On entry, *numgroups is set to the maximum number of groups to return. On exit, *numgroups is set to the actual number of groups returned.
+ */
+OM_uint32 gss_pname_to_unix_cred
+ (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 */
+ gid_t *gidp, /* pointer to GID for result */
+ int *numgroups, /* number of groups */
+ gid_t *groups /* pointer to group list */
+ );
+
+/*
+ * Mbuf oriented message signing and encryption.
+ *
+ * Get_mic allocates an mbuf to hold the message checksum. Verify_mic
+ * may modify the passed-in mic but will not free it.
+ *
+ * Wrap and unwrap
+ * consume the message and generate a new mbuf chain with the
+ * result. The original message is freed on error.
+ */
+struct mbuf;
+OM_uint32 gss_get_mic_mbuf
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ gss_qop_t, /* qop_req */
+ struct mbuf *, /* message_buffer */
+ struct mbuf ** /* message_token */
+ );
+
+OM_uint32 gss_verify_mic_mbuf
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ struct mbuf *, /* message_buffer */
+ struct mbuf *, /* token_buffer */
+ gss_qop_t * /* qop_state */
+ );
+
+OM_uint32 gss_wrap_mbuf
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ int, /* conf_req_flag */
+ gss_qop_t, /* qop_req */
+ struct mbuf **, /* message_buffer */
+ int * /* conf_state */
+ );
+
+OM_uint32 gss_unwrap_mbuf
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ struct mbuf **, /* message_buffer */
+ int *, /* conf_state */
+ gss_qop_t * /* qop_state */
+ );
+
+__END_DECLS
+
+#endif /* _KGSSAPI_GSSAPI_H_ */
diff --git a/sys/kgssapi/gssapi_impl.h b/sys/kgssapi/gssapi_impl.h
new file mode 100644
index 0000000..629b80b
--- /dev/null
+++ b/sys/kgssapi/gssapi_impl.h
@@ -0,0 +1,67 @@
+/*-
+ * 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 "gssd.h"
+
+MALLOC_DECLARE(M_GSSAPI);
+
+struct _gss_ctx_id_t {
+ KOBJ_FIELDS;
+ gssd_ctx_id_t handle;
+};
+
+struct _gss_cred_id_t {
+ gssd_cred_id_t handle;
+};
+
+struct _gss_name_t {
+ gssd_name_t handle;
+};
+
+struct kgss_mech {
+ LIST_ENTRY(kgss_mech) km_link;
+ gss_OID km_mech_type;
+ const char *km_mech_name;
+ struct kobj_class *km_class;
+};
+LIST_HEAD(kgss_mech_list, kgss_mech);
+
+extern CLIENT *kgss_gssd_handle;
+extern struct kgss_mech_list kgss_mechs;
+
+int kgss_oid_equal(const gss_OID oid1, const gss_OID oid2);
+extern void kgss_install_mech(gss_OID mech_type, const char *name,
+ struct kobj_class *cls);
+extern void kgss_uninstall_mech(gss_OID mech_type);
+extern gss_OID kgss_find_mech_by_name(const char *name);
+extern const char *kgss_find_mech_by_oid(const gss_OID oid);
+extern gss_ctx_id_t kgss_create_context(gss_OID mech_type);
+extern void kgss_delete_context(gss_ctx_id_t ctx, gss_buffer_t output_token);
+extern OM_uint32 kgss_transfer_context(gss_ctx_id_t ctx);
+extern void kgss_copy_buffer(const gss_buffer_t from, gss_buffer_t to);
diff --git a/sys/kgssapi/gssd.x b/sys/kgssapi/gssd.x
new file mode 100644
index 0000000..f5682a0
--- /dev/null
+++ b/sys/kgssapi/gssd.x
@@ -0,0 +1,265 @@
+/*-
+ * 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$ */
+
+#ifdef RPC_HDR
+
+%#ifdef _KERNEL
+%#include <kgssapi/gssapi.h>
+%#else
+%#include <gssapi/gssapi.h>
+%#endif
+
+%extern bool_t xdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *buf);
+%extern bool_t xdr_gss_OID_desc(XDR *xdrs, gss_OID_desc *oid);
+%extern bool_t xdr_gss_OID(XDR *xdrs, gss_OID *oidp);
+%extern bool_t xdr_gss_OID_set_desc(XDR *xdrs, gss_OID_set_desc *set);
+%extern bool_t xdr_gss_OID_set(XDR *xdrs, gss_OID_set *setp);
+%extern bool_t xdr_gss_channel_bindings_t(XDR *xdrs, gss_channel_bindings_t *chp);
+
+#endif
+
+typedef uint64_t gssd_ctx_id_t;
+typedef uint64_t gssd_cred_id_t;
+typedef uint64_t gssd_name_t;
+
+struct init_sec_context_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ gssd_ctx_id_t ctx;
+ gss_OID actual_mech_type;
+ gss_buffer_desc output_token;
+ uint32_t ret_flags;
+ uint32_t time_rec;
+};
+
+struct init_sec_context_args {
+ uint32_t uid;
+ gssd_cred_id_t cred;
+ gssd_ctx_id_t ctx;
+ gssd_name_t name;
+ gss_OID mech_type;
+ uint32_t req_flags;
+ uint32_t time_req;
+ gss_channel_bindings_t input_chan_bindings;
+ gss_buffer_desc input_token;
+};
+
+struct accept_sec_context_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ gssd_ctx_id_t ctx;
+ gssd_name_t src_name;
+ gss_OID mech_type;
+ gss_buffer_desc output_token;
+ uint32_t ret_flags;
+ uint32_t time_rec;
+ gssd_cred_id_t delegated_cred_handle;
+};
+
+struct accept_sec_context_args {
+ gssd_ctx_id_t ctx;
+ gssd_cred_id_t cred;
+ gss_buffer_desc input_token;
+ gss_channel_bindings_t input_chan_bindings;
+};
+
+struct delete_sec_context_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ gss_buffer_desc output_token;
+};
+
+struct delete_sec_context_args {
+ gssd_ctx_id_t ctx;
+};
+
+enum sec_context_format {
+ KGSS_HEIMDAL_0_6,
+ KGSS_HEIMDAL_1_1
+};
+
+struct export_sec_context_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ enum sec_context_format format;
+ gss_buffer_desc interprocess_token;
+};
+
+struct export_sec_context_args {
+ gssd_ctx_id_t ctx;
+};
+
+struct import_name_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ gssd_name_t output_name;
+};
+
+struct import_name_args {
+ gss_buffer_desc input_name_buffer;
+ gss_OID input_name_type;
+};
+
+struct canonicalize_name_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ gssd_name_t output_name;
+};
+
+struct canonicalize_name_args {
+ gssd_name_t input_name;
+ gss_OID mech_type;
+};
+
+struct export_name_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ gss_buffer_desc exported_name;
+};
+
+struct export_name_args {
+ gssd_name_t input_name;
+};
+
+struct release_name_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+};
+
+struct release_name_args {
+ gssd_name_t input_name;
+};
+
+struct pname_to_uid_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t gidlist<>;
+};
+
+struct pname_to_uid_args {
+ gssd_name_t pname;
+ gss_OID mech;
+};
+
+struct acquire_cred_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ gssd_cred_id_t output_cred;
+ gss_OID_set actual_mechs;
+ uint32_t time_rec;
+};
+
+struct acquire_cred_args {
+ uint32_t uid;
+ gssd_name_t desired_name;
+ uint32_t time_req;
+ gss_OID_set desired_mechs;
+ int cred_usage;
+};
+
+struct set_cred_option_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+};
+
+struct set_cred_option_args {
+ gssd_cred_id_t cred;
+ gss_OID option_name;
+ gss_buffer_desc option_value;
+};
+
+struct release_cred_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+};
+
+struct release_cred_args {
+ gssd_cred_id_t cred;
+};
+
+struct display_status_res {
+ uint32_t major_status;
+ uint32_t minor_status;
+ uint32_t message_context;
+ gss_buffer_desc status_string;
+};
+
+struct display_status_args {
+ uint32_t status_value;
+ int status_type;
+ gss_OID mech_type;
+ uint32_t message_context;
+};
+
+program GSSD {
+ version GSSDVERS {
+ void GSSD_NULL(void) = 0;
+
+ init_sec_context_res
+ GSSD_INIT_SEC_CONTEXT(init_sec_context_args) = 1;
+
+ accept_sec_context_res
+ GSSD_ACCEPT_SEC_CONTEXT(accept_sec_context_args) = 2;
+
+ delete_sec_context_res
+ GSSD_DELETE_SEC_CONTEXT(delete_sec_context_args) = 3;
+
+ export_sec_context_res
+ GSSD_EXPORT_SEC_CONTEXT(export_sec_context_args) = 4;
+
+ import_name_res
+ GSSD_IMPORT_NAME(import_name_args) = 5;
+
+ canonicalize_name_res
+ GSSD_CANONICALIZE_NAME(canonicalize_name_args) = 6;
+
+ export_name_res
+ GSSD_EXPORT_NAME(export_name_args) = 7;
+
+ release_name_res
+ GSSD_RELEASE_NAME(release_name_args) = 8;
+
+ pname_to_uid_res
+ GSSD_PNAME_TO_UID(pname_to_uid_args) = 9;
+
+ acquire_cred_res
+ GSSD_ACQUIRE_CRED(acquire_cred_args) = 10;
+
+ set_cred_option_res
+ GSSD_SET_CRED_OPTION(set_cred_option_args) = 11;
+
+ release_cred_res
+ GSSD_RELEASE_CRED(release_cred_args) = 12;
+
+ display_status_res
+ GSSD_DISPLAY_STATUS(display_status_args) = 13;
+ } = 1;
+} = 0x40677373;
diff --git a/sys/kgssapi/gssd_prot.c b/sys/kgssapi/gssd_prot.c
new file mode 100644
index 0000000..3b8fbc5
--- /dev/null
+++ b/sys/kgssapi/gssd_prot.c
@@ -0,0 +1,244 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifdef _KERNEL
+#include <sys/malloc.h>
+#else
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+
+#include "gssd.h"
+
+bool_t
+xdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *buf)
+{
+ char *val;
+ u_int len;
+
+ len = buf->length;
+ val = buf->value;
+ if (!xdr_bytes(xdrs, &val, &len, ~0))
+ return (FALSE);
+ buf->length = len;
+ buf->value = val;
+
+ return (TRUE);
+}
+
+bool_t
+xdr_gss_OID_desc(XDR *xdrs, gss_OID_desc *oid)
+{
+ char *val;
+ u_int len;
+
+ len = oid->length;
+ val = oid->elements;
+ if (!xdr_bytes(xdrs, &val, &len, ~0))
+ return (FALSE);
+ oid->length = len;
+ oid->elements = val;
+
+ return (TRUE);
+}
+
+bool_t
+xdr_gss_OID(XDR *xdrs, gss_OID *oidp)
+{
+ gss_OID oid;
+ bool_t is_null;
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ oid = *oidp;
+ if (oid) {
+ is_null = FALSE;
+ if (!xdr_bool(xdrs, &is_null)
+ || !xdr_gss_OID_desc(xdrs, oid))
+ return (FALSE);
+ } else {
+ is_null = TRUE;
+ if (!xdr_bool(xdrs, &is_null))
+ return (FALSE);
+ }
+ break;
+
+ case XDR_DECODE:
+ if (!xdr_bool(xdrs, &is_null))
+ return (FALSE);
+ if (is_null) {
+ *oidp = GSS_C_NO_OID;
+ } else {
+ oid = mem_alloc(sizeof(gss_OID_desc));
+ memset(oid, 0, sizeof(*oid));
+ if (!xdr_gss_OID_desc(xdrs, oid))
+ return (FALSE);
+ *oidp = oid;
+ }
+ break;
+
+ case XDR_FREE:
+ oid = *oidp;
+ if (oid) {
+ xdr_gss_OID_desc(xdrs, oid);
+ mem_free(oid, sizeof(gss_OID_desc));
+ }
+ }
+
+ return (TRUE);
+}
+
+bool_t
+xdr_gss_OID_set_desc(XDR *xdrs, gss_OID_set_desc *set)
+{
+ caddr_t addr;
+ u_int len;
+
+ len = set->count;
+ addr = (caddr_t) set->elements;
+ if (!xdr_array(xdrs, &addr, &len, ~0, sizeof(gss_OID_desc),
+ (xdrproc_t) xdr_gss_OID_desc))
+ return (FALSE);
+ set->count = len;
+ set->elements = (gss_OID) addr;
+
+ return (TRUE);
+}
+
+bool_t
+xdr_gss_OID_set(XDR *xdrs, gss_OID_set *setp)
+{
+ gss_OID_set set;
+ bool_t is_null;
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ set = *setp;
+ if (set) {
+ is_null = FALSE;
+ if (!xdr_bool(xdrs, &is_null)
+ || !xdr_gss_OID_set_desc(xdrs, set))
+ return (FALSE);
+ } else {
+ is_null = TRUE;
+ if (!xdr_bool(xdrs, &is_null))
+ return (FALSE);
+ }
+ break;
+
+ case XDR_DECODE:
+ if (!xdr_bool(xdrs, &is_null))
+ return (FALSE);
+ if (is_null) {
+ *setp = GSS_C_NO_OID_SET;
+ } else {
+ set = mem_alloc(sizeof(gss_OID_set_desc));
+ memset(set, 0, sizeof(*set));
+ if (!xdr_gss_OID_set_desc(xdrs, set))
+ return (FALSE);
+ *setp = set;
+ }
+ break;
+
+ case XDR_FREE:
+ set = *setp;
+ if (set) {
+ xdr_gss_OID_set_desc(xdrs, set);
+ mem_free(set, sizeof(gss_OID_set_desc));
+ }
+ }
+
+ return (TRUE);
+}
+
+bool_t
+xdr_gss_channel_bindings_t(XDR *xdrs, gss_channel_bindings_t *chp)
+{
+ gss_channel_bindings_t ch;
+ bool_t is_null;
+
+ switch (xdrs->x_op) {
+ case XDR_ENCODE:
+ ch = *chp;
+ if (ch) {
+ is_null = FALSE;
+ if (!xdr_bool(xdrs, &is_null)
+ || !xdr_uint32_t(xdrs, &ch->initiator_addrtype)
+ || !xdr_gss_buffer_desc(xdrs,
+ &ch->initiator_address)
+ || !xdr_uint32_t(xdrs, &ch->acceptor_addrtype)
+ || !xdr_gss_buffer_desc(xdrs,
+ &ch->acceptor_address)
+ || !xdr_gss_buffer_desc(xdrs,
+ &ch->application_data))
+ return (FALSE);
+ } else {
+ is_null = TRUE;
+ if (!xdr_bool(xdrs, &is_null))
+ return (FALSE);
+ }
+ break;
+
+ case XDR_DECODE:
+ if (!xdr_bool(xdrs, &is_null))
+ return (FALSE);
+ if (is_null) {
+ *chp = GSS_C_NO_CHANNEL_BINDINGS;
+ } else {
+ ch = mem_alloc(sizeof(*ch));
+ memset(ch, 0, sizeof(*ch));
+ if (!xdr_uint32_t(xdrs, &ch->initiator_addrtype)
+ || !xdr_gss_buffer_desc(xdrs,
+ &ch->initiator_address)
+ || !xdr_uint32_t(xdrs, &ch->acceptor_addrtype)
+ || !xdr_gss_buffer_desc(xdrs,
+ &ch->acceptor_address)
+ || !xdr_gss_buffer_desc(xdrs,
+ &ch->application_data))
+ return (FALSE);
+ *chp = ch;
+ }
+ break;
+
+ case XDR_FREE:
+ ch = *chp;
+ if (ch) {
+ xdr_gss_buffer_desc(xdrs, &ch->initiator_address);
+ xdr_gss_buffer_desc(xdrs, &ch->acceptor_address);
+ xdr_gss_buffer_desc(xdrs, &ch->application_data);
+ mem_free(ch, sizeof(*ch));
+ }
+ }
+
+ return (TRUE);
+}
diff --git a/sys/kgssapi/gsstest.c b/sys/kgssapi/gsstest.c
new file mode 100644
index 0000000..1764703
--- /dev/null
+++ b/sys/kgssapi/gsstest.c
@@ -0,0 +1,1141 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/ctype.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/socketvar.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+#include <rpc/rpcb_prot.h>
+#include <rpc/rpcsec_gss.h>
+
+static void
+report_error(gss_OID mech, OM_uint32 maj, OM_uint32 min)
+{
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 message_context;
+ gss_buffer_desc buf;
+
+ uprintf("major_stat=%d, minor_stat=%d\n", maj, min);
+ message_context = 0;
+ do {
+ maj_stat = gss_display_status(&min_stat, maj,
+ GSS_C_GSS_CODE, GSS_C_NO_OID, &message_context, &buf);
+ if (GSS_ERROR(maj_stat))
+ break;
+ uprintf("%.*s\n", (int)buf.length, (char *) buf.value);
+ gss_release_buffer(&min_stat, &buf);
+ } while (message_context);
+ if (mech && min) {
+ message_context = 0;
+ do {
+ maj_stat = gss_display_status(&min_stat, min,
+ GSS_C_MECH_CODE, mech, &message_context, &buf);
+ if (GSS_ERROR(maj_stat))
+ break;
+ uprintf("%.*s\n", (int)buf.length, (char *) buf.value);
+ gss_release_buffer(&min_stat, &buf);
+ } while (message_context);
+ }
+}
+
+#if 0
+static void
+send_token_to_peer(const gss_buffer_t token)
+{
+ const uint8_t *p;
+ size_t i;
+
+ printf("send token:\n");
+ printf("%d ", (int) token->length);
+ p = (const uint8_t *) token->value;
+ for (i = 0; i < token->length; i++)
+ printf("%02x", *p++);
+ printf("\n");
+}
+
+static void
+receive_token_from_peer(gss_buffer_t token)
+{
+ char line[8192];
+ char *p;
+ uint8_t *q;
+ int len, val;
+
+ printf("receive token:\n");
+ fgets(line, sizeof(line), stdin);
+ if (line[strlen(line) - 1] != '\n') {
+ printf("token truncated\n");
+ exit(1);
+ }
+ p = line;
+ if (sscanf(line, "%d ", &len) != 1) {
+ printf("bad token\n");
+ exit(1);
+ }
+ p = strchr(p, ' ') + 1;
+ token->length = len;
+ token->value = malloc(len);
+ q = (uint8_t *) token->value;
+ while (len) {
+ if (sscanf(p, "%02x", &val) != 1) {
+ printf("bad token\n");
+ exit(1);
+ }
+ *q++ = val;
+ p += 2;
+ len--;
+ }
+}
+#endif
+
+#if 0
+void
+server(int argc, char** argv)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc input_token, output_token;
+ gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;
+ gss_name_t client_name;
+ gss_OID mech_type;
+
+ if (argc != 1)
+ usage();
+
+ do {
+ receive_token_from_peer(&input_token);
+ maj_stat = gss_accept_sec_context(&min_stat,
+ &context_hdl,
+ GSS_C_NO_CREDENTIAL,
+ &input_token,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client_name,
+ &mech_type,
+ &output_token,
+ NULL,
+ NULL,
+ NULL);
+ if (GSS_ERROR(maj_stat)) {
+ report_error(mech_type, maj_stat, min_stat);
+ }
+ if (output_token.length != 0) {
+ send_token_to_peer(&output_token);
+ gss_release_buffer(&min_stat, &output_token);
+ }
+ if (GSS_ERROR(maj_stat)) {
+ if (context_hdl != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&min_stat,
+ &context_hdl,
+ GSS_C_NO_BUFFER);
+ break;
+ }
+ } while (maj_stat & GSS_S_CONTINUE_NEEDED);
+
+ if (client_name) {
+ gss_buffer_desc name_desc;
+ char buf[512];
+
+ gss_display_name(&min_stat, client_name, &name_desc, NULL);
+ memcpy(buf, name_desc.value, name_desc.length);
+ buf[name_desc.length] = 0;
+ gss_release_buffer(&min_stat, &name_desc);
+ printf("client name is %s\n", buf);
+ }
+
+ receive_token_from_peer(&input_token);
+ gss_unwrap(&min_stat, context_hdl, &input_token, &output_token,
+ NULL, NULL);
+ printf("%.*s\n", (int)output_token.length, (char *) output_token.value);
+ gss_release_buffer(&min_stat, &output_token);
+}
+#endif
+
+/* 1.2.752.43.13.14 */
+static gss_OID_desc gss_krb5_set_allowable_enctypes_x_desc =
+{6, (void *) "\x2a\x85\x70\x2b\x0d\x0e"};
+
+gss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X = &gss_krb5_set_allowable_enctypes_x_desc;
+#define ETYPE_DES_CBC_CRC 1
+
+/*
+ * Create an initiator context and acceptor context in the kernel and
+ * use them to exchange signed and sealed messages.
+ */
+static int
+gsstest_1(void)
+{
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 smaj_stat, smin_stat;
+ int context_established = 0;
+ gss_ctx_id_t client_context = GSS_C_NO_CONTEXT;
+ gss_ctx_id_t server_context = GSS_C_NO_CONTEXT;
+ gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
+ gss_name_t name = GSS_C_NO_NAME;
+ gss_name_t received_name = GSS_C_NO_NAME;
+ gss_buffer_desc name_desc;
+ gss_buffer_desc client_token, server_token, message_buf;
+ gss_OID mech, actual_mech, mech_type;
+ static gss_OID_desc krb5_desc =
+ {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
+#if 0
+ static gss_OID_desc spnego_desc =
+ {6, (void *)"\x2b\x06\x01\x05\x05\x02"};
+ static gss_OID_desc ntlm_desc =
+ {10, (void *)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"};
+#endif
+ char enctype[sizeof(uint32_t)];
+
+ mech = GSS_C_NO_OID;
+
+ {
+ static char sbuf[512];
+ snprintf(sbuf, sizeof(sbuf), "nfs@%s", hostname);
+ name_desc.value = sbuf;
+ }
+
+ name_desc.length = strlen((const char *) name_desc.value);
+ maj_stat = gss_import_name(&min_stat, &name_desc,
+ GSS_C_NT_HOSTBASED_SERVICE, &name);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_import_name failed\n");
+ report_error(mech, maj_stat, min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME,
+ 0, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client_cred,
+ NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_acquire_cred (client) failed\n");
+ report_error(mech, maj_stat, min_stat);
+ goto out;
+ }
+
+ enctype[0] = (ETYPE_DES_CBC_CRC >> 24) & 0xff;
+ enctype[1] = (ETYPE_DES_CBC_CRC >> 16) & 0xff;
+ enctype[2] = (ETYPE_DES_CBC_CRC >> 8) & 0xff;
+ enctype[3] = ETYPE_DES_CBC_CRC & 0xff;
+ message_buf.length = sizeof(enctype);
+ message_buf.value = enctype;
+ maj_stat = gss_set_cred_option(&min_stat, &client_cred,
+ GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, &message_buf);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_set_cred_option failed\n");
+ report_error(mech, maj_stat, min_stat);
+ goto out;
+ }
+
+ server_token.length = 0;
+ server_token.value = NULL;
+ while (!context_established) {
+ client_token.length = 0;
+ client_token.value = NULL;
+ maj_stat = gss_init_sec_context(&min_stat,
+ client_cred,
+ &client_context,
+ name,
+ mech,
+ GSS_C_MUTUAL_FLAG|GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &server_token,
+ &actual_mech,
+ &client_token,
+ NULL,
+ NULL);
+ if (server_token.length)
+ gss_release_buffer(&smin_stat, &server_token);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_init_sec_context failed\n");
+ report_error(mech, maj_stat, min_stat);
+ goto out;
+ }
+
+ if (client_token.length != 0) {
+ if (!server_cred) {
+ gss_OID_set_desc oid_set;
+ oid_set.count = 1;
+ oid_set.elements = &krb5_desc;
+ smaj_stat = gss_acquire_cred(&smin_stat,
+ name, 0, &oid_set, GSS_C_ACCEPT, &server_cred,
+ NULL, NULL);
+ if (GSS_ERROR(smaj_stat)) {
+ printf("gss_acquire_cred (server) failed\n");
+ report_error(mech_type, smaj_stat, smin_stat);
+ goto out;
+ }
+ }
+ smaj_stat = gss_accept_sec_context(&smin_stat,
+ &server_context,
+ server_cred,
+ &client_token,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &received_name,
+ &mech_type,
+ &server_token,
+ NULL,
+ NULL,
+ NULL);
+ if (GSS_ERROR(smaj_stat)) {
+ printf("gss_accept_sec_context failed\n");
+ report_error(mech_type, smaj_stat, smin_stat);
+ goto out;
+ }
+ gss_release_buffer(&min_stat, &client_token);
+ }
+ if (GSS_ERROR(maj_stat)) {
+ if (client_context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&min_stat,
+ &client_context,
+ GSS_C_NO_BUFFER);
+ break;
+ }
+
+ if (maj_stat == GSS_S_COMPLETE) {
+ context_established = 1;
+ }
+ }
+
+ message_buf.length = strlen("Hello world");
+ message_buf.value = (void *) "Hello world";
+
+ maj_stat = gss_get_mic(&min_stat, client_context,
+ GSS_C_QOP_DEFAULT, &message_buf, &client_token);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_get_mic failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ maj_stat = gss_verify_mic(&min_stat, server_context,
+ &message_buf, &client_token, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_verify_mic failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ gss_release_buffer(&min_stat, &client_token);
+
+ maj_stat = gss_wrap(&min_stat, client_context,
+ TRUE, GSS_C_QOP_DEFAULT, &message_buf, NULL, &client_token);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_wrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ maj_stat = gss_unwrap(&min_stat, server_context,
+ &client_token, &server_token, NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_unwrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ if (message_buf.length != server_token.length
+ || memcmp(message_buf.value, server_token.value,
+ message_buf.length))
+ printf("unwrap result corrupt\n");
+
+ gss_release_buffer(&min_stat, &client_token);
+ gss_release_buffer(&min_stat, &server_token);
+
+out:
+ if (client_context)
+ gss_delete_sec_context(&min_stat, &client_context,
+ GSS_C_NO_BUFFER);
+ if (server_context)
+ gss_delete_sec_context(&min_stat, &server_context,
+ GSS_C_NO_BUFFER);
+ if (client_cred)
+ gss_release_cred(&min_stat, &client_cred);
+ if (server_cred)
+ gss_release_cred(&min_stat, &server_cred);
+ if (name)
+ gss_release_name(&min_stat, &name);
+ if (received_name)
+ gss_release_name(&min_stat, &received_name);
+
+ return (0);
+}
+
+/*
+ * Interoperability with userland. This takes several steps:
+ *
+ * 1. Accept an initiator token from userland, return acceptor
+ * token. Repeat this step until both userland and kernel return
+ * GSS_S_COMPLETE.
+ *
+ * 2. Receive a signed message from userland and verify the
+ * signature. Return a signed reply to userland for it to verify.
+ *
+ * 3. Receive a wrapped message from userland and unwrap it. Return a
+ * wrapped reply to userland.
+ */
+static int
+gsstest_2(int step, const gss_buffer_t input_token,
+ OM_uint32 *maj_stat_res, OM_uint32 *min_stat_res, gss_buffer_t output_token)
+{
+ OM_uint32 maj_stat, min_stat;
+ static int context_established = 0;
+ static gss_ctx_id_t server_context = GSS_C_NO_CONTEXT;
+ static gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
+ static gss_name_t name = GSS_C_NO_NAME;
+ gss_buffer_desc name_desc;
+ gss_buffer_desc message_buf;
+ gss_OID mech_type = GSS_C_NO_OID;
+ char enctype[sizeof(uint32_t)];
+ int error = EINVAL;
+
+ maj_stat = GSS_S_FAILURE;
+ min_stat = 0;
+ switch (step) {
+
+ case 1:
+ if (server_context == GSS_C_NO_CONTEXT) {
+ static char sbuf[512];
+ snprintf(sbuf, sizeof(sbuf), "nfs@%s", hostname);
+ name_desc.value = sbuf;
+ name_desc.length = strlen((const char *)
+ name_desc.value);
+ maj_stat = gss_import_name(&min_stat, &name_desc,
+ GSS_C_NT_HOSTBASED_SERVICE, &name);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_import_name failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat,
+ name, 0, GSS_C_NO_OID_SET, GSS_C_ACCEPT,
+ &server_cred, NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_acquire_cred (server) failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ enctype[0] = (ETYPE_DES_CBC_CRC >> 24) & 0xff;
+ enctype[1] = (ETYPE_DES_CBC_CRC >> 16) & 0xff;
+ enctype[2] = (ETYPE_DES_CBC_CRC >> 8) & 0xff;
+ enctype[3] = ETYPE_DES_CBC_CRC & 0xff;
+ message_buf.length = sizeof(enctype);
+ message_buf.value = enctype;
+ maj_stat = gss_set_cred_option(&min_stat, &server_cred,
+ GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, &message_buf);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_set_cred_option failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ }
+
+ maj_stat = gss_accept_sec_context(&min_stat,
+ &server_context,
+ server_cred,
+ input_token,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ NULL,
+ &mech_type,
+ output_token,
+ NULL,
+ NULL,
+ NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_accept_sec_context failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ if (maj_stat == GSS_S_COMPLETE) {
+ context_established = 1;
+ }
+ *maj_stat_res = maj_stat;
+ *min_stat_res = min_stat;
+ break;
+
+ case 2:
+ message_buf.length = strlen("Hello world");
+ message_buf.value = (void *) "Hello world";
+
+ maj_stat = gss_verify_mic(&min_stat, server_context,
+ &message_buf, input_token, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_verify_mic failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_get_mic(&min_stat, server_context,
+ GSS_C_QOP_DEFAULT, &message_buf, output_token);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_get_mic failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ break;
+
+ case 3:
+ maj_stat = gss_unwrap(&min_stat, server_context,
+ input_token, &message_buf, NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_unwrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ gss_release_buffer(&min_stat, &message_buf);
+
+ message_buf.length = strlen("Hello world");
+ message_buf.value = (void *) "Hello world";
+ maj_stat = gss_wrap(&min_stat, server_context,
+ TRUE, GSS_C_QOP_DEFAULT, &message_buf, NULL, output_token);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_wrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ break;
+
+ case 4:
+ maj_stat = gss_unwrap(&min_stat, server_context,
+ input_token, &message_buf, NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_unwrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ gss_release_buffer(&min_stat, &message_buf);
+
+ message_buf.length = strlen("Hello world");
+ message_buf.value = (void *) "Hello world";
+ maj_stat = gss_wrap(&min_stat, server_context,
+ FALSE, GSS_C_QOP_DEFAULT, &message_buf, NULL, output_token);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_wrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ break;
+
+ case 5:
+ error = 0;
+ goto out;
+ }
+ *maj_stat_res = maj_stat;
+ *min_stat_res = min_stat;
+ return (0);
+
+out:
+ *maj_stat_res = maj_stat;
+ *min_stat_res = min_stat;
+ if (server_context)
+ gss_delete_sec_context(&min_stat, &server_context,
+ GSS_C_NO_BUFFER);
+ if (server_cred)
+ gss_release_cred(&min_stat, &server_cred);
+ if (name)
+ gss_release_name(&min_stat, &name);
+
+ return (error);
+}
+
+/*
+ * Create an RPC client handle for the given (address,prog,vers)
+ * triple using UDP.
+ */
+static CLIENT *
+gsstest_get_rpc(struct sockaddr *sa, rpcprog_t prog, rpcvers_t vers)
+{
+ struct thread *td = curthread;
+ const char* protofmly;
+ struct sockaddr_storage ss;
+ struct socket *so;
+ CLIENT *rpcb;
+ struct timeval timo;
+ RPCB parms;
+ char *uaddr;
+ enum clnt_stat stat = RPC_SUCCESS;
+ int rpcvers = RPCBVERS4;
+ bool_t do_tcp = FALSE;
+ struct portmap mapping;
+ u_short port = 0;
+
+ /*
+ * First we need to contact the remote RPCBIND service to find
+ * the right port.
+ */
+ memcpy(&ss, sa, sa->sa_len);
+ switch (ss.ss_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)&ss)->sin_port = htons(111);
+ protofmly = "inet";
+ socreate(AF_INET, &so, SOCK_DGRAM, 0, td->td_ucred, td);
+ break;
+
+#ifdef INET6
+ case AF_INET6:
+ ((struct sockaddr_in6 *)&ss)->sin6_port = htons(111);
+ protofmly = "inet6";
+ socreate(AF_INET6, &so, SOCK_DGRAM, 0, td->td_ucred, td);
+ break;
+#endif
+
+ default:
+ /*
+ * Unsupported address family - fail.
+ */
+ return (NULL);
+ }
+
+ rpcb = clnt_dg_create(so, (struct sockaddr *)&ss,
+ RPCBPROG, rpcvers, 0, 0);
+ if (!rpcb)
+ return (NULL);
+
+try_tcp:
+ parms.r_prog = prog;
+ parms.r_vers = vers;
+ if (do_tcp)
+ parms.r_netid = "tcp";
+ else
+ parms.r_netid = "udp";
+ parms.r_addr = "";
+ parms.r_owner = "";
+
+ /*
+ * Use the default timeout.
+ */
+ timo.tv_sec = 25;
+ timo.tv_usec = 0;
+again:
+ switch (rpcvers) {
+ case RPCBVERS4:
+ case RPCBVERS:
+ /*
+ * Try RPCBIND 4 then 3.
+ */
+ uaddr = NULL;
+ stat = CLNT_CALL(rpcb, (rpcprog_t) RPCBPROC_GETADDR,
+ (xdrproc_t) xdr_rpcb, &parms,
+ (xdrproc_t) xdr_wrapstring, &uaddr, timo);
+ if (stat == RPC_PROGVERSMISMATCH) {
+ if (rpcvers == RPCBVERS4)
+ rpcvers = RPCBVERS;
+ else if (rpcvers == RPCBVERS)
+ rpcvers = PMAPVERS;
+ CLNT_CONTROL(rpcb, CLSET_VERS, &rpcvers);
+ goto again;
+ } else if (stat == RPC_SUCCESS) {
+ /*
+ * We have a reply from the remote RPCBIND - turn it
+ * into an appropriate address and make a new client
+ * that can talk to the remote service.
+ *
+ * XXX fixup IPv6 scope ID.
+ */
+ struct netbuf *a;
+ a = __rpc_uaddr2taddr_af(ss.ss_family, uaddr);
+ xdr_free((xdrproc_t) xdr_wrapstring, &uaddr);
+ if (!a) {
+ CLNT_DESTROY(rpcb);
+ return (NULL);
+ }
+ memcpy(&ss, a->buf, a->len);
+ free(a->buf, M_RPC);
+ free(a, M_RPC);
+ }
+ break;
+ case PMAPVERS:
+ /*
+ * Try portmap.
+ */
+ mapping.pm_prog = parms.r_prog;
+ mapping.pm_vers = parms.r_vers;
+ mapping.pm_prot = do_tcp ? IPPROTO_TCP : IPPROTO_UDP;
+ mapping.pm_port = 0;
+
+ stat = CLNT_CALL(rpcb, (rpcprog_t) PMAPPROC_GETPORT,
+ (xdrproc_t) xdr_portmap, &mapping,
+ (xdrproc_t) xdr_u_short, &port, timo);
+
+ if (stat == RPC_SUCCESS) {
+ switch (ss.ss_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)&ss)->sin_port =
+ htons(port);
+ break;
+
+#ifdef INET6
+ case AF_INET6:
+ ((struct sockaddr_in6 *)&ss)->sin6_port =
+ htons(port);
+ break;
+#endif
+ }
+ }
+ break;
+ default:
+ panic("invalid rpcvers %d", rpcvers);
+ }
+ /*
+ * We may have a positive response from the portmapper, but
+ * the requested service was not found. Make sure we received
+ * a valid port.
+ */
+ switch (ss.ss_family) {
+ case AF_INET:
+ port = ((struct sockaddr_in *)&ss)->sin_port;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ port = ((struct sockaddr_in6 *)&ss)->sin6_port;
+ break;
+#endif
+ }
+ if (stat != RPC_SUCCESS || !port) {
+ /*
+ * If we were able to talk to rpcbind or portmap, but the udp
+ * variant wasn't available, ask about tcp.
+ *
+ * XXX - We could also check for a TCP portmapper, but
+ * if the host is running a portmapper at all, we should be able
+ * to hail it over UDP.
+ */
+ if (stat == RPC_SUCCESS && !do_tcp) {
+ do_tcp = TRUE;
+ goto try_tcp;
+ }
+
+ /* Otherwise, bad news. */
+ printf("gsstest_get_rpc: failed to contact remote rpcbind, "
+ "stat = %d, port = %d\n",
+ (int) stat, port);
+ CLNT_DESTROY(rpcb);
+ return (NULL);
+ }
+
+ if (do_tcp) {
+ /*
+ * Destroy the UDP client we used to speak to rpcbind and
+ * recreate as a TCP client.
+ */
+ struct netconfig *nconf = NULL;
+
+ CLNT_DESTROY(rpcb);
+
+ switch (ss.ss_family) {
+ case AF_INET:
+ nconf = getnetconfigent("tcp");
+ break;
+#ifdef INET6
+ case AF_INET6:
+ nconf = getnetconfigent("tcp6");
+ break;
+#endif
+ }
+
+ rpcb = clnt_reconnect_create(nconf, (struct sockaddr *)&ss,
+ prog, vers, 0, 0);
+ } else {
+ /*
+ * Re-use the client we used to speak to rpcbind.
+ */
+ CLNT_CONTROL(rpcb, CLSET_SVC_ADDR, &ss);
+ CLNT_CONTROL(rpcb, CLSET_PROG, &prog);
+ CLNT_CONTROL(rpcb, CLSET_VERS, &vers);
+ }
+
+ return (rpcb);
+}
+
+/*
+ * RPCSEC_GSS client
+ */
+static int
+gsstest_3(void)
+{
+ struct sockaddr_in sin;
+ char service[128];
+ CLIENT *client;
+ AUTH *auth;
+ rpc_gss_options_ret_t options_ret;
+ enum clnt_stat stat;
+ struct timeval tv;
+ rpc_gss_service_t svc;
+ int i;
+
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = 0;
+
+ client = gsstest_get_rpc((struct sockaddr *) &sin, 123456, 1);
+ if (!client) {
+ uprintf("Can't connect to service\n");
+ return(1);
+ }
+
+ snprintf(service, sizeof(service), "host@%s", hostname);
+
+ auth = rpc_gss_seccreate(client, curthread->td_ucred,
+ service, "kerberosv5", rpc_gss_svc_privacy,
+ NULL, NULL, &options_ret);
+ if (!auth) {
+ gss_OID oid;
+ uprintf("Can't authorize to service (mech=%s)\n",
+ options_ret.actual_mechanism);
+ oid = GSS_C_NO_OID;
+ rpc_gss_mech_to_oid(options_ret.actual_mechanism, &oid);
+ report_error(oid, options_ret.major_status,
+ options_ret.minor_status);
+ CLNT_DESTROY(client);
+ return (1);
+ }
+
+ for (svc = rpc_gss_svc_none; svc <= rpc_gss_svc_privacy; svc++) {
+ const char *svc_names[] = {
+ "rpc_gss_svc_default",
+ "rpc_gss_svc_none",
+ "rpc_gss_svc_integrity",
+ "rpc_gss_svc_privacy"
+ };
+ int num;
+
+ rpc_gss_set_defaults(auth, svc, NULL);
+
+ client->cl_auth = auth;
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ for (i = 42; i < 142; i++) {
+ num = i;
+ stat = CLNT_CALL(client, 1,
+ (xdrproc_t) xdr_int, (char *) &num,
+ (xdrproc_t) xdr_int, (char *) &num, tv);
+ if (stat == RPC_SUCCESS) {
+ if (num != i + 100)
+ uprintf("unexpected reply %d\n", num);
+ } else {
+ uprintf("call failed, stat=%d\n", (int) stat);
+ break;
+ }
+ }
+ if (i == 142)
+ uprintf("call succeeded with %s\n", svc_names[svc]);
+ }
+
+ AUTH_DESTROY(auth);
+ CLNT_RELEASE(client);
+
+ return (0);
+}
+
+/*
+ * RPCSEC_GSS server
+ */
+static rpc_gss_principal_t server_acl = NULL;
+static bool_t server_new_context(struct svc_req *req, gss_cred_id_t deleg,
+ gss_ctx_id_t gss_context, rpc_gss_lock_t *lock, void **cookie);
+static void server_program_1(struct svc_req *rqstp, register SVCXPRT *transp);
+
+static int
+gsstest_4(void)
+{
+ SVCPOOL *pool;
+ char principal[128 + 5];
+ const char **mechs;
+ static rpc_gss_callback_t cb;
+
+ snprintf(principal, sizeof(principal), "host@%s", hostname);
+
+ mechs = rpc_gss_get_mechanisms();
+ while (*mechs) {
+ if (!rpc_gss_set_svc_name(principal, *mechs, GSS_C_INDEFINITE,
+ 123456, 1)) {
+ rpc_gss_error_t e;
+
+ rpc_gss_get_error(&e);
+ printf("setting name for %s for %s failed: %d, %d\n",
+ principal, *mechs,
+ e.rpc_gss_error, e.system_error);
+ }
+ mechs++;
+ }
+
+ cb.program = 123456;
+ cb.version = 1;
+ cb.callback = server_new_context;
+ rpc_gss_set_callback(&cb);
+
+ pool = svcpool_create("gsstest", NULL);
+
+ svc_create(pool, server_program_1, 123456, 1, NULL);
+ svc_run(pool);
+
+ rpc_gss_clear_svc_name(123456, 1);
+ rpc_gss_clear_callback(&cb);
+
+ svcpool_destroy(pool);
+
+ return (0);
+}
+
+static void
+server_program_1(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+ rpc_gss_rawcred_t *rcred;
+ rpc_gss_ucred_t *ucred;
+ int i, num;
+
+ if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) {
+ svcerr_weakauth(rqstp);
+ return;
+ }
+
+ if (!rpc_gss_getcred(rqstp, &rcred, &ucred, NULL)) {
+ svcerr_systemerr(rqstp);
+ return;
+ }
+
+ printf("svc=%d, mech=%s, uid=%d, gid=%d, gids={",
+ rcred->service, rcred->mechanism, ucred->uid, ucred->gid);
+ for (i = 0; i < ucred->gidlen; i++) {
+ if (i > 0) printf(",");
+ printf("%d", ucred->gidlist[i]);
+ }
+ printf("}\n");
+
+ switch (rqstp->rq_proc) {
+ case 0:
+ if (!svc_getargs(rqstp, (xdrproc_t) xdr_void, 0)) {
+ svcerr_decode(rqstp);
+ goto out;
+ }
+ if (!svc_sendreply(rqstp, (xdrproc_t) xdr_void, 0)) {
+ svcerr_systemerr(rqstp);
+ }
+ goto out;
+
+ case 1:
+ if (!svc_getargs(rqstp, (xdrproc_t) xdr_int,
+ (char *) &num)) {
+ svcerr_decode(rqstp);
+ goto out;
+ }
+ num += 100;
+ if (!svc_sendreply(rqstp, (xdrproc_t) xdr_int,
+ (char *) &num)) {
+ svcerr_systemerr(rqstp);
+ }
+ goto out;
+
+ default:
+ svcerr_noproc(rqstp);
+ goto out;
+ }
+
+out:
+ return;
+}
+
+static void
+print_principal(rpc_gss_principal_t principal)
+{
+ int i, len, n;
+ uint8_t *p;
+
+ len = principal->len;
+ p = (uint8_t *) principal->name;
+ while (len > 0) {
+ n = len;
+ if (n > 16)
+ n = 16;
+ for (i = 0; i < n; i++)
+ printf("%02x ", p[i]);
+ for (; i < 16; i++)
+ printf(" ");
+ printf("|");
+ for (i = 0; i < n; i++)
+ printf("%c", isprint(p[i]) ? p[i] : '.');
+ printf("|\n");
+ len -= n;
+ p += n;
+ }
+}
+
+static bool_t
+server_new_context(__unused struct svc_req *req,
+ gss_cred_id_t deleg,
+ __unused gss_ctx_id_t gss_context,
+ rpc_gss_lock_t *lock,
+ __unused void **cookie)
+{
+ rpc_gss_rawcred_t *rcred = lock->raw_cred;
+ OM_uint32 junk;
+
+ printf("new security context version=%d, mech=%s, qop=%s:\n",
+ rcred->version, rcred->mechanism, rcred->qop);
+ print_principal(rcred->client_principal);
+
+ if (server_acl) {
+ if (rcred->client_principal->len != server_acl->len
+ || memcmp(rcred->client_principal->name, server_acl->name,
+ server_acl->len)) {
+ return (FALSE);
+ }
+ }
+ gss_release_cred(&junk, &deleg);
+
+ return (TRUE);
+}
+
+/*
+ * Hook up a syscall for gssapi testing.
+ */
+
+struct gsstest_args {
+ int a_op;
+ void *a_args;
+ void *a_res;
+};
+
+struct gsstest_2_args {
+ int step; /* test step number */
+ gss_buffer_desc input_token; /* token from userland */
+ gss_buffer_desc output_token; /* buffer to receive reply token */
+};
+struct gsstest_2_res {
+ OM_uint32 maj_stat; /* maj_stat from kernel */
+ OM_uint32 min_stat; /* min_stat from kernel */
+ gss_buffer_desc output_token; /* reply token (using space from gsstest_2_args.output) */
+};
+
+static int
+gsstest(struct thread *td, struct gsstest_args *uap)
+{
+ int error;
+
+ switch (uap->a_op) {
+ case 1:
+ return (gsstest_1());
+
+ case 2: {
+ struct gsstest_2_args args;
+ struct gsstest_2_res res;
+ gss_buffer_desc input_token, output_token;
+ OM_uint32 junk;
+
+ error = copyin(uap->a_args, &args, sizeof(args));
+ if (error)
+ return (error);
+ input_token.length = args.input_token.length;
+ input_token.value = malloc(input_token.length, M_GSSAPI,
+ M_WAITOK);
+ error = copyin(args.input_token.value, input_token.value,
+ input_token.length);
+ if (error) {
+ gss_release_buffer(&junk, &input_token);
+ return (error);
+ }
+ output_token.length = 0;
+ output_token.value = NULL;
+ gsstest_2(args.step, &input_token,
+ &res.maj_stat, &res.min_stat, &output_token);
+ gss_release_buffer(&junk, &input_token);
+ if (output_token.length > args.output_token.length) {
+ gss_release_buffer(&junk, &output_token);
+ return (EOVERFLOW);
+ }
+ res.output_token.length = output_token.length;
+ res.output_token.value = args.output_token.value;
+ error = copyout(output_token.value, res.output_token.value,
+ output_token.length);
+ gss_release_buffer(&junk, &output_token);
+ if (error)
+ return (error);
+
+ return (copyout(&res, uap->a_res, sizeof(res)));
+
+ break;
+ }
+ case 3:
+ return (gsstest_3());
+ case 4:
+ return (gsstest_4());
+ }
+
+ return (EINVAL);
+}
+
+/*
+ * The `sysent' for the new syscall
+ */
+static struct sysent gsstest_sysent = {
+ 3, /* sy_narg */
+ (sy_call_t *) gsstest /* sy_call */
+};
+
+/*
+ * The offset in sysent where the syscall is allocated.
+ */
+static int gsstest_offset = NO_SYSCALL;
+
+/*
+ * The function called at load/unload.
+ */
+
+
+static int
+gsstest_load(struct module *module, int cmd, void *arg)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case MOD_LOAD :
+ break;
+ case MOD_UNLOAD :
+ break;
+ default :
+ error = EOPNOTSUPP;
+ break;
+ }
+ return error;
+}
+
+SYSCALL_MODULE(gsstest_syscall, &gsstest_offset, &gsstest_sysent,
+ gsstest_load, NULL);
diff --git a/sys/kgssapi/kgss_if.m b/sys/kgssapi/kgss_if.m
new file mode 100644
index 0000000..53d499a
--- /dev/null
+++ b/sys/kgssapi/kgss_if.m
@@ -0,0 +1,95 @@
+#-
+# 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$
+
+# Interface for the in-kernel part of a GSS-API mechanism
+
+#include <kgssapi/gssapi.h>
+#include "gssd.h"
+
+INTERFACE kgss;
+
+METHOD void init {
+ gss_ctx_id_t ctx;
+};
+
+METHOD OM_uint32 import {
+ gss_ctx_id_t ctx;
+ enum sec_context_format format;
+ const gss_buffer_t context_token;
+};
+
+METHOD void delete {
+ gss_ctx_id_t ctx;
+ gss_buffer_t output_token;
+};
+
+METHOD gss_OID mech_type {
+ gss_ctx_id_t ctx;
+};
+
+METHOD OM_uint32 get_mic {
+ gss_ctx_id_t ctx;
+ OM_uint32 *minor_status;
+ gss_qop_t qop_req;
+ struct mbuf *message_buffer;
+ struct mbuf **message_token;
+};
+
+METHOD OM_uint32 verify_mic {
+ gss_ctx_id_t ctx;
+ OM_uint32 *minor_status;
+ struct mbuf *message_buffer;
+ struct mbuf *token_buffer;
+ gss_qop_t *qop_state;
+};
+
+METHOD OM_uint32 wrap {
+ gss_ctx_id_t ctx;
+ OM_uint32 *minor_status;
+ int conf_req_flag;
+ gss_qop_t qop_req;
+ struct mbuf **message_buffer;
+ int *conf_state;
+};
+
+METHOD OM_uint32 unwrap {
+ gss_ctx_id_t ctx;
+ OM_uint32 *minor_status;
+ struct mbuf **message_buffer;
+ int *conf_state;
+ gss_qop_t *qop_state;
+};
+
+METHOD OM_uint32 wrap_size_limit {
+ gss_ctx_id_t ctx;
+ OM_uint32 *minor_status;
+ int conf_req_flag;
+ gss_qop_t qop_req;
+ OM_uint32 req_ouput_size;
+ OM_uint32 *max_input_size;
+}
diff --git a/sys/kgssapi/krb5/kcrypto.c b/sys/kgssapi/krb5/kcrypto.c
new file mode 100644
index 0000000..27ee3ec
--- /dev/null
+++ b/sys/kgssapi/krb5/kcrypto.c
@@ -0,0 +1,266 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/kobj.h>
+#include <sys/mbuf.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kcrypto.h"
+
+static struct krb5_encryption_class *krb5_encryption_classes[] = {
+ &krb5_des_encryption_class,
+ &krb5_des3_encryption_class,
+ &krb5_aes128_encryption_class,
+ &krb5_aes256_encryption_class,
+ &krb5_arcfour_encryption_class,
+ &krb5_arcfour_56_encryption_class,
+ NULL
+};
+
+struct krb5_encryption_class *
+krb5_find_encryption_class(int etype)
+{
+ int i;
+
+ for (i = 0; krb5_encryption_classes[i]; i++) {
+ if (krb5_encryption_classes[i]->ec_type == etype)
+ return (krb5_encryption_classes[i]);
+ }
+ return (NULL);
+}
+
+struct krb5_key_state *
+krb5_create_key(const struct krb5_encryption_class *ec)
+{
+ struct krb5_key_state *ks;
+
+ ks = malloc(sizeof(struct krb5_key_state), M_GSSAPI, M_WAITOK);
+ ks->ks_class = ec;
+ refcount_init(&ks->ks_refs, 1);
+ ks->ks_key = malloc(ec->ec_keylen, M_GSSAPI, M_WAITOK);
+ ec->ec_init(ks);
+
+ return (ks);
+}
+
+void
+krb5_free_key(struct krb5_key_state *ks)
+{
+
+ if (refcount_release(&ks->ks_refs)) {
+ ks->ks_class->ec_destroy(ks);
+ bzero(ks->ks_key, ks->ks_class->ec_keylen);
+ free(ks->ks_key, M_GSSAPI);
+ free(ks, M_GSSAPI);
+ }
+}
+
+static size_t
+gcd(size_t a, size_t b)
+{
+
+ if (b == 0)
+ return (a);
+ return gcd(b, a % b);
+}
+
+static size_t
+lcm(size_t a, size_t b)
+{
+ return ((a * b) / gcd(a, b));
+}
+
+/*
+ * Rotate right 13 of a variable precision number in 'in', storing the
+ * result in 'out'. The number is assumed to be big-endian in memory
+ * representation.
+ */
+static void
+krb5_rotate_right_13(uint8_t *out, uint8_t *in, size_t numlen)
+{
+ uint32_t carry;
+ size_t i;
+
+ /*
+ * Special case when numlen == 1. A rotate right 13 of a
+ * single byte number changes to a rotate right 5.
+ */
+ if (numlen == 1) {
+ carry = in[0] >> 5;
+ out[0] = (in[0] << 3) | carry;
+ return;
+ }
+
+ carry = ((in[numlen - 2] & 31) << 8) | in[numlen - 1];
+ for (i = 2; i < numlen; i++) {
+ out[i] = ((in[i - 2] & 31) << 3) | (in[i - 1] >> 5);
+ }
+ out[1] = ((carry & 31) << 3) | (in[0] >> 5);
+ out[0] = carry >> 5;
+}
+
+/*
+ * Add two variable precision numbers in big-endian representation
+ * using ones-complement arithmetic.
+ */
+static void
+krb5_ones_complement_add(uint8_t *out, const uint8_t *in, size_t len)
+{
+ int n, i;
+
+ /*
+ * First calculate the 2s complement sum, remembering the
+ * carry.
+ */
+ n = 0;
+ for (i = len - 1; i >= 0; i--) {
+ n = out[i] + in[i] + n;
+ out[i] = n;
+ n >>= 8;
+ }
+ /*
+ * Then add back the carry.
+ */
+ for (i = len - 1; n && i >= 0; i--) {
+ n = out[i] + n;
+ out[i] = n;
+ n >>= 8;
+ }
+}
+
+static void
+krb5_n_fold(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
+{
+ size_t tmplen;
+ uint8_t *tmp;
+ size_t i;
+ uint8_t *p;
+
+ tmplen = lcm(inlen, outlen);
+ tmp = malloc(tmplen, M_GSSAPI, M_WAITOK);
+
+ bcopy(in, tmp, inlen);
+ for (i = inlen, p = tmp; i < tmplen; i += inlen, p += inlen) {
+ krb5_rotate_right_13(p + inlen, p, inlen);
+ }
+ bzero(out, outlen);
+ for (i = 0, p = tmp; i < tmplen; i += outlen, p += outlen) {
+ krb5_ones_complement_add(out, p, outlen);
+ }
+ free(tmp, M_GSSAPI);
+}
+
+struct krb5_key_state *
+krb5_derive_key(struct krb5_key_state *inkey,
+ void *constant, size_t constantlen)
+{
+ struct krb5_key_state *dk;
+ const struct krb5_encryption_class *ec = inkey->ks_class;
+ uint8_t *folded;
+ uint8_t *bytes, *p, *q;
+ struct mbuf *m;
+ int randomlen, i;
+
+ /*
+ * Expand the constant to blocklen bytes.
+ */
+ folded = malloc(ec->ec_blocklen, M_GSSAPI, M_WAITOK);
+ krb5_n_fold(folded, ec->ec_blocklen, constant, constantlen);
+
+ /*
+ * Generate enough bytes for keybits rounded up to a multiple
+ * of blocklen.
+ */
+ randomlen = ((ec->ec_keybits/8 + ec->ec_blocklen - 1) / ec->ec_blocklen)
+ * ec->ec_blocklen;
+ bytes = malloc(randomlen, M_GSSAPI, M_WAITOK);
+ MGET(m, M_WAITOK, MT_DATA);
+ m->m_len = ec->ec_blocklen;
+ for (i = 0, p = bytes, q = folded; i < randomlen;
+ q = p, i += ec->ec_blocklen, p += ec->ec_blocklen) {
+ bcopy(q, m->m_data, ec->ec_blocklen);
+ krb5_encrypt(inkey, m, 0, ec->ec_blocklen, NULL, 0);
+ bcopy(m->m_data, p, ec->ec_blocklen);
+ }
+ m_free(m);
+
+ dk = krb5_create_key(ec);
+ krb5_random_to_key(dk, bytes);
+
+ free(folded, M_GSSAPI);
+ free(bytes, M_GSSAPI);
+
+ return (dk);
+}
+
+static struct krb5_key_state *
+krb5_get_usage_key(struct krb5_key_state *basekey, int usage, int which)
+{
+ const struct krb5_encryption_class *ec = basekey->ks_class;
+
+ if (ec->ec_flags & EC_DERIVED_KEYS) {
+ uint8_t constant[5];
+
+ constant[0] = usage >> 24;
+ constant[1] = usage >> 16;
+ constant[2] = usage >> 8;
+ constant[3] = usage;
+ constant[4] = which;
+ return (krb5_derive_key(basekey, constant, 5));
+ } else {
+ refcount_acquire(&basekey->ks_refs);
+ return (basekey);
+ }
+}
+
+struct krb5_key_state *
+krb5_get_encryption_key(struct krb5_key_state *basekey, int usage)
+{
+
+ return (krb5_get_usage_key(basekey, usage, 0xaa));
+}
+
+struct krb5_key_state *
+krb5_get_integrity_key(struct krb5_key_state *basekey, int usage)
+{
+
+ return (krb5_get_usage_key(basekey, usage, 0x55));
+}
+
+struct krb5_key_state *
+krb5_get_checksum_key(struct krb5_key_state *basekey, int usage)
+{
+
+ return (krb5_get_usage_key(basekey, usage, 0x99));
+}
diff --git a/sys/kgssapi/krb5/kcrypto.h b/sys/kgssapi/krb5/kcrypto.h
new file mode 100644
index 0000000..5486641
--- /dev/null
+++ b/sys/kgssapi/krb5/kcrypto.h
@@ -0,0 +1,154 @@
+/*-
+ * 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 <sys/_iovec.h>
+
+#define ETYPE_NULL 0
+#define ETYPE_DES_CBC_CRC 1
+#define ETYPE_DES_CBC_MD4 2
+#define ETYPE_DES_CBC_MD5 3
+#define ETYPE_DES3_CBC_MD5 5
+#define ETYPE_OLD_DES3_CBC_SHA1 7
+#define ETYPE_DES3_CBC_SHA1 16
+#define ETYPE_AES128_CTS_HMAC_SHA1_96 17
+#define ETYPE_AES256_CTS_HMAC_SHA1_96 18
+#define ETYPE_ARCFOUR_HMAC_MD5 23
+#define ETYPE_ARCFOUR_HMAC_MD5_56 24
+
+/*
+ * Key usages for des3-cbc-sha1 tokens
+ */
+#define KG_USAGE_SEAL 22
+#define KG_USAGE_SIGN 23
+#define KG_USAGE_SEQ 24
+
+/*
+ * Key usages for RFC4121 tokens
+ */
+#define KG_USAGE_ACCEPTOR_SEAL 22
+#define KG_USAGE_ACCEPTOR_SIGN 23
+#define KG_USAGE_INITIATOR_SEAL 24
+#define KG_USAGE_INITIATOR_SIGN 25
+
+struct krb5_key_state;
+
+typedef void init_func(struct krb5_key_state *ks);
+typedef void destroy_func(struct krb5_key_state *ks);
+typedef void set_key_func(struct krb5_key_state *ks, const void *in);
+typedef void random_to_key_func(struct krb5_key_state *ks, const void *in);
+typedef void encrypt_func(const struct krb5_key_state *ks,
+ struct mbuf *inout, size_t skip, size_t len, void *ivec, size_t ivlen);
+typedef void checksum_func(const struct krb5_key_state *ks, int usage,
+ struct mbuf *inout, size_t skip, size_t inlen, size_t outlen);
+
+struct krb5_encryption_class {
+ const char *ec_name;
+ int ec_type;
+ int ec_flags;
+#define EC_DERIVED_KEYS 1
+ size_t ec_blocklen;
+ size_t ec_msgblocklen;
+ size_t ec_checksumlen;
+ size_t ec_keybits; /* key length in bits */
+ size_t ec_keylen; /* size of key in memory */
+ init_func *ec_init;
+ destroy_func *ec_destroy;
+ set_key_func *ec_set_key;
+ random_to_key_func *ec_random_to_key;
+ encrypt_func *ec_encrypt;
+ encrypt_func *ec_decrypt;
+ checksum_func *ec_checksum;
+};
+
+struct krb5_key_state {
+ const struct krb5_encryption_class *ks_class;
+ volatile u_int ks_refs;
+ void *ks_key;
+ void *ks_priv;
+};
+
+extern struct krb5_encryption_class krb5_des_encryption_class;
+extern struct krb5_encryption_class krb5_des3_encryption_class;
+extern struct krb5_encryption_class krb5_aes128_encryption_class;
+extern struct krb5_encryption_class krb5_aes256_encryption_class;
+extern struct krb5_encryption_class krb5_arcfour_encryption_class;
+extern struct krb5_encryption_class krb5_arcfour_56_encryption_class;
+
+static __inline void
+krb5_set_key(struct krb5_key_state *ks, const void *keydata)
+{
+
+ ks->ks_class->ec_set_key(ks, keydata);
+}
+
+static __inline void
+krb5_random_to_key(struct krb5_key_state *ks, const void *keydata)
+{
+
+ ks->ks_class->ec_random_to_key(ks, keydata);
+}
+
+static __inline void
+krb5_encrypt(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, size_t ivlen)
+{
+
+ ks->ks_class->ec_encrypt(ks, inout, skip, len, ivec, ivlen);
+}
+
+static __inline void
+krb5_decrypt(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, size_t ivlen)
+{
+
+ ks->ks_class->ec_decrypt(ks, inout, skip, len, ivec, ivlen);
+}
+
+static __inline void
+krb5_checksum(const struct krb5_key_state *ks, int usage,
+ struct mbuf *inout, size_t skip, size_t inlen, size_t outlen)
+{
+
+ ks->ks_class->ec_checksum(ks, usage, inout, skip, inlen, outlen);
+}
+
+extern struct krb5_encryption_class *
+ krb5_find_encryption_class(int etype);
+extern struct krb5_key_state *
+ krb5_create_key(const struct krb5_encryption_class *ec);
+extern void krb5_free_key(struct krb5_key_state *ks);
+extern struct krb5_key_state *
+ krb5_derive_key(struct krb5_key_state *inkey,
+ void *constant, size_t constantlen);
+extern struct krb5_key_state *
+ krb5_get_encryption_key(struct krb5_key_state *basekey, int usage);
+extern struct krb5_key_state *
+ krb5_get_integrity_key(struct krb5_key_state *basekey, int usage);
+extern struct krb5_key_state *
+ krb5_get_checksum_key(struct krb5_key_state *basekey, int usage);
diff --git a/sys/kgssapi/krb5/kcrypto_aes.c b/sys/kgssapi/krb5/kcrypto_aes.c
new file mode 100644
index 0000000..d2dac21
--- /dev/null
+++ b/sys/kgssapi/krb5/kcrypto_aes.c
@@ -0,0 +1,384 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/kobj.h>
+#include <sys/mbuf.h>
+#include <opencrypto/cryptodev.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kcrypto.h"
+
+struct aes_state {
+ struct mtx as_lock;
+ uint64_t as_session;
+};
+
+static void
+aes_init(struct krb5_key_state *ks)
+{
+ struct aes_state *as;
+
+ as = malloc(sizeof(struct aes_state), M_GSSAPI, M_WAITOK|M_ZERO);
+ mtx_init(&as->as_lock, "gss aes lock", NULL, MTX_DEF);
+ ks->ks_priv = as;
+}
+
+static void
+aes_destroy(struct krb5_key_state *ks)
+{
+ struct aes_state *as = ks->ks_priv;
+
+ if (as->as_session)
+ crypto_freesession(as->as_session);
+ mtx_destroy(&as->as_lock);
+ free(ks->ks_priv, M_GSSAPI);
+}
+
+static void
+aes_set_key(struct krb5_key_state *ks, const void *in)
+{
+ void *kp = ks->ks_key;
+ struct aes_state *as = ks->ks_priv;
+ struct cryptoini cri[2];
+
+ if (kp != in)
+ bcopy(in, kp, ks->ks_class->ec_keylen);
+
+ if (as->as_session)
+ crypto_freesession(as->as_session);
+
+ bzero(cri, sizeof(cri));
+
+ /*
+ * We only want the first 96 bits of the HMAC.
+ */
+ cri[0].cri_alg = CRYPTO_SHA1_HMAC;
+ cri[0].cri_klen = ks->ks_class->ec_keybits;
+ cri[0].cri_mlen = 12;
+ cri[0].cri_key = ks->ks_key;
+ cri[0].cri_next = &cri[1];
+
+ cri[1].cri_alg = CRYPTO_AES_CBC;
+ cri[1].cri_klen = ks->ks_class->ec_keybits;
+ cri[1].cri_mlen = 0;
+ cri[1].cri_key = ks->ks_key;
+ cri[1].cri_next = NULL;
+
+ crypto_newsession(&as->as_session, cri,
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE);
+}
+
+static void
+aes_random_to_key(struct krb5_key_state *ks, const void *in)
+{
+
+ aes_set_key(ks, in);
+}
+
+static int
+aes_crypto_cb(struct cryptop *crp)
+{
+ int error;
+ struct aes_state *as = (struct aes_state *) crp->crp_opaque;
+
+ if (CRYPTO_SESID2CAPS(as->as_session) & CRYPTOCAP_F_SYNC)
+ return (0);
+
+ error = crp->crp_etype;
+ if (error == EAGAIN)
+ error = crypto_dispatch(crp);
+ mtx_lock(&as->as_lock);
+ if (error || (crp->crp_flags & CRYPTO_F_DONE))
+ wakeup(crp);
+ mtx_unlock(&as->as_lock);
+
+ return (0);
+}
+
+static void
+aes_encrypt_1(const struct krb5_key_state *ks, int buftype, void *buf,
+ size_t skip, size_t len, void *ivec, int encdec)
+{
+ struct aes_state *as = ks->ks_priv;
+ struct cryptop *crp;
+ struct cryptodesc *crd;
+ int error;
+
+ crp = crypto_getreq(1);
+ crd = crp->crp_desc;
+
+ crd->crd_skip = skip;
+ crd->crd_len = len;
+ crd->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT | encdec;
+ if (ivec) {
+ bcopy(ivec, crd->crd_iv, 16);
+ } else {
+ bzero(crd->crd_iv, 16);
+ }
+ crd->crd_next = NULL;
+ crd->crd_alg = CRYPTO_AES_CBC;
+
+ crp->crp_sid = as->as_session;
+ crp->crp_flags = buftype | CRYPTO_F_CBIFSYNC;
+ crp->crp_buf = buf;
+ crp->crp_opaque = (void *) as;
+ crp->crp_callback = aes_crypto_cb;
+
+ error = crypto_dispatch(crp);
+
+ if ((CRYPTO_SESID2CAPS(as->as_session) & CRYPTOCAP_F_SYNC) == 0) {
+ mtx_lock(&as->as_lock);
+ if (!error && !(crp->crp_flags & CRYPTO_F_DONE))
+ error = msleep(crp, &as->as_lock, 0, "gssaes", 0);
+ mtx_unlock(&as->as_lock);
+ }
+
+ crypto_freereq(crp);
+}
+
+static void
+aes_encrypt(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, size_t ivlen)
+{
+ size_t blocklen = 16, plen;
+ struct {
+ uint8_t cn_1[16], cn[16];
+ } last2;
+ int i, off;
+
+ /*
+ * AES encryption with cyphertext stealing:
+ *
+ * CTSencrypt(P[0], ..., P[n], IV, K):
+ * len = length(P[n])
+ * (C[0], ..., C[n-2], E[n-1]) =
+ * CBCencrypt(P[0], ..., P[n-1], IV, K)
+ * P = pad(P[n], 0, blocksize)
+ * E[n] = CBCencrypt(P, E[n-1], K);
+ * C[n-1] = E[n]
+ * C[n] = E[n-1]{0..len-1}
+ */
+ plen = len % blocklen;
+ if (len == blocklen) {
+ /*
+ * Note: caller will ensure len >= blocklen.
+ */
+ aes_encrypt_1(ks, CRYPTO_F_IMBUF, inout, skip, len, ivec,
+ CRD_F_ENCRYPT);
+ } else if (plen == 0) {
+ /*
+ * This is equivalent to CBC mode followed by swapping
+ * the last two blocks. We assume that neither of the
+ * last two blocks cross iov boundaries.
+ */
+ aes_encrypt_1(ks, CRYPTO_F_IMBUF, inout, skip, len, ivec,
+ CRD_F_ENCRYPT);
+ off = skip + len - 2 * blocklen;
+ m_copydata(inout, off, 2 * blocklen, (void*) &last2);
+ m_copyback(inout, off, blocklen, last2.cn);
+ m_copyback(inout, off + blocklen, blocklen, last2.cn_1);
+ } else {
+ /*
+ * This is the difficult case. We encrypt all but the
+ * last partial block first. We then create a padded
+ * copy of the last block and encrypt that using the
+ * second to last encrypted block as IV. Once we have
+ * the encrypted versions of the last two blocks, we
+ * reshuffle to create the final result.
+ */
+ aes_encrypt_1(ks, CRYPTO_F_IMBUF, inout, skip, len - plen,
+ ivec, CRD_F_ENCRYPT);
+
+ /*
+ * Copy out the last two blocks, pad the last block
+ * and encrypt it. Rearrange to get the final
+ * result. The cyphertext for cn_1 is in cn. The
+ * cyphertext for cn is the first plen bytes of what
+ * is in cn_1 now.
+ */
+ off = skip + len - blocklen - plen;
+ m_copydata(inout, off, blocklen + plen, (void*) &last2);
+ for (i = plen; i < blocklen; i++)
+ last2.cn[i] = 0;
+ aes_encrypt_1(ks, 0, last2.cn, 0, blocklen, last2.cn_1,
+ CRD_F_ENCRYPT);
+ m_copyback(inout, off, blocklen, last2.cn);
+ m_copyback(inout, off + blocklen, plen, last2.cn_1);
+ }
+}
+
+static void
+aes_decrypt(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, size_t ivlen)
+{
+ size_t blocklen = 16, plen;
+ struct {
+ uint8_t cn_1[16], cn[16];
+ } last2;
+ int i, off, t;
+
+ /*
+ * AES decryption with cyphertext stealing:
+ *
+ * CTSencrypt(C[0], ..., C[n], IV, K):
+ * len = length(C[n])
+ * E[n] = C[n-1]
+ * X = decrypt(E[n], K)
+ * P[n] = (X ^ C[n]){0..len-1}
+ * E[n-1] = {C[n,0],...,C[n,len-1],X[len],...,X[blocksize-1]}
+ * (P[0],...,P[n-1]) = CBCdecrypt(C[0],...,C[n-2],E[n-1], IV, K)
+ */
+ plen = len % blocklen;
+ if (len == blocklen) {
+ /*
+ * Note: caller will ensure len >= blocklen.
+ */
+ aes_encrypt_1(ks, CRYPTO_F_IMBUF, inout, skip, len, ivec, 0);
+ } else if (plen == 0) {
+ /*
+ * This is equivalent to CBC mode followed by swapping
+ * the last two blocks.
+ */
+ off = skip + len - 2 * blocklen;
+ m_copydata(inout, off, 2 * blocklen, (void*) &last2);
+ m_copyback(inout, off, blocklen, last2.cn);
+ m_copyback(inout, off + blocklen, blocklen, last2.cn_1);
+ aes_encrypt_1(ks, CRYPTO_F_IMBUF, inout, skip, len, ivec, 0);
+ } else {
+ /*
+ * This is the difficult case. We first decrypt the
+ * second to last block with a zero IV to make X. The
+ * plaintext for the last block is the XOR of X and
+ * the last cyphertext block.
+ *
+ * We derive a new cypher text for the second to last
+ * block by mixing the unused bytes of X with the last
+ * cyphertext block. The result of that can be
+ * decrypted with the rest in CBC mode.
+ */
+ off = skip + len - plen - blocklen;
+ aes_encrypt_1(ks, CRYPTO_F_IMBUF, inout, off, blocklen,
+ NULL, 0);
+ m_copydata(inout, off, blocklen + plen, (void*) &last2);
+
+ for (i = 0; i < plen; i++) {
+ t = last2.cn[i];
+ last2.cn[i] ^= last2.cn_1[i];
+ last2.cn_1[i] = t;
+ }
+
+ m_copyback(inout, off, blocklen + plen, (void*) &last2);
+ aes_encrypt_1(ks, CRYPTO_F_IMBUF, inout, skip, len - plen,
+ ivec, 0);
+ }
+
+}
+
+static void
+aes_checksum(const struct krb5_key_state *ks, int usage,
+ struct mbuf *inout, size_t skip, size_t inlen, size_t outlen)
+{
+ struct aes_state *as = ks->ks_priv;
+ struct cryptop *crp;
+ struct cryptodesc *crd;
+ int error;
+
+ crp = crypto_getreq(1);
+ crd = crp->crp_desc;
+
+ crd->crd_skip = skip;
+ crd->crd_len = inlen;
+ crd->crd_inject = skip + inlen;
+ crd->crd_flags = 0;
+ crd->crd_next = NULL;
+ crd->crd_alg = CRYPTO_SHA1_HMAC;
+
+ crp->crp_sid = as->as_session;
+ crp->crp_ilen = inlen;
+ crp->crp_olen = 12;
+ crp->crp_etype = 0;
+ crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
+ crp->crp_buf = (void *) inout;
+ crp->crp_opaque = (void *) as;
+ crp->crp_callback = aes_crypto_cb;
+
+ error = crypto_dispatch(crp);
+
+ if ((CRYPTO_SESID2CAPS(as->as_session) & CRYPTOCAP_F_SYNC) == 0) {
+ mtx_lock(&as->as_lock);
+ if (!error && !(crp->crp_flags & CRYPTO_F_DONE))
+ error = msleep(crp, &as->as_lock, 0, "gssaes", 0);
+ mtx_unlock(&as->as_lock);
+ }
+
+ crypto_freereq(crp);
+}
+
+struct krb5_encryption_class krb5_aes128_encryption_class = {
+ "aes128-cts-hmac-sha1-96", /* name */
+ ETYPE_AES128_CTS_HMAC_SHA1_96, /* etype */
+ EC_DERIVED_KEYS, /* flags */
+ 16, /* blocklen */
+ 1, /* msgblocklen */
+ 12, /* checksumlen */
+ 128, /* keybits */
+ 16, /* keylen */
+ aes_init,
+ aes_destroy,
+ aes_set_key,
+ aes_random_to_key,
+ aes_encrypt,
+ aes_decrypt,
+ aes_checksum
+};
+
+struct krb5_encryption_class krb5_aes256_encryption_class = {
+ "aes256-cts-hmac-sha1-96", /* name */
+ ETYPE_AES256_CTS_HMAC_SHA1_96, /* etype */
+ EC_DERIVED_KEYS, /* flags */
+ 16, /* blocklen */
+ 1, /* msgblocklen */
+ 12, /* checksumlen */
+ 256, /* keybits */
+ 32, /* keylen */
+ aes_init,
+ aes_destroy,
+ aes_set_key,
+ aes_random_to_key,
+ aes_encrypt,
+ aes_decrypt,
+ aes_checksum
+};
diff --git a/sys/kgssapi/krb5/kcrypto_arcfour.c b/sys/kgssapi/krb5/kcrypto_arcfour.c
new file mode 100644
index 0000000..d672186
--- /dev/null
+++ b/sys/kgssapi/krb5/kcrypto_arcfour.c
@@ -0,0 +1,220 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/md5.h>
+#include <sys/kobj.h>
+#include <sys/mbuf.h>
+#include <crypto/rc4/rc4.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kcrypto.h"
+
+static void
+arcfour_init(struct krb5_key_state *ks)
+{
+
+ ks->ks_priv = NULL;
+}
+
+static void
+arcfour_destroy(struct krb5_key_state *ks)
+{
+
+}
+
+static void
+arcfour_set_key(struct krb5_key_state *ks, const void *in)
+{
+ void *kp = ks->ks_key;
+
+ if (kp != in)
+ bcopy(in, kp, 16);
+}
+
+static void
+arcfour_random_to_key(struct krb5_key_state *ks, const void *in)
+{
+
+ arcfour_set_key(ks, in);
+}
+
+static void
+arcfour_hmac(uint8_t *key, uint8_t *data, size_t datalen,
+ uint8_t *result)
+{
+ uint8_t buf[64];
+ MD5_CTX md5;
+ int i;
+
+ for (i = 0; i < 16; i++)
+ buf[i] = key[i] ^ 0x36;
+ for (; i < 64; i++)
+ buf[i] = 0x36;
+
+ MD5Init(&md5);
+ MD5Update(&md5, buf, 64);
+ MD5Update(&md5, data, datalen);
+ MD5Final(result, &md5);
+
+ for (i = 0; i < 16; i++)
+ buf[i] = key[i] ^ 0x5c;
+ for (; i < 64; i++)
+ buf[i] = 0x5c;
+
+ MD5Init(&md5);
+ MD5Update(&md5, buf, 64);
+ MD5Update(&md5, result, 16);
+ MD5Final(result, &md5);
+}
+
+static void
+arcfour_derive_key(const struct krb5_key_state *ks, uint32_t usage,
+ uint8_t *newkey)
+{
+ uint8_t t[4];
+
+ t[0] = (usage >> 24);
+ t[1] = (usage >> 16);
+ t[2] = (usage >> 8);
+ t[3] = (usage >> 0);
+ if (ks->ks_class->ec_type == ETYPE_ARCFOUR_HMAC_MD5_56) {
+ uint8_t L40[14] = "fortybits";
+ bcopy(t, L40 + 10, 4);
+ arcfour_hmac(ks->ks_key, L40, 14, newkey);
+ memset(newkey + 7, 0xab, 9);
+ } else {
+ arcfour_hmac(ks->ks_key, t, 4, newkey);
+ }
+}
+
+static int
+rc4_crypt_int(void *rs, void *buf, u_int len)
+{
+
+ rc4_crypt(rs, buf, buf, len);
+ return (0);
+}
+
+static void
+arcfour_encrypt(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, size_t ivlen)
+{
+ struct rc4_state rs;
+ uint8_t newkey[16];
+
+ arcfour_derive_key(ks, 0, newkey);
+
+ /*
+ * If we have an IV, then generate a new key from it using HMAC.
+ */
+ if (ivec) {
+ uint8_t kk[16];
+ arcfour_hmac(newkey, ivec, ivlen, kk);
+ rc4_init(&rs, kk, 16);
+ } else {
+ rc4_init(&rs, newkey, 16);
+ }
+
+ m_apply(inout, skip, len, rc4_crypt_int, &rs);
+}
+
+static int
+MD5Update_int(void *ctx, void *buf, u_int len)
+{
+
+ MD5Update(ctx, buf, len);
+ return (0);
+}
+
+static void
+arcfour_checksum(const struct krb5_key_state *ks, int usage,
+ struct mbuf *inout, size_t skip, size_t inlen, size_t outlen)
+{
+ MD5_CTX md5;
+ uint8_t Ksign[16];
+ uint8_t t[4];
+ uint8_t sgn_cksum[16];
+
+ arcfour_hmac(ks->ks_key, "signaturekey", 13, Ksign);
+
+ t[0] = usage >> 0;
+ t[1] = usage >> 8;
+ t[2] = usage >> 16;
+ t[3] = usage >> 24;
+
+ MD5Init(&md5);
+ MD5Update(&md5, t, 4);
+ m_apply(inout, skip, inlen, MD5Update_int, &md5);
+ MD5Final(sgn_cksum, &md5);
+
+ arcfour_hmac(Ksign, sgn_cksum, 16, sgn_cksum);
+ m_copyback(inout, skip + inlen, outlen, sgn_cksum);
+}
+
+struct krb5_encryption_class krb5_arcfour_encryption_class = {
+ "arcfour-hmac-md5", /* name */
+ ETYPE_ARCFOUR_HMAC_MD5, /* etype */
+ 0, /* flags */
+ 1, /* blocklen */
+ 1, /* msgblocklen */
+ 8, /* checksumlen */
+ 128, /* keybits */
+ 16, /* keylen */
+ arcfour_init,
+ arcfour_destroy,
+ arcfour_set_key,
+ arcfour_random_to_key,
+ arcfour_encrypt,
+ arcfour_encrypt,
+ arcfour_checksum
+};
+
+struct krb5_encryption_class krb5_arcfour_56_encryption_class = {
+ "arcfour-hmac-md5-56", /* name */
+ ETYPE_ARCFOUR_HMAC_MD5_56, /* etype */
+ 0, /* flags */
+ 1, /* blocklen */
+ 1, /* msgblocklen */
+ 8, /* checksumlen */
+ 128, /* keybits */
+ 16, /* keylen */
+ arcfour_init,
+ arcfour_destroy,
+ arcfour_set_key,
+ arcfour_random_to_key,
+ arcfour_encrypt,
+ arcfour_encrypt,
+ arcfour_checksum
+};
diff --git a/sys/kgssapi/krb5/kcrypto_des.c b/sys/kgssapi/krb5/kcrypto_des.c
new file mode 100644
index 0000000..3958897
--- /dev/null
+++ b/sys/kgssapi/krb5/kcrypto_des.c
@@ -0,0 +1,262 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/md5.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <crypto/des/des.h>
+#include <opencrypto/cryptodev.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kcrypto.h"
+
+struct des1_state {
+ struct mtx ds_lock;
+ uint64_t ds_session;
+};
+
+static void
+des1_init(struct krb5_key_state *ks)
+{
+ struct des1_state *ds;
+
+ ds = malloc(sizeof(struct des1_state), M_GSSAPI, M_WAITOK|M_ZERO);
+ mtx_init(&ds->ds_lock, "gss des lock", NULL, MTX_DEF);
+ ks->ks_priv = ds;
+}
+
+static void
+des1_destroy(struct krb5_key_state *ks)
+{
+ struct des1_state *ds = ks->ks_priv;
+
+ if (ds->ds_session)
+ crypto_freesession(ds->ds_session);
+ mtx_destroy(&ds->ds_lock);
+ free(ks->ks_priv, M_GSSAPI);
+
+}
+
+static void
+des1_set_key(struct krb5_key_state *ks, const void *in)
+{
+ void *kp = ks->ks_key;
+ struct des1_state *ds = ks->ks_priv;
+ struct cryptoini cri[1];
+
+ if (kp != in)
+ bcopy(in, kp, ks->ks_class->ec_keylen);
+
+ if (ds->ds_session)
+ crypto_freesession(ds->ds_session);
+
+ bzero(cri, sizeof(cri));
+
+ cri[0].cri_alg = CRYPTO_DES_CBC;
+ cri[0].cri_klen = 64;
+ cri[0].cri_mlen = 0;
+ cri[0].cri_key = ks->ks_key;
+ cri[0].cri_next = NULL;
+
+ crypto_newsession(&ds->ds_session, cri,
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE);
+}
+
+static void
+des1_random_to_key(struct krb5_key_state *ks, const void *in)
+{
+ uint8_t *outkey = ks->ks_key;
+ const uint8_t *inkey = in;
+
+ /*
+ * Expand 56 bits of random data to 64 bits as follows
+ * (in the example, bit number 1 is the MSB of the 56
+ * bits of random data):
+ *
+ * expanded =
+ * 1 2 3 4 5 6 7 p
+ * 9 10 11 12 13 14 15 p
+ * 17 18 19 20 21 22 23 p
+ * 25 26 27 28 29 30 31 p
+ * 33 34 35 36 37 38 39 p
+ * 41 42 43 44 45 46 47 p
+ * 49 50 51 52 53 54 55 p
+ * 56 48 40 32 24 16 8 p
+ */
+ outkey[0] = inkey[0];
+ outkey[1] = inkey[1];
+ outkey[2] = inkey[2];
+ outkey[3] = inkey[3];
+ outkey[4] = inkey[4];
+ outkey[5] = inkey[5];
+ outkey[6] = inkey[6];
+ outkey[7] = (((inkey[0] & 1) << 1)
+ | ((inkey[1] & 1) << 2)
+ | ((inkey[2] & 1) << 3)
+ | ((inkey[3] & 1) << 4)
+ | ((inkey[4] & 1) << 5)
+ | ((inkey[5] & 1) << 6)
+ | ((inkey[6] & 1) << 7));
+ des_set_odd_parity((des_cblock *) outkey);
+ if (des_is_weak_key((des_cblock *) outkey))
+ outkey[7] ^= 0xf0;
+
+ des1_set_key(ks, ks->ks_key);
+}
+
+static int
+des1_crypto_cb(struct cryptop *crp)
+{
+ int error;
+ struct des1_state *ds = (struct des1_state *) crp->crp_opaque;
+
+ if (CRYPTO_SESID2CAPS(ds->ds_session) & CRYPTOCAP_F_SYNC)
+ return (0);
+
+ error = crp->crp_etype;
+ if (error == EAGAIN)
+ error = crypto_dispatch(crp);
+ mtx_lock(&ds->ds_lock);
+ if (error || (crp->crp_flags & CRYPTO_F_DONE))
+ wakeup(crp);
+ mtx_unlock(&ds->ds_lock);
+
+ return (0);
+}
+
+static void
+des1_encrypt_1(const struct krb5_key_state *ks, int buftype, void *buf,
+ size_t skip, size_t len, void *ivec, int encdec)
+{
+ struct des1_state *ds = ks->ks_priv;
+ struct cryptop *crp;
+ struct cryptodesc *crd;
+ int error;
+
+ crp = crypto_getreq(1);
+ crd = crp->crp_desc;
+
+ crd->crd_skip = skip;
+ crd->crd_len = len;
+ crd->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT | encdec;
+ if (ivec) {
+ bcopy(ivec, crd->crd_iv, 8);
+ } else {
+ bzero(crd->crd_iv, 8);
+ }
+ crd->crd_next = NULL;
+ crd->crd_alg = CRYPTO_DES_CBC;
+
+ crp->crp_sid = ds->ds_session;
+ crp->crp_flags = buftype | CRYPTO_F_CBIFSYNC;
+ crp->crp_buf = buf;
+ crp->crp_opaque = (void *) ds;
+ crp->crp_callback = des1_crypto_cb;
+
+ error = crypto_dispatch(crp);
+
+ if ((CRYPTO_SESID2CAPS(ds->ds_session) & CRYPTOCAP_F_SYNC) == 0) {
+ mtx_lock(&ds->ds_lock);
+ if (!error && !(crp->crp_flags & CRYPTO_F_DONE))
+ error = msleep(crp, &ds->ds_lock, 0, "gssdes", 0);
+ mtx_unlock(&ds->ds_lock);
+ }
+
+ crypto_freereq(crp);
+}
+
+static void
+des1_encrypt(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, size_t ivlen)
+{
+
+ des1_encrypt_1(ks, CRYPTO_F_IMBUF, inout, skip, len, ivec,
+ CRD_F_ENCRYPT);
+}
+
+static void
+des1_decrypt(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, size_t ivlen)
+{
+
+ des1_encrypt_1(ks, CRYPTO_F_IMBUF, inout, skip, len, ivec, 0);
+}
+
+static int
+MD5Update_int(void *ctx, void *buf, u_int len)
+{
+
+ MD5Update(ctx, buf, len);
+ return (0);
+}
+
+static void
+des1_checksum(const struct krb5_key_state *ks, int usage,
+ struct mbuf *inout, size_t skip, size_t inlen, size_t outlen)
+{
+ char hash[16];
+ MD5_CTX md5;
+
+ /*
+ * This checksum is specifically for GSS-API. First take the
+ * MD5 checksum of the message, then calculate the CBC mode
+ * checksum of that MD5 checksum using a zero IV.
+ */
+ MD5Init(&md5);
+ m_apply(inout, skip, inlen, MD5Update_int, &md5);
+ MD5Final(hash, &md5);
+
+ des1_encrypt_1(ks, 0, hash, 0, 16, NULL, CRD_F_ENCRYPT);
+ m_copyback(inout, skip + inlen, outlen, hash + 8);
+}
+
+struct krb5_encryption_class krb5_des_encryption_class = {
+ "des-cbc-md5", /* name */
+ ETYPE_DES_CBC_CRC, /* etype */
+ 0, /* flags */
+ 8, /* blocklen */
+ 8, /* msgblocklen */
+ 8, /* checksumlen */
+ 56, /* keybits */
+ 8, /* keylen */
+ des1_init,
+ des1_destroy,
+ des1_set_key,
+ des1_random_to_key,
+ des1_encrypt,
+ des1_decrypt,
+ des1_checksum
+};
diff --git a/sys/kgssapi/krb5/kcrypto_des3.c b/sys/kgssapi/krb5/kcrypto_des3.c
new file mode 100644
index 0000000..ea39c10
--- /dev/null
+++ b/sys/kgssapi/krb5/kcrypto_des3.c
@@ -0,0 +1,402 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/kobj.h>
+#include <sys/mbuf.h>
+#include <crypto/des/des.h>
+#include <opencrypto/cryptodev.h>
+
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kcrypto.h"
+
+#define DES3_FLAGS (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)
+
+struct des3_state {
+ struct mtx ds_lock;
+ uint64_t ds_session;
+};
+
+static void
+des3_init(struct krb5_key_state *ks)
+{
+ struct des3_state *ds;
+
+ ds = malloc(sizeof(struct des3_state), M_GSSAPI, M_WAITOK|M_ZERO);
+ mtx_init(&ds->ds_lock, "gss des3 lock", NULL, MTX_DEF);
+ ks->ks_priv = ds;
+}
+
+static void
+des3_destroy(struct krb5_key_state *ks)
+{
+ struct des3_state *ds = ks->ks_priv;
+
+ if (ds->ds_session)
+ crypto_freesession(ds->ds_session);
+ mtx_destroy(&ds->ds_lock);
+ free(ks->ks_priv, M_GSSAPI);
+}
+
+static void
+des3_set_key(struct krb5_key_state *ks, const void *in)
+{
+ void *kp = ks->ks_key;
+ struct des3_state *ds = ks->ks_priv;
+ struct cryptoini cri[2];
+
+ if (kp != in)
+ bcopy(in, kp, ks->ks_class->ec_keylen);
+
+ if (ds->ds_session)
+ crypto_freesession(ds->ds_session);
+
+ bzero(cri, sizeof(cri));
+
+ cri[0].cri_alg = CRYPTO_SHA1_HMAC;
+ cri[0].cri_klen = 192;
+ cri[0].cri_mlen = 0;
+ cri[0].cri_key = ks->ks_key;
+ cri[0].cri_next = &cri[1];
+
+ cri[1].cri_alg = CRYPTO_3DES_CBC;
+ cri[1].cri_klen = 192;
+ cri[1].cri_mlen = 0;
+ cri[1].cri_key = ks->ks_key;
+ cri[1].cri_next = NULL;
+
+ crypto_newsession(&ds->ds_session, cri,
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE);
+}
+
+static void
+des3_random_to_key(struct krb5_key_state *ks, const void *in)
+{
+ uint8_t *outkey;
+ const uint8_t *inkey;
+ int subkey;
+
+ for (subkey = 0, outkey = ks->ks_key, inkey = in; subkey < 3;
+ subkey++, outkey += 8, inkey += 7) {
+ /*
+ * Expand 56 bits of random data to 64 bits as follows
+ * (in the example, bit number 1 is the MSB of the 56
+ * bits of random data):
+ *
+ * expanded =
+ * 1 2 3 4 5 6 7 p
+ * 9 10 11 12 13 14 15 p
+ * 17 18 19 20 21 22 23 p
+ * 25 26 27 28 29 30 31 p
+ * 33 34 35 36 37 38 39 p
+ * 41 42 43 44 45 46 47 p
+ * 49 50 51 52 53 54 55 p
+ * 56 48 40 32 24 16 8 p
+ */
+ outkey[0] = inkey[0];
+ outkey[1] = inkey[1];
+ outkey[2] = inkey[2];
+ outkey[3] = inkey[3];
+ outkey[4] = inkey[4];
+ outkey[5] = inkey[5];
+ outkey[6] = inkey[6];
+ outkey[7] = (((inkey[0] & 1) << 1)
+ | ((inkey[1] & 1) << 2)
+ | ((inkey[2] & 1) << 3)
+ | ((inkey[3] & 1) << 4)
+ | ((inkey[4] & 1) << 5)
+ | ((inkey[5] & 1) << 6)
+ | ((inkey[6] & 1) << 7));
+ des_set_odd_parity((des_cblock *) outkey);
+ if (des_is_weak_key((des_cblock *) outkey))
+ outkey[7] ^= 0xf0;
+ }
+
+ des3_set_key(ks, ks->ks_key);
+}
+
+static int
+des3_crypto_cb(struct cryptop *crp)
+{
+ int error;
+ struct des3_state *ds = (struct des3_state *) crp->crp_opaque;
+
+ if (CRYPTO_SESID2CAPS(ds->ds_session) & CRYPTOCAP_F_SYNC)
+ return (0);
+
+ error = crp->crp_etype;
+ if (error == EAGAIN)
+ error = crypto_dispatch(crp);
+ mtx_lock(&ds->ds_lock);
+ if (error || (crp->crp_flags & CRYPTO_F_DONE))
+ wakeup(crp);
+ mtx_unlock(&ds->ds_lock);
+
+ return (0);
+}
+
+static void
+des3_encrypt_1(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, int encdec)
+{
+ struct des3_state *ds = ks->ks_priv;
+ struct cryptop *crp;
+ struct cryptodesc *crd;
+ int error;
+
+ crp = crypto_getreq(1);
+ crd = crp->crp_desc;
+
+ crd->crd_skip = skip;
+ crd->crd_len = len;
+ crd->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT | encdec;
+ if (ivec) {
+ bcopy(ivec, crd->crd_iv, 8);
+ } else {
+ bzero(crd->crd_iv, 8);
+ }
+ crd->crd_next = NULL;
+ crd->crd_alg = CRYPTO_3DES_CBC;
+
+ crp->crp_sid = ds->ds_session;
+ crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
+ crp->crp_buf = (void *) inout;
+ crp->crp_opaque = (void *) ds;
+ crp->crp_callback = des3_crypto_cb;
+
+ error = crypto_dispatch(crp);
+
+ if ((CRYPTO_SESID2CAPS(ds->ds_session) & CRYPTOCAP_F_SYNC) == 0) {
+ mtx_lock(&ds->ds_lock);
+ if (!error && !(crp->crp_flags & CRYPTO_F_DONE))
+ error = msleep(crp, &ds->ds_lock, 0, "gssdes3", 0);
+ mtx_unlock(&ds->ds_lock);
+ }
+
+ crypto_freereq(crp);
+}
+
+static void
+des3_encrypt(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, size_t ivlen)
+{
+
+ des3_encrypt_1(ks, inout, skip, len, ivec, CRD_F_ENCRYPT);
+}
+
+static void
+des3_decrypt(const struct krb5_key_state *ks, struct mbuf *inout,
+ size_t skip, size_t len, void *ivec, size_t ivlen)
+{
+
+ des3_encrypt_1(ks, inout, skip, len, ivec, 0);
+}
+
+static void
+des3_checksum(const struct krb5_key_state *ks, int usage,
+ struct mbuf *inout, size_t skip, size_t inlen, size_t outlen)
+{
+ struct des3_state *ds = ks->ks_priv;
+ struct cryptop *crp;
+ struct cryptodesc *crd;
+ int error;
+
+ crp = crypto_getreq(1);
+ crd = crp->crp_desc;
+
+ crd->crd_skip = skip;
+ crd->crd_len = inlen;
+ crd->crd_inject = skip + inlen;
+ crd->crd_flags = 0;
+ crd->crd_next = NULL;
+ crd->crd_alg = CRYPTO_SHA1_HMAC;
+
+ crp->crp_sid = ds->ds_session;
+ crp->crp_ilen = inlen;
+ crp->crp_olen = 20;
+ crp->crp_etype = 0;
+ crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
+ crp->crp_buf = (void *) inout;
+ crp->crp_opaque = (void *) ds;
+ crp->crp_callback = des3_crypto_cb;
+
+ error = crypto_dispatch(crp);
+
+ if ((CRYPTO_SESID2CAPS(ds->ds_session) & CRYPTOCAP_F_SYNC) == 0) {
+ mtx_lock(&ds->ds_lock);
+ if (!error && !(crp->crp_flags & CRYPTO_F_DONE))
+ error = msleep(crp, &ds->ds_lock, 0, "gssdes3", 0);
+ mtx_unlock(&ds->ds_lock);
+ }
+
+ crypto_freereq(crp);
+}
+
+struct krb5_encryption_class krb5_des3_encryption_class = {
+ "des3-cbc-sha1", /* name */
+ ETYPE_DES3_CBC_SHA1, /* etype */
+ EC_DERIVED_KEYS, /* flags */
+ 8, /* blocklen */
+ 8, /* msgblocklen */
+ 20, /* checksumlen */
+ 168, /* keybits */
+ 24, /* keylen */
+ des3_init,
+ des3_destroy,
+ des3_set_key,
+ des3_random_to_key,
+ des3_encrypt,
+ des3_decrypt,
+ des3_checksum
+};
+
+#if 0
+struct des3_dk_test {
+ uint8_t key[24];
+ uint8_t usage[8];
+ size_t usagelen;
+ uint8_t dk[24];
+};
+struct des3_dk_test tests[] = {
+ {{0xdc, 0xe0, 0x6b, 0x1f, 0x64, 0xc8, 0x57, 0xa1, 0x1c, 0x3d, 0xb5,
+ 0x7c, 0x51, 0x89, 0x9b, 0x2c, 0xc1, 0x79, 0x10, 0x08, 0xce, 0x97,
+ 0x3b, 0x92},
+ {0x00, 0x00, 0x00, 0x01, 0x55}, 5,
+ {0x92, 0x51, 0x79, 0xd0, 0x45, 0x91, 0xa7, 0x9b, 0x5d, 0x31, 0x92,
+ 0xc4, 0xa7, 0xe9, 0xc2, 0x89, 0xb0, 0x49, 0xc7, 0x1f, 0x6e, 0xe6,
+ 0x04, 0xcd}},
+
+ {{0x5e, 0x13, 0xd3, 0x1c, 0x70, 0xef, 0x76, 0x57, 0x46, 0x57, 0x85,
+ 0x31, 0xcb, 0x51, 0xc1, 0x5b, 0xf1, 0x1c, 0xa8, 0x2c, 0x97, 0xce,
+ 0xe9, 0xf2},
+ {0x00, 0x00, 0x00, 0x01, 0xaa}, 5,
+ {0x9e, 0x58, 0xe5, 0xa1, 0x46, 0xd9, 0x94, 0x2a, 0x10, 0x1c, 0x46,
+ 0x98, 0x45, 0xd6, 0x7a, 0x20, 0xe3, 0xc4, 0x25, 0x9e, 0xd9, 0x13,
+ 0xf2, 0x07}},
+
+ {{0x98, 0xe6, 0xfd, 0x8a, 0x04, 0xa4, 0xb6, 0x85, 0x9b, 0x75, 0xa1,
+ 0x76, 0x54, 0x0b, 0x97, 0x52, 0xba, 0xd3, 0xec, 0xd6, 0x10, 0xa2,
+ 0x52, 0xbc},
+ {0x00, 0x00, 0x00, 0x01, 0x55}, 5,
+ {0x13, 0xfe, 0xf8, 0x0d, 0x76, 0x3e, 0x94, 0xec, 0x6d, 0x13, 0xfd,
+ 0x2c, 0xa1, 0xd0, 0x85, 0x07, 0x02, 0x49, 0xda, 0xd3, 0x98, 0x08,
+ 0xea, 0xbf}},
+
+ {{0x62, 0x2a, 0xec, 0x25, 0xa2, 0xfe, 0x2c, 0xad, 0x70, 0x94, 0x68,
+ 0x0b, 0x7c, 0x64, 0x94, 0x02, 0x80, 0x08, 0x4c, 0x1a, 0x7c, 0xec,
+ 0x92, 0xb5},
+ {0x00, 0x00, 0x00, 0x01, 0xaa}, 5,
+ {0xf8, 0xdf, 0xbf, 0x04, 0xb0, 0x97, 0xe6, 0xd9, 0xdc, 0x07, 0x02,
+ 0x68, 0x6b, 0xcb, 0x34, 0x89, 0xd9, 0x1f, 0xd9, 0xa4, 0x51, 0x6b,
+ 0x70, 0x3e}},
+
+ {{0xd3, 0xf8, 0x29, 0x8c, 0xcb, 0x16, 0x64, 0x38, 0xdc, 0xb9, 0xb9,
+ 0x3e, 0xe5, 0xa7, 0x62, 0x92, 0x86, 0xa4, 0x91, 0xf8, 0x38, 0xf8,
+ 0x02, 0xfb},
+ {0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73}, 8,
+ {0x23, 0x70, 0xda, 0x57, 0x5d, 0x2a, 0x3d, 0xa8, 0x64, 0xce, 0xbf,
+ 0xdc, 0x52, 0x04, 0xd5, 0x6d, 0xf7, 0x79, 0xa7, 0xdf, 0x43, 0xd9,
+ 0xda, 0x43}},
+
+ {{0xc1, 0x08, 0x16, 0x49, 0xad, 0xa7, 0x43, 0x62, 0xe6, 0xa1, 0x45,
+ 0x9d, 0x01, 0xdf, 0xd3, 0x0d, 0x67, 0xc2, 0x23, 0x4c, 0x94, 0x07,
+ 0x04, 0xda},
+ {0x00, 0x00, 0x00, 0x01, 0x55}, 5,
+ {0x34, 0x80, 0x57, 0xec, 0x98, 0xfd, 0xc4, 0x80, 0x16, 0x16, 0x1c,
+ 0x2a, 0x4c, 0x7a, 0x94, 0x3e, 0x92, 0xae, 0x49, 0x2c, 0x98, 0x91,
+ 0x75, 0xf7}},
+
+ {{0x5d, 0x15, 0x4a, 0xf2, 0x38, 0xf4, 0x67, 0x13, 0x15, 0x57, 0x19,
+ 0xd5, 0x5e, 0x2f, 0x1f, 0x79, 0x0d, 0xd6, 0x61, 0xf2, 0x79, 0xa7,
+ 0x91, 0x7c},
+ {0x00, 0x00, 0x00, 0x01, 0xaa}, 5,
+ {0xa8, 0x80, 0x8a, 0xc2, 0x67, 0xda, 0xda, 0x3d, 0xcb, 0xe9, 0xa7,
+ 0xc8, 0x46, 0x26, 0xfb, 0xc7, 0x61, 0xc2, 0x94, 0xb0, 0x13, 0x15,
+ 0xe5, 0xc1}},
+
+ {{0x79, 0x85, 0x62, 0xe0, 0x49, 0x85, 0x2f, 0x57, 0xdc, 0x8c, 0x34,
+ 0x3b, 0xa1, 0x7f, 0x2c, 0xa1, 0xd9, 0x73, 0x94, 0xef, 0xc8, 0xad,
+ 0xc4, 0x43},
+ {0x00, 0x00, 0x00, 0x01, 0x55}, 5,
+ {0xc8, 0x13, 0xf8, 0x8a, 0x3b, 0xe3, 0xb3, 0x34, 0xf7, 0x54, 0x25,
+ 0xce, 0x91, 0x75, 0xfb, 0xe3, 0xc8, 0x49, 0x3b, 0x89, 0xc8, 0x70,
+ 0x3b, 0x49}},
+
+ {{0x26, 0xdc, 0xe3, 0x34, 0xb5, 0x45, 0x29, 0x2f, 0x2f, 0xea, 0xb9,
+ 0xa8, 0x70, 0x1a, 0x89, 0xa4, 0xb9, 0x9e, 0xb9, 0x94, 0x2c, 0xec,
+ 0xd0, 0x16},
+ {0x00, 0x00, 0x00, 0x01, 0xaa}, 5,
+ {0xf4, 0x8f, 0xfd, 0x6e, 0x83, 0xf8, 0x3e, 0x73, 0x54, 0xe6, 0x94,
+ 0xfd, 0x25, 0x2c, 0xf8, 0x3b, 0xfe, 0x58, 0xf7, 0xd5, 0xba, 0x37,
+ 0xec, 0x5d}},
+};
+#define N_TESTS (sizeof(tests) / sizeof(tests[0]))
+
+int
+main(int argc, char **argv)
+{
+ struct krb5_key_state *key, *dk;
+ uint8_t *dkp;
+ int j, i;
+
+ for (j = 0; j < N_TESTS; j++) {
+ struct des3_dk_test *t = &tests[j];
+ key = krb5_create_key(&des3_encryption_class);
+ krb5_set_key(key, t->key);
+ dk = krb5_derive_key(key, t->usage, t->usagelen);
+ krb5_free_key(key);
+ if (memcmp(dk->ks_key, t->dk, 24)) {
+ printf("DES3 dk(");
+ for (i = 0; i < 24; i++)
+ printf("%02x", t->key[i]);
+ printf(", ");
+ for (i = 0; i < t->usagelen; i++)
+ printf("%02x", t->usage[i]);
+ printf(") failed\n");
+ printf("should be: ");
+ for (i = 0; i < 24; i++)
+ printf("%02x", t->dk[i]);
+ printf("\n result was: ");
+ dkp = dk->ks_key;
+ for (i = 0; i < 24; i++)
+ printf("%02x", dkp[i]);
+ printf("\n");
+ }
+ krb5_free_key(dk);
+ }
+
+ return (0);
+}
+#endif
diff --git a/sys/kgssapi/krb5/krb5_mech.c b/sys/kgssapi/krb5/krb5_mech.c
new file mode 100644
index 0000000..2a1c0b6
--- /dev/null
+++ b/sys/kgssapi/krb5/krb5_mech.c
@@ -0,0 +1,2100 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <kgssapi/gssapi.h>
+#include <kgssapi/gssapi_impl.h>
+
+#include "kgss_if.h"
+#include "kcrypto.h"
+
+#define GSS_TOKEN_SENT_BY_ACCEPTOR 1
+#define GSS_TOKEN_SEALED 2
+#define GSS_TOKEN_ACCEPTOR_SUBKEY 4
+
+static gss_OID_desc krb5_mech_oid =
+{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
+
+struct krb5_data {
+ size_t kd_length;
+ void *kd_data;
+};
+
+struct krb5_keyblock {
+ uint16_t kk_type; /* encryption type */
+ struct krb5_data kk_key; /* key data */
+};
+
+struct krb5_address {
+ uint16_t ka_type;
+ struct krb5_data ka_addr;
+};
+
+/*
+ * The km_elem array is ordered so that the highest received sequence
+ * number is listed first.
+ */
+struct krb5_msg_order {
+ uint32_t km_flags;
+ uint32_t km_start;
+ uint32_t km_length;
+ uint32_t km_jitter_window;
+ uint32_t km_first_seq;
+ uint32_t *km_elem;
+};
+
+struct krb5_context {
+ struct _gss_ctx_id_t kc_common;
+ struct mtx kc_lock;
+ uint32_t kc_ac_flags;
+ uint32_t kc_ctx_flags;
+ uint32_t kc_more_flags;
+#define LOCAL 1
+#define OPEN 2
+#define COMPAT_OLD_DES3 4
+#define COMPAT_OLD_DES3_SELECTED 8
+#define ACCEPTOR_SUBKEY 16
+ struct krb5_address kc_local_address;
+ struct krb5_address kc_remote_address;
+ uint16_t kc_local_port;
+ uint16_t kc_remote_port;
+ struct krb5_keyblock kc_keyblock;
+ struct krb5_keyblock kc_local_subkey;
+ struct krb5_keyblock kc_remote_subkey;
+ volatile uint32_t kc_local_seqnumber;
+ uint32_t kc_remote_seqnumber;
+ uint32_t kc_keytype;
+ uint32_t kc_cksumtype;
+ struct krb5_data kc_source_name;
+ struct krb5_data kc_target_name;
+ uint32_t kc_lifetime;
+ struct krb5_msg_order kc_msg_order;
+ struct krb5_key_state *kc_tokenkey;
+ struct krb5_key_state *kc_encryptkey;
+ struct krb5_key_state *kc_checksumkey;
+
+ struct krb5_key_state *kc_send_seal_Ke;
+ struct krb5_key_state *kc_send_seal_Ki;
+ struct krb5_key_state *kc_send_seal_Kc;
+ struct krb5_key_state *kc_send_sign_Kc;
+
+ struct krb5_key_state *kc_recv_seal_Ke;
+ struct krb5_key_state *kc_recv_seal_Ki;
+ struct krb5_key_state *kc_recv_seal_Kc;
+ struct krb5_key_state *kc_recv_sign_Kc;
+};
+
+static uint16_t
+get_uint16(const uint8_t **pp, size_t *lenp)
+{
+ const uint8_t *p = *pp;
+ uint16_t v;
+
+ if (*lenp < 2)
+ return (0);
+
+ v = (p[0] << 8) | p[1];
+ *pp = p + 2;
+ *lenp = *lenp - 2;
+
+ return (v);
+}
+
+static uint32_t
+get_uint32(const uint8_t **pp, size_t *lenp)
+{
+ const uint8_t *p = *pp;
+ uint32_t v;
+
+ if (*lenp < 4)
+ return (0);
+
+ v = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ *pp = p + 4;
+ *lenp = *lenp - 4;
+
+ return (v);
+}
+
+static void
+get_data(const uint8_t **pp, size_t *lenp, struct krb5_data *dp)
+{
+ size_t sz = get_uint32(pp, lenp);
+
+ dp->kd_length = sz;
+ dp->kd_data = malloc(sz, M_GSSAPI, M_WAITOK);
+
+ if (*lenp < sz)
+ sz = *lenp;
+ bcopy(*pp, dp->kd_data, sz);
+ (*pp) += sz;
+ (*lenp) -= sz;
+}
+
+static void
+delete_data(struct krb5_data *dp)
+{
+ if (dp->kd_data) {
+ free(dp->kd_data, M_GSSAPI);
+ dp->kd_length = 0;
+ dp->kd_data = NULL;
+ }
+}
+
+static void
+get_address(const uint8_t **pp, size_t *lenp, struct krb5_address *ka)
+{
+
+ ka->ka_type = get_uint16(pp, lenp);
+ get_data(pp, lenp, &ka->ka_addr);
+}
+
+static void
+delete_address(struct krb5_address *ka)
+{
+ delete_data(&ka->ka_addr);
+}
+
+static void
+get_keyblock(const uint8_t **pp, size_t *lenp, struct krb5_keyblock *kk)
+{
+
+ kk->kk_type = get_uint16(pp, lenp);
+ get_data(pp, lenp, &kk->kk_key);
+}
+
+static void
+delete_keyblock(struct krb5_keyblock *kk)
+{
+ if (kk->kk_key.kd_data)
+ bzero(kk->kk_key.kd_data, kk->kk_key.kd_length);
+ delete_data(&kk->kk_key);
+}
+
+static void
+copy_key(struct krb5_keyblock *from, struct krb5_keyblock **to)
+{
+
+ if (from->kk_key.kd_length)
+ *to = from;
+ else
+ *to = NULL;
+}
+
+/*
+ * Return non-zero if we are initiator.
+ */
+static __inline int
+is_initiator(struct krb5_context *kc)
+{
+ return (kc->kc_more_flags & LOCAL);
+}
+
+/*
+ * Return non-zero if we are acceptor.
+ */
+static __inline int
+is_acceptor(struct krb5_context *kc)
+{
+ return !(kc->kc_more_flags & LOCAL);
+}
+
+static void
+get_initiator_subkey(struct krb5_context *kc, struct krb5_keyblock **kdp)
+{
+
+ if (is_initiator(kc))
+ copy_key(&kc->kc_local_subkey, kdp);
+ else
+ copy_key(&kc->kc_remote_subkey, kdp);
+ if (!*kdp)
+ copy_key(&kc->kc_keyblock, kdp);
+}
+
+static void
+get_acceptor_subkey(struct krb5_context *kc, struct krb5_keyblock **kdp)
+{
+
+ if (is_initiator(kc))
+ copy_key(&kc->kc_remote_subkey, kdp);
+ else
+ copy_key(&kc->kc_local_subkey, kdp);
+}
+
+static OM_uint32
+get_keys(struct krb5_context *kc)
+{
+ struct krb5_keyblock *keydata;
+ struct krb5_encryption_class *ec;
+ struct krb5_key_state *key;
+ int etype;
+
+ keydata = NULL;
+ get_acceptor_subkey(kc, &keydata);
+ if (!keydata)
+ if ((kc->kc_more_flags & ACCEPTOR_SUBKEY) == 0)
+ get_initiator_subkey(kc, &keydata);
+ if (!keydata)
+ return (GSS_S_FAILURE);
+
+ /*
+ * GSS-API treats all DES etypes the same and all DES3 etypes
+ * the same.
+ */
+ switch (keydata->kk_type) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ etype = ETYPE_DES_CBC_CRC;
+ break;
+
+ case ETYPE_DES3_CBC_MD5:
+ case ETYPE_DES3_CBC_SHA1:
+ case ETYPE_OLD_DES3_CBC_SHA1:
+ etype = ETYPE_DES3_CBC_SHA1;
+
+ default:
+ etype = keydata->kk_type;
+ }
+
+ ec = krb5_find_encryption_class(etype);
+ if (!ec)
+ return (GSS_S_FAILURE);
+
+ key = krb5_create_key(ec);
+ krb5_set_key(key, keydata->kk_key.kd_data);
+ kc->kc_tokenkey = key;
+
+ switch (etype) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56: {
+ /*
+ * Single DES and ARCFOUR uses a 'derived' key (XOR
+ * with 0xf0) for encrypting wrap tokens. The original
+ * key is used for checksums and sequence numbers.
+ */
+ struct krb5_key_state *ekey;
+ uint8_t *ekp, *kp;
+ int i;
+
+ ekey = krb5_create_key(ec);
+ ekp = ekey->ks_key;
+ kp = key->ks_key;
+ for (i = 0; i < ec->ec_keylen; i++)
+ ekp[i] = kp[i] ^ 0xf0;
+ krb5_set_key(ekey, ekp);
+ kc->kc_encryptkey = ekey;
+ refcount_acquire(&key->ks_refs);
+ kc->kc_checksumkey = key;
+ break;
+ }
+
+ case ETYPE_DES3_CBC_SHA1:
+ /*
+ * Triple DES uses a RFC 3961 style derived key with
+ * usage number KG_USAGE_SIGN for checksums. The
+ * original key is used for encryption and sequence
+ * numbers.
+ */
+ kc->kc_checksumkey = krb5_get_checksum_key(key, KG_USAGE_SIGN);
+ refcount_acquire(&key->ks_refs);
+ kc->kc_encryptkey = key;
+ break;
+
+ default:
+ /*
+ * We need eight derived keys four for sending and
+ * four for receiving.
+ */
+ if (is_initiator(kc)) {
+ /*
+ * We are initiator.
+ */
+ kc->kc_send_seal_Ke = krb5_get_encryption_key(key,
+ KG_USAGE_INITIATOR_SEAL);
+ kc->kc_send_seal_Ki = krb5_get_integrity_key(key,
+ KG_USAGE_INITIATOR_SEAL);
+ kc->kc_send_seal_Kc = krb5_get_checksum_key(key,
+ KG_USAGE_INITIATOR_SEAL);
+ kc->kc_send_sign_Kc = krb5_get_checksum_key(key,
+ KG_USAGE_INITIATOR_SIGN);
+
+ kc->kc_recv_seal_Ke = krb5_get_encryption_key(key,
+ KG_USAGE_ACCEPTOR_SEAL);
+ kc->kc_recv_seal_Ki = krb5_get_integrity_key(key,
+ KG_USAGE_ACCEPTOR_SEAL);
+ kc->kc_recv_seal_Kc = krb5_get_checksum_key(key,
+ KG_USAGE_ACCEPTOR_SEAL);
+ kc->kc_recv_sign_Kc = krb5_get_checksum_key(key,
+ KG_USAGE_ACCEPTOR_SIGN);
+ } else {
+ /*
+ * We are acceptor.
+ */
+ kc->kc_send_seal_Ke = krb5_get_encryption_key(key,
+ KG_USAGE_ACCEPTOR_SEAL);
+ kc->kc_send_seal_Ki = krb5_get_integrity_key(key,
+ KG_USAGE_ACCEPTOR_SEAL);
+ kc->kc_send_seal_Kc = krb5_get_checksum_key(key,
+ KG_USAGE_ACCEPTOR_SEAL);
+ kc->kc_send_sign_Kc = krb5_get_checksum_key(key,
+ KG_USAGE_ACCEPTOR_SIGN);
+
+ kc->kc_recv_seal_Ke = krb5_get_encryption_key(key,
+ KG_USAGE_INITIATOR_SEAL);
+ kc->kc_recv_seal_Ki = krb5_get_integrity_key(key,
+ KG_USAGE_INITIATOR_SEAL);
+ kc->kc_recv_seal_Kc = krb5_get_checksum_key(key,
+ KG_USAGE_INITIATOR_SEAL);
+ kc->kc_recv_sign_Kc = krb5_get_checksum_key(key,
+ KG_USAGE_INITIATOR_SIGN);
+ }
+ break;
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+static void
+krb5_init(struct krb5_context *kc)
+{
+
+ mtx_init(&kc->kc_lock, "krb5 gss lock", NULL, MTX_DEF);
+}
+
+static OM_uint32
+krb5_import(struct krb5_context *kc,
+ enum sec_context_format format,
+ const gss_buffer_t context_token)
+{
+ OM_uint32 res;
+ const uint8_t *p = (const uint8_t *) context_token->value;
+ size_t len = context_token->length;
+ uint32_t flags;
+ int i;
+
+ /*
+ * We support heimdal 0.6 and heimdal 1.1
+ */
+ if (format != KGSS_HEIMDAL_0_6 && format != KGSS_HEIMDAL_1_1)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+#define SC_LOCAL_ADDRESS 1
+#define SC_REMOTE_ADDRESS 2
+#define SC_KEYBLOCK 4
+#define SC_LOCAL_SUBKEY 8
+#define SC_REMOTE_SUBKEY 16
+
+ /*
+ * Ensure that the token starts with krb5 oid.
+ */
+ if (p[0] != 0x00 || p[1] != krb5_mech_oid.length
+ || len < krb5_mech_oid.length + 2
+ || bcmp(krb5_mech_oid.elements, p + 2,
+ krb5_mech_oid.length))
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += krb5_mech_oid.length + 2;
+ len -= krb5_mech_oid.length + 2;
+
+ flags = get_uint32(&p, &len);
+ kc->kc_ac_flags = get_uint32(&p, &len);
+ if (flags & SC_LOCAL_ADDRESS)
+ get_address(&p, &len, &kc->kc_local_address);
+ if (flags & SC_REMOTE_ADDRESS)
+ get_address(&p, &len, &kc->kc_remote_address);
+ kc->kc_local_port = get_uint16(&p, &len);
+ kc->kc_remote_port = get_uint16(&p, &len);
+ if (flags & SC_KEYBLOCK)
+ get_keyblock(&p, &len, &kc->kc_keyblock);
+ if (flags & SC_LOCAL_SUBKEY)
+ get_keyblock(&p, &len, &kc->kc_local_subkey);
+ if (flags & SC_REMOTE_SUBKEY)
+ get_keyblock(&p, &len, &kc->kc_remote_subkey);
+ kc->kc_local_seqnumber = get_uint32(&p, &len);
+ kc->kc_remote_seqnumber = get_uint32(&p, &len);
+ kc->kc_keytype = get_uint32(&p, &len);
+ kc->kc_cksumtype = get_uint32(&p, &len);
+ get_data(&p, &len, &kc->kc_source_name);
+ get_data(&p, &len, &kc->kc_target_name);
+ kc->kc_ctx_flags = get_uint32(&p, &len);
+ kc->kc_more_flags = get_uint32(&p, &len);
+ kc->kc_lifetime = get_uint32(&p, &len);
+ /*
+ * Heimdal 1.1 adds the message order stuff.
+ */
+ if (format == KGSS_HEIMDAL_1_1) {
+ kc->kc_msg_order.km_flags = get_uint32(&p, &len);
+ kc->kc_msg_order.km_start = get_uint32(&p, &len);
+ kc->kc_msg_order.km_length = get_uint32(&p, &len);
+ kc->kc_msg_order.km_jitter_window = get_uint32(&p, &len);
+ kc->kc_msg_order.km_first_seq = get_uint32(&p, &len);
+ kc->kc_msg_order.km_elem =
+ malloc(kc->kc_msg_order.km_jitter_window * sizeof(uint32_t),
+ M_GSSAPI, M_WAITOK);
+ for (i = 0; i < kc->kc_msg_order.km_jitter_window; i++)
+ kc->kc_msg_order.km_elem[i] = get_uint32(&p, &len);
+ } else {
+ kc->kc_msg_order.km_flags = 0;
+ }
+
+ res = get_keys(kc);
+ if (GSS_ERROR(res))
+ return (res);
+
+ /*
+ * We don't need these anymore.
+ */
+ delete_keyblock(&kc->kc_keyblock);
+ delete_keyblock(&kc->kc_local_subkey);
+ delete_keyblock(&kc->kc_remote_subkey);
+
+ return (GSS_S_COMPLETE);
+}
+
+static void
+krb5_delete(struct krb5_context *kc, gss_buffer_t output_token)
+{
+
+ delete_address(&kc->kc_local_address);
+ delete_address(&kc->kc_remote_address);
+ delete_keyblock(&kc->kc_keyblock);
+ delete_keyblock(&kc->kc_local_subkey);
+ delete_keyblock(&kc->kc_remote_subkey);
+ delete_data(&kc->kc_source_name);
+ delete_data(&kc->kc_target_name);
+ if (kc->kc_msg_order.km_elem)
+ free(kc->kc_msg_order.km_elem, M_GSSAPI);
+ if (output_token) {
+ output_token->length = 0;
+ output_token->value = NULL;
+ }
+ if (kc->kc_tokenkey) {
+ krb5_free_key(kc->kc_tokenkey);
+ if (kc->kc_encryptkey) {
+ krb5_free_key(kc->kc_encryptkey);
+ krb5_free_key(kc->kc_checksumkey);
+ } else {
+ krb5_free_key(kc->kc_send_seal_Ke);
+ krb5_free_key(kc->kc_send_seal_Ki);
+ krb5_free_key(kc->kc_send_seal_Kc);
+ krb5_free_key(kc->kc_send_sign_Kc);
+ krb5_free_key(kc->kc_recv_seal_Ke);
+ krb5_free_key(kc->kc_recv_seal_Ki);
+ krb5_free_key(kc->kc_recv_seal_Kc);
+ krb5_free_key(kc->kc_recv_sign_Kc);
+ }
+ }
+ mtx_destroy(&kc->kc_lock);
+}
+
+static gss_OID
+krb5_mech_type(struct krb5_context *kc)
+{
+
+ return (&krb5_mech_oid);
+}
+
+/*
+ * Make a token with the given type and length (the length includes
+ * the TOK_ID), initialising the token header appropriately. Return a
+ * pointer to the TOK_ID of the token. A new mbuf is allocated with
+ * the framing header plus hlen bytes of space.
+ *
+ * Format is as follows:
+ *
+ * 0x60 [APPLICATION 0] SEQUENCE
+ * DER encoded length length of oid + type + inner token length
+ * 0x06 NN <oid data> OID of mechanism type
+ * TT TT TOK_ID
+ * <inner token> data for inner token
+ *
+ * 1: der encoded length
+ */
+static void *
+krb5_make_token(char tok_id[2], size_t hlen, size_t len, struct mbuf **mp)
+{
+ size_t inside_len, len_len, tlen;
+ gss_OID oid = &krb5_mech_oid;
+ struct mbuf *m;
+ uint8_t *p;
+
+ inside_len = 2 + oid->length + len;
+ if (inside_len < 128)
+ len_len = 1;
+ else if (inside_len < 0x100)
+ len_len = 2;
+ else if (inside_len < 0x10000)
+ len_len = 3;
+ else if (inside_len < 0x1000000)
+ len_len = 4;
+ else
+ len_len = 5;
+
+ tlen = 1 + len_len + 2 + oid->length + hlen;
+ KASSERT(tlen <= MLEN, ("token head too large"));
+ MGET(m, M_WAITOK, MT_DATA);
+ M_ALIGN(m, tlen);
+ m->m_len = tlen;
+
+ p = (uint8_t *) m->m_data;
+ *p++ = 0x60;
+ switch (len_len) {
+ case 1:
+ *p++ = inside_len;
+ break;
+ case 2:
+ *p++ = 0x81;
+ *p++ = inside_len;
+ break;
+ case 3:
+ *p++ = 0x82;
+ *p++ = inside_len >> 8;
+ *p++ = inside_len;
+ break;
+ case 4:
+ *p++ = 0x83;
+ *p++ = inside_len >> 16;
+ *p++ = inside_len >> 8;
+ *p++ = inside_len;
+ break;
+ case 5:
+ *p++ = 0x84;
+ *p++ = inside_len >> 24;
+ *p++ = inside_len >> 16;
+ *p++ = inside_len >> 8;
+ *p++ = inside_len;
+ break;
+ }
+
+ *p++ = 0x06;
+ *p++ = oid->length;
+ bcopy(oid->elements, p, oid->length);
+ p += oid->length;
+
+ p[0] = tok_id[0];
+ p[1] = tok_id[1];
+
+ *mp = m;
+
+ return (p);
+}
+
+/*
+ * Verify a token, checking the inner token length and mechanism oid.
+ * pointer to the first byte of the TOK_ID. The length of the
+ * encapsulated data is checked to be at least len bytes; the actual
+ * length of the encapsulated data (including TOK_ID) is returned in
+ * *encap_len.
+ *
+ * If can_pullup is TRUE and the token header is fragmented, we will
+ * rearrange it.
+ *
+ * Format is as follows:
+ *
+ * 0x60 [APPLICATION 0] SEQUENCE
+ * DER encoded length length of oid + type + inner token length
+ * 0x06 NN <oid data> OID of mechanism type
+ * TT TT TOK_ID
+ * <inner token> data for inner token
+ *
+ * 1: der encoded length
+ */
+static void *
+krb5_verify_token(char tok_id[2], size_t len, struct mbuf **mp,
+ size_t *encap_len, bool_t can_pullup)
+{
+ struct mbuf *m;
+ size_t tlen, hlen, len_len, inside_len;
+ gss_OID oid = &krb5_mech_oid;
+ uint8_t *p;
+
+ m = *mp;
+ tlen = m_length(m, NULL);
+ if (tlen < 2)
+ return (NULL);
+
+ /*
+ * Ensure that at least the framing part of the token is
+ * contigous.
+ */
+ if (m->m_len < 2) {
+ if (can_pullup)
+ *mp = m = m_pullup(m, 2);
+ else
+ return (NULL);
+ }
+
+ p = m->m_data;
+
+ if (*p++ != 0x60)
+ return (NULL);
+
+ if (*p < 0x80) {
+ inside_len = *p++;
+ len_len = 1;
+ } else {
+ /*
+ * Ensure there is enough space for the DER encoded length.
+ */
+ len_len = (*p & 0x7f) + 1;
+ if (tlen < len_len + 1)
+ return (NULL);
+ if (m->m_len < len_len + 1) {
+ if (can_pullup)
+ *mp = m = m_pullup(m, len_len + 1);
+ else
+ return (NULL);
+ p = m->m_data + 1;
+ }
+
+ switch (*p++) {
+ case 0x81:
+ inside_len = *p++;
+ break;
+
+ case 0x82:
+ inside_len = (p[0] << 8) | p[1];
+ p += 2;
+ break;
+
+ case 0x83:
+ inside_len = (p[0] << 16) | (p[1] << 8) | p[2];
+ p += 3;
+ break;
+
+ case 0x84:
+ inside_len = (p[0] << 24) | (p[1] << 16)
+ | (p[2] << 8) | p[3];
+ p += 4;
+ break;
+
+ default:
+ return (NULL);
+ }
+ }
+
+ if (tlen != inside_len + len_len + 1)
+ return (NULL);
+ if (inside_len < 2 + oid->length + len)
+ return (NULL);
+
+ /*
+ * Now that we know the value of len_len, we can pullup the
+ * whole header. The header is 1 + len_len + 2 + oid->length +
+ * len bytes.
+ */
+ hlen = 1 + len_len + 2 + oid->length + len;
+ if (m->m_len < hlen) {
+ if (can_pullup)
+ *mp = m = m_pullup(m, hlen);
+ else
+ return (NULL);
+ p = m->m_data + 1 + len_len;
+ }
+
+ if (*p++ != 0x06)
+ return (NULL);
+ if (*p++ != oid->length)
+ return (NULL);
+ if (bcmp(oid->elements, p, oid->length))
+ return (NULL);
+ p += oid->length;
+
+ if (p[0] != tok_id[0])
+ return (NULL);
+
+ if (p[1] != tok_id[1])
+ return (NULL);
+
+ *encap_len = inside_len - 2 - oid->length;
+
+ return (p);
+}
+
+static void
+krb5_insert_seq(struct krb5_msg_order *mo, uint32_t seq, int index)
+{
+ int i;
+
+ if (mo->km_length < mo->km_jitter_window)
+ mo->km_length++;
+
+ for (i = mo->km_length - 1; i > index; i--)
+ mo->km_elem[i] = mo->km_elem[i - 1];
+ mo->km_elem[index] = seq;
+}
+
+/*
+ * Check sequence numbers according to RFC 2743 section 1.2.3.
+ */
+static OM_uint32
+krb5_sequence_check(struct krb5_context *kc, uint32_t seq)
+{
+ OM_uint32 res = GSS_S_FAILURE;
+ struct krb5_msg_order *mo = &kc->kc_msg_order;
+ int check_sequence = mo->km_flags & GSS_C_SEQUENCE_FLAG;
+ int check_replay = mo->km_flags & GSS_C_REPLAY_FLAG;
+ int i;
+
+ mtx_lock(&kc->kc_lock);
+
+ /*
+ * Message is in-sequence with no gap.
+ */
+ if (mo->km_length == 0 || seq == mo->km_elem[0] + 1) {
+ /*
+ * This message is received in-sequence with no gaps.
+ */
+ krb5_insert_seq(mo, seq, 0);
+ res = GSS_S_COMPLETE;
+ goto out;
+ }
+
+ if (seq > mo->km_elem[0]) {
+ /*
+ * This message is received in-sequence with a gap.
+ */
+ krb5_insert_seq(mo, seq, 0);
+ if (check_sequence)
+ res = GSS_S_GAP_TOKEN;
+ else
+ res = GSS_S_COMPLETE;
+ goto out;
+ }
+
+ if (seq < mo->km_elem[mo->km_length - 1]) {
+ if (check_replay && !check_sequence)
+ res = GSS_S_OLD_TOKEN;
+ else
+ res = GSS_S_UNSEQ_TOKEN;
+ goto out;
+ }
+
+ for (i = 0; i < mo->km_length; i++) {
+ if (mo->km_elem[i] == seq) {
+ res = GSS_S_DUPLICATE_TOKEN;
+ goto out;
+ }
+ if (mo->km_elem[i] < seq) {
+ /*
+ * We need to insert this seq here,
+ */
+ krb5_insert_seq(mo, seq, i);
+ if (check_replay && !check_sequence)
+ res = GSS_S_COMPLETE;
+ else
+ res = GSS_S_UNSEQ_TOKEN;
+ goto out;
+ }
+ }
+
+out:
+ mtx_unlock(&kc->kc_lock);
+
+ return (res);
+}
+
+static uint8_t sgn_alg_des_md5[] = { 0x00, 0x00 };
+static uint8_t seal_alg_des[] = { 0x00, 0x00 };
+static uint8_t sgn_alg_des3_sha1[] = { 0x04, 0x00 };
+static uint8_t seal_alg_des3[] = { 0x02, 0x00 };
+static uint8_t seal_alg_rc4[] = { 0x10, 0x00 };
+static uint8_t sgn_alg_hmac_md5[] = { 0x11, 0x00 };
+
+/*
+ * Return the size of the inner token given the use of the key's
+ * encryption class. For wrap tokens, the length of the padded
+ * plaintext will be added to this.
+ */
+static size_t
+token_length(struct krb5_key_state *key)
+{
+
+ return (16 + key->ks_class->ec_checksumlen);
+}
+
+static OM_uint32
+krb5_get_mic_old(struct krb5_context *kc, struct mbuf *m,
+ struct mbuf **micp, uint8_t sgn_alg[2])
+{
+ struct mbuf *mlast, *mic, *tm;
+ uint8_t *p, dir;
+ size_t tlen, mlen, cklen;
+ uint32_t seq;
+ char buf[8];
+
+ mlen = m_length(m, &mlast);
+
+ tlen = token_length(kc->kc_tokenkey);
+ p = krb5_make_token("\x01\x01", tlen, tlen, &mic);
+ p += 2; /* TOK_ID */
+ *p++ = sgn_alg[0]; /* SGN_ALG */
+ *p++ = sgn_alg[1];
+
+ *p++ = 0xff; /* filler */
+ *p++ = 0xff;
+ *p++ = 0xff;
+ *p++ = 0xff;
+
+ /*
+ * SGN_CKSUM:
+ *
+ * Calculate the keyed checksum of the token header plus the
+ * message.
+ */
+ cklen = kc->kc_checksumkey->ks_class->ec_checksumlen;
+
+ mic->m_len = p - (uint8_t *) mic->m_data;
+ mic->m_next = m;
+ MGET(tm, M_WAITOK, MT_DATA);
+ tm->m_len = cklen;
+ mlast->m_next = tm;
+
+ krb5_checksum(kc->kc_checksumkey, 15, mic, mic->m_len - 8,
+ 8 + mlen, cklen);
+ bcopy(tm->m_data, p + 8, cklen);
+ mic->m_next = NULL;
+ mlast->m_next = NULL;
+ m_free(tm);
+
+ /*
+ * SND_SEQ:
+ *
+ * Take the four bytes of the sequence number least
+ * significant first followed by four bytes of direction
+ * marker (zero for initiator and 0xff for acceptor). Encrypt
+ * that data using the SGN_CKSUM as IV. Note: ARC4 wants the
+ * sequence number big-endian.
+ */
+ seq = atomic_fetchadd_32(&kc->kc_local_seqnumber, 1);
+ if (sgn_alg[0] == 0x11) {
+ p[0] = (seq >> 24);
+ p[1] = (seq >> 16);
+ p[2] = (seq >> 8);
+ p[3] = (seq >> 0);
+ } else {
+ p[0] = (seq >> 0);
+ p[1] = (seq >> 8);
+ p[2] = (seq >> 16);
+ p[3] = (seq >> 24);
+ }
+ if (is_initiator(kc)) {
+ dir = 0;
+ } else {
+ dir = 0xff;
+ }
+ p[4] = dir;
+ p[5] = dir;
+ p[6] = dir;
+ p[7] = dir;
+ bcopy(p + 8, buf, 8);
+
+ /*
+ * Set the mic buffer to its final size so that the encrypt
+ * can see the SND_SEQ part.
+ */
+ mic->m_len += 8 + cklen;
+ krb5_encrypt(kc->kc_tokenkey, mic, mic->m_len - cklen - 8, 8, buf, 8);
+
+ *micp = mic;
+ return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+krb5_get_mic_new(struct krb5_context *kc, struct mbuf *m,
+ struct mbuf **micp)
+{
+ struct krb5_key_state *key = kc->kc_send_sign_Kc;
+ struct mbuf *mlast, *mic;
+ uint8_t *p;
+ int flags;
+ size_t mlen, cklen;
+ uint32_t seq;
+
+ mlen = m_length(m, &mlast);
+ cklen = key->ks_class->ec_checksumlen;
+
+ KASSERT(16 + cklen <= MLEN, ("checksum too large for an mbuf"));
+ MGET(mic, M_WAITOK, MT_DATA);
+ M_ALIGN(mic, 16 + cklen);
+ mic->m_len = 16 + cklen;
+ p = mic->m_data;
+
+ /* TOK_ID */
+ p[0] = 0x04;
+ p[1] = 0x04;
+
+ /* Flags */
+ flags = 0;
+ if (is_acceptor(kc))
+ flags |= GSS_TOKEN_SENT_BY_ACCEPTOR;
+ if (kc->kc_more_flags & ACCEPTOR_SUBKEY)
+ flags |= GSS_TOKEN_ACCEPTOR_SUBKEY;
+ p[2] = flags;
+
+ /* Filler */
+ p[3] = 0xff;
+ p[4] = 0xff;
+ p[5] = 0xff;
+ p[6] = 0xff;
+ p[7] = 0xff;
+
+ /* SND_SEQ */
+ p[8] = 0;
+ p[9] = 0;
+ p[10] = 0;
+ p[11] = 0;
+ seq = atomic_fetchadd_32(&kc->kc_local_seqnumber, 1);
+ p[12] = (seq >> 24);
+ p[13] = (seq >> 16);
+ p[14] = (seq >> 8);
+ p[15] = (seq >> 0);
+
+ /*
+ * SGN_CKSUM:
+ *
+ * Calculate the keyed checksum of the message plus the first
+ * 16 bytes of the token header.
+ */
+ mlast->m_next = mic;
+ krb5_checksum(key, 0, m, 0, mlen + 16, cklen);
+ mlast->m_next = NULL;
+
+ *micp = mic;
+ return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+krb5_get_mic(struct krb5_context *kc, OM_uint32 *minor_status,
+ gss_qop_t qop_req, struct mbuf *m, struct mbuf **micp)
+{
+
+ *minor_status = 0;
+
+ if (qop_req != GSS_C_QOP_DEFAULT)
+ return (GSS_S_BAD_QOP);
+
+ if (time_uptime > kc->kc_lifetime)
+ return (GSS_S_CONTEXT_EXPIRED);
+
+ switch (kc->kc_tokenkey->ks_class->ec_type) {
+ case ETYPE_DES_CBC_CRC:
+ return (krb5_get_mic_old(kc, m, micp, sgn_alg_des_md5));
+
+ case ETYPE_DES3_CBC_SHA1:
+ return (krb5_get_mic_old(kc, m, micp, sgn_alg_des3_sha1));
+
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56:
+ return (krb5_get_mic_old(kc, m, micp, sgn_alg_hmac_md5));
+
+ default:
+ return (krb5_get_mic_new(kc, m, micp));
+ }
+
+ return (GSS_S_FAILURE);
+}
+
+static OM_uint32
+krb5_verify_mic_old(struct krb5_context *kc, struct mbuf *m, struct mbuf *mic,
+ uint8_t sgn_alg[2])
+{
+ struct mbuf *mlast, *tm;
+ uint8_t *p, *tp, dir;
+ size_t mlen, tlen, elen, miclen;
+ size_t cklen;
+ uint32_t seq;
+
+ mlen = m_length(m, &mlast);
+
+ tlen = token_length(kc->kc_tokenkey);
+ p = krb5_verify_token("\x01\x01", tlen, &mic, &elen, FALSE);
+ if (!p)
+ return (GSS_S_DEFECTIVE_TOKEN);
+#if 0
+ /*
+ * Disable this check - heimdal-1.1 generates DES3 MIC tokens
+ * that are 2 bytes too big.
+ */
+ if (elen != tlen)
+ return (GSS_S_DEFECTIVE_TOKEN);
+#endif
+ /* TOK_ID */
+ p += 2;
+
+ /* SGN_ALG */
+ if (p[0] != sgn_alg[0] || p[1] != sgn_alg[1])
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += 2;
+
+ if (p[0] != 0xff || p[1] != 0xff || p[2] != 0xff || p[3] != 0xff)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += 4;
+
+ /*
+ * SGN_CKSUM:
+ *
+ * Calculate the keyed checksum of the token header plus the
+ * message.
+ */
+ cklen = kc->kc_checksumkey->ks_class->ec_checksumlen;
+ miclen = mic->m_len;
+ mic->m_len = p - (uint8_t *) mic->m_data;
+ mic->m_next = m;
+ MGET(tm, M_WAITOK, MT_DATA);
+ tm->m_len = cklen;
+ mlast->m_next = tm;
+
+ krb5_checksum(kc->kc_checksumkey, 15, mic, mic->m_len - 8,
+ 8 + mlen, cklen);
+ mic->m_next = NULL;
+ mlast->m_next = NULL;
+ if (bcmp(tm->m_data, p + 8, cklen)) {
+ m_free(tm);
+ return (GSS_S_BAD_SIG);
+ }
+
+ /*
+ * SND_SEQ:
+ *
+ * Take the four bytes of the sequence number least
+ * significant first followed by four bytes of direction
+ * marker (zero for initiator and 0xff for acceptor). Encrypt
+ * that data using the SGN_CKSUM as IV. Note: ARC4 wants the
+ * sequence number big-endian.
+ */
+ bcopy(p, tm->m_data, 8);
+ tm->m_len = 8;
+ krb5_decrypt(kc->kc_tokenkey, tm, 0, 8, p + 8, 8);
+
+ tp = tm->m_data;
+ if (sgn_alg[0] == 0x11) {
+ seq = tp[3] | (tp[2] << 8) | (tp[1] << 16) | (tp[0] << 24);
+ } else {
+ seq = tp[0] | (tp[1] << 8) | (tp[2] << 16) | (tp[3] << 24);
+ }
+
+ if (is_initiator(kc)) {
+ dir = 0xff;
+ } else {
+ dir = 0;
+ }
+ if (tp[4] != dir || tp[5] != dir || tp[6] != dir || tp[7] != dir) {
+ m_free(tm);
+ return (GSS_S_DEFECTIVE_TOKEN);
+ }
+ m_free(tm);
+
+ if (kc->kc_msg_order.km_flags &
+ (GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG)) {
+ return (krb5_sequence_check(kc, seq));
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+krb5_verify_mic_new(struct krb5_context *kc, struct mbuf *m, struct mbuf *mic)
+{
+ OM_uint32 res;
+ struct krb5_key_state *key = kc->kc_recv_sign_Kc;
+ struct mbuf *mlast;
+ uint8_t *p;
+ int flags;
+ size_t mlen, cklen;
+ char buf[32];
+
+ mlen = m_length(m, &mlast);
+ cklen = key->ks_class->ec_checksumlen;
+
+ KASSERT(mic->m_next == NULL, ("MIC should be contiguous"));
+ if (mic->m_len != 16 + cklen)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p = mic->m_data;
+
+ /* TOK_ID */
+ if (p[0] != 0x04)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if (p[1] != 0x04)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ /* Flags */
+ flags = 0;
+ if (is_initiator(kc))
+ flags |= GSS_TOKEN_SENT_BY_ACCEPTOR;
+ if (kc->kc_more_flags & ACCEPTOR_SUBKEY)
+ flags |= GSS_TOKEN_ACCEPTOR_SUBKEY;
+ if (p[2] != flags)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ /* Filler */
+ if (p[3] != 0xff)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if (p[4] != 0xff)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if (p[5] != 0xff)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if (p[6] != 0xff)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if (p[7] != 0xff)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ /* SND_SEQ */
+ if (kc->kc_msg_order.km_flags &
+ (GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG)) {
+ uint32_t seq;
+ if (p[8] || p[9] || p[10] || p[11]) {
+ res = GSS_S_UNSEQ_TOKEN;
+ } else {
+ seq = (p[12] << 24) | (p[13] << 16)
+ | (p[14] << 8) | p[15];
+ res = krb5_sequence_check(kc, seq);
+ }
+ if (GSS_ERROR(res))
+ return (res);
+ } else {
+ res = GSS_S_COMPLETE;
+ }
+
+ /*
+ * SGN_CKSUM:
+ *
+ * Calculate the keyed checksum of the message plus the first
+ * 16 bytes of the token header.
+ */
+ m_copydata(mic, 16, cklen, buf);
+ mlast->m_next = mic;
+ krb5_checksum(key, 0, m, 0, mlen + 16, cklen);
+ mlast->m_next = NULL;
+ if (bcmp(buf, p + 16, cklen)) {
+ return (GSS_S_BAD_SIG);
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+krb5_verify_mic(struct krb5_context *kc, OM_uint32 *minor_status,
+ struct mbuf *m, struct mbuf *mic, gss_qop_t *qop_state)
+{
+
+ *minor_status = 0;
+ if (qop_state)
+ *qop_state = GSS_C_QOP_DEFAULT;
+
+ if (time_uptime > kc->kc_lifetime)
+ return (GSS_S_CONTEXT_EXPIRED);
+
+ switch (kc->kc_tokenkey->ks_class->ec_type) {
+ case ETYPE_DES_CBC_CRC:
+ return (krb5_verify_mic_old(kc, m, mic, sgn_alg_des_md5));
+
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56:
+ return (krb5_verify_mic_old(kc, m, mic, sgn_alg_hmac_md5));
+
+ case ETYPE_DES3_CBC_SHA1:
+ return (krb5_verify_mic_old(kc, m, mic, sgn_alg_des3_sha1));
+
+ default:
+ return (krb5_verify_mic_new(kc, m, mic));
+ }
+
+ return (GSS_S_FAILURE);
+}
+
+static OM_uint32
+krb5_wrap_old(struct krb5_context *kc, int conf_req_flag,
+ struct mbuf **mp, int *conf_state,
+ uint8_t sgn_alg[2], uint8_t seal_alg[2])
+{
+ struct mbuf *m, *mlast, *tm, *cm, *pm;
+ size_t mlen, tlen, padlen, datalen;
+ uint8_t *p, dir;
+ size_t cklen;
+ uint8_t buf[8];
+ uint32_t seq;
+
+ /*
+ * How many trailing pad bytes do we need?
+ */
+ m = *mp;
+ mlen = m_length(m, &mlast);
+ tlen = kc->kc_tokenkey->ks_class->ec_msgblocklen;
+ padlen = tlen - (mlen % tlen);
+
+ /*
+ * The data part of the token has eight bytes of random
+ * confounder prepended and followed by up to eight bytes of
+ * padding bytes each of which is set to the number of padding
+ * bytes.
+ */
+ datalen = mlen + 8 + padlen;
+ tlen = token_length(kc->kc_tokenkey);
+
+ p = krb5_make_token("\x02\x01", tlen, datalen + tlen, &tm);
+ p += 2; /* TOK_ID */
+ *p++ = sgn_alg[0]; /* SGN_ALG */
+ *p++ = sgn_alg[1];
+ if (conf_req_flag) {
+ *p++ = seal_alg[0]; /* SEAL_ALG */
+ *p++ = seal_alg[1];
+ } else {
+ *p++ = 0xff; /* SEAL_ALG = none */
+ *p++ = 0xff;
+ }
+
+ *p++ = 0xff; /* filler */
+ *p++ = 0xff;
+
+ /*
+ * Copy the padded message data.
+ */
+ if (M_LEADINGSPACE(m) >= 8) {
+ m->m_data -= 8;
+ m->m_len += 8;
+ } else {
+ MGET(cm, M_WAITOK, MT_DATA);
+ cm->m_len = 8;
+ cm->m_next = m;
+ m = cm;
+ }
+ arc4rand(m->m_data, 8, 0);
+ if (M_TRAILINGSPACE(mlast) >= padlen) {
+ memset(mlast->m_data + mlast->m_len, padlen, padlen);
+ mlast->m_len += padlen;
+ } else {
+ MGET(pm, M_WAITOK, MT_DATA);
+ memset(pm->m_data, padlen, padlen);
+ pm->m_len = padlen;
+ mlast->m_next = pm;
+ mlast = pm;
+ }
+ tm->m_next = m;
+
+ /*
+ * SGN_CKSUM:
+ *
+ * Calculate the keyed checksum of the token header plus the
+ * padded message. Fiddle with tm->m_len so that we only
+ * checksum the 8 bytes of head that we care about.
+ */
+ cklen = kc->kc_checksumkey->ks_class->ec_checksumlen;
+ tlen = tm->m_len;
+ tm->m_len = p - (uint8_t *) tm->m_data;
+ MGET(cm, M_WAITOK, MT_DATA);
+ cm->m_len = cklen;
+ mlast->m_next = cm;
+ krb5_checksum(kc->kc_checksumkey, 13, tm, tm->m_len - 8,
+ datalen + 8, cklen);
+ tm->m_len = tlen;
+ mlast->m_next = NULL;
+ bcopy(cm->m_data, p + 8, cklen);
+ m_free(cm);
+
+ /*
+ * SND_SEQ:
+ *
+ * Take the four bytes of the sequence number least
+ * significant first (most signficant first for ARCFOUR)
+ * followed by four bytes of direction marker (zero for
+ * initiator and 0xff for acceptor). Encrypt that data using
+ * the SGN_CKSUM as IV.
+ */
+ seq = atomic_fetchadd_32(&kc->kc_local_seqnumber, 1);
+ if (sgn_alg[0] == 0x11) {
+ p[0] = (seq >> 24);
+ p[1] = (seq >> 16);
+ p[2] = (seq >> 8);
+ p[3] = (seq >> 0);
+ } else {
+ p[0] = (seq >> 0);
+ p[1] = (seq >> 8);
+ p[2] = (seq >> 16);
+ p[3] = (seq >> 24);
+ }
+ if (is_initiator(kc)) {
+ dir = 0;
+ } else {
+ dir = 0xff;
+ }
+ p[4] = dir;
+ p[5] = dir;
+ p[6] = dir;
+ p[7] = dir;
+ krb5_encrypt(kc->kc_tokenkey, tm, p - (uint8_t *) tm->m_data,
+ 8, p + 8, 8);
+
+ if (conf_req_flag) {
+ /*
+ * Encrypt the padded message with an IV of zero for
+ * DES and DES3, or an IV of the sequence number in
+ * big-endian format for ARCFOUR.
+ */
+ if (seal_alg[0] == 0x10) {
+ buf[0] = (seq >> 24);
+ buf[1] = (seq >> 16);
+ buf[2] = (seq >> 8);
+ buf[3] = (seq >> 0);
+ krb5_encrypt(kc->kc_encryptkey, m, 0, datalen,
+ buf, 4);
+ } else {
+ krb5_encrypt(kc->kc_encryptkey, m, 0, datalen,
+ NULL, 0);
+ }
+ }
+
+ if (conf_state)
+ *conf_state = conf_req_flag;
+
+ *mp = tm;
+ return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+krb5_wrap_new(struct krb5_context *kc, int conf_req_flag,
+ struct mbuf **mp, int *conf_state)
+{
+ struct krb5_key_state *Ke = kc->kc_send_seal_Ke;
+ struct krb5_key_state *Ki = kc->kc_send_seal_Ki;
+ struct krb5_key_state *Kc = kc->kc_send_seal_Kc;
+ const struct krb5_encryption_class *ec = Ke->ks_class;
+ struct mbuf *m, *mlast, *tm;
+ uint8_t *p;
+ int flags, EC;
+ size_t mlen, blen, mblen, cklen, ctlen;
+ uint32_t seq;
+ static char zpad[32];
+
+ m = *mp;
+ mlen = m_length(m, &mlast);
+
+ blen = ec->ec_blocklen;
+ mblen = ec->ec_msgblocklen;
+ cklen = ec->ec_checksumlen;
+
+ if (conf_req_flag) {
+ /*
+ * For sealed messages, we need space for 16 bytes of
+ * header, blen confounder, plaintext, padding, copy
+ * of header and checksum.
+ *
+ * We pad to mblen (which may be different from
+ * blen). If the encryption class is using CTS, mblen
+ * will be one (i.e. no padding required).
+ */
+ if (mblen > 1)
+ EC = mlen % mblen;
+ else
+ EC = 0;
+ ctlen = blen + mlen + EC + 16;
+
+ /*
+ * Put initial header and confounder before the
+ * message.
+ */
+ M_PREPEND(m, 16 + blen, M_WAITOK);
+
+ /*
+ * Append padding + copy of header and checksum. Try
+ * to fit this into the end of the original message,
+ * otherwise allocate a trailer.
+ */
+ if (M_TRAILINGSPACE(mlast) >= EC + 16 + cklen) {
+ tm = NULL;
+ mlast->m_len += EC + 16 + cklen;
+ } else {
+ MGET(tm, M_WAITOK, MT_DATA);
+ tm->m_len = EC + 16 + cklen;
+ mlast->m_next = tm;
+ }
+ } else {
+ /*
+ * For unsealed messages, we need 16 bytes of header
+ * plus space for the plaintext and a checksum. EC is
+ * set to the checksum size. We leave space in tm for
+ * a copy of the header - this will be trimmed later.
+ */
+ M_PREPEND(m, 16, M_WAITOK);
+
+ MGET(tm, M_WAITOK, MT_DATA);
+ tm->m_len = cklen + 16;
+ mlast->m_next = tm;
+ ctlen = 0;
+ EC = cklen;
+ }
+
+ p = m->m_data;
+
+ /* TOK_ID */
+ p[0] = 0x05;
+ p[1] = 0x04;
+
+ /* Flags */
+ flags = 0;
+ if (conf_req_flag)
+ flags = GSS_TOKEN_SEALED;
+ if (is_acceptor(kc))
+ flags |= GSS_TOKEN_SENT_BY_ACCEPTOR;
+ if (kc->kc_more_flags & ACCEPTOR_SUBKEY)
+ flags |= GSS_TOKEN_ACCEPTOR_SUBKEY;
+ p[2] = flags;
+
+ /* Filler */
+ p[3] = 0xff;
+
+ /* EC + RRC - set to zero initially */
+ p[4] = 0;
+ p[5] = 0;
+ p[6] = 0;
+ p[7] = 0;
+
+ /* SND_SEQ */
+ p[8] = 0;
+ p[9] = 0;
+ p[10] = 0;
+ p[11] = 0;
+ seq = atomic_fetchadd_32(&kc->kc_local_seqnumber, 1);
+ p[12] = (seq >> 24);
+ p[13] = (seq >> 16);
+ p[14] = (seq >> 8);
+ p[15] = (seq >> 0);
+
+ if (conf_req_flag) {
+ /*
+ * Encrypt according to RFC 4121 section 4.2 and RFC
+ * 3961 section 5.3. Note: we don't generate tokens
+ * with RRC values other than zero. If we did, we
+ * should zero RRC in the copied header.
+ */
+ arc4rand(p + 16, blen, 0);
+ if (EC) {
+ m_copyback(m, 16 + blen + mlen, EC, zpad);
+ }
+ m_copyback(m, 16 + blen + mlen + EC, 16, p);
+
+ krb5_checksum(Ki, 0, m, 16, ctlen, cklen);
+ krb5_encrypt(Ke, m, 16, ctlen, NULL, 0);
+ } else {
+ /*
+ * The plaintext message is followed by a checksum of
+ * the plaintext plus a version of the header where EC
+ * and RRC are set to zero. Also, the original EC must
+ * be our checksum size.
+ */
+ bcopy(p, tm->m_data, 16);
+ krb5_checksum(Kc, 0, m, 16, mlen + 16, cklen);
+ tm->m_data += 16;
+ tm->m_len -= 16;
+ }
+
+ /*
+ * Finally set EC to its actual value
+ */
+ p[4] = EC >> 8;
+ p[5] = EC;
+
+ *mp = m;
+ return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+krb5_wrap(struct krb5_context *kc, OM_uint32 *minor_status,
+ int conf_req_flag, gss_qop_t qop_req,
+ struct mbuf **mp, int *conf_state)
+{
+
+ *minor_status = 0;
+ if (conf_state)
+ *conf_state = 0;
+
+ if (qop_req != GSS_C_QOP_DEFAULT)
+ return (GSS_S_BAD_QOP);
+
+ if (time_uptime > kc->kc_lifetime)
+ return (GSS_S_CONTEXT_EXPIRED);
+
+ switch (kc->kc_tokenkey->ks_class->ec_type) {
+ case ETYPE_DES_CBC_CRC:
+ return (krb5_wrap_old(kc, conf_req_flag,
+ mp, conf_state, sgn_alg_des_md5, seal_alg_des));
+
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56:
+ return (krb5_wrap_old(kc, conf_req_flag,
+ mp, conf_state, sgn_alg_hmac_md5, seal_alg_rc4));
+
+ case ETYPE_DES3_CBC_SHA1:
+ return (krb5_wrap_old(kc, conf_req_flag,
+ mp, conf_state, sgn_alg_des3_sha1, seal_alg_des3));
+
+ default:
+ return (krb5_wrap_new(kc, conf_req_flag, mp, conf_state));
+ }
+
+ return (GSS_S_FAILURE);
+}
+
+static void
+m_trim(struct mbuf *m, int len)
+{
+ struct mbuf *n;
+ int off;
+
+ n = m_getptr(m, len, &off);
+ if (n) {
+ n->m_len = off;
+ if (n->m_next) {
+ m_freem(n->m_next);
+ n->m_next = NULL;
+ }
+ }
+}
+
+static OM_uint32
+krb5_unwrap_old(struct krb5_context *kc, struct mbuf **mp, int *conf_state,
+ uint8_t sgn_alg[2], uint8_t seal_alg[2])
+{
+ OM_uint32 res;
+ struct mbuf *m, *mlast, *hm, *cm;
+ uint8_t *p, dir;
+ size_t mlen, tlen, elen, datalen, padlen;
+ size_t cklen;
+ uint8_t buf[32];
+ uint32_t seq;
+ int i, conf;
+
+ m = *mp;
+ mlen = m_length(m, &mlast);
+
+ tlen = token_length(kc->kc_tokenkey);
+ cklen = kc->kc_tokenkey->ks_class->ec_checksumlen;
+
+ p = krb5_verify_token("\x02\x01", tlen, &m, &elen, TRUE);
+ *mp = m;
+ if (!p)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ datalen = elen - tlen;
+
+ /*
+ * Trim the framing header first to make life a little easier
+ * later.
+ */
+ m_adj(m, p - (uint8_t *) m->m_data);
+
+ /* TOK_ID */
+ p += 2;
+
+ /* SGN_ALG */
+ if (p[0] != sgn_alg[0] || p[1] != sgn_alg[1])
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += 2;
+
+ /* SEAL_ALG */
+ if (p[0] == seal_alg[0] && p[1] == seal_alg[1])
+ conf = 1;
+ else if (p[0] == 0xff && p[1] == 0xff)
+ conf = 0;
+ else
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += 2;
+
+ if (p[0] != 0xff || p[1] != 0xff)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += 2;
+
+ /*
+ * SND_SEQ:
+ *
+ * Take the four bytes of the sequence number least
+ * significant first (most significant for ARCFOUR) followed
+ * by four bytes of direction marker (zero for initiator and
+ * 0xff for acceptor). Encrypt that data using the SGN_CKSUM
+ * as IV.
+ */
+ krb5_decrypt(kc->kc_tokenkey, m, 8, 8, p + 8, 8);
+ if (sgn_alg[0] == 0x11) {
+ seq = p[3] | (p[2] << 8) | (p[1] << 16) | (p[0] << 24);
+ } else {
+ seq = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+ }
+
+ if (is_initiator(kc)) {
+ dir = 0xff;
+ } else {
+ dir = 0;
+ }
+ if (p[4] != dir || p[5] != dir || p[6] != dir || p[7] != dir)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ if (kc->kc_msg_order.km_flags &
+ (GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG)) {
+ res = krb5_sequence_check(kc, seq);
+ if (GSS_ERROR(res))
+ return (res);
+ } else {
+ res = GSS_S_COMPLETE;
+ }
+
+ /*
+ * If the token was encrypted, decode it in-place.
+ */
+ if (conf) {
+ /*
+ * Decrypt the padded message with an IV of zero for
+ * DES and DES3 or an IV of the big-endian encoded
+ * sequence number for ARCFOUR.
+ */
+ if (seal_alg[0] == 0x10) {
+ krb5_decrypt(kc->kc_encryptkey, m, 16 + cklen,
+ datalen, p, 4);
+ } else {
+ krb5_decrypt(kc->kc_encryptkey, m, 16 + cklen,
+ datalen, NULL, 0);
+ }
+ }
+ if (conf_state)
+ *conf_state = conf;
+
+ /*
+ * Check the trailing pad bytes.
+ */
+ KASSERT(mlast->m_len > 0, ("Unexpected empty mbuf"));
+ padlen = mlast->m_data[mlast->m_len - 1];
+ m_copydata(m, tlen + datalen - padlen, padlen, buf);
+ for (i = 0; i < padlen; i++) {
+ if (buf[i] != padlen) {
+ return (GSS_S_DEFECTIVE_TOKEN);
+ }
+ }
+
+ /*
+ * SGN_CKSUM:
+ *
+ * Calculate the keyed checksum of the token header plus the
+ * padded message. We do a little mbuf surgery to trim out the
+ * parts we don't want to checksum.
+ */
+ hm = m;
+ *mp = m = m_split(m, 16 + cklen, M_WAITOK);
+ mlast = m_last(m);
+ hm->m_len = 8;
+ hm->m_next = m;
+ MGET(cm, M_WAITOK, MT_DATA);
+ cm->m_len = cklen;
+ mlast->m_next = cm;
+
+ krb5_checksum(kc->kc_checksumkey, 13, hm, 0, datalen + 8, cklen);
+ hm->m_next = NULL;
+ mlast->m_next = NULL;
+
+ if (bcmp(cm->m_data, hm->m_data + 16, cklen)) {
+ m_freem(hm);
+ m_free(cm);
+ return (GSS_S_BAD_SIG);
+ }
+ m_freem(hm);
+ m_free(cm);
+
+ /*
+ * Trim off the confounder and padding.
+ */
+ m_adj(m, 8);
+ if (mlast->m_len >= padlen) {
+ mlast->m_len -= padlen;
+ } else {
+ m_trim(m, datalen - 8 - padlen);
+ }
+
+ *mp = m;
+ return (res);
+}
+
+static OM_uint32
+krb5_unwrap_new(struct krb5_context *kc, struct mbuf **mp, int *conf_state)
+{
+ OM_uint32 res;
+ struct krb5_key_state *Ke = kc->kc_recv_seal_Ke;
+ struct krb5_key_state *Ki = kc->kc_recv_seal_Ki;
+ struct krb5_key_state *Kc = kc->kc_recv_seal_Kc;
+ const struct krb5_encryption_class *ec = Ke->ks_class;
+ struct mbuf *m, *mlast, *hm, *cm;
+ uint8_t *p, *pp;
+ int sealed, flags, EC, RRC;
+ size_t blen, cklen, ctlen, mlen, plen, tlen;
+ char buf[32], buf2[32];
+
+ m = *mp;
+ mlen = m_length(m, &mlast);
+
+ if (mlen <= 16)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if (m->m_len < 16) {
+ m = m_pullup(m, 16);
+ *mp = m;
+ }
+ p = m->m_data;
+
+ /* TOK_ID */
+ if (p[0] != 0x05)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if (p[1] != 0x04)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ /* Flags */
+ sealed = p[2] & GSS_TOKEN_SEALED;
+ flags = sealed;
+ if (is_initiator(kc))
+ flags |= GSS_TOKEN_SENT_BY_ACCEPTOR;
+ if (kc->kc_more_flags & ACCEPTOR_SUBKEY)
+ flags |= GSS_TOKEN_ACCEPTOR_SUBKEY;
+ if (p[2] != flags)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ /* Filler */
+ if (p[3] != 0xff)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ /* EC + RRC */
+ EC = (p[4] << 8) + p[5];
+ RRC = (p[6] << 8) + p[7];
+
+ /* SND_SEQ */
+ if (kc->kc_msg_order.km_flags &
+ (GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG)) {
+ uint32_t seq;
+ if (p[8] || p[9] || p[10] || p[11]) {
+ res = GSS_S_UNSEQ_TOKEN;
+ } else {
+ seq = (p[12] << 24) | (p[13] << 16)
+ | (p[14] << 8) | p[15];
+ res = krb5_sequence_check(kc, seq);
+ }
+ if (GSS_ERROR(res))
+ return (res);
+ } else {
+ res = GSS_S_COMPLETE;
+ }
+
+ /*
+ * Separate the header before dealing with RRC. We only need
+ * to keep the header if the message isn't encrypted.
+ */
+ if (sealed) {
+ hm = NULL;
+ m_adj(m, 16);
+ } else {
+ hm = m;
+ *mp = m = m_split(m, 16, M_WAITOK);
+ mlast = m_last(m);
+ }
+
+ /*
+ * Undo the effects of RRC by rotating left.
+ */
+ if (RRC > 0) {
+ struct mbuf *rm;
+ size_t rlen;
+
+ rlen = mlen - 16;
+ if (RRC <= sizeof(buf) && m->m_len >= rlen) {
+ /*
+ * Simple case, just rearrange the bytes in m.
+ */
+ bcopy(m->m_data, buf, RRC);
+ bcopy(m->m_data + RRC, m->m_data, rlen - RRC);
+ bcopy(buf, m->m_data + rlen - RRC, RRC);
+ } else {
+ /*
+ * More complicated - rearrange the mbuf
+ * chain.
+ */
+ rm = m;
+ *mp = m = m_split(m, RRC, M_WAITOK);
+ m_cat(m, rm);
+ mlast = rm;
+ }
+ }
+
+ blen = ec->ec_blocklen;
+ cklen = ec->ec_checksumlen;
+ if (sealed) {
+ /*
+ * Decrypt according to RFC 4121 section 4.2 and RFC
+ * 3961 section 5.3. The message must be large enough
+ * for a blocksize confounder, at least one block of
+ * cyphertext and a checksum.
+ */
+ if (mlen < 16 + 2*blen + cklen)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ ctlen = mlen - 16 - cklen;
+ krb5_decrypt(Ke, m, 0, ctlen, NULL, 0);
+
+ /*
+ * The size of the plaintext is ctlen minus blocklen
+ * (for the confounder), 16 (for the copy of the token
+ * header) and EC (for the filler). The actual
+ * plaintext starts after the confounder.
+ */
+ plen = ctlen - blen - 16 - EC;
+ pp = p + 16 + blen;
+
+ /*
+ * Checksum the padded plaintext.
+ */
+ m_copydata(m, ctlen, cklen, buf);
+ krb5_checksum(Ki, 0, m, 0, ctlen, cklen);
+ m_copydata(m, ctlen, cklen, buf2);
+
+ if (bcmp(buf, buf2, cklen))
+ return (GSS_S_BAD_SIG);
+
+ /*
+ * Trim the message back to just plaintext.
+ */
+ m_adj(m, blen);
+ tlen = 16 + EC + cklen;
+ if (mlast->m_len >= tlen) {
+ mlast->m_len -= tlen;
+ } else {
+ m_trim(m, plen);
+ }
+ } else {
+ /*
+ * The plaintext message is followed by a checksum of
+ * the plaintext plus a version of the header where EC
+ * and RRC are set to zero. Also, the original EC must
+ * be our checksum size.
+ */
+ if (mlen < 16 + cklen || EC != cklen)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ /*
+ * The size of the plaintext is simply the message
+ * size less header and checksum. The plaintext starts
+ * right after the header (which we have saved in hm).
+ */
+ plen = mlen - 16 - cklen;
+
+ /*
+ * Insert a copy of the header (with EC and RRC set to
+ * zero) between the plaintext message and the
+ * checksum.
+ */
+ p = hm->m_data;
+ p[4] = p[5] = p[6] = p[7] = 0;
+
+ cm = m_split(m, plen, M_WAITOK);
+ mlast = m_last(m);
+ m->m_next = hm;
+ hm->m_next = cm;
+
+ bcopy(cm->m_data, buf, cklen);
+ krb5_checksum(Kc, 0, m, 0, plen + 16, cklen);
+ if (bcmp(cm->m_data, buf, cklen))
+ return (GSS_S_BAD_SIG);
+
+ /*
+ * The checksum matches, discard all buf the plaintext.
+ */
+ mlast->m_next = NULL;
+ m_freem(hm);
+ }
+
+ if (conf_state)
+ *conf_state = (sealed != 0);
+
+ return (res);
+}
+
+static OM_uint32
+krb5_unwrap(struct krb5_context *kc, OM_uint32 *minor_status,
+ struct mbuf **mp, int *conf_state, gss_qop_t *qop_state)
+{
+ OM_uint32 maj_stat;
+
+ *minor_status = 0;
+ if (qop_state)
+ *qop_state = GSS_C_QOP_DEFAULT;
+ if (conf_state)
+ *conf_state = 0;
+
+ if (time_uptime > kc->kc_lifetime)
+ return (GSS_S_CONTEXT_EXPIRED);
+
+ switch (kc->kc_tokenkey->ks_class->ec_type) {
+ case ETYPE_DES_CBC_CRC:
+ maj_stat = krb5_unwrap_old(kc, mp, conf_state,
+ sgn_alg_des_md5, seal_alg_des);
+ break;
+
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56:
+ maj_stat = krb5_unwrap_old(kc, mp, conf_state,
+ sgn_alg_hmac_md5, seal_alg_rc4);
+ break;
+
+ case ETYPE_DES3_CBC_SHA1:
+ maj_stat = krb5_unwrap_old(kc, mp, conf_state,
+ sgn_alg_des3_sha1, seal_alg_des3);
+ break;
+
+ default:
+ maj_stat = krb5_unwrap_new(kc, mp, conf_state);
+ break;
+ }
+
+ if (GSS_ERROR(maj_stat)) {
+ m_freem(*mp);
+ *mp = NULL;
+ }
+
+ return (maj_stat);
+}
+
+static OM_uint32
+krb5_wrap_size_limit(struct krb5_context *kc, OM_uint32 *minor_status,
+ int conf_req_flag, gss_qop_t qop_req, OM_uint32 req_output_size,
+ OM_uint32 *max_input_size)
+{
+ const struct krb5_encryption_class *ec;
+ OM_uint32 overhead;
+
+ *minor_status = 0;
+ *max_input_size = 0;
+
+ if (qop_req != GSS_C_QOP_DEFAULT)
+ return (GSS_S_BAD_QOP);
+
+ ec = kc->kc_tokenkey->ks_class;
+ switch (ec->ec_type) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES3_CBC_SHA1:
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56:
+ /*
+ * up to 5 bytes for [APPLICATION 0] SEQUENCE
+ * 2 + krb5 oid length
+ * 8 bytes of header
+ * 8 bytes of confounder
+ * maximum of 8 bytes of padding
+ * checksum
+ */
+ overhead = 5 + 2 + krb5_mech_oid.length;
+ overhead += 8 + 8 + ec->ec_msgblocklen;
+ overhead += ec->ec_checksumlen;
+ break;
+
+ default:
+ if (conf_req_flag) {
+ /*
+ * 16 byts of header
+ * blocklen bytes of confounder
+ * up to msgblocklen - 1 bytes of padding
+ * 16 bytes for copy of header
+ * checksum
+ */
+ overhead = 16 + ec->ec_blocklen;
+ overhead += ec->ec_msgblocklen - 1;
+ overhead += 16;
+ overhead += ec->ec_checksumlen;
+ } else {
+ /*
+ * 16 bytes of header plus checksum.
+ */
+ overhead = 16 + ec->ec_checksumlen;
+ }
+ }
+
+ *max_input_size = req_output_size - overhead;
+
+ return (GSS_S_COMPLETE);
+}
+
+static kobj_method_t krb5_methods[] = {
+ KOBJMETHOD(kgss_init, krb5_init),
+ KOBJMETHOD(kgss_import, krb5_import),
+ KOBJMETHOD(kgss_delete, krb5_delete),
+ KOBJMETHOD(kgss_mech_type, krb5_mech_type),
+ KOBJMETHOD(kgss_get_mic, krb5_get_mic),
+ KOBJMETHOD(kgss_verify_mic, krb5_verify_mic),
+ KOBJMETHOD(kgss_wrap, krb5_wrap),
+ KOBJMETHOD(kgss_unwrap, krb5_unwrap),
+ KOBJMETHOD(kgss_wrap_size_limit, krb5_wrap_size_limit),
+ { 0, 0 }
+};
+
+static struct kobj_class krb5_class = {
+ "kerberosv5",
+ krb5_methods,
+ sizeof(struct krb5_context)
+};
+
+/*
+ * Kernel module glue
+ */
+static int
+kgssapi_krb5_modevent(module_t mod, int type, void *data)
+{
+
+ switch (type) {
+ case MOD_LOAD:
+ kgss_install_mech(&krb5_mech_oid, "kerberosv5", &krb5_class);
+ break;
+
+ case MOD_UNLOAD:
+ kgss_uninstall_mech(&krb5_mech_oid);
+ break;
+ }
+
+
+ return (0);
+}
+static moduledata_t kgssapi_krb5_mod = {
+ "kgssapi_krb5",
+ kgssapi_krb5_modevent,
+ NULL,
+};
+DECLARE_MODULE(kgssapi_krb5, kgssapi_krb5_mod, SI_SUB_VFS, SI_ORDER_ANY);
+MODULE_DEPEND(kgssapi_krb5, kgssapi, 1, 1, 1);
+MODULE_DEPEND(kgssapi_krb5, crypto, 1, 1, 1);
+MODULE_DEPEND(kgssapi_krb5, rc4, 1, 1, 1);
+MODULE_VERSION(kgssapi_krb5, 1);
diff --git a/sys/modules/kgssapi/Makefile b/sys/modules/kgssapi/Makefile
new file mode 100644
index 0000000..223ef24
--- /dev/null
+++ b/sys/modules/kgssapi/Makefile
@@ -0,0 +1,55 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../kgssapi ${.CURDIR}/../../rpc/rpcsec_gss
+KMOD= kgssapi
+
+SRCS= gss_accept_sec_context.c \
+ gss_add_oid_set_member.c \
+ gss_acquire_cred.c \
+ gss_canonicalize_name.c \
+ gss_create_empty_oid_set.c \
+ gss_delete_sec_context.c \
+ gss_display_status.c \
+ gss_export_name.c \
+ gss_get_mic.c \
+ gss_init_sec_context.c \
+ gss_impl.c \
+ gss_import_name.c \
+ gss_names.c \
+ gss_pname_to_uid.c \
+ gss_release_buffer.c \
+ gss_release_cred.c \
+ gss_release_name.c \
+ gss_release_oid_set.c \
+ gss_set_cred_option.c \
+ gss_test_oid_set_member.c \
+ gss_unwrap.c \
+ gss_verify_mic.c \
+ gss_wrap.c \
+ gss_wrap_size_limit.c \
+ gssd_prot.c
+
+SRCS+= rpcsec_gss.c \
+ rpcsec_gss_conf.c \
+ rpcsec_gss_misc.c \
+ rpcsec_gss_prot.c \
+ svc_rpcsec_gss.c
+
+SRCS+= kgss_if.h kgss_if.c
+MFILES= kgssapi/kgss_if.m
+
+SRCS+= gssd.h gssd_xdr.c gssd_clnt.c
+CLEANFILES= gssd.h gssd_xdr.c gssd_clnt.c
+
+S= ${.CURDIR}/../..
+
+gssd.h: $S/kgssapi/gssd.x
+ rpcgen -hM $S/kgssapi/gssd.x | grep -v pthread.h > gssd.h
+
+gssd_xdr.c: $S/kgssapi/gssd.x
+ rpcgen -c $S/kgssapi/gssd.x -o gssd_xdr.c
+
+gssd_clnt.c: $S/kgssapi/gssd.x
+ rpcgen -lM $S/kgssapi/gssd.x | grep -v string.h > gssd_clnt.c
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/kgssapi_krb5/Makefile b/sys/modules/kgssapi_krb5/Makefile
new file mode 100644
index 0000000..c2ee417
--- /dev/null
+++ b/sys/modules/kgssapi_krb5/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../kgssapi/krb5
+KMOD= kgssapi_krb5
+
+SRCS= krb5_mech.c \
+ kcrypto.c \
+ kcrypto_des.c \
+ kcrypto_des3.c \
+ kcrypto_aes.c \
+ kcrypto_arcfour.c
+
+SRCS+= kgss_if.h gssd.h
+MFILES= kgssapi/kgss_if.m
+
+S= ${.CURDIR}/../..
+
+gssd.h: $S/kgssapi/gssd.x
+ rpcgen -hM $S/kgssapi/gssd.x | grep -v pthread.h > gssd.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/nfsclient/Makefile b/sys/modules/nfsclient/Makefile
index 1804201..1562a64 100644
--- a/sys/modules/nfsclient/Makefile
+++ b/sys/modules/nfsclient/Makefile
@@ -6,11 +6,11 @@
KMOD= nfsclient
SRCS= vnode_if.h \
nfs_bio.c nfs_lock.c nfs_node.c nfs_socket.c nfs_subs.c nfs_nfsiod.c \
- nfs_vfsops.c nfs_vnops.c nfs_common.c \
+ nfs_vfsops.c nfs_vnops.c nfs_common.c nfs_krpc.c \
opt_inet.h opt_nfs.h opt_bootp.h opt_nfsroot.h
SRCS+= nfs4_dev.c nfs4_idmap.c nfs4_socket.c nfs4_subs.c \
nfs4_vfs_subs.c nfs4_vfsops.c nfs4_vn_subs.c nfs4_vnops.c
-SRCS+= opt_inet6.h
+SRCS+= opt_inet6.h opt_kgssapi.h
# USE THE RPCCLNT:
CFLAGS+= -DRPCCLNT_DEBUG
diff --git a/sys/modules/nfsserver/Makefile b/sys/modules/nfsserver/Makefile
index 8e33c46..771794a 100644
--- a/sys/modules/nfsserver/Makefile
+++ b/sys/modules/nfsserver/Makefile
@@ -3,8 +3,8 @@
.PATH: ${.CURDIR}/../../nfsserver ${.CURDIR}/../../nfs
KMOD= nfsserver
SRCS= vnode_if.h \
- nfs_serv.c nfs_srvsock.c nfs_srvcache.c nfs_srvsubs.c nfs_syscalls.c \
- nfs_common.c \
+ nfs_serv.c nfs_srvkrpc.c nfs_srvsock.c nfs_srvcache.c nfs_srvsubs.c \
+ nfs_syscalls.c nfs_common.c \
opt_mac.h \
opt_nfs.h
SRCS+= opt_inet6.h
diff --git a/sys/nfsclient/nfs.h b/sys/nfsclient/nfs.h
index 9e52420..c5b1c51 100644
--- a/sys/nfsclient/nfs.h
+++ b/sys/nfsclient/nfs.h
@@ -132,7 +132,9 @@ MALLOC_DECLARE(M_NFSDIRECTIO);
extern struct uma_zone *nfsmount_zone;
+#ifdef NFS_LEGACYRPC
extern struct callout nfs_callout;
+#endif
extern struct nfsstats nfsstats;
extern struct mtx nfs_iod_mtx;
@@ -157,6 +159,8 @@ extern int nfsv3_procid[NFS_NPROCS];
(e) != ERESTART && (e) != EWOULDBLOCK && \
((s) & PR_CONNREQUIRED) == 0)
+#ifdef NFS_LEGACYRPC
+
/*
* Nfs outstanding request list element
*/
@@ -196,6 +200,17 @@ extern TAILQ_HEAD(nfs_reqq, nfsreq) nfs_reqq;
#define R_GETONEREP 0x80 /* Probe for one reply only */
#define R_PIN_REQ 0x100 /* Pin request down (rexmit in prog or other) */
+#else
+
+/*
+ * This is only needed to keep things working while we support
+ * compiling for both RPC implementations.
+ */
+struct nfsreq;
+struct nfsmount;
+
+#endif
+
struct buf;
struct socket;
struct uio;
@@ -291,12 +306,18 @@ vfs_init_t nfs_init;
vfs_uninit_t nfs_uninit;
int nfs_mountroot(struct mount *mp, struct thread *td);
+#ifdef NFS_LEGACYRPC
#ifndef NFS4_USE_RPCCLNT
int nfs_send(struct socket *, struct sockaddr *, struct mbuf *,
struct nfsreq *);
int nfs_connect_lock(struct nfsreq *);
void nfs_connect_unlock(struct nfsreq *);
+void nfs_up(struct nfsreq *, struct nfsmount *, struct thread *,
+ const char *, int);
+void nfs_down(struct nfsreq *, struct nfsmount *, struct thread *,
+ const char *, int, int);
#endif /* ! NFS4_USE_RPCCLNT */
+#endif
int nfs_vinvalbuf(struct vnode *, int, struct thread *, int);
int nfs_readrpc(struct vnode *, struct uio *, struct ucred *);
@@ -309,10 +330,6 @@ int nfs_nfsiodnew(void);
int nfs_asyncio(struct nfsmount *, struct buf *, struct ucred *, struct thread *);
int nfs_doio(struct vnode *, struct buf *, struct ucred *, struct thread *);
void nfs_doio_directwrite (struct buf *);
-void nfs_up(struct nfsreq *, struct nfsmount *, struct thread *,
- const char *, int);
-void nfs_down(struct nfsreq *, struct nfsmount *, struct thread *,
- const char *, int, int);
int nfs_readlinkrpc(struct vnode *, struct uio *, struct ucred *);
int nfs_sigintr(struct nfsmount *, struct nfsreq *, struct thread *);
int nfs_readdirplusrpc(struct vnode *, struct uio *, struct ucred *);
diff --git a/sys/nfsclient/nfs_krpc.c b/sys/nfsclient/nfs_krpc.c
new file mode 100644
index 0000000..8a5f805
--- /dev/null
+++ b/sys/nfsclient/nfs_krpc.c
@@ -0,0 +1,769 @@
+/*-
+ * Copyright (c) 1989, 1991, 1993, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * 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.
+ * 4. 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 BY THE REGENTS 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 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.
+ *
+ * @(#)nfs_socket.c 8.5 (Berkeley) 3/30/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Socket operations for use by nfs
+ */
+
+#include "opt_inet6.h"
+#include "opt_kgssapi.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/signalvar.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <sys/vnode.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpcclnt.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsclient/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfsclient/nfsm_subs.h>
+#include <nfsclient/nfsmount.h>
+#include <nfsclient/nfsnode.h>
+
+#include <nfs4client/nfs4.h>
+
+#ifndef NFS_LEGACYRPC
+
+static int nfs_realign_test;
+static int nfs_realign_count;
+static int nfs_bufpackets = 4;
+static int nfs_reconnects;
+static int nfs3_jukebox_delay = 10;
+static int nfs_skip_wcc_data_onerr = 1;
+static int fake_wchan;
+
+SYSCTL_DECL(_vfs_nfs);
+
+SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_test, CTLFLAG_RW, &nfs_realign_test, 0,
+ "Number of realign tests done");
+SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_count, CTLFLAG_RW, &nfs_realign_count, 0,
+ "Number of mbuf realignments done");
+SYSCTL_INT(_vfs_nfs, OID_AUTO, bufpackets, CTLFLAG_RW, &nfs_bufpackets, 0,
+ "Buffer reservation size 2 < x < 64");
+SYSCTL_INT(_vfs_nfs, OID_AUTO, reconnects, CTLFLAG_RD, &nfs_reconnects, 0,
+ "Number of times the nfs client has had to reconnect");
+SYSCTL_INT(_vfs_nfs, OID_AUTO, nfs3_jukebox_delay, CTLFLAG_RW, &nfs3_jukebox_delay, 0,
+ "Number of seconds to delay a retry after receiving EJUKEBOX");
+SYSCTL_INT(_vfs_nfs, OID_AUTO, skip_wcc_data_onerr, CTLFLAG_RW, &nfs_skip_wcc_data_onerr, 0,
+ "Disable weak cache consistency checking when server returns an error");
+
+static void nfs_down(struct nfsmount *, struct thread *, const char *,
+ int, int);
+static void nfs_up(struct nfsmount *, struct thread *, const char *,
+ int, int);
+static int nfs_msg(struct thread *, const char *, const char *, int);
+
+extern int nfsv2_procid[];
+
+struct nfs_cached_auth {
+ int ca_refs; /* refcount, including 1 from the cache */
+ uid_t ca_uid; /* uid that corresponds to this auth */
+ AUTH *ca_auth; /* RPC auth handle */
+};
+
+/*
+ * RTT estimator
+ */
+
+static enum nfs_rto_timer_t nfs_proct[NFS_NPROCS] = {
+ NFS_DEFAULT_TIMER, /* NULL */
+ NFS_GETATTR_TIMER, /* GETATTR */
+ NFS_DEFAULT_TIMER, /* SETATTR */
+ NFS_LOOKUP_TIMER, /* LOOKUP */
+ NFS_GETATTR_TIMER, /* ACCESS */
+ NFS_READ_TIMER, /* READLINK */
+ NFS_READ_TIMER, /* READ */
+ NFS_WRITE_TIMER, /* WRITE */
+ NFS_DEFAULT_TIMER, /* CREATE */
+ NFS_DEFAULT_TIMER, /* MKDIR */
+ NFS_DEFAULT_TIMER, /* SYMLINK */
+ NFS_DEFAULT_TIMER, /* MKNOD */
+ NFS_DEFAULT_TIMER, /* REMOVE */
+ NFS_DEFAULT_TIMER, /* RMDIR */
+ NFS_DEFAULT_TIMER, /* RENAME */
+ NFS_DEFAULT_TIMER, /* LINK */
+ NFS_READ_TIMER, /* READDIR */
+ NFS_READ_TIMER, /* READDIRPLUS */
+ NFS_DEFAULT_TIMER, /* FSSTAT */
+ NFS_DEFAULT_TIMER, /* FSINFO */
+ NFS_DEFAULT_TIMER, /* PATHCONF */
+ NFS_DEFAULT_TIMER, /* COMMIT */
+ NFS_DEFAULT_TIMER, /* NOOP */
+};
+
+/*
+ * Choose the correct RTT timer for this NFS procedure.
+ */
+static inline enum nfs_rto_timer_t
+nfs_rto_timer(u_int32_t procnum)
+{
+ return nfs_proct[procnum];
+}
+
+/*
+ * Initialize the RTT estimator state for a new mount point.
+ */
+static void
+nfs_init_rtt(struct nfsmount *nmp)
+{
+ int i;
+
+ for (i = 0; i < NFS_MAX_TIMER; i++) {
+ nmp->nm_timers[i].rt_srtt = hz;
+ nmp->nm_timers[i].rt_deviate = 0;
+ nmp->nm_timers[i].rt_rtxcur = hz;
+ }
+}
+
+/*
+ * Initialize sockets and congestion for a new NFS connection.
+ * We do not free the sockaddr if error.
+ */
+int
+nfs_connect(struct nfsmount *nmp, struct nfsreq *rep)
+{
+ int rcvreserve, sndreserve;
+ int pktscale;
+ struct sockaddr *saddr;
+ struct ucred *origcred;
+ struct thread *td = curthread;
+ CLIENT *client;
+ struct netconfig *nconf;
+ rpcvers_t vers;
+ int one = 1, retries;
+
+ /*
+ * We need to establish the socket using the credentials of
+ * the mountpoint. Some parts of this process (such as
+ * sobind() and soconnect()) will use the curent thread's
+ * credential instead of the socket credential. To work
+ * around this, temporarily change the current thread's
+ * credential to that of the mountpoint.
+ *
+ * XXX: It would be better to explicitly pass the correct
+ * credential to sobind() and soconnect().
+ */
+ origcred = td->td_ucred;
+ td->td_ucred = nmp->nm_mountp->mnt_cred;
+ saddr = nmp->nm_nam;
+
+ vers = NFS_VER2;
+ if (nmp->nm_flag & NFSMNT_NFSV3)
+ vers = NFS_VER3;
+ else if (nmp->nm_flag & NFSMNT_NFSV4)
+ vers = NFS_VER4;
+ if (saddr->sa_family == AF_INET)
+ if (nmp->nm_sotype == SOCK_DGRAM)
+ nconf = getnetconfigent("udp");
+ else
+ nconf = getnetconfigent("tcp");
+ else
+ if (nmp->nm_sotype == SOCK_DGRAM)
+ nconf = getnetconfigent("udp6");
+ else
+ nconf = getnetconfigent("tcp6");
+
+ /*
+ * Get buffer reservation size from sysctl, but impose reasonable
+ * limits.
+ */
+ pktscale = nfs_bufpackets;
+ if (pktscale < 2)
+ pktscale = 2;
+ if (pktscale > 64)
+ pktscale = 64;
+ mtx_lock(&nmp->nm_mtx);
+ if (nmp->nm_sotype == SOCK_DGRAM) {
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR) * pktscale;
+ rcvreserve = (max(nmp->nm_rsize, nmp->nm_readdirsize) +
+ NFS_MAXPKTHDR) * pktscale;
+ } else if (nmp->nm_sotype == SOCK_SEQPACKET) {
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR) * pktscale;
+ rcvreserve = (max(nmp->nm_rsize, nmp->nm_readdirsize) +
+ NFS_MAXPKTHDR) * pktscale;
+ } else {
+ if (nmp->nm_sotype != SOCK_STREAM)
+ panic("nfscon sotype");
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR +
+ sizeof (u_int32_t)) * pktscale;
+ rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR +
+ sizeof (u_int32_t)) * pktscale;
+ }
+ mtx_unlock(&nmp->nm_mtx);
+
+ client = clnt_reconnect_create(nconf, saddr, NFS_PROG, vers,
+ sndreserve, rcvreserve);
+ CLNT_CONTROL(client, CLSET_WAITCHAN, "nfsreq");
+ if (nmp->nm_flag & NFSMNT_INT)
+ CLNT_CONTROL(client, CLSET_INTERRUPTIBLE, &one);
+ if (nmp->nm_flag & NFSMNT_RESVPORT)
+ CLNT_CONTROL(client, CLSET_PRIVPORT, &one);
+ if (nmp->nm_flag & NFSMNT_SOFT)
+ retries = nmp->nm_retry;
+ else
+ retries = INT_MAX;
+ CLNT_CONTROL(client, CLSET_RETRIES, &retries);
+
+ mtx_lock(&nmp->nm_mtx);
+ if (nmp->nm_client) {
+ /*
+ * Someone else already connected.
+ */
+ CLNT_RELEASE(client);
+ } else {
+ nmp->nm_client = client;
+ }
+
+ /*
+ * Protocols that do not require connections may be optionally left
+ * unconnected for servers that reply from a port other than NFS_PORT.
+ */
+ if (!(nmp->nm_flag & NFSMNT_NOCONN)) {
+ mtx_unlock(&nmp->nm_mtx);
+ CLNT_CONTROL(client, CLSET_CONNECT, &one);
+ } else {
+ mtx_unlock(&nmp->nm_mtx);
+ }
+
+ /* Restore current thread's credentials. */
+ td->td_ucred = origcred;
+
+ mtx_lock(&nmp->nm_mtx);
+ /* Initialize other non-zero congestion variables */
+ nfs_init_rtt(nmp);
+ mtx_unlock(&nmp->nm_mtx);
+ return (0);
+}
+
+/*
+ * NFS disconnect. Clean up and unlink.
+ */
+void
+nfs_disconnect(struct nfsmount *nmp)
+{
+ CLIENT *client;
+
+ mtx_lock(&nmp->nm_mtx);
+ if (nmp->nm_client) {
+ client = nmp->nm_client;
+ nmp->nm_client = NULL;
+ mtx_unlock(&nmp->nm_mtx);
+#ifdef KGSSAPI
+ rpc_gss_secpurge(client);
+#endif
+ CLNT_CLOSE(client);
+ CLNT_RELEASE(client);
+ } else {
+ mtx_unlock(&nmp->nm_mtx);
+ }
+}
+
+void
+nfs_safedisconnect(struct nfsmount *nmp)
+{
+
+ nfs_disconnect(nmp);
+}
+
+static AUTH *
+nfs_getauth(struct nfsmount *nmp, struct ucred *cred)
+{
+#ifdef KGSSAPI
+ rpc_gss_service_t svc;
+ AUTH *auth;
+#endif
+
+ switch (nmp->nm_secflavor) {
+#ifdef KGSSAPI
+ case RPCSEC_GSS_KRB5:
+ case RPCSEC_GSS_KRB5I:
+ case RPCSEC_GSS_KRB5P:
+ if (!nmp->nm_mech_oid) {
+ if (!rpc_gss_mech_to_oid("kerberosv5",
+ &nmp->nm_mech_oid))
+ return (NULL);
+ }
+ if (nmp->nm_secflavor == RPCSEC_GSS_KRB5)
+ svc = rpc_gss_svc_none;
+ else if (nmp->nm_secflavor == RPCSEC_GSS_KRB5I)
+ svc = rpc_gss_svc_integrity;
+ else
+ svc = rpc_gss_svc_privacy;
+ auth = rpc_gss_secfind(nmp->nm_client, cred,
+ nmp->nm_principal, nmp->nm_mech_oid, svc);
+ if (auth)
+ return (auth);
+ /* fallthrough */
+#endif
+ case AUTH_SYS:
+ default:
+ return (authunix_create(cred));
+
+ }
+}
+
+/*
+ * Callback from the RPC code to generate up/down notifications.
+ */
+
+struct nfs_feedback_arg {
+ struct nfsmount *nf_mount;
+ int nf_lastmsg; /* last tprintf */
+ int nf_tprintfmsg;
+ struct thread *nf_td;
+};
+
+static void
+nfs_feedback(int type, int proc, void *arg)
+{
+ struct nfs_feedback_arg *nf = (struct nfs_feedback_arg *) arg;
+ struct nfsmount *nmp = nf->nf_mount;
+ struct timeval now;
+
+ getmicrouptime(&now);
+
+ switch (type) {
+ case FEEDBACK_REXMIT2:
+ case FEEDBACK_RECONNECT:
+ if (nf->nf_lastmsg + nmp->nm_tprintf_delay < now.tv_sec) {
+ nfs_down(nmp, nf->nf_td,
+ "not responding", 0, NFSSTA_TIMEO);
+ nf->nf_tprintfmsg = TRUE;
+ nf->nf_lastmsg = now.tv_sec;
+ }
+ break;
+
+ case FEEDBACK_OK:
+ nfs_up(nf->nf_mount, nf->nf_td,
+ "is alive again", NFSSTA_TIMEO, nf->nf_tprintfmsg);
+ break;
+ }
+}
+
+/*
+ * nfs_request - goes something like this
+ * - fill in request struct
+ * - links it into list
+ * - calls nfs_send() for first transmit
+ * - calls nfs_receive() to get reply
+ * - break down rpc header and return with nfs reply pointed to
+ * by mrep or error
+ * nb: always frees up mreq mbuf list
+ */
+int
+nfs_request(struct vnode *vp, struct mbuf *mreq, int procnum,
+ struct thread *td, struct ucred *cred, struct mbuf **mrp,
+ struct mbuf **mdp, caddr_t *dposp)
+{
+ struct mbuf *mrep;
+ u_int32_t *tl;
+ struct nfsmount *nmp;
+ struct mbuf *md;
+ time_t waituntil;
+ caddr_t dpos;
+ int error = 0;
+ struct timeval now;
+ AUTH *auth = NULL;
+ enum nfs_rto_timer_t timer;
+ struct nfs_feedback_arg nf;
+ struct rpc_callextra ext;
+ enum clnt_stat stat;
+ struct timeval timo;
+
+ /* Reject requests while attempting a forced unmount. */
+ if (vp->v_mount->mnt_kern_flag & MNTK_UNMOUNTF) {
+ m_freem(mreq);
+ return (ESTALE);
+ }
+ nmp = VFSTONFS(vp->v_mount);
+ if ((nmp->nm_flag & NFSMNT_NFSV4) != 0)
+ return nfs4_request(vp, mreq, procnum, td, cred, mrp, mdp, dposp);
+ bzero(&nf, sizeof(struct nfs_feedback_arg));
+ nf.nf_mount = nmp;
+ nf.nf_td = td;
+ getmicrouptime(&now);
+ nf.nf_lastmsg = now.tv_sec -
+ ((nmp->nm_tprintf_delay) - (nmp->nm_tprintf_initial_delay));
+
+ /*
+ * XXX if not already connected call nfs_connect now. Longer
+ * term, change nfs_mount to call nfs_connect unconditionally
+ * and let clnt_reconnect_create handle reconnects.
+ */
+ if (!nmp->nm_client)
+ nfs_connect(nmp, NULL);
+
+ auth = nfs_getauth(nmp, cred);
+ if (!auth) {
+ m_freem(mreq);
+ return (EACCES);
+ }
+ bzero(&ext, sizeof(ext));
+ ext.rc_auth = auth;
+
+ ext.rc_feedback = nfs_feedback;
+ ext.rc_feedback_arg = &nf;
+
+ /*
+ * Use a conservative timeout for RPCs other than getattr,
+ * lookup, read or write. The justification for doing "other"
+ * this way is that these RPCs happen so infrequently that
+ * timer est. would probably be stale. Also, since many of
+ * these RPCs are non-idempotent, a conservative timeout is
+ * desired.
+ */
+ timer = nfs_rto_timer(procnum);
+ if (timer != NFS_DEFAULT_TIMER) {
+ ext.rc_timers = &nmp->nm_timers[timer - 1];
+ } else {
+ ext.rc_timers = NULL;
+ }
+
+ nfsstats.rpcrequests++;
+tryagain:
+ timo.tv_sec = nmp->nm_timeo / NFS_HZ;
+ timo.tv_usec = (nmp->nm_timeo * 1000000) / NFS_HZ;
+ mrep = NULL;
+ stat = CLNT_CALL_MBUF(nmp->nm_client, &ext,
+ (nmp->nm_flag & NFSMNT_NFSV3) ? procnum : nfsv2_procid[procnum],
+ mreq, &mrep, timo);
+
+ /*
+ * If there was a successful reply and a tprintf msg.
+ * tprintf a response.
+ */
+ if (stat == RPC_SUCCESS) {
+ error = 0;
+ } else if (stat == RPC_TIMEDOUT) {
+ error = ETIMEDOUT;
+ } else if (stat == RPC_VERSMISMATCH) {
+ error = EOPNOTSUPP;
+ } else if (stat == RPC_PROGVERSMISMATCH) {
+ error = EPROTONOSUPPORT;
+ } else {
+ error = EACCES;
+ }
+ md = mrep;
+ if (error) {
+ m_freem(mreq);
+ AUTH_DESTROY(auth);
+ return (error);
+ }
+
+ KASSERT(mrep != NULL, ("mrep shouldn't be NULL if no error\n"));
+
+ dpos = mtod(mrep, caddr_t);
+ tl = nfsm_dissect(u_int32_t *, NFSX_UNSIGNED);
+ if (*tl != 0) {
+ error = fxdr_unsigned(int, *tl);
+ if ((nmp->nm_flag & NFSMNT_NFSV3) &&
+ error == NFSERR_TRYLATER) {
+ m_freem(mrep);
+ error = 0;
+ waituntil = time_second + nfs3_jukebox_delay;
+ while (time_second < waituntil) {
+ (void) tsleep(&fake_wchan, PSOCK, "nqnfstry", hz);
+ }
+ goto tryagain;
+ }
+
+ /*
+ * If the File Handle was stale, invalidate the lookup
+ * cache, just in case.
+ */
+ if (error == ESTALE)
+ cache_purge(vp);
+ /*
+ * Skip wcc data on NFS errors for now. NetApp filers
+ * return corrupt postop attrs in the wcc data for NFS
+ * err EROFS. Not sure if they could return corrupt
+ * postop attrs for others errors.
+ */
+ if ((nmp->nm_flag & NFSMNT_NFSV3) && !nfs_skip_wcc_data_onerr) {
+ *mrp = mrep;
+ *mdp = md;
+ *dposp = dpos;
+ error |= NFSERR_RETERR;
+ } else
+ m_freem(mrep);
+ m_freem(mreq);
+ AUTH_DESTROY(auth);
+ return (error);
+ }
+
+ m_freem(mreq);
+ *mrp = mrep;
+ *mdp = md;
+ *dposp = dpos;
+ AUTH_DESTROY(auth);
+ return (0);
+
+nfsmout:
+ m_freem(mreq);
+ if (auth)
+ AUTH_DESTROY(auth);
+ return (error);
+}
+
+/*
+ * Mark all of an nfs mount's outstanding requests with R_SOFTTERM and
+ * wait for all requests to complete. This is used by forced unmounts
+ * to terminate any outstanding RPCs.
+ */
+int
+nfs_nmcancelreqs(struct nfsmount *nmp)
+{
+
+ if (nmp->nm_client)
+ CLNT_CLOSE(nmp->nm_client);
+ return (0);
+}
+
+/*
+ * Any signal that can interrupt an NFS operation in an intr mount
+ * should be added to this set. SIGSTOP and SIGKILL cannot be masked.
+ */
+int nfs_sig_set[] = {
+ SIGINT,
+ SIGTERM,
+ SIGHUP,
+ SIGKILL,
+ SIGSTOP,
+ SIGQUIT
+};
+
+/*
+ * Check to see if one of the signals in our subset is pending on
+ * the process (in an intr mount).
+ */
+static int
+nfs_sig_pending(sigset_t set)
+{
+ int i;
+
+ for (i = 0 ; i < sizeof(nfs_sig_set)/sizeof(int) ; i++)
+ if (SIGISMEMBER(set, nfs_sig_set[i]))
+ return (1);
+ return (0);
+}
+
+/*
+ * The set/restore sigmask functions are used to (temporarily) overwrite
+ * the process p_sigmask during an RPC call (for example). These are also
+ * used in other places in the NFS client that might tsleep().
+ */
+void
+nfs_set_sigmask(struct thread *td, sigset_t *oldset)
+{
+ sigset_t newset;
+ int i;
+ struct proc *p;
+
+ SIGFILLSET(newset);
+ if (td == NULL)
+ td = curthread; /* XXX */
+ p = td->td_proc;
+ /* Remove the NFS set of signals from newset */
+ PROC_LOCK(p);
+ mtx_lock(&p->p_sigacts->ps_mtx);
+ for (i = 0 ; i < sizeof(nfs_sig_set)/sizeof(int) ; i++) {
+ /*
+ * But make sure we leave the ones already masked
+ * by the process, ie. remove the signal from the
+ * temporary signalmask only if it wasn't already
+ * in p_sigmask.
+ */
+ if (!SIGISMEMBER(td->td_sigmask, nfs_sig_set[i]) &&
+ !SIGISMEMBER(p->p_sigacts->ps_sigignore, nfs_sig_set[i]))
+ SIGDELSET(newset, nfs_sig_set[i]);
+ }
+ mtx_unlock(&p->p_sigacts->ps_mtx);
+ PROC_UNLOCK(p);
+ kern_sigprocmask(td, SIG_SETMASK, &newset, oldset, 0);
+}
+
+void
+nfs_restore_sigmask(struct thread *td, sigset_t *set)
+{
+ if (td == NULL)
+ td = curthread; /* XXX */
+ kern_sigprocmask(td, SIG_SETMASK, set, NULL, 0);
+}
+
+/*
+ * NFS wrapper to msleep(), that shoves a new p_sigmask and restores the
+ * old one after msleep() returns.
+ */
+int
+nfs_msleep(struct thread *td, void *ident, struct mtx *mtx, int priority, char *wmesg, int timo)
+{
+ sigset_t oldset;
+ int error;
+ struct proc *p;
+
+ if ((priority & PCATCH) == 0)
+ return msleep(ident, mtx, priority, wmesg, timo);
+ if (td == NULL)
+ td = curthread; /* XXX */
+ nfs_set_sigmask(td, &oldset);
+ error = msleep(ident, mtx, priority, wmesg, timo);
+ nfs_restore_sigmask(td, &oldset);
+ p = td->td_proc;
+ return (error);
+}
+
+/*
+ * Test for a termination condition pending on the process.
+ * This is used for NFSMNT_INT mounts.
+ */
+int
+nfs_sigintr(struct nfsmount *nmp, struct nfsreq *rep, struct thread *td)
+{
+ struct proc *p;
+ sigset_t tmpset;
+
+ if ((nmp->nm_flag & NFSMNT_NFSV4) != 0)
+ return nfs4_sigintr(nmp, rep, td);
+ /* Terminate all requests while attempting a forced unmount. */
+ if (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF)
+ return (EIO);
+ if (!(nmp->nm_flag & NFSMNT_INT))
+ return (0);
+ if (td == NULL)
+ return (0);
+ p = td->td_proc;
+ PROC_LOCK(p);
+ tmpset = p->p_siglist;
+ SIGSETOR(tmpset, td->td_siglist);
+ SIGSETNAND(tmpset, td->td_sigmask);
+ mtx_lock(&p->p_sigacts->ps_mtx);
+ SIGSETNAND(tmpset, p->p_sigacts->ps_sigignore);
+ mtx_unlock(&p->p_sigacts->ps_mtx);
+ if ((SIGNOTEMPTY(p->p_siglist) || SIGNOTEMPTY(td->td_siglist))
+ && nfs_sig_pending(tmpset)) {
+ PROC_UNLOCK(p);
+ return (EINTR);
+ }
+ PROC_UNLOCK(p);
+ return (0);
+}
+
+static int
+nfs_msg(struct thread *td, const char *server, const char *msg, int error)
+{
+ struct proc *p;
+
+ p = td ? td->td_proc : NULL;
+ if (error) {
+ tprintf(p, LOG_INFO, "nfs server %s: %s, error %d\n", server,
+ msg, error);
+ } else {
+ tprintf(p, LOG_INFO, "nfs server %s: %s\n", server, msg);
+ }
+ return (0);
+}
+
+static void
+nfs_down(struct nfsmount *nmp, struct thread *td, const char *msg,
+ int error, int flags)
+{
+ if (nmp == NULL)
+ return;
+ mtx_lock(&nmp->nm_mtx);
+ if ((flags & NFSSTA_TIMEO) && !(nmp->nm_state & NFSSTA_TIMEO)) {
+ nmp->nm_state |= NFSSTA_TIMEO;
+ mtx_unlock(&nmp->nm_mtx);
+ vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
+ VQ_NOTRESP, 0);
+ } else
+ mtx_unlock(&nmp->nm_mtx);
+ mtx_lock(&nmp->nm_mtx);
+ if ((flags & NFSSTA_LOCKTIMEO) && !(nmp->nm_state & NFSSTA_LOCKTIMEO)) {
+ nmp->nm_state |= NFSSTA_LOCKTIMEO;
+ mtx_unlock(&nmp->nm_mtx);
+ vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
+ VQ_NOTRESPLOCK, 0);
+ } else
+ mtx_unlock(&nmp->nm_mtx);
+ nfs_msg(td, nmp->nm_mountp->mnt_stat.f_mntfromname, msg, error);
+}
+
+static void
+nfs_up(struct nfsmount *nmp, struct thread *td, const char *msg,
+ int flags, int tprintfmsg)
+{
+ if (nmp == NULL)
+ return;
+ if (tprintfmsg) {
+ nfs_msg(td, nmp->nm_mountp->mnt_stat.f_mntfromname, msg, 0);
+ }
+
+ mtx_lock(&nmp->nm_mtx);
+ if ((flags & NFSSTA_TIMEO) && (nmp->nm_state & NFSSTA_TIMEO)) {
+ nmp->nm_state &= ~NFSSTA_TIMEO;
+ mtx_unlock(&nmp->nm_mtx);
+ vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
+ VQ_NOTRESP, 1);
+ } else
+ mtx_unlock(&nmp->nm_mtx);
+
+ mtx_lock(&nmp->nm_mtx);
+ if ((flags & NFSSTA_LOCKTIMEO) && (nmp->nm_state & NFSSTA_LOCKTIMEO)) {
+ nmp->nm_state &= ~NFSSTA_LOCKTIMEO;
+ mtx_unlock(&nmp->nm_mtx);
+ vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
+ VQ_NOTRESPLOCK, 1);
+ } else
+ mtx_unlock(&nmp->nm_mtx);
+}
+
+#endif /* !NFS_LEGACYRPC */
diff --git a/sys/nfsclient/nfs_socket.c b/sys/nfsclient/nfs_socket.c
index a3b23a1..a61962f 100644
--- a/sys/nfsclient/nfs_socket.c
+++ b/sys/nfsclient/nfs_socket.c
@@ -74,6 +74,8 @@ __FBSDID("$FreeBSD$");
#include <nfs4client/nfs4.h>
+#ifdef NFS_LEGACYRPC
+
#define TRUE 1
#define FALSE 0
@@ -1976,3 +1978,5 @@ nfs_up(rep, nmp, td, msg, flags)
mtx_unlock(&nmp->nm_mtx);
#endif
}
+
+#endif /* NFS_LEGACYRPC */
diff --git a/sys/nfsclient/nfs_subs.c b/sys/nfsclient/nfs_subs.c
index b97fc3f..be6ab92 100644
--- a/sys/nfsclient/nfs_subs.c
+++ b/sys/nfsclient/nfs_subs.c
@@ -99,8 +99,10 @@ static enum vtype nv2tov_type[8]= {
int nfs_ticks;
int nfs_pbuf_freecnt = -1; /* start out unlimited */
+#ifdef NFS_LEGACYRPC
struct nfs_reqq nfs_reqq;
struct mtx nfs_reqq_mtx;
+#endif
struct nfs_bufq nfs_bufq;
static struct mtx nfs_xid_mtx;
@@ -430,9 +432,11 @@ nfs_init(struct vfsconf *vfsp)
/*
* Initialize reply list and start timer
*/
+#ifdef NFS_LEGACYRPC
TAILQ_INIT(&nfs_reqq);
- callout_init(&nfs_callout, CALLOUT_MPSAFE);
mtx_init(&nfs_reqq_mtx, "NFS reqq lock", NULL, MTX_DEF);
+ callout_init(&nfs_callout, CALLOUT_MPSAFE);
+#endif
mtx_init(&nfs_iod_mtx, "NFS iod lock", NULL, MTX_DEF);
mtx_init(&nfs_xid_mtx, "NFS xid lock", NULL, MTX_DEF);
@@ -446,10 +450,12 @@ nfs_uninit(struct vfsconf *vfsp)
{
int i;
+#ifdef NFS_LEGACYRPC
callout_stop(&nfs_callout);
KASSERT(TAILQ_EMPTY(&nfs_reqq),
("nfs_uninit: request queue not empty"));
+#endif
/*
* Tell all nfsiod processes to exit. Clear nfs_iodmax, and wakeup
diff --git a/sys/nfsclient/nfs_vfsops.c b/sys/nfsclient/nfs_vfsops.c
index d3044ff..00332f0 100644
--- a/sys/nfsclient/nfs_vfsops.c
+++ b/sys/nfsclient/nfs_vfsops.c
@@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/in.h>
#include <rpc/rpcclnt.h>
+#include <rpc/rpc.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
@@ -142,6 +143,12 @@ VFS_SET(nfs_vfsops, nfs, VFCF_NETWORK);
/* So that loader and kldload(2) can find us, wherever we are.. */
MODULE_VERSION(nfs, 1);
+#ifndef NFS_LEGACYRPC
+MODULE_DEPEND(nfs, krpc, 1, 1, 1);
+#endif
+#ifdef KGSSAPI
+MODULE_DEPEND(nfs, kgssapi, 1, 1, 1);
+#endif
static struct nfs_rpcops nfs_rpcops = {
nfs_readrpc,
@@ -546,6 +553,26 @@ nfs_mountdiskless(char *path,
return (0);
}
+#ifndef NFS_LEGACYRPC
+static int
+nfs_sec_name_to_num(char *sec)
+{
+ if (!strcmp(sec, "krb5"))
+ return (RPCSEC_GSS_KRB5);
+ if (!strcmp(sec, "krb5i"))
+ return (RPCSEC_GSS_KRB5I);
+ if (!strcmp(sec, "krb5p"))
+ return (RPCSEC_GSS_KRB5P);
+ if (!strcmp(sec, "sys"))
+ return (AUTH_SYS);
+ /*
+ * Userland should validate the string but we will try and
+ * cope with unexpected values.
+ */
+ return (AUTH_SYS);
+}
+#endif
+
static void
nfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
const char *hostname)
@@ -554,6 +581,10 @@ nfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
int adjsock;
int maxio;
char *p;
+#ifndef NFS_LEGACYRPC
+ char *secname;
+ char *principal;
+#endif
s = splnet();
@@ -705,7 +736,13 @@ nfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
nmp->nm_sotype = argp->sotype;
nmp->nm_soproto = argp->proto;
- if (nmp->nm_so && adjsock) {
+ if (
+#ifdef NFS_LEGACYRPC
+ nmp->nm_so
+#else
+ nmp->nm_client
+#endif
+ && adjsock) {
nfs_safedisconnect(nmp);
if (nmp->nm_sotype == SOCK_DGRAM)
while (nfs_connect(nmp, NULL)) {
@@ -721,6 +758,24 @@ nfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
if (p)
*p = '\0';
}
+
+#ifndef NFS_LEGACYRPC
+ if (vfs_getopt(mp->mnt_optnew, "sec",
+ (void **) &secname, NULL) == 0) {
+ nmp->nm_secflavor = nfs_sec_name_to_num(secname);
+ } else {
+ nmp->nm_secflavor = AUTH_SYS;
+ }
+
+ if (vfs_getopt(mp->mnt_optnew, "principal",
+ (void **) &principal, NULL) == 0) {
+ strlcpy(nmp->nm_principal, principal,
+ sizeof(nmp->nm_principal));
+ } else {
+ snprintf(nmp->nm_principal, sizeof(nmp->nm_principal),
+ "nfs@%s", nmp->nm_hostname);
+ }
+#endif
}
static const char *nfs_opts[] = { "from", "nfs_args",
@@ -729,8 +784,8 @@ static const char *nfs_opts[] = { "from", "nfs_args",
"async", "dumbtimer", "noconn", "nolockd", "intr", "rdirplus", "resvport",
"readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
"retrans", "acregmin", "acregmax", "acdirmin", "acdirmax",
- "deadthresh", "hostname", "timeout", "addr", "fh", "nfsv3",
- "maxgroups",
+ "deadthresh", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
+ "maxgroups", "principal",
NULL };
/*
diff --git a/sys/nfsclient/nfsmount.h b/sys/nfsclient/nfsmount.h
index 6fa7f8b..75360fe 100644
--- a/sys/nfsclient/nfsmount.h
+++ b/sys/nfsclient/nfsmount.h
@@ -36,6 +36,25 @@
#ifndef _NFSCLIENT_NFSMOUNT_H_
#define _NFSCLIENT_NFSMOUNT_H_
+#ifndef NFS_LEGACYRPC
+
+#undef RPC_SUCCESS
+#undef RPC_PROGUNAVAIL
+#undef RPC_PROCUNAVAIL
+#undef AUTH_OK
+#undef AUTH_BADCRED
+#undef AUTH_BADVERF
+#undef AUTH_TOOWEAK
+
+#include <rpc/types.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpcsec_gss.h>
+
+#endif
+
+#ifdef NFS_LEGACYRPC
+
struct nfs_tcp_mountstate {
int rpcresid;
#define NFS_TCP_EXPECT_RPCMARKER 0x0001 /* Expect to see a RPC/TCP marker next */
@@ -45,6 +64,8 @@ struct nfs_tcp_mountstate {
int sock_send_inprog;
};
+#endif
+
/*
* Mount structure.
* One allocated on every NFS mount.
@@ -59,18 +80,22 @@ struct nfsmount {
u_char nm_fh[NFSX_V4FH]; /* File handle of root dir */
int nm_fhsize; /* Size of root file handle */
struct rpcclnt nm_rpcclnt; /* rpc state */
+#ifdef NFS_LEGACYRPC
struct socket *nm_so; /* Rpc socket */
+#endif
int nm_sotype; /* Type of socket */
int nm_soproto; /* and protocol */
int nm_soflags; /* pr_flags for socket protocol */
struct sockaddr *nm_nam; /* Addr of server */
int nm_timeo; /* Init timer for NFSMNT_DUMBTIMR */
int nm_retry; /* Max retries */
+#ifdef NFS_LEGACYRPC
int nm_srtt[NFS_MAX_TIMER], /* RTT Timers for rpcs */
nm_sdrtt[NFS_MAX_TIMER];
int nm_sent; /* Request send count */
int nm_cwnd; /* Request send window */
int nm_timeouts; /* Request timeouts */
+#endif
int nm_deadthresh; /* Threshold of timeouts-->dead server*/
int nm_rsize; /* Max size of read rpc */
int nm_wsize; /* Max size of write rpc */
@@ -90,8 +115,17 @@ struct nfsmount {
struct nfs_rpcops *nm_rpcops;
int nm_tprintf_initial_delay; /* initial delay */
int nm_tprintf_delay; /* interval for messages */
+#ifdef NFS_LEGACYRPC
struct nfs_tcp_mountstate nm_nfstcpstate;
+#endif
char nm_hostname[MNAMELEN]; /* server's name */
+#ifndef NFS_LEGACYRPC
+ int nm_secflavor; /* auth flavor to use for rpc */
+ struct __rpc_client *nm_client;
+ struct rpc_timers nm_timers[NFS_MAX_TIMER]; /* RTT Timers for rpcs */
+ char nm_principal[MNAMELEN]; /* GSS-API principal of server */
+ gss_OID nm_mech_oid; /* OID of selected GSS-API mechanism */
+#endif
/* NFSv4 */
uint64_t nm_clientid;
diff --git a/sys/nfsserver/nfs.h b/sys/nfsserver/nfs.h
index beb9359..2709377 100644
--- a/sys/nfsserver/nfs.h
+++ b/sys/nfsserver/nfs.h
@@ -89,13 +89,26 @@
* Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
* should ever try and use it.
*/
-struct nfsd_args {
+
+/*
+ * Add a socket to monitor for NFS requests.
+ */
+struct nfsd_addsock_args {
int sock; /* Socket to serve */
caddr_t name; /* Client addr for connection based sockets */
int namelen; /* Length of name */
};
/*
+ * Start processing requests.
+ */
+struct nfsd_nfsd_args {
+ const char *principal; /* GSS-API service principal name */
+ int minthreads; /* minimum service thread count */
+ int maxthreads; /* maximum service thread count */
+};
+
+/*
* XXX to allow amd to include nfs.h without nfsproto.h
*/
#ifdef NFS_NPROCS
@@ -105,8 +118,9 @@ struct nfsd_args {
/*
* Flags for nfssvc() system call.
*/
-#define NFSSVC_NFSD 0x004
+#define NFSSVC_OLDNFSD 0x004
#define NFSSVC_ADDSOCK 0x008
+#define NFSSVC_NFSD 0x010
/*
* vfs.nfsrv sysctl(3) identifiers
@@ -167,6 +181,7 @@ extern int32_t (*nfsrv3_procs[NFS_NPROCS])(struct nfsrv_descript *nd,
#define NWDELAYHASH(sock, f) \
(&(sock)->ns_wdelayhashtbl[(*((u_int32_t *)(f))) % NFS_WDELAYHASHSIZ])
+#ifdef NFS_LEGACYRPC
/*
* Network address hash list element
*/
@@ -257,11 +272,37 @@ struct nfsrv_descript {
struct timeval nd_starttime; /* Time RPC initiated */
fhandle_t nd_fh; /* File handle */
struct ucred *nd_cr; /* Credentials */
+ int nd_credflavor; /* Security flavor */
};
+#else
+
+/*
+ * This structure is used by the server for describing each request.
+ */
+struct nfsrv_descript {
+ struct mbuf *nd_mrep; /* Request mbuf list */
+ struct mbuf *nd_md; /* Current dissect mbuf */
+ struct mbuf *nd_mreq; /* Reply mbuf list */
+ struct sockaddr *nd_nam; /* and socket addr */
+ struct sockaddr *nd_nam2; /* return socket addr */
+ caddr_t nd_dpos; /* Current dissect pos */
+ u_int32_t nd_procnum; /* RPC # */
+ int nd_stable; /* storage type */
+ int nd_flag; /* nd_flag */
+ int nd_repstat; /* Reply status */
+ fhandle_t nd_fh; /* File handle */
+ struct ucred *nd_cr; /* Credentials */
+ int nd_credflavor; /* Security flavor */
+};
+
+#endif
+
/* Bits for "nd_flag" */
#define ND_NFSV3 0x08
+#ifdef NFS_LEGACYRPC
+
extern TAILQ_HEAD(nfsd_head, nfsd) nfsd_head;
extern int nfsd_head_flag;
#define NFSD_CHECKSLP 0x01
@@ -273,6 +314,8 @@ extern int nfsd_head_flag;
((o)->nd_eoff >= (n)->nd_off && \
!bcmp((caddr_t)&(o)->nd_fh, (caddr_t)&(n)->nd_fh, NFSX_V3FH))
+#endif
+
/*
* Defines for WebNFS
*/
@@ -315,38 +358,42 @@ extern int nfs_debug;
#endif
+#ifdef NFS_LEGACYRPC
+int netaddr_match(int, union nethostaddr *, struct sockaddr *);
int nfs_getreq(struct nfsrv_descript *, struct nfsd *, int);
int nfsrv_send(struct socket *, struct sockaddr *, struct mbuf *);
-struct mbuf *nfs_rephead(int, struct nfsrv_descript *, int, struct mbuf **,
- caddr_t *);
+int nfsrv_dorec(struct nfssvc_sock *, struct nfsd *,
+ struct nfsrv_descript **);
int nfs_slplock(struct nfssvc_sock *, int);
void nfs_slpunlock(struct nfssvc_sock *);
+void nfsrv_initcache(void);
+void nfsrv_destroycache(void);
+void nfsrv_timer(void *);
+int nfsrv_getcache(struct nfsrv_descript *, struct mbuf **);
+void nfsrv_updatecache(struct nfsrv_descript *, int, struct mbuf *);
+void nfsrv_cleancache(void);
+void nfsrv_rcv(struct socket *so, void *arg, int waitflag);
+void nfsrv_slpderef(struct nfssvc_sock *slp);
+void nfsrv_wakenfsd(struct nfssvc_sock *slp);
+int nfsrv_writegather(struct nfsrv_descript **, struct nfssvc_sock *,
+ struct mbuf **);
+#endif
+struct mbuf *nfs_rephead(int, struct nfsrv_descript *, int, struct mbuf **,
+ caddr_t *);
void nfsm_srvfattr(struct nfsrv_descript *, struct vattr *,
struct nfs_fattr *);
void nfsm_srvwcc(struct nfsrv_descript *, int, struct vattr *, int,
struct vattr *, struct mbuf **, char **);
void nfsm_srvpostopattr(struct nfsrv_descript *, int, struct vattr *,
struct mbuf **, char **);
-int netaddr_match(int, union nethostaddr *, struct sockaddr *);
-int nfs_namei(struct nameidata *, fhandle_t *, int,
- struct nfssvc_sock *, struct sockaddr *, struct mbuf **,
+int nfs_namei(struct nameidata *, struct nfsrv_descript *, fhandle_t *,
+ int, struct nfssvc_sock *, struct sockaddr *, struct mbuf **,
caddr_t *, struct vnode **, int, struct vattr *, int *, int);
void nfsm_adj(struct mbuf *, int, int);
int nfsm_mbuftouio(struct mbuf **, struct uio *, int, caddr_t *);
-void nfsrv_initcache(void);
-void nfsrv_destroycache(void);
-void nfsrv_timer(void *);
-int nfsrv_dorec(struct nfssvc_sock *, struct nfsd *,
- struct nfsrv_descript **);
-int nfsrv_getcache(struct nfsrv_descript *, struct mbuf **);
-void nfsrv_updatecache(struct nfsrv_descript *, int, struct mbuf *);
-void nfsrv_cleancache(void);
void nfsrv_init(int);
int nfsrv_errmap(struct nfsrv_descript *, int);
void nfsrvw_sort(gid_t *, int);
-void nfsrv_wakenfsd(struct nfssvc_sock *slp);
-int nfsrv_writegather(struct nfsrv_descript **, struct nfssvc_sock *,
- struct mbuf **);
int nfsrv3_access(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
struct mbuf **mrq);
@@ -354,8 +401,9 @@ int nfsrv_commit(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
struct mbuf **mrq);
int nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
struct mbuf **mrq);
-int nfsrv_fhtovp(fhandle_t *, int, struct vnode **, int *, struct ucred *,
- struct nfssvc_sock *, struct sockaddr *, int *, int);
+int nfsrv_fhtovp(fhandle_t *, int, struct vnode **, int *,
+ struct nfsrv_descript *, struct nfssvc_sock *, struct sockaddr *,
+ int *, int);
int nfsrv_setpublicfs(struct mount *, struct netexport *,
struct export_args *);
int nfs_ispublicfh(fhandle_t *);
@@ -399,8 +447,6 @@ int nfsrv_symlink(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
struct mbuf **mrq);
int nfsrv_write(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
struct mbuf **mrq);
-void nfsrv_rcv(struct socket *so, void *arg, int waitflag);
-void nfsrv_slpderef(struct nfssvc_sock *slp);
#endif /* _KERNEL */
#endif
diff --git a/sys/nfsserver/nfs_fha.c b/sys/nfsserver/nfs_fha.c
new file mode 100644
index 0000000..a2e4ca3
--- /dev/null
+++ b/sys/nfsserver/nfs_fha.c
@@ -0,0 +1,597 @@
+/*-
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysproto.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mbuf.h>
+#include <sys/sbuf.h>
+
+#include <rpc/rpc.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsserver/nfs.h>
+#include <nfsserver/nfsm_subs.h>
+#include <nfsserver/nfs_fha.h>
+
+#ifndef NFS_LEGACYRPC
+
+static MALLOC_DEFINE(M_NFS_FHA, "NFS FHA", "NFS FHA");
+
+/* Sysctl defaults. */
+#define DEF_BIN_SHIFT 18 /* 256k */
+#define DEF_MAX_NFSDS_PER_FH 8
+#define DEF_MAX_REQS_PER_NFSD 4
+
+struct fha_ctls {
+ u_int32_t bin_shift;
+ u_int32_t max_nfsds_per_fh;
+ u_int32_t max_reqs_per_nfsd;
+} fha_ctls;
+
+struct sysctl_ctx_list fha_clist;
+
+SYSCTL_DECL(_vfs_nfsrv);
+SYSCTL_DECL(_vfs_nfsrv_fha);
+
+/* Static sysctl node for the fha from the top-level vfs_nfsrv node. */
+SYSCTL_NODE(_vfs_nfsrv, OID_AUTO, fha, CTLFLAG_RD, 0, "fha node");
+
+/* This is the global structure that represents the state of the fha system. */
+static struct fha_global {
+ struct fha_hash_entry_list *hashtable;
+ u_long hashmask;
+} g_fha;
+
+/*
+ * These are the entries in the filehandle hash. They talk about a specific
+ * file, requests against which are being handled by one or more nfsds. We keep
+ * a chain of nfsds against the file. We only have more than one if reads are
+ * ongoing, and then only if the reads affect disparate regions of the file.
+ *
+ * In general, we want to assign a new request to an existing nfsd if it is
+ * going to contend with work happening already on that nfsd, or if the
+ * operation is a read and the nfsd is already handling a proximate read. We
+ * do this to avoid jumping around in the read stream unnecessarily, and to
+ * avoid contention between threads over single files.
+ */
+struct fha_hash_entry {
+ LIST_ENTRY(fha_hash_entry) link;
+ u_int64_t fh;
+ u_int16_t num_reads;
+ u_int16_t num_writes;
+ u_int8_t num_threads;
+ struct svcthread_list threads;
+};
+LIST_HEAD(fha_hash_entry_list, fha_hash_entry);
+
+/* A structure used for passing around data internally. */
+struct fha_info {
+ u_int64_t fh;
+ off_t offset;
+ int locktype;
+};
+
+static int fhe_stats_sysctl(SYSCTL_HANDLER_ARGS);
+
+static void
+nfs_fha_init(void *foo)
+{
+
+ /*
+ * A small hash table to map filehandles to fha_hash_entry
+ * structures.
+ */
+ g_fha.hashtable = hashinit(256, M_NFS_FHA, &g_fha.hashmask);
+
+ /*
+ * Initialize the sysctl context list for the fha module.
+ */
+ sysctl_ctx_init(&fha_clist);
+
+ fha_ctls.bin_shift = DEF_BIN_SHIFT;
+ fha_ctls.max_nfsds_per_fh = DEF_MAX_NFSDS_PER_FH;
+ fha_ctls.max_reqs_per_nfsd = DEF_MAX_REQS_PER_NFSD;
+
+ SYSCTL_ADD_UINT(&fha_clist, SYSCTL_STATIC_CHILDREN(_vfs_nfsrv_fha),
+ OID_AUTO, "bin_shift", CTLFLAG_RW,
+ &fha_ctls.bin_shift, 0, "For FHA reads, no two requests will "
+ "contend if they're 2^(bin_shift) bytes apart");
+
+ SYSCTL_ADD_UINT(&fha_clist, SYSCTL_STATIC_CHILDREN(_vfs_nfsrv_fha),
+ OID_AUTO, "max_nfsds_per_fh", CTLFLAG_RW,
+ &fha_ctls.max_nfsds_per_fh, 0, "Maximum nfsd threads that "
+ "should be working on requests for the same file handle");
+
+ SYSCTL_ADD_UINT(&fha_clist, SYSCTL_STATIC_CHILDREN(_vfs_nfsrv_fha),
+ OID_AUTO, "max_reqs_per_nfsd", CTLFLAG_RW,
+ &fha_ctls.max_reqs_per_nfsd, 0, "Maximum requests that "
+ "single nfsd thread should be working on at any time");
+
+ SYSCTL_ADD_OID(&fha_clist, SYSCTL_STATIC_CHILDREN(_vfs_nfsrv_fha),
+ OID_AUTO, "fhe_stats", CTLTYPE_STRING | CTLFLAG_RD, 0, 0,
+ fhe_stats_sysctl, "A", "");
+}
+
+static void
+nfs_fha_uninit(void *foo)
+{
+
+ hashdestroy(g_fha.hashtable, M_NFS_FHA, g_fha.hashmask);
+}
+
+SYSINIT(nfs_fha, SI_SUB_ROOT_CONF, SI_ORDER_ANY, nfs_fha_init, NULL);
+SYSUNINIT(nfs_fha, SI_SUB_ROOT_CONF, SI_ORDER_ANY, nfs_fha_uninit, NULL);
+
+/*
+ * This just specifies that offsets should obey affinity when within
+ * the same 1Mbyte (1<<20) chunk for the file (reads only for now).
+ */
+static void
+fha_extract_info(struct svc_req *req, struct fha_info *i)
+{
+ struct mbuf *md = req->rq_args;
+ fhandle_t fh;
+ caddr_t dpos = mtod(md, caddr_t);
+ static u_int64_t random_fh = 0;
+ int error;
+ int v3 = (req->rq_vers == 3);
+ u_int32_t *tl;
+ rpcproc_t procnum;
+
+ /*
+ * We start off with a random fh. If we get a reasonable
+ * procnum, we set the fh. If there's a concept of offset
+ * that we're interested in, we set that.
+ */
+ i->fh = ++random_fh;
+ i->offset = 0;
+ i->locktype = LK_EXCLUSIVE;
+
+ /*
+ * Extract the procnum and convert to v3 form if necessary.
+ */
+ procnum = req->rq_proc;
+ if (!v3)
+ procnum = nfsrv_nfsv3_procid[procnum];
+
+ /*
+ * We do affinity for most. However, we divide a realm of affinity
+ * by file offset so as to allow for concurrent random access. We
+ * only do this for reads today, but this may change when IFS supports
+ * efficient concurrent writes.
+ */
+ if (procnum == NFSPROC_FSSTAT ||
+ procnum == NFSPROC_FSINFO ||
+ procnum == NFSPROC_PATHCONF ||
+ procnum == NFSPROC_NOOP ||
+ procnum == NFSPROC_NULL)
+ goto out;
+
+ /* Grab the filehandle. */
+ error = nfsm_srvmtofh_xx(&fh, v3, &md, &dpos);
+ if (error)
+ goto out;
+
+ i->fh = *(const u_int64_t *)(fh.fh_fid.fid_data);
+
+ /* Content ourselves with zero offset for all but reads. */
+ if (procnum != NFSPROC_READ)
+ goto out;
+
+ if (v3) {
+ tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, &md, &dpos);
+ if (tl == NULL)
+ goto out;
+ i->offset = fxdr_hyper(tl);
+ } else {
+ tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, &md, &dpos);
+ if (tl == NULL)
+ goto out;
+ i->offset = fxdr_unsigned(u_int32_t, *tl);
+ }
+ out:
+ switch (procnum) {
+ case NFSPROC_NULL:
+ case NFSPROC_GETATTR:
+ case NFSPROC_LOOKUP:
+ case NFSPROC_ACCESS:
+ case NFSPROC_READLINK:
+ case NFSPROC_READ:
+ case NFSPROC_READDIR:
+ case NFSPROC_READDIRPLUS:
+ i->locktype = LK_SHARED;
+ break;
+ case NFSPROC_SETATTR:
+ case NFSPROC_WRITE:
+ case NFSPROC_CREATE:
+ case NFSPROC_MKDIR:
+ case NFSPROC_SYMLINK:
+ case NFSPROC_MKNOD:
+ case NFSPROC_REMOVE:
+ case NFSPROC_RMDIR:
+ case NFSPROC_RENAME:
+ case NFSPROC_LINK:
+ case NFSPROC_FSSTAT:
+ case NFSPROC_FSINFO:
+ case NFSPROC_PATHCONF:
+ case NFSPROC_COMMIT:
+ case NFSPROC_NOOP:
+ i->locktype = LK_EXCLUSIVE;
+ break;
+ }
+}
+
+static struct fha_hash_entry *
+fha_hash_entry_new(u_int64_t fh)
+{
+ struct fha_hash_entry *e;
+
+ e = malloc(sizeof(*e), M_NFS_FHA, M_WAITOK);
+ e->fh = fh;
+ e->num_reads = 0;
+ e->num_writes = 0;
+ e->num_threads = 0;
+ LIST_INIT(&e->threads);
+
+ return e;
+}
+
+static void
+fha_hash_entry_destroy(struct fha_hash_entry *e)
+{
+
+ if (e->num_reads + e->num_writes)
+ panic("nonempty fhe");
+ free(e, M_NFS_FHA);
+}
+
+static void
+fha_hash_entry_remove(struct fha_hash_entry *e)
+{
+
+ LIST_REMOVE(e, link);
+ fha_hash_entry_destroy(e);
+}
+
+static struct fha_hash_entry *
+fha_hash_entry_lookup(SVCPOOL *pool, u_int64_t fh)
+{
+ struct fha_hash_entry *fhe, *new_fhe;
+
+ LIST_FOREACH(fhe, &g_fha.hashtable[fh % g_fha.hashmask], link) {
+ if (fhe->fh == fh)
+ break;
+ }
+
+ if (!fhe) {
+ /* Allocate a new entry. */
+ mtx_unlock(&pool->sp_lock);
+ new_fhe = fha_hash_entry_new(fh);
+ mtx_lock(&pool->sp_lock);
+
+ /* Double-check to make sure we still need the new entry. */
+ LIST_FOREACH(fhe, &g_fha.hashtable[fh % g_fha.hashmask], link) {
+ if (fhe->fh == fh)
+ break;
+ }
+ if (!fhe) {
+ fhe = new_fhe;
+ LIST_INSERT_HEAD(&g_fha.hashtable[fh % g_fha.hashmask],
+ fhe, link);
+ } else {
+ fha_hash_entry_destroy(new_fhe);
+ }
+ }
+
+ return fhe;
+}
+
+static void
+fha_hash_entry_add_thread(struct fha_hash_entry *fhe, SVCTHREAD *thread)
+{
+ LIST_INSERT_HEAD(&fhe->threads, thread, st_alink);
+ fhe->num_threads++;
+}
+
+static void
+fha_hash_entry_remove_thread(struct fha_hash_entry *fhe, SVCTHREAD *thread)
+{
+
+ LIST_REMOVE(thread, st_alink);
+ fhe->num_threads--;
+}
+
+/*
+ * Account for an ongoing operation associated with this file.
+ */
+static void
+fha_hash_entry_add_op(struct fha_hash_entry *fhe, int locktype, int count)
+{
+
+ if (LK_EXCLUSIVE == locktype)
+ fhe->num_writes += count;
+ else
+ fhe->num_reads += count;
+}
+
+static SVCTHREAD *
+get_idle_thread(SVCPOOL *pool)
+{
+ SVCTHREAD *st;
+
+ LIST_FOREACH(st, &pool->sp_idlethreads, st_ilink) {
+ if (st->st_xprt == NULL && STAILQ_EMPTY(&st->st_reqs))
+ return (st);
+ }
+ return (NULL);
+}
+
+
+/*
+ * Get the service thread currently associated with the fhe that is
+ * appropriate to handle this operation.
+ */
+SVCTHREAD *
+fha_hash_entry_choose_thread(SVCPOOL *pool, struct fha_hash_entry *fhe,
+ struct fha_info *i, SVCTHREAD *this_thread);
+
+SVCTHREAD *
+fha_hash_entry_choose_thread(SVCPOOL *pool, struct fha_hash_entry *fhe,
+ struct fha_info *i, SVCTHREAD *this_thread)
+{
+ SVCTHREAD *thread, *min_thread = NULL;
+ int req_count, min_count = 0;
+ off_t offset1, offset2;
+
+ LIST_FOREACH(thread, &fhe->threads, st_alink) {
+ req_count = thread->st_reqcount;
+
+ /* If there are any writes in progress, use the first thread. */
+ if (fhe->num_writes) {
+#if 0
+ ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
+ "fha: %p(%d)w", thread, req_count);
+#endif
+ return (thread);
+ }
+
+ /*
+ * Check for read locality, making sure that we won't
+ * exceed our per-thread load limit in the process.
+ */
+ offset1 = i->offset >> fha_ctls.bin_shift;
+ offset2 = STAILQ_FIRST(&thread->st_reqs)->rq_p3
+ >> fha_ctls.bin_shift;
+ if (offset1 == offset2) {
+ if ((fha_ctls.max_reqs_per_nfsd == 0) ||
+ (req_count < fha_ctls.max_reqs_per_nfsd)) {
+#if 0
+ ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
+ "fha: %p(%d)r", thread, req_count);
+#endif
+ return (thread);
+ }
+ }
+
+ /*
+ * We don't have a locality match, so skip this thread,
+ * but keep track of the most attractive thread in case
+ * we need to come back to it later.
+ */
+#if 0
+ ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
+ "fha: %p(%d)s off1 %llu off2 %llu", thread,
+ req_count, offset1, offset2);
+#endif
+ if ((min_thread == NULL) || (req_count < min_count)) {
+ min_count = req_count;
+ min_thread = thread;
+ }
+ }
+
+ /*
+ * We didn't find a good match yet. See if we can add
+ * a new thread to this file handle entry's thread list.
+ */
+ if ((fha_ctls.max_nfsds_per_fh == 0) ||
+ (fhe->num_threads < fha_ctls.max_nfsds_per_fh)) {
+ /*
+ * We can add a new thread, so try for an idle thread
+ * first, and fall back to this_thread if none are idle.
+ */
+ if (STAILQ_EMPTY(&this_thread->st_reqs)) {
+ thread = this_thread;
+#if 0
+ ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
+ "fha: %p(%d)t", thread, thread->st_reqcount);
+#endif
+ } else if ((thread = get_idle_thread(pool))) {
+#if 0
+ ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
+ "fha: %p(%d)i", thread, thread->st_reqcount);
+#endif
+ } else {
+ thread = this_thread;
+#if 0
+ ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
+ "fha: %p(%d)b", thread, thread->st_reqcount);
+#endif
+ }
+ fha_hash_entry_add_thread(fhe, thread);
+ } else {
+ /*
+ * We don't want to use any more threads for this file, so
+ * go back to the most attractive nfsd we're already using.
+ */
+ thread = min_thread;
+ }
+
+ return (thread);
+}
+
+/*
+ * After getting a request, try to assign it to some thread. Usually we
+ * handle it ourselves.
+ */
+SVCTHREAD *
+fha_assign(SVCTHREAD *this_thread, struct svc_req *req)
+{
+ SVCPOOL *pool;
+ SVCTHREAD *thread;
+ struct fha_info i;
+ struct fha_hash_entry *fhe;
+
+ /*
+ * Only do placement if this is an NFS request.
+ */
+ if (req->rq_prog != NFS_PROG)
+ return (this_thread);
+
+ if (req->rq_vers != 2 && req->rq_vers != 3)
+ return (this_thread);
+
+ pool = req->rq_xprt->xp_pool;
+ fha_extract_info(req, &i);
+
+ /*
+ * We save the offset associated with this request for later
+ * nfsd matching.
+ */
+ fhe = fha_hash_entry_lookup(pool, i.fh);
+ req->rq_p1 = fhe;
+ req->rq_p2 = i.locktype;
+ req->rq_p3 = i.offset;
+
+ /*
+ * Choose a thread, taking into consideration locality, thread load,
+ * and the number of threads already working on this file.
+ */
+ thread = fha_hash_entry_choose_thread(pool, fhe, &i, this_thread);
+ KASSERT(thread, ("fha_assign: NULL thread!"));
+ fha_hash_entry_add_op(fhe, i.locktype, 1);
+
+ return (thread);
+}
+
+/*
+ * Called when we're done with an operation. The request has already
+ * been de-queued.
+ */
+void
+fha_nd_complete(SVCTHREAD *thread, struct svc_req *req)
+{
+ struct fha_hash_entry *fhe = req->rq_p1;
+
+ /*
+ * This may be called for reqs that didn't go through
+ * fha_assign (e.g. extra NULL ops used for RPCSEC_GSS.
+ */
+ if (!fhe)
+ return;
+
+ fha_hash_entry_add_op(fhe, req->rq_p2, -1);
+
+ if (thread->st_reqcount == 0) {
+ fha_hash_entry_remove_thread(fhe, thread);
+ if (0 == fhe->num_reads + fhe->num_writes)
+ fha_hash_entry_remove(fhe);
+ }
+}
+
+extern SVCPOOL *nfsrv_pool;
+
+static int
+fhe_stats_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ int error, count, i;
+ struct sbuf sb;
+ struct fha_hash_entry *fhe;
+ bool_t first = TRUE;
+ SVCTHREAD *thread;
+
+ sbuf_new(&sb, NULL, 4096, SBUF_FIXEDLEN);
+
+ if (!nfsrv_pool) {
+ sbuf_printf(&sb, "NFSD not running\n");
+ goto out;
+ }
+
+ mtx_lock(&nfsrv_pool->sp_lock);
+ count = 0;
+ for (i = 0; i <= g_fha.hashmask; i++)
+ if (!LIST_EMPTY(&g_fha.hashtable[i]))
+ count++;
+
+ if (count == 0) {
+ sbuf_printf(&sb, "No file handle entries.\n");
+ goto out;
+ }
+
+ for (i = 0; i <= g_fha.hashmask; i++) {
+ LIST_FOREACH(fhe, &g_fha.hashtable[i], link) {
+ sbuf_printf(&sb, "%sfhe %p: {\n", first ? "" : ", ", fhe);
+
+ sbuf_printf(&sb, " fh: %ju\n", (uintmax_t) fhe->fh);
+ sbuf_printf(&sb, " num_reads: %d\n", fhe->num_reads);
+ sbuf_printf(&sb, " num_writes: %d\n", fhe->num_writes);
+ sbuf_printf(&sb, " num_threads: %d\n", fhe->num_threads);
+
+ LIST_FOREACH(thread, &fhe->threads, st_alink) {
+ sbuf_printf(&sb, " thread %p (count %d)\n",
+ thread, thread->st_reqcount);
+ }
+
+ sbuf_printf(&sb, "}");
+ first = FALSE;
+
+ /* Limit the output. */
+ if (++count > 128) {
+ sbuf_printf(&sb, "...");
+ break;
+ }
+ }
+ }
+
+ out:
+ if (nfsrv_pool)
+ mtx_unlock(&nfsrv_pool->sp_lock);
+ sbuf_trim(&sb);
+ sbuf_finish(&sb);
+ error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
+ sbuf_delete(&sb);
+ return (error);
+}
+
+#endif /* !NFS_LEGACYRPC */
diff --git a/sys/nfsserver/nfs_fha.h b/sys/nfsserver/nfs_fha.h
new file mode 100644
index 0000000..e7344ed
--- /dev/null
+++ b/sys/nfsserver/nfs_fha.h
@@ -0,0 +1,28 @@
+/*-
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ *
+ * 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$ */
+
+void fha_nd_complete(SVCTHREAD *, struct svc_req *);
+SVCTHREAD *fha_assign(SVCTHREAD *, struct svc_req *);
diff --git a/sys/nfsserver/nfs_serv.c b/sys/nfsserver/nfs_serv.c
index a0a0308..d528769 100644
--- a/sys/nfsserver/nfs_serv.c
+++ b/sys/nfsserver/nfs_serv.c
@@ -142,8 +142,10 @@ SYSCTL_STRUCT(_vfs_nfsrv, NFS_NFSRVSTATS, nfsrvstats, CTLFLAG_RW,
static int nfsrv_access(struct vnode *, accmode_t, struct ucred *,
int, int);
+#ifdef NFS_LEGACYRPC
static void nfsrvw_coalesce(struct nfsrv_descript *,
struct nfsrv_descript *);
+#endif
/*
* Clear nameidata fields that are tested in nsfmout cleanup code prior
@@ -216,7 +218,7 @@ nfsrv3_access(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
fhp = &nfh.fh_generic;
nfsm_srvmtofh(fhp);
tl = nfsm_dissect_nonblock(u_int32_t *, NFSX_UNSIGNED);
- error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, nfsd, slp,
nam, &rdonly, TRUE);
if (error) {
nfsm_reply(NFSX_UNSIGNED);
@@ -283,7 +285,7 @@ nfsrv_getattr(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
vfslocked = 0;
fhp = &nfh.fh_generic;
nfsm_srvmtofh(fhp);
- error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, cred, slp, nam,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, nfsd, slp, nam,
&rdonly, TRUE);
if (error) {
nfsm_reply(0);
@@ -392,7 +394,7 @@ nfsrv_setattr(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
/*
* Now that we have all the fields, lets do it.
*/
- error = nfsrv_fhtovp(fhp, 1, &vp, &tvfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &tvfslocked, nfsd, slp,
nam, &rdonly, TRUE);
vfslocked = nfsrv_lockedpair(vfslocked, tvfslocked);
if (error) {
@@ -505,7 +507,7 @@ nfsrv_lookup(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = LOOKUP;
nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART | MPSAFE;
- error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, nfsd, fhp, len, slp, nam, &md, &dpos,
&dirp, v3, &dirattr, &dirattr_ret, pubflag);
vfslocked = NDHASGIANT(&nd);
@@ -715,7 +717,7 @@ nfsrv_readlink(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
uiop->uio_rw = UIO_READ;
uiop->uio_segflg = UIO_SYSSPACE;
uiop->uio_td = NULL;
- error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, nfsd, slp,
nam, &rdonly, TRUE);
if (error) {
nfsm_reply(2 * NFSX_UNSIGNED);
@@ -811,7 +813,7 @@ nfsrv_read(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
* as well.
*/
- error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, nfsd, slp,
nam, &rdonly, TRUE);
if (error) {
vp = NULL;
@@ -1112,7 +1114,7 @@ nfsrv_write(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
error = 0;
goto nfsmout;
}
- error = nfsrv_fhtovp(fhp, 1, &vp, &tvfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &tvfslocked, nfsd, slp,
nam, &rdonly, TRUE);
vfslocked = nfsrv_lockedpair(vfslocked, tvfslocked);
if (error) {
@@ -1227,6 +1229,16 @@ nfsmout:
return(error);
}
+#ifdef NFS_LEGACYRPC
+
+/*
+ * XXX dfr - write gathering isn't supported by the new RPC code since
+ * its really only useful for NFSv2. If there is a real need, we could
+ * attempt to fit it into the filehandle affinity system, e.g. by
+ * looking to see if there are queued write requests that overlap this
+ * one.
+ */
+
/*
* For the purposes of write gathering, we must decide if the credential
* associated with two pending requests have equivilent privileges. Since
@@ -1432,7 +1444,7 @@ loop1:
cred = nfsd->nd_cr;
v3 = (nfsd->nd_flag & ND_NFSV3);
forat_ret = aftat_ret = 1;
- error = nfsrv_fhtovp(&nfsd->nd_fh, 1, &vp, &vfslocked, cred,
+ error = nfsrv_fhtovp(&nfsd->nd_fh, 1, &vp, &vfslocked, nfsd,
slp, nfsd->nd_nam, &rdonly, TRUE);
if (!error) {
if (v3)
@@ -1634,6 +1646,8 @@ nfsrvw_coalesce(struct nfsrv_descript *owp, struct nfsrv_descript *nfsd)
}
}
+#endif
+
/*
* nfs create service
* now does a truncate to 0 length via. setattr if it already exists
@@ -1697,7 +1711,7 @@ nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
* be valid at all if an error occurs so we have to invalidate it
* prior to calling nfsm_reply ( which might goto nfsmout ).
*/
- error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, nfsd, fhp, len, slp, nam, &md, &dpos,
&dirp, v3, &dirfor, &dirfor_ret, FALSE);
vfslocked = nfsrv_lockedpair_nd(vfslocked, &nd);
if (dirp && !v3) {
@@ -1987,7 +2001,7 @@ nfsrv_mknod(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
* nfsmout.
*/
- error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, nfsd, fhp, len, slp, nam, &md, &dpos,
&dirp, v3, &dirfor, &dirfor_ret, FALSE);
vfslocked = nfsrv_lockedpair_nd(vfslocked, &nd);
if (error) {
@@ -2169,7 +2183,7 @@ nfsrv_remove(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = DELETE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | MPSAFE;
- error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, nfsd, fhp, len, slp, nam, &md, &dpos,
&dirp, v3, &dirfor, &dirfor_ret, FALSE);
vfslocked = nfsrv_lockedpair_nd(vfslocked, &nd);
if (dirp && !v3) {
@@ -2296,7 +2310,7 @@ nfsrv_rename(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
fromnd.ni_cnd.cn_cred = cred;
fromnd.ni_cnd.cn_nameiop = DELETE;
fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART | MPSAFE;
- error = nfs_namei(&fromnd, ffhp, len, slp, nam, &md,
+ error = nfs_namei(&fromnd, nfsd, ffhp, len, slp, nam, &md,
&dpos, &fdirp, v3, &fdirfor, &fdirfor_ret, FALSE);
vfslocked = nfsrv_lockedpair_nd(vfslocked, &fromnd);
if (fdirp && !v3) {
@@ -2319,7 +2333,7 @@ nfsrv_rename(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
tond.ni_cnd.cn_cred = cred;
tond.ni_cnd.cn_nameiop = RENAME;
tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | MPSAFE;
- error = nfs_namei(&tond, tfhp, len2, slp, nam, &md,
+ error = nfs_namei(&tond, nfsd, tfhp, len2, slp, nam, &md,
&dpos, &tdirp, v3, &tdirfor, &tdirfor_ret, FALSE);
vfslocked = nfsrv_lockedpair_nd(vfslocked, &tond);
if (tdirp && !v3) {
@@ -2512,7 +2526,7 @@ nfsrv_link(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nfsm_srvmtofh(dfhp);
nfsm_srvnamesiz(len);
- error = nfsrv_fhtovp(fhp, TRUE, &vp, &tvfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, TRUE, &vp, &tvfslocked, nfsd, slp,
nam, &rdonly, TRUE);
vfslocked = nfsrv_lockedpair(vfslocked, tvfslocked);
if (error) {
@@ -2535,7 +2549,7 @@ nfsrv_link(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT | MPSAFE | MPSAFE;
- error = nfs_namei(&nd, dfhp, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, nfsd, dfhp, len, slp, nam, &md, &dpos,
&dirp, v3, &dirfor, &dirfor_ret, FALSE);
vfslocked = nfsrv_lockedpair_nd(vfslocked, &nd);
if (dirp && !v3) {
@@ -2664,7 +2678,7 @@ nfsrv_symlink(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT | SAVESTART | MPSAFE;
- error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, nfsd, fhp, len, slp, nam, &md, &dpos,
&dirp, v3, &dirfor, &dirfor_ret, FALSE);
vfslocked = nfsrv_lockedpair_nd(vfslocked, &nd);
if (error == 0) {
@@ -2847,7 +2861,7 @@ nfsrv_mkdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT | MPSAFE;
- error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, nfsd, fhp, len, slp, nam, &md, &dpos,
&dirp, v3, &dirfor, &dirfor_ret, FALSE);
vfslocked = nfsrv_lockedpair_nd(vfslocked, &nd);
if (dirp && !v3) {
@@ -3005,7 +3019,7 @@ nfsrv_rmdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = DELETE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | MPSAFE;
- error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, nfsd, fhp, len, slp, nam, &md, &dpos,
&dirp, v3, &dirfor, &dirfor_ret, FALSE);
vfslocked = nfsrv_lockedpair_nd(vfslocked, &nd);
if (dirp && !v3) {
@@ -3180,7 +3194,7 @@ nfsrv_readdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
if (siz > xfer)
siz = xfer;
fullsiz = siz;
- error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, nfsd, slp,
nam, &rdonly, TRUE);
if (!error && vp->v_type != VDIR) {
error = ENOTDIR;
@@ -3474,7 +3488,7 @@ nfsrv_readdirplus(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
if (siz > xfer)
siz = xfer;
fullsiz = siz;
- error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, nfsd, slp,
nam, &rdonly, TRUE);
if (!error && vp->v_type != VDIR) {
error = ENOTDIR;
@@ -3815,7 +3829,7 @@ nfsrv_commit(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
off = fxdr_hyper(tl);
tl += 2;
cnt = fxdr_unsigned(int, *tl);
- error = nfsrv_fhtovp(fhp, 1, &vp, &tvfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &tvfslocked, nfsd, slp,
nam, &rdonly, TRUE);
vfslocked = nfsrv_lockedpair(vfslocked, tvfslocked);
if (error) {
@@ -3960,7 +3974,7 @@ nfsrv_statfs(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
vfslocked = 0;
fhp = &nfh.fh_generic;
nfsm_srvmtofh(fhp);
- error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, nfsd, slp,
nam, &rdonly, TRUE);
if (error) {
nfsm_reply(NFSX_UNSIGNED);
@@ -4055,7 +4069,7 @@ nfsrv_fsinfo(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
fhp = &nfh.fh_generic;
vfslocked = 0;
nfsm_srvmtofh(fhp);
- error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, nfsd, slp,
nam, &rdonly, TRUE);
if (error) {
nfsm_reply(NFSX_UNSIGNED);
@@ -4080,10 +4094,7 @@ nfsrv_fsinfo(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
* There should be filesystem VFS OP(s) to get this information.
* For now, assume ufs.
*/
- if (slp->ns_so->so_type == SOCK_DGRAM)
- pref = NFS_MAXDGRAMDATA;
- else
- pref = NFS_MAXDATA;
+ pref = NFS_SRVMAXDATA(nfsd);
sip->fs_rtmax = txdr_unsigned(pref);
sip->fs_rtpref = txdr_unsigned(pref);
sip->fs_rtmult = txdr_unsigned(NFS_FABLKSIZE);
@@ -4133,7 +4144,7 @@ nfsrv_pathconf(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
vfslocked = 0;
fhp = &nfh.fh_generic;
nfsm_srvmtofh(fhp);
- error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, cred, slp,
+ error = nfsrv_fhtovp(fhp, 1, &vp, &vfslocked, nfsd, slp,
nam, &rdonly, TRUE);
if (error) {
nfsm_reply(NFSX_UNSIGNED);
diff --git a/sys/nfsserver/nfs_srvcache.c b/sys/nfsserver/nfs_srvcache.c
index 1e36a45..5121690 100644
--- a/sys/nfsserver/nfs_srvcache.c
+++ b/sys/nfsserver/nfs_srvcache.c
@@ -57,6 +57,8 @@ __FBSDID("$FreeBSD$");
#include <nfsserver/nfs.h>
#include <nfsserver/nfsrvcache.h>
+#ifdef NFS_LEGACYRPC
+
static long numnfsrvcache;
static long desirednfsrvcache;
@@ -385,3 +387,5 @@ nfsrv_cleancache(void)
}
numnfsrvcache = 0;
}
+
+#endif /* NFS_LEGACYRPC */
diff --git a/sys/nfsserver/nfs_srvkrpc.c b/sys/nfsserver/nfs_srvkrpc.c
new file mode 100644
index 0000000..509dc58
--- /dev/null
+++ b/sys/nfsserver/nfs_srvkrpc.c
@@ -0,0 +1,565 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * 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.
+ * 4. 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 BY THE REGENTS 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 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.
+ *
+ * @(#)nfs_syscalls.c 8.5 (Berkeley) 3/30/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet6.h"
+#include "opt_kgssapi.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysproto.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/namei.h>
+#include <sys/fcntl.h>
+#include <sys/lockf.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef INET6
+#include <net/if.h>
+#include <netinet6/in6_var.h>
+#endif
+
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+#include <rpc/replay.h>
+
+#include <nfs/xdr_subs.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsserver/nfs.h>
+#include <nfsserver/nfsm_subs.h>
+#include <nfsserver/nfsrvcache.h>
+#include <nfsserver/nfs_fha.h>
+
+#ifndef NFS_LEGACYRPC
+
+static MALLOC_DEFINE(M_NFSSVC, "nfss_srvsock", "Nfs server structure");
+
+MALLOC_DEFINE(M_NFSRVDESC, "nfss_srvdesc", "NFS server socket descriptor");
+MALLOC_DEFINE(M_NFSD, "nfss_daemon", "Nfs server daemon structure");
+
+#define TRUE 1
+#define FALSE 0
+
+SYSCTL_DECL(_vfs_nfsrv);
+
+SVCPOOL *nfsrv_pool;
+int nfsd_waiting = 0;
+int nfsrv_numnfsd = 0;
+static int nfs_realign_test;
+static int nfs_realign_count;
+struct callout nfsrv_callout;
+static eventhandler_tag nfsrv_nmbclusters_tag;
+
+static int nfs_privport = 0;
+SYSCTL_INT(_vfs_nfsrv, NFS_NFSPRIVPORT, nfs_privport, CTLFLAG_RW,
+ &nfs_privport, 0,
+ "Only allow clients using a privileged port");
+SYSCTL_INT(_vfs_nfsrv, OID_AUTO, gatherdelay, CTLFLAG_RW,
+ &nfsrvw_procrastinate, 0,
+ "Delay value for write gathering");
+SYSCTL_INT(_vfs_nfsrv, OID_AUTO, gatherdelay_v3, CTLFLAG_RW,
+ &nfsrvw_procrastinate_v3, 0,
+ "Delay in seconds for NFSv3 write gathering");
+SYSCTL_INT(_vfs_nfsrv, OID_AUTO, realign_test, CTLFLAG_RW,
+ &nfs_realign_test, 0, "");
+SYSCTL_INT(_vfs_nfsrv, OID_AUTO, realign_count, CTLFLAG_RW,
+ &nfs_realign_count, 0, "");
+
+static int nfssvc_addsock(struct file *, struct thread *);
+static int nfssvc_nfsd(struct thread *, struct nfsd_nfsd_args *);
+
+extern u_long sb_max_adj;
+
+int32_t (*nfsrv3_procs[NFS_NPROCS])(struct nfsrv_descript *nd,
+ struct nfssvc_sock *slp, struct mbuf **mreqp) = {
+ nfsrv_null,
+ nfsrv_getattr,
+ nfsrv_setattr,
+ nfsrv_lookup,
+ nfsrv3_access,
+ nfsrv_readlink,
+ nfsrv_read,
+ nfsrv_write,
+ nfsrv_create,
+ nfsrv_mkdir,
+ nfsrv_symlink,
+ nfsrv_mknod,
+ nfsrv_remove,
+ nfsrv_rmdir,
+ nfsrv_rename,
+ nfsrv_link,
+ nfsrv_readdir,
+ nfsrv_readdirplus,
+ nfsrv_statfs,
+ nfsrv_fsinfo,
+ nfsrv_pathconf,
+ nfsrv_commit,
+ nfsrv_noop
+};
+
+/*
+ * NFS server system calls
+ */
+
+/*
+ * Nfs server psuedo system call for the nfsd's
+ * Based on the flag value it either:
+ * - adds a socket to the selection list
+ * - remains in the kernel as an nfsd
+ * - remains in the kernel as an nfsiod
+ * For INET6 we suppose that nfsd provides only IN6P_IPV6_V6ONLY sockets
+ * and that mountd provides
+ * - sockaddr with no IPv4-mapped addresses
+ * - mask for both INET and INET6 families if there is IPv4-mapped overlap
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct nfssvc_args {
+ int flag;
+ caddr_t argp;
+};
+#endif
+int
+nfssvc(struct thread *td, struct nfssvc_args *uap)
+{
+ struct file *fp;
+ struct nfsd_addsock_args addsockarg;
+ struct nfsd_nfsd_args nfsdarg;
+ int error;
+
+ KASSERT(!mtx_owned(&Giant), ("nfssvc(): called with Giant"));
+
+ error = priv_check(td, PRIV_NFS_DAEMON);
+ if (error)
+ return (error);
+ if (uap->flag & NFSSVC_ADDSOCK) {
+ error = copyin(uap->argp, (caddr_t)&addsockarg,
+ sizeof(addsockarg));
+ if (error)
+ return (error);
+ if ((error = fget(td, addsockarg.sock, &fp)) != 0)
+ return (error);
+ if (fp->f_type != DTYPE_SOCKET) {
+ fdrop(fp, td);
+ return (error); /* XXXRW: Should be EINVAL? */
+ }
+ error = nfssvc_addsock(fp, td);
+ fdrop(fp, td);
+ } else if (uap->flag & NFSSVC_OLDNFSD) {
+ error = nfssvc_nfsd(td, NULL);
+ } else if (uap->flag & NFSSVC_NFSD) {
+ if (!uap->argp)
+ return (EINVAL);
+ error = copyin(uap->argp, (caddr_t)&nfsdarg,
+ sizeof(nfsdarg));
+ if (error)
+ return (error);
+ error = nfssvc_nfsd(td, &nfsdarg);
+ } else {
+ error = ENXIO;
+ }
+ if (error == EINTR || error == ERESTART)
+ error = 0;
+ return (error);
+}
+
+/*
+ * Generate the rpc reply header
+ * siz arg. is used to decide if adding a cluster is worthwhile
+ */
+struct mbuf *
+nfs_rephead(int siz, struct nfsrv_descript *nd, int err,
+ struct mbuf **mbp, caddr_t *bposp)
+{
+ u_int32_t *tl;
+ struct mbuf *mreq;
+ caddr_t bpos;
+ struct mbuf *mb;
+
+ if (err == EBADRPC)
+ return (NULL);
+
+ nd->nd_repstat = err;
+ if (err && (nd->nd_flag & ND_NFSV3) == 0) /* XXX recheck */
+ siz = 0;
+
+ MGET(mreq, M_WAIT, MT_DATA);
+
+ /*
+ * If this is a big reply, use a cluster
+ */
+ mreq->m_len = 0;
+ if (siz >= MINCLSIZE) {
+ MCLGET(mreq, M_WAIT);
+ }
+ mb = mreq;
+ bpos = mtod(mb, caddr_t);
+
+ if (err != NFSERR_RETVOID) {
+ tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
+ if (err)
+ *tl = txdr_unsigned(nfsrv_errmap(nd, err));
+ else
+ *tl = 0;
+ }
+
+ *mbp = mb;
+ *bposp = bpos;
+ if (err != 0 && err != NFSERR_RETVOID)
+ nfsrvstats.srvrpc_errs++;
+
+ return (mreq);
+}
+
+/*
+ * nfs_realign:
+ *
+ * Check for badly aligned mbuf data and realign by copying the unaligned
+ * portion of the data into a new mbuf chain and freeing the portions
+ * of the old chain that were replaced.
+ *
+ * We cannot simply realign the data within the existing mbuf chain
+ * because the underlying buffers may contain other rpc commands and
+ * we cannot afford to overwrite them.
+ *
+ * We would prefer to avoid this situation entirely. The situation does
+ * not occur with NFS/UDP and is supposed to only occassionally occur
+ * with TCP. Use vfs.nfs.realign_count and realign_test to check this.
+ */
+static void
+nfs_realign(struct mbuf **pm) /* XXX COMMON */
+{
+ struct mbuf *m;
+ struct mbuf *n = NULL;
+ int off = 0;
+
+ ++nfs_realign_test;
+ while ((m = *pm) != NULL) {
+ if ((m->m_len & 0x3) || (mtod(m, intptr_t) & 0x3)) {
+ MGET(n, M_WAIT, MT_DATA);
+ if (m->m_len >= MINCLSIZE) {
+ MCLGET(n, M_WAIT);
+ }
+ n->m_len = 0;
+ break;
+ }
+ pm = &m->m_next;
+ }
+
+ /*
+ * If n is non-NULL, loop on m copying data, then replace the
+ * portion of the chain that had to be realigned.
+ */
+ if (n != NULL) {
+ ++nfs_realign_count;
+ while (m) {
+ m_copyback(n, off, m->m_len, mtod(m, caddr_t));
+ off += m->m_len;
+ m = m->m_next;
+ }
+ m_freem(*pm);
+ *pm = n;
+ }
+}
+
+static void
+nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
+{
+ rpcproc_t procnum;
+ int32_t (*proc)(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
+ struct mbuf **mreqp);
+ int flag;
+ struct nfsrv_descript nd;
+ struct mbuf *mreq, *mrep;
+ int error;
+
+ if (rqst->rq_vers == NFS_VER2) {
+ if (rqst->rq_proc > NFSV2PROC_STATFS) {
+ svcerr_noproc(rqst);
+ svc_freereq(rqst);
+ return;
+ }
+ procnum = nfsrv_nfsv3_procid[rqst->rq_proc];
+ flag = 0;
+ } else {
+ if (rqst->rq_proc >= NFS_NPROCS) {
+ svcerr_noproc(rqst);
+ svc_freereq(rqst);
+ return;
+ }
+ procnum = rqst->rq_proc;
+ flag = ND_NFSV3;
+ }
+ proc = nfsrv3_procs[procnum];
+
+ mreq = mrep = NULL;
+ mreq = rqst->rq_args;
+ rqst->rq_args = NULL;
+ nfs_realign(&mreq);
+
+ /*
+ * Note: we want rq_addr, not svc_getrpccaller -
+ * NFS_SRVMAXDATA uses a NULL value for nd_nam2 to detect TCP
+ * mounts.
+ */
+ memset(&nd, 0, sizeof(nd));
+ nd.nd_md = nd.nd_mrep = mreq;
+ nd.nd_dpos = mtod(mreq, caddr_t);
+ nd.nd_nam = (struct sockaddr *) &xprt->xp_ltaddr;
+ nd.nd_nam2 = rqst->rq_addr;
+ nd.nd_procnum = procnum;
+ nd.nd_cr = NULL;
+ nd.nd_flag = flag;
+
+ if (proc != nfsrv_null) {
+ if (!svc_getcred(rqst, &nd.nd_cr, &nd.nd_credflavor)) {
+ svcerr_weakauth(rqst);
+ svc_freereq(rqst);
+ return;
+ }
+#ifdef MAC
+ mac_cred_associate_nfsd(nd.nd_cr);
+#endif
+ }
+ nfsrvstats.srvrpccnt[nd.nd_procnum]++;
+
+ error = proc(&nd, NULL, &mrep);
+
+ if (nd.nd_cr)
+ crfree(nd.nd_cr);
+
+ if (mrep == NULL) {
+ svcerr_decode(rqst);
+ svc_freereq(rqst);
+ return;
+ }
+ if (error && error != NFSERR_RETVOID) {
+ svcerr_systemerr(rqst);
+ svc_freereq(rqst);
+ return;
+ }
+ if (!svc_sendreply_mbuf(rqst, mrep))
+ svcerr_systemerr(rqst);
+ svc_freereq(rqst);
+}
+
+/*
+ * Adds a socket to the list for servicing by nfsds.
+ */
+static int
+nfssvc_addsock(struct file *fp, struct thread *td)
+{
+ int siz;
+ struct socket *so;
+ int error;
+ SVCXPRT *xprt;
+
+ so = fp->f_data;
+
+ siz = sb_max_adj;
+ error = soreserve(so, siz, siz);
+ if (error) {
+ return (error);
+ }
+
+ /*
+ * Steal the socket from userland so that it doesn't close
+ * unexpectedly.
+ */
+ if (so->so_type == SOCK_DGRAM)
+ xprt = svc_dg_create(nfsrv_pool, so, 0, 0);
+ else
+ xprt = svc_vc_create(nfsrv_pool, so, 0, 0);
+ if (xprt) {
+ fp->f_ops = &badfileops;
+ fp->f_data = NULL;
+ svc_reg(xprt, NFS_PROG, NFS_VER2, nfssvc_program, NULL);
+ svc_reg(xprt, NFS_PROG, NFS_VER3, nfssvc_program, NULL);
+ }
+
+ return (0);
+}
+
+/*
+ * Called by nfssvc() for nfsds. Just loops around servicing rpc requests
+ * until it is killed by a signal.
+ */
+static int
+nfssvc_nfsd(struct thread *td, struct nfsd_nfsd_args *args)
+{
+#ifdef KGSSAPI
+ char principal[128];
+ int error;
+#endif
+
+#ifdef KGSSAPI
+ if (args) {
+ error = copyinstr(args->principal, principal,
+ sizeof(principal), NULL);
+ if (error)
+ return (error);
+ } else {
+ snprintf(principal, sizeof(principal), "nfs@%s", hostname);
+ }
+#endif
+
+ /*
+ * Only the first nfsd actually does any work. The RPC code
+ * adds threads to it as needed. Any extra processes offered
+ * by nfsd just exit. If nfsd is new enough, it will call us
+ * once with a structure that specifies how many threads to
+ * use.
+ */
+ NFSD_LOCK();
+ if (nfsrv_numnfsd == 0) {
+ nfsrv_numnfsd++;
+
+ NFSD_UNLOCK();
+
+#ifdef KGSSAPI
+ rpc_gss_set_svc_name(principal, "kerberosv5",
+ GSS_C_INDEFINITE, NFS_PROG, NFS_VER2);
+ rpc_gss_set_svc_name(principal, "kerberosv5",
+ GSS_C_INDEFINITE, NFS_PROG, NFS_VER3);
+#endif
+
+ if (args) {
+ nfsrv_pool->sp_minthreads = args->minthreads;
+ nfsrv_pool->sp_maxthreads = args->maxthreads;
+ } else {
+ nfsrv_pool->sp_minthreads = 4;
+ nfsrv_pool->sp_maxthreads = 4;
+ }
+
+ svc_run(nfsrv_pool);
+
+#ifdef KGSSAPI
+ rpc_gss_clear_svc_name(NFS_PROG, NFS_VER2);
+ rpc_gss_clear_svc_name(NFS_PROG, NFS_VER3);
+#endif
+
+ NFSD_LOCK();
+ nfsrv_numnfsd--;
+ nfsrv_init(TRUE);
+ }
+ NFSD_UNLOCK();
+
+ return (0);
+}
+
+/*
+ * Size the NFS server's duplicate request cache at 1/2 the
+ * nmbclusters, floating within a (64, 2048) range. This is to
+ * prevent all mbuf clusters being tied up in the NFS dupreq
+ * cache for small values of nmbclusters.
+ */
+static size_t
+nfsrv_replay_size(void)
+{
+ size_t replaysiz;
+
+ replaysiz = nmbclusters / 2;
+ if (replaysiz > NFSRVCACHE_MAX_SIZE)
+ replaysiz = NFSRVCACHE_MAX_SIZE;
+ if (replaysiz < NFSRVCACHE_MIN_SIZE)
+ replaysiz = NFSRVCACHE_MIN_SIZE;
+ replaysiz *= MCLBYTES;
+
+ return (replaysiz);
+}
+
+/*
+ * Called when nmbclusters changes - we resize the replay cache
+ * accordingly.
+ */
+static void
+nfsrv_nmbclusters_change(void *tag)
+{
+
+ if (nfsrv_pool)
+ replay_setsize(nfsrv_pool->sp_rcache, nfsrv_replay_size());
+}
+
+/*
+ * Initialize the data structures for the server.
+ * Handshake with any new nfsds starting up to avoid any chance of
+ * corruption.
+ */
+void
+nfsrv_init(int terminating)
+{
+
+ NFSD_LOCK_ASSERT();
+
+ if (terminating) {
+ NFSD_UNLOCK();
+ EVENTHANDLER_DEREGISTER(nmbclusters_change,
+ nfsrv_nmbclusters_tag);
+ svcpool_destroy(nfsrv_pool);
+ nfsrv_pool = NULL;
+ NFSD_LOCK();
+ } else
+ nfs_pub.np_valid = 0;
+
+ NFSD_UNLOCK();
+
+ nfsrv_pool = svcpool_create("nfsd", SYSCTL_STATIC_CHILDREN(_vfs_nfsrv));
+ nfsrv_pool->sp_rcache = replay_newcache(nfsrv_replay_size());
+ nfsrv_pool->sp_assign = fha_assign;
+ nfsrv_pool->sp_done = fha_nd_complete;
+ nfsrv_nmbclusters_tag = EVENTHANDLER_REGISTER(nmbclusters_change,
+ nfsrv_nmbclusters_change, NULL, EVENTHANDLER_PRI_FIRST);
+
+ NFSD_LOCK();
+}
+
+#endif /* !NFS_LEGACYRPC */
diff --git a/sys/nfsserver/nfs_srvsock.c b/sys/nfsserver/nfs_srvsock.c
index f4362f4..6f42e31 100644
--- a/sys/nfsserver/nfs_srvsock.c
+++ b/sys/nfsserver/nfs_srvsock.c
@@ -70,6 +70,8 @@ __FBSDID("$FreeBSD$");
#include <security/mac/mac_framework.h>
+#ifdef NFS_LEGACYRPC
+
#define TRUE 1
#define FALSE 0
@@ -383,6 +385,7 @@ nfs_getreq(struct nfsrv_descript *nd, struct nfsd *nfsd, int has_header)
}
if (len > 0)
nfsm_adv(nfsm_rndup(len));
+ nd->nd_credflavor = RPCAUTH_UNIX;
} else {
nd->nd_repstat = (NFSERR_AUTHERR | AUTH_REJECTCRED);
nd->nd_procnum = NFSPROC_NOOP;
@@ -809,3 +812,5 @@ nfsrv_timer(void *arg)
NFSD_UNLOCK();
callout_reset(&nfsrv_callout, nfsrv_ticks, nfsrv_timer, NULL);
}
+
+#endif /* NFS_LEGACYRPC */
diff --git a/sys/nfsserver/nfs_srvsubs.c b/sys/nfsserver/nfs_srvsubs.c
index d738fdf..2cce8be 100644
--- a/sys/nfsserver/nfs_srvsubs.c
+++ b/sys/nfsserver/nfs_srvsubs.c
@@ -93,10 +93,12 @@ static const nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR,
int nfsrv_ticks;
+#ifdef NFS_LEGACYRPC
struct nfssvc_sockhead nfssvc_sockhead;
int nfssvc_sockhead_flag;
struct nfsd_head nfsd_head;
int nfsd_head_flag;
+#endif
static int nfssvc_offset = SYS_nfssvc;
static struct sysent nfssvc_prev_sysent;
@@ -545,12 +547,18 @@ nfsrv_modevent(module_t mod, int type, void *data)
if (nfsrv_ticks < 1)
nfsrv_ticks = 1;
+#ifdef NFS_LEGACYRPC
nfsrv_initcache(); /* Init the server request cache */
NFSD_LOCK();
nfsrv_init(0); /* Init server data structures */
callout_init(&nfsrv_callout, CALLOUT_MPSAFE);
NFSD_UNLOCK();
nfsrv_timer(0);
+#else
+ NFSD_LOCK();
+ nfsrv_init(0); /* Init server data structures */
+ NFSD_UNLOCK();
+#endif
error = syscall_register(&nfssvc_offset, &nfssvc_sysent,
&nfssvc_prev_sysent);
@@ -568,7 +576,9 @@ nfsrv_modevent(module_t mod, int type, void *data)
if (registered)
syscall_deregister(&nfssvc_offset, &nfssvc_prev_sysent);
callout_drain(&nfsrv_callout);
+#ifdef NFS_LEGACYRPC
nfsrv_destroycache(); /* Free the server request cache */
+#endif
mtx_destroy(&nfsd_mtx);
break;
default:
@@ -604,8 +614,9 @@ MODULE_VERSION(nfsserver, 1);
* released by the caller.
*/
int
-nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
- struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
+nfs_namei(struct nameidata *ndp, struct nfsrv_descript *nfsd,
+ fhandle_t *fhp, int len, struct nfssvc_sock *slp,
+ struct sockaddr *nam, struct mbuf **mdp,
caddr_t *dposp, struct vnode **retdirp, int v3, struct vattr *retdirattrp,
int *retdirattr_retp, int pubflag)
{
@@ -667,7 +678,7 @@ nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
* Extract and set starting directory.
*/
error = nfsrv_fhtovp(fhp, FALSE, &dp, &dvfslocked,
- ndp->ni_cnd.cn_cred, slp, nam, &rdonly, pubflag);
+ nfsd, slp, nam, &rdonly, pubflag);
if (error)
goto out;
vfslocked = VFS_LOCK_GIANT(dp->v_mount);
@@ -1079,17 +1090,21 @@ nfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
*/
int
nfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp, int *vfslockedp,
- struct ucred *cred, struct nfssvc_sock *slp, struct sockaddr *nam,
- int *rdonlyp, int pubflag)
+ struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
+ struct sockaddr *nam, int *rdonlyp, int pubflag)
{
struct mount *mp;
int i;
- struct ucred *credanon;
+ struct ucred *cred, *credanon;
int error, exflags;
#ifdef MNT_EXNORESPORT /* XXX needs mountd and /etc/exports help yet */
struct sockaddr_int *saddr;
#endif
+ int credflavor;
int vfslocked;
+ int numsecflavors, *secflavors;
+ int v3 = nfsd->nd_flag & ND_NFSV3;
+ int mountreq;
*vfslockedp = 0;
*vpp = NULL;
@@ -1104,9 +1119,35 @@ nfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp, int *vfslockedp,
if (!mp)
return (ESTALE);
vfslocked = VFS_LOCK_GIANT(mp);
- error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
+ error = VFS_CHECKEXP(mp, nam, &exflags, &credanon,
+ &numsecflavors, &secflavors);
if (error)
goto out;
+ credflavor = nfsd->nd_credflavor;
+ for (i = 0; i < numsecflavors; i++) {
+ if (secflavors[i] == credflavor)
+ break;
+ }
+ if (i == numsecflavors) {
+ /*
+ * RFC 2623 section 2.3.2 - allow certain procedures
+ * used at NFS client mount time even if they have
+ * weak authentication.
+ */
+ mountreq = FALSE;
+ if (v3) {
+ if (nfsd->nd_procnum == NFSPROC_FSINFO)
+ mountreq = TRUE;
+ } else {
+ if (nfsd->nd_procnum == NFSPROC_FSSTAT
+ || nfsd->nd_procnum == NFSPROC_GETATTR)
+ mountreq = TRUE;
+ }
+ if (!mountreq) {
+ error = NFSERR_AUTHERR | AUTH_REJECTCRED;
+ goto out;
+ }
+ }
error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
if (error)
goto out;
@@ -1126,6 +1167,7 @@ nfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp, int *vfslockedp,
/*
* Check/setup credentials.
*/
+ cred = nfsd->nd_cr;
if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
cred->cr_uid = credanon->cr_uid;
for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
@@ -1168,6 +1210,8 @@ nfs_ispublicfh(fhandle_t *fhp)
return (TRUE);
}
+#ifdef NFS_LEGACYRPC
+
/*
* This function compares two net addresses by family and returns TRUE
* if they are the same host.
@@ -1210,6 +1254,8 @@ netaddr_match(int family, union nethostaddr *haddr, struct sockaddr *nam)
return (0);
}
+#endif
+
/*
* Map errnos to NFS error numbers. For Version 3 also filter out error
* numbers not specified for the associated procedure.
@@ -1364,13 +1410,12 @@ nfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
}
int
-nfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd, struct mbuf **md,
- caddr_t *dpos)
+nfsm_srvmtofh_xx(fhandle_t *f, int v3, struct mbuf **md, caddr_t *dpos)
{
u_int32_t *tl;
int fhlen;
- if (nfsd->nd_flag & ND_NFSV3) {
+ if (v3) {
tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
if (tl == NULL)
return EBADRPC;
diff --git a/sys/nfsserver/nfs_syscalls.c b/sys/nfsserver/nfs_syscalls.c
index d5e5e67..0c936c9 100644
--- a/sys/nfsserver/nfs_syscalls.c
+++ b/sys/nfsserver/nfs_syscalls.c
@@ -73,6 +73,8 @@ __FBSDID("$FreeBSD$");
#include <nfsserver/nfsm_subs.h>
#include <nfsserver/nfsrvcache.h>
+#ifdef NFS_LEGACYRPC
+
static MALLOC_DEFINE(M_NFSSVC, "nfss_srvsock", "Nfs server structure");
MALLOC_DEFINE(M_NFSRVDESC, "nfss_srvdesc", "NFS server socket descriptor");
@@ -130,7 +132,7 @@ nfssvc(struct thread *td, struct nfssvc_args *uap)
{
struct file *fp;
struct sockaddr *nam;
- struct nfsd_args nfsdarg;
+ struct nfsd_addsock_args nfsdarg;
int error;
KASSERT(!mtx_owned(&Giant), ("nfssvc(): called with Giant"));
@@ -170,7 +172,7 @@ nfssvc(struct thread *td, struct nfssvc_args *uap)
}
error = nfssvc_addsock(fp, nam);
fdrop(fp, td);
- } else if (uap->flag & NFSSVC_NFSD) {
+ } else if (uap->flag & NFSSVC_OLDNFSD) {
error = nfssvc_nfsd();
} else {
error = ENXIO;
@@ -727,3 +729,5 @@ nfsrv_init(int terminating)
TAILQ_INSERT_TAIL(&nfssvc_sockhead, nfs_cltpsock, ns_chain);
#endif
}
+
+#endif /* NFS_LEGACYRPC */
diff --git a/sys/nfsserver/nfsm_subs.h b/sys/nfsserver/nfsm_subs.h
index f2dafc4..fc9b76d 100644
--- a/sys/nfsserver/nfsm_subs.h
+++ b/sys/nfsserver/nfsm_subs.h
@@ -75,8 +75,7 @@
int nfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos);
int nfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos);
int nfsm_srvnamesiz0_xx(int *s, int m, struct mbuf **md, caddr_t *dpos);
-int nfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd,
- struct mbuf **md, caddr_t *dpos);
+int nfsm_srvmtofh_xx(fhandle_t *f, int v3, struct mbuf **md, caddr_t *dpos);
int nfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos);
#define nfsm_srvstrsiz(s, m) \
@@ -112,7 +111,7 @@ do { \
#define nfsm_srvmtofh(f) \
do { \
int t1; \
- t1 = nfsm_srvmtofh_xx((f), nfsd, &md, &dpos); \
+ t1 = nfsm_srvmtofh_xx((f), nfsd->nd_flag & ND_NFSV3, &md, &dpos); \
if (t1) { \
error = t1; \
nfsm_reply(0); \
diff --git a/sys/nfsserver/nfsrvcache.h b/sys/nfsserver/nfsrvcache.h
index 66176f4..9c527e9 100644
--- a/sys/nfsserver/nfsrvcache.h
+++ b/sys/nfsserver/nfsrvcache.h
@@ -44,6 +44,8 @@
#define NFSRVCACHE_MAX_SIZE 2048
#define NFSRVCACHE_MIN_SIZE 64
+#ifdef NFS_LEGACYRPC
+
struct nfsrvcache {
TAILQ_ENTRY(nfsrvcache) rc_lru; /* LRU chain */
LIST_ENTRY(nfsrvcache) rc_hash; /* Hash chain */
@@ -83,3 +85,5 @@ struct nfsrvcache {
#define RC_NAM 0x40
#endif
+
+#endif
diff --git a/sys/nlm/nlm.h b/sys/nlm/nlm.h
index addd07e..27b921f 100644
--- a/sys/nlm/nlm.h
+++ b/sys/nlm/nlm.h
@@ -93,7 +93,7 @@ extern void nlm_host_release(struct nlm_host *host);
* Return an RPC client handle that can be used to talk to the NLM
* running on the given host.
*/
-extern CLIENT *nlm_host_get_rpc(struct nlm_host *host);
+extern CLIENT *nlm_host_get_rpc(struct nlm_host *host, bool_t isserver);
/*
* Return the system ID for a host.
diff --git a/sys/nlm/nlm_advlock.c b/sys/nlm/nlm_advlock.c
index 5d1cc83..2c1f1a6 100644
--- a/sys/nlm/nlm_advlock.c
+++ b/sys/nlm/nlm_advlock.c
@@ -267,6 +267,7 @@ nlm_advlock_internal(struct vnode *vp, void *id, int op, struct flock *fl,
ext.rc_feedback = nlm_feedback;
ext.rc_feedback_arg = &nf;
+ ext.rc_timers = NULL;
ns = NULL;
if (flags & F_FLOCK) {
@@ -753,7 +754,7 @@ nlm_setlock(struct nlm_host *host, struct rpc_callextra *ext,
retry = 5*hz;
for (;;) {
- client = nlm_host_get_rpc(host);
+ client = nlm_host_get_rpc(host, FALSE);
if (!client)
return (ENOLCK); /* XXX retry? */
@@ -834,7 +835,7 @@ nlm_setlock(struct nlm_host *host, struct rpc_callextra *ext,
cancel.alock = args.alock;
do {
- client = nlm_host_get_rpc(host);
+ client = nlm_host_get_rpc(host, FALSE);
if (!client)
/* XXX retry? */
return (ENOLCK);
@@ -942,7 +943,7 @@ nlm_clearlock(struct nlm_host *host, struct rpc_callextra *ext,
return (error);
for (;;) {
- client = nlm_host_get_rpc(host);
+ client = nlm_host_get_rpc(host, FALSE);
if (!client)
return (ENOLCK); /* XXX retry? */
@@ -1023,7 +1024,7 @@ nlm_getlock(struct nlm_host *host, struct rpc_callextra *ext,
args.exclusive = exclusive;
for (;;) {
- client = nlm_host_get_rpc(host);
+ client = nlm_host_get_rpc(host, FALSE);
if (!client)
return (ENOLCK); /* XXX retry? */
diff --git a/sys/nlm/nlm_prot_impl.c b/sys/nlm/nlm_prot_impl.c
index 831d330..1add718 100644
--- a/sys/nlm/nlm_prot_impl.c
+++ b/sys/nlm/nlm_prot_impl.c
@@ -26,6 +26,7 @@
*/
#include "opt_inet6.h"
+#include "opt_nfs.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@@ -205,6 +206,12 @@ enum nlm_host_state {
NLM_MONITOR_FAILED,
NLM_RECOVERING
};
+
+struct nlm_rpc {
+ CLIENT *nr_client; /* (l) RPC client handle */
+ time_t nr_create_time; /* (l) when client was created */
+};
+
struct nlm_host {
struct mtx nh_lock;
volatile u_int nh_refs; /* (a) reference count */
@@ -213,12 +220,12 @@ struct nlm_host {
uint32_t nh_sysid; /* (c) our allocaed system ID */
char nh_sysid_string[10]; /* (c) string rep. of sysid */
struct sockaddr_storage nh_addr; /* (s) remote address of host */
- CLIENT *nh_rpc; /* (l) RPC handle to send to host */
+ struct nlm_rpc nh_srvrpc; /* (l) RPC for server replies */
+ struct nlm_rpc nh_clntrpc; /* (l) RPC for client requests */
rpcvers_t nh_vers; /* (s) NLM version of host */
int nh_state; /* (s) last seen NSM state of host */
enum nlm_host_state nh_monstate; /* (l) local NSM monitoring state */
time_t nh_idle_timeout; /* (s) Time at which host is idle */
- time_t nh_rpc_create_time; /* (s) Time we create RPC client */
struct sysctl_ctx_list nh_sysctl; /* (c) vfs.nlm.sysid nodes */
struct nlm_async_lock_list nh_pending; /* (l) pending async locks */
struct nlm_async_lock_list nh_finished; /* (l) finished async locks */
@@ -283,7 +290,7 @@ nlm_copy_netobj(struct netobj *dst, struct netobj *src,
static CLIENT *
nlm_get_rpc(struct sockaddr *sa, rpcprog_t prog, rpcvers_t vers)
{
- const char *wchan = "nlmrcv";
+ char *wchan = "nlmrcv";
const char* protofmly;
struct sockaddr_storage ss;
struct socket *so;
@@ -472,7 +479,7 @@ again:
rpcb = clnt_reconnect_create(nconf, (struct sockaddr *)&ss,
prog, vers, 0, 0);
- CLNT_CONTROL(rpcb, CLSET_WAITCHAN, &wchan);
+ CLNT_CONTROL(rpcb, CLSET_WAITCHAN, wchan);
rpcb->cl_auth = nlm_auth;
} else {
@@ -482,7 +489,7 @@ again:
CLNT_CONTROL(rpcb, CLSET_SVC_ADDR, &ss);
CLNT_CONTROL(rpcb, CLSET_PROG, &prog);
CLNT_CONTROL(rpcb, CLSET_VERS, &vers);
- CLNT_CONTROL(rpcb, CLSET_WAITCHAN, &wchan);
+ CLNT_CONTROL(rpcb, CLSET_WAITCHAN, wchan);
rpcb->cl_auth = nlm_auth;
}
@@ -646,13 +653,17 @@ nlm_host_destroy(struct nlm_host *host)
TAILQ_REMOVE(&nlm_hosts, host, nh_link);
mtx_unlock(&nlm_global_lock);
- if (host->nh_rpc)
- CLNT_RELEASE(host->nh_rpc);
+ if (host->nh_srvrpc.nr_client)
+ CLNT_RELEASE(host->nh_srvrpc.nr_client);
+ if (host->nh_clntrpc.nr_client)
+ CLNT_RELEASE(host->nh_clntrpc.nr_client);
mtx_destroy(&host->nh_lock);
sysctl_ctx_free(&host->nh_sysctl);
free(host, M_NLM);
}
+#ifdef NFSCLIENT
+
/*
* Thread start callback for client lock recovery
*/
@@ -677,6 +688,8 @@ nlm_client_recovery_start(void *arg)
kthread_exit();
}
+#endif
+
/*
* This is called when we receive a host state change notification. We
* unlock any active locks owned by the host. When rpc.lockd is
@@ -716,6 +729,7 @@ nlm_host_notify(struct nlm_host *host, int newstate)
lf_clearremotesys(host->nh_sysid);
host->nh_state = newstate;
+#ifdef NFSCLIENT
/*
* If we have any remote locks for this host (i.e. it
* represents a remote NFS server that our local NFS client
@@ -730,6 +744,7 @@ nlm_host_notify(struct nlm_host *host, int newstate)
kthread_add(nlm_client_recovery_start, host, curproc, &td, 0, 0,
"NFS lock recovery for %s", host->nh_caller_name);
}
+#endif
}
/*
@@ -783,7 +798,6 @@ nlm_create_host(const char* caller_name)
host->nh_sysid = nlm_next_sysid++;
snprintf(host->nh_sysid_string, sizeof(host->nh_sysid_string),
"%d", host->nh_sysid);
- host->nh_rpc = NULL;
host->nh_vers = 0;
host->nh_state = 0;
host->nh_monstate = NLM_UNMONITORED;
@@ -933,15 +947,15 @@ nlm_find_host_by_name(const char *name, const struct sockaddr *addr,
* have an RPC client handle, make sure the address is
* the same, otherwise discard the client handle.
*/
- if (host->nh_addr.ss_len && host->nh_rpc) {
+ if (host->nh_addr.ss_len && host->nh_srvrpc.nr_client) {
if (!nlm_compare_addr(
(struct sockaddr *) &host->nh_addr,
addr)
|| host->nh_vers != vers) {
CLIENT *client;
mtx_lock(&host->nh_lock);
- client = host->nh_rpc;
- host->nh_rpc = NULL;
+ client = host->nh_srvrpc.nr_client;
+ host->nh_srvrpc.nr_client = NULL;
mtx_unlock(&host->nh_lock);
if (client) {
CLNT_RELEASE(client);
@@ -1173,12 +1187,18 @@ nlm_host_monitor(struct nlm_host *host, int state)
* running on the given host.
*/
CLIENT *
-nlm_host_get_rpc(struct nlm_host *host)
+nlm_host_get_rpc(struct nlm_host *host, bool_t isserver)
{
+ struct nlm_rpc *rpc;
CLIENT *client;
mtx_lock(&host->nh_lock);
+ if (isserver)
+ rpc = &host->nh_srvrpc;
+ else
+ rpc = &host->nh_clntrpc;
+
/*
* We can't hold onto RPC handles for too long - the async
* call/reply protocol used by some NLM clients makes it hard
@@ -1187,33 +1207,33 @@ nlm_host_get_rpc(struct nlm_host *host)
* holding any locks, it won't bother to notify us. We
* expire the RPC handles after two minutes.
*/
- if (host->nh_rpc && time_uptime > host->nh_rpc_create_time + 2*60) {
- client = host->nh_rpc;
- host->nh_rpc = NULL;
+ if (rpc->nr_client && time_uptime > rpc->nr_create_time + 2*60) {
+ client = rpc->nr_client;
+ rpc->nr_client = NULL;
mtx_unlock(&host->nh_lock);
CLNT_RELEASE(client);
mtx_lock(&host->nh_lock);
}
- if (!host->nh_rpc) {
+ if (!rpc->nr_client) {
mtx_unlock(&host->nh_lock);
client = nlm_get_rpc((struct sockaddr *)&host->nh_addr,
NLM_PROG, host->nh_vers);
mtx_lock(&host->nh_lock);
if (client) {
- if (host->nh_rpc) {
+ if (rpc->nr_client) {
mtx_unlock(&host->nh_lock);
CLNT_DESTROY(client);
mtx_lock(&host->nh_lock);
} else {
- host->nh_rpc = client;
- host->nh_rpc_create_time = time_uptime;
+ rpc->nr_client = client;
+ rpc->nr_create_time = time_uptime;
}
}
}
- client = host->nh_rpc;
+ client = rpc->nr_client;
if (client)
CLNT_ACQUIRE(client);
mtx_unlock(&host->nh_lock);
@@ -1439,8 +1459,10 @@ nlm_server_main(int addr_count, char **addrs)
enum clnt_stat stat;
struct nlm_host *host, *nhost;
struct nlm_waiting_lock *nw;
+#ifdef NFSCLIENT
vop_advlock_t *old_nfs_advlock;
vop_reclaim_t *old_nfs_reclaim;
+#endif
int v4_used;
#ifdef INET6
int v6_used;
@@ -1512,7 +1534,7 @@ nlm_server_main(int addr_count, char **addrs)
goto out;
}
- pool = svcpool_create();
+ pool = svcpool_create("NLM", NULL);
error = nlm_register_services(pool, addr_count, addrs);
if (error)
@@ -1541,16 +1563,20 @@ nlm_server_main(int addr_count, char **addrs)
printf("NLM: local NSM state is %d\n", smstat.state);
nlm_nsm_state = smstat.state;
+#ifdef NFSCLIENT
old_nfs_advlock = nfs_advlock_p;
nfs_advlock_p = nlm_advlock;
old_nfs_reclaim = nfs_reclaim_p;
nfs_reclaim_p = nlm_reclaim;
+#endif
svc_run(pool);
error = 0;
+#ifdef NFSCLIENT
nfs_advlock_p = old_nfs_advlock;
nfs_reclaim_p = old_nfs_reclaim;
+#endif
out:
if (pool)
@@ -1595,7 +1621,8 @@ out:
}
TAILQ_FOREACH_SAFE(host, &nlm_hosts, nh_link, nhost) {
mtx_lock(&host->nh_lock);
- if (host->nh_rpc) {
+ if (host->nh_srvrpc.nr_client
+ || host->nh_clntrpc.nr_client) {
if (host->nh_addr.ss_family == AF_INET)
v4_used++;
#ifdef INET6
@@ -1607,7 +1634,12 @@ out:
* correctly with the fact that a socket may
* be used by many rpc handles.
*/
- CLNT_CONTROL(host->nh_rpc, CLSET_FD_CLOSE, 0);
+ if (host->nh_srvrpc.nr_client)
+ CLNT_CONTROL(host->nh_srvrpc.nr_client,
+ CLSET_FD_CLOSE, 0);
+ if (host->nh_clntrpc.nr_client)
+ CLNT_CONTROL(host->nh_clntrpc.nr_client,
+ CLSET_FD_CLOSE, 0);
}
mtx_unlock(&host->nh_lock);
}
@@ -1687,11 +1719,10 @@ static int
nlm_get_vfs_state(struct nlm_host *host, struct svc_req *rqstp,
fhandle_t *fhp, struct vfs_state *vs)
{
- int error, exflags, freecred;
+ int error, exflags;
struct ucred *cred = NULL, *credanon;
memset(vs, 0, sizeof(*vs));
- freecred = FALSE;
vs->vs_mp = vfs_getvfs(&fhp->fh_fsid);
if (!vs->vs_mp) {
@@ -1700,7 +1731,7 @@ nlm_get_vfs_state(struct nlm_host *host, struct svc_req *rqstp,
vs->vs_vfslocked = VFS_LOCK_GIANT(vs->vs_mp);
error = VFS_CHECKEXP(vs->vs_mp, (struct sockaddr *)&host->nh_addr,
- &exflags, &credanon);
+ &exflags, &credanon, NULL, NULL);
if (error)
goto out;
@@ -1714,16 +1745,13 @@ nlm_get_vfs_state(struct nlm_host *host, struct svc_req *rqstp,
goto out;
vs->vs_vnlocked = TRUE;
- cred = crget();
- freecred = TRUE;
- if (!svc_getcred(rqstp, cred, NULL)) {
+ if (!svc_getcred(rqstp, &cred, NULL)) {
error = EINVAL;
goto out;
}
if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
crfree(cred);
- cred = credanon;
- freecred = FALSE;
+ cred = crhold(credanon);
}
/*
@@ -1741,7 +1769,7 @@ nlm_get_vfs_state(struct nlm_host *host, struct svc_req *rqstp,
vs->vs_vnlocked = FALSE;
out:
- if (freecred)
+ if (cred)
crfree(cred);
return (error);
@@ -1788,7 +1816,7 @@ nlm_do_test(nlm4_testargs *argp, nlm4_testres *result, struct svc_req *rqstp,
memset(&vs, 0, sizeof(vs));
host = nlm_find_host_by_name(argp->alock.caller_name,
- (struct sockaddr *) rqstp->rq_xprt->xp_rtaddr.buf, rqstp->rq_vers);
+ svc_getrpccaller(rqstp), rqstp->rq_vers);
if (!host) {
result->stat.stat = nlm4_denied_nolocks;
return (ENOMEM);
@@ -1866,7 +1894,7 @@ nlm_do_test(nlm4_testargs *argp, nlm4_testres *result, struct svc_req *rqstp,
out:
nlm_release_vfs_state(&vs);
if (rpcp)
- *rpcp = nlm_host_get_rpc(host);
+ *rpcp = nlm_host_get_rpc(host, TRUE);
nlm_host_release(host);
return (0);
}
@@ -1885,7 +1913,7 @@ nlm_do_lock(nlm4_lockargs *argp, nlm4_res *result, struct svc_req *rqstp,
memset(&vs, 0, sizeof(vs));
host = nlm_find_host_by_name(argp->alock.caller_name,
- (struct sockaddr *) rqstp->rq_xprt->xp_rtaddr.buf, rqstp->rq_vers);
+ svc_getrpccaller(rqstp), rqstp->rq_vers);
if (!host) {
result->stat.stat = nlm4_denied_nolocks;
return (ENOMEM);
@@ -1937,7 +1965,7 @@ nlm_do_lock(nlm4_lockargs *argp, nlm4_res *result, struct svc_req *rqstp,
/*
* First, make sure we can contact the host's NLM.
*/
- client = nlm_host_get_rpc(host);
+ client = nlm_host_get_rpc(host, TRUE);
if (!client) {
result->stat.stat = nlm4_failed;
goto out;
@@ -2049,7 +2077,7 @@ nlm_do_lock(nlm4_lockargs *argp, nlm4_res *result, struct svc_req *rqstp,
out:
nlm_release_vfs_state(&vs);
if (rpcp)
- *rpcp = nlm_host_get_rpc(host);
+ *rpcp = nlm_host_get_rpc(host, TRUE);
nlm_host_release(host);
return (0);
}
@@ -2069,7 +2097,7 @@ nlm_do_cancel(nlm4_cancargs *argp, nlm4_res *result, struct svc_req *rqstp,
memset(&vs, 0, sizeof(vs));
host = nlm_find_host_by_name(argp->alock.caller_name,
- (struct sockaddr *) rqstp->rq_xprt->xp_rtaddr.buf, rqstp->rq_vers);
+ svc_getrpccaller(rqstp), rqstp->rq_vers);
if (!host) {
result->stat.stat = nlm4_denied_nolocks;
return (ENOMEM);
@@ -2140,7 +2168,7 @@ nlm_do_cancel(nlm4_cancargs *argp, nlm4_res *result, struct svc_req *rqstp,
out:
nlm_release_vfs_state(&vs);
if (rpcp)
- *rpcp = nlm_host_get_rpc(host);
+ *rpcp = nlm_host_get_rpc(host, TRUE);
nlm_host_release(host);
return (0);
}
@@ -2159,7 +2187,7 @@ nlm_do_unlock(nlm4_unlockargs *argp, nlm4_res *result, struct svc_req *rqstp,
memset(&vs, 0, sizeof(vs));
host = nlm_find_host_by_name(argp->alock.caller_name,
- (struct sockaddr *) rqstp->rq_xprt->xp_rtaddr.buf, rqstp->rq_vers);
+ svc_getrpccaller(rqstp), rqstp->rq_vers);
if (!host) {
result->stat.stat = nlm4_denied_nolocks;
return (ENOMEM);
@@ -2203,7 +2231,7 @@ nlm_do_unlock(nlm4_unlockargs *argp, nlm4_res *result, struct svc_req *rqstp,
out:
nlm_release_vfs_state(&vs);
if (rpcp)
- *rpcp = nlm_host_get_rpc(host);
+ *rpcp = nlm_host_get_rpc(host, TRUE);
nlm_host_release(host);
return (0);
}
@@ -2218,9 +2246,7 @@ nlm_do_granted(nlm4_testargs *argp, nlm4_res *result, struct svc_req *rqstp,
memset(result, 0, sizeof(*result));
- host = nlm_find_host_by_addr(
- (struct sockaddr *) rqstp->rq_xprt->xp_rtaddr.buf,
- rqstp->rq_vers);
+ host = nlm_find_host_by_addr(svc_getrpccaller(rqstp), rqstp->rq_vers);
if (!host) {
result->stat.stat = nlm4_denied_nolocks;
return (ENOMEM);
@@ -2247,7 +2273,7 @@ nlm_do_granted(nlm4_testargs *argp, nlm4_res *result, struct svc_req *rqstp,
}
mtx_unlock(&nlm_global_lock);
if (rpcp)
- *rpcp = nlm_host_get_rpc(host);
+ *rpcp = nlm_host_get_rpc(host, TRUE);
nlm_host_release(host);
return (0);
}
diff --git a/sys/nlm/nlm_prot_svc.c b/sys/nlm/nlm_prot_svc.c
index 3b1a140..5141f87 100644
--- a/sys/nlm/nlm_prot_svc.c
+++ b/sys/nlm/nlm_prot_svc.c
@@ -57,8 +57,9 @@ nlm_prog_0(struct svc_req *rqstp, SVCXPRT *transp)
switch (rqstp->rq_proc) {
case NULLPROC:
- (void) svc_sendreply(transp,
+ (void) svc_sendreply(rqstp,
(xdrproc_t) xdr_void, (char *)NULL);
+ svc_freereq(rqstp);
return;
case NLM_SM_NOTIFY:
@@ -68,19 +69,22 @@ nlm_prog_0(struct svc_req *rqstp, SVCXPRT *transp)
break;
default:
- svcerr_noproc(transp);
+ svcerr_noproc(rqstp);
+ svc_freereq(rqstp);
return;
}
(void) memset((char *)&argument, 0, sizeof (argument));
- if (!svc_getargs(transp, xdr_argument, (char *)(caddr_t) &argument)) {
- svcerr_decode(transp);
+ if (!svc_getargs(rqstp, xdr_argument, (char *)(caddr_t) &argument)) {
+ svcerr_decode(rqstp);
+ svc_freereq(rqstp);
return;
}
retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);
- if (retval > 0 && !svc_sendreply(transp, xdr_result, (char *)&result)) {
- svcerr_systemerr(transp);
+ if (retval > 0 && !svc_sendreply(rqstp, xdr_result, (char *)&result)) {
+ svcerr_systemerr(rqstp);
}
- if (!svc_freeargs(transp, xdr_argument, (char *)(caddr_t) &argument)) {
+ svc_freereq(rqstp);
+ if (!svc_freeargs(rqstp, xdr_argument, (char *)(caddr_t) &argument)) {
printf("unable to free arguments");
//exit(1);
}
@@ -121,8 +125,9 @@ nlm_prog_1(struct svc_req *rqstp, SVCXPRT *transp)
switch (rqstp->rq_proc) {
case NULLPROC:
- (void) svc_sendreply(transp,
+ (void) svc_sendreply(rqstp,
(xdrproc_t) xdr_void, (char *)NULL);
+ svc_freereq(rqstp);
return;
case NLM_TEST:
@@ -216,22 +221,25 @@ nlm_prog_1(struct svc_req *rqstp, SVCXPRT *transp)
break;
default:
- svcerr_noproc(transp);
+ svcerr_noproc(rqstp);
+ svc_freereq(rqstp);
return;
}
(void) memset((char *)&argument, 0, sizeof (argument));
- if (!svc_getargs(transp, xdr_argument, (char *)(caddr_t) &argument)) {
- svcerr_decode(transp);
+ if (!svc_getargs(rqstp, xdr_argument, (char *)(caddr_t) &argument)) {
+ svcerr_decode(rqstp);
+ svc_freereq(rqstp);
return;
}
retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);
- if (retval > 0 && !svc_sendreply(transp, xdr_result, (char *)&result)) {
- svcerr_systemerr(transp);
+ if (retval > 0 && !svc_sendreply(rqstp, xdr_result, (char *)&result)) {
+ svcerr_systemerr(rqstp);
}
- if (!svc_freeargs(transp, xdr_argument, (char *)(caddr_t) &argument)) {
+ if (!svc_freeargs(rqstp, xdr_argument, (char *)(caddr_t) &argument)) {
printf("unable to free arguments");
//exit(1);
}
+ svc_freereq(rqstp);
if (!nlm_prog_1_freeresult(transp, xdr_result, (caddr_t) &result))
printf("unable to free results");
@@ -258,8 +266,9 @@ nlm_prog_3(struct svc_req *rqstp, SVCXPRT *transp)
switch (rqstp->rq_proc) {
case NULLPROC:
- (void) svc_sendreply(transp,
+ (void) svc_sendreply(rqstp,
(xdrproc_t) xdr_void, (char *)NULL);
+ svc_freereq(rqstp);
return;
case NLM_TEST:
@@ -305,22 +314,25 @@ nlm_prog_3(struct svc_req *rqstp, SVCXPRT *transp)
break;
default:
- svcerr_noproc(transp);
+ svcerr_noproc(rqstp);
+ svc_freereq(rqstp);
return;
}
(void) memset((char *)&argument, 0, sizeof (argument));
- if (!svc_getargs(transp, xdr_argument, (char *)(caddr_t) &argument)) {
- svcerr_decode(transp);
+ if (!svc_getargs(rqstp, xdr_argument, (char *)(caddr_t) &argument)) {
+ svcerr_decode(rqstp);
+ svc_freereq(rqstp);
return;
}
retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);
- if (retval > 0 && !svc_sendreply(transp, xdr_result, (char *)&result)) {
- svcerr_systemerr(transp);
+ if (retval > 0 && !svc_sendreply(rqstp, xdr_result, (char *)&result)) {
+ svcerr_systemerr(rqstp);
}
- if (!svc_freeargs(transp, xdr_argument, (char *)(caddr_t) &argument)) {
+ if (!svc_freeargs(rqstp, xdr_argument, (char *)(caddr_t) &argument)) {
printf("unable to free arguments");
//exit(1);
}
+ svc_freereq(rqstp);
if (!nlm_prog_3_freeresult(transp, xdr_result, (caddr_t) &result))
printf("unable to free results");
@@ -367,8 +379,9 @@ nlm_prog_4(struct svc_req *rqstp, SVCXPRT *transp)
switch (rqstp->rq_proc) {
case NULLPROC:
- (void) svc_sendreply(transp,
+ (void) svc_sendreply(rqstp,
(xdrproc_t) xdr_void, (char *)NULL);
+ svc_freereq(rqstp);
return;
case NLM4_TEST:
@@ -486,22 +499,25 @@ nlm_prog_4(struct svc_req *rqstp, SVCXPRT *transp)
break;
default:
- svcerr_noproc(transp);
+ svcerr_noproc(rqstp);
+ svc_freereq(rqstp);
return;
}
(void) memset((char *)&argument, 0, sizeof (argument));
- if (!svc_getargs(transp, xdr_argument, (char *)(caddr_t) &argument)) {
- svcerr_decode(transp);
+ if (!svc_getargs(rqstp, xdr_argument, (char *)(caddr_t) &argument)) {
+ svcerr_decode(rqstp);
+ svc_freereq(rqstp);
return;
}
retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);
- if (retval > 0 && !svc_sendreply(transp, xdr_result, (char *)&result)) {
- svcerr_systemerr(transp);
+ if (retval > 0 && !svc_sendreply(rqstp, xdr_result, (char *)&result)) {
+ svcerr_systemerr(rqstp);
}
- if (!svc_freeargs(transp, xdr_argument, (char *)(caddr_t) &argument)) {
+ if (!svc_freeargs(rqstp, xdr_argument, (char *)(caddr_t) &argument)) {
printf("unable to free arguments");
//exit(1);
}
+ svc_freereq(rqstp);
if (!nlm_prog_4_freeresult(transp, xdr_result, (caddr_t) &result))
printf("unable to free results");
diff --git a/sys/rpc/auth.h b/sys/rpc/auth.h
index b919559..6be08b6 100644
--- a/sys/rpc/auth.h
+++ b/sys/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 {
@@ -171,6 +177,7 @@ struct opaque_auth {
/*
* Auth handle, interface to client side authenticators.
*/
+struct rpc_err;
typedef struct __auth {
struct opaque_auth ah_cred;
struct opaque_auth ah_verf;
@@ -178,10 +185,11 @@ typedef struct __auth {
struct auth_ops {
void (*ah_nextverf) (struct __auth *);
/* nextverf & serialize */
- int (*ah_marshal) (struct __auth *, XDR *);
+ int (*ah_marshal) (struct __auth *, uint32_t, XDR *,
+ struct mbuf *);
/* validate verifier */
- int (*ah_validate) (struct __auth *,
- struct opaque_auth *);
+ int (*ah_validate) (struct __auth *, uint32_t,
+ struct opaque_auth *, struct mbuf **);
/* refresh credentials */
int (*ah_refresh) (struct __auth *, void *);
/* destroy this structure */
@@ -201,29 +209,18 @@ typedef struct __auth {
*/
#define AUTH_NEXTVERF(auth) \
((*((auth)->ah_ops->ah_nextverf))(auth))
-#define auth_nextverf(auth) \
- ((*((auth)->ah_ops->ah_nextverf))(auth))
-#define AUTH_MARSHALL(auth, xdrs) \
- ((*((auth)->ah_ops->ah_marshal))(auth, xdrs))
-#define auth_marshall(auth, xdrs) \
- ((*((auth)->ah_ops->ah_marshal))(auth, xdrs))
+#define AUTH_MARSHALL(auth, xid, xdrs, args) \
+ ((*((auth)->ah_ops->ah_marshal))(auth, xid, xdrs, args))
-#define AUTH_VALIDATE(auth, verfp) \
- ((*((auth)->ah_ops->ah_validate))((auth), verfp))
-#define auth_validate(auth, verfp) \
- ((*((auth)->ah_ops->ah_validate))((auth), verfp))
+#define AUTH_VALIDATE(auth, xid, verfp, resultsp) \
+ ((*((auth)->ah_ops->ah_validate))((auth), xid, verfp, resultsp))
#define AUTH_REFRESH(auth, msg) \
((*((auth)->ah_ops->ah_refresh))(auth, msg))
-#define auth_refresh(auth, msg) \
- ((*((auth)->ah_ops->ah_refresh))(auth, msg))
#define AUTH_DESTROY(auth) \
((*((auth)->ah_ops->ah_destroy))(auth))
-#define auth_destroy(auth) \
- ((*((auth)->ah_ops->ah_destroy))(auth))
-
__BEGIN_DECLS
extern struct opaque_auth _null_auth;
@@ -357,5 +354,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/sys/rpc/auth_none.c b/sys/rpc/auth_none.c
index 8530437..a256b83 100644
--- a/sys/rpc/auth_none.c
+++ b/sys/rpc/auth_none.c
@@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
+#include <rpc/clnt.h>
#define MAX_MARSHAL_SIZE 20
@@ -61,9 +62,10 @@ __FBSDID("$FreeBSD$");
* Authenticator operations routines
*/
-static bool_t authnone_marshal (AUTH *, XDR *);
+static bool_t authnone_marshal (AUTH *, uint32_t, XDR *, struct mbuf *);
static void authnone_verf (AUTH *);
-static bool_t authnone_validate (AUTH *, struct opaque_auth *);
+static bool_t authnone_validate (AUTH *, uint32_t, struct opaque_auth *,
+ struct mbuf **);
static bool_t authnone_refresh (AUTH *, void *);
static void authnone_destroy (AUTH *);
@@ -72,7 +74,7 @@ static struct auth_ops authnone_ops = {
.ah_marshal = authnone_marshal,
.ah_validate = authnone_validate,
.ah_refresh = authnone_refresh,
- .ah_destroy = authnone_destroy
+ .ah_destroy = authnone_destroy,
};
struct authnone_private {
@@ -109,13 +111,18 @@ authnone_create()
/*ARGSUSED*/
static bool_t
-authnone_marshal(AUTH *client, XDR *xdrs)
+authnone_marshal(AUTH *client, uint32_t xid, XDR *xdrs, struct mbuf *args)
{
struct authnone_private *ap = &authnone_private;
KASSERT(xdrs != NULL, ("authnone_marshal: xdrs is null"));
- return (xdrs->x_ops->x_putbytes(xdrs, ap->mclient, ap->mcnt));
+ if (!XDR_PUTBYTES(xdrs, ap->mclient, ap->mcnt))
+ return (FALSE);
+
+ xdrmbuf_append(xdrs, args);
+
+ return (TRUE);
}
/* All these unused parameters are required to keep ANSI-C from grumbling */
@@ -127,7 +134,8 @@ authnone_verf(AUTH *client)
/*ARGSUSED*/
static bool_t
-authnone_validate(AUTH *client, struct opaque_auth *opaque)
+authnone_validate(AUTH *client, uint32_t xid, struct opaque_auth *opaque,
+ struct mbuf **mrepp)
{
return (TRUE);
diff --git a/sys/rpc/auth_unix.c b/sys/rpc/auth_unix.c
index e30e59e..bd4be34 100644
--- a/sys/rpc/auth_unix.c
+++ b/sys/rpc/auth_unix.c
@@ -62,13 +62,15 @@ __FBSDID("$FreeBSD$");
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
+#include <rpc/clnt.h>
#include <rpc/rpc_com.h>
/* auth_unix.c */
static void authunix_nextverf (AUTH *);
-static bool_t authunix_marshal (AUTH *, XDR *);
-static bool_t authunix_validate (AUTH *, struct opaque_auth *);
+static bool_t authunix_marshal (AUTH *, uint32_t, XDR *, struct mbuf *);
+static bool_t authunix_validate (AUTH *, uint32_t, struct opaque_auth *,
+ struct mbuf **);
static bool_t authunix_refresh (AUTH *, void *);
static void authunix_destroy (AUTH *);
static void marshal_new_auth (AUTH *);
@@ -78,7 +80,7 @@ static struct auth_ops authunix_ops = {
.ah_marshal = authunix_marshal,
.ah_validate = authunix_validate,
.ah_refresh = authunix_refresh,
- .ah_destroy = authunix_destroy
+ .ah_destroy = authunix_destroy,
};
/*
@@ -246,23 +248,32 @@ authunix_nextverf(AUTH *auth)
}
static bool_t
-authunix_marshal(AUTH *auth, XDR *xdrs)
+authunix_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
{
struct audata *au;
au = AUTH_PRIVATE(auth);
- return (XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos));
+ if (!XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos))
+ return (FALSE);
+
+ xdrmbuf_append(xdrs, args);
+
+ return (TRUE);
}
static bool_t
-authunix_validate(AUTH *auth, struct opaque_auth *verf)
+authunix_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
+ struct mbuf **mrepp)
{
struct audata *au;
- XDR xdrs;
+ XDR txdrs;
+
+ if (!verf)
+ return (TRUE);
if (verf->oa_flavor == AUTH_SHORT) {
au = AUTH_PRIVATE(auth);
- xdrmem_create(&xdrs, verf->oa_base, verf->oa_length,
+ xdrmem_create(&txdrs, verf->oa_base, verf->oa_length,
XDR_DECODE);
if (au->au_shcred.oa_base != NULL) {
@@ -270,16 +281,17 @@ authunix_validate(AUTH *auth, struct opaque_auth *verf)
au->au_shcred.oa_length);
au->au_shcred.oa_base = NULL;
}
- if (xdr_opaque_auth(&xdrs, &au->au_shcred)) {
+ if (xdr_opaque_auth(&txdrs, &au->au_shcred)) {
auth->ah_cred = au->au_shcred;
} else {
- xdrs.x_op = XDR_FREE;
- (void)xdr_opaque_auth(&xdrs, &au->au_shcred);
+ txdrs.x_op = XDR_FREE;
+ (void)xdr_opaque_auth(&txdrs, &au->au_shcred);
au->au_shcred.oa_base = NULL;
auth->ah_cred = au->au_origcred;
}
marshal_new_auth(auth);
}
+
return (TRUE);
}
diff --git a/sys/rpc/clnt.h b/sys/rpc/clnt.h
index 03e3112..74d5813 100644
--- a/sys/rpc/clnt.h
+++ b/sys/rpc/clnt.h
@@ -118,6 +118,15 @@ struct rpc_err {
typedef void rpc_feedback(int cmd, int procnum, void *);
/*
+ * Timers used for the pseudo-transport protocol when using datagrams
+ */
+struct rpc_timers {
+ u_short rt_srtt; /* smoothed round-trip time */
+ u_short rt_deviate; /* estimated deviation */
+ u_long rt_rtxcur; /* current (backed-off) rto */
+};
+
+/*
* A structure used with CLNT_CALL_EXT to pass extra information used
* while processing an RPC call.
*/
@@ -125,6 +134,8 @@ struct rpc_callextra {
AUTH *rc_auth; /* auth handle to use for this call */
rpc_feedback *rc_feedback; /* callback for retransmits etc. */
void *rc_feedback_arg; /* argument for callback */
+ struct rpc_timers *rc_timers; /* optional RTT timers */
+ struct rpc_err rc_err; /* detailed call status */
};
#endif
@@ -140,8 +151,8 @@ typedef struct __rpc_client {
struct clnt_ops {
/* call remote procedure */
enum clnt_stat (*cl_call)(struct __rpc_client *,
- struct rpc_callextra *, rpcproc_t, xdrproc_t, void *,
- xdrproc_t, void *, struct timeval);
+ struct rpc_callextra *, rpcproc_t,
+ struct mbuf *, struct mbuf **, struct timeval);
/* abort a call */
void (*cl_abort)(struct __rpc_client *);
/* get specific error code */
@@ -150,6 +161,8 @@ typedef struct __rpc_client {
/* frees results */
bool_t (*cl_freeres)(struct __rpc_client *,
xdrproc_t, void *);
+ /* close the connection and terminate pending RPCs */
+ void (*cl_close)(struct __rpc_client *);
/* destroy this structure */
void (*cl_destroy)(struct __rpc_client *);
/* the ioctl() of rpc */
@@ -183,15 +196,6 @@ typedef struct __rpc_client {
char *cl_tp; /* device name */
} CLIENT;
-/*
- * Timers used for the pseudo-transport protocol when using datagrams
- */
-struct rpc_timers {
- u_short rt_srtt; /* smoothed round-trip time */
- u_short rt_deviate; /* estimated deviation */
- u_long rt_rtxcur; /* current (backed-off) rto */
-};
-
/*
* Feedback values used for possible congestion and rate control
*/
@@ -222,6 +226,32 @@ struct rpc_timers {
CLNT_DESTROY(rh)
/*
+ * void
+ * CLNT_CLOSE(rh);
+ * CLIENT *rh;
+ */
+#define CLNT_CLOSE(rh) ((*(rh)->cl_ops->cl_close)(rh))
+
+enum clnt_stat clnt_call_private(CLIENT *, struct rpc_callextra *, rpcproc_t,
+ xdrproc_t, void *, xdrproc_t, void *, struct timeval);
+
+/*
+ * enum clnt_stat
+ * CLNT_CALL_MBUF(rh, ext, proc, mreq, mrepp, timeout)
+ * CLIENT *rh;
+ * struct rpc_callextra *ext;
+ * rpcproc_t proc;
+ * struct mbuf *mreq;
+ * struct mbuf **mrepp;
+ * struct timeval timeout;
+ *
+ * Call arguments in mreq which is consumed by the call (even if there
+ * is an error). Results returned in *mrepp.
+ */
+#define CLNT_CALL_MBUF(rh, ext, proc, mreq, mrepp, secs) \
+ ((*(rh)->cl_ops->cl_call)(rh, ext, proc, mreq, mrepp, secs))
+
+/*
* enum clnt_stat
* CLNT_CALL_EXT(rh, ext, proc, xargs, argsp, xres, resp, timeout)
* CLIENT *rh;
@@ -234,8 +264,8 @@ struct rpc_timers {
* struct timeval timeout;
*/
#define CLNT_CALL_EXT(rh, ext, proc, xargs, argsp, xres, resp, secs) \
- ((*(rh)->cl_ops->cl_call)(rh, ext, proc, xargs, \
- argsp, xres, resp, secs))
+ clnt_call_private(rh, ext, proc, xargs, \
+ argsp, xres, resp, secs)
#endif
/*
@@ -250,12 +280,12 @@ struct rpc_timers {
* struct timeval timeout;
*/
#ifdef _KERNEL
-#define CLNT_CALL(rh, proc, xargs, argsp, xres, resp, secs) \
- ((*(rh)->cl_ops->cl_call)(rh, NULL, proc, xargs, \
- argsp, xres, resp, secs))
-#define clnt_call(rh, proc, xargs, argsp, xres, resp, secs) \
- ((*(rh)->cl_ops->cl_call)(rh, NULL, proc, xargs, \
- argsp, xres, resp, secs))
+#define CLNT_CALL(rh, proc, xargs, argsp, xres, resp, secs) \
+ clnt_call_private(rh, NULL, proc, xargs, \
+ argsp, xres, resp, secs)
+#define clnt_call(rh, proc, xargs, argsp, xres, resp, secs) \
+ clnt_call_private(rh, NULL, proc, xargs, \
+ argsp, xres, resp, secs)
#else
#define CLNT_CALL(rh, proc, xargs, argsp, xres, resp, secs) \
((*(rh)->cl_ops->cl_call)(rh, proc, xargs, \
@@ -340,6 +370,8 @@ struct rpc_timers {
#define CLGET_INTERRUPTIBLE 24 /* set interruptible flag */
#define CLSET_RETRIES 25 /* set retry count for reconnect */
#define CLGET_RETRIES 26 /* get retry count for reconnect */
+#define CLSET_PRIVPORT 27 /* set privileged source port flag */
+#define CLGET_PRIVPORT 28 /* get privileged source port flag */
#endif
diff --git a/sys/rpc/clnt_dg.c b/sys/rpc/clnt_dg.c
index f14e1d6..e6d101d 100644
--- a/sys/rpc/clnt_dg.c
+++ b/sys/rpc/clnt_dg.c
@@ -72,11 +72,12 @@ __FBSDID("$FreeBSD$");
static bool_t time_not_ok(struct timeval *);
static enum clnt_stat clnt_dg_call(CLIENT *, struct rpc_callextra *,
- rpcproc_t, xdrproc_t, void *, xdrproc_t, void *, struct timeval);
+ rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
static void clnt_dg_geterr(CLIENT *, struct rpc_err *);
static bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *);
static void clnt_dg_abort(CLIENT *);
static bool_t clnt_dg_control(CLIENT *, u_int, void *);
+static void clnt_dg_close(CLIENT *);
static void clnt_dg_destroy(CLIENT *);
static void clnt_dg_soupcall(struct socket *so, void *arg, int waitflag);
@@ -85,6 +86,7 @@ static struct clnt_ops clnt_dg_ops = {
.cl_abort = clnt_dg_abort,
.cl_geterr = clnt_dg_geterr,
.cl_freeres = clnt_dg_freeres,
+ .cl_close = clnt_dg_close,
.cl_destroy = clnt_dg_destroy,
.cl_control = clnt_dg_control
};
@@ -102,6 +104,7 @@ struct cu_request {
uint32_t cr_xid; /* XID of request */
struct mbuf *cr_mrep; /* reply received by upcall */
int cr_error; /* any error from upcall */
+ char cr_verf[MAX_AUTH_BYTES]; /* reply verf */
};
TAILQ_HEAD(cu_request_list, cu_request);
@@ -120,7 +123,6 @@ struct cu_socket {
struct mtx cs_lock;
int cs_refs; /* Count of clients */
struct cu_request_list cs_pending; /* Requests awaiting replies */
-
};
/*
@@ -128,7 +130,8 @@ struct cu_socket {
*/
struct cu_data {
int cu_threads; /* # threads in clnt_vc_call */
- bool_t cu_closing; /* TRUE if we are destroying */
+ bool_t cu_closing; /* TRUE if we are closing */
+ bool_t cu_closed; /* TRUE if we are closed */
struct socket *cu_socket; /* connection socket */
bool_t cu_closeit; /* opened by library */
struct sockaddr_storage cu_raddr; /* remote address */
@@ -146,8 +149,14 @@ struct cu_data {
int cu_connected; /* Have done connect(). */
const char *cu_waitchan;
int cu_waitflag;
+ int cu_cwnd; /* congestion window */
+ int cu_sent; /* number of in-flight RPCs */
+ bool_t cu_cwnd_wait;
};
+#define CWNDSCALE 256
+#define MAXCWND (32 * CWNDSCALE)
+
/*
* Connection less client creation returns with client handle parameters.
* Default options are set, which the user can change using clnt_control().
@@ -211,6 +220,7 @@ clnt_dg_create(
cu = mem_alloc(sizeof (*cu));
cu->cu_threads = 0;
cu->cu_closing = FALSE;
+ cu->cu_closed = FALSE;
(void) memcpy(&cu->cu_raddr, svcaddr, (size_t)svcaddr->sa_len);
cu->cu_rlen = svcaddr->sa_len;
/* Other values can also be set through clnt_control() */
@@ -225,6 +235,9 @@ clnt_dg_create(
cu->cu_connected = FALSE;
cu->cu_waitchan = "rpcrecv";
cu->cu_waitflag = 0;
+ cu->cu_cwnd = MAXCWND / 2;
+ cu->cu_sent = 0;
+ cu->cu_cwnd_wait = FALSE;
(void) getmicrotime(&now);
cu->cu_xid = __RPC_GETXID(&now);
call_msg.rm_xid = cu->cu_xid;
@@ -304,15 +317,16 @@ clnt_dg_call(
CLIENT *cl, /* client handle */
struct rpc_callextra *ext, /* call metadata */
rpcproc_t proc, /* procedure number */
- xdrproc_t xargs, /* xdr routine for args */
- void *argsp, /* pointer to args */
- xdrproc_t xresults, /* xdr routine for results */
- void *resultsp, /* pointer to results */
+ struct mbuf *args, /* pointer to args */
+ struct mbuf **resultsp, /* pointer to results */
struct timeval utimeout) /* seconds to wait before giving up */
{
struct cu_data *cu = (struct cu_data *)cl->cl_private;
struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
+ struct rpc_timers *rt;
AUTH *auth;
+ struct rpc_err *errp;
+ enum clnt_stat stat;
XDR xdrs;
struct rpc_msg reply_msg;
bool_t ok;
@@ -321,11 +335,11 @@ clnt_dg_call(
struct timeval *tvp;
int timeout;
int retransmit_time;
- int next_sendtime, starttime, time_waited, tv;
+ int next_sendtime, starttime, rtt, time_waited, tv = 0;
struct sockaddr *sa;
socklen_t salen;
- uint32_t xid;
- struct mbuf *mreq = NULL;
+ uint32_t xid = 0;
+ struct mbuf *mreq = NULL, *results;
struct cu_request *cr;
int error;
@@ -333,17 +347,20 @@ clnt_dg_call(
mtx_lock(&cs->cs_lock);
- if (cu->cu_closing) {
+ if (cu->cu_closing || cu->cu_closed) {
mtx_unlock(&cs->cs_lock);
free(cr, M_RPC);
return (RPC_CANTSEND);
}
cu->cu_threads++;
- if (ext)
+ if (ext) {
auth = ext->rc_auth;
- else
+ errp = &ext->rc_err;
+ } else {
auth = cl->cl_auth;
+ errp = &cu->cu_error;
+ }
cr->cr_client = cl;
cr->cr_mrep = NULL;
@@ -365,8 +382,8 @@ clnt_dg_call(
(struct sockaddr *)&cu->cu_raddr, curthread);
mtx_lock(&cs->cs_lock);
if (error) {
- cu->cu_error.re_errno = error;
- cu->cu_error.re_status = RPC_CANTSEND;
+ errp->re_errno = error;
+ errp->re_status = stat = RPC_CANTSEND;
goto out;
}
cu->cu_connected = 1;
@@ -380,7 +397,15 @@ clnt_dg_call(
}
time_waited = 0;
retrans = 0;
- retransmit_time = next_sendtime = tvtohz(&cu->cu_wait);
+ if (ext && ext->rc_timers) {
+ rt = ext->rc_timers;
+ if (!rt->rt_rtxcur)
+ rt->rt_rtxcur = tvtohz(&cu->cu_wait);
+ retransmit_time = next_sendtime = rt->rt_rtxcur;
+ } else {
+ rt = NULL;
+ retransmit_time = next_sendtime = tvtohz(&cu->cu_wait);
+ }
starttime = ticks;
@@ -394,9 +419,9 @@ send_again:
mtx_unlock(&cs->cs_lock);
MGETHDR(mreq, M_WAIT, MT_DATA);
- MCLGET(mreq, M_WAIT);
- mreq->m_len = 0;
- m_append(mreq, cu->cu_mcalllen, cu->cu_mcallc);
+ KASSERT(cu->cu_mcalllen <= MHLEN, ("RPC header too big"));
+ bcopy(cu->cu_mcallc, mreq->m_data, cu->cu_mcalllen);
+ mreq->m_len = cu->cu_mcalllen;
/*
* The XID is the first thing in the request.
@@ -405,20 +430,36 @@ send_again:
xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
- if (cu->cu_async == TRUE && xargs == NULL)
+ if (cu->cu_async == TRUE && args == NULL)
goto get_reply;
if ((! XDR_PUTINT32(&xdrs, &proc)) ||
- (! AUTH_MARSHALL(auth, &xdrs)) ||
- (! (*xargs)(&xdrs, argsp))) {
- cu->cu_error.re_status = RPC_CANTENCODEARGS;
+ (! AUTH_MARSHALL(auth, xid, &xdrs,
+ m_copym(args, 0, M_COPYALL, M_WAITOK)))) {
+ errp->re_status = stat = RPC_CANTENCODEARGS;
mtx_lock(&cs->cs_lock);
goto out;
}
- m_fixhdr(mreq);
+ mreq->m_pkthdr.len = m_length(mreq, NULL);
cr->cr_xid = xid;
mtx_lock(&cs->cs_lock);
+
+ /*
+ * Try to get a place in the congestion window.
+ */
+ while (cu->cu_sent >= cu->cu_cwnd) {
+ cu->cu_cwnd_wait = TRUE;
+ error = msleep(&cu->cu_cwnd_wait, &cs->cs_lock,
+ cu->cu_waitflag, "rpccwnd", 0);
+ if (error) {
+ errp->re_errno = error;
+ errp->re_status = stat = RPC_CANTSEND;
+ goto out;
+ }
+ }
+ cu->cu_sent += CWNDSCALE;
+
TAILQ_INSERT_TAIL(&cs->cs_pending, cr, cr_link);
mtx_unlock(&cs->cs_lock);
@@ -433,15 +474,22 @@ send_again:
* some clock time to spare while the packets are in flight.
* (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;
+ reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL;
+ reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf;
+ reply_msg.acpted_rply.ar_verf.oa_length = 0;
+ reply_msg.acpted_rply.ar_results.where = NULL;
+ reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
mtx_lock(&cs->cs_lock);
if (error) {
TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
- cu->cu_error.re_errno = error;
- cu->cu_error.re_status = RPC_CANTSEND;
+ errp->re_errno = error;
+ errp->re_status = stat = RPC_CANTSEND;
+ cu->cu_sent -= CWNDSCALE;
+ if (cu->cu_cwnd_wait) {
+ cu->cu_cwnd_wait = FALSE;
+ wakeup(&cu->cu_cwnd_wait);
+ }
goto out;
}
@@ -451,12 +499,22 @@ send_again:
*/
if (cr->cr_error) {
TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
- cu->cu_error.re_errno = cr->cr_error;
- cu->cu_error.re_status = RPC_CANTRECV;
+ errp->re_errno = cr->cr_error;
+ errp->re_status = stat = RPC_CANTRECV;
+ cu->cu_sent -= CWNDSCALE;
+ if (cu->cu_cwnd_wait) {
+ cu->cu_cwnd_wait = FALSE;
+ wakeup(&cu->cu_cwnd_wait);
+ }
goto out;
}
if (cr->cr_mrep) {
TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
+ cu->cu_sent -= CWNDSCALE;
+ if (cu->cu_cwnd_wait) {
+ cu->cu_cwnd_wait = FALSE;
+ wakeup(&cu->cu_cwnd_wait);
+ }
goto got_reply;
}
@@ -465,7 +523,12 @@ send_again:
*/
if (timeout == 0) {
TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
- cu->cu_error.re_status = RPC_TIMEDOUT;
+ errp->re_status = stat = RPC_TIMEDOUT;
+ cu->cu_sent -= CWNDSCALE;
+ if (cu->cu_cwnd_wait) {
+ cu->cu_cwnd_wait = FALSE;
+ wakeup(&cu->cu_cwnd_wait);
+ }
goto out;
}
@@ -479,7 +542,7 @@ get_reply:
tv -= time_waited;
if (tv > 0) {
- if (cu->cu_closing)
+ if (cu->cu_closing || cu->cu_closed)
error = 0;
else
error = msleep(cr, &cs->cs_lock,
@@ -489,6 +552,11 @@ get_reply:
}
TAILQ_REMOVE(&cs->cs_pending, cr, cr_link);
+ cu->cu_sent -= CWNDSCALE;
+ if (cu->cu_cwnd_wait) {
+ cu->cu_cwnd_wait = FALSE;
+ wakeup(&cu->cu_cwnd_wait);
+ }
if (!error) {
/*
@@ -497,10 +565,52 @@ get_reply:
* otherwise we have a reply.
*/
if (cr->cr_error) {
- cu->cu_error.re_errno = cr->cr_error;
- cu->cu_error.re_status = RPC_CANTRECV;
+ errp->re_errno = cr->cr_error;
+ errp->re_status = stat = RPC_CANTRECV;
goto out;
}
+
+ cu->cu_cwnd += (CWNDSCALE * CWNDSCALE
+ + cu->cu_cwnd / 2) / cu->cu_cwnd;
+ if (cu->cu_cwnd > MAXCWND)
+ cu->cu_cwnd = MAXCWND;
+
+ if (rt) {
+ /*
+ * Add one to the time since a tick
+ * count of N means that the actual
+ * time taken was somewhere between N
+ * and N+1.
+ */
+ rtt = ticks - starttime + 1;
+
+ /*
+ * Update our estimate of the round
+ * trip time using roughly the
+ * algorithm described in RFC
+ * 2988. Given an RTT sample R:
+ *
+ * RTTVAR = (1-beta) * RTTVAR + beta * |SRTT-R|
+ * SRTT = (1-alpha) * SRTT + alpha * R
+ *
+ * where alpha = 0.125 and beta = 0.25.
+ *
+ * The initial retransmit timeout is
+ * SRTT + 4*RTTVAR and doubles on each
+ * retransmision.
+ */
+ if (rt->rt_srtt == 0) {
+ rt->rt_srtt = rtt;
+ rt->rt_deviate = rtt / 2;
+ } else {
+ int32_t error = rtt - rt->rt_srtt;
+ rt->rt_srtt += error / 8;
+ error = abs(error) - rt->rt_deviate;
+ rt->rt_deviate += error / 4;
+ }
+ rt->rt_rtxcur = rt->rt_srtt + 4*rt->rt_deviate;
+ }
+
break;
}
@@ -510,11 +620,11 @@ get_reply:
* re-send the request.
*/
if (error != EWOULDBLOCK) {
- cu->cu_error.re_errno = error;
+ errp->re_errno = error;
if (error == EINTR)
- cu->cu_error.re_status = RPC_INTR;
+ errp->re_status = stat = RPC_INTR;
else
- cu->cu_error.re_status = RPC_CANTRECV;
+ errp->re_status = stat = RPC_CANTRECV;
goto out;
}
@@ -522,13 +632,16 @@ get_reply:
/* Check for timeout. */
if (time_waited > timeout) {
- cu->cu_error.re_errno = EWOULDBLOCK;
- cu->cu_error.re_status = RPC_TIMEDOUT;
+ errp->re_errno = EWOULDBLOCK;
+ errp->re_status = stat = RPC_TIMEDOUT;
goto out;
}
/* Retransmit if necessary. */
if (time_waited >= next_sendtime) {
+ cu->cu_cwnd /= 2;
+ if (cu->cu_cwnd < CWNDSCALE)
+ cu->cu_cwnd = CWNDSCALE;
if (ext && ext->rc_feedback) {
mtx_unlock(&cs->cs_lock);
if (retrans == 0)
@@ -539,9 +652,9 @@ get_reply:
proc, ext->rc_feedback_arg);
mtx_lock(&cs->cs_lock);
}
- if (cu->cu_closing) {
- cu->cu_error.re_errno = ESHUTDOWN;
- cu->cu_error.re_status = RPC_CANTRECV;
+ if (cu->cu_closing || cu->cu_closed) {
+ errp->re_errno = ESHUTDOWN;
+ errp->re_status = stat = RPC_CANTRECV;
goto out;
}
retrans++;
@@ -566,47 +679,72 @@ got_reply:
xdrmbuf_create(&xdrs, cr->cr_mrep, XDR_DECODE);
ok = xdr_replymsg(&xdrs, &reply_msg);
- XDR_DESTROY(&xdrs);
cr->cr_mrep = NULL;
- mtx_lock(&cs->cs_lock);
-
if (ok) {
if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
- (reply_msg.acpted_rply.ar_stat == SUCCESS))
- cu->cu_error.re_status = RPC_SUCCESS;
+ (reply_msg.acpted_rply.ar_stat == SUCCESS))
+ errp->re_status = stat = RPC_SUCCESS;
else
- _seterr_reply(&reply_msg, &(cu->cu_error));
-
- if (cu->cu_error.re_status == RPC_SUCCESS) {
- if (! AUTH_VALIDATE(cl->cl_auth,
- &reply_msg.acpted_rply.ar_verf)) {
- cu->cu_error.re_status = RPC_AUTHERROR;
- cu->cu_error.re_why = AUTH_INVALIDRESP;
- }
- if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
- xdrs.x_op = XDR_FREE;
- (void) xdr_opaque_auth(&xdrs,
- &(reply_msg.acpted_rply.ar_verf));
+ stat = _seterr_reply(&reply_msg, &(cu->cu_error));
+
+ if (errp->re_status == RPC_SUCCESS) {
+ results = xdrmbuf_getall(&xdrs);
+ if (! AUTH_VALIDATE(auth, xid,
+ &reply_msg.acpted_rply.ar_verf,
+ &results)) {
+ errp->re_status = stat = RPC_AUTHERROR;
+ errp->re_why = AUTH_INVALIDRESP;
+ if (retrans &&
+ 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.
+ */
+ XDR_DESTROY(&xdrs);
+ mtx_lock(&cs->cs_lock);
+ TAILQ_INSERT_TAIL(&cs->cs_pending,
+ cr, cr_link);
+ cr->cr_mrep = NULL;
+ goto get_reply;
+ }
+ } else {
+ *resultsp = results;
}
} /* end successful completion */
/*
* If unsuccesful AND error is an authentication error
* then refresh credentials and try again, else break
*/
- else if (cu->cu_error.re_status == RPC_AUTHERROR)
+ else if (stat == RPC_AUTHERROR)
/* maybe our credentials need to be refreshed ... */
if (nrefreshes > 0 &&
- AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
+ AUTH_REFRESH(auth, &reply_msg)) {
nrefreshes--;
+ XDR_DESTROY(&xdrs);
+ mtx_lock(&cs->cs_lock);
goto call_again;
}
/* end of unsuccessful completion */
} /* end of valid reply message */
else {
- cu->cu_error.re_status = RPC_CANTDECODERES;
+ errp->re_status = stat = RPC_CANTDECODERES;
}
+ XDR_DESTROY(&xdrs);
+ mtx_lock(&cs->cs_lock);
out:
mtx_assert(&cs->cs_lock, MA_OWNED);
@@ -621,9 +759,12 @@ out:
mtx_unlock(&cs->cs_lock);
+ if (auth && stat != RPC_SUCCESS)
+ AUTH_VALIDATE(auth, xid, NULL, NULL);
+
free(cr, M_RPC);
- return (cu->cu_error.re_status);
+ return (stat);
}
static void
@@ -759,7 +900,7 @@ clnt_dg_control(CLIENT *cl, u_int request, void *info)
cu->cu_connect = *(int *)info;
break;
case CLSET_WAITCHAN:
- cu->cu_waitchan = *(const char **)info;
+ cu->cu_waitchan = (const char *)info;
break;
case CLGET_WAITCHAN:
*(const char **) info = cu->cu_waitchan;
@@ -785,16 +926,27 @@ clnt_dg_control(CLIENT *cl, u_int request, void *info)
}
static void
-clnt_dg_destroy(CLIENT *cl)
+clnt_dg_close(CLIENT *cl)
{
struct cu_data *cu = (struct cu_data *)cl->cl_private;
struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
struct cu_request *cr;
- struct socket *so = NULL;
- bool_t lastsocketref;
mtx_lock(&cs->cs_lock);
+ if (cu->cu_closed) {
+ mtx_unlock(&cs->cs_lock);
+ return;
+ }
+
+ if (cu->cu_closing) {
+ while (cu->cu_closing)
+ msleep(cu, &cs->cs_lock, 0, "rpcclose", 0);
+ KASSERT(cu->cu_closed, ("client should be closed"));
+ mtx_unlock(&cs->cs_lock);
+ return;
+ }
+
/*
* Abort any pending requests and wait until everyone
* has finished with clnt_vc_call.
@@ -811,6 +963,25 @@ clnt_dg_destroy(CLIENT *cl)
while (cu->cu_threads)
msleep(cu, &cs->cs_lock, 0, "rpcclose", 0);
+ cu->cu_closing = FALSE;
+ cu->cu_closed = TRUE;
+
+ mtx_unlock(&cs->cs_lock);
+ wakeup(cu);
+}
+
+static void
+clnt_dg_destroy(CLIENT *cl)
+{
+ struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ struct cu_socket *cs = (struct cu_socket *) cu->cu_socket->so_upcallarg;
+ struct socket *so = NULL;
+ bool_t lastsocketref;
+
+ clnt_dg_close(cl);
+
+ mtx_lock(&cs->cs_lock);
+
cs->cs_refs--;
if (cs->cs_refs == 0) {
mtx_destroy(&cs->cs_lock);
@@ -894,7 +1065,8 @@ clnt_dg_soupcall(struct socket *so, void *arg, int waitflag)
/*
* The XID is in the first uint32_t of the reply.
*/
- m = m_pullup(m, sizeof(xid));
+ if (m->m_len < sizeof(xid))
+ m = m_pullup(m, sizeof(xid));
if (!m)
/*
* Should never happen.
diff --git a/sys/rpc/clnt_rc.c b/sys/rpc/clnt_rc.c
index f0ad673..8d7bfd6 100644
--- a/sys/rpc/clnt_rc.c
+++ b/sys/rpc/clnt_rc.c
@@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/malloc.h>
@@ -46,11 +47,12 @@ __FBSDID("$FreeBSD$");
#include <rpc/rpc_com.h>
static enum clnt_stat clnt_reconnect_call(CLIENT *, struct rpc_callextra *,
- rpcproc_t, xdrproc_t, void *, xdrproc_t, void *, struct timeval);
+ rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
static void clnt_reconnect_geterr(CLIENT *, struct rpc_err *);
static bool_t clnt_reconnect_freeres(CLIENT *, xdrproc_t, void *);
static void clnt_reconnect_abort(CLIENT *);
static bool_t clnt_reconnect_control(CLIENT *, u_int, void *);
+static void clnt_reconnect_close(CLIENT *);
static void clnt_reconnect_destroy(CLIENT *);
static struct clnt_ops clnt_reconnect_ops = {
@@ -58,10 +60,13 @@ static struct clnt_ops clnt_reconnect_ops = {
.cl_abort = clnt_reconnect_abort,
.cl_geterr = clnt_reconnect_geterr,
.cl_freeres = clnt_reconnect_freeres,
+ .cl_close = clnt_reconnect_close,
.cl_destroy = clnt_reconnect_destroy,
.cl_control = clnt_reconnect_control
};
+static int fake_wchan;
+
struct rc_data {
struct mtx rc_lock;
struct sockaddr_storage rc_addr; /* server address */
@@ -73,10 +78,14 @@ struct rc_data {
struct timeval rc_timeout;
struct timeval rc_retry;
int rc_retries;
- const char *rc_waitchan;
+ int rc_privport;
+ char *rc_waitchan;
int rc_intr;
int rc_connecting;
+ int rc_closed;
+ struct ucred *rc_ucred;
CLIENT* rc_client; /* underlying RPC client */
+ struct rpc_err rc_err;
};
CLIENT *
@@ -110,9 +119,12 @@ clnt_reconnect_create(
rc->rc_retry.tv_sec = 3;
rc->rc_retry.tv_usec = 0;
rc->rc_retries = INT_MAX;
+ rc->rc_privport = FALSE;
rc->rc_waitchan = "rpcrecv";
rc->rc_intr = 0;
rc->rc_connecting = FALSE;
+ rc->rc_closed = FALSE;
+ rc->rc_ucred = crdup(curthread->td_ucred);
rc->rc_client = NULL;
cl->cl_refs = 1;
@@ -127,16 +139,22 @@ clnt_reconnect_create(
static enum clnt_stat
clnt_reconnect_connect(CLIENT *cl)
{
+ struct thread *td = curthread;
struct rc_data *rc = (struct rc_data *)cl->cl_private;
struct socket *so;
enum clnt_stat stat;
int error;
int one = 1;
+ struct ucred *oldcred;
mtx_lock(&rc->rc_lock);
again:
+ if (rc->rc_closed) {
+ mtx_unlock(&rc->rc_lock);
+ return (RPC_CANTSEND);
+ }
if (rc->rc_connecting) {
- while (!rc->rc_client) {
+ while (!rc->rc_closed && !rc->rc_client) {
error = msleep(rc, &rc->rc_lock,
rc->rc_intr ? PCATCH : 0, "rpcrecon", 0);
if (error) {
@@ -163,7 +181,11 @@ again:
rpc_createerr.cf_error.re_errno = 0;
goto out;
}
+ if (rc->rc_privport)
+ bindresvport(so, NULL);
+ oldcred = td->td_ucred;
+ td->td_ucred = rc->rc_ucred;
if (rc->rc_nconf->nc_semantics == NC_TPI_CLTS)
rc->rc_client = clnt_dg_create(so,
(struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
@@ -172,8 +194,11 @@ again:
rc->rc_client = clnt_vc_create(so,
(struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
rc->rc_sendsz, rc->rc_recvsz);
+ td->td_ucred = oldcred;
if (!rc->rc_client) {
+ soclose(so);
+ rc->rc_err = rpc_createerr.cf_error;
stat = rpc_createerr.cf_stat;
goto out;
}
@@ -182,12 +207,19 @@ again:
CLNT_CONTROL(rc->rc_client, CLSET_CONNECT, &one);
CLNT_CONTROL(rc->rc_client, CLSET_TIMEOUT, &rc->rc_timeout);
CLNT_CONTROL(rc->rc_client, CLSET_RETRY_TIMEOUT, &rc->rc_retry);
- CLNT_CONTROL(rc->rc_client, CLSET_WAITCHAN, &rc->rc_waitchan);
+ CLNT_CONTROL(rc->rc_client, CLSET_WAITCHAN, rc->rc_waitchan);
CLNT_CONTROL(rc->rc_client, CLSET_INTERRUPTIBLE, &rc->rc_intr);
stat = RPC_SUCCESS;
out:
mtx_lock(&rc->rc_lock);
+ if (rc->rc_closed) {
+ if (rc->rc_client) {
+ CLNT_CLOSE(rc->rc_client);
+ CLNT_RELEASE(rc->rc_client);
+ rc->rc_client = NULL;
+ }
+ }
rc->rc_connecting = FALSE;
wakeup(rc);
mtx_unlock(&rc->rc_lock);
@@ -200,11 +232,9 @@ clnt_reconnect_call(
CLIENT *cl, /* client handle */
struct rpc_callextra *ext, /* call metadata */
rpcproc_t proc, /* procedure number */
- xdrproc_t xargs, /* xdr routine for args */
- void *argsp, /* pointer to args */
- xdrproc_t xresults, /* xdr routine for results */
- void *resultsp, /* pointer to results */
- struct timeval utimeout) /* seconds to wait before giving up */
+ struct mbuf *args, /* pointer to args */
+ struct mbuf **resultsp, /* pointer to results */
+ struct timeval utimeout)
{
struct rc_data *rc = (struct rc_data *)cl->cl_private;
CLIENT *client;
@@ -213,18 +243,40 @@ clnt_reconnect_call(
tries = 0;
do {
+ if (rc->rc_closed) {
+ return (RPC_CANTSEND);
+ }
+
if (!rc->rc_client) {
stat = clnt_reconnect_connect(cl);
+ if (stat == RPC_SYSTEMERROR) {
+ (void) tsleep(&fake_wchan, 0,
+ "rpccon", hz);
+ tries++;
+ if (tries >= rc->rc_retries)
+ return (stat);
+ continue;
+ }
if (stat != RPC_SUCCESS)
return (stat);
}
mtx_lock(&rc->rc_lock);
+ if (!rc->rc_client) {
+ mtx_unlock(&rc->rc_lock);
+ stat = RPC_FAILED;
+ continue;
+ }
CLNT_ACQUIRE(rc->rc_client);
client = rc->rc_client;
mtx_unlock(&rc->rc_lock);
- stat = CLNT_CALL_EXT(client, ext, proc, xargs, argsp,
- xresults, resultsp, utimeout);
+ stat = CLNT_CALL_MBUF(client, ext, proc, args,
+ resultsp, utimeout);
+
+ if (stat != RPC_SUCCESS) {
+ if (!ext)
+ CLNT_GETERR(client, &rc->rc_err);
+ }
CLNT_RELEASE(client);
if (stat == RPC_TIMEDOUT) {
@@ -241,10 +293,8 @@ clnt_reconnect_call(
}
}
- if (stat == RPC_INTR)
- break;
-
- if (stat != RPC_SUCCESS) {
+ if (stat == RPC_TIMEDOUT || stat == RPC_CANTSEND
+ || stat == RPC_CANTRECV) {
tries++;
if (tries >= rc->rc_retries)
break;
@@ -263,9 +313,14 @@ clnt_reconnect_call(
rc->rc_client = NULL;
}
mtx_unlock(&rc->rc_lock);
+ } else {
+ break;
}
} while (stat != RPC_SUCCESS);
+ KASSERT(stat != RPC_SUCCESS || *resultsp,
+ ("RPC_SUCCESS without reply"));
+
return (stat);
}
@@ -274,10 +329,7 @@ clnt_reconnect_geterr(CLIENT *cl, struct rpc_err *errp)
{
struct rc_data *rc = (struct rc_data *)cl->cl_private;
- if (rc->rc_client)
- CLNT_GETERR(rc->rc_client, errp);
- else
- memset(errp, 0, sizeof(*errp));
+ *errp = rc->rc_err;
}
static bool_t
@@ -344,7 +396,7 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
break;
case CLSET_WAITCHAN:
- rc->rc_waitchan = *(const char **)info;
+ rc->rc_waitchan = (char *)info;
if (rc->rc_client)
CLNT_CONTROL(rc->rc_client, request, info);
break;
@@ -371,6 +423,14 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
*(int *) info = rc->rc_retries;
break;
+ case CLSET_PRIVPORT:
+ rc->rc_privport = *(int *) info;
+ break;
+
+ case CLGET_PRIVPORT:
+ *(int *) info = rc->rc_privport;
+ break;
+
default:
return (FALSE);
}
@@ -379,12 +439,38 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
}
static void
+clnt_reconnect_close(CLIENT *cl)
+{
+ struct rc_data *rc = (struct rc_data *)cl->cl_private;
+ CLIENT *client;
+
+ mtx_lock(&rc->rc_lock);
+
+ if (rc->rc_closed) {
+ mtx_unlock(&rc->rc_lock);
+ return;
+ }
+
+ rc->rc_closed = TRUE;
+ client = rc->rc_client;
+ rc->rc_client = NULL;
+
+ mtx_unlock(&rc->rc_lock);
+
+ if (client) {
+ CLNT_CLOSE(client);
+ CLNT_RELEASE(client);
+ }
+}
+
+static void
clnt_reconnect_destroy(CLIENT *cl)
{
struct rc_data *rc = (struct rc_data *)cl->cl_private;
if (rc->rc_client)
CLNT_DESTROY(rc->rc_client);
+ crfree(rc->rc_ucred);
mtx_destroy(&rc->rc_lock);
mem_free(rc, sizeof(*rc));
mem_free(cl, sizeof (CLIENT));
diff --git a/sys/rpc/clnt_vc.c b/sys/rpc/clnt_vc.c
index cb09352..11fc201 100644
--- a/sys/rpc/clnt_vc.c
+++ b/sys/rpc/clnt_vc.c
@@ -64,11 +64,13 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/pcpu.h>
#include <sys/proc.h>
+#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syslog.h>
#include <sys/time.h>
#include <sys/uio.h>
+#include <netinet/tcp.h>
#include <rpc/rpc.h>
#include <rpc/rpc_com.h>
@@ -81,11 +83,12 @@ struct cmessage {
};
static enum clnt_stat clnt_vc_call(CLIENT *, struct rpc_callextra *,
- rpcproc_t, xdrproc_t, void *, xdrproc_t, void *, struct timeval);
+ rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
static void clnt_vc_geterr(CLIENT *, struct rpc_err *);
static bool_t clnt_vc_freeres(CLIENT *, xdrproc_t, void *);
static void clnt_vc_abort(CLIENT *);
static bool_t clnt_vc_control(CLIENT *, u_int, void *);
+static void clnt_vc_close(CLIENT *);
static void clnt_vc_destroy(CLIENT *);
static bool_t time_not_ok(struct timeval *);
static void clnt_vc_soupcall(struct socket *so, void *arg, int waitflag);
@@ -95,6 +98,7 @@ static struct clnt_ops clnt_vc_ops = {
.cl_abort = clnt_vc_abort,
.cl_geterr = clnt_vc_geterr,
.cl_freeres = clnt_vc_freeres,
+ .cl_close = clnt_vc_close,
.cl_destroy = clnt_vc_destroy,
.cl_control = clnt_vc_control
};
@@ -109,6 +113,7 @@ struct ct_request {
uint32_t cr_xid; /* XID of request */
struct mbuf *cr_mrep; /* reply received by upcall */
int cr_error; /* any error from upcall */
+ char cr_verf[MAX_AUTH_BYTES]; /* reply verf */
};
TAILQ_HEAD(ct_request_list, ct_request);
@@ -116,7 +121,8 @@ TAILQ_HEAD(ct_request_list, ct_request);
struct ct_data {
struct mtx ct_lock;
int ct_threads; /* number of threads in clnt_vc_call */
- bool_t ct_closing; /* TRUE if we are destroying client */
+ bool_t ct_closing; /* TRUE if we are closing */
+ bool_t ct_closed; /* TRUE if we are closed */
struct socket *ct_socket; /* connection socket */
bool_t ct_closeit; /* close it on destroy */
struct timeval ct_wait; /* wait interval in milliseconds */
@@ -165,7 +171,8 @@ clnt_vc_create(
static uint32_t disrupt;
struct __rpc_sockinfo si;
XDR xdrs;
- int error, interrupted;
+ int error, interrupted, one = 1;
+ struct sockopt sopt;
if (disrupt == 0)
disrupt = (uint32_t)(long)raddr;
@@ -176,6 +183,7 @@ clnt_vc_create(
mtx_init(&ct->ct_lock, "ct->ct_lock", NULL, MTX_DEF);
ct->ct_threads = 0;
ct->ct_closing = FALSE;
+ ct->ct_closed = FALSE;
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) {
error = soconnect(so, raddr, curthread);
@@ -208,6 +216,26 @@ clnt_vc_create(
if (!__rpc_socket2sockinfo(so, &si))
goto err;
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
+ bzero(&sopt, sizeof(sopt));
+ sopt.sopt_dir = SOPT_SET;
+ sopt.sopt_level = SOL_SOCKET;
+ sopt.sopt_name = SO_KEEPALIVE;
+ sopt.sopt_val = &one;
+ sopt.sopt_valsize = sizeof(one);
+ sosetopt(so, &sopt);
+ }
+
+ if (so->so_proto->pr_protocol == IPPROTO_TCP) {
+ bzero(&sopt, sizeof(sopt));
+ sopt.sopt_dir = SOPT_SET;
+ sopt.sopt_level = IPPROTO_TCP;
+ sopt.sopt_name = TCP_NODELAY;
+ sopt.sopt_val = &one;
+ sopt.sopt_valsize = sizeof(one);
+ sosetopt(so, &sopt);
+ }
+
ct->ct_closeit = FALSE;
/*
@@ -255,6 +283,7 @@ clnt_vc_create(
cl->cl_auth = authnone_create();
sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
+ soreserve(ct->ct_socket, sendsz, recvsz);
SOCKBUF_LOCK(&ct->ct_socket->so_rcv);
ct->ct_socket->so_upcallarg = ct;
@@ -280,24 +309,24 @@ err:
static enum clnt_stat
clnt_vc_call(
- CLIENT *cl,
- struct rpc_callextra *ext,
- rpcproc_t proc,
- xdrproc_t xdr_args,
- void *args_ptr,
- xdrproc_t xdr_results,
- void *results_ptr,
- struct timeval utimeout)
+ CLIENT *cl, /* client handle */
+ struct rpc_callextra *ext, /* call metadata */
+ rpcproc_t proc, /* procedure number */
+ struct mbuf *args, /* pointer to args */
+ struct mbuf **resultsp, /* pointer to results */
+ struct timeval utimeout)
{
struct ct_data *ct = (struct ct_data *) cl->cl_private;
AUTH *auth;
+ struct rpc_err *errp;
+ enum clnt_stat stat;
XDR xdrs;
struct rpc_msg reply_msg;
bool_t ok;
int nrefreshes = 2; /* number of times to refresh cred */
struct timeval timeout;
uint32_t xid;
- struct mbuf *mreq = NULL;
+ struct mbuf *mreq = NULL, *results;
struct ct_request *cr;
int error;
@@ -305,17 +334,20 @@ clnt_vc_call(
mtx_lock(&ct->ct_lock);
- if (ct->ct_closing) {
+ if (ct->ct_closing || ct->ct_closed) {
mtx_unlock(&ct->ct_lock);
free(cr, M_RPC);
return (RPC_CANTSEND);
}
ct->ct_threads++;
- if (ext)
+ if (ext) {
auth = ext->rc_auth;
- else
+ errp = &ext->rc_err;
+ } else {
auth = cl->cl_auth;
+ errp = &ct->ct_error;
+ }
cr->cr_mrep = NULL;
cr->cr_error = 0;
@@ -338,10 +370,11 @@ call_again:
* Leave space to pre-pend the record mark.
*/
MGETHDR(mreq, M_WAIT, MT_DATA);
- MCLGET(mreq, M_WAIT);
- mreq->m_len = 0;
mreq->m_data += sizeof(uint32_t);
- m_append(mreq, ct->ct_mpos, ct->ct_mcallc);
+ KASSERT(ct->ct_mpos + sizeof(uint32_t) <= MHLEN,
+ ("RPC header too big"));
+ bcopy(ct->ct_mcallc, mreq->m_data, ct->ct_mpos);
+ mreq->m_len = ct->ct_mpos;
/*
* The XID is the first thing in the request.
@@ -350,17 +383,16 @@ call_again:
xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
- ct->ct_error.re_status = RPC_SUCCESS;
+ errp->re_status = stat = RPC_SUCCESS;
if ((! XDR_PUTINT32(&xdrs, &proc)) ||
- (! AUTH_MARSHALL(auth, &xdrs)) ||
- (! (*xdr_args)(&xdrs, args_ptr))) {
- if (ct->ct_error.re_status == RPC_SUCCESS)
- ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (! AUTH_MARSHALL(auth, xid, &xdrs,
+ m_copym(args, 0, M_COPYALL, M_WAITOK)))) {
+ errp->re_status = stat = RPC_CANTENCODEARGS;
mtx_lock(&ct->ct_lock);
goto out;
}
- m_fixhdr(mreq);
+ mreq->m_pkthdr.len = m_length(mreq, NULL);
/*
* Prepend a record marker containing the packet length.
@@ -379,16 +411,27 @@ call_again:
*/
error = sosend(ct->ct_socket, NULL, NULL, mreq, NULL, 0, curthread);
mreq = NULL;
+ if (error == EMSGSIZE) {
+ SOCKBUF_LOCK(&ct->ct_socket->so_snd);
+ sbwait(&ct->ct_socket->so_snd);
+ SOCKBUF_UNLOCK(&ct->ct_socket->so_snd);
+ AUTH_VALIDATE(auth, xid, NULL, NULL);
+ mtx_lock(&ct->ct_lock);
+ TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
+ goto call_again;
+ }
- reply_msg.acpted_rply.ar_verf = _null_auth;
- reply_msg.acpted_rply.ar_results.where = results_ptr;
- reply_msg.acpted_rply.ar_results.proc = xdr_results;
+ reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL;
+ reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf;
+ reply_msg.acpted_rply.ar_verf.oa_length = 0;
+ reply_msg.acpted_rply.ar_results.where = NULL;
+ reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
mtx_lock(&ct->ct_lock);
if (error) {
TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
- ct->ct_error.re_errno = error;
- ct->ct_error.re_status = RPC_CANTSEND;
+ errp->re_errno = error;
+ errp->re_status = stat = RPC_CANTSEND;
goto out;
}
@@ -399,8 +442,8 @@ call_again:
*/
if (cr->cr_error) {
TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
- ct->ct_error.re_errno = cr->cr_error;
- ct->ct_error.re_status = RPC_CANTRECV;
+ errp->re_errno = cr->cr_error;
+ errp->re_status = stat = RPC_CANTRECV;
goto out;
}
if (cr->cr_mrep) {
@@ -413,7 +456,7 @@ call_again:
*/
if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
- ct->ct_error.re_status = RPC_TIMEDOUT;
+ errp->re_status = stat = RPC_TIMEDOUT;
goto out;
}
@@ -428,17 +471,18 @@ call_again:
* on the list. Turn the error code into an
* appropriate client status.
*/
- ct->ct_error.re_errno = error;
+ errp->re_errno = error;
switch (error) {
case EINTR:
- ct->ct_error.re_status = RPC_INTR;
+ stat = RPC_INTR;
break;
case EWOULDBLOCK:
- ct->ct_error.re_status = RPC_TIMEDOUT;
+ stat = RPC_TIMEDOUT;
break;
default:
- ct->ct_error.re_status = RPC_CANTRECV;
+ stat = RPC_CANTRECV;
}
+ errp->re_status = stat;
goto out;
} else {
/*
@@ -447,8 +491,8 @@ call_again:
* otherwise we have a reply.
*/
if (cr->cr_error) {
- ct->ct_error.re_errno = cr->cr_error;
- ct->ct_error.re_status = RPC_CANTRECV;
+ errp->re_errno = cr->cr_error;
+ errp->re_status = stat = RPC_CANTRECV;
goto out;
}
}
@@ -460,51 +504,59 @@ got_reply:
*/
mtx_unlock(&ct->ct_lock);
+ if (ext && ext->rc_feedback)
+ ext->rc_feedback(FEEDBACK_OK, proc, ext->rc_feedback_arg);
+
xdrmbuf_create(&xdrs, cr->cr_mrep, XDR_DECODE);
ok = xdr_replymsg(&xdrs, &reply_msg);
- XDR_DESTROY(&xdrs);
cr->cr_mrep = NULL;
- mtx_lock(&ct->ct_lock);
-
if (ok) {
if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
- (reply_msg.acpted_rply.ar_stat == SUCCESS))
- ct->ct_error.re_status = RPC_SUCCESS;
+ (reply_msg.acpted_rply.ar_stat == SUCCESS))
+ errp->re_status = stat = RPC_SUCCESS;
else
- _seterr_reply(&reply_msg, &(ct->ct_error));
-
- if (ct->ct_error.re_status == RPC_SUCCESS) {
- if (! AUTH_VALIDATE(cl->cl_auth,
- &reply_msg.acpted_rply.ar_verf)) {
- ct->ct_error.re_status = RPC_AUTHERROR;
- ct->ct_error.re_why = AUTH_INVALIDRESP;
- }
- if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
- xdrs.x_op = XDR_FREE;
- (void) xdr_opaque_auth(&xdrs,
- &(reply_msg.acpted_rply.ar_verf));
+ stat = _seterr_reply(&reply_msg, errp);
+
+ if (stat == RPC_SUCCESS) {
+ results = xdrmbuf_getall(&xdrs);
+ if (!AUTH_VALIDATE(auth, xid,
+ &reply_msg.acpted_rply.ar_verf,
+ &results)) {
+ errp->re_status = stat = RPC_AUTHERROR;
+ errp->re_why = AUTH_INVALIDRESP;
+ } else {
+ KASSERT(results,
+ ("auth validated but no result"));
+ *resultsp = results;
}
} /* end successful completion */
/*
* If unsuccesful AND error is an authentication error
* then refresh credentials and try again, else break
*/
- else if (ct->ct_error.re_status == RPC_AUTHERROR)
+ else if (stat == RPC_AUTHERROR)
/* maybe our credentials need to be refreshed ... */
if (nrefreshes > 0 &&
- AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
+ AUTH_REFRESH(auth, &reply_msg)) {
nrefreshes--;
+ XDR_DESTROY(&xdrs);
+ mtx_lock(&ct->ct_lock);
goto call_again;
}
/* end of unsuccessful completion */
} /* end of valid reply message */
else {
- ct->ct_error.re_status = RPC_CANTDECODERES;
+ errp->re_status = stat = RPC_CANTDECODERES;
}
+ XDR_DESTROY(&xdrs);
+ mtx_lock(&ct->ct_lock);
out:
mtx_assert(&ct->ct_lock, MA_OWNED);
+ KASSERT(stat != RPC_SUCCESS || *resultsp,
+ ("RPC_SUCCESS without reply"));
+
if (mreq)
m_freem(mreq);
if (cr->cr_mrep)
@@ -516,9 +568,12 @@ out:
mtx_unlock(&ct->ct_lock);
+ if (auth && stat != RPC_SUCCESS)
+ AUTH_VALIDATE(auth, xid, NULL, NULL);
+
free(cr, M_RPC);
- return (ct->ct_error.re_status);
+ return (stat);
}
static void
@@ -642,7 +697,7 @@ clnt_vc_control(CLIENT *cl, u_int request, void *info)
break;
case CLSET_WAITCHAN:
- ct->ct_waitchan = *(const char **)info;
+ ct->ct_waitchan = (const char *)info;
break;
case CLGET_WAITCHAN:
@@ -673,14 +728,26 @@ clnt_vc_control(CLIENT *cl, u_int request, void *info)
}
static void
-clnt_vc_destroy(CLIENT *cl)
+clnt_vc_close(CLIENT *cl)
{
struct ct_data *ct = (struct ct_data *) cl->cl_private;
struct ct_request *cr;
- struct socket *so = NULL;
mtx_lock(&ct->ct_lock);
+ if (ct->ct_closed) {
+ mtx_unlock(&ct->ct_lock);
+ return;
+ }
+
+ if (ct->ct_closing) {
+ while (ct->ct_closing)
+ msleep(ct, &ct->ct_lock, 0, "rpcclose", 0);
+ KASSERT(ct->ct_closed, ("client should be closed"));
+ mtx_unlock(&ct->ct_lock);
+ return;
+ }
+
if (ct->ct_socket) {
SOCKBUF_LOCK(&ct->ct_socket->so_rcv);
ct->ct_socket->so_upcallarg = NULL;
@@ -701,7 +768,25 @@ clnt_vc_destroy(CLIENT *cl)
while (ct->ct_threads)
msleep(ct, &ct->ct_lock, 0, "rpcclose", 0);
+ }
+
+ ct->ct_closing = FALSE;
+ ct->ct_closed = TRUE;
+ mtx_unlock(&ct->ct_lock);
+ wakeup(ct);
+}
+static void
+clnt_vc_destroy(CLIENT *cl)
+{
+ struct ct_data *ct = (struct ct_data *) cl->cl_private;
+ struct socket *so = NULL;
+
+ clnt_vc_close(cl);
+
+ mtx_lock(&ct->ct_lock);
+
+ if (ct->ct_socket) {
if (ct->ct_closeit) {
so = ct->ct_socket;
}
@@ -738,6 +823,7 @@ clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
struct ct_request *cr;
int error, rcvflag, foundreq;
uint32_t xid, header;
+ bool_t do_read;
uio.uio_td = curthread;
do {
@@ -746,7 +832,6 @@ clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
* record mark.
*/
if (ct->ct_record_resid == 0) {
- bool_t do_read;
/*
* Make sure there is either a whole record
@@ -795,7 +880,7 @@ clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
mtx_unlock(&ct->ct_lock);
break;
}
- memcpy(&header, mtod(m, uint32_t *), sizeof(uint32_t));
+ bcopy(mtod(m, uint32_t *), &header, sizeof(uint32_t));
header = ntohl(header);
ct->ct_record = NULL;
ct->ct_record_resid = header & 0x7fffffff;
@@ -803,6 +888,21 @@ clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
m_freem(m);
} else {
/*
+ * Wait until the socket has the whole record
+ * buffered.
+ */
+ do_read = FALSE;
+ SOCKBUF_LOCK(&so->so_rcv);
+ if (so->so_rcv.sb_cc >= ct->ct_record_resid
+ || (so->so_rcv.sb_state & SBS_CANTRCVMORE)
+ || so->so_error)
+ do_read = TRUE;
+ SOCKBUF_UNLOCK(&so->so_rcv);
+
+ if (!do_read)
+ return;
+
+ /*
* We have the record mark. Read as much as
* the socket has buffered up to the end of
* this record.
@@ -839,13 +939,14 @@ clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
* The XID is in the first uint32_t of
* the reply.
*/
- ct->ct_record =
- m_pullup(ct->ct_record, sizeof(xid));
+ if (ct->ct_record->m_len < sizeof(xid))
+ ct->ct_record =
+ m_pullup(ct->ct_record,
+ sizeof(xid));
if (!ct->ct_record)
break;
- memcpy(&xid,
- mtod(ct->ct_record, uint32_t *),
- sizeof(uint32_t));
+ bcopy(mtod(ct->ct_record, uint32_t *),
+ &xid, sizeof(uint32_t));
xid = ntohl(xid);
mtx_lock(&ct->ct_lock);
diff --git a/sys/rpc/replay.c b/sys/rpc/replay.c
new file mode 100644
index 0000000..d82fc20
--- /dev/null
+++ b/sys/rpc/replay.c
@@ -0,0 +1,248 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/hash.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+
+#include <rpc/rpc.h>
+#include <rpc/replay.h>
+
+struct replay_cache_entry {
+ int rce_hash;
+ struct rpc_msg rce_msg;
+ struct sockaddr_storage rce_addr;
+ struct rpc_msg rce_repmsg;
+ struct mbuf *rce_repbody;
+
+ TAILQ_ENTRY(replay_cache_entry) rce_link;
+ TAILQ_ENTRY(replay_cache_entry) rce_alllink;
+};
+TAILQ_HEAD(replay_cache_list, replay_cache_entry);
+
+static struct replay_cache_entry *
+ replay_alloc(struct replay_cache *rc, struct rpc_msg *msg,
+ struct sockaddr *addr, int h);
+static void replay_free(struct replay_cache *rc,
+ struct replay_cache_entry *rce);
+static void replay_prune(struct replay_cache *rc);
+
+#define REPLAY_HASH_SIZE 256
+#define REPLAY_MAX 1024
+
+struct replay_cache {
+ struct replay_cache_list rc_cache[REPLAY_HASH_SIZE];
+ struct replay_cache_list rc_all;
+ struct mtx rc_lock;
+ int rc_count;
+ size_t rc_size;
+ size_t rc_maxsize;
+};
+
+struct replay_cache *
+replay_newcache(size_t maxsize)
+{
+ struct replay_cache *rc;
+ int i;
+
+ rc = malloc(sizeof(*rc), M_RPC, M_WAITOK|M_ZERO);
+ for (i = 0; i < REPLAY_HASH_SIZE; i++)
+ TAILQ_INIT(&rc->rc_cache[i]);
+ TAILQ_INIT(&rc->rc_all);
+ mtx_init(&rc->rc_lock, "rc_lock", NULL, MTX_DEF);
+ rc->rc_maxsize = maxsize;
+
+ return (rc);
+}
+
+void
+replay_setsize(struct replay_cache *rc, size_t newmaxsize)
+{
+
+ rc->rc_maxsize = newmaxsize;
+ replay_prune(rc);
+}
+
+void
+replay_freecache(struct replay_cache *rc)
+{
+
+ mtx_lock(&rc->rc_lock);
+ while (TAILQ_FIRST(&rc->rc_all))
+ replay_free(rc, TAILQ_FIRST(&rc->rc_all));
+ mtx_destroy(&rc->rc_lock);
+ free(rc, M_RPC);
+}
+
+static struct replay_cache_entry *
+replay_alloc(struct replay_cache *rc,
+ struct rpc_msg *msg, struct sockaddr *addr, int h)
+{
+ struct replay_cache_entry *rce;
+
+ rc->rc_count++;
+ rce = malloc(sizeof(*rce), M_RPC, M_NOWAIT|M_ZERO);
+ rce->rce_hash = h;
+ rce->rce_msg = *msg;
+ bcopy(addr, &rce->rce_addr, addr->sa_len);
+
+ TAILQ_INSERT_HEAD(&rc->rc_cache[h], rce, rce_link);
+ TAILQ_INSERT_HEAD(&rc->rc_all, rce, rce_alllink);
+
+ return (rce);
+}
+
+static void
+replay_free(struct replay_cache *rc, struct replay_cache_entry *rce)
+{
+
+ rc->rc_count--;
+ TAILQ_REMOVE(&rc->rc_cache[rce->rce_hash], rce, rce_link);
+ TAILQ_REMOVE(&rc->rc_all, rce, rce_alllink);
+ if (rce->rce_repbody) {
+ rc->rc_size -= m_length(rce->rce_repbody, NULL);
+ m_freem(rce->rce_repbody);
+ }
+ free(rce, M_RPC);
+}
+
+static void
+replay_prune(struct replay_cache *rc)
+{
+ struct replay_cache_entry *rce;
+ bool_t freed_one;
+
+ if (rc->rc_count >= REPLAY_MAX || rc->rc_size > rc->rc_maxsize) {
+ freed_one = FALSE;
+ do {
+ /*
+ * Try to free an entry. Don't free in-progress entries
+ */
+ TAILQ_FOREACH_REVERSE(rce, &rc->rc_all,
+ replay_cache_list, rce_alllink) {
+ if (rce->rce_repmsg.rm_xid) {
+ replay_free(rc, rce);
+ freed_one = TRUE;
+ break;
+ }
+ }
+ } while (freed_one
+ && (rc->rc_count >= REPLAY_MAX
+ || rc->rc_size > rc->rc_maxsize));
+ }
+}
+
+enum replay_state
+replay_find(struct replay_cache *rc, struct rpc_msg *msg,
+ struct sockaddr *addr, struct rpc_msg *repmsg, struct mbuf **mp)
+{
+ int h = HASHSTEP(HASHINIT, msg->rm_xid) % REPLAY_HASH_SIZE;
+ struct replay_cache_entry *rce;
+
+ mtx_lock(&rc->rc_lock);
+ TAILQ_FOREACH(rce, &rc->rc_cache[h], rce_link) {
+ if (rce->rce_msg.rm_xid == msg->rm_xid
+ && rce->rce_msg.rm_call.cb_prog == msg->rm_call.cb_prog
+ && rce->rce_msg.rm_call.cb_vers == msg->rm_call.cb_vers
+ && rce->rce_msg.rm_call.cb_proc == msg->rm_call.cb_proc
+ && rce->rce_addr.ss_len == addr->sa_len
+ && bcmp(&rce->rce_addr, addr, addr->sa_len) == 0) {
+ if (rce->rce_repmsg.rm_xid) {
+ /*
+ * We have a reply for this
+ * message. Copy it and return. Keep
+ * replay_all LRU sorted
+ */
+ TAILQ_REMOVE(&rc->rc_all, rce, rce_alllink);
+ TAILQ_INSERT_HEAD(&rc->rc_all, rce,
+ rce_alllink);
+ *repmsg = rce->rce_repmsg;
+ if (rce->rce_repbody) {
+ *mp = m_copym(rce->rce_repbody,
+ 0, M_COPYALL, M_NOWAIT);
+ mtx_unlock(&rc->rc_lock);
+ if (!*mp)
+ return (RS_ERROR);
+ } else {
+ mtx_unlock(&rc->rc_lock);
+ }
+ return (RS_DONE);
+ } else {
+ mtx_unlock(&rc->rc_lock);
+ return (RS_INPROGRESS);
+ }
+ }
+ }
+
+ replay_prune(rc);
+
+ rce = replay_alloc(rc, msg, addr, h);
+
+ mtx_unlock(&rc->rc_lock);
+
+ if (!rce)
+ return (RS_ERROR);
+ else
+ return (RS_NEW);
+}
+
+void
+replay_setreply(struct replay_cache *rc,
+ struct rpc_msg *repmsg, struct sockaddr *addr, struct mbuf *m)
+{
+ int h = HASHSTEP(HASHINIT, repmsg->rm_xid) % REPLAY_HASH_SIZE;
+ struct replay_cache_entry *rce;
+
+ /*
+ * Copy the reply before the lock so we can sleep.
+ */
+ if (m)
+ m = m_copym(m, 0, M_COPYALL, M_WAITOK);
+
+ mtx_lock(&rc->rc_lock);
+ TAILQ_FOREACH(rce, &rc->rc_cache[h], rce_link) {
+ if (rce->rce_msg.rm_xid == repmsg->rm_xid
+ && rce->rce_addr.ss_len == addr->sa_len
+ && bcmp(&rce->rce_addr, addr, addr->sa_len) == 0) {
+ break;
+ }
+ }
+ if (rce) {
+ rce->rce_repmsg = *repmsg;
+ rce->rce_repbody = m;
+ if (m)
+ rc->rc_size += m_length(m, NULL);
+ }
+ mtx_unlock(&rc->rc_lock);
+}
diff --git a/sys/rpc/replay.h b/sys/rpc/replay.h
new file mode 100644
index 0000000..0ef7bf3
--- /dev/null
+++ b/sys/rpc/replay.h
@@ -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$
+ */
+
+#ifndef _RPC_REPLAY_H
+#define _RPC_REPLAY_H
+
+enum replay_state {
+ RS_NEW, /* new request - caller should execute */
+ RS_DONE, /* request was executed and reply sent */
+ RS_INPROGRESS, /* request is being executed now */
+ RS_ERROR /* allocation or other failure */
+};
+
+struct replay_cache;
+
+/*
+ * Create a new replay cache.
+ */
+struct replay_cache *replay_newcache(size_t);
+
+/*
+ * Set the replay cache size.
+ */
+void replay_setsize(struct replay_cache *, size_t);
+
+/*
+ * Free a replay cache. Caller must ensure that no cache entries are
+ * in-progress.
+ */
+void replay_freecache(struct replay_cache *rc);
+
+/*
+ * Check a replay cache for a message from a given address.
+ *
+ * If this is a new request, RS_NEW is returned. Caller should call
+ * replay_setreply with the results of the request.
+ *
+ * If this is a request which is currently executing
+ * (i.e. replay_setreply hasn't been called for it yet), RS_INPROGRESS
+ * is returned. The caller should silently drop the request.
+ *
+ * If a reply to this message already exists, *repmsg and *mp are set
+ * to point at the reply and, RS_DONE is returned. The caller should
+ * re-send this reply.
+ *
+ * If the attempt to update the replay cache or copy a replay failed
+ * for some reason (typically memory shortage), RS_ERROR is returned.
+ */
+enum replay_state replay_find(struct replay_cache *rc,
+ struct rpc_msg *msg, struct sockaddr *addr,
+ struct rpc_msg *repmsg, struct mbuf **mp);
+
+/*
+ * Call this after executing a request to record the reply.
+ */
+void replay_setreply(struct replay_cache *rc,
+ struct rpc_msg *repmsg, struct sockaddr *addr, struct mbuf *m);
+
+#endif /* !_RPC_REPLAY_H */
diff --git a/sys/rpc/rpc_com.h b/sys/rpc/rpc_com.h
index ad9cc68..e50e513 100644
--- a/sys/rpc/rpc_com.h
+++ b/sys/rpc/rpc_com.h
@@ -115,6 +115,7 @@ extern const char *__rpc_inet_ntop(int af, const void * __restrict src,
char * __restrict dst, socklen_t size);
extern int __rpc_inet_pton(int af, const char * __restrict src,
void * __restrict dst);
+extern int bindresvport(struct socket *so, struct sockaddr *sa);
struct xucred;
struct __rpc_xdr;
diff --git a/sys/rpc/rpc_generic.c b/sys/rpc/rpc_generic.c
index ee8ee8a..d9100b3 100644
--- a/sys/rpc/rpc_generic.c
+++ b/sys/rpc/rpc_generic.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
+#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/proc.h>
#include <sys/protosw.h>
@@ -722,6 +723,139 @@ __rpc_sockisbound(struct socket *so)
}
/*
+ * Implement XDR-style API for RPC call.
+ */
+enum clnt_stat
+clnt_call_private(
+ CLIENT *cl, /* client handle */
+ struct rpc_callextra *ext, /* call metadata */
+ rpcproc_t proc, /* procedure number */
+ xdrproc_t xargs, /* xdr routine for args */
+ void *argsp, /* pointer to args */
+ xdrproc_t xresults, /* xdr routine for results */
+ void *resultsp, /* pointer to results */
+ struct timeval utimeout) /* seconds to wait before giving up */
+{
+ XDR xdrs;
+ struct mbuf *mreq;
+ struct mbuf *mrep;
+ enum clnt_stat stat;
+
+ MGET(mreq, M_WAIT, MT_DATA);
+ MCLGET(mreq, M_WAIT);
+ mreq->m_len = 0;
+
+ xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
+ if (!xargs(&xdrs, argsp)) {
+ m_freem(mreq);
+ return (RPC_CANTENCODEARGS);
+ }
+ XDR_DESTROY(&xdrs);
+
+ stat = CLNT_CALL_MBUF(cl, ext, proc, mreq, &mrep, utimeout);
+ m_freem(mreq);
+
+ if (stat == RPC_SUCCESS) {
+ xdrmbuf_create(&xdrs, mrep, XDR_DECODE);
+ if (!xresults(&xdrs, resultsp)) {
+ XDR_DESTROY(&xdrs);
+ return (RPC_CANTDECODERES);
+ }
+ XDR_DESTROY(&xdrs);
+ }
+
+ return (stat);
+}
+
+/*
+ * Bind a socket to a privileged IP port
+ */
+int
+bindresvport(struct socket *so, struct sockaddr *sa)
+{
+ int old, error, af;
+ bool_t freesa = FALSE;
+ struct sockaddr_in *sin;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+ struct sockopt opt;
+ int proto, portrange, portlow;
+ u_int16_t *portp;
+ socklen_t salen;
+
+ if (sa == NULL) {
+ error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
+ if (error)
+ return (error);
+ freesa = TRUE;
+ af = sa->sa_family;
+ salen = sa->sa_len;
+ memset(sa, 0, sa->sa_len);
+ } else {
+ af = sa->sa_family;
+ salen = sa->sa_len;
+ }
+
+ switch (af) {
+ case AF_INET:
+ proto = IPPROTO_IP;
+ portrange = IP_PORTRANGE;
+ portlow = IP_PORTRANGE_LOW;
+ sin = (struct sockaddr_in *)sa;
+ portp = &sin->sin_port;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ proto = IPPROTO_IPV6;
+ portrange = IPV6_PORTRANGE;
+ portlow = IPV6_PORTRANGE_LOW;
+ sin6 = (struct sockaddr_in6 *)sa;
+ portp = &sin6->sin6_port;
+ break;
+#endif
+ default:
+ return (EPFNOSUPPORT);
+ }
+
+ sa->sa_family = af;
+ sa->sa_len = salen;
+
+ if (*portp == 0) {
+ bzero(&opt, sizeof(opt));
+ opt.sopt_dir = SOPT_GET;
+ opt.sopt_level = proto;
+ opt.sopt_name = portrange;
+ opt.sopt_val = &old;
+ opt.sopt_valsize = sizeof(old);
+ error = sogetopt(so, &opt);
+ if (error)
+ goto out;
+
+ opt.sopt_dir = SOPT_SET;
+ opt.sopt_val = &portlow;
+ error = sosetopt(so, &opt);
+ if (error)
+ goto out;
+ }
+
+ error = sobind(so, sa, curthread);
+
+ if (*portp == 0) {
+ if (error) {
+ opt.sopt_dir = SOPT_SET;
+ opt.sopt_val = &old;
+ sosetopt(so, &opt);
+ }
+ }
+out:
+ if (freesa)
+ free(sa, M_SONAME);
+
+ return (error);
+}
+
+/*
* Kernel module glue
*/
static int
diff --git a/sys/rpc/rpc_msg.h b/sys/rpc/rpc_msg.h
index 707250a..ff2a6d8 100644
--- a/sys/rpc/rpc_msg.h
+++ b/sys/rpc/rpc_msg.h
@@ -208,7 +208,7 @@ extern bool_t xdr_rejected_reply(XDR *, struct rejected_reply *);
* struct rpc_msg *msg;
* struct rpc_err *error;
*/
-extern void _seterr_reply(struct rpc_msg *, struct rpc_err *);
+extern enum clnt_stat _seterr_reply(struct rpc_msg *, struct rpc_err *);
__END_DECLS
#endif /* !_RPC_RPC_MSG_H */
diff --git a/sys/rpc/rpc_prot.c b/sys/rpc/rpc_prot.c
index 16f602f..294c4e3 100644
--- a/sys/rpc/rpc_prot.c
+++ b/sys/rpc/rpc_prot.c
@@ -64,8 +64,8 @@ MALLOC_DEFINE(M_RPC, "rpc", "Remote Procedure Call");
#define assert(exp) KASSERT(exp, ("bad arguments"))
-static void accepted(enum accept_stat, struct rpc_err *);
-static void rejected(enum reject_stat, struct rpc_err *);
+static enum clnt_stat accepted(enum accept_stat, struct rpc_err *);
+static enum clnt_stat rejected(enum reject_stat, struct rpc_err *);
/* * * * * * * * * * * * * * XDR Authentication * * * * * * * * * * * */
@@ -111,7 +111,11 @@ xdr_accepted_reply(XDR *xdrs, struct accepted_reply *ar)
switch (ar->ar_stat) {
case SUCCESS:
- return ((*(ar->ar_results.proc))(xdrs, ar->ar_results.where));
+ if (ar->ar_results.proc != (xdrproc_t) xdr_void)
+ return ((*(ar->ar_results.proc))(xdrs,
+ ar->ar_results.where));
+ else
+ return (TRUE);
case PROG_MISMATCH:
if (! xdr_uint32_t(xdrs, &(ar->ar_vers.low)))
@@ -171,12 +175,34 @@ static const struct xdr_discrim reply_dscrm[3] = {
bool_t
xdr_replymsg(XDR *xdrs, struct rpc_msg *rmsg)
{
+ int32_t *buf;
enum msg_type *prm_direction;
enum reply_stat *prp_stat;
assert(xdrs != NULL);
assert(rmsg != NULL);
+ if (xdrs->x_op == XDR_DECODE) {
+ buf = XDR_INLINE(xdrs, 3 * BYTES_PER_XDR_UNIT);
+ if (buf != NULL) {
+ rmsg->rm_xid = IXDR_GET_UINT32(buf);
+ rmsg->rm_direction = IXDR_GET_ENUM(buf, enum msg_type);
+ if (rmsg->rm_direction != REPLY) {
+ return (FALSE);
+ }
+ rmsg->rm_reply.rp_stat =
+ IXDR_GET_ENUM(buf, enum reply_stat);
+ if (rmsg->rm_reply.rp_stat == MSG_ACCEPTED)
+ return (xdr_accepted_reply(xdrs,
+ &rmsg->acpted_rply));
+ else if (rmsg->rm_reply.rp_stat == MSG_DENIED)
+ return (xdr_rejected_reply(xdrs,
+ &rmsg->rjcted_rply));
+ else
+ return (FALSE);
+ }
+ }
+
prm_direction = &rmsg->rm_direction;
prp_stat = &rmsg->rm_reply.rp_stat;
@@ -220,7 +246,7 @@ xdr_callhdr(XDR *xdrs, struct rpc_msg *cmsg)
/* ************************** Client utility routine ************* */
-static void
+static enum clnt_stat
accepted(enum accept_stat acpt_stat, struct rpc_err *error)
{
@@ -230,36 +256,32 @@ accepted(enum accept_stat acpt_stat, struct rpc_err *error)
case PROG_UNAVAIL:
error->re_status = RPC_PROGUNAVAIL;
- return;
+ return (RPC_PROGUNAVAIL);
case PROG_MISMATCH:
error->re_status = RPC_PROGVERSMISMATCH;
- return;
+ return (RPC_PROGVERSMISMATCH);
case PROC_UNAVAIL:
- error->re_status = RPC_PROCUNAVAIL;
- return;
+ return (RPC_PROCUNAVAIL);
case GARBAGE_ARGS:
- error->re_status = RPC_CANTDECODEARGS;
- return;
+ return (RPC_CANTDECODEARGS);
case SYSTEM_ERR:
- error->re_status = RPC_SYSTEMERROR;
- return;
+ return (RPC_SYSTEMERROR);
case SUCCESS:
- error->re_status = RPC_SUCCESS;
- return;
+ return (RPC_SUCCESS);
}
/* NOTREACHED */
/* something's wrong, but we don't know what ... */
- error->re_status = RPC_FAILED;
error->re_lb.s1 = (int32_t)MSG_ACCEPTED;
error->re_lb.s2 = (int32_t)acpt_stat;
+ return (RPC_FAILED);
}
-static void
+static enum clnt_stat
rejected(enum reject_stat rjct_stat, struct rpc_err *error)
{
@@ -267,26 +289,25 @@ rejected(enum reject_stat rjct_stat, struct rpc_err *error)
switch (rjct_stat) {
case RPC_MISMATCH:
- error->re_status = RPC_VERSMISMATCH;
- return;
+ return (RPC_VERSMISMATCH);
case AUTH_ERROR:
- error->re_status = RPC_AUTHERROR;
- return;
+ return (RPC_AUTHERROR);
}
/* something's wrong, but we don't know what ... */
/* NOTREACHED */
- error->re_status = RPC_FAILED;
error->re_lb.s1 = (int32_t)MSG_DENIED;
error->re_lb.s2 = (int32_t)rjct_stat;
+ return (RPC_FAILED);
}
/*
* given a reply message, fills in the error
*/
-void
+enum clnt_stat
_seterr_reply(struct rpc_msg *msg, struct rpc_err *error)
{
+ enum clnt_stat stat;
assert(msg != NULL);
assert(error != NULL);
@@ -296,22 +317,24 @@ _seterr_reply(struct rpc_msg *msg, struct rpc_err *error)
case MSG_ACCEPTED:
if (msg->acpted_rply.ar_stat == SUCCESS) {
- error->re_status = RPC_SUCCESS;
- return;
+ stat = RPC_SUCCESS;
+ return (stat);
}
- accepted(msg->acpted_rply.ar_stat, error);
+ stat = accepted(msg->acpted_rply.ar_stat, error);
break;
case MSG_DENIED:
- rejected(msg->rjcted_rply.rj_stat, error);
+ stat = rejected(msg->rjcted_rply.rj_stat, error);
break;
default:
- error->re_status = RPC_FAILED;
+ stat = RPC_FAILED;
error->re_lb.s1 = (int32_t)(msg->rm_reply.rp_stat);
break;
}
- switch (error->re_status) {
+ error->re_status = stat;
+
+ switch (stat) {
case RPC_VERSMISMATCH:
error->re_vers.low = msg->rjcted_rply.rj_vers.low;
@@ -345,4 +368,6 @@ _seterr_reply(struct rpc_msg *msg, struct rpc_err *error)
default:
break;
}
+
+ return (stat);
}
diff --git a/sys/rpc/rpcsec_gss.h b/sys/rpc/rpcsec_gss.h
new file mode 100644
index 0000000..563205c
--- /dev/null
+++ b/sys/rpc/rpcsec_gss.h
@@ -0,0 +1,189 @@
+/*-
+ * 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 <kgssapi/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
+
+#ifdef _KERNEL
+AUTH *rpc_gss_secfind(CLIENT *clnt, struct ucred *cred,
+ const char *principal, gss_OID mech_oid, rpc_gss_service_t service);
+void rpc_gss_secpurge(CLIENT *clnt);
+#endif
+AUTH *rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred,
+ 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);
+void rpc_gss_clear_svc_name(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);
+void rpc_gss_clear_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.
+ */
+#ifndef _KERNEL
+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);
+#endif
+bool_t __rpc_gss_set_error(int rpc_gss_error, int system_error);
+
+__END_DECLS
+
+#endif /* !_RPCSEC_GSS_H */
diff --git a/sys/rpc/rpcsec_gss/rpcsec_gss.c b/sys/rpc/rpcsec_gss/rpcsec_gss.c
new file mode 100644
index 0000000..790804d
--- /dev/null
+++ b/sys/rpc/rpcsec_gss/rpcsec_gss.c
@@ -0,0 +1,1064 @@
+/*-
+ * 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.
+ */
+/*
+ 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/hash.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/refcount.h>
+#include <sys/sx.h>
+#include <sys/ucred.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 *, uint32_t, XDR *, struct mbuf *);
+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 *, uint32_t, struct opaque_auth *,
+ struct mbuf **);
+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,
+ RPCSEC_GSS_DESTROYING
+};
+
+struct rpc_pending_request {
+ uint32_t pr_xid; /* XID of rpc */
+ uint32_t pr_seq; /* matching GSS seq */
+ LIST_ENTRY(rpc_pending_request) pr_link;
+};
+LIST_HEAD(rpc_pending_request_list, rpc_pending_request);
+
+struct rpc_gss_data {
+ volatile u_int gd_refs; /* number of current users */
+ struct mtx gd_lock;
+ uint32_t gd_hash;
+ AUTH *gd_auth; /* link back to AUTH */
+ struct ucred *gd_ucred; /* matching local cred */
+ char *gd_principal; /* server principal name */
+ 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_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 */
+ uint32_t gd_seq; /* next sequence number */
+ u_int gd_win; /* sequence window */
+ struct rpc_pending_request_list gd_reqs;
+ TAILQ_ENTRY(rpc_gss_data) gd_link;
+ TAILQ_ENTRY(rpc_gss_data) gd_alllink;
+};
+TAILQ_HEAD(rpc_gss_data_list, rpc_gss_data);
+
+#define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
+
+static struct timeval AUTH_TIMEOUT = { 25, 0 };
+
+#define RPC_GSS_HASH_SIZE 11
+#define RPC_GSS_MAX 256
+static struct rpc_gss_data_list rpc_gss_cache[RPC_GSS_HASH_SIZE];
+static struct rpc_gss_data_list rpc_gss_all;
+static struct sx rpc_gss_lock;
+static int rpc_gss_count;
+
+static AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *,
+ gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
+ rpc_gss_options_ret_t *);
+
+static void
+rpc_gss_hashinit(void *dummy)
+{
+ int i;
+
+ for (i = 0; i < RPC_GSS_HASH_SIZE; i++)
+ TAILQ_INIT(&rpc_gss_cache[i]);
+ TAILQ_INIT(&rpc_gss_all);
+ sx_init(&rpc_gss_lock, "rpc_gss_lock");
+}
+SYSINIT(rpc_gss_hashinit, SI_SUB_KMEM, SI_ORDER_ANY, rpc_gss_hashinit, NULL);
+
+static uint32_t
+rpc_gss_hash(const char *principal, gss_OID mech,
+ struct ucred *cred, rpc_gss_service_t service)
+{
+ uint32_t h;
+
+ h = HASHSTEP(HASHINIT, cred->cr_uid);
+ h = hash32_str(principal, h);
+ h = hash32_buf(mech->elements, mech->length, h);
+ h = HASHSTEP(h, (int) service);
+
+ return (h % RPC_GSS_HASH_SIZE);
+}
+
+/*
+ * Simplified interface to create a security association for the
+ * current thread's * ucred.
+ */
+AUTH *
+rpc_gss_secfind(CLIENT *clnt, struct ucred *cred, const char *principal,
+ gss_OID mech_oid, rpc_gss_service_t service)
+{
+ uint32_t h, th;
+ AUTH *auth;
+ struct rpc_gss_data *gd, *tgd;
+
+ if (rpc_gss_count > RPC_GSS_MAX) {
+ while (rpc_gss_count > RPC_GSS_MAX) {
+ sx_xlock(&rpc_gss_lock);
+ tgd = TAILQ_FIRST(&rpc_gss_all);
+ th = tgd->gd_hash;
+ TAILQ_REMOVE(&rpc_gss_cache[th], tgd, gd_link);
+ TAILQ_REMOVE(&rpc_gss_all, tgd, gd_alllink);
+ rpc_gss_count--;
+ sx_xunlock(&rpc_gss_lock);
+ AUTH_DESTROY(tgd->gd_auth);
+ }
+ }
+
+ /*
+ * See if we already have an AUTH which matches.
+ */
+ h = rpc_gss_hash(principal, mech_oid, cred, service);
+
+again:
+ sx_slock(&rpc_gss_lock);
+ TAILQ_FOREACH(gd, &rpc_gss_cache[h], gd_link) {
+ if (gd->gd_ucred->cr_uid == cred->cr_uid
+ && !strcmp(gd->gd_principal, principal)
+ && gd->gd_mech == mech_oid
+ && gd->gd_cred.gc_svc == service) {
+ refcount_acquire(&gd->gd_refs);
+ if (sx_try_upgrade(&rpc_gss_lock)) {
+ /*
+ * Keep rpc_gss_all LRU sorted.
+ */
+ TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
+ TAILQ_INSERT_TAIL(&rpc_gss_all, gd,
+ gd_alllink);
+ sx_xunlock(&rpc_gss_lock);
+ } else {
+ sx_sunlock(&rpc_gss_lock);
+ }
+ return (gd->gd_auth);
+ }
+ }
+ sx_sunlock(&rpc_gss_lock);
+
+ /*
+ * We missed in the cache - create a new association.
+ */
+ auth = rpc_gss_seccreate_int(clnt, cred, principal, mech_oid, service,
+ GSS_C_QOP_DEFAULT, NULL, NULL);
+ if (!auth)
+ return (NULL);
+
+ gd = AUTH_PRIVATE(auth);
+ gd->gd_hash = h;
+
+ sx_xlock(&rpc_gss_lock);
+ TAILQ_FOREACH(tgd, &rpc_gss_cache[h], gd_link) {
+ if (tgd->gd_ucred->cr_uid == cred->cr_uid
+ && !strcmp(tgd->gd_principal, principal)
+ && tgd->gd_mech == mech_oid
+ && tgd->gd_cred.gc_svc == service) {
+ /*
+ * We lost a race to create the AUTH that
+ * matches this cred.
+ */
+ sx_xunlock(&rpc_gss_lock);
+ AUTH_DESTROY(auth);
+ goto again;
+ }
+ }
+
+ rpc_gss_count++;
+ TAILQ_INSERT_TAIL(&rpc_gss_cache[h], gd, gd_link);
+ TAILQ_INSERT_TAIL(&rpc_gss_all, gd, gd_alllink);
+ refcount_acquire(&gd->gd_refs); /* one for the cache, one for user */
+ sx_xunlock(&rpc_gss_lock);
+
+ return (auth);
+}
+
+void
+rpc_gss_secpurge(CLIENT *clnt)
+{
+ uint32_t h;
+ struct rpc_gss_data *gd, *tgd;
+
+ TAILQ_FOREACH_SAFE(gd, &rpc_gss_all, gd_alllink, tgd) {
+ if (gd->gd_clnt == clnt) {
+ sx_xlock(&rpc_gss_lock);
+ h = gd->gd_hash;
+ TAILQ_REMOVE(&rpc_gss_cache[h], gd, gd_link);
+ TAILQ_REMOVE(&rpc_gss_all, gd, gd_alllink);
+ rpc_gss_count--;
+ sx_xunlock(&rpc_gss_lock);
+ AUTH_DESTROY(gd->gd_auth);
+ }
+ }
+}
+
+AUTH *
+rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, 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)
+{
+ gss_OID oid;
+ u_int qop_num;
+
+ /*
+ * 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;
+ }
+
+ return (rpc_gss_seccreate_int(clnt, cred, principal, oid, service,
+ qop_num, options_req, options_ret));
+}
+
+static AUTH *
+rpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred, const char *principal,
+ gss_OID mech_oid, rpc_gss_service_t service, u_int qop_num,
+ rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
+{
+ AUTH *auth;
+ rpc_gss_options_ret_t options;
+ struct rpc_gss_data *gd;
+
+ /*
+ * 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));
+
+ rpc_gss_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;
+ mem_free(auth, sizeof(*auth));
+ return (NULL);
+ }
+
+ auth->ah_ops = &rpc_gss_ops;
+ auth->ah_private = (caddr_t) gd;
+ auth->ah_cred.oa_flavor = RPCSEC_GSS;
+
+ refcount_init(&gd->gd_refs, 1);
+ mtx_init(&gd->gd_lock, "gd->gd_lock", NULL, MTX_DEF);
+ gd->gd_auth = auth;
+ gd->gd_ucred = crdup(cred);
+ gd->gd_principal = strdup(principal, M_RPC);
+
+
+ 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;
+ }
+ CLNT_ACQUIRE(clnt);
+ gd->gd_clnt = clnt;
+ gd->gd_ctx = GSS_C_NO_CONTEXT;
+ gd->gd_mech = 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;
+ LIST_INIT(&gd->gd_reqs);
+
+ if (!rpc_gss_init(auth, options_ret)) {
+ goto bad;
+ }
+
+ 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_purge_xid(struct rpc_gss_data *gd, uint32_t xid)
+{
+ struct rpc_pending_request *pr, *npr;
+ struct rpc_pending_request_list reqs;
+
+ LIST_INIT(&reqs);
+ mtx_lock(&gd->gd_lock);
+ LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
+ if (pr->pr_xid == xid) {
+ LIST_REMOVE(pr, pr_link);
+ LIST_INSERT_HEAD(&reqs, pr, pr_link);
+ }
+ }
+
+ mtx_unlock(&gd->gd_lock);
+
+ LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
+ mem_free(pr, sizeof(*pr));
+ }
+}
+
+static uint32_t
+rpc_gss_alloc_seq(struct rpc_gss_data *gd)
+{
+ uint32_t seq;
+
+ mtx_lock(&gd->gd_lock);
+ seq = gd->gd_seq;
+ gd->gd_seq++;
+ mtx_unlock(&gd->gd_lock);
+
+ return (seq);
+}
+
+static void
+rpc_gss_nextverf(__unused AUTH *auth)
+{
+
+ /* not used */
+}
+
+static bool_t
+rpc_gss_marshal(AUTH *auth, uint32_t xid, XDR *xdrs, struct mbuf *args)
+{
+ struct rpc_gss_data *gd;
+ struct rpc_pending_request *pr;
+ uint32_t seq;
+ XDR tmpxdrs;
+ struct rpc_gss_cred gsscred;
+ char credbuf[MAX_AUTH_BYTES];
+ struct opaque_auth creds, verf;
+ gss_buffer_desc rpcbuf, checksum;
+ OM_uint32 maj_stat, min_stat;
+ bool_t xdr_stat;
+
+ rpc_gss_log_debug("in rpc_gss_marshal()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ gsscred = gd->gd_cred;
+ seq = rpc_gss_alloc_seq(gd);
+ gsscred.gc_seq = seq;
+
+ xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
+ if (!xdr_rpc_gss_cred(&tmpxdrs, &gsscred)) {
+ 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);
+
+ xdr_opaque_auth(xdrs, &creds);
+
+ 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);
+ }
+ xdrmbuf_append(xdrs, args);
+ return (TRUE);
+ } else {
+ /*
+ * Keep track of this XID + seq pair so that we can do
+ * the matching gss_verify_mic in AUTH_VALIDATE.
+ */
+ pr = mem_alloc(sizeof(struct rpc_pending_request));
+ mtx_lock(&gd->gd_lock);
+ pr->pr_xid = xid;
+ pr->pr_seq = seq;
+ LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
+ mtx_unlock(&gd->gd_lock);
+
+ /*
+ * Checksum serialized RPC header, up to and including
+ * credential. For the in-kernel environment, we
+ * assume that our XDR stream is on a contiguous
+ * memory buffer (e.g. an mbuf).
+ */
+ rpcbuf.length = XDR_GETPOS(xdrs);
+ XDR_SETPOS(xdrs, 0);
+ rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
+
+ maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
+ &rpcbuf, &checksum);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ rpc_gss_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) {
+ xdrmbuf_append(xdrs, args);
+ return (TRUE);
+ } else {
+ if (!xdr_rpc_gss_wrap_data(&args,
+ gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
+ seq))
+ return (FALSE);
+ xdrmbuf_append(xdrs, args);
+ return (TRUE);
+ }
+ }
+
+ return (TRUE);
+}
+
+static bool_t
+rpc_gss_validate(AUTH *auth, uint32_t xid, struct opaque_auth *verf,
+ struct mbuf **resultsp)
+{
+ struct rpc_gss_data *gd;
+ struct rpc_pending_request *pr, *npr;
+ struct rpc_pending_request_list reqs;
+ gss_qop_t qop_state;
+ uint32_t num, seq;
+ gss_buffer_desc signbuf, checksum;
+ OM_uint32 maj_stat, min_stat;
+
+ rpc_gss_log_debug("in rpc_gss_validate()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ /*
+ * The client will call us with a NULL verf when it gives up
+ * on an XID.
+ */
+ if (!verf) {
+ rpc_gss_purge_xid(gd, xid);
+ return (TRUE);
+ }
+
+ 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) {
+ printf("gss_validate: out of memory\n");
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
+ m_freem(*resultsp);
+ *resultsp = NULL;
+ return (FALSE);
+ }
+ memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
+ gd->gd_verf.length = verf->oa_length;
+
+ return (TRUE);
+ }
+
+ /*
+ * We need to check the verifier against all the requests
+ * we've send for this XID - for unreliable protocols, we
+ * retransmit with the same XID but different sequence
+ * number. We temporarily take this set of requests out of the
+ * list so that we can work through the list without having to
+ * hold the lock.
+ */
+ mtx_lock(&gd->gd_lock);
+ LIST_INIT(&reqs);
+ LIST_FOREACH_SAFE(pr, &gd->gd_reqs, pr_link, npr) {
+ if (pr->pr_xid == xid) {
+ LIST_REMOVE(pr, pr_link);
+ LIST_INSERT_HEAD(&reqs, pr, pr_link);
+ }
+ }
+ mtx_unlock(&gd->gd_lock);
+ LIST_FOREACH(pr, &reqs, pr_link) {
+ if (pr->pr_xid == xid) {
+ seq = pr->pr_seq;
+ num = htonl(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) {
+ continue;
+ }
+ if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
+ rpc_gss_destroy_context(auth, TRUE);
+ break;
+ }
+ //rpc_gss_purge_reqs(gd, seq);
+ LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr)
+ mem_free(pr, sizeof(*pr));
+
+ if (gd->gd_cred.gc_svc == rpc_gss_svc_none) {
+ return (TRUE);
+ } else {
+ if (!xdr_rpc_gss_unwrap_data(resultsp,
+ gd->gd_ctx, gd->gd_qop,
+ gd->gd_cred.gc_svc, seq)) {
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+ }
+ }
+
+ /*
+ * We didn't match - put back any entries for this XID so that
+ * a future call to validate can retry.
+ */
+ mtx_lock(&gd->gd_lock);
+ LIST_FOREACH_SAFE(pr, &reqs, pr_link, npr) {
+ LIST_REMOVE(pr, pr_link);
+ LIST_INSERT_HEAD(&gd->gd_reqs, pr, pr_link);
+ }
+ mtx_unlock(&gd->gd_lock);
+
+ /*
+ * Nothing matches - give up.
+ */
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
+ m_freem(*resultsp);
+ *resultsp = NULL;
+ return (FALSE);
+}
+
+static bool_t
+rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
+{
+ struct thread *td = curthread;
+ struct ucred *crsave;
+ struct rpc_gss_data *gd;
+ struct rpc_gss_init_res gr;
+ gss_buffer_desc principal_desc;
+ gss_buffer_desc *recv_tokenp, recv_token, send_token;
+ gss_name_t name;
+ OM_uint32 maj_stat, min_stat, call_stat;
+ const char *mech;
+ struct rpc_callextra ext;
+
+ rpc_gss_log_debug("in rpc_gss_refresh()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ mtx_lock(&gd->gd_lock);
+ /*
+ * If the context isn't in START state, someone else is
+ * refreshing - we wait till they are done. If they fail, they
+ * will put the state back to START and we can try (most
+ * likely to also fail).
+ */
+ while (gd->gd_state != RPCSEC_GSS_START
+ && gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
+ msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
+ }
+ if (gd->gd_state == RPCSEC_GSS_ESTABLISHED) {
+ mtx_unlock(&gd->gd_lock);
+ return (TRUE);
+ }
+ gd->gd_state = RPCSEC_GSS_CONTEXT;
+ mtx_unlock(&gd->gd_lock);
+
+ principal_desc.value = (void *)gd->gd_principal;
+ principal_desc.length = strlen(gd->gd_principal);
+ maj_stat = gss_import_name(&min_stat, &principal_desc,
+ GSS_C_NT_HOSTBASED_SERVICE, &name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ options_ret->major_status = maj_stat;
+ options_ret->minor_status = min_stat;
+ goto out;
+ }
+
+ /* GSS context establishment loop. */
+ 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));
+ memset(options_ret, 0, sizeof(*options_ret));
+ options_ret->major_status = GSS_S_FAILURE;
+ recv_tokenp = GSS_C_NO_BUFFER;
+
+ for (;;) {
+ crsave = td->td_ucred;
+ td->td_ucred = gd->gd_ucred;
+ maj_stat = gss_init_sec_context(&min_stat,
+ gd->gd_options.my_cred,
+ &gd->gd_ctx,
+ 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);
+ td->td_ucred = crsave;
+
+ /*
+ * 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 (gd->gd_mech && rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
+ strlcpy(options_ret->actual_mechanism,
+ mech,
+ sizeof(options_ret->actual_mechanism));
+ }
+ if (maj_stat != GSS_S_COMPLETE &&
+ maj_stat != GSS_S_CONTINUE_NEEDED) {
+ rpc_gss_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));
+
+ bzero(&ext, sizeof(ext));
+ ext.rc_auth = auth;
+ call_stat = CLNT_CALL_EXT(gd->gd_clnt, &ext, 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) {
+ rpc_gss_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) {
+ rpc_gss_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;
+ break;
+ }
+
+ 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;
+
+ gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
+ gd->gd_seq = 1;
+ gd->gd_win = gr.gr_win;
+ break;
+ }
+ }
+
+ gss_release_name(&min_stat, &name);
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &gd->gd_verf);
+
+out:
+ /* 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);
+ if (gd->gd_ctx) {
+ gss_delete_sec_context(&min_stat, &gd->gd_ctx,
+ GSS_C_NO_BUFFER);
+ }
+ mtx_lock(&gd->gd_lock);
+ gd->gd_state = RPCSEC_GSS_START;
+ wakeup(gd);
+ mtx_unlock(&gd->gd_lock);
+ return (FALSE);
+ }
+
+ mtx_lock(&gd->gd_lock);
+ gd->gd_state = RPCSEC_GSS_ESTABLISHED;
+ wakeup(gd);
+ mtx_unlock(&gd->gd_lock);
+
+ 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;
+ struct rpc_pending_request *pr;
+ OM_uint32 min_stat;
+ struct rpc_callextra ext;
+
+ rpc_gss_log_debug("in rpc_gss_destroy_context()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ mtx_lock(&gd->gd_lock);
+ /*
+ * If the context isn't in ESTABISHED state, someone else is
+ * destroying/refreshing - we wait till they are done.
+ */
+ if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
+ while (gd->gd_state != RPCSEC_GSS_START
+ && gd->gd_state != RPCSEC_GSS_ESTABLISHED)
+ msleep(gd, &gd->gd_lock, 0, "gssstate", 0);
+ mtx_unlock(&gd->gd_lock);
+ return;
+ }
+ gd->gd_state = RPCSEC_GSS_DESTROYING;
+ mtx_unlock(&gd->gd_lock);
+
+ if (send_destroy) {
+ gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
+ bzero(&ext, sizeof(ext));
+ ext.rc_auth = auth;
+ CLNT_CALL_EXT(gd->gd_clnt, &ext, NULLPROC,
+ (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
+ }
+
+ while ((pr = LIST_FIRST(&gd->gd_reqs)) != NULL) {
+ LIST_REMOVE(pr, pr_link);
+ mem_free(pr, sizeof(*pr));
+ }
+
+ /*
+ * 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);
+
+ mtx_lock(&gd->gd_lock);
+ gd->gd_state = RPCSEC_GSS_START;
+ wakeup(gd);
+ mtx_unlock(&gd->gd_lock);
+}
+
+static void
+rpc_gss_destroy(AUTH *auth)
+{
+ struct rpc_gss_data *gd;
+
+ rpc_gss_log_debug("in rpc_gss_destroy()");
+
+ gd = AUTH_PRIVATE(auth);
+
+ if (!refcount_release(&gd->gd_refs))
+ return;
+
+ rpc_gss_destroy_context(auth, TRUE);
+
+ CLNT_RELEASE(gd->gd_clnt);
+ crfree(gd->gd_ucred);
+ free(gd->gd_principal, M_RPC);
+ if (gd->gd_verf.value)
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &gd->gd_verf);
+ mtx_destroy(&gd->gd_lock);
+
+ mem_free(gd, sizeof(*gd));
+ mem_free(auth, sizeof(*auth));
+}
+
+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 {
+ rpc_gss_log_status("gss_wrap_size_limit", gd->gd_mech,
+ maj_stat, min_stat);
+ return (0);
+ }
+}
diff --git a/sys/rpc/rpcsec_gss/rpcsec_gss_conf.c b/sys/rpc/rpcsec_gss/rpcsec_gss_conf.c
new file mode 100644
index 0000000..b5e99d4
--- /dev/null
+++ b/sys/rpc/rpcsec_gss/rpcsec_gss_conf.c
@@ -0,0 +1,163 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kobj.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+
+#include "rpcsec_gss_int.h"
+
+bool_t
+rpc_gss_mech_to_oid(const char *mech, gss_OID *oid_ret)
+{
+ gss_OID oid = kgss_find_mech_by_name(mech);
+
+ if (oid) {
+ *oid_ret = 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)
+{
+ const char *name = kgss_find_mech_by_oid(oid);
+
+ if (name) {
+ *mech_ret = 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)
+{
+
+ if (!strcmp(qop, "default")) {
+ *num_ret = GSS_C_QOP_DEFAULT;
+ 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)
+{
+
+ if (num == GSS_C_QOP_DEFAULT)
+ return "default";
+
+ return (NULL);
+}
+
+const char **
+rpc_gss_get_mechanisms(void)
+{
+ static const char **mech_names = NULL;
+ struct kgss_mech *km;
+ int count;
+
+ if (mech_names)
+ return (mech_names);
+
+ count = 0;
+ LIST_FOREACH(km, &kgss_mechs, km_link) {
+ count++;
+ }
+ count++;
+
+ mech_names = malloc(count * sizeof(const char *), M_RPC, M_WAITOK);
+ count = 0;
+ LIST_FOREACH(km, &kgss_mechs, km_link) {
+ mech_names[count++] = km->km_mech_name;
+ }
+ mech_names[count++] = NULL;
+
+ return (mech_names);
+}
+
+#if 0
+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);
+}
+#endif
+
+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)
+{
+ gss_OID oid = kgss_find_mech_by_name(mech);
+
+ if (oid)
+ return (TRUE);
+ else
+ return (FALSE);
+}
+
diff --git a/sys/rpc/rpcsec_gss/rpcsec_gss_int.h b/sys/rpc/rpcsec_gss/rpcsec_gss_int.h
new file mode 100644
index 0000000..4f38828
--- /dev/null
+++ b/sys/rpc/rpcsec_gss/rpcsec_gss_int.h
@@ -0,0 +1,94 @@
+/*
+ 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
+
+#include <kgssapi/gssapi_impl.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_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(struct mbuf **argsp,
+ gss_ctx_id_t ctx, gss_qop_t qop, rpc_gss_service_t svc,
+ u_int seq);
+bool_t xdr_rpc_gss_unwrap_data(struct mbuf **resultsp,
+ 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 rpc_gss_log_debug(const char *fmt, ...);
+void rpc_gss_log_status(const char *m, gss_OID mech, OM_uint32 major,
+ OM_uint32 minor);
+
+__END_DECLS
+
+#endif /* !_RPCSEC_GSS_INT_H */
diff --git a/sys/rpc/rpcsec_gss/rpcsec_gss_misc.c b/sys/rpc/rpcsec_gss/rpcsec_gss_misc.c
new file mode 100644
index 0000000..5c8bf91
--- /dev/null
+++ b/sys/rpc/rpcsec_gss/rpcsec_gss_misc.c
@@ -0,0 +1,53 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#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/sys/rpc/rpcsec_gss/rpcsec_gss_prot.c b/sys/rpc/rpcsec_gss/rpcsec_gss_prot.c
new file mode 100644
index 0000000..0654a6e
--- /dev/null
+++ b/sys/rpc/rpcsec_gss/rpcsec_gss_prot.c
@@ -0,0 +1,359 @@
+/*
+ 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 $
+*/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kobj.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+
+#include "rpcsec_gss_int.h"
+
+#define MAX_GSS_SIZE 10240 /* XXX */
+
+#if 0 /* use the one from kgssapi */
+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);
+}
+#endif
+
+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));
+}
+
+static void
+put_uint32(struct mbuf **mp, uint32_t v)
+{
+ struct mbuf *m = *mp;
+ uint32_t n;
+
+ M_PREPEND(m, sizeof(uint32_t), M_WAIT);
+ n = htonl(v);
+ bcopy(&n, mtod(m, uint32_t *), sizeof(uint32_t));
+ *mp = m;
+}
+
+bool_t
+xdr_rpc_gss_wrap_data(struct mbuf **argsp,
+ gss_ctx_id_t ctx, gss_qop_t qop,
+ rpc_gss_service_t svc, u_int seq)
+{
+ struct mbuf *args, *mic;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+ u_int len;
+ static char zpad[4];
+
+ args = *argsp;
+
+ /*
+ * Prepend the sequence number before calling gss_get_mic or gss_wrap.
+ */
+ put_uint32(&args, seq);
+ len = m_length(args, NULL);
+
+ if (svc == rpc_gss_svc_integrity) {
+ /* Checksum rpc_gss_data_t. */
+ maj_stat = gss_get_mic_mbuf(&min_stat, ctx, qop, args, &mic);
+ if (maj_stat != GSS_S_COMPLETE) {
+ rpc_gss_log_debug("gss_get_mic failed");
+ m_freem(args);
+ return (FALSE);
+ }
+
+ /*
+ * Marshal databody_integ. Note that since args is
+ * already RPC encoded, there will be no padding.
+ */
+ put_uint32(&args, len);
+
+ /*
+ * Marshal checksum. This is likely to need padding.
+ */
+ len = m_length(mic, NULL);
+ put_uint32(&mic, len);
+ if (len != RNDUP(len)) {
+ m_append(mic, RNDUP(len) - len, zpad);
+ }
+
+ /*
+ * Concatenate databody_integ with checksum.
+ */
+ m_cat(args, mic);
+ } else if (svc == rpc_gss_svc_privacy) {
+ /* Encrypt rpc_gss_data_t. */
+ maj_stat = gss_wrap_mbuf(&min_stat, ctx, TRUE, qop,
+ &args, &conf_state);
+ if (maj_stat != GSS_S_COMPLETE) {
+ rpc_gss_log_status("gss_wrap", NULL,
+ maj_stat, min_stat);
+ return (FALSE);
+ }
+
+ /*
+ * Marshal databody_priv and deal with RPC padding.
+ */
+ len = m_length(args, NULL);
+ put_uint32(&args, len);
+ if (len != RNDUP(len)) {
+ m_append(args, RNDUP(len) - len, zpad);
+ }
+ }
+ *argsp = args;
+ return (TRUE);
+}
+
+static uint32_t
+get_uint32(struct mbuf **mp)
+{
+ struct mbuf *m = *mp;
+ uint32_t n;
+
+ if (m->m_len < sizeof(uint32_t)) {
+ m = m_pullup(m, sizeof(uint32_t));
+ if (!m) {
+ *mp = NULL;
+ return (0);
+ }
+ }
+ bcopy(mtod(m, uint32_t *), &n, sizeof(uint32_t));
+ m_adj(m, sizeof(uint32_t));
+ *mp = m;
+ return (ntohl(n));
+}
+
+static void
+m_trim(struct mbuf *m, int len)
+{
+ struct mbuf *n;
+ int off;
+
+ n = m_getptr(m, len, &off);
+ if (n) {
+ n->m_len = off;
+ if (n->m_next) {
+ m_freem(n->m_next);
+ n->m_next = NULL;
+ }
+ }
+}
+
+bool_t
+xdr_rpc_gss_unwrap_data(struct mbuf **resultsp,
+ gss_ctx_id_t ctx, gss_qop_t qop,
+ rpc_gss_service_t svc, u_int seq)
+{
+ struct mbuf *results, *message, *mic;
+ uint32_t len, cklen;
+ OM_uint32 maj_stat, min_stat;
+ u_int seq_num, conf_state, qop_state;
+
+ results = *resultsp;
+ *resultsp = NULL;
+
+ message = NULL;
+ if (svc == rpc_gss_svc_integrity) {
+ /*
+ * Extract the seq+message part. Remember that there
+ * may be extra RPC padding in the checksum. The
+ * message part is RPC encoded already so no
+ * padding.
+ */
+ len = get_uint32(&results);
+ message = results;
+ results = m_split(results, len, M_WAIT);
+ if (!results) {
+ m_freem(message);
+ return (FALSE);
+ }
+
+ /*
+ * Extract the MIC and make it contiguous.
+ */
+ cklen = get_uint32(&results);
+ KASSERT(cklen <= MHLEN, ("unexpected large GSS-API checksum"));
+ mic = results;
+ if (cklen > mic->m_len)
+ mic = m_pullup(mic, cklen);
+ if (cklen != RNDUP(cklen))
+ m_trim(mic, cklen);
+
+ /* Verify checksum and QOP. */
+ maj_stat = gss_verify_mic_mbuf(&min_stat, ctx,
+ message, mic, &qop_state);
+ m_freem(mic);
+
+ if (maj_stat != GSS_S_COMPLETE || qop_state != qop) {
+ m_freem(message);
+ rpc_gss_log_status("gss_verify_mic", NULL,
+ maj_stat, min_stat);
+ return (FALSE);
+ }
+ } else if (svc == rpc_gss_svc_privacy) {
+ /* Decode databody_priv. */
+ len = get_uint32(&results);
+
+ /* Decrypt databody. */
+ message = results;
+ if (len != RNDUP(len))
+ m_trim(message, len);
+ maj_stat = gss_unwrap_mbuf(&min_stat, ctx, &message,
+ &conf_state, &qop_state);
+
+ /* Verify encryption and QOP. */
+ if (maj_stat != GSS_S_COMPLETE) {
+ rpc_gss_log_status("gss_unwrap", NULL,
+ maj_stat, min_stat);
+ return (FALSE);
+ }
+ if (qop_state != qop || conf_state != TRUE) {
+ m_freem(results);
+ return (FALSE);
+ }
+ }
+
+ /* Decode rpc_gss_data_t (sequence number + arguments). */
+ seq_num = get_uint32(&message);
+
+ /* Verify sequence number. */
+ if (seq_num != seq) {
+ rpc_gss_log_debug("wrong sequence number in databody");
+ m_freem(message);
+ return (FALSE);
+ }
+
+ *resultsp = message;
+ return (TRUE);
+}
+
+#ifdef DEBUG
+#include <ctype.h>
+
+void
+rpc_gss_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
+rpc_gss_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);
+ printf("%s - ", (char *)msg.value);
+ gss_release_buffer(&min, &msg);
+
+ gss_display_status(&min, min_stat, GSS_C_MECH_CODE, mech,
+ &msg_ctx, &msg);
+ printf("%s\n", (char *)msg.value);
+ gss_release_buffer(&min, &msg);
+}
+
+#else
+
+void
+rpc_gss_log_debug(__unused const char *fmt, ...)
+{
+}
+
+void
+rpc_gss_log_status(__unused const char *m, __unused gss_OID mech,
+ __unused OM_uint32 maj_stat, __unused OM_uint32 min_stat)
+{
+}
+
+#endif
+
+
diff --git a/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c b/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c
new file mode 100644
index 0000000..e2469fd
--- /dev/null
+++ b/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c
@@ -0,0 +1,1485 @@
+/*-
+ * 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.
+ */
+/*
+ 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/ucred.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+
+#include "rpcsec_gss_int.h"
+
+static bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
+static bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
+static void svc_rpc_gss_release(SVCAUTH *);
+static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
+static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
+
+static struct svc_auth_ops svc_auth_gss_ops = {
+ svc_rpc_gss_wrap,
+ svc_rpc_gss_unwrap,
+ svc_rpc_gss_release,
+};
+
+struct sx svc_rpc_gss_lock;
+
+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_clientid {
+ uint32_t ci_hostid;
+ uint32_t ci_boottime;
+ uint32_t ci_id;
+};
+
+struct svc_rpc_gss_client {
+ TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
+ TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
+ volatile u_int cl_refs;
+ struct sx cl_lock;
+ struct svc_rpc_gss_clientid 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 */
+ struct ucred *cl_cred; /* kernel-style credentials */
+ int cl_rpcflavor; /* RPC pseudo sec flavor */
+ bool_t cl_done_callback; /* TRUE after call */
+ void *cl_cookie; /* user cookie from callback */
+ gid_t cl_gid_storage[NGROUPS];
+ gss_OID cl_mech; /* mechanism */
+ gss_qop_t cl_qop; /* quality of protection */
+ uint32_t cl_seqlast; /* sequence window origin */
+ uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
+};
+TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
+
+/*
+ * This structure holds enough information to unwrap arguments or wrap
+ * results for a given request. We use the rq_clntcred area for this
+ * (which is a per-request buffer).
+ */
+struct svc_rpc_gss_cookedcred {
+ struct svc_rpc_gss_client *cc_client;
+ rpc_gss_service_t cc_service;
+ uint32_t cc_seq;
+};
+
+#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;
+
+static void
+svc_rpc_gss_init(void *arg)
+{
+ int i;
+
+ 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, rpc_gss_svc_getcred);
+ sx_init(&svc_rpc_gss_lock, "gsslock");
+}
+SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
+
+bool_t
+rpc_gss_set_callback(rpc_gss_callback_t *cb)
+{
+ struct svc_rpc_gss_callback *scb;
+
+ scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
+ if (!scb) {
+ _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
+ return (FALSE);
+ }
+ scb->cb_callback = *cb;
+ sx_xlock(&svc_rpc_gss_lock);
+ SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
+ sx_xunlock(&svc_rpc_gss_lock);
+
+ return (TRUE);
+}
+
+void
+rpc_gss_clear_callback(rpc_gss_callback_t *cb)
+{
+ struct svc_rpc_gss_callback *scb;
+
+ sx_xlock(&svc_rpc_gss_lock);
+ SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
+ if (scb->cb_callback.program == cb->program
+ && scb->cb_callback.version == cb->version
+ && scb->cb_callback.callback == cb->callback) {
+ SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
+ svc_rpc_gss_callback, cb_link);
+ sx_xunlock(&svc_rpc_gss_lock);
+ mem_free(scb, sizeof(*scb));
+ return;
+ }
+ }
+ sx_xunlock(&svc_rpc_gss_lock);
+}
+
+static bool_t
+rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc namebuf;
+ gss_name_t name;
+ gss_OID_set_desc oid_set;
+
+ oid_set.count = 1;
+ oid_set.elements = sname->sn_mech;
+
+ namebuf.value = (void *) sname->sn_principal;
+ namebuf.length = strlen(sname->sn_principal);
+
+ maj_stat = gss_import_name(&min_stat, &namebuf,
+ GSS_C_NT_HOSTBASED_SERVICE, &name);
+ if (maj_stat != GSS_S_COMPLETE)
+ return (FALSE);
+
+ if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&min_stat, &sname->sn_cred);
+
+ maj_stat = gss_acquire_cred(&min_stat, name,
+ sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
+ NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ gss_release_name(&min_stat, &name);
+ return (FALSE);
+ }
+ gss_release_name(&min_stat, &name);
+
+ 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)
+{
+ struct svc_rpc_gss_svc_name *sname;
+ gss_OID mech_oid;
+
+ if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
+ return (FALSE);
+
+ sname = mem_alloc(sizeof(*sname));
+ if (!sname)
+ return (FALSE);
+ sname->sn_principal = strdup(principal, M_RPC);
+ sname->sn_mech = mech_oid;
+ sname->sn_req_time = req_time;
+ sname->sn_cred = GSS_C_NO_CREDENTIAL;
+ sname->sn_program = program;
+ sname->sn_version = version;
+
+ if (!rpc_gss_acquire_svc_cred(sname)) {
+ free(sname->sn_principal, M_RPC);
+ mem_free(sname, sizeof(*sname));
+ return (FALSE);
+ }
+
+ sx_xlock(&svc_rpc_gss_lock);
+ SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
+ sx_xunlock(&svc_rpc_gss_lock);
+
+ return (TRUE);
+}
+
+void
+rpc_gss_clear_svc_name(u_int program, u_int version)
+{
+ OM_uint32 min_stat;
+ struct svc_rpc_gss_svc_name *sname;
+
+ sx_xlock(&svc_rpc_gss_lock);
+ SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
+ if (sname->sn_program == program
+ && sname->sn_version == version) {
+ SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
+ svc_rpc_gss_svc_name, sn_link);
+ sx_xunlock(&svc_rpc_gss_lock);
+ gss_release_cred(&min_stat, &sname->sn_cred);
+ free(sname->sn_principal, M_RPC);
+ mem_free(sname, sizeof(*sname));
+ return;
+ }
+ }
+ sx_xunlock(&svc_rpc_gss_lock);
+}
+
+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;
+
+ 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 = mem_alloc(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);
+ mem_free(buf.value, buf.length);
+ if (maj_stat != GSS_S_COMPLETE) {
+ rpc_gss_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) {
+ rpc_gss_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) {
+ rpc_gss_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 = mem_alloc(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_cookedcred *cc;
+ struct svc_rpc_gss_client *client;
+
+ if (req->rq_cred.oa_flavor != RPCSEC_GSS)
+ return (FALSE);
+
+ cc = req->rq_clntcred;
+ client = cc->cc_client;
+ if (rcred)
+ *rcred = &client->cl_rawcred;
+ if (ucred)
+ *ucred = &client->cl_ucred;
+ if (cookie)
+ *cookie = client->cl_cookie;
+ return (TRUE);
+}
+
+/*
+ * This simpler interface is used by svc_getcred to copy the cred data
+ * into a kernel cred structure.
+ */
+static int
+rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
+{
+ struct ucred *cr;
+ struct svc_rpc_gss_cookedcred *cc;
+ struct svc_rpc_gss_client *client;
+ rpc_gss_ucred_t *uc;
+ int i;
+
+ if (req->rq_cred.oa_flavor != RPCSEC_GSS)
+ return (FALSE);
+
+ cc = req->rq_clntcred;
+ client = cc->cc_client;
+
+ if (flavorp)
+ *flavorp = client->cl_rpcflavor;
+
+ if (client->cl_cred) {
+ *crp = crhold(client->cl_cred);
+ return (TRUE);
+ }
+
+ uc = &client->cl_ucred;
+ cr = client->cl_cred = crget();
+ cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
+ cr->cr_rgid = cr->cr_svgid = uc->gid;
+ cr->cr_ngroups = uc->gidlen;
+ if (cr->cr_ngroups > NGROUPS)
+ cr->cr_ngroups = NGROUPS;
+ for (i = 0; i < cr->cr_ngroups; i++)
+ cr->cr_groups[i] = uc->gidlist[i];
+ *crp = crhold(cr);
+
+ return (TRUE);
+}
+
+int
+rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
+{
+ struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
+ struct svc_rpc_gss_client *client = cc->cc_client;
+ 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 {
+ rpc_gss_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(struct svc_rpc_gss_clientid *id)
+{
+ struct svc_rpc_gss_client *client;
+ struct svc_rpc_gss_client_list *list;
+
+ rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
+
+ if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
+ return (NULL);
+
+ list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
+ sx_xlock(&svc_rpc_gss_lock);
+ TAILQ_FOREACH(client, list, cl_link) {
+ if (client->cl_id.ci_id == id->ci_id) {
+ /*
+ * 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);
+ refcount_acquire(&client->cl_refs);
+ break;
+ }
+ }
+ sx_xunlock(&svc_rpc_gss_lock);
+
+ return (client);
+}
+
+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;
+
+ rpc_gss_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));
+ refcount_init(&client->cl_refs, 1);
+ sx_init(&client->cl_lock, "GSS-client");
+ client->cl_id.ci_hostid = hostid;
+ client->cl_id.ci_boottime = boottime.tv_sec;
+ client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
+ list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
+ sx_xlock(&svc_rpc_gss_lock);
+ TAILQ_INSERT_HEAD(list, client, cl_link);
+ TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
+ svc_rpc_gss_client_count++;
+ sx_xunlock(&svc_rpc_gss_lock);
+
+ /*
+ * 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_uptime + 5*60;
+
+ return (client);
+}
+
+static void
+svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
+{
+ OM_uint32 min_stat;
+
+ rpc_gss_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)
+ mem_free(client->cl_rawcred.client_principal,
+ sizeof(*client->cl_rawcred.client_principal)
+ + client->cl_rawcred.client_principal->len);
+
+ if (client->cl_cred)
+ crfree(client->cl_cred);
+
+ sx_destroy(&client->cl_lock);
+ mem_free(client, sizeof(*client));
+}
+
+/*
+ * Drop a reference to a client and free it if that was the last reference.
+ */
+static void
+svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
+{
+
+ if (!refcount_release(&client->cl_refs))
+ return;
+ svc_rpc_gss_destroy_client(client);
+}
+
+/*
+ * Remove a client from our global lists and free it if we can.
+ */
+static void
+svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
+{
+ struct svc_rpc_gss_client_list *list;
+
+ list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
+ sx_xlock(&svc_rpc_gss_lock);
+ TAILQ_REMOVE(list, client, cl_link);
+ TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
+ svc_rpc_gss_client_count--;
+ sx_xunlock(&svc_rpc_gss_lock);
+ svc_rpc_gss_release_client(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_uptime;
+
+ rpc_gss_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_forget_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) {
+ rpc_gss_log_debug("expiring client %p", client);
+ svc_rpc_gss_forget_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 *) mem_alloc(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;
+ rpc_gss_ucred_t *uc = &client->cl_ucred;
+ int numgroups;
+
+ uc->uid = 65534;
+ uc->gid = 65534;
+ uc->gidlist = client->cl_gid_storage;
+
+ numgroups = NGROUPS;
+ maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
+ &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
+ if (GSS_ERROR(maj_stat))
+ uc->gidlen = 0;
+ else
+ uc->gidlen = numgroups;
+}
+
+static void
+svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
+{
+ static gss_OID_desc krb5_mech_oid =
+ {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
+
+ /*
+ * Attempt to translate mech type and service into a
+ * 'pseudo flavor'. Hardwire in krb5 support for now.
+ */
+ if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
+ switch (client->cl_rawcred.service) {
+ case rpc_gss_svc_default:
+ case rpc_gss_svc_none:
+ client->cl_rpcflavor = RPCSEC_GSS_KRB5;
+ break;
+ case rpc_gss_svc_integrity:
+ client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
+ break;
+ case rpc_gss_svc_privacy:
+ client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
+ break;
+ }
+ } else {
+ client->cl_rpcflavor = RPCSEC_GSS;
+ }
+}
+
+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;
+
+ rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
+
+ /* Deserialize arguments. */
+ memset(&recv_tok, 0, sizeof(recv_tok));
+
+ if (!svc_getargs(rqst,
+ (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.
+ */
+ sx_xlock(&svc_rpc_gss_lock);
+ 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) {
+ retry:
+ 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_CREDENTIALS_EXPIRED) {
+ /*
+ * Either our creds really did
+ * expire or gssd was
+ * restarted.
+ */
+ if (rpc_gss_acquire_svc_cred(sname))
+ goto retry;
+ }
+ client->cl_sname = sname;
+ break;
+ }
+ }
+ if (!sname) {
+ xdr_free((xdrproc_t) xdr_gss_buffer_desc,
+ (char *) &recv_tok);
+ sx_xunlock(&svc_rpc_gss_lock);
+ return (FALSE);
+ }
+ } 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);
+ }
+ sx_xunlock(&svc_rpc_gss_lock);
+
+ 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) {
+ rpc_gss_log_status("accept_sec_context", client->cl_mech,
+ gr->gr_major, gr->gr_minor);
+ client->cl_state = CLIENT_STALE;
+ return (TRUE);
+ }
+
+ gr->gr_handle.value = &client->cl_id;
+ gr->gr_handle.length = sizeof(client->cl_id);
+ gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
+
+ /* Save client info. */
+ client->cl_mech = mech;
+ client->cl_qop = GSS_C_QOP_DEFAULT;
+ 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_uptime + 24*60*60;
+
+ client->cl_expiration = time_uptime + 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) {
+ rpc_gss_log_status("gss_export_name", client->cl_mech,
+ maj_stat, min_stat);
+ return (FALSE);
+ }
+ client->cl_rawcred.client_principal =
+ mem_alloc(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);
+ svc_rpc_gss_set_flavor(client);
+ gss_release_name(&min_stat, &client->cl_cname);
+
+#ifdef DEBUG
+ {
+ gss_buffer_desc mechname;
+
+ gss_oid_to_str(&min_stat, mech, &mechname);
+
+ rpc_gss_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;
+ int32_t rpchdr[128 / sizeof(int32_t)];
+ int32_t *buf;
+
+ rpc_gss_log_debug("in svc_rpc_gss_validate()");
+
+ memset(rpchdr, 0, sizeof(rpchdr));
+
+ /* Reconstruct RPC header for signing (from xdr_callmsg). */
+ buf = 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 - (u_char *)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) {
+ rpc_gss_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;
+ gss_buffer_desc mic;
+ OM_uint32 maj_stat, min_stat;
+ uint32_t nseq;
+
+ rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
+
+ nseq = htonl(seq);
+ signbuf.value = &nseq;
+ signbuf.length = sizeof(nseq);
+
+ maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
+ &signbuf, &mic);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
+ client->cl_state = CLIENT_STALE;
+ return (FALSE);
+ }
+
+ KASSERT(mic.length <= MAX_AUTH_BYTES,
+ ("MIC too large for RPCSEC_GSS"));
+
+ rqst->rq_verf.oa_flavor = RPCSEC_GSS;
+ rqst->rq_verf.oa_length = mic.length;
+ bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
+
+ gss_release_buffer(&min_stat, &mic);
+
+ 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;
+ bool_t result;
+
+ sx_xlock(&client->cl_lock);
+ 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 >= SVC_RPC_GSS_SEQWINDOW) {
+ result = FALSE;
+ goto out;
+ }
+ word = offset / 32;
+ bit = offset % 32;
+ if (client->cl_seqmask[word] & (1 << bit)) {
+ result = FALSE;
+ goto out;
+ }
+ }
+
+ result = TRUE;
+out:
+ sx_xunlock(&client->cl_lock);
+ return (result);
+}
+
+static void
+svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
+{
+ int offset, i, word, bit;
+ uint32_t carry, newcarry;
+
+ sx_xlock(&client->cl_lock);
+ 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;
+ } else {
+ offset = client->cl_seqlast - seq;
+ word = offset / 32;
+ bit = offset % 32;
+ client->cl_seqmask[word] |= (1 << bit);
+ }
+ sx_xunlock(&client->cl_lock);
+}
+
+enum auth_stat
+svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
+
+{
+ OM_uint32 min_stat;
+ XDR xdrs;
+ struct svc_rpc_gss_cookedcred *cc;
+ 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;
+
+ rpc_gss_log_debug("in svc_rpc_gss()");
+
+ /* Garbage collect old clients. */
+ svc_rpc_gss_timeout_clients();
+
+ /* Initialize reply. */
+ rqst->rq_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);
+
+ client = NULL;
+
+ /* 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) {
+ if (gc.gc_handle.length != 0) {
+ result = AUTH_BADCRED;
+ goto out;
+ }
+ client = svc_rpc_gss_create_client();
+ refcount_acquire(&client->cl_refs);
+ } else {
+ struct svc_rpc_gss_clientid *p;
+ if (gc.gc_handle.length != sizeof(*p)) {
+ result = AUTH_BADCRED;
+ goto out;
+ }
+ 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;
+ }
+ }
+ cc = rqst->rq_clntcred;
+ cc->cc_client = client;
+ cc->cc_service = gc.gc_svc;
+ cc->cc_seq = gc.gc_seq;
+
+ /*
+ * 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;
+ }
+
+ /*
+ * 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) {
+ /*
+ * We borrow the space for the call verf to
+ * pack our reply verf.
+ */
+ rqst->rq_verf = msg->rm_call.cb_verf;
+ if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
+ result = AUTH_REJECTEDCRED;
+ break;
+ }
+ } else {
+ rqst->rq_verf = _null_auth;
+ }
+
+ call_stat = svc_sendreply(rqst,
+ (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;
+ }
+
+ /*
+ * We borrow the space for the call verf to pack our
+ * reply verf.
+ */
+ rqst->rq_verf = msg->rm_call.cb_verf;
+ 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 request 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. Acquire an extra reference to the client
+ * which will be released by svc_rpc_gss_release()
+ * after the request has finished processing.
+ */
+ refcount_acquire(&client->cl_refs);
+ rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
+ rqst->rq_auth.svc_ah_private = cc;
+
+ 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.
+ */
+ sx_xlock(&client->cl_lock);
+ 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;
+ sx_xunlock(&client->cl_lock);
+ break;
+ }
+ }
+ sx_xunlock(&client->cl_lock);
+
+ /*
+ * 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.
+ */
+ if (client->cl_rawcred.service != gc.gc_svc) {
+ client->cl_rawcred.service = gc.gc_svc;
+ svc_rpc_gss_set_flavor(client);
+ }
+
+ result = AUTH_OK;
+ } else {
+ if (rqst->rq_proc != NULLPROC) {
+ result = AUTH_REJECTEDCRED;
+ break;
+ }
+
+ call_stat = svc_sendreply(rqst,
+ (xdrproc_t) xdr_void, (caddr_t) NULL);
+
+ if (!call_stat) {
+ result = AUTH_FAILED;
+ break;
+ }
+
+ svc_rpc_gss_forget_client(client);
+
+ result = RPCSEC_GSS_NODISPATCH;
+ break;
+ }
+ break;
+
+ default:
+ result = AUTH_BADCRED;
+ break;
+ }
+out:
+ if (client)
+ svc_rpc_gss_release_client(client);
+
+ xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
+ return (result);
+}
+
+static bool_t
+svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
+{
+ struct svc_rpc_gss_cookedcred *cc;
+ struct svc_rpc_gss_client *client;
+
+ rpc_gss_log_debug("in svc_rpc_gss_wrap()");
+
+ cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
+ client = cc->cc_client;
+ if (client->cl_state != CLIENT_ESTABLISHED
+ || cc->cc_service == rpc_gss_svc_none) {
+ return (TRUE);
+ }
+
+ return (xdr_rpc_gss_wrap_data(mp,
+ client->cl_ctx, client->cl_qop,
+ cc->cc_service, cc->cc_seq));
+}
+
+static bool_t
+svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
+{
+ struct svc_rpc_gss_cookedcred *cc;
+ struct svc_rpc_gss_client *client;
+
+ rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
+
+ cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
+ client = cc->cc_client;
+ if (client->cl_state != CLIENT_ESTABLISHED
+ || cc->cc_service == rpc_gss_svc_none) {
+ return (TRUE);
+ }
+
+ return (xdr_rpc_gss_unwrap_data(mp,
+ client->cl_ctx, client->cl_qop,
+ cc->cc_service, cc->cc_seq));
+}
+
+static void
+svc_rpc_gss_release(SVCAUTH *auth)
+{
+ struct svc_rpc_gss_cookedcred *cc;
+ struct svc_rpc_gss_client *client;
+
+ rpc_gss_log_debug("in svc_rpc_gss_release()");
+
+ cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
+ client = cc->cc_client;
+ svc_rpc_gss_release_client(client);
+}
diff --git a/sys/rpc/svc.c b/sys/rpc/svc.c
index d6d6d78..8af9e80 100644
--- a/sys/rpc/svc.c
+++ b/sys/rpc/svc.c
@@ -49,37 +49,105 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/lock.h>
#include <sys/kernel.h>
+#include <sys/kthread.h>
#include <sys/malloc.h>
+#include <sys/mbuf.h>
#include <sys/mutex.h>
+#include <sys/proc.h>
#include <sys/queue.h>
+#include <sys/socketvar.h>
#include <sys/systm.h>
#include <sys/ucred.h>
#include <rpc/rpc.h>
#include <rpc/rpcb_clnt.h>
+#include <rpc/replay.h>
#include <rpc/rpc_com.h>
#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)
static struct svc_callout *svc_find(SVCPOOL *pool, rpcprog_t, rpcvers_t,
char *);
-static void __xprt_do_unregister (SVCXPRT *xprt, bool_t dolock);
+static void svc_new_thread(SVCPOOL *pool);
+static void xprt_unregister_locked(SVCXPRT *xprt);
/* *************** SVCXPRT related stuff **************** */
+static int svcpool_minthread_sysctl(SYSCTL_HANDLER_ARGS);
+static int svcpool_maxthread_sysctl(SYSCTL_HANDLER_ARGS);
+
SVCPOOL*
-svcpool_create(void)
+svcpool_create(const char *name, struct sysctl_oid_list *sysctl_base)
{
SVCPOOL *pool;
pool = malloc(sizeof(SVCPOOL), M_RPC, M_WAITOK|M_ZERO);
mtx_init(&pool->sp_lock, "sp_lock", NULL, MTX_DEF);
+ pool->sp_name = name;
+ pool->sp_state = SVCPOOL_INIT;
+ pool->sp_proc = NULL;
TAILQ_INIT(&pool->sp_xlist);
TAILQ_INIT(&pool->sp_active);
TAILQ_INIT(&pool->sp_callouts);
+ LIST_INIT(&pool->sp_threads);
+ LIST_INIT(&pool->sp_idlethreads);
+ pool->sp_minthreads = 1;
+ pool->sp_maxthreads = 1;
+ pool->sp_threadcount = 0;
+
+ /*
+ * Don't use more than a quarter of mbuf clusters or more than
+ * 45Mb buffering requests.
+ */
+ pool->sp_space_high = nmbclusters * MCLBYTES / 4;
+ if (pool->sp_space_high > 45 << 20)
+ pool->sp_space_high = 45 << 20;
+ pool->sp_space_low = 2 * pool->sp_space_high / 3;
+
+ sysctl_ctx_init(&pool->sp_sysctl);
+ if (sysctl_base) {
+ SYSCTL_ADD_PROC(&pool->sp_sysctl, sysctl_base, OID_AUTO,
+ "minthreads", CTLTYPE_INT | CTLFLAG_RW,
+ pool, 0, svcpool_minthread_sysctl, "I", "");
+ SYSCTL_ADD_PROC(&pool->sp_sysctl, sysctl_base, OID_AUTO,
+ "maxthreads", CTLTYPE_INT | CTLFLAG_RW,
+ pool, 0, svcpool_maxthread_sysctl, "I", "");
+ SYSCTL_ADD_INT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
+ "threads", CTLFLAG_RD, &pool->sp_threadcount, 0, "");
+
+ SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
+ "request_space_used", CTLFLAG_RD,
+ &pool->sp_space_used, 0,
+ "Space in parsed but not handled requests.");
+
+ SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
+ "request_space_used_highest", CTLFLAG_RD,
+ &pool->sp_space_used_highest, 0,
+ "Highest space used since reboot.");
+
+ SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
+ "request_space_high", CTLFLAG_RW,
+ &pool->sp_space_high, 0,
+ "Maximum space in parsed but not handled requests.");
+
+ SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
+ "request_space_low", CTLFLAG_RW,
+ &pool->sp_space_low, 0,
+ "Low water mark for request space.");
+
+ SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
+ "request_space_throttled", CTLFLAG_RD,
+ &pool->sp_space_throttled, 0,
+ "Whether nfs requests are currently throttled");
+
+ SYSCTL_ADD_UINT(&pool->sp_sysctl, sysctl_base, OID_AUTO,
+ "request_space_throttle_count", CTLFLAG_RD,
+ &pool->sp_space_throttle_count, 0,
+ "Count of times throttling based on request space has occurred");
+ }
return pool;
}
@@ -87,16 +155,17 @@ svcpool_create(void)
void
svcpool_destroy(SVCPOOL *pool)
{
- SVCXPRT *xprt;
+ SVCXPRT *xprt, *nxprt;
struct svc_callout *s;
+ struct svcxprt_list cleanup;
+ TAILQ_INIT(&cleanup);
mtx_lock(&pool->sp_lock);
while (TAILQ_FIRST(&pool->sp_xlist)) {
xprt = TAILQ_FIRST(&pool->sp_xlist);
- mtx_unlock(&pool->sp_lock);
- SVC_DESTROY(xprt);
- mtx_lock(&pool->sp_lock);
+ xprt_unregister_locked(xprt);
+ TAILQ_INSERT_TAIL(&cleanup, xprt, xp_link);
}
while (TAILQ_FIRST(&pool->sp_callouts)) {
@@ -107,9 +176,97 @@ svcpool_destroy(SVCPOOL *pool)
}
mtx_destroy(&pool->sp_lock);
+
+ TAILQ_FOREACH_SAFE(xprt, &cleanup, xp_link, nxprt) {
+ SVC_RELEASE(xprt);
+ }
+
+ if (pool->sp_rcache)
+ replay_freecache(pool->sp_rcache);
+
+ sysctl_ctx_free(&pool->sp_sysctl);
free(pool, M_RPC);
}
+static bool_t
+svcpool_active(SVCPOOL *pool)
+{
+ enum svcpool_state state = pool->sp_state;
+
+ if (state == SVCPOOL_INIT || state == SVCPOOL_CLOSING)
+ return (FALSE);
+ return (TRUE);
+}
+
+/*
+ * Sysctl handler to set the minimum thread count on a pool
+ */
+static int
+svcpool_minthread_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ SVCPOOL *pool;
+ int newminthreads, error, n;
+
+ pool = oidp->oid_arg1;
+ newminthreads = pool->sp_minthreads;
+ error = sysctl_handle_int(oidp, &newminthreads, 0, req);
+ if (error == 0 && newminthreads != pool->sp_minthreads) {
+ if (newminthreads > pool->sp_maxthreads)
+ return (EINVAL);
+ mtx_lock(&pool->sp_lock);
+ if (newminthreads > pool->sp_minthreads
+ && svcpool_active(pool)) {
+ /*
+ * If the pool is running and we are
+ * increasing, create some more threads now.
+ */
+ n = newminthreads - pool->sp_threadcount;
+ if (n > 0) {
+ mtx_unlock(&pool->sp_lock);
+ while (n--)
+ svc_new_thread(pool);
+ mtx_lock(&pool->sp_lock);
+ }
+ }
+ pool->sp_minthreads = newminthreads;
+ mtx_unlock(&pool->sp_lock);
+ }
+ return (error);
+}
+
+/*
+ * Sysctl handler to set the maximum thread count on a pool
+ */
+static int
+svcpool_maxthread_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ SVCPOOL *pool;
+ SVCTHREAD *st;
+ int newmaxthreads, error;
+
+ pool = oidp->oid_arg1;
+ newmaxthreads = pool->sp_maxthreads;
+ error = sysctl_handle_int(oidp, &newmaxthreads, 0, req);
+ if (error == 0 && newmaxthreads != pool->sp_maxthreads) {
+ if (newmaxthreads < pool->sp_minthreads)
+ return (EINVAL);
+ mtx_lock(&pool->sp_lock);
+ if (newmaxthreads < pool->sp_maxthreads
+ && svcpool_active(pool)) {
+ /*
+ * If the pool is running and we are
+ * decreasing, wake up some idle threads to
+ * encourage them to exit.
+ */
+ LIST_FOREACH(st, &pool->sp_idlethreads, st_ilink)
+ cv_signal(&st->st_cond);
+ }
+ pool->sp_maxthreads = newmaxthreads;
+ mtx_unlock(&pool->sp_lock);
+ }
+ return (error);
+}
+
/*
* Activate a transport handle.
*/
@@ -125,40 +282,70 @@ xprt_register(SVCXPRT *xprt)
mtx_unlock(&pool->sp_lock);
}
-void
-xprt_unregister(SVCXPRT *xprt)
-{
- __xprt_do_unregister(xprt, TRUE);
-}
-
-void
-__xprt_unregister_unlocked(SVCXPRT *xprt)
-{
- __xprt_do_unregister(xprt, FALSE);
-}
-
/*
- * De-activate a transport handle.
+ * De-activate a transport handle. Note: the locked version doesn't
+ * release the transport - caller must do that after dropping the pool
+ * lock.
*/
static void
-__xprt_do_unregister(SVCXPRT *xprt, bool_t dolock)
+xprt_unregister_locked(SVCXPRT *xprt)
{
SVCPOOL *pool = xprt->xp_pool;
- //__svc_generic_cleanup(xprt);
-
- if (dolock)
- mtx_lock(&pool->sp_lock);
-
if (xprt->xp_active) {
TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
xprt->xp_active = FALSE;
}
TAILQ_REMOVE(&pool->sp_xlist, xprt, xp_link);
xprt->xp_registered = FALSE;
+}
- if (dolock)
- mtx_unlock(&pool->sp_lock);
+void
+xprt_unregister(SVCXPRT *xprt)
+{
+ SVCPOOL *pool = xprt->xp_pool;
+
+ mtx_lock(&pool->sp_lock);
+ xprt_unregister_locked(xprt);
+ mtx_unlock(&pool->sp_lock);
+
+ SVC_RELEASE(xprt);
+}
+
+static void
+xprt_assignthread(SVCXPRT *xprt)
+{
+ SVCPOOL *pool = xprt->xp_pool;
+ SVCTHREAD *st;
+
+ /*
+ * Attempt to assign a service thread to this
+ * transport.
+ */
+ LIST_FOREACH(st, &pool->sp_idlethreads, st_ilink) {
+ if (st->st_xprt == NULL && STAILQ_EMPTY(&st->st_reqs))
+ break;
+ }
+ if (st) {
+ SVC_ACQUIRE(xprt);
+ xprt->xp_thread = st;
+ st->st_xprt = xprt;
+ cv_signal(&st->st_cond);
+ } else {
+ /*
+ * See if we can create a new thread. The
+ * actual thread creation happens in
+ * svc_run_internal because our locking state
+ * is poorly defined (we are typically called
+ * from a socket upcall). Don't create more
+ * than one thread per second.
+ */
+ if (pool->sp_state == SVCPOOL_ACTIVE
+ && pool->sp_lastcreatetime < time_uptime
+ && pool->sp_threadcount < pool->sp_maxthreads) {
+ pool->sp_state = SVCPOOL_THREADWANTED;
+ }
+ }
}
void
@@ -166,30 +353,42 @@ xprt_active(SVCXPRT *xprt)
{
SVCPOOL *pool = xprt->xp_pool;
+ if (!xprt->xp_registered) {
+ /*
+ * Race with xprt_unregister - we lose.
+ */
+ return;
+ }
+
mtx_lock(&pool->sp_lock);
if (!xprt->xp_active) {
TAILQ_INSERT_TAIL(&pool->sp_active, xprt, xp_alink);
xprt->xp_active = TRUE;
+ xprt_assignthread(xprt);
}
- wakeup(&pool->sp_active);
mtx_unlock(&pool->sp_lock);
}
void
-xprt_inactive(SVCXPRT *xprt)
+xprt_inactive_locked(SVCXPRT *xprt)
{
SVCPOOL *pool = xprt->xp_pool;
- mtx_lock(&pool->sp_lock);
-
if (xprt->xp_active) {
TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
xprt->xp_active = FALSE;
}
- wakeup(&pool->sp_active);
+}
+
+void
+xprt_inactive(SVCXPRT *xprt)
+{
+ SVCPOOL *pool = xprt->xp_pool;
+ mtx_lock(&pool->sp_lock);
+ xprt_inactive_locked(xprt);
mtx_unlock(&pool->sp_lock);
}
@@ -253,9 +452,11 @@ rpcb_it:
if (nconf) {
bool_t dummy;
struct netconfig tnc;
+ struct netbuf nb;
tnc = *nconf;
- dummy = rpcb_set(prog, vers, &tnc,
- &((SVCXPRT *) xprt)->xp_ltaddr);
+ nb.buf = &xprt->xp_ltaddr;
+ nb.len = xprt->xp_ltaddr.ss_len;
+ dummy = rpcb_set(prog, vers, &tnc, &nb);
return (dummy);
}
return (TRUE);
@@ -305,270 +506,809 @@ svc_find(SVCPOOL *pool, rpcprog_t prog, rpcvers_t vers, char *netid)
/* ******************* REPLY GENERATION ROUTINES ************ */
+static bool_t
+svc_sendreply_common(struct svc_req *rqstp, struct rpc_msg *rply,
+ struct mbuf *body)
+{
+ SVCXPRT *xprt = rqstp->rq_xprt;
+ bool_t ok;
+
+ if (rqstp->rq_args) {
+ m_freem(rqstp->rq_args);
+ rqstp->rq_args = NULL;
+ }
+
+ if (xprt->xp_pool->sp_rcache)
+ replay_setreply(xprt->xp_pool->sp_rcache,
+ rply, svc_getrpccaller(rqstp), body);
+
+ if (!SVCAUTH_WRAP(&rqstp->rq_auth, &body))
+ return (FALSE);
+
+ ok = SVC_REPLY(xprt, rply, rqstp->rq_addr, body);
+ if (rqstp->rq_addr) {
+ free(rqstp->rq_addr, M_SONAME);
+ rqstp->rq_addr = NULL;
+ }
+
+ return (ok);
+}
+
/*
* Send a reply to an rpc request
*/
bool_t
-svc_sendreply(SVCXPRT *xprt, xdrproc_t xdr_results, void * xdr_location)
+svc_sendreply(struct svc_req *rqstp, xdrproc_t xdr_results, void * xdr_location)
{
struct rpc_msg rply;
+ struct mbuf *m;
+ XDR xdrs;
+ bool_t ok;
+ rply.rm_xid = rqstp->rq_xid;
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
- rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_verf = rqstp->rq_verf;
rply.acpted_rply.ar_stat = SUCCESS;
- rply.acpted_rply.ar_results.where = xdr_location;
- rply.acpted_rply.ar_results.proc = xdr_results;
+ rply.acpted_rply.ar_results.where = NULL;
+ rply.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
+
+ MGET(m, M_WAIT, MT_DATA);
+ MCLGET(m, M_WAIT);
+ m->m_len = 0;
+ xdrmbuf_create(&xdrs, m, XDR_ENCODE);
+ ok = xdr_results(&xdrs, xdr_location);
+ XDR_DESTROY(&xdrs);
+
+ if (ok) {
+ return (svc_sendreply_common(rqstp, &rply, m));
+ } else {
+ m_freem(m);
+ return (FALSE);
+ }
+}
- return (SVC_REPLY(xprt, &rply));
+bool_t
+svc_sendreply_mbuf(struct svc_req *rqstp, struct mbuf *m)
+{
+ struct rpc_msg rply;
+
+ rply.rm_xid = rqstp->rq_xid;
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = rqstp->rq_verf;
+ rply.acpted_rply.ar_stat = SUCCESS;
+ rply.acpted_rply.ar_results.where = NULL;
+ rply.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
+
+ return (svc_sendreply_common(rqstp, &rply, m));
}
/*
* No procedure error reply
*/
void
-svcerr_noproc(SVCXPRT *xprt)
+svcerr_noproc(struct svc_req *rqstp)
{
+ SVCXPRT *xprt = rqstp->rq_xprt;
struct rpc_msg rply;
+ rply.rm_xid = rqstp->rq_xid;
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
- rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_verf = rqstp->rq_verf;
rply.acpted_rply.ar_stat = PROC_UNAVAIL;
- SVC_REPLY(xprt, &rply);
+ if (xprt->xp_pool->sp_rcache)
+ replay_setreply(xprt->xp_pool->sp_rcache,
+ &rply, svc_getrpccaller(rqstp), NULL);
+
+ svc_sendreply_common(rqstp, &rply, NULL);
}
/*
* Can't decode args error reply
*/
void
-svcerr_decode(SVCXPRT *xprt)
+svcerr_decode(struct svc_req *rqstp)
{
+ SVCXPRT *xprt = rqstp->rq_xprt;
struct rpc_msg rply;
+ rply.rm_xid = rqstp->rq_xid;
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
- rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_verf = rqstp->rq_verf;
rply.acpted_rply.ar_stat = GARBAGE_ARGS;
- SVC_REPLY(xprt, &rply);
+ if (xprt->xp_pool->sp_rcache)
+ replay_setreply(xprt->xp_pool->sp_rcache,
+ &rply, (struct sockaddr *) &xprt->xp_rtaddr, NULL);
+
+ svc_sendreply_common(rqstp, &rply, NULL);
}
/*
* Some system error
*/
void
-svcerr_systemerr(SVCXPRT *xprt)
+svcerr_systemerr(struct svc_req *rqstp)
{
+ SVCXPRT *xprt = rqstp->rq_xprt;
struct rpc_msg rply;
+ rply.rm_xid = rqstp->rq_xid;
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
- rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_verf = rqstp->rq_verf;
rply.acpted_rply.ar_stat = SYSTEM_ERR;
- SVC_REPLY(xprt, &rply);
+ if (xprt->xp_pool->sp_rcache)
+ replay_setreply(xprt->xp_pool->sp_rcache,
+ &rply, svc_getrpccaller(rqstp), NULL);
+
+ svc_sendreply_common(rqstp, &rply, NULL);
}
/*
* Authentication error reply
*/
void
-svcerr_auth(SVCXPRT *xprt, enum auth_stat why)
+svcerr_auth(struct svc_req *rqstp, enum auth_stat why)
{
+ SVCXPRT *xprt = rqstp->rq_xprt;
struct rpc_msg rply;
+ rply.rm_xid = rqstp->rq_xid;
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_DENIED;
rply.rjcted_rply.rj_stat = AUTH_ERROR;
rply.rjcted_rply.rj_why = why;
- SVC_REPLY(xprt, &rply);
+ if (xprt->xp_pool->sp_rcache)
+ replay_setreply(xprt->xp_pool->sp_rcache,
+ &rply, svc_getrpccaller(rqstp), NULL);
+
+ svc_sendreply_common(rqstp, &rply, NULL);
}
/*
* Auth too weak error reply
*/
void
-svcerr_weakauth(SVCXPRT *xprt)
+svcerr_weakauth(struct svc_req *rqstp)
{
- svcerr_auth(xprt, AUTH_TOOWEAK);
+ svcerr_auth(rqstp, AUTH_TOOWEAK);
}
/*
* Program unavailable error reply
*/
void
-svcerr_noprog(SVCXPRT *xprt)
+svcerr_noprog(struct svc_req *rqstp)
{
+ SVCXPRT *xprt = rqstp->rq_xprt;
struct rpc_msg rply;
+ rply.rm_xid = rqstp->rq_xid;
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
- rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_verf = rqstp->rq_verf;
rply.acpted_rply.ar_stat = PROG_UNAVAIL;
- SVC_REPLY(xprt, &rply);
+ if (xprt->xp_pool->sp_rcache)
+ replay_setreply(xprt->xp_pool->sp_rcache,
+ &rply, svc_getrpccaller(rqstp), NULL);
+
+ svc_sendreply_common(rqstp, &rply, NULL);
}
/*
* Program version mismatch error reply
*/
void
-svcerr_progvers(SVCXPRT *xprt, rpcvers_t low_vers, rpcvers_t high_vers)
+svcerr_progvers(struct svc_req *rqstp, rpcvers_t low_vers, rpcvers_t high_vers)
{
+ SVCXPRT *xprt = rqstp->rq_xprt;
struct rpc_msg rply;
+ rply.rm_xid = rqstp->rq_xid;
rply.rm_direction = REPLY;
rply.rm_reply.rp_stat = MSG_ACCEPTED;
- rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_verf = rqstp->rq_verf;
rply.acpted_rply.ar_stat = PROG_MISMATCH;
rply.acpted_rply.ar_vers.low = (uint32_t)low_vers;
rply.acpted_rply.ar_vers.high = (uint32_t)high_vers;
- SVC_REPLY(xprt, &rply);
+ if (xprt->xp_pool->sp_rcache)
+ replay_setreply(xprt->xp_pool->sp_rcache,
+ &rply, svc_getrpccaller(rqstp), NULL);
+
+ svc_sendreply_common(rqstp, &rply, NULL);
}
-/* ******************* SERVER INPUT STUFF ******************* */
+/*
+ * 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;
+ refcount_init(&xprt->xp_refs, 1);
+
+ return (xprt);
+}
/*
- * Get server side input from some transport.
- *
- * Statement of authentication parameters management:
- * This function owns and manages all authentication parameters, specifically
- * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
- * the "cooked" credentials (rqst->rq_clntcred).
- * In-kernel, we represent non-trivial cooked creds with struct ucred.
- * In all events, all three parameters are freed upon exit from this routine.
- * The storage is trivially management on the call stack in user land, but
- * is mallocated in kernel land.
+ * Free a server transport structure.
*/
+void
+svc_xprt_free(xprt)
+ SVCXPRT *xprt;
+{
-static void
-svc_getreq(SVCXPRT *xprt)
+ mem_free(xprt->xp_p3, sizeof(SVCXPRT_EXT));
+ mem_free(xprt, sizeof(SVCXPRT));
+}
+
+/* ******************* SERVER INPUT STUFF ******************* */
+
+/*
+ * Read RPC requests from a transport and queue them to be
+ * executed. We handle authentication and replay cache replies here.
+ * Actually dispatching the RPC is deferred till svc_executereq.
+ */
+static enum xprt_stat
+svc_getreq(SVCXPRT *xprt, struct svc_req **rqstp_ret)
{
SVCPOOL *pool = xprt->xp_pool;
- struct svc_req r;
+ struct svc_req *r;
struct rpc_msg msg;
- int prog_found;
- rpcvers_t low_vers;
- rpcvers_t high_vers;
+ struct mbuf *args;
enum xprt_stat stat;
- char cred_area[2*MAX_AUTH_BYTES + sizeof(struct xucred)];
-
- msg.rm_call.cb_cred.oa_base = cred_area;
- msg.rm_call.cb_verf.oa_base = &cred_area[MAX_AUTH_BYTES];
- r.rq_clntcred = &cred_area[2*MAX_AUTH_BYTES];
/* now receive msgs from xprtprt (support batch calls) */
- do {
- if (SVC_RECV(xprt, &msg)) {
-
- /* now find the exported program and call it */
- struct svc_callout *s;
- enum auth_stat why;
-
- r.rq_xprt = xprt;
- r.rq_prog = msg.rm_call.cb_prog;
- r.rq_vers = msg.rm_call.cb_vers;
- r.rq_proc = msg.rm_call.cb_proc;
- r.rq_cred = msg.rm_call.cb_cred;
- /* first authenticate the message */
- if ((why = _authenticate(&r, &msg)) != AUTH_OK) {
- svcerr_auth(xprt, why);
+ r = malloc(sizeof(*r), M_RPC, M_WAITOK|M_ZERO);
+
+ msg.rm_call.cb_cred.oa_base = r->rq_credarea;
+ msg.rm_call.cb_verf.oa_base = &r->rq_credarea[MAX_AUTH_BYTES];
+ r->rq_clntcred = &r->rq_credarea[2*MAX_AUTH_BYTES];
+ if (SVC_RECV(xprt, &msg, &r->rq_addr, &args)) {
+ enum auth_stat why;
+
+ /*
+ * Handle replays and authenticate before queuing the
+ * request to be executed.
+ */
+ SVC_ACQUIRE(xprt);
+ r->rq_xprt = xprt;
+ if (pool->sp_rcache) {
+ struct rpc_msg repmsg;
+ struct mbuf *repbody;
+ enum replay_state rs;
+ rs = replay_find(pool->sp_rcache, &msg,
+ svc_getrpccaller(r), &repmsg, &repbody);
+ switch (rs) {
+ case RS_NEW:
+ break;
+ case RS_DONE:
+ SVC_REPLY(xprt, &repmsg, r->rq_addr,
+ repbody);
+ if (r->rq_addr) {
+ free(r->rq_addr, M_SONAME);
+ r->rq_addr = NULL;
+ }
+ goto call_done;
+
+ default:
goto call_done;
}
- /* now match message with a registered service*/
- prog_found = FALSE;
- low_vers = (rpcvers_t) -1L;
- high_vers = (rpcvers_t) 0L;
- TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) {
- if (s->sc_prog == r.rq_prog) {
- if (s->sc_vers == r.rq_vers) {
- (*s->sc_dispatch)(&r, xprt);
- goto call_done;
- } /* found correct version */
- prog_found = TRUE;
- if (s->sc_vers < low_vers)
- low_vers = s->sc_vers;
- if (s->sc_vers > high_vers)
- high_vers = s->sc_vers;
- } /* found correct program */
- }
+ }
+
+ r->rq_xid = msg.rm_xid;
+ r->rq_prog = msg.rm_call.cb_prog;
+ r->rq_vers = msg.rm_call.cb_vers;
+ r->rq_proc = msg.rm_call.cb_proc;
+ r->rq_size = sizeof(*r) + m_length(args, NULL);
+ r->rq_args = args;
+ if ((why = _authenticate(r, &msg)) != AUTH_OK) {
/*
- * if we got here, the program or version
- * is not served ...
+ * 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 (prog_found)
- svcerr_progvers(xprt, low_vers, high_vers);
- else
- svcerr_noprog(xprt);
- /* Fall through to ... */
+ if (why != RPCSEC_GSS_NODISPATCH)
+ svcerr_auth(r, why);
+ goto call_done;
}
+
+ if (!SVCAUTH_UNWRAP(&r->rq_auth, &r->rq_args)) {
+ svcerr_decode(r);
+ goto call_done;
+ }
+
/*
- * Check if the xprt has been disconnected in a
- * recursive call in the service dispatch routine.
- * If so, then break.
+ * Everything checks out, return request to caller.
*/
- mtx_lock(&pool->sp_lock);
- if (!xprt->xp_registered) {
- mtx_unlock(&pool->sp_lock);
- break;
- }
- mtx_unlock(&pool->sp_lock);
+ *rqstp_ret = r;
+ r = NULL;
+ }
call_done:
- if ((stat = SVC_STAT(xprt)) == XPRT_DIED) {
- SVC_DESTROY(xprt);
- break;
+ if (r) {
+ svc_freereq(r);
+ r = NULL;
+ }
+ if ((stat = SVC_STAT(xprt)) == XPRT_DIED) {
+ xprt_unregister(xprt);
+ }
+
+ return (stat);
+}
+
+static void
+svc_executereq(struct svc_req *rqstp)
+{
+ SVCXPRT *xprt = rqstp->rq_xprt;
+ SVCPOOL *pool = xprt->xp_pool;
+ int prog_found;
+ rpcvers_t low_vers;
+ rpcvers_t high_vers;
+ struct svc_callout *s;
+
+ /* now match message with a registered service*/
+ prog_found = FALSE;
+ low_vers = (rpcvers_t) -1L;
+ high_vers = (rpcvers_t) 0L;
+ TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) {
+ if (s->sc_prog == rqstp->rq_prog) {
+ if (s->sc_vers == rqstp->rq_vers) {
+ /*
+ * We hand ownership of r to the
+ * dispatch method - they must call
+ * svc_freereq.
+ */
+ (*s->sc_dispatch)(rqstp, xprt);
+ return;
+ } /* found correct version */
+ prog_found = TRUE;
+ if (s->sc_vers < low_vers)
+ low_vers = s->sc_vers;
+ if (s->sc_vers > high_vers)
+ high_vers = s->sc_vers;
+ } /* found correct program */
+ }
+
+ /*
+ * if we got here, the program or version
+ * is not served ...
+ */
+ if (prog_found)
+ svcerr_progvers(rqstp, low_vers, high_vers);
+ else
+ svcerr_noprog(rqstp);
+
+ svc_freereq(rqstp);
+}
+
+static void
+svc_checkidle(SVCPOOL *pool)
+{
+ SVCXPRT *xprt, *nxprt;
+ time_t timo;
+ struct svcxprt_list cleanup;
+
+ TAILQ_INIT(&cleanup);
+ TAILQ_FOREACH_SAFE(xprt, &pool->sp_xlist, xp_link, nxprt) {
+ /*
+ * Only some transports have idle timers. Don't time
+ * something out which is just waking up.
+ */
+ if (!xprt->xp_idletimeout || xprt->xp_thread)
+ continue;
+
+ timo = xprt->xp_lastactive + xprt->xp_idletimeout;
+ if (time_uptime > timo) {
+ xprt_unregister_locked(xprt);
+ TAILQ_INSERT_TAIL(&cleanup, xprt, xp_link);
}
- } while (stat == XPRT_MOREREQS);
+ }
+
+ mtx_unlock(&pool->sp_lock);
+ TAILQ_FOREACH_SAFE(xprt, &cleanup, xp_link, nxprt) {
+ SVC_RELEASE(xprt);
+ }
+ mtx_lock(&pool->sp_lock);
+
}
-void
-svc_run(SVCPOOL *pool)
+static void
+svc_assign_waiting_sockets(SVCPOOL *pool)
+{
+ SVCXPRT *xprt;
+
+ TAILQ_FOREACH(xprt, &pool->sp_active, xp_alink) {
+ if (!xprt->xp_thread) {
+ xprt_assignthread(xprt);
+ }
+ }
+}
+
+static bool_t
+svc_request_space_available(SVCPOOL *pool)
+{
+
+ mtx_assert(&pool->sp_lock, MA_OWNED);
+
+ if (pool->sp_space_throttled) {
+ /*
+ * Below the low-water yet? If so, assign any waiting sockets.
+ */
+ if (pool->sp_space_used < pool->sp_space_low) {
+ pool->sp_space_throttled = FALSE;
+ svc_assign_waiting_sockets(pool);
+ return TRUE;
+ }
+
+ return FALSE;
+ } else {
+ if (pool->sp_space_used
+ >= pool->sp_space_high) {
+ pool->sp_space_throttled = TRUE;
+ pool->sp_space_throttle_count++;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+
+static void
+svc_run_internal(SVCPOOL *pool, bool_t ismaster)
{
+ SVCTHREAD *st, *stpref;
SVCXPRT *xprt;
+ enum xprt_stat stat;
+ struct svc_req *rqstp;
int error;
+ st = mem_alloc(sizeof(*st));
+ st->st_xprt = NULL;
+ STAILQ_INIT(&st->st_reqs);
+ cv_init(&st->st_cond, "rpcsvc");
+
mtx_lock(&pool->sp_lock);
+ LIST_INSERT_HEAD(&pool->sp_threads, st, st_link);
- pool->sp_exited = FALSE;
+ /*
+ * If we are a new thread which was spawned to cope with
+ * increased load, set the state back to SVCPOOL_ACTIVE.
+ */
+ if (pool->sp_state == SVCPOOL_THREADSTARTING)
+ pool->sp_state = SVCPOOL_ACTIVE;
- while (!pool->sp_exited) {
- xprt = TAILQ_FIRST(&pool->sp_active);
- if (!xprt) {
- error = msleep(&pool->sp_active, &pool->sp_lock, PCATCH,
- "rpcsvc", 0);
- if (error)
+ while (pool->sp_state != SVCPOOL_CLOSING) {
+ /*
+ * Check for idle transports once per second.
+ */
+ if (time_uptime > pool->sp_lastidlecheck) {
+ pool->sp_lastidlecheck = time_uptime;
+ svc_checkidle(pool);
+ }
+
+ xprt = st->st_xprt;
+ if (!xprt && STAILQ_EMPTY(&st->st_reqs)) {
+ /*
+ * Enforce maxthreads count.
+ */
+ if (pool->sp_threadcount > pool->sp_maxthreads)
+ break;
+
+ /*
+ * Before sleeping, see if we can find an
+ * active transport which isn't being serviced
+ * by a thread.
+ */
+ if (svc_request_space_available(pool)) {
+ TAILQ_FOREACH(xprt, &pool->sp_active,
+ xp_alink) {
+ if (!xprt->xp_thread) {
+ SVC_ACQUIRE(xprt);
+ xprt->xp_thread = st;
+ st->st_xprt = xprt;
+ break;
+ }
+ }
+ }
+ if (st->st_xprt)
+ continue;
+
+ LIST_INSERT_HEAD(&pool->sp_idlethreads, st, st_ilink);
+ error = cv_timedwait_sig(&st->st_cond, &pool->sp_lock,
+ 5 * hz);
+ LIST_REMOVE(st, st_ilink);
+
+ /*
+ * Reduce worker thread count when idle.
+ */
+ if (error == EWOULDBLOCK) {
+ if (!ismaster
+ && (pool->sp_threadcount
+ > pool->sp_minthreads)
+ && !st->st_xprt
+ && STAILQ_EMPTY(&st->st_reqs))
+ break;
+ }
+ if (error == EWOULDBLOCK)
+ continue;
+ if (error) {
+ if (pool->sp_state != SVCPOOL_CLOSING) {
+ mtx_unlock(&pool->sp_lock);
+ svc_exit(pool);
+ mtx_lock(&pool->sp_lock);
+ }
break;
+ }
+
+ if (pool->sp_state == SVCPOOL_THREADWANTED) {
+ pool->sp_state = SVCPOOL_THREADSTARTING;
+ pool->sp_lastcreatetime = time_uptime;
+ mtx_unlock(&pool->sp_lock);
+ svc_new_thread(pool);
+ mtx_lock(&pool->sp_lock);
+ }
continue;
}
+ if (xprt) {
+ /*
+ * Drain the transport socket and queue up any
+ * RPCs.
+ */
+ xprt->xp_lastactive = time_uptime;
+ stat = XPRT_IDLE;
+ do {
+ if (!svc_request_space_available(pool))
+ break;
+ rqstp = NULL;
+ mtx_unlock(&pool->sp_lock);
+ stat = svc_getreq(xprt, &rqstp);
+ mtx_lock(&pool->sp_lock);
+ if (rqstp) {
+ /*
+ * See if the application has
+ * a preference for some other
+ * thread.
+ */
+ stpref = st;
+ if (pool->sp_assign)
+ stpref = pool->sp_assign(st,
+ rqstp);
+
+ pool->sp_space_used +=
+ rqstp->rq_size;
+ if (pool->sp_space_used
+ > pool->sp_space_used_highest)
+ pool->sp_space_used_highest =
+ pool->sp_space_used;
+ rqstp->rq_thread = stpref;
+ STAILQ_INSERT_TAIL(&stpref->st_reqs,
+ rqstp, rq_link);
+ stpref->st_reqcount++;
+
+ /*
+ * If we assigned the request
+ * to another thread, make
+ * sure its awake and continue
+ * reading from the
+ * socket. Otherwise, try to
+ * find some other thread to
+ * read from the socket and
+ * execute the request
+ * immediately.
+ */
+ if (stpref != st) {
+ cv_signal(&stpref->st_cond);
+ continue;
+ } else {
+ break;
+ }
+ }
+ } while (stat == XPRT_MOREREQS
+ && pool->sp_state != SVCPOOL_CLOSING);
+
+ /*
+ * Move this transport to the end of the
+ * active list to ensure fairness when
+ * multiple transports are active. If this was
+ * the last queued request, svc_getreq will
+ * end up calling xprt_inactive to remove from
+ * the active list.
+ */
+ xprt->xp_thread = NULL;
+ st->st_xprt = NULL;
+ if (xprt->xp_active) {
+ xprt_assignthread(xprt);
+ TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
+ TAILQ_INSERT_TAIL(&pool->sp_active, xprt,
+ xp_alink);
+ }
+ mtx_unlock(&pool->sp_lock);
+ SVC_RELEASE(xprt);
+ mtx_lock(&pool->sp_lock);
+ }
+
/*
- * Move this transport to the end to ensure fairness
- * when multiple transports are active. If this was
- * the last queued request, svc_getreq will end up
- * calling xprt_inactive to remove from the active
- * list.
+ * Execute what we have queued.
*/
- TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink);
- TAILQ_INSERT_TAIL(&pool->sp_active, xprt, xp_alink);
+ while ((rqstp = STAILQ_FIRST(&st->st_reqs)) != NULL) {
+ size_t sz = rqstp->rq_size;
+ mtx_unlock(&pool->sp_lock);
+ svc_executereq(rqstp);
+ mtx_lock(&pool->sp_lock);
+ pool->sp_space_used -= sz;
+ }
+ }
- mtx_unlock(&pool->sp_lock);
- svc_getreq(xprt);
- mtx_lock(&pool->sp_lock);
+ if (st->st_xprt) {
+ xprt = st->st_xprt;
+ st->st_xprt = NULL;
+ SVC_RELEASE(xprt);
+ }
+
+ KASSERT(STAILQ_EMPTY(&st->st_reqs), ("stray reqs on exit"));
+ LIST_REMOVE(st, st_link);
+ pool->sp_threadcount--;
+
+ mtx_unlock(&pool->sp_lock);
+
+ cv_destroy(&st->st_cond);
+ mem_free(st, sizeof(*st));
+
+ if (!ismaster)
+ wakeup(pool);
+}
+
+static void
+svc_thread_start(void *arg)
+{
+
+ svc_run_internal((SVCPOOL *) arg, FALSE);
+ kthread_exit();
+}
+
+static void
+svc_new_thread(SVCPOOL *pool)
+{
+ struct thread *td;
+
+ pool->sp_threadcount++;
+ kthread_add(svc_thread_start, pool,
+ pool->sp_proc, &td, 0, 0,
+ "%s: service", pool->sp_name);
+}
+
+void
+svc_run(SVCPOOL *pool)
+{
+ int i;
+ struct proc *p;
+ struct thread *td;
+
+ p = curproc;
+ td = curthread;
+ snprintf(td->td_name, sizeof(td->td_name),
+ "%s: master", pool->sp_name);
+ pool->sp_state = SVCPOOL_ACTIVE;
+ pool->sp_proc = p;
+ pool->sp_lastcreatetime = time_uptime;
+ pool->sp_threadcount = 1;
+
+ for (i = 1; i < pool->sp_minthreads; i++) {
+ svc_new_thread(pool);
}
+ svc_run_internal(pool, TRUE);
+
+ mtx_lock(&pool->sp_lock);
+ while (pool->sp_threadcount > 0)
+ msleep(pool, &pool->sp_lock, 0, "svcexit", 0);
mtx_unlock(&pool->sp_lock);
}
void
svc_exit(SVCPOOL *pool)
{
+ SVCTHREAD *st;
+
mtx_lock(&pool->sp_lock);
- pool->sp_exited = TRUE;
- wakeup(&pool->sp_active);
+
+ pool->sp_state = SVCPOOL_CLOSING;
+ LIST_FOREACH(st, &pool->sp_idlethreads, st_ilink)
+ cv_signal(&st->st_cond);
+
mtx_unlock(&pool->sp_lock);
}
+
+bool_t
+svc_getargs(struct svc_req *rqstp, xdrproc_t xargs, void *args)
+{
+ struct mbuf *m;
+ XDR xdrs;
+ bool_t stat;
+
+ m = rqstp->rq_args;
+ rqstp->rq_args = NULL;
+
+ xdrmbuf_create(&xdrs, m, XDR_DECODE);
+ stat = xargs(&xdrs, args);
+ XDR_DESTROY(&xdrs);
+
+ return (stat);
+}
+
+bool_t
+svc_freeargs(struct svc_req *rqstp, xdrproc_t xargs, void *args)
+{
+ XDR xdrs;
+
+ if (rqstp->rq_addr) {
+ free(rqstp->rq_addr, M_SONAME);
+ rqstp->rq_addr = NULL;
+ }
+
+ xdrs.x_op = XDR_FREE;
+ return (xargs(&xdrs, args));
+}
+
+void
+svc_freereq(struct svc_req *rqstp)
+{
+ SVCTHREAD *st;
+ SVCXPRT *xprt;
+ SVCPOOL *pool;
+
+ st = rqstp->rq_thread;
+ xprt = rqstp->rq_xprt;
+ if (xprt)
+ pool = xprt->xp_pool;
+ else
+ pool = NULL;
+ if (st) {
+ mtx_lock(&pool->sp_lock);
+ KASSERT(rqstp == STAILQ_FIRST(&st->st_reqs),
+ ("Freeing request out of order"));
+ STAILQ_REMOVE_HEAD(&st->st_reqs, rq_link);
+ st->st_reqcount--;
+ if (pool->sp_done)
+ pool->sp_done(st, rqstp);
+ mtx_unlock(&pool->sp_lock);
+ }
+
+ if (rqstp->rq_auth.svc_ah_ops)
+ SVCAUTH_RELEASE(&rqstp->rq_auth);
+
+ if (rqstp->rq_xprt) {
+ SVC_RELEASE(rqstp->rq_xprt);
+ }
+
+ if (rqstp->rq_addr)
+ free(rqstp->rq_addr, M_SONAME);
+
+ if (rqstp->rq_args)
+ m_freem(rqstp->rq_args);
+
+ free(rqstp, M_RPC);
+}
diff --git a/sys/rpc/svc.h b/sys/rpc/svc.h
index 21c7491..eac9bc0 100644
--- a/sys/rpc/svc.h
+++ b/sys/rpc/svc.h
@@ -47,6 +47,9 @@
#include <sys/queue.h>
#include <sys/_lock.h>
#include <sys/_mutex.h>
+#include <sys/_sx.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
#endif
/*
@@ -92,8 +95,23 @@ enum xprt_stat {
};
struct __rpc_svcxprt;
+struct mbuf;
struct xp_ops {
+#ifdef _KERNEL
+ /* receive incoming requests */
+ bool_t (*xp_recv)(struct __rpc_svcxprt *, struct rpc_msg *,
+ struct sockaddr **, struct mbuf **);
+ /* get transport status */
+ enum xprt_stat (*xp_stat)(struct __rpc_svcxprt *);
+ /* send reply */
+ bool_t (*xp_reply)(struct __rpc_svcxprt *, struct rpc_msg *,
+ struct sockaddr *, struct mbuf *);
+ /* destroy this struct */
+ void (*xp_destroy)(struct __rpc_svcxprt *);
+ /* catch-all function */
+ bool_t (*xp_control)(struct __rpc_svcxprt *, const u_int, void *);
+#else
/* receive incoming requests */
bool_t (*xp_recv)(struct __rpc_svcxprt *, struct rpc_msg *);
/* get transport status */
@@ -106,9 +124,6 @@ struct xp_ops {
bool_t (*xp_freeargs)(struct __rpc_svcxprt *, xdrproc_t, void *);
/* destroy this struct */
void (*xp_destroy)(struct __rpc_svcxprt *);
-#ifdef _KERNEL
- /* catch-all function */
- bool_t (*xp_control)(struct __rpc_svcxprt *, const u_int, void *);
#endif
};
@@ -121,32 +136,35 @@ struct xp_ops2 {
#ifdef _KERNEL
struct __rpc_svcpool;
+struct __rpc_svcthread;
#endif
/*
- * Server side transport handle
+ * Server side transport handle. In the kernel, transports have a
+ * reference count which tracks the number of currently assigned
+ * worker threads plus one for the service pool's reference.
*/
typedef struct __rpc_svcxprt {
#ifdef _KERNEL
- struct mtx xp_lock;
+ volatile u_int xp_refs;
+ struct sx xp_lock;
struct __rpc_svcpool *xp_pool; /* owning pool (see below) */
TAILQ_ENTRY(__rpc_svcxprt) xp_link;
TAILQ_ENTRY(__rpc_svcxprt) xp_alink;
bool_t xp_registered; /* xprt_register has been called */
bool_t xp_active; /* xprt_active has been called */
+ struct __rpc_svcthread *xp_thread; /* assigned service thread */
struct socket* xp_socket;
const struct xp_ops *xp_ops;
char *xp_netid; /* network token */
- struct netbuf xp_ltaddr; /* local transport address */
- struct netbuf xp_rtaddr; /* remote transport address */
- struct opaque_auth xp_verf; /* raw response verifier */
- uint32_t xp_xid; /* current transaction ID */
- XDR xp_xdrreq; /* xdr stream for decoding request */
- XDR xp_xdrrep; /* xdr stream for encoding reply */
+ struct sockaddr_storage xp_ltaddr; /* local transport address */
+ struct sockaddr_storage xp_rtaddr; /* remote transport address */
void *xp_p1; /* private: for use by svc ops */
void *xp_p2; /* private: for use by svc ops */
void *xp_p3; /* private: for use by svc lib */
int xp_type; /* transport type */
+ int xp_idletimeout; /* idle time before closing */
+ time_t xp_lastactive; /* time of last RPC */
#else
int xp_fd;
u_short xp_port; /* associated port number */
@@ -167,6 +185,33 @@ typedef struct __rpc_svcxprt {
#endif
} SVCXPRT;
+/*
+ * Interface to server-side authentication flavors.
+ */
+typedef struct __rpc_svcauth {
+ struct svc_auth_ops {
+#ifdef _KERNEL
+ int (*svc_ah_wrap)(struct __rpc_svcauth *, struct mbuf **);
+ int (*svc_ah_unwrap)(struct __rpc_svcauth *, struct mbuf **);
+ void (*svc_ah_release)(struct __rpc_svcauth *);
+#else
+ int (*svc_ah_wrap)(struct __rpc_svcauth *, XDR *,
+ xdrproc_t, caddr_t);
+ int (*svc_ah_unwrap)(struct __rpc_svcauth *, XDR *,
+ xdrproc_t, caddr_t);
+#endif
+ } *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;
+
#ifdef _KERNEL
/*
@@ -184,6 +229,61 @@ struct svc_callout {
};
TAILQ_HEAD(svc_callout_list, svc_callout);
+struct __rpc_svcthread;
+
+/*
+ * Service request
+ */
+struct svc_req {
+ STAILQ_ENTRY(svc_req) rq_link; /* list of requests for a thread */
+ struct __rpc_svcthread *rq_thread; /* thread which is to execute this */
+ uint32_t rq_xid; /* RPC transaction ID */
+ uint32_t rq_prog; /* service program number */
+ uint32_t rq_vers; /* service protocol version */
+ uint32_t rq_proc; /* the desired procedure */
+ size_t rq_size; /* space used by request */
+ struct mbuf *rq_args; /* XDR-encoded procedure arguments */
+ struct opaque_auth rq_cred; /* raw creds from the wire */
+ struct opaque_auth rq_verf; /* verifier for the reply */
+ void *rq_clntcred; /* read only cooked cred */
+ SVCAUTH rq_auth; /* interface to auth methods */
+ SVCXPRT *rq_xprt; /* associated transport */
+ struct sockaddr *rq_addr; /* reply address or NULL if connected */
+ void *rq_p1; /* application workspace */
+ int rq_p2; /* application workspace */
+ uint64_t rq_p3; /* application workspace */
+ char rq_credarea[3*MAX_AUTH_BYTES];
+};
+STAILQ_HEAD(svc_reqlist, svc_req);
+
+#define svc_getrpccaller(rq) \
+ ((rq)->rq_addr ? (rq)->rq_addr : \
+ (struct sockaddr *) &(rq)->rq_xprt->xp_rtaddr)
+
+/*
+ * This structure is used to manage a thread which is executing
+ * requests from a service pool. A service thread is in one of three
+ * states:
+ *
+ * SVCTHREAD_SLEEPING waiting for a request to process
+ * SVCTHREAD_ACTIVE processing a request
+ * SVCTHREAD_EXITING exiting after finishing current request
+ *
+ * Threads which have no work to process sleep on the pool's sp_active
+ * list. When a transport becomes active, it is assigned a service
+ * thread to read and execute pending RPCs.
+ */
+typedef struct __rpc_svcthread {
+ SVCXPRT *st_xprt; /* transport we are processing */
+ struct svc_reqlist st_reqs; /* RPC requests to execute */
+ int st_reqcount; /* number of queued reqs */
+ struct cv st_cond; /* sleeping for work */
+ LIST_ENTRY(__rpc_svcthread) st_link; /* all threads list */
+ LIST_ENTRY(__rpc_svcthread) st_ilink; /* idle threads list */
+ LIST_ENTRY(__rpc_svcthread) st_alink; /* application thread list */
+} SVCTHREAD;
+LIST_HEAD(svcthread_list, __rpc_svcthread);
+
/*
* In the kernel, we can't use global variables to store lists of
* transports etc. since otherwise we could not have two unrelated RPC
@@ -197,15 +297,55 @@ TAILQ_HEAD(svc_callout_list, svc_callout);
* server.
*/
TAILQ_HEAD(svcxprt_list, __rpc_svcxprt);
+enum svcpool_state {
+ SVCPOOL_INIT, /* svc_run not called yet */
+ SVCPOOL_ACTIVE, /* normal running state */
+ SVCPOOL_THREADWANTED, /* new service thread requested */
+ SVCPOOL_THREADSTARTING, /* new service thread started */
+ SVCPOOL_CLOSING /* svc_exit called */
+};
+typedef SVCTHREAD *pool_assign_fn(SVCTHREAD *, struct svc_req *);
+typedef void pool_done_fn(SVCTHREAD *, struct svc_req *);
typedef struct __rpc_svcpool {
struct mtx sp_lock; /* protect the transport lists */
+ const char *sp_name; /* pool name (e.g. "nfsd", "NLM" */
+ enum svcpool_state sp_state; /* current pool state */
+ struct proc *sp_proc; /* process which is in svc_run */
struct svcxprt_list sp_xlist; /* all transports in the pool */
struct svcxprt_list sp_active; /* transports needing service */
struct svc_callout_list sp_callouts; /* (prog,vers)->dispatch list */
- bool_t sp_exited; /* true if shutting down */
+ struct svcthread_list sp_threads; /* service threads */
+ struct svcthread_list sp_idlethreads; /* idle service threads */
+ int sp_minthreads; /* minimum service thread count */
+ int sp_maxthreads; /* maximum service thread count */
+ int sp_threadcount; /* current service thread count */
+ time_t sp_lastcreatetime; /* when we last started a thread */
+ time_t sp_lastidlecheck; /* when we last checked idle transports */
+
+ /*
+ * Hooks to allow an application to control request to thread
+ * placement.
+ */
+ pool_assign_fn *sp_assign;
+ pool_done_fn *sp_done;
+
+ /*
+ * These variables are used to put an upper bound on the
+ * amount of memory used by RPC requests which are queued
+ * waiting for execution.
+ */
+ unsigned int sp_space_low;
+ unsigned int sp_space_high;
+ unsigned int sp_space_used;
+ unsigned int sp_space_used_highest;
+ bool_t sp_space_throttled;
+ int sp_space_throttle_count;
+
+ struct replay_cache *sp_rcache; /* optional replay cache */
+ struct sysctl_ctx_list sp_sysctl;
} SVCPOOL;
-#endif
+#else
/*
* Service request
@@ -224,6 +364,8 @@ struct svc_req {
*/
#define svc_getrpccaller(x) (&(x)->xp_rtaddr)
+#endif
+
/*
* Operations defined on an SVCXPRT handle
*
@@ -232,6 +374,32 @@ struct svc_req {
* xdrproc_t xargs;
* void * argsp;
*/
+#ifdef _KERNEL
+
+#define SVC_ACQUIRE(xprt) \
+ refcount_acquire(&(xprt)->xp_refs)
+
+#define SVC_RELEASE(xprt) \
+ if (refcount_release(&(xprt)->xp_refs)) \
+ SVC_DESTROY(xprt)
+
+#define SVC_RECV(xprt, msg, addr, args) \
+ (*(xprt)->xp_ops->xp_recv)((xprt), (msg), (addr), (args))
+
+#define SVC_STAT(xprt) \
+ (*(xprt)->xp_ops->xp_stat)(xprt)
+
+#define SVC_REPLY(xprt, msg, addr, m) \
+ (*(xprt)->xp_ops->xp_reply) ((xprt), (msg), (addr), (m))
+
+#define SVC_DESTROY(xprt) \
+ (*(xprt)->xp_ops->xp_destroy)(xprt)
+
+#define SVC_CONTROL(xprt, rq, in) \
+ (*(xprt)->xp_ops->xp_control)((xprt), (rq), (in))
+
+#else
+
#define SVC_RECV(xprt, msg) \
(*(xprt)->xp_ops->xp_recv)((xprt), (msg))
#define svc_recv(xprt, msg) \
@@ -262,12 +430,32 @@ struct svc_req {
#define svc_destroy(xprt) \
(*(xprt)->xp_ops->xp_destroy)(xprt)
-#ifdef _KERNEL
-#define SVC_CONTROL(xprt, rq, in) \
- (*(xprt)->xp_ops->xp_control)((xprt), (rq), (in))
-#else
#define SVC_CONTROL(xprt, rq, in) \
(*(xprt)->xp_ops2->xp_control)((xprt), (rq), (in))
+
+#endif
+
+#define SVC_EXT(xprt) \
+ ((SVCXPRT_EXT *) xprt->xp_p3)
+
+#define SVC_AUTH(xprt) \
+ (SVC_EXT(xprt)->xp_auth)
+
+/*
+ * Operations defined on an SVCAUTH handle
+ */
+#ifdef _KERNEL
+#define SVCAUTH_WRAP(auth, mp) \
+ ((auth)->svc_ah_ops->svc_ah_wrap(auth, mp))
+#define SVCAUTH_UNWRAP(auth, mp) \
+ ((auth)->svc_ah_ops->svc_ah_unwrap(auth, mp))
+#define SVCAUTH_RELEASE(auth) \
+ ((auth)->svc_ah_ops->svc_ah_release(auth))
+#else
+#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))
#endif
/*
@@ -332,6 +520,7 @@ __END_DECLS
__BEGIN_DECLS
extern void xprt_active(SVCXPRT *);
extern void xprt_inactive(SVCXPRT *);
+extern void xprt_inactive_locked(SVCXPRT *);
__END_DECLS
#endif
@@ -363,6 +552,17 @@ __END_DECLS
*/
__BEGIN_DECLS
+#ifdef _KERNEL
+extern bool_t svc_sendreply(struct svc_req *, xdrproc_t, void *);
+extern bool_t svc_sendreply_mbuf(struct svc_req *, struct mbuf *);
+extern void svcerr_decode(struct svc_req *);
+extern void svcerr_weakauth(struct svc_req *);
+extern void svcerr_noproc(struct svc_req *);
+extern void svcerr_progvers(struct svc_req *, rpcvers_t, rpcvers_t);
+extern void svcerr_auth(struct svc_req *, enum auth_stat);
+extern void svcerr_noprog(struct svc_req *);
+extern void svcerr_systemerr(struct svc_req *);
+#else
extern bool_t svc_sendreply(SVCXPRT *, xdrproc_t, void *);
extern void svcerr_decode(SVCXPRT *);
extern void svcerr_weakauth(SVCXPRT *);
@@ -371,6 +571,7 @@ extern void svcerr_progvers(SVCXPRT *, rpcvers_t, rpcvers_t);
extern void svcerr_auth(SVCXPRT *, enum auth_stat);
extern void svcerr_noprog(SVCXPRT *);
extern void svcerr_systemerr(SVCXPRT *);
+#endif
extern int rpc_reg(rpcprog_t, rpcvers_t, rpcproc_t,
char *(*)(char *), xdrproc_t, xdrproc_t,
char *);
@@ -410,6 +611,8 @@ extern void rpctest_service(void);
__END_DECLS
__BEGIN_DECLS
+extern SVCXPRT *svc_xprt_alloc(void);
+extern void svc_xprt_free(SVCXPRT *);
#ifndef _KERNEL
extern void svc_getreq(int);
extern void svc_getreqset(fd_set *);
@@ -421,6 +624,10 @@ extern void svc_exit(void);
#else
extern void svc_run(SVCPOOL *);
extern void svc_exit(SVCPOOL *);
+extern bool_t svc_getargs(struct svc_req *, xdrproc_t, void *);
+extern bool_t svc_freeargs(struct svc_req *, xdrproc_t, void *);
+extern void svc_freereq(struct svc_req *);
+
#endif
__END_DECLS
@@ -441,7 +648,8 @@ __BEGIN_DECLS
/*
* Create a new service pool.
*/
-extern SVCPOOL* svcpool_create(void);
+extern SVCPOOL* svcpool_create(const char *name,
+ struct sysctl_oid_list *sysctl_base);
/*
* Destroy a service pool, including all registered transports.
diff --git a/sys/rpc/svc_auth.c b/sys/rpc/svc_auth.c
index 22d4e61..6d5a79b 100644
--- a/sys/rpc/svc_auth.c
+++ b/sys/rpc/svc_auth.c
@@ -52,6 +52,13 @@ __FBSDID("$FreeBSD$");
#include <rpc/rpc.h>
+static enum auth_stat (*_svcauth_rpcsec_gss)(struct svc_req *,
+ struct rpc_msg *) = NULL;
+static int (*_svcauth_rpcsec_gss_getcred)(struct svc_req *,
+ struct ucred **, int *);
+
+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
@@ -77,8 +84,8 @@ _authenticate(struct svc_req *rqst, struct rpc_msg *msg)
enum auth_stat dummy;
rqst->rq_cred = msg->rm_call.cb_cred;
- rqst->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor;
- rqst->rq_xprt->xp_verf.oa_length = 0;
+ rqst->rq_auth.svc_ah_ops = &svc_auth_null_ops;
+ rqst->rq_auth.svc_ah_private = NULL;
cred_flavor = rqst->rq_cred.oa_flavor;
switch (cred_flavor) {
case AUTH_NULL:
@@ -90,6 +97,11 @@ _authenticate(struct svc_req *rqst, struct rpc_msg *msg)
case AUTH_SHORT:
dummy = _svcauth_short(rqst, msg);
return (dummy);
+ case RPCSEC_GSS:
+ if (!_svcauth_rpcsec_gss)
+ return (AUTH_REJECTEDCRED);
+ dummy = _svcauth_rpcsec_gss(rqst, msg);
+ return (dummy);
default:
break;
}
@@ -97,21 +109,65 @@ _authenticate(struct svc_req *rqst, struct rpc_msg *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(SVCAUTH *auth, struct mbuf **mp)
+{
+
+ return (TRUE);
+}
+
+static bool_t
+svcauth_null_unwrap(SVCAUTH *auth, struct mbuf **mp)
+{
+
+ return (TRUE);
+}
+
+static void
+svcauth_null_release(SVCAUTH *auth)
+{
+
+}
+
+static struct svc_auth_ops svc_auth_null_ops = {
+ svcauth_null_wrap,
+ svcauth_null_unwrap,
+ svcauth_null_release,
+};
+
/*ARGSUSED*/
enum auth_stat
_svcauth_null(struct svc_req *rqst, struct rpc_msg *msg)
{
+
+ rqst->rq_verf = _null_auth;
return (AUTH_OK);
}
int
-svc_getcred(struct svc_req *rqst, struct ucred *cr, int *flavorp)
+svc_auth_reg(int flavor,
+ enum auth_stat (*svcauth)(struct svc_req *, struct rpc_msg *),
+ int (*getcred)(struct svc_req *, struct ucred **, int *))
{
+
+ if (flavor == RPCSEC_GSS) {
+ _svcauth_rpcsec_gss = svcauth;
+ _svcauth_rpcsec_gss_getcred = getcred;
+ }
+ return (TRUE);
+}
+
+int
+svc_getcred(struct svc_req *rqst, struct ucred **crp, int *flavorp)
+{
+ struct ucred *cr = NULL;
int flavor, i;
struct xucred *xcr;
- KASSERT(!crshared(cr), ("svc_getcred with shared cred"));
-
flavor = rqst->rq_cred.oa_flavor;
if (flavorp)
*flavorp = flavor;
@@ -119,13 +175,20 @@ svc_getcred(struct svc_req *rqst, struct ucred *cr, int *flavorp)
switch (flavor) {
case AUTH_UNIX:
xcr = (struct xucred *) rqst->rq_clntcred;
+ cr = crget();
cr->cr_uid = cr->cr_ruid = cr->cr_svuid = xcr->cr_uid;
cr->cr_ngroups = xcr->cr_ngroups;
for (i = 0; i < xcr->cr_ngroups; i++)
cr->cr_groups[i] = xcr->cr_groups[i];
- cr->cr_rgid = cr->cr_groups[0];
+ cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
+ *crp = cr;
return (TRUE);
+ case RPCSEC_GSS:
+ if (!_svcauth_rpcsec_gss_getcred)
+ return (FALSE);
+ return (_svcauth_rpcsec_gss_getcred(rqst, crp, flavorp));
+
default:
return (FALSE);
}
diff --git a/sys/rpc/svc_auth.h b/sys/rpc/svc_auth.h
index 26c191a..9e23876 100644
--- a/sys/rpc/svc_auth.h
+++ b/sys/rpc/svc_auth.h
@@ -47,19 +47,31 @@
*/
__BEGIN_DECLS
extern enum auth_stat _authenticate(struct svc_req *, struct rpc_msg *);
+#ifdef _KERNEL
+extern int svc_auth_reg(int,
+ enum auth_stat (*)(struct svc_req *, struct rpc_msg *),
+ int (*)(struct svc_req *, struct ucred **, int *));
+#else
+extern int svc_auth_reg(int, enum auth_stat (*)(struct svc_req *,
+ struct rpc_msg *));
+#endif
-extern int svc_getcred(struct svc_req *, struct ucred *, int *);
+
+extern int svc_getcred(struct svc_req *, struct ucred **, int *);
/*
* struct svc_req *req; -- RPC request
- * struct ucred *cr -- Kernel cred to modify
+ * struct ucred **crp -- Kernel cred to modify
* int *flavorp -- Return RPC auth flavor
*
* Retrieve unix creds corresponding to an RPC request, if
* possible. The auth flavor (AUTH_NONE or AUTH_UNIX) is returned in
- * *flavorp. If the flavor is AUTH_UNIX the caller's ucred structure
- * will be modified to reflect the values from the request. Return's
- * non-zero if credentials were retrieved form the request, otherwise
- * zero.
+ * *flavorp. If the flavor is AUTH_UNIX the caller's ucred pointer
+ * will be modified to point at a ucred structure which reflects the
+ * values from the request. The caller should call crfree on this
+ * pointer.
+ *
+ * Return's non-zero if credentials were retrieved from the request,
+ * otherwise zero.
*/
__END_DECLS
diff --git a/sys/rpc/svc_auth_unix.c b/sys/rpc/svc_auth_unix.c
index 9c6cdd7..0c11a4a 100644
--- a/sys/rpc/svc_auth_unix.c
+++ b/sys/rpc/svc_auth_unix.c
@@ -120,8 +120,7 @@ _svcauth_unix(struct svc_req *rqst, struct rpc_msg *msg)
goto done;
}
- rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
- rqst->rq_xprt->xp_verf.oa_length = 0;
+ rqst->rq_verf = _null_auth;
stat = AUTH_OK;
done:
XDR_DESTROY(&xdrs);
diff --git a/sys/rpc/svc_dg.c b/sys/rpc/svc_dg.c
index 666b952..72721b0 100644
--- a/sys/rpc/svc_dg.c
+++ b/sys/rpc/svc_dg.c
@@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
+#include <sys/sx.h>
#include <sys/systm.h>
#include <sys/uio.h>
@@ -61,10 +62,10 @@ __FBSDID("$FreeBSD$");
#include <rpc/rpc_com.h>
static enum xprt_stat svc_dg_stat(SVCXPRT *);
-static bool_t svc_dg_recv(SVCXPRT *, struct rpc_msg *);
-static bool_t svc_dg_reply(SVCXPRT *, struct rpc_msg *);
-static bool_t svc_dg_getargs(SVCXPRT *, xdrproc_t, void *);
-static bool_t svc_dg_freeargs(SVCXPRT *, xdrproc_t, void *);
+static bool_t svc_dg_recv(SVCXPRT *, struct rpc_msg *,
+ struct sockaddr **, struct mbuf **);
+static bool_t svc_dg_reply(SVCXPRT *, struct rpc_msg *,
+ struct sockaddr *, struct mbuf *);
static void svc_dg_destroy(SVCXPRT *);
static bool_t svc_dg_control(SVCXPRT *, const u_int, void *);
static void svc_dg_soupcall(struct socket *so, void *arg, int waitflag);
@@ -72,9 +73,7 @@ static void svc_dg_soupcall(struct socket *so, void *arg, int waitflag);
static struct xp_ops svc_dg_ops = {
.xp_recv = svc_dg_recv,
.xp_stat = svc_dg_stat,
- .xp_getargs = svc_dg_getargs,
.xp_reply = svc_dg_reply,
- .xp_freeargs = svc_dg_freeargs,
.xp_destroy = svc_dg_destroy,
.xp_control = svc_dg_control,
};
@@ -116,9 +115,8 @@ svc_dg_create(SVCPOOL *pool, struct socket *so, size_t sendsize,
return (NULL);
}
- xprt = mem_alloc(sizeof (SVCXPRT));
- memset(xprt, 0, sizeof (SVCXPRT));
- mtx_init(&xprt->xp_lock, "xprt->xp_lock", NULL, MTX_DEF);
+ xprt = svc_xprt_alloc();
+ sx_init(&xprt->xp_lock, "xprt->xp_lock");
xprt->xp_pool = pool;
xprt->xp_socket = so;
xprt->xp_p1 = NULL;
@@ -129,16 +127,9 @@ svc_dg_create(SVCPOOL *pool, struct socket *so, size_t sendsize,
if (error)
goto freedata;
- xprt->xp_ltaddr.buf = mem_alloc(sizeof (struct sockaddr_storage));
- xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_storage);
- xprt->xp_ltaddr.len = sa->sa_len;
- memcpy(xprt->xp_ltaddr.buf, sa, sa->sa_len);
+ memcpy(&xprt->xp_ltaddr, sa, sa->sa_len);
free(sa, M_SONAME);
- xprt->xp_rtaddr.buf = mem_alloc(sizeof (struct sockaddr_storage));
- xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage);
- xprt->xp_rtaddr.len = 0;
-
xprt_register(xprt);
SOCKBUF_LOCK(&so->so_rcv);
@@ -151,7 +142,7 @@ svc_dg_create(SVCPOOL *pool, struct socket *so, size_t sendsize,
freedata:
(void) printf(svc_dg_str, __no_mem_str);
if (xprt) {
- (void) mem_free(xprt, sizeof (SVCXPRT));
+ svc_xprt_free(xprt);
}
return (NULL);
}
@@ -161,34 +152,34 @@ static enum xprt_stat
svc_dg_stat(SVCXPRT *xprt)
{
+ if (soreadable(xprt->xp_socket))
+ return (XPRT_MOREREQS);
+
return (XPRT_IDLE);
}
static bool_t
-svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg)
+svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg,
+ struct sockaddr **addrp, struct mbuf **mp)
{
struct uio uio;
struct sockaddr *raddr;
struct mbuf *mreq;
+ XDR xdrs;
int error, rcvflag;
/*
+ * Serialise access to the socket.
+ */
+ sx_xlock(&xprt->xp_lock);
+
+ /*
* The socket upcall calls xprt_active() which will eventually
* cause the server to call us here. We attempt to read a
* packet from the socket and process it. If the read fails,
* we have drained all pending requests so we call
* xprt_inactive().
- *
- * The lock protects us in the case where a new packet arrives
- * on the socket after our call to soreceive fails with
- * EWOULDBLOCK - the call to xprt_active() in the upcall will
- * happen only after our call to xprt_inactive() which ensures
- * that we will remain active. It might be possible to use
- * SOCKBUF_LOCK for this - its not clear to me what locks are
- * held during the upcall.
*/
- mtx_lock(&xprt->xp_lock);
-
uio.uio_resid = 1000000000;
uio.uio_td = curthread;
mreq = NULL;
@@ -196,8 +187,19 @@ svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg)
error = soreceive(xprt->xp_socket, &raddr, &uio, &mreq, NULL, &rcvflag);
if (error == EWOULDBLOCK) {
- xprt_inactive(xprt);
- mtx_unlock(&xprt->xp_lock);
+ /*
+ * We must re-test for readability after taking the
+ * lock to protect us in the case where a new packet
+ * arrives on the socket after our call to soreceive
+ * fails with EWOULDBLOCK. The pool lock protects us
+ * from racing the upcall after our soreadable() call
+ * returns false.
+ */
+ mtx_lock(&xprt->xp_pool->sp_lock);
+ if (!soreadable(xprt->xp_socket))
+ xprt_inactive_locked(xprt);
+ mtx_unlock(&xprt->xp_pool->sp_lock);
+ sx_xunlock(&xprt->xp_lock);
return (FALSE);
}
@@ -208,45 +210,52 @@ svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg)
xprt->xp_socket->so_rcv.sb_flags &= ~SB_UPCALL;
SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
xprt_inactive(xprt);
- mtx_unlock(&xprt->xp_lock);
+ sx_xunlock(&xprt->xp_lock);
return (FALSE);
}
- mtx_unlock(&xprt->xp_lock);
-
- KASSERT(raddr->sa_len < xprt->xp_rtaddr.maxlen,
- ("Unexpected remote address length"));
- memcpy(xprt->xp_rtaddr.buf, raddr, raddr->sa_len);
- xprt->xp_rtaddr.len = raddr->sa_len;
- free(raddr, M_SONAME);
+ sx_xunlock(&xprt->xp_lock);
- xdrmbuf_create(&xprt->xp_xdrreq, mreq, XDR_DECODE);
- if (! xdr_callmsg(&xprt->xp_xdrreq, msg)) {
- XDR_DESTROY(&xprt->xp_xdrreq);
+ xdrmbuf_create(&xdrs, mreq, XDR_DECODE);
+ if (! xdr_callmsg(&xdrs, msg)) {
+ XDR_DESTROY(&xdrs);
return (FALSE);
}
- xprt->xp_xid = msg->rm_xid;
+
+ *addrp = raddr;
+ *mp = xdrmbuf_getall(&xdrs);
+ XDR_DESTROY(&xdrs);
return (TRUE);
}
static bool_t
-svc_dg_reply(SVCXPRT *xprt, struct rpc_msg *msg)
+svc_dg_reply(SVCXPRT *xprt, struct rpc_msg *msg,
+ struct sockaddr *addr, struct mbuf *m)
{
+ XDR xdrs;
struct mbuf *mrep;
- bool_t stat = FALSE;
+ bool_t stat = TRUE;
int error;
MGETHDR(mrep, M_WAIT, MT_DATA);
- MCLGET(mrep, M_WAIT);
mrep->m_len = 0;
- xdrmbuf_create(&xprt->xp_xdrrep, mrep, XDR_ENCODE);
- msg->rm_xid = xprt->xp_xid;
- if (xdr_replymsg(&xprt->xp_xdrrep, msg)) {
+ xdrmbuf_create(&xdrs, mrep, XDR_ENCODE);
+
+ if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
+ msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
+ if (!xdr_replymsg(&xdrs, msg))
+ stat = FALSE;
+ else
+ xdrmbuf_append(&xdrs, m);
+ } else {
+ stat = xdr_replymsg(&xdrs, msg);
+ }
+
+ if (stat) {
m_fixhdr(mrep);
- error = sosend(xprt->xp_socket,
- (struct sockaddr *) xprt->xp_rtaddr.buf, NULL, mrep, NULL,
+ error = sosend(xprt->xp_socket, addr, NULL, mrep, NULL,
0, curthread);
if (!error) {
stat = TRUE;
@@ -255,61 +264,29 @@ svc_dg_reply(SVCXPRT *xprt, struct rpc_msg *msg)
m_freem(mrep);
}
- /*
- * This frees the request mbuf chain as well. The reply mbuf
- * chain was consumed by sosend.
- */
- XDR_DESTROY(&xprt->xp_xdrreq);
- XDR_DESTROY(&xprt->xp_xdrrep);
+ XDR_DESTROY(&xdrs);
xprt->xp_p2 = NULL;
return (stat);
}
-static bool_t
-svc_dg_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
-{
-
- return (xdr_args(&xprt->xp_xdrreq, args_ptr));
-}
-
-static bool_t
-svc_dg_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
-{
- XDR xdrs;
-
- /*
- * Free the request mbuf here - this allows us to handle
- * protocols where not all requests have replies
- * (i.e. NLM). Note that xdrmbuf_destroy handles being called
- * twice correctly - the mbuf will only be freed once.
- */
- XDR_DESTROY(&xprt->xp_xdrreq);
-
- xdrs.x_op = XDR_FREE;
- return (xdr_args(&xdrs, args_ptr));
-}
-
static void
svc_dg_destroy(SVCXPRT *xprt)
{
+
SOCKBUF_LOCK(&xprt->xp_socket->so_rcv);
xprt->xp_socket->so_upcallarg = NULL;
xprt->xp_socket->so_upcall = NULL;
xprt->xp_socket->so_rcv.sb_flags &= ~SB_UPCALL;
SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
- xprt_unregister(xprt);
-
- mtx_destroy(&xprt->xp_lock);
+ sx_destroy(&xprt->xp_lock);
if (xprt->xp_socket)
(void)soclose(xprt->xp_socket);
- if (xprt->xp_rtaddr.buf)
- (void) mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen);
- if (xprt->xp_ltaddr.buf)
- (void) mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen);
- (void) mem_free(xprt, sizeof (SVCXPRT));
+ if (xprt->xp_netid)
+ (void) mem_free(xprt->xp_netid, strlen(xprt->xp_netid) + 1);
+ svc_xprt_free(xprt);
}
static bool_t
@@ -328,7 +305,5 @@ svc_dg_soupcall(struct socket *so, void *arg, int waitflag)
{
SVCXPRT *xprt = (SVCXPRT *) arg;
- mtx_lock(&xprt->xp_lock);
xprt_active(xprt);
- mtx_unlock(&xprt->xp_lock);
}
diff --git a/sys/rpc/svc_generic.c b/sys/rpc/svc_generic.c
index 1f9b2e2..790b4ba 100644
--- a/sys/rpc/svc_generic.c
+++ b/sys/rpc/svc_generic.c
@@ -178,102 +178,13 @@ svc_tp_create(
"svc_tp_create: Could not register prog %u vers %u on %s\n",
(unsigned)prognum, (unsigned)versnum,
nconf->nc_netid);
- SVC_DESTROY(xprt);
+ xprt_unregister(xprt);
return (NULL);
}
return (xprt);
}
/*
- * Bind a socket to a privileged IP port
- */
-int bindresvport(struct socket *so, struct sockaddr *sa);
-int
-bindresvport(struct socket *so, struct sockaddr *sa)
-{
- int old, error, af;
- bool_t freesa = FALSE;
- struct sockaddr_in *sin;
-#ifdef INET6
- struct sockaddr_in6 *sin6;
-#endif
- struct sockopt opt;
- int proto, portrange, portlow;
- u_int16_t *portp;
- socklen_t salen;
-
- if (sa == NULL) {
- error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
- if (error)
- return (error);
- freesa = TRUE;
- af = sa->sa_family;
- salen = sa->sa_len;
- memset(sa, 0, sa->sa_len);
- } else {
- af = sa->sa_family;
- salen = sa->sa_len;
- }
-
- switch (af) {
- case AF_INET:
- proto = IPPROTO_IP;
- portrange = IP_PORTRANGE;
- portlow = IP_PORTRANGE_LOW;
- sin = (struct sockaddr_in *)sa;
- portp = &sin->sin_port;
- break;
-#ifdef INET6
- case AF_INET6:
- proto = IPPROTO_IPV6;
- portrange = IPV6_PORTRANGE;
- portlow = IPV6_PORTRANGE_LOW;
- sin6 = (struct sockaddr_in6 *)sa;
- portp = &sin6->sin6_port;
- break;
-#endif
- default:
- return (EPFNOSUPPORT);
- }
-
- sa->sa_family = af;
- sa->sa_len = salen;
-
- if (*portp == 0) {
- bzero(&opt, sizeof(opt));
- opt.sopt_dir = SOPT_GET;
- opt.sopt_level = proto;
- opt.sopt_name = portrange;
- opt.sopt_val = &old;
- opt.sopt_valsize = sizeof(old);
- error = sogetopt(so, &opt);
- if (error)
- goto out;
-
- opt.sopt_dir = SOPT_SET;
- opt.sopt_val = &portlow;
- error = sosetopt(so, &opt);
- if (error)
- goto out;
- }
-
- error = sobind(so, sa, curthread);
-
- if (*portp == 0) {
- if (error) {
- opt.sopt_dir = SOPT_SET;
- opt.sopt_val = &old;
- sosetopt(so, &opt);
- }
- }
-out:
- if (freesa)
- free(sa, M_SONAME);
-
- return (error);
-}
-
-/*
* If so is NULL, then it opens a socket for the given transport
* provider (nconf cannot be NULL then). If the t_state is T_UNBND and
* bindaddr is NON-NULL, it performs a t_bind using the bindaddr. For
@@ -401,7 +312,7 @@ freedata:
if (xprt) {
if (!madeso) /* so that svc_destroy doesnt close fd */
xprt->xp_socket = NULL;
- SVC_DESTROY(xprt);
+ xprt_unregister(xprt);
}
return (NULL);
}
diff --git a/sys/rpc/svc_vc.c b/sys/rpc/svc_vc.c
index 47530da..e3f0350 100644
--- a/sys/rpc/svc_vc.c
+++ b/sys/rpc/svc_vc.c
@@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
+#include <sys/sx.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <netinet/tcp.h>
@@ -62,16 +63,17 @@ __FBSDID("$FreeBSD$");
#include <rpc/rpc_com.h>
-static bool_t svc_vc_rendezvous_recv(SVCXPRT *, struct rpc_msg *);
+static bool_t svc_vc_rendezvous_recv(SVCXPRT *, struct rpc_msg *,
+ struct sockaddr **, struct mbuf **);
static enum xprt_stat svc_vc_rendezvous_stat(SVCXPRT *);
static void svc_vc_rendezvous_destroy(SVCXPRT *);
static bool_t svc_vc_null(void);
static void svc_vc_destroy(SVCXPRT *);
static enum xprt_stat svc_vc_stat(SVCXPRT *);
-static bool_t svc_vc_recv(SVCXPRT *, struct rpc_msg *);
-static bool_t svc_vc_getargs(SVCXPRT *, xdrproc_t, void *);
-static bool_t svc_vc_freeargs(SVCXPRT *, xdrproc_t, void *);
-static bool_t svc_vc_reply(SVCXPRT *, struct rpc_msg *);
+static bool_t svc_vc_recv(SVCXPRT *, struct rpc_msg *,
+ struct sockaddr **, struct mbuf **);
+static bool_t svc_vc_reply(SVCXPRT *, struct rpc_msg *,
+ struct sockaddr *, struct mbuf *);
static bool_t svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in);
static bool_t svc_vc_rendezvous_control (SVCXPRT *xprt, const u_int rq,
void *in);
@@ -83,9 +85,8 @@ static void svc_vc_soupcall(struct socket *so, void *arg, int waitflag);
static struct xp_ops svc_vc_rendezvous_ops = {
.xp_recv = svc_vc_rendezvous_recv,
.xp_stat = svc_vc_rendezvous_stat,
- .xp_getargs = (bool_t (*)(SVCXPRT *, xdrproc_t, void *))svc_vc_null,
- .xp_reply = (bool_t (*)(SVCXPRT *, struct rpc_msg *))svc_vc_null,
- .xp_freeargs = (bool_t (*)(SVCXPRT *, xdrproc_t, void *))svc_vc_null,
+ .xp_reply = (bool_t (*)(SVCXPRT *, struct rpc_msg *,
+ struct sockaddr *, struct mbuf *))svc_vc_null,
.xp_destroy = svc_vc_rendezvous_destroy,
.xp_control = svc_vc_rendezvous_control
};
@@ -93,9 +94,7 @@ static struct xp_ops svc_vc_rendezvous_ops = {
static struct xp_ops svc_vc_ops = {
.xp_recv = svc_vc_recv,
.xp_stat = svc_vc_stat,
- .xp_getargs = svc_vc_getargs,
.xp_reply = svc_vc_reply,
- .xp_freeargs = svc_vc_freeargs,
.xp_destroy = svc_vc_destroy,
.xp_control = svc_vc_control
};
@@ -141,28 +140,21 @@ svc_vc_create(SVCPOOL *pool, struct socket *so, size_t sendsize,
return (xprt);
}
- xprt = mem_alloc(sizeof(SVCXPRT));
- mtx_init(&xprt->xp_lock, "xprt->xp_lock", NULL, MTX_DEF);
+ xprt = svc_xprt_alloc();
+ sx_init(&xprt->xp_lock, "xprt->xp_lock");
xprt->xp_pool = pool;
xprt->xp_socket = so;
xprt->xp_p1 = NULL;
xprt->xp_p2 = NULL;
- xprt->xp_p3 = NULL;
- xprt->xp_verf = _null_auth;
xprt->xp_ops = &svc_vc_rendezvous_ops;
error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
if (error)
goto cleanup_svc_vc_create;
- xprt->xp_ltaddr.buf = mem_alloc(sizeof (struct sockaddr_storage));
- xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_storage);
- xprt->xp_ltaddr.len = sa->sa_len;
- memcpy(xprt->xp_ltaddr.buf, sa, sa->sa_len);
+ memcpy(&xprt->xp_ltaddr, sa, sa->sa_len);
free(sa, M_SONAME);
- xprt->xp_rtaddr.maxlen = 0;
-
xprt_register(xprt);
solisten(so, SOMAXCONN, curthread);
@@ -176,7 +168,7 @@ svc_vc_create(SVCPOOL *pool, struct socket *so, size_t sendsize,
return (xprt);
cleanup_svc_vc_create:
if (xprt)
- mem_free(xprt, sizeof(*xprt));
+ svc_xprt_free(xprt);
return (NULL);
}
@@ -218,29 +210,27 @@ svc_vc_create_conn(SVCPOOL *pool, struct socket *so, struct sockaddr *raddr)
cd = mem_alloc(sizeof(*cd));
cd->strm_stat = XPRT_IDLE;
- xprt = mem_alloc(sizeof(SVCXPRT));
- mtx_init(&xprt->xp_lock, "xprt->xp_lock", NULL, MTX_DEF);
+ xprt = svc_xprt_alloc();
+ sx_init(&xprt->xp_lock, "xprt->xp_lock");
xprt->xp_pool = pool;
xprt->xp_socket = so;
xprt->xp_p1 = cd;
xprt->xp_p2 = NULL;
- xprt->xp_p3 = NULL;
- xprt->xp_verf = _null_auth;
xprt->xp_ops = &svc_vc_ops;
- xprt->xp_rtaddr.buf = mem_alloc(sizeof (struct sockaddr_storage));
- xprt->xp_rtaddr.maxlen = sizeof (struct sockaddr_storage);
- xprt->xp_rtaddr.len = raddr->sa_len;
- memcpy(xprt->xp_rtaddr.buf, raddr, raddr->sa_len);
+ /*
+ * See http://www.connectathon.org/talks96/nfstcp.pdf - client
+ * has a 5 minute timer, server has a 6 minute timer.
+ */
+ xprt->xp_idletimeout = 6 * 60;
+
+ memcpy(&xprt->xp_rtaddr, raddr, raddr->sa_len);
error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
if (error)
goto cleanup_svc_vc_create;
- xprt->xp_ltaddr.buf = mem_alloc(sizeof (struct sockaddr_storage));
- xprt->xp_ltaddr.maxlen = sizeof (struct sockaddr_storage);
- xprt->xp_ltaddr.len = sa->sa_len;
- memcpy(xprt->xp_ltaddr.buf, sa, sa->sa_len);
+ memcpy(&xprt->xp_ltaddr, sa, sa->sa_len);
free(sa, M_SONAME);
xprt_register(xprt);
@@ -255,19 +245,13 @@ svc_vc_create_conn(SVCPOOL *pool, struct socket *so, struct sockaddr *raddr)
* Throw the transport into the active list in case it already
* has some data buffered.
*/
- mtx_lock(&xprt->xp_lock);
+ sx_xlock(&xprt->xp_lock);
xprt_active(xprt);
- mtx_unlock(&xprt->xp_lock);
+ sx_xunlock(&xprt->xp_lock);
return (xprt);
cleanup_svc_vc_create:
if (xprt) {
- if (xprt->xp_ltaddr.buf)
- mem_free(xprt->xp_ltaddr.buf,
- sizeof(struct sockaddr_storage));
- if (xprt->xp_rtaddr.buf)
- mem_free(xprt->xp_rtaddr.buf,
- sizeof(struct sockaddr_storage));
mem_free(xprt, sizeof(*xprt));
}
if (cd)
@@ -335,7 +319,8 @@ done:
/*ARGSUSED*/
static bool_t
-svc_vc_rendezvous_recv(SVCXPRT *xprt, struct rpc_msg *msg)
+svc_vc_rendezvous_recv(SVCXPRT *xprt, struct rpc_msg *msg,
+ struct sockaddr **addrp, struct mbuf **mp)
{
struct socket *so = NULL;
struct sockaddr *sa = NULL;
@@ -347,22 +332,27 @@ svc_vc_rendezvous_recv(SVCXPRT *xprt, struct rpc_msg *msg)
* connection from the socket and turn it into a new
* transport. If the accept fails, we have drained all pending
* connections so we call xprt_inactive().
- *
- * The lock protects us in the case where a new connection arrives
- * on the socket after our call to accept fails with
- * EWOULDBLOCK - the call to xprt_active() in the upcall will
- * happen only after our call to xprt_inactive() which ensures
- * that we will remain active. It might be possible to use
- * SOCKBUF_LOCK for this - its not clear to me what locks are
- * held during the upcall.
*/
- mtx_lock(&xprt->xp_lock);
+ sx_xlock(&xprt->xp_lock);
error = svc_vc_accept(xprt->xp_socket, &so);
if (error == EWOULDBLOCK) {
- xprt_inactive(xprt);
- mtx_unlock(&xprt->xp_lock);
+ /*
+ * We must re-test for new connections after taking
+ * the lock to protect us in the case where a new
+ * connection arrives after our call to accept fails
+ * with EWOULDBLOCK. The pool lock protects us from
+ * racing the upcall after our TAILQ_EMPTY() call
+ * returns false.
+ */
+ ACCEPT_LOCK();
+ mtx_lock(&xprt->xp_pool->sp_lock);
+ if (TAILQ_EMPTY(&xprt->xp_socket->so_comp))
+ xprt_inactive_locked(xprt);
+ mtx_unlock(&xprt->xp_pool->sp_lock);
+ ACCEPT_UNLOCK();
+ sx_xunlock(&xprt->xp_lock);
return (FALSE);
}
@@ -373,11 +363,11 @@ svc_vc_rendezvous_recv(SVCXPRT *xprt, struct rpc_msg *msg)
xprt->xp_socket->so_rcv.sb_flags &= ~SB_UPCALL;
SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
xprt_inactive(xprt);
- mtx_unlock(&xprt->xp_lock);
+ sx_xunlock(&xprt->xp_lock);
return (FALSE);
}
- mtx_unlock(&xprt->xp_lock);
+ sx_xunlock(&xprt->xp_lock);
sa = 0;
error = soaccept(so, &sa);
@@ -420,18 +410,13 @@ svc_vc_destroy_common(SVCXPRT *xprt)
xprt->xp_socket->so_rcv.sb_flags &= ~SB_UPCALL;
SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
- xprt_unregister(xprt);
-
- mtx_destroy(&xprt->xp_lock);
+ sx_destroy(&xprt->xp_lock);
if (xprt->xp_socket)
(void)soclose(xprt->xp_socket);
- if (xprt->xp_rtaddr.buf)
- (void) mem_free(xprt->xp_rtaddr.buf, xprt->xp_rtaddr.maxlen);
- if (xprt->xp_ltaddr.buf)
- (void) mem_free(xprt->xp_ltaddr.buf, xprt->xp_ltaddr.maxlen);
- (void) mem_free(xprt, sizeof (SVCXPRT));
-
+ if (xprt->xp_netid)
+ (void) mem_free(xprt->xp_netid, strlen(xprt->xp_netid) + 1);
+ svc_xprt_free(xprt);
}
static void
@@ -483,32 +468,48 @@ svc_vc_stat(SVCXPRT *xprt)
/*
* Return XPRT_MOREREQS if we have buffered data and we are
- * mid-record or if we have enough data for a record marker.
+ * mid-record or if we have enough data for a record
+ * marker. Since this is only a hint, we read mpending and
+ * resid outside the lock. We do need to take the lock if we
+ * have to traverse the mbuf chain.
*/
if (cd->mpending) {
if (cd->resid)
return (XPRT_MOREREQS);
n = 0;
+ sx_xlock(&xprt->xp_lock);
m = cd->mpending;
while (m && n < sizeof(uint32_t)) {
n += m->m_len;
m = m->m_next;
}
+ sx_xunlock(&xprt->xp_lock);
if (n >= sizeof(uint32_t))
return (XPRT_MOREREQS);
}
+ if (soreadable(xprt->xp_socket))
+ return (XPRT_MOREREQS);
+
return (XPRT_IDLE);
}
static bool_t
-svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
+svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg,
+ struct sockaddr **addrp, struct mbuf **mp)
{
struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1;
struct uio uio;
struct mbuf *m;
+ XDR xdrs;
int error, rcvflag;
+ /*
+ * Serialise access to the socket and our own record parsing
+ * state.
+ */
+ sx_xlock(&xprt->xp_lock);
+
for (;;) {
/*
* If we have an mbuf chain in cd->mpending, try to parse a
@@ -539,7 +540,9 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
}
if (n < sizeof(uint32_t))
goto readmore;
- cd->mpending = m_pullup(cd->mpending, sizeof(uint32_t));
+ if (cd->mpending->m_len < sizeof(uint32_t))
+ cd->mpending = m_pullup(cd->mpending,
+ sizeof(uint32_t));
memcpy(&header, mtod(cd->mpending, uint32_t *),
sizeof(header));
header = ntohl(header);
@@ -557,8 +560,12 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
*/
while (cd->mpending && cd->resid) {
m = cd->mpending;
- cd->mpending = m_split(cd->mpending, cd->resid,
- M_WAIT);
+ if (cd->mpending->m_next
+ || cd->mpending->m_len > cd->resid)
+ cd->mpending = m_split(cd->mpending,
+ cd->resid, M_WAIT);
+ else
+ cd->mpending = NULL;
if (cd->mreq)
m_last(cd->mreq)->m_next = m;
else
@@ -582,13 +589,18 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
* Success - we have a complete record in
* cd->mreq.
*/
- xdrmbuf_create(&xprt->xp_xdrreq, cd->mreq, XDR_DECODE);
+ xdrmbuf_create(&xdrs, cd->mreq, XDR_DECODE);
cd->mreq = NULL;
- if (! xdr_callmsg(&xprt->xp_xdrreq, msg)) {
- XDR_DESTROY(&xprt->xp_xdrreq);
+ sx_xunlock(&xprt->xp_lock);
+
+ if (! xdr_callmsg(&xdrs, msg)) {
+ XDR_DESTROY(&xdrs);
return (FALSE);
}
- xprt->xp_xid = msg->rm_xid;
+
+ *addrp = NULL;
+ *mp = xdrmbuf_getall(&xdrs);
+ XDR_DESTROY(&xdrs);
return (TRUE);
}
@@ -602,17 +614,7 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
* the result in cd->mpending. If the read fails,
* we have drained both cd->mpending and the socket so
* we can call xprt_inactive().
- *
- * The lock protects us in the case where a new packet arrives
- * on the socket after our call to soreceive fails with
- * EWOULDBLOCK - the call to xprt_active() in the upcall will
- * happen only after our call to xprt_inactive() which ensures
- * that we will remain active. It might be possible to use
- * SOCKBUF_LOCK for this - its not clear to me what locks are
- * held during the upcall.
*/
- mtx_lock(&xprt->xp_lock);
-
uio.uio_resid = 1000000000;
uio.uio_td = curthread;
m = NULL;
@@ -621,8 +623,20 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
&rcvflag);
if (error == EWOULDBLOCK) {
- xprt_inactive(xprt);
- mtx_unlock(&xprt->xp_lock);
+ /*
+ * We must re-test for readability after
+ * taking the lock to protect us in the case
+ * where a new packet arrives on the socket
+ * after our call to soreceive fails with
+ * EWOULDBLOCK. The pool lock protects us from
+ * racing the upcall after our soreadable()
+ * call returns false.
+ */
+ mtx_lock(&xprt->xp_pool->sp_lock);
+ if (!soreadable(xprt->xp_socket))
+ xprt_inactive_locked(xprt);
+ mtx_unlock(&xprt->xp_pool->sp_lock);
+ sx_xunlock(&xprt->xp_lock);
return (FALSE);
}
@@ -634,7 +648,7 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
xprt_inactive(xprt);
cd->strm_stat = XPRT_DIED;
- mtx_unlock(&xprt->xp_lock);
+ sx_xunlock(&xprt->xp_lock);
return (FALSE);
}
@@ -642,8 +656,9 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
/*
* EOF - the other end has closed the socket.
*/
+ xprt_inactive(xprt);
cd->strm_stat = XPRT_DIED;
- mtx_unlock(&xprt->xp_lock);
+ sx_xunlock(&xprt->xp_lock);
return (FALSE);
}
@@ -651,53 +666,38 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg)
m_last(cd->mpending)->m_next = m;
else
cd->mpending = m;
-
- mtx_unlock(&xprt->xp_lock);
}
}
static bool_t
-svc_vc_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
-{
-
- return (xdr_args(&xprt->xp_xdrreq, args_ptr));
-}
-
-static bool_t
-svc_vc_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
+svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg,
+ struct sockaddr *addr, struct mbuf *m)
{
XDR xdrs;
-
- /*
- * Free the request mbuf here - this allows us to handle
- * protocols where not all requests have replies
- * (i.e. NLM). Note that xdrmbuf_destroy handles being called
- * twice correctly - the mbuf will only be freed once.
- */
- XDR_DESTROY(&xprt->xp_xdrreq);
-
- xdrs.x_op = XDR_FREE;
- return (xdr_args(&xdrs, args_ptr));
-}
-
-static bool_t
-svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg)
-{
struct mbuf *mrep;
- bool_t stat = FALSE;
+ bool_t stat = TRUE;
int error;
/*
* Leave space for record mark.
*/
MGETHDR(mrep, M_WAIT, MT_DATA);
- MCLGET(mrep, M_WAIT);
mrep->m_len = 0;
mrep->m_data += sizeof(uint32_t);
- xdrmbuf_create(&xprt->xp_xdrrep, mrep, XDR_ENCODE);
- msg->rm_xid = xprt->xp_xid;
- if (xdr_replymsg(&xprt->xp_xdrrep, msg)) {
+ xdrmbuf_create(&xdrs, mrep, XDR_ENCODE);
+
+ if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
+ msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
+ if (!xdr_replymsg(&xdrs, msg))
+ stat = FALSE;
+ else
+ xdrmbuf_append(&xdrs, m);
+ } else {
+ stat = xdr_replymsg(&xdrs, msg);
+ }
+
+ if (stat) {
m_fixhdr(mrep);
/*
@@ -716,12 +716,7 @@ svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg)
m_freem(mrep);
}
- /*
- * This frees the request mbuf chain as well. The reply mbuf
- * chain was consumed by sosend.
- */
- XDR_DESTROY(&xprt->xp_xdrreq);
- XDR_DESTROY(&xprt->xp_xdrrep);
+ XDR_DESTROY(&xdrs);
xprt->xp_p2 = NULL;
return (stat);
@@ -739,9 +734,7 @@ svc_vc_soupcall(struct socket *so, void *arg, int waitflag)
{
SVCXPRT *xprt = (SVCXPRT *) arg;
- mtx_lock(&xprt->xp_lock);
xprt_active(xprt);
- mtx_unlock(&xprt->xp_lock);
}
#if 0
@@ -757,7 +750,7 @@ __rpc_get_local_uid(SVCXPRT *transp, uid_t *uid) {
struct sockaddr *sa;
sock = transp->xp_fd;
- sa = (struct sockaddr *)transp->xp_rtaddr.buf;
+ sa = (struct sockaddr *)transp->xp_rtaddr;
if (sa->sa_family == AF_LOCAL) {
ret = getpeereid(sock, &euid, &egid);
if (ret == 0)
diff --git a/sys/rpc/xdr.h b/sys/rpc/xdr.h
index bebd448..947bf4f 100644
--- a/sys/rpc/xdr.h
+++ b/sys/rpc/xdr.h
@@ -348,6 +348,8 @@ extern void xdrmem_create(XDR *, char *, u_int, enum xdr_op);
/* XDR using mbufs */
struct mbuf;
extern void xdrmbuf_create(XDR *, struct mbuf *, enum xdr_op);
+extern void xdrmbuf_append(XDR *, struct mbuf *);
+extern struct mbuf * xdrmbuf_getall(XDR *);
/* XDR pseudo records for tcp */
extern void xdrrec_create(XDR *, u_int, u_int, void *,
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index 7438c18..bd56521 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -365,8 +365,23 @@ struct fhandle {
typedef struct fhandle fhandle_t;
/*
+ * Old export arguments without security flavor list
+ */
+struct oexport_args {
+ int ex_flags; /* export related flags */
+ uid_t ex_root; /* mapping for root uid */
+ struct xucred ex_anon; /* mapping for anonymous user */
+ struct sockaddr *ex_addr; /* net address to which exported */
+ u_char ex_addrlen; /* and the net address length */
+ struct sockaddr *ex_mask; /* mask of valid bits in saddr */
+ u_char ex_masklen; /* and the smask length */
+ char *ex_indexfile; /* index file for WebNFS URLs */
+};
+
+/*
* Export arguments for local filesystem mount calls.
*/
+#define MAXSECFLAVORS 5
struct export_args {
int ex_flags; /* export related flags */
uid_t ex_root; /* mapping for root uid */
@@ -376,6 +391,8 @@ struct export_args {
struct sockaddr *ex_mask; /* mask of valid bits in saddr */
u_char ex_masklen; /* and the smask length */
char *ex_indexfile; /* index file for WebNFS URLs */
+ int ex_numsecflavors; /* security flavor count */
+ int ex_secflavors[MAXSECFLAVORS]; /* list of security flavors */
};
/*
@@ -542,7 +559,8 @@ typedef int vfs_vget_t(struct mount *mp, ino_t ino, int flags,
struct vnode **vpp);
typedef int vfs_fhtovp_t(struct mount *mp, struct fid *fhp, struct vnode **vpp);
typedef int vfs_checkexp_t(struct mount *mp, struct sockaddr *nam,
- int *extflagsp, struct ucred **credanonp);
+ int *extflagsp, struct ucred **credanonp,
+ int *numsecflavors, int **secflavors);
typedef int vfs_init_t(struct vfsconf *);
typedef int vfs_uninit_t(struct vfsconf *);
typedef int vfs_extattrctl_t(struct mount *mp, int cmd,
@@ -584,8 +602,8 @@ vfs_statfs_t __vfs_statfs;
(*(MP)->mnt_op->vfs_vget)(MP, INO, FLAGS, VPP)
#define VFS_FHTOVP(MP, FIDP, VPP) \
(*(MP)->mnt_op->vfs_fhtovp)(MP, FIDP, VPP)
-#define VFS_CHECKEXP(MP, NAM, EXFLG, CRED) \
- (*(MP)->mnt_op->vfs_checkexp)(MP, NAM, EXFLG, CRED)
+#define VFS_CHECKEXP(MP, NAM, EXFLG, CRED, NUMSEC, SEC) \
+ (*(MP)->mnt_op->vfs_checkexp)(MP, NAM, EXFLG, CRED, NUMSEC, SEC)
#define VFS_EXTATTRCTL(MP, C, FN, NS, N, P) \
(*(MP)->mnt_op->vfs_extattrctl)(MP, C, FN, NS, N, P)
#define VFS_SYSCTL(MP, OP, REQ) \
diff --git a/sys/xdr/xdr_mbuf.c b/sys/xdr/xdr_mbuf.c
index 770dfc3..e6f7c9d 100644
--- a/sys/xdr/xdr_mbuf.c
+++ b/sys/xdr/xdr_mbuf.c
@@ -78,6 +78,51 @@ xdrmbuf_create(XDR *xdrs, struct mbuf *m, enum xdr_op op)
}
}
+void
+xdrmbuf_append(XDR *xdrs, struct mbuf *madd)
+{
+ struct mbuf *m;
+
+ KASSERT(xdrs->x_ops == &xdrmbuf_ops && xdrs->x_op == XDR_ENCODE,
+ ("xdrmbuf_append: invalid XDR stream"));
+
+ if (m_length(madd, NULL) == 0) {
+ m_freem(madd);
+ return;
+ }
+
+ m = (struct mbuf *) xdrs->x_private;
+ m->m_next = madd;
+
+ m = m_last(madd);
+ xdrs->x_private = m;
+ xdrs->x_handy = m->m_len;
+}
+
+struct mbuf *
+xdrmbuf_getall(XDR *xdrs)
+{
+ struct mbuf *m0, *m;
+
+ KASSERT(xdrs->x_ops == &xdrmbuf_ops && xdrs->x_op == XDR_DECODE,
+ ("xdrmbuf_append: invalid XDR stream"));
+
+ m0 = (struct mbuf *) xdrs->x_base;
+ m = (struct mbuf *) xdrs->x_private;
+ if (m0 != m) {
+ while (m0->m_next != m)
+ m0 = m0->m_next;
+ m0->m_next = NULL;
+ xdrs->x_private = NULL;
+ } else {
+ xdrs->x_base = NULL;
+ xdrs->x_private = NULL;
+ }
+
+ m_adj(m, xdrs->x_handy);
+ return (m);
+}
+
static void
xdrmbuf_destroy(XDR *xdrs)
{
@@ -92,9 +137,16 @@ xdrmbuf_destroy(XDR *xdrs)
static bool_t
xdrmbuf_getlong(XDR *xdrs, long *lp)
{
+ int32_t *p;
int32_t t;
- xdrmbuf_getbytes(xdrs, (char *) &t, sizeof(int32_t));
+ p = xdrmbuf_inline(xdrs, sizeof(int32_t));
+ if (p) {
+ t = *p;
+ } else {
+ xdrmbuf_getbytes(xdrs, (char *) &t, sizeof(int32_t));
+ }
+
*lp = ntohl(t);
return (TRUE);
}
@@ -104,10 +156,16 @@ xdrmbuf_putlong(xdrs, lp)
XDR *xdrs;
const long *lp;
{
+ int32_t *p;
int32_t t = htonl(*lp);
- xdrmbuf_putbytes(xdrs, (char *) &t, sizeof(int32_t));
- return (TRUE);
+ p = xdrmbuf_inline(xdrs, sizeof(int32_t));
+ if (p) {
+ *p = t;
+ return (TRUE);
+ } else {
+ return (xdrmbuf_putbytes(xdrs, (char *) &t, sizeof(int32_t)));
+ }
}
static bool_t
@@ -130,7 +188,7 @@ xdrmbuf_getbytes(XDR *xdrs, char *addr, u_int len)
sz = m->m_len - xdrs->x_handy;
if (sz > len)
sz = len;
- memcpy(addr, mtod(m, const char *) + xdrs->x_handy, sz);
+ bcopy(mtod(m, const char *) + xdrs->x_handy, addr, sz);
addr += sz;
xdrs->x_handy += sz;
@@ -157,7 +215,7 @@ xdrmbuf_putbytes(XDR *xdrs, const char *addr, u_int len)
sz = M_TRAILINGSPACE(m) + (m->m_len - xdrs->x_handy);
if (sz > len)
sz = len;
- memcpy(mtod(m, char *) + xdrs->x_handy, addr, sz);
+ bcopy(addr, mtod(m, char *) + xdrs->x_handy, sz);
addr += sz;
xdrs->x_handy += sz;
if (xdrs->x_handy > m->m_len)
@@ -167,6 +225,8 @@ xdrmbuf_putbytes(XDR *xdrs, const char *addr, u_int len)
if (xdrs->x_handy == m->m_len && M_TRAILINGSPACE(m) == 0) {
if (!m->m_next) {
MGET(n, M_TRYWAIT, m->m_type);
+ if (m->m_flags & M_EXT)
+ MCLGET(n, M_TRYWAIT);
m->m_next = n;
}
m = m->m_next;
diff --git a/tools/regression/kgssapi/Makefile b/tools/regression/kgssapi/Makefile
new file mode 100644
index 0000000..49059fa
--- /dev/null
+++ b/tools/regression/kgssapi/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= gsstest
+NO_MAN=
+WARNS?= 2
+LDADD= -lgssapi -lgssapi_krb5
+DEBUG_FLAGS= -g -O0
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/kgssapi/gsstest.c b/tools/regression/kgssapi/gsstest.c
new file mode 100644
index 0000000..acebf4c
--- /dev/null
+++ b/tools/regression/kgssapi/gsstest.c
@@ -0,0 +1,307 @@
+/*-
+ * 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 <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/module.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <krb5.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+
+struct gsstest_2_args {
+ int step; /* test step number */
+ gss_buffer_desc input_token; /* token from userland */
+ gss_buffer_desc output_token; /* buffer to receive reply token */
+};
+struct gsstest_2_res {
+ OM_uint32 maj_stat; /* maj_stat from kernel */
+ OM_uint32 min_stat; /* min_stat from kernel */
+ gss_buffer_desc output_token; /* reply token (using space from gsstest_2_args.output) */
+};
+
+static void
+report_error(gss_OID mech, OM_uint32 maj, OM_uint32 min)
+{
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 message_context;
+ gss_buffer_desc buf;
+
+ printf("major_stat=%d, minor_stat=%d\n", maj, min);
+ message_context = 0;
+ do {
+ maj_stat = gss_display_status(&min_stat, maj,
+ GSS_C_GSS_CODE, GSS_C_NO_OID, &message_context, &buf);
+ printf("%.*s\n", (int)buf.length, (char *) buf.value);
+ gss_release_buffer(&min_stat, &buf);
+ } while (message_context);
+ if (mech) {
+ message_context = 0;
+ do {
+ maj_stat = gss_display_status(&min_stat, min,
+ GSS_C_MECH_CODE, mech, &message_context, &buf);
+ printf("%.*s\n", (int)buf.length, (char *) buf.value);
+ gss_release_buffer(&min_stat, &buf);
+ } while (message_context);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ struct module_stat stat;
+ int mod;
+ int syscall_num;
+
+ stat.version = sizeof(stat);
+ mod = modfind("gsstest_syscall");
+ if (mod < 0) {
+ fprintf(stderr, "%s: kernel support not present\n", argv[0]);
+ exit(1);
+ }
+ modstat(mod, &stat);
+ syscall_num = stat.data.intval;
+
+ switch (atoi(argv[1])) {
+ case 1:
+ syscall(syscall_num, 1, NULL, NULL);
+ break;
+
+ case 2: {
+ struct gsstest_2_args args;
+ struct gsstest_2_res res;
+ char hostname[512];
+ char token_buffer[8192];
+ OM_uint32 maj_stat, min_stat;
+ gss_ctx_id_t client_context = GSS_C_NO_CONTEXT;
+ gss_cred_id_t client_cred;
+ gss_OID mech_type = GSS_C_NO_OID;
+ gss_buffer_desc name_buf, message_buf;
+ gss_name_t name;
+ int32_t enctypes[] = {
+ ETYPE_DES_CBC_CRC,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_ARCFOUR_HMAC_MD5_56,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_DES3_CBC_SHA1,
+ };
+ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
+ int established;
+ int i;
+
+ for (i = 0; i < num_enctypes; i++) {
+ printf("testing etype %d\n", enctypes[i]);
+ args.output_token.length = sizeof(token_buffer);
+ args.output_token.value = token_buffer;
+
+ gethostname(hostname, sizeof(hostname));
+ snprintf(token_buffer, sizeof(token_buffer),
+ "nfs@%s", hostname);
+ name_buf.length = strlen(token_buffer);
+ name_buf.value = token_buffer;
+ maj_stat = gss_import_name(&min_stat, &name_buf,
+ GSS_C_NT_HOSTBASED_SERVICE, &name);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_import_name failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME,
+ 0, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client_cred,
+ NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_acquire_cred (client) failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_krb5_set_allowable_enctypes(&min_stat,
+ client_cred, 1, &enctypes[i]);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_krb5_set_allowable_enctypes failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ res.output_token.length = 0;
+ res.output_token.value = 0;
+ established = 0;
+ while (!established) {
+ maj_stat = gss_init_sec_context(&min_stat,
+ client_cred,
+ &client_context,
+ name,
+ GSS_C_NO_OID,
+ (GSS_C_MUTUAL_FLAG
+ |GSS_C_CONF_FLAG
+ |GSS_C_INTEG_FLAG
+ |GSS_C_SEQUENCE_FLAG
+ |GSS_C_REPLAY_FLAG),
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &res.output_token,
+ &mech_type,
+ &args.input_token,
+ NULL,
+ NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_init_sec_context failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ if (args.input_token.length) {
+ args.step = 1;
+ syscall(syscall_num, 2, &args, &res);
+ gss_release_buffer(&min_stat,
+ &args.input_token);
+ if (res.maj_stat != GSS_S_COMPLETE
+ && res.maj_stat != GSS_S_CONTINUE_NEEDED) {
+ printf("gss_accept_sec_context (kernel) failed\n");
+ report_error(mech_type, res.maj_stat,
+ res.min_stat);
+ goto out;
+ }
+ }
+ if (maj_stat == GSS_S_COMPLETE)
+ established = 1;
+ }
+
+ message_buf.value = "Hello world";
+ message_buf.length = strlen((char *) message_buf.value);
+
+ maj_stat = gss_get_mic(&min_stat, client_context,
+ GSS_C_QOP_DEFAULT, &message_buf, &args.input_token);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_get_mic failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ args.step = 2;
+ syscall(syscall_num, 2, &args, &res);
+ gss_release_buffer(&min_stat, &args.input_token);
+ if (GSS_ERROR(res.maj_stat)) {
+ printf("kernel gss_verify_mic failed\n");
+ report_error(mech_type, res.maj_stat, res.min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_verify_mic(&min_stat, client_context,
+ &message_buf, &res.output_token, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_verify_mic failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_wrap(&min_stat, client_context,
+ TRUE, GSS_C_QOP_DEFAULT, &message_buf, NULL,
+ &args.input_token);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_wrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ args.step = 3;
+ syscall(syscall_num, 2, &args, &res);
+ gss_release_buffer(&min_stat, &args.input_token);
+ if (GSS_ERROR(res.maj_stat)) {
+ printf("kernel gss_unwrap failed\n");
+ report_error(mech_type, res.maj_stat, res.min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_unwrap(&min_stat, client_context,
+ &res.output_token, &message_buf, NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_unwrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ gss_release_buffer(&min_stat, &message_buf);
+
+ maj_stat = gss_wrap(&min_stat, client_context,
+ FALSE, GSS_C_QOP_DEFAULT, &message_buf, NULL,
+ &args.input_token);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_wrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+
+ args.step = 4;
+ syscall(syscall_num, 2, &args, &res);
+ gss_release_buffer(&min_stat, &args.input_token);
+ if (GSS_ERROR(res.maj_stat)) {
+ printf("kernel gss_unwrap failed\n");
+ report_error(mech_type, res.maj_stat, res.min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_unwrap(&min_stat, client_context,
+ &res.output_token, &message_buf, NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ printf("gss_unwrap failed\n");
+ report_error(mech_type, maj_stat, min_stat);
+ goto out;
+ }
+ gss_release_buffer(&min_stat, &message_buf);
+
+ args.step = 5;
+ syscall(syscall_num, 2, &args, &res);
+
+ gss_release_name(&min_stat, &name);
+ gss_release_cred(&min_stat, &client_cred);
+ gss_delete_sec_context(&min_stat, &client_context,
+ GSS_C_NO_BUFFER);
+ }
+
+ break;
+ }
+ case 3:
+ syscall(syscall_num, 3, NULL, NULL);
+ break;
+ case 4:
+ syscall(syscall_num, 4, NULL, NULL);
+ break;
+ }
+ return (0);
+
+out:
+ return (1);
+}
diff --git a/tools/regression/rpcsec_gss/Makefile b/tools/regression/rpcsec_gss/Makefile
new file mode 100644
index 0000000..29b14d6
--- /dev/null
+++ b/tools/regression/rpcsec_gss/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= rpctest
+NO_MAN=
+WARNS?= 6
+LDADD= -lrpcsec_gss
+DEBUG_FLAGS= -g -O0
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/rpcsec_gss/rpctest.c b/tools/regression/rpcsec_gss/rpctest.c
new file mode 100644
index 0000000..847ae3b
--- /dev/null
+++ b/tools/regression/rpcsec_gss/rpctest.c
@@ -0,0 +1,400 @@
+/*-
+ * 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$
+ */
+
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>
+#else
+#define __unused
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpcsec_gss.h>
+
+static rpc_gss_principal_t server_acl = NULL;
+
+static void
+usage(void)
+{
+ printf("rpctest client | server\n");
+ exit(1);
+}
+
+static void
+print_principal(rpc_gss_principal_t principal)
+{
+ int i, len, n;
+ uint8_t *p;
+
+ len = principal->len;
+ p = (uint8_t *) principal->name;
+ while (len > 0) {
+ n = len;
+ if (n > 16)
+ n = 16;
+ for (i = 0; i < n; i++)
+ printf("%02x ", p[i]);
+ for (; i < 16; i++)
+ printf(" ");
+ printf("|");
+ for (i = 0; i < n; i++)
+ printf("%c", isprint(p[i]) ? p[i] : '.');
+ printf("|\n");
+ len -= n;
+ p += n;
+ }
+}
+
+static void
+test_client(int argc, const char **argv)
+{
+ rpcproc_t prog = 123456;
+ rpcvers_t vers = 1;
+ const char *netid = "tcp";
+ char hostname[128], service[128+5];
+ CLIENT *client;
+ AUTH *auth;
+ const char **mechs;
+ rpc_gss_options_req_t options_req;
+ rpc_gss_options_ret_t options_ret;
+ rpc_gss_service_t svc;
+ struct timeval tv;
+ enum clnt_stat stat;
+
+ if (argc == 2)
+ strlcpy(hostname, argv[1], sizeof(hostname));
+ else
+ gethostname(hostname, sizeof(hostname));
+
+ client = clnt_create(hostname, prog, vers, netid);
+ if (!client) {
+ printf("rpc_createerr.cf_stat = %d\n",
+ rpc_createerr.cf_stat);
+ printf("rpc_createerr.cf_error.re_errno = %d\n",
+ rpc_createerr.cf_error.re_errno);
+ return;
+ }
+
+ strcpy(service, "host");
+ strcat(service, "@");
+ strcat(service, hostname);
+
+ mechs = rpc_gss_get_mechanisms();
+ auth = NULL;
+ while (*mechs) {
+ options_req.req_flags = GSS_C_MUTUAL_FLAG;
+ options_req.time_req = 600;
+ options_req.my_cred = GSS_C_NO_CREDENTIAL;
+ options_req.input_channel_bindings = NULL;
+ auth = rpc_gss_seccreate(client, service,
+ *mechs,
+ rpc_gss_svc_none,
+ NULL,
+ &options_req,
+ &options_ret);
+ if (auth)
+ break;
+ mechs++;
+ }
+ if (!auth) {
+ clnt_pcreateerror("rpc_gss_seccreate");
+ printf("Can't authenticate with server %s.\n",
+ hostname);
+ exit(1);
+ }
+ client->cl_auth = auth;
+
+ for (svc = rpc_gss_svc_none; svc <= rpc_gss_svc_privacy; svc++) {
+ const char *svc_names[] = {
+ "rpc_gss_svc_default",
+ "rpc_gss_svc_none",
+ "rpc_gss_svc_integrity",
+ "rpc_gss_svc_privacy"
+ };
+ int num;
+
+ rpc_gss_set_defaults(auth, svc, NULL);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ num = 42;
+ stat = CLNT_CALL(client, 1,
+ (xdrproc_t) xdr_int, (char *) &num,
+ (xdrproc_t) xdr_int, (char *) &num, tv);
+ if (stat == RPC_SUCCESS) {
+ printf("succeeded with %s\n", svc_names[svc]);
+ if (num != 142)
+ printf("unexpected reply %d\n", num);
+ } else {
+ clnt_perror(client, "call failed");
+ }
+ }
+ AUTH_DESTROY(auth);
+ CLNT_DESTROY(client);
+}
+
+static void
+server_program_1(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+ rpc_gss_rawcred_t *rcred;
+ rpc_gss_ucred_t *ucred;
+ int i, num;
+
+ if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) {
+ svcerr_weakauth(transp);
+ return;
+ }
+
+ if (!rpc_gss_getcred(rqstp, &rcred, &ucred, NULL)) {
+ svcerr_systemerr(transp);
+ return;
+ }
+
+ printf("svc=%d, mech=%s, uid=%d, gid=%d, gids={",
+ rcred->service, rcred->mechanism, ucred->uid, ucred->gid);
+ for (i = 0; i < ucred->gidlen; i++) {
+ if (i > 0) printf(",");
+ printf("%d", ucred->gidlist[i]);
+ }
+ printf("}\n");
+
+ switch (rqstp->rq_proc) {
+ case 0:
+ if (!svc_getargs(transp, (xdrproc_t) xdr_void, 0)) {
+ svcerr_decode(transp);
+ goto out;
+ }
+ if (!svc_sendreply(transp, (xdrproc_t) xdr_void, 0)) {
+ svcerr_systemerr(transp);
+ }
+ goto out;
+
+ case 1:
+ if (!svc_getargs(transp, (xdrproc_t) xdr_int,
+ (char *) &num)) {
+ svcerr_decode(transp);
+ goto out;
+ }
+ num += 100;
+ if (!svc_sendreply(transp, (xdrproc_t) xdr_int,
+ (char *) &num)) {
+ svcerr_systemerr(transp);
+ }
+ goto out;
+
+ default:
+ svcerr_noproc(transp);
+ goto out;
+ }
+
+out:
+ return;
+}
+
+#if 0
+static void
+report_error(gss_OID mech, OM_uint32 maj, OM_uint32 min)
+{
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 message_context;
+ gss_buffer_desc buf;
+
+ printf("major_stat=%d, minor_stat=%d\n", maj, min);
+
+ message_context = 0;
+ do {
+ maj_stat = gss_display_status(&min_stat, maj,
+ GSS_C_GSS_CODE, GSS_C_NO_OID, &message_context, &buf);
+ printf("%.*s\n", (int)buf.length, (char *) buf.value);
+ gss_release_buffer(&min_stat, &buf);
+ } while (message_context);
+ if (mech) {
+ message_context = 0;
+ do {
+ maj_stat = gss_display_status(&min_stat, min,
+ GSS_C_MECH_CODE, mech, &message_context, &buf);
+ printf("%.*s\n", (int)buf.length, (char *) buf.value);
+ gss_release_buffer(&min_stat, &buf);
+ } while (message_context);
+ }
+ exit(1);
+}
+#endif
+
+static bool_t
+server_new_context(__unused struct svc_req *req,
+ __unused gss_cred_id_t deleg,
+ __unused gss_ctx_id_t gss_context,
+ rpc_gss_lock_t *lock,
+ __unused void **cookie)
+{
+ rpc_gss_rawcred_t *rcred = lock->raw_cred;
+
+ printf("new security context version=%d, mech=%s, qop=%s:\n",
+ rcred->version, rcred->mechanism, rcred->qop);
+ print_principal(rcred->client_principal);
+
+ if (!server_acl)
+ return (TRUE);
+
+ if (rcred->client_principal->len != server_acl->len
+ || memcmp(rcred->client_principal->name, server_acl->name,
+ server_acl->len)) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+static void
+test_server(__unused int argc, __unused const char **argv)
+{
+ char hostname[128];
+ char principal[128 + 5];
+ const char **mechs;
+ static rpc_gss_callback_t cb;
+
+ if (argc == 3) {
+ if (!rpc_gss_get_principal_name(&server_acl, argv[1],
+ argv[2], NULL, NULL)) {
+ printf("Can't create %s ACL entry for %s\n",
+ argv[1], argv[2]);
+ return;
+ }
+ }
+
+ gethostname(hostname, sizeof(hostname));;
+ snprintf(principal, sizeof(principal), "host@%s", hostname);
+
+ mechs = rpc_gss_get_mechanisms();
+ while (*mechs) {
+ if (!rpc_gss_set_svc_name(principal, *mechs, GSS_C_INDEFINITE,
+ 123456, 1)) {
+ rpc_gss_error_t e;
+
+ rpc_gss_get_error(&e);
+ printf("setting name for %s for %s failed: %d, %d\n",
+ principal, *mechs,
+ e.rpc_gss_error, e.system_error);
+
+#if 0
+ gss_OID mech_oid;
+ gss_OID_set_desc oid_set;
+ gss_name_t name;
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc namebuf;
+ gss_cred_id_t cred;
+
+ rpc_gss_mech_to_oid(*mechs, &mech_oid);
+ oid_set.count = 1;
+ oid_set.elements = mech_oid;
+
+ namebuf.value = principal;
+ namebuf.length = strlen(principal);
+ maj_stat = gss_import_name(&min_stat, &namebuf,
+ GSS_C_NT_HOSTBASED_SERVICE, &name);
+ if (maj_stat) {
+ printf("gss_import_name failed\n");
+ report_error(mech_oid, maj_stat, min_stat);
+ }
+ maj_stat = gss_acquire_cred(&min_stat, name,
+ 0, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
+ if (maj_stat) {
+ printf("gss_acquire_cred failed\n");
+ report_error(mech_oid, maj_stat, min_stat);
+ }
+#endif
+ }
+ mechs++;
+ }
+
+ cb.program = 123456;
+ cb.version = 1;
+ cb.callback = server_new_context;
+ rpc_gss_set_callback(&cb);
+
+ svc_create(server_program_1, 123456, 1, 0);
+ svc_run();
+}
+
+static void
+test_get_principal_name(int argc, const char **argv)
+{
+ const char *mechname, *name, *node, *domain;
+ rpc_gss_principal_t principal;
+
+ if (argc < 3 || argc > 5) {
+ printf("usage: rpctest principal <mechname> <name> "
+ "[<node> [<domain>] ]\n");
+ exit(1);
+ }
+
+ mechname = argv[1];
+ name = argv[2];
+ node = NULL;
+ domain = NULL;
+ if (argc > 3) {
+ node = argv[3];
+ if (argc > 4)
+ domain = argv[4];
+ }
+
+ if (rpc_gss_get_principal_name(&principal, mechname, name,
+ node, domain)) {
+ printf("succeeded:\n");
+ print_principal(principal);
+ free(principal);
+ } else {
+ printf("failed\n");
+ }
+}
+
+int
+main(int argc, const char **argv)
+{
+
+ if (argc < 2)
+ usage();
+ if (!strcmp(argv[1], "client"))
+ test_client(argc - 1, argv + 1);
+ else if (!strcmp(argv[1], "server"))
+ test_server(argc - 1, argv + 1);
+ else if (!strcmp(argv[1], "principal"))
+ test_get_principal_name(argc - 1, argv + 1);
+ else
+ usage();
+
+ return (0);
+}
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 7b10edb..993eb8a 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -63,6 +63,7 @@ SUBDIR= ${_ac} \
getfmac \
getpmac \
gstat \
+ ${_gssd} \
ifmcstat \
inetd \
iostat \
@@ -256,6 +257,10 @@ _fdwrite= fdwrite
_freebsd-update= freebsd-update
.endif
+.if ${MK_GSSAPI} != no
+_gssd= gssd
+.endif
+
.if ${MK_INET6} != "no"
_faithd= faithd
_ip6addrctl= ip6addrctl
diff --git a/usr.sbin/gssd/Makefile b/usr.sbin/gssd/Makefile
new file mode 100644
index 0000000..ffdb5aa
--- /dev/null
+++ b/usr.sbin/gssd/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+PROG= gssd
+MAN= gssd.8
+SRCS= gssd.c gssd_svc.c gssd_xdr.c gssd_prot.c
+
+CFLAGS+= -I.
+WARNS?= 1
+
+DPADD= ${LIBGSSAPI}
+LDADD= -lgssapi
+
+CLEANFILES= gssd_svc.c gssd.h
+
+RPCSRC= ${.CURDIR}/../../sys/kgssapi/gssd.x
+RPCGEN= rpcgen -L -C -M
+
+gssd_svc.c: ${RPCSRC} gssd.h
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+gssd_xdr.c: ${RPCSRC} gssd.h
+ ${RPCGEN} -c -o ${.TARGET} ${RPCSRC}
+
+gssd.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+.PATH: ${.CURDIR}/../../sys/kgssapi
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/gssd/gssd.8 b/usr.sbin/gssd/gssd.8
new file mode 100644
index 0000000..20a9a96
--- /dev/null
+++ b/usr.sbin/gssd/gssd.8
@@ -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$
+.\"
+.\" Note: The date here should be updated whenever a non-trivial
+.\" change is made to the manual page.
+.Dd August 25, 2008
+.Dt GSSD 8
+.Os
+.Sh NAME
+.Nm gssd
+.Nd "Generic Security Services Daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Sh DESCRIPTION
+The
+.Nm
+program provides support for the kernel GSS-API implementation.
+.Pp
+The options are as follows:
+.Bl -tag
+.It Fl d
+Run in debug mode.
+In this mode,
+.Nm
+will not for when it starts.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/krb5.keytab" -compact
+.It Pa /etc/krb5.keytab
+Contains Kerberos service principals which may be used as credentials
+by kernel GSS-API services.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr gssapi 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/usr.sbin/gssd/gssd.c b/usr.sbin/gssd/gssd.c
new file mode 100644
index 0000000..ba2805b
--- /dev/null
+++ b/usr.sbin/gssd/gssd.c
@@ -0,0 +1,610 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+#include <ctype.h>
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <gssapi/gssapi.h>
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+
+#include "gssd.h"
+
+#ifndef _PATH_GSS_MECH
+#define _PATH_GSS_MECH "/etc/gss/mech"
+#endif
+#ifndef _PATH_GSSDSOCK
+#define _PATH_GSSDSOCK "/var/run/gssd.sock"
+#endif
+
+struct gss_resource {
+ LIST_ENTRY(gss_resource) gr_link;
+ uint64_t gr_id; /* indentifier exported to kernel */
+ void* gr_res; /* GSS-API resource pointer */
+};
+LIST_HEAD(gss_resource_list, gss_resource) gss_resources;
+int gss_resource_count;
+uint32_t gss_next_id;
+uint32_t gss_start_time;
+int debug_level;
+
+static void gssd_load_mech(void);
+
+extern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp);
+extern int gssd_syscall(char *path);
+
+int
+main(int argc, char **argv)
+{
+ /*
+ * We provide an RPC service on a local-domain socket. The
+ * kernel's GSS-API code will pass what it can't handle
+ * directly to us.
+ */
+ struct sockaddr_un sun;
+ int fd, oldmask, ch, debug;
+ SVCXPRT *xprt;
+
+ debug = 0;
+ while ((ch = getopt(argc, argv, "d")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug_level++;
+ break;
+ default:
+ fprintf(stderr, "usage: %s [-d]\n", argv[0]);
+ exit(1);
+ break;
+ }
+ }
+
+ gssd_load_mech();
+
+ if (!debug_level)
+ daemon(0, 0);
+
+ memset(&sun, 0, sizeof sun);
+ sun.sun_family = AF_LOCAL;
+ unlink(_PATH_GSSDSOCK);
+ strcpy(sun.sun_path, _PATH_GSSDSOCK);
+ sun.sun_len = SUN_LEN(&sun);
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (!fd) {
+ err(1, "Can't create local gssd socket");
+ }
+ oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+ if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) {
+ err(1, "Can't bind local gssd socket");
+ }
+ umask(oldmask);
+ if (listen(fd, SOMAXCONN) < 0) {
+ err(1, "Can't listen on local gssd socket");
+ }
+ xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+ if (!xprt) {
+ err(1, "Can't create transport for local gssd socket");
+ }
+ if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) {
+ err(1, "Can't register service for local gssd socket");
+ }
+
+ LIST_INIT(&gss_resources);
+ gss_next_id = 1;
+ gss_start_time = time(0);
+
+ gssd_syscall(_PATH_GSSDSOCK);
+ svc_run();
+
+ return (0);
+}
+
+static void
+gssd_load_mech(void)
+{
+ FILE *fp;
+ char buf[256];
+ char *p;
+ char *name, *oid, *lib, *kobj;
+
+ fp = fopen(_PATH_GSS_MECH, "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++;
+ 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;
+
+ if (strcmp(kobj, "-")) {
+ /*
+ * Attempt to load the kernel module if its
+ * not already present.
+ */
+ if (modfind(kobj) < 0) {
+ if (kldload(kobj) < 0) {
+ fprintf(stderr,
+ "%s: can't find or load kernel module %s for %s\n",
+ getprogname(), kobj, name);
+ }
+ }
+ }
+ }
+ fclose(fp);
+}
+
+static void *
+gssd_find_resource(uint64_t id)
+{
+ struct gss_resource *gr;
+
+ if (!id)
+ return (NULL);
+
+ LIST_FOREACH(gr, &gss_resources, gr_link)
+ if (gr->gr_id == id)
+ return (gr->gr_res);
+
+ return (NULL);
+}
+
+static uint64_t
+gssd_make_resource(void *res)
+{
+ struct gss_resource *gr;
+
+ if (!res)
+ return (0);
+
+ gr = malloc(sizeof(struct gss_resource));
+ if (!gr)
+ return (0);
+ gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32);
+ gr->gr_res = res;
+ LIST_INSERT_HEAD(&gss_resources, gr, gr_link);
+ gss_resource_count++;
+ if (debug_level > 1)
+ printf("%d resources allocated\n", gss_resource_count);
+
+ return (gr->gr_id);
+}
+
+static void
+gssd_delete_resource(uint64_t id)
+{
+ struct gss_resource *gr;
+
+ LIST_FOREACH(gr, &gss_resources, gr_link) {
+ if (gr->gr_id == id) {
+ LIST_REMOVE(gr, gr_link);
+ free(gr);
+ gss_resource_count--;
+ if (debug_level > 1)
+ printf("%d resources allocated\n",
+ gss_resource_count);
+ return;
+ }
+ }
+}
+
+bool_t
+gssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp)
+{
+
+ return (TRUE);
+}
+
+bool_t
+gssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp)
+{
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_name_t name = GSS_C_NO_NAME;
+ char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1];
+
+ snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
+ (int) argp->uid);
+ setenv("KRB5CCNAME", ccname, TRUE);
+
+ memset(result, 0, sizeof(*result));
+ if (argp->cred) {
+ cred = gssd_find_resource(argp->cred);
+ if (!cred) {
+ result->major_status = GSS_S_CREDENTIALS_EXPIRED;
+ return (TRUE);
+ }
+ }
+ if (argp->ctx) {
+ ctx = gssd_find_resource(argp->ctx);
+ if (!ctx) {
+ result->major_status = GSS_S_CONTEXT_EXPIRED;
+ return (TRUE);
+ }
+ }
+ if (argp->name) {
+ name = gssd_find_resource(argp->name);
+ if (!name) {
+ result->major_status = GSS_S_BAD_NAME;
+ return (TRUE);
+ }
+ }
+
+ memset(result, 0, sizeof(*result));
+ result->major_status = gss_init_sec_context(&result->minor_status,
+ cred, &ctx, name, argp->mech_type,
+ argp->req_flags, argp->time_req, argp->input_chan_bindings,
+ &argp->input_token, &result->actual_mech_type,
+ &result->output_token, &result->ret_flags, &result->time_rec);
+
+ if (result->major_status == GSS_S_COMPLETE
+ || result->major_status == GSS_S_CONTINUE_NEEDED) {
+ if (argp->ctx)
+ result->ctx = argp->ctx;
+ else
+ result->ctx = gssd_make_resource(ctx);
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp)
+{
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_name_t src_name;
+ gss_cred_id_t delegated_cred_handle;
+
+ memset(result, 0, sizeof(*result));
+ if (argp->ctx) {
+ ctx = gssd_find_resource(argp->ctx);
+ if (!ctx) {
+ result->major_status = GSS_S_CONTEXT_EXPIRED;
+ return (TRUE);
+ }
+ }
+ if (argp->cred) {
+ cred = gssd_find_resource(argp->cred);
+ if (!cred) {
+ result->major_status = GSS_S_CREDENTIALS_EXPIRED;
+ return (TRUE);
+ }
+ }
+
+ memset(result, 0, sizeof(*result));
+ result->major_status = gss_accept_sec_context(&result->minor_status,
+ &ctx, cred, &argp->input_token, argp->input_chan_bindings,
+ &src_name, &result->mech_type, &result->output_token,
+ &result->ret_flags, &result->time_rec,
+ &delegated_cred_handle);
+
+ if (result->major_status == GSS_S_COMPLETE
+ || result->major_status == GSS_S_CONTINUE_NEEDED) {
+ if (argp->ctx)
+ result->ctx = argp->ctx;
+ else
+ result->ctx = gssd_make_resource(ctx);
+ result->src_name = gssd_make_resource(src_name);
+ result->delegated_cred_handle =
+ gssd_make_resource(delegated_cred_handle);
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp)
+{
+ gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
+
+ if (ctx) {
+ result->major_status = gss_delete_sec_context(
+ &result->minor_status, &ctx, &result->output_token);
+ gssd_delete_resource(argp->ctx);
+ } else {
+ result->major_status = GSS_S_COMPLETE;
+ result->minor_status = 0;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp)
+{
+ gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
+
+ if (ctx) {
+ result->major_status = gss_export_sec_context(
+ &result->minor_status, &ctx,
+ &result->interprocess_token);
+ result->format = KGSS_HEIMDAL_1_1;
+ gssd_delete_resource(argp->ctx);
+ } else {
+ result->major_status = GSS_S_FAILURE;
+ result->minor_status = 0;
+ result->interprocess_token.length = 0;
+ result->interprocess_token.value = NULL;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name;
+
+ result->major_status = gss_import_name(&result->minor_status,
+ &argp->input_name_buffer, argp->input_name_type, &name);
+
+ if (result->major_status == GSS_S_COMPLETE)
+ result->output_name = gssd_make_resource(name);
+ else
+ result->output_name = 0;
+
+ return (TRUE);
+}
+
+bool_t
+gssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name = gssd_find_resource(argp->input_name);
+ gss_name_t output_name;
+
+ memset(result, 0, sizeof(*result));
+ if (!name) {
+ result->major_status = GSS_S_BAD_NAME;
+ return (TRUE);
+ }
+
+ result->major_status = gss_canonicalize_name(&result->minor_status,
+ name, argp->mech_type, &output_name);
+
+ if (result->major_status == GSS_S_COMPLETE)
+ result->output_name = gssd_make_resource(output_name);
+ else
+ result->output_name = 0;
+
+ return (TRUE);
+}
+
+bool_t
+gssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name = gssd_find_resource(argp->input_name);
+
+ memset(result, 0, sizeof(*result));
+ if (!name) {
+ result->major_status = GSS_S_BAD_NAME;
+ return (TRUE);
+ }
+
+ result->major_status = gss_export_name(&result->minor_status,
+ name, &result->exported_name);
+
+ return (TRUE);
+}
+
+bool_t
+gssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name = gssd_find_resource(argp->input_name);
+
+ if (name) {
+ result->major_status = gss_release_name(&result->minor_status,
+ &name);
+ gssd_delete_resource(argp->input_name);
+ } else {
+ result->major_status = GSS_S_COMPLETE;
+ result->minor_status = 0;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name = gssd_find_resource(argp->pname);
+ uid_t uid;
+ char buf[128];
+ struct passwd pwd, *pw;
+
+ memset(result, 0, sizeof(*result));
+ if (name) {
+ result->major_status =
+ gss_pname_to_uid(&result->minor_status,
+ name, argp->mech, &uid);
+ if (result->major_status == GSS_S_COMPLETE) {
+ result->uid = uid;
+ getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
+ if (pw) {
+ int len = NGRPS;
+ int groups[NGRPS];
+ result->gid = pw->pw_gid;
+ getgrouplist(pw->pw_name, pw->pw_gid,
+ groups, &len);
+ result->gidlist.gidlist_len = len;
+ result->gidlist.gidlist_val =
+ mem_alloc(len * sizeof(int));
+ memcpy(result->gidlist.gidlist_val, groups,
+ len * sizeof(int));
+ } else {
+ result->gid = 65534;
+ result->gidlist.gidlist_len = 0;
+ result->gidlist.gidlist_val = NULL;
+ }
+ }
+ } else {
+ result->major_status = GSS_S_BAD_NAME;
+ result->minor_status = 0;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp)
+{
+ gss_name_t desired_name = GSS_C_NO_NAME;
+ gss_cred_id_t cred;
+ char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1];
+
+ snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
+ (int) argp->uid);
+ setenv("KRB5CCNAME", ccname, TRUE);
+
+ memset(result, 0, sizeof(*result));
+ if (argp->desired_name) {
+ desired_name = gssd_find_resource(argp->desired_name);
+ if (!desired_name) {
+ result->major_status = GSS_S_BAD_NAME;
+ return (TRUE);
+ }
+ }
+
+ result->major_status = gss_acquire_cred(&result->minor_status,
+ desired_name, argp->time_req, argp->desired_mechs,
+ argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec);
+
+ if (result->major_status == GSS_S_COMPLETE)
+ result->output_cred = gssd_make_resource(cred);
+ else
+ result->output_cred = 0;
+
+ return (TRUE);
+}
+
+bool_t
+gssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp)
+{
+ gss_cred_id_t cred = gssd_find_resource(argp->cred);
+
+ memset(result, 0, sizeof(*result));
+ if (!cred) {
+ result->major_status = GSS_S_CREDENTIALS_EXPIRED;
+ return (TRUE);
+ }
+
+ result->major_status = gss_set_cred_option(&result->minor_status,
+ &cred, argp->option_name, &argp->option_value);
+
+ return (TRUE);
+}
+
+bool_t
+gssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp)
+{
+ gss_cred_id_t cred = gssd_find_resource(argp->cred);
+
+ if (cred) {
+ result->major_status = gss_release_cred(&result->minor_status,
+ &cred);
+ gssd_delete_resource(argp->cred);
+ } else {
+ result->major_status = GSS_S_COMPLETE;
+ result->minor_status = 0;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp)
+{
+
+ result->message_context = argp->message_context;
+ result->major_status = gss_display_status(&result->minor_status,
+ argp->status_value, argp->status_type, argp->mech_type,
+ &result->message_context, &result->status_string);
+
+ return (TRUE);
+}
+
+int
+gssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
+{
+ /*
+ * We don't use XDR to free the results - anything which was
+ * allocated came from GSS-API. We use xdr_result to figure
+ * out what to do.
+ */
+ OM_uint32 junk;
+
+ if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) {
+ init_sec_context_res *p = (init_sec_context_res *) result;
+ gss_release_buffer(&junk, &p->output_token);
+ } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) {
+ accept_sec_context_res *p = (accept_sec_context_res *) result;
+ gss_release_buffer(&junk, &p->output_token);
+ } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) {
+ delete_sec_context_res *p = (delete_sec_context_res *) result;
+ gss_release_buffer(&junk, &p->output_token);
+ } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) {
+ export_sec_context_res *p = (export_sec_context_res *) result;
+ if (p->interprocess_token.length)
+ memset(p->interprocess_token.value, 0,
+ p->interprocess_token.length);
+ gss_release_buffer(&junk, &p->interprocess_token);
+ } else if (xdr_result == (xdrproc_t) xdr_export_name_res) {
+ export_name_res *p = (export_name_res *) result;
+ gss_release_buffer(&junk, &p->exported_name);
+ } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) {
+ acquire_cred_res *p = (acquire_cred_res *) result;
+ gss_release_oid_set(&junk, &p->actual_mechs);
+ } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) {
+ pname_to_uid_res *p = (pname_to_uid_res *) result;
+ if (p->gidlist.gidlist_val)
+ free(p->gidlist.gidlist_val);
+ } else if (xdr_result == (xdrproc_t) xdr_display_status_res) {
+ display_status_res *p = (display_status_res *) result;
+ gss_release_buffer(&junk, &p->status_string);
+ }
+
+ return (TRUE);
+}
diff --git a/usr.sbin/mountd/exports.5 b/usr.sbin/mountd/exports.5
index f04b6b1..dca4f2d 100644
--- a/usr.sbin/mountd/exports.5
+++ b/usr.sbin/mountd/exports.5
@@ -149,6 +149,17 @@ option is given,
all users (including root) will be mapped to that credential in
place of their own.
.Pp
+.Sm off
+.Fl sec Li = Sy flavor1:flavor2...
+.Sm on
+specifies a colon separated list of acceptable security flavors to be
+used for remote access.
+Supported security flavors are sys, krb5, krb5i and krb5p.
+If multiple flavors are listed, they should be ordered with the most
+preferred flavor first.
+If this option is not present,
+the default security flavor list of just sys is used.
+.Pp
The
.Fl ro
option specifies that the file system should be exported read-only
@@ -305,6 +316,8 @@ the default remote mount-point file
/u2 -maproot=root friends
/u2 -alldirs -network cis-net -mask cis-mask
/cdrom -alldirs,quiet,ro -network 192.168.33.0 -mask 255.255.255.0
+/private -sec=krb5i
+/secret -sec=krb5p
.Ed
.Pp
Given that
@@ -411,6 +424,15 @@ While there is no CD-ROM medium mounted under
it would export the (normally empty) directory
.Pa /cdrom
of the root file system instead.
+.Pp
+The file system rooted at
+.Pa /private
+will be exported using Kerberos 5 authentication and will require
+integrity protected messages for all accesses.
+The file system rooted at
+.Pa /secret
+will also be exported using Kerberos 5 authentication and all messages
+used to access it will be encrypted.
.Sh SEE ALSO
.Xr netgroup 5 ,
.Xr mountd 8 ,
diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c
index fbcb4ec..82ab1b8 100644
--- a/usr.sbin/mountd/mountd.c
+++ b/usr.sbin/mountd/mountd.c
@@ -113,6 +113,8 @@ struct exportlist {
fsid_t ex_fs;
char *ex_fsdir;
char *ex_indexfile;
+ int ex_numsecflavors;
+ int ex_secflavors[MAXSECFLAVORS];
};
/* ex_flag bits */
#define EX_LINKED 0x1
@@ -150,6 +152,8 @@ struct fhreturn {
int fhr_flag;
int fhr_vers;
nfsfh_t fhr_fh;
+ int fhr_numsecflavors;
+ int *fhr_secflavors;
};
/* Global defs */
@@ -240,6 +244,7 @@ struct pidfh *pfh = NULL;
#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */
#define OP_QUIET 0x100
#define OP_MASKLEN 0x200
+#define OP_SEC 0x400
#ifdef DEBUG
int debug = 1;
@@ -817,6 +822,8 @@ mntsrv(rqstp, transp)
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
return;
}
+ fhr.fhr_numsecflavors = ep->ex_numsecflavors;
+ fhr.fhr_secflavors = ep->ex_secflavors;
if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
(caddr_t)&fhr))
syslog(LOG_ERR, "can't send reply");
@@ -934,6 +941,7 @@ xdr_fhs(xdrsp, cp)
{
struct fhreturn *fhrp = (struct fhreturn *)cp;
u_long ok = 0, len, auth;
+ int i;
if (!xdr_long(xdrsp, &ok))
return (0);
@@ -946,11 +954,20 @@ xdr_fhs(xdrsp, cp)
return (0);
if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
return (0);
- auth = RPCAUTH_UNIX;
- len = 1;
- if (!xdr_long(xdrsp, &len))
- return (0);
- return (xdr_long(xdrsp, &auth));
+ if (fhrp->fhr_numsecflavors) {
+ if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
+ return (0);
+ for (i = 0; i < fhrp->fhr_numsecflavors; i++)
+ if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
+ return (0);
+ return (1);
+ } else {
+ auth = AUTH_SYS;
+ len = 1;
+ if (!xdr_long(xdrsp, &len))
+ return (0);
+ return (xdr_long(xdrsp, &auth));
+ }
};
return (0);
}
@@ -1765,6 +1782,57 @@ free_dir(dp)
}
/*
+ * Parse a colon separated list of security flavors
+ */
+int
+parsesec(seclist, ep)
+ char *seclist;
+ struct exportlist *ep;
+{
+ char *cp, savedc;
+ int flavor;
+
+ ep->ex_numsecflavors = 0;
+ for (;;) {
+ cp = strchr(seclist, ':');
+ if (cp) {
+ savedc = *cp;
+ *cp = '\0';
+ }
+
+ if (!strcmp(seclist, "sys"))
+ flavor = AUTH_SYS;
+ else if (!strcmp(seclist, "krb5"))
+ flavor = RPCSEC_GSS_KRB5;
+ else if (!strcmp(seclist, "krb5i"))
+ flavor = RPCSEC_GSS_KRB5I;
+ else if (!strcmp(seclist, "krb5p"))
+ flavor = RPCSEC_GSS_KRB5P;
+ else {
+ if (cp)
+ *cp = savedc;
+ syslog(LOG_ERR, "bad sec flavor: %s", seclist);
+ return (1);
+ }
+ if (ep->ex_numsecflavors == MAXSECFLAVORS) {
+ if (cp)
+ *cp = savedc;
+ syslog(LOG_ERR, "too many sec flavors: %s", seclist);
+ return (1);
+ }
+ ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
+ ep->ex_numsecflavors++;
+ if (cp) {
+ *cp = savedc;
+ seclist = cp + 1;
+ } else {
+ break;
+ }
+ }
+ return (0);
+}
+
+/*
* Parse the option string and update fields.
* Option arguments may either be -<option>=<value> or
* -<option> <value>
@@ -1859,6 +1927,11 @@ do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
ep->ex_indexfile = strdup(cpoptarg);
} else if (!strcmp(cpopt, "quiet")) {
opt_flags |= OP_QUIET;
+ } else if (!strcmp(cpopt, "sec")) {
+ if (parsesec(cpoptarg, ep))
+ return (1);
+ opt_flags |= OP_SEC;
+ usedarg++;
} else {
syslog(LOG_ERR, "bad opt %s", cpopt);
return (1);
@@ -2018,7 +2091,7 @@ do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
int done;
char savedc;
struct iovec *iov;
- int iovlen;
+ int i, iovlen;
int ret;
cp = NULL;
@@ -2036,6 +2109,13 @@ do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
ai = grp->gr_ptr.gt_addrinfo;
else
ai = NULL;
+ eap.ex_numsecflavors = ep->ex_numsecflavors;
+ for (i = 0; i < eap.ex_numsecflavors; i++)
+ eap.ex_secflavors[i] = ep->ex_secflavors[i];
+ if (eap.ex_numsecflavors == 0) {
+ eap.ex_numsecflavors = 1;
+ eap.ex_secflavors[0] = AUTH_SYS;
+ }
done = FALSE;
build_iovec(&iov, &iovlen, "fstype", NULL, 0);
diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c
index 09795ec8..788d1c2 100644
--- a/usr.sbin/nfsd/nfsd.c
+++ b/usr.sbin/nfsd/nfsd.c
@@ -81,6 +81,7 @@ int debug = 0;
#define DEFNFSDCNT 4
pid_t children[MAXNFSDCNT]; /* PIDs of children */
int nfsdcnt; /* number of children */
+int new_syscall;
void cleanup(int);
void child_cleanup(int);
@@ -116,7 +117,7 @@ void usage(void);
int
main(int argc, char **argv)
{
- struct nfsd_args nfsdargs;
+ struct nfsd_addsock_args addsockargs;
struct addrinfo *ai_udp, *ai_tcp, *ai_udp6, *ai_tcp6, hints;
struct netconfig *nconf_udp, *nconf_tcp, *nconf_udp6, *nconf_tcp6;
struct netbuf nb_udp, nb_tcp, nb_udp6, nb_tcp6;
@@ -326,23 +327,54 @@ main(int argc, char **argv)
openlog("nfsd", LOG_PID, LOG_DAEMON);
- /* If we use UDP only, we start the last server below. */
- srvcnt = tcpflag ? nfsdcnt : nfsdcnt - 1;
- for (i = 0; i < srvcnt; i++) {
- switch ((pid = fork())) {
- case -1:
+ /*
+ * Figure out if the kernel supports the new-style
+ * NFSSVC_NFSD. Old kernels will return ENXIO because they
+ * don't recognise the flag value, new ones will return EINVAL
+ * because argp is NULL.
+ */
+ new_syscall = FALSE;
+ if (nfssvc(NFSSVC_NFSD, NULL) < 0 && errno == EINVAL)
+ new_syscall = TRUE;
+ new_syscall = FALSE;
+
+ if (!new_syscall) {
+ /* If we use UDP only, we start the last server below. */
+ srvcnt = tcpflag ? nfsdcnt : nfsdcnt - 1;
+ for (i = 0; i < srvcnt; i++) {
+ switch ((pid = fork())) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ nfsd_exit(1);
+ case 0:
+ break;
+ default:
+ children[i] = pid;
+ continue;
+ }
+ (void)signal(SIGUSR1, child_cleanup);
+ setproctitle("server");
+
+ start_server(0);
+ }
+ } else if (tcpflag) {
+ /*
+ * For TCP mode, we fork once to start the first
+ * kernel nfsd thread. The kernel will add more
+ * threads as needed.
+ */
+ pid = fork();
+ if (pid == -1) {
syslog(LOG_ERR, "fork: %m");
nfsd_exit(1);
- case 0:
- break;
- default:
- children[i] = pid;
- continue;
}
- (void)signal(SIGUSR1, child_cleanup);
- setproctitle("server");
-
- start_server(0);
+ if (pid) {
+ children[0] = pid;
+ } else {
+ (void)signal(SIGUSR1, child_cleanup);
+ setproctitle("server");
+ start_server(0);
+ }
}
(void)signal(SIGUSR1, cleanup);
@@ -378,10 +410,10 @@ main(int argc, char **argv)
nfsd_exit(1);
}
freeaddrinfo(ai_udp);
- nfsdargs.sock = sock;
- nfsdargs.name = NULL;
- nfsdargs.namelen = 0;
- if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ addsockargs.sock = sock;
+ addsockargs.name = NULL;
+ addsockargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &addsockargs) < 0) {
syslog(LOG_ERR, "can't Add UDP socket");
nfsd_exit(1);
}
@@ -446,10 +478,10 @@ main(int argc, char **argv)
nfsd_exit(1);
}
freeaddrinfo(ai_udp6);
- nfsdargs.sock = sock;
- nfsdargs.name = NULL;
- nfsdargs.namelen = 0;
- if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ addsockargs.sock = sock;
+ addsockargs.name = NULL;
+ addsockargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &addsockargs) < 0) {
syslog(LOG_ERR,
"can't add UDP6 socket");
nfsd_exit(1);
@@ -676,10 +708,10 @@ main(int argc, char **argv)
SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
syslog(LOG_ERR,
"setsockopt SO_KEEPALIVE: %m");
- nfsdargs.sock = msgsock;
- nfsdargs.name = (caddr_t)&inetpeer;
- nfsdargs.namelen = len;
- nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ addsockargs.sock = msgsock;
+ addsockargs.name = (caddr_t)&inetpeer;
+ addsockargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &addsockargs);
(void)close(msgsock);
} else if (FD_ISSET(tcpsock, &v6bits)) {
len = sizeof(inet6peer);
@@ -698,10 +730,10 @@ main(int argc, char **argv)
sizeof(on)) < 0)
syslog(LOG_ERR, "setsockopt "
"SO_KEEPALIVE: %m");
- nfsdargs.sock = msgsock;
- nfsdargs.name = (caddr_t)&inet6peer;
- nfsdargs.namelen = len;
- nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ addsockargs.sock = msgsock;
+ addsockargs.name = (caddr_t)&inet6peer;
+ addsockargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &addsockargs);
(void)close(msgsock);
}
}
@@ -829,12 +861,27 @@ nfsd_exit(int status)
void
start_server(int master)
{
+ char principal[128];
+ char hostname[128];
+ struct nfsd_nfsd_args nfsdargs;
int status;
status = 0;
- if (nfssvc(NFSSVC_NFSD, NULL) < 0) {
- syslog(LOG_ERR, "nfssvc: %m");
- status = 1;
+ if (new_syscall) {
+ gethostname(hostname, sizeof(hostname));
+ snprintf(principal, sizeof(principal), "nfs@%s", hostname);
+ nfsdargs.principal = principal;
+ nfsdargs.minthreads = nfsdcnt;
+ nfsdargs.maxthreads = nfsdcnt;
+ if (nfssvc(NFSSVC_NFSD, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ status = 1;
+ }
+ } else {
+ if (nfssvc(NFSSVC_OLDNFSD, NULL) < 0) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ status = 1;
+ }
}
if (master)
nfsd_exit(status);
OpenPOWER on IntegriCloud