summaryrefslogtreecommitdiffstats
path: root/sys/kgssapi
diff options
context:
space:
mode:
authordfr <dfr@FreeBSD.org>2008-11-03 10:38:00 +0000
committerdfr <dfr@FreeBSD.org>2008-11-03 10:38:00 +0000
commit2fb03513fc4b5d35a398f1ceb4b439fe4bb5fb74 (patch)
treec59f88924c0b3ead68523ce14806894836f8d9a7 /sys/kgssapi
parent8b86595849b35ac7c26977f1b8206c1678c9b5bb (diff)
downloadFreeBSD-src-2fb03513fc4b5d35a398f1ceb4b439fe4bb5fb74.zip
FreeBSD-src-2fb03513fc4b5d35a398f1ceb4b439fe4bb5fb74.tar.gz
Implement support for RPCSEC_GSS authentication to both the NFS client
and server. This replaces the RPC implementation of the NFS client and server with the newer RPC implementation originally developed (actually ported from the userland sunrpc code) to support the NFS Lock Manager. I have tested this code extensively and I believe it is stable and that performance is at least equal to the legacy RPC implementation. The NFS code currently contains support for both the new RPC implementation and the older legacy implementation inherited from the original NFS codebase. The default is to use the new implementation - add the NFS_LEGACYRPC option to fall back to the old code. When I merge this support back to RELENG_7, I will probably change this so that users have to 'opt in' to get the new code. To use RPCSEC_GSS on either client or server, you must build a kernel which includes the KGSSAPI option and the crypto device. On the userland side, you must build at least a new libc, mountd, mount_nfs and gssd. You must install new versions of /etc/rc.d/gssd and /etc/rc.d/nfsd and add 'gssd_enable=YES' to /etc/rc.conf. As long as gssd is running, you should be able to mount an NFS filesystem from a server that requires RPCSEC_GSS authentication. The mount itself can happen without any kerberos credentials but all access to the filesystem will be denied unless the accessing user has a valid ticket file in the standard place (/tmp/krb5cc_<uid>). There is currently no support for situations where the ticket file is in a different place, such as when the user logged in via SSH and has delegated credentials from that login. This restriction is also present in Solaris and Linux. In theory, we could improve this in future, possibly using Brooks Davis' implementation of variant symlinks. Supporting RPCSEC_GSS on a server is nearly as simple. You must create service creds for the server in the form 'nfs/<fqdn>@<REALM>' and install them in /etc/krb5.keytab. The standard heimdal utility ktutil makes this fairly easy. After the service creds have been created, you can add a '-sec=krb5' option to /etc/exports and restart both mountd and nfsd. The only other difference an administrator should notice is that nfsd doesn't fork to create service threads any more. In normal operation, there will be two nfsd processes, one in userland waiting for TCP connections and one in the kernel handling requests. The latter process will create as many kthreads as required - these should be visible via 'top -H'. The code has some support for varying the number of service threads according to load but initially at least, nfsd uses a fixed number of threads according to the value supplied to its '-n' option. Sponsored by: Isilon Systems MFC after: 1 month
Diffstat (limited to 'sys/kgssapi')
-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
37 files changed, 8492 insertions, 0 deletions
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);
OpenPOWER on IntegriCloud