summaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/Makefile.in2
-rw-r--r--utils/gssd/Makefile16
-rw-r--r--utils/gssd/context.c467
-rw-r--r--utils/gssd/context.h38
-rw-r--r--utils/gssd/context_heimdal.c256
-rw-r--r--utils/gssd/err_util.c92
-rw-r--r--utils/gssd/err_util.h37
-rw-r--r--utils/gssd/gss_clnt_send_err.c104
-rw-r--r--utils/gssd/gss_destroy_creds11
-rw-r--r--utils/gssd/gss_oids.c39
-rw-r--r--utils/gssd/gss_oids.h46
-rw-r--r--utils/gssd/gss_util.c212
-rw-r--r--utils/gssd/gss_util.h44
-rw-r--r--utils/gssd/gssd.c134
-rw-r--r--utils/gssd/gssd.h89
-rw-r--r--utils/gssd/gssd.man63
-rw-r--r--utils/gssd/gssd_main_loop.c144
-rw-r--r--utils/gssd/gssd_proc.c661
-rw-r--r--utils/gssd/krb5_util.c809
-rw-r--r--utils/gssd/krb5_util.h30
-rw-r--r--utils/gssd/write_bytes.h139
-rw-r--r--utils/gssdestroycreds/Makefile14
-rw-r--r--utils/svcgssd/Makefile23
-rw-r--r--utils/svcgssd/cacheio.c289
-rw-r--r--utils/svcgssd/cacheio.h48
-rw-r--r--utils/svcgssd/svcgssd.c209
-rw-r--r--utils/svcgssd/svcgssd.h43
-rw-r--r--utils/svcgssd/svcgssd.man41
-rw-r--r--utils/svcgssd/svcgssd_main_loop.c86
-rw-r--r--utils/svcgssd/svcgssd_mech2file.c77
-rw-r--r--utils/svcgssd/svcgssd_proc.c344
31 files changed, 4606 insertions, 1 deletions
diff --git a/utils/Makefile.in b/utils/Makefile.in
index 02bafbf..539632f 100644
--- a/utils/Makefile.in
+++ b/utils/Makefile.in
@@ -3,7 +3,7 @@
#
SUBDIRS = exportfs mountd nfsd statd nfsstat @RQUOTAD@ showmount \
- nhfsstone lockd @IDMAPD@
+ nhfsstone lockd @IDMAPD@ @GSSD@ @SVCGSSD@
include $(TOP)rules.mk
diff --git a/utils/gssd/Makefile b/utils/gssd/Makefile
new file mode 100644
index 0000000..6eba0f5
--- /dev/null
+++ b/utils/gssd/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for rpc.gssd
+#
+
+PROGRAM = gssd
+PREFIX = rpc.
+OBJS = gssd.o gssd_main_loop.o gssd_proc.o err_util.o gss_util.o \
+ gss_oids.o context.o context_heimdal.o krb5_util.o
+LIBDEPS = $(TOP)support/lib/librpc.a $(TOP)support/lib/libgssapi.a
+LIBS = -Wl,-rpath=$(KRBDIR)/lib -lrpc -lgssapi -ldl $(KRBLIB)
+MAN8 = gssd
+
+include $(TOP)rules.mk
+
+CFLAGS += -DKRB5_VERSION=$(KRB5_VERSION) -I$(TOP)support/rpc/include/ \
+ -I$(KRBDIR)/include
diff --git a/utils/gssd/context.c b/utils/gssd/context.c
new file mode 100644
index 0000000..08979f3
--- /dev/null
+++ b/utils/gssd/context.c
@@ -0,0 +1,467 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#ifdef HAVE_KRB5
+#include <krb5.h>
+#endif
+#include <rpc/rpc.h>
+#include <rpc/auth_gss.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+/* spkm3 seems to actually want it this big, yipes. */
+#define MAX_CTX_LEN 4096
+
+#ifdef HAVE_KRB5 /* MIT Kerberos */
+
+#ifdef HAVE_LUCID_CONTEXT_SUPPORT
+
+/* Don't use the private structure, use the exported lucid structure */
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+
+#elif (KRB5_VERSION > 131)
+/* XXX argggg, there's gotta be a better way than just duplicating this
+ * whole struct. Unfortunately, this is in a "private" header file,
+ * so this is our best choice at this point :-/
+ *
+ * XXX Does this match the Heimdal definition? */
+
+typedef struct _krb5_gss_ctx_id_rec {
+ unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */
+ unsigned int established : 1;
+ unsigned int big_endian : 1;
+ unsigned int have_acceptor_subkey : 1;
+ unsigned int seed_init : 1; /* XXX tested but never actually set */
+#ifdef CFX_EXERCISE
+ unsigned int testing_unknown_tokid : 1; /* for testing only */
+#endif
+ OM_uint32 gss_flags;
+ unsigned char seed[16];
+ krb5_principal here;
+ krb5_principal there;
+ krb5_keyblock *subkey;
+ int signalg;
+ size_t cksum_size;
+ int sealalg;
+ krb5_keyblock *enc;
+ krb5_keyblock *seq;
+ krb5_timestamp endtime;
+ krb5_flags krb_flags;
+ /* XXX these used to be signed. the old spec is inspecific, and
+ the new spec specifies unsigned. I don't believe that the change
+ affects the wire encoding. */
+ uint64_t seq_send; /* gssint_uint64 */
+ uint64_t seq_recv; /* gssint_uint64 */
+ void *seqstate;
+ krb5_auth_context auth_context;
+ gss_buffer_desc *mech_used; /* gss_OID_desc */
+ /* Protocol spec revision
+ 0 => RFC 1964 with 3DES and RC4 enhancements
+ 1 => draft-ietf-krb-wg-gssapi-cfx-01
+ No others defined so far. */
+ int proto;
+ krb5_cksumtype cksumtype; /* for "main" subkey */
+ krb5_keyblock *acceptor_subkey; /* CFX only */
+ krb5_cksumtype acceptor_subkey_cksumtype;
+#ifdef CFX_EXERCISE
+ gss_buffer_desc init_token;
+#endif
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#else /* KRB5_VERSION */
+
+typedef struct _krb5_gss_ctx_id_rec {
+ int initiate;
+ u_int32_t gss_flags;
+ int seed_init;
+ unsigned char seed[16];
+ krb5_principal here;
+ krb5_principal there;
+ krb5_keyblock *subkey;
+ int signalg;
+ int cksum_size;
+ int sealalg;
+ krb5_keyblock *enc;
+ krb5_keyblock *seq;
+ krb5_timestamp endtime;
+ krb5_flags krb_flags;
+ krb5_ui_4 seq_send;
+ krb5_ui_4 seq_recv;
+ void *seqstate;
+ int established;
+ int big_endian;
+ krb5_auth_context auth_context;
+ gss_buffer_desc *mech_used;
+ int nctypes;
+ krb5_cksumtype *ctypes;
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#endif /* KRB5_VERSION */
+#endif /* HAVE_KRB5 */
+
+/* XXX We have the same issue as above. We can require SPKM-3 source
+ * at the time we compile gssd, or copy the context structure definitions
+ * here.
+ */
+
+/* structure typedefs */
+
+typedef struct spkm3_ctx_id_t {
+ int length;
+ unsigned char *data;
+} spkm3_ctx_id,
+ *spkm3_ctx_id_t;
+
+/* first pass at spkm3 context. will add a bunch of stuff .... */
+
+typedef struct spkm3_gss_ctx_id_desc_t {
+ spkm3_ctx_id ctx_id; /* per spkm token contextid */
+ int established;
+ int qop; /* negotiated qop */
+ gss_OID mech_used;
+ OM_uint32 ret_flags;
+ OM_uint32 req_flags;
+ /* DH should be abstracted to an EVP_ struct able to hold
+ * various kalg results */
+ /* XXX The following is defined as "DH *dh" in the original
+ * header we're gonna cheat and use "void *dh" here. */
+ void *dh;
+ gss_buffer_desc share_key;
+ /* derived keys are result from applying the owf_alg to the
+ * shared key - see spkm3_derive_supkey */
+ gss_buffer_desc derived_conf_key;
+ gss_buffer_desc derived_integ_key;
+ /* openssl NID's of the negotiated algorithms */
+ int keyestb_alg; /* key establishment */
+ int owf_alg; /* one way function */
+ int intg_alg; /* integrity */
+ int conf_alg; /* privacy */
+ /* der encoded REQ_TOKEN reqcontets and length */
+ unsigned char *der_reqcontents;
+ int der_req_len;
+} spkm3_gss_ctx_id_desc;
+
+
+/* adapted from mit kerberos 5 ../lib/gssapi/mechglue/mglueP.h
+ * this is what gets passed around when the mechglue code is enabled : */
+typedef struct gss_union_ctx_id_t {
+ gss_OID mech_type;
+ gss_ctx_id_t internal_ctx_id;
+} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
+
+#ifdef HAVE_KRB5 /* MIT Kerberos */
+#ifdef HAVE_LUCID_CONTEXT_SUPPORT /* Lucid context support */
+static int
+write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
+{
+ gss_buffer_desc tmp;
+
+ if (WRITE_BYTES(p, end, key->type)) return -1;
+ tmp.length = key->length;
+ tmp.value = key->data;
+ if (write_buffer(p, end, &tmp)) return -1;
+ return 0;
+}
+
+#else /* lucid context support */
+
+static int
+write_keyblock(char **p, char *end, struct _krb5_keyblock *arg)
+{
+ gss_buffer_desc tmp;
+
+ if (WRITE_BYTES(p, end, arg->enctype)) return -1;
+ tmp.length = arg->length;
+ tmp.value = arg->contents;
+ if (write_buffer(p, end, &tmp)) return -1;
+ return 0;
+}
+#endif /* lucid context support */
+#endif /* HAVE_KRB5 */
+
+#ifdef HAVE_KRB5
+#ifdef HAVE_LUCID_CONTEXT_SUPPORT /* Lucid context support */
+static int
+prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf)
+{
+ char *p, *end;
+ static int constant_zero = 0;
+ unsigned char fakeseed[16];
+ uint32_t word_send_seq;
+ gss_krb5_lucid_key_t enc_key;
+ int i;
+ char *skd, *dkd;
+ gss_buffer_desc fakeoid;
+
+ /*
+ * The new Kerberos interface to get the gss context
+ * does not include the seed or seed_init fields
+ * because we never really use them. But for now,
+ * send down a fake buffer so we can use the same
+ * interface to the kernel.
+ */
+ memset(&enc_key, 0, sizeof(enc_key));
+ memset(&fakeoid, 0, sizeof(fakeoid));
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
+
+ /* seed_init and seed not used by kernel anyway */
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
+
+ if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.sign_alg)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.seal_alg)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+ word_send_seq = lctx->send_seq; /* XXX send_seq is 64-bit */
+ if (WRITE_BYTES(&p, end, word_send_seq)) goto out_err;
+ if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
+
+ /* derive the encryption key and copy it into buffer */
+ enc_key.type = lctx->rfc1964_kd.ctx_key.type;
+ enc_key.length = lctx->rfc1964_kd.ctx_key.length;
+ if ((enc_key.data = calloc(1, enc_key.length)) == NULL)
+ goto out_err;
+ skd = (char *) lctx->rfc1964_kd.ctx_key.data;
+ dkd = (char *) enc_key.data;
+ for (i = 0; i < enc_key.length; i++)
+ dkd[i] = skd[i] ^ 0xf0;
+ if (write_lucid_keyblock(&p, end, &enc_key)) {
+ free(enc_key.data);
+ goto out_err;
+ }
+ free(enc_key.data);
+
+ if (write_lucid_keyblock(&p, end, &lctx->rfc1964_kd.ctx_key))
+ goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+ if (enc_key.data) free(enc_key.data);
+ return -1;
+}
+
+static int
+prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf)
+{
+ printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n");
+ return -1;
+}
+
+static int
+serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+ OM_uint32 maj_stat, min_stat;
+ void *return_ctx = 0;
+ OM_uint32 vers;
+ gss_krb5_lucid_context_v1_t *lctx = 0;
+ int retcode = 0;
+
+ printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n");
+ maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, &ctx,
+ 1, &return_ctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ goto out_err;
+
+ /* Check the version returned, we only support v1 right now */
+ vers = ((gss_krb5_lucid_context_version_t *)return_ctx)->version;
+ switch (vers) {
+ case 1:
+ lctx = (gss_krb5_lucid_context_v1_t *) return_ctx;
+ break;
+ default:
+ printerr(0, "ERROR: unsupported lucid sec context version %d\n",
+ vers);
+ goto out_err;
+ break;
+ }
+
+ /* Now lctx points to a lucid context that we can send down to kernel */
+ if (lctx->protocol == 0)
+ retcode = prepare_krb5_rfc1964_buffer(lctx, buf);
+ else
+ retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf);
+
+ maj_stat = gss_krb5_free_lucid_sec_context(&min_stat,
+ (void *)lctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ printerr(0, "WARN: failed to free lucid sec context\n");
+ if (retcode)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ return -1;
+}
+
+
+#else /* lucid context support */
+
+static int
+serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+ krb5_gss_ctx_id_t kctx = (krb5_gss_ctx_id_t)ctx;
+ char *p, *end;
+ static int constant_one = 1;
+ static int constant_zero = 0;
+ uint32_t word_seq_send;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ if (kctx->initiate) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+ if (kctx->seed_init) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+ if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed)))
+ goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+ word_seq_send = kctx->seq_send;
+ if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err;
+ if (write_buffer(&p, end, kctx->mech_used)) goto out_err;
+ if (write_keyblock(&p, end, kctx->enc)) goto out_err;
+ if (write_keyblock(&p, end, kctx->seq)) goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+ return -1;
+}
+#endif /* lucid context support */
+#endif /* HAVE_KRB5 */
+
+
+/* ANDROS: need to determine which fields of the spkm3_gss_ctx_id_desc_t
+ * are needed in the kernel for get_mic, validate, wrap, unwrap, and destroy
+ * and only export those fields to the kernel.
+ */
+static int
+serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+ spkm3_gss_ctx_id_desc *sctx = (spkm3_gss_ctx_id_desc *)ctx;
+ char *p, *end;
+
+ printerr(1, "serialize_spkm3_ctx called\n");
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+/* buf->length
+ctx_id 4 + 12
+qop 4
+mech_used 4 + 7
+ret_fl 4
+req_fl 4
+share 4 + 16
+conf_alg 4
+d_conf_key 4 + 0
+intg_alg 4
+d_intg_key 4 + 0
+kyestb 4
+owl alg 4
+*/
+ if (write_buffer(&p, end, (gss_buffer_desc *)&sctx->ctx_id))
+ goto out_err;
+ if (WRITE_BYTES(&p, end, sctx->qop)) goto out_err;
+ if (write_buffer(&p, end, (gss_buffer_desc *)sctx->mech_used)) goto out_err;
+ if (WRITE_BYTES(&p, end, sctx->ret_flags)) goto out_err;
+ if (WRITE_BYTES(&p, end, sctx->req_flags)) goto out_err;
+ if (write_buffer(&p, end, &sctx->share_key))
+ goto out_err;
+
+ if (WRITE_BYTES(&p, end, sctx->conf_alg)) goto out_err;
+ if (write_buffer(&p, end, &sctx->derived_conf_key))
+ goto out_err;
+
+ if (WRITE_BYTES(&p, end, sctx->intg_alg)) goto out_err;
+ if (write_buffer(&p, end, &sctx->derived_integ_key))
+ goto out_err;
+
+ if (WRITE_BYTES(&p, end, sctx->keyestb_alg)) goto out_err;
+ if (WRITE_BYTES(&p, end, sctx->owf_alg)) goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+out_err:
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+ return -1;
+}
+
+int
+serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+ gss_union_ctx_id_t uctx = (gss_union_ctx_id_t)ctx;
+
+ if (g_OID_equal(&krb5oid, uctx->mech_type))
+ return serialize_krb5_ctx(uctx->internal_ctx_id, buf);
+ else if (g_OID_equal(&spkm3oid, uctx->mech_type))
+ return serialize_spkm3_ctx(uctx->internal_ctx_id, buf);
+ else {
+ printerr(0, "ERROR: attempting to serialize context with "
+ "unknown mechanism oid\n");
+ return -1;
+ }
+}
diff --git a/utils/gssd/context.h b/utils/gssd/context.h
new file mode 100644
index 0000000..d896bd0
--- /dev/null
+++ b/utils/gssd/context.h
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _CONTEXT_H_
+#define _CONTEXT_H_
+
+#include <rpc/rpc.h>
+
+int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf);
+
+#endif /* _CONTEXT_H_ */
diff --git a/utils/gssd/context_heimdal.c b/utils/gssd/context_heimdal.c
new file mode 100644
index 0000000..27c44a3
--- /dev/null
+++ b/utils/gssd/context_heimdal.c
@@ -0,0 +1,256 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+
+#ifdef HAVE_HEIMDAL
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <gssapi.h>
+#include <krb5.h>
+#include <com_err.h>
+#include "err_util.h"
+#include "gss_oids.h"
+#include "write_bytes.h"
+
+#define MAX_CTX_LEN 4096
+
+int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
+{
+ gss_buffer_desc tmp;
+ int code = -1;
+
+ if (WRITE_BYTES(p, end, key->keytype)) goto out_err;
+ tmp.length = key->keyvalue.length;
+ tmp.value = key->keyvalue.data;
+ if (write_buffer(p, end, &tmp)) goto out_err;
+ code = 0;
+ out_err:
+ return(code);
+}
+
+int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+ krb5_keyblock enc_key, *key;
+ krb5_context context;
+ krb5_error_code ret;
+ int i;
+ char *skd, *dkd;
+ int code = -1;
+
+ if ((ret = krb5_init_context(&context))) {
+ printerr(0, "ERROR: initializing krb5_context: %s\n",
+ error_message(ret));
+ goto out_err;
+ }
+
+ if ((ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context, &key))){
+ printerr(0, "ERROR: getting auth_context key: %s\n",
+ error_message(ret));
+ goto out_err_free_context;
+ }
+
+ memset(&enc_key, 0, sizeof(enc_key));
+ printerr(1, "WARN: write_heimdal_enc_key: "
+ "overriding heimdal keytype\n");
+ enc_key.keytype = 4 /* XXX XXX XXX XXX key->keytype */;
+ enc_key.keyvalue.length = key->keyvalue.length;
+ if ((enc_key.keyvalue.data =
+ calloc(1, enc_key.keyvalue.length)) == NULL) {
+
+ printerr(0, "ERROR: allocating memory for enc key: %s\n",
+ error_message(ENOMEM));
+ goto out_err_free_key;
+ }
+ skd = (char *) key->keyvalue.data;
+ dkd = (char *) enc_key.keyvalue.data;
+ for (i = 0; i < enc_key.keyvalue.length; i++)
+ dkd[i] = skd[i] ^ 0xf0;
+ if (write_heimdal_keyblock(p, end, &enc_key)) {
+ goto out_err_free_enckey;
+ }
+
+ code = 0;
+
+ out_err_free_enckey:
+ krb5_free_keyblock_contents(context, &enc_key);
+ out_err_free_key:
+ krb5_free_keyblock(context, key);
+ out_err_free_context:
+ krb5_free_context(context);
+ out_err:
+ printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS");
+ return(code);
+}
+
+int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+ krb5_keyblock *key;
+ krb5_context context;
+ krb5_error_code ret;
+ int code = -1;
+
+ if ((ret = krb5_init_context(&context))) {
+ printerr(0, "ERROR: initializing krb5_context: %s\n",
+ error_message(ret));
+ goto out_err;
+ }
+
+ if ((ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context, &key))){
+ printerr(0, "ERROR: getting auth_context key: %s\n",
+ error_message(ret));
+ goto out_err_free_context;
+ }
+
+ printerr(1, "WARN: write_heimdal_seq_key: "
+ "overriding heimdal keytype\n");
+ key->keytype = 4; /* XXX XXX XXX XXX XXX */
+
+ if (write_heimdal_keyblock(p, end, key)) {
+ goto out_err_free_key;
+ }
+
+ code = 0;
+
+ out_err_free_key:
+ krb5_free_keyblock(context, key);
+ out_err_free_context:
+ krb5_free_context(context);
+ out_err:
+ printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS");
+ return(code);
+}
+
+/*
+ * The following is the kernel structure that we are filling in:
+ *
+ * struct krb5_ctx {
+ * int initiate;
+ * int seed_init;
+ * unsigned char seed[16];
+ * int signalg;
+ * int sealalg;
+ * struct crypto_tfm *enc;
+ * struct crypto_tfm *seq;
+ * s32 endtime;
+ * u32 seq_send;
+ * struct xdr_netobj mech_used;
+ * };
+ *
+ * However, note that we do not send the data fields in the
+ * order they appear in the structure. The order they are
+ * sent down in is:
+ *
+ * initiate
+ * seed_init
+ * seed
+ * signalg
+ * sealalg
+ * endtime
+ * seq_send
+ * mech_used
+ * enc key
+ * seq key
+ *
+ */
+
+int
+serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+
+ char *p, *end;
+ static int constant_one = 1;
+ static int constant_zero = 0;
+ unsigned char fakeseed[16];
+ uint32_t algorithm;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+
+ /* initiate: 1 => initiating 0 => accepting */
+ if (ctx->more_flags & LOCAL) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+
+ /* seed_init: not used by kernel code */
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+
+ /* seed: not used by kernel code */
+ memset(&fakeseed, 0, sizeof(fakeseed));
+ if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
+
+ /* signalg */
+ algorithm = 0; /* SGN_ALG_DES_MAC_MD5 XXX */
+ if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+ /* sealalg */
+ algorithm = 0; /* SEAL_ALG_DES XXX */
+ if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+ /* endtime */
+ if (WRITE_BYTES(&p, end, ctx->lifetime)) goto out_err;
+
+ /* seq_send */
+ if (WRITE_BYTES(&p, end, ctx->auth_context->local_seqnumber))
+ goto out_err;
+ /* mech_used */
+ if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
+
+ /* enc: derive the encryption key and copy it into buffer */
+ if (write_heimdal_enc_key(&p, end, ctx)) goto out_err;
+
+ /* seq: get the sequence number key and copy it into buffer */
+ if (write_heimdal_seq_key(&p, end, ctx)) goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ printerr(2, "serialize_krb5_ctx: returning buffer "
+ "with %d bytes\n", buf->length);
+
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed exporting Heimdal krb5 ctx to kernel\n");
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+ return -1;
+}
+
+#endif /* HAVE_HEIMDAL */
diff --git a/utils/gssd/err_util.c b/utils/gssd/err_util.c
new file mode 100644
index 0000000..ca9b028
--- /dev/null
+++ b/utils/gssd/err_util.c
@@ -0,0 +1,92 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include "err_util.h"
+
+static int verbosity = 0;
+static int fg = 0;
+
+static char message_buf[500];
+static char tmp_buf[500];
+
+void initerr(char *progname, int set_verbosity, int set_fg)
+{
+ verbosity = set_verbosity;
+ fg = set_fg;
+ if (!fg)
+ openlog(progname, LOG_PID, LOG_DAEMON);
+}
+
+void printerr(int priority, char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ /* aggregate lines: only print buffer when we get to the end of a
+ * line or run out of space: */
+ va_start(args, format);
+ ret = vsnprintf(tmp_buf, sizeof(tmp_buf), format, args);
+ va_end(args);
+ if ((ret < 0) || (ret >= sizeof(tmp_buf)))
+ goto output;
+ if (strlen(tmp_buf) + strlen(message_buf) + 1 > sizeof(message_buf))
+ goto output;
+ strcat(message_buf, tmp_buf);
+ if (tmp_buf[strlen(tmp_buf) - 1] == '\n')
+ goto output;
+ return;
+output:
+ priority -= verbosity;
+ if (priority < 0)
+ priority = 0;
+ if (fg) {
+ if (priority == 0)
+ fprintf(stderr, "%s", message_buf);
+ } else {
+ int sys_pri;
+ switch (priority) {
+ case 0:
+ sys_pri = LOG_ERR;
+ break;
+ case 1:
+ sys_pri = LOG_DEBUG;
+ break;
+ default:
+ goto out;
+ }
+ syslog(sys_pri, "%s", message_buf);
+ }
+out:
+ memset(message_buf, 0, sizeof(message_buf));
+}
diff --git a/utils/gssd/err_util.h b/utils/gssd/err_util.h
new file mode 100644
index 0000000..5e5af48
--- /dev/null
+++ b/utils/gssd/err_util.h
@@ -0,0 +1,37 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _ERR_UTIL_H_
+#define _ERR_UTIL_H_
+
+void initerr(char *progname, int verbosity, int fg);
+void printerr(int priority, char *format, ...);
+
+#endif /* _ERR_UTIL_H_ */
diff --git a/utils/gssd/gss_clnt_send_err.c b/utils/gssd/gss_clnt_send_err.c
new file mode 100644
index 0000000..5260b53
--- /dev/null
+++ b/utils/gssd/gss_clnt_send_err.c
@@ -0,0 +1,104 @@
+/*
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2004 Bruce Fields <bfields@umich.edu>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <fcntl.h>
+
+#include "gssd.h"
+#include "write_bytes.h"
+
+char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s clntdir user [user ...]\n", progname);
+ exit(1);
+}
+
+static int
+do_error_downcall(int k5_fd, uid_t uid, int err)
+{
+ char buf[1024];
+ char *p = buf, *end = buf + 1024;
+ unsigned int timeout = 0;
+ int zero = 0;
+
+ if (WRITE_BYTES(&p, end, uid)) return -1;
+ if (WRITE_BYTES(&p, end, timeout)) return -1;
+ /* use seq_win = 0 to indicate an error: */
+ if (WRITE_BYTES(&p, end, zero)) return -1;
+ if (WRITE_BYTES(&p, end, err)) return -1;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) return -1;
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ int i;
+ uid_t uid;
+ char *endptr;
+ struct passwd *pw;
+
+ if (argc < 3)
+ usage(argv[0]);
+ fd = open(argv[1], O_WRONLY);
+ if (fd == -1)
+ err(1, "unable to open %s", argv[1]);
+
+ for (i = 2; i < argc; i++) {
+ uid = strtol(argv[i], &endptr, 10);
+ if (*endptr != '\0') {
+ pw = getpwnam(argv[i]);
+ if (!pw)
+ err(1, "unknown user %s", argv[i]);
+ uid = pw->pw_uid;
+ }
+ if (do_error_downcall(fd, uid, -1))
+ err(1, "failed to destroy cred for user %s", argv[i]);
+ }
+ exit(0);
+}
diff --git a/utils/gssd/gss_destroy_creds b/utils/gssd/gss_destroy_creds
new file mode 100644
index 0000000..666bdd9
--- /dev/null
+++ b/utils/gssd/gss_destroy_creds
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+path=`mount|grep rpc_pipefs|head -1|awk '{ print $3 }'`
+
+if [ -z "$path" ]; then
+ echo "unable to find rpc_pipefs; is it mounted?"
+ exit 1
+fi;
+
+find "$path" -name 'krb5' -exec gss_clnt_send_err '{}' $* ';'
+
diff --git a/utils/gssd/gss_oids.c b/utils/gssd/gss_oids.c
new file mode 100644
index 0000000..e800115
--- /dev/null
+++ b/utils/gssd/gss_oids.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <sys/types.h>
+#include <gssapi/gssapi.h>
+
+/* from kerberos source, gssapi_krb5.c */
+gss_OID_desc krb5oid =
+ {9, "\052\206\110\206\367\022\001\002\002"};
+
+gss_OID_desc spkm3oid =
+ {7, "\052\006\001\005\005\001\003"};
diff --git a/utils/gssd/gss_oids.h b/utils/gssd/gss_oids.h
new file mode 100644
index 0000000..850c013
--- /dev/null
+++ b/utils/gssd/gss_oids.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GSS_OIDS_H_
+#define _GSS_OIDS_H_
+
+#include <sys/types.h>
+#include <gssapi/gssapi.h>
+
+extern gss_OID_desc krb5oid;
+extern gss_OID_desc spkm3oid;
+
+#ifndef g_OID_equal
+#define g_OID_equal(o1,o2) \
+ (((o1)->length == (o2)->length) && \
+ (memcmp((o1)->elements,(o2)->elements,(unsigned int) (o1)->length) == 0))
+#endif
+
+#endif /* _GSS_OIDS_H_ */
diff --git a/utils/gssd/gss_util.c b/utils/gssd/gss_util.c
new file mode 100644
index 0000000..3493280
--- /dev/null
+++ b/utils/gssd/gss_util.c
@@ -0,0 +1,212 @@
+/*
+ * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ * Copyright (c) 2002 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "config.h"
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <gssapi/gssapi.h>
+#include "gss_util.h"
+#include "err_util.h"
+#include "gssd.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+/* Global gssd_credentials handle */
+gss_cred_id_t gssd_creds;
+
+gss_OID g_mechOid = GSS_C_NULL_OID;;
+
+#if 0
+static void
+display_status_1(char *m, u_int32_t code, int type, const gss_OID mech)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
+ u_int32_t msg_ctx = 0;
+ char *typestr;
+
+ switch (type) {
+ case GSS_C_GSS_CODE:
+ typestr = "GSS";
+ break;
+ case GSS_C_MECH_CODE:
+ typestr = "mechanism";
+ break;
+ default:
+ return;
+ /* NOTREACHED */
+ }
+
+ for (;;) {
+ maj_stat = gss_display_status(&min_stat, code,
+ type, mech, &msg_ctx, &msg);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "ERROR: in call to "
+ "gss_display_status called from %s\n", m);
+ break;
+ } else {
+ printerr(0, "ERROR: GSS-API: (%s) error in %s(): %s\n",
+ typestr, m, (char *)msg.value);
+ }
+
+ if (msg.length != 0)
+ (void) gss_release_buffer(&min_stat, &msg);
+
+ if (msg_ctx == 0)
+ break;
+ }
+}
+#endif
+
+static void
+display_status_2(char *m, u_int32_t major, u_int32_t minor, const gss_OID mech)
+{
+ u_int32_t maj_stat1, min_stat1;
+ u_int32_t maj_stat2, min_stat2;
+ gss_buffer_desc maj_gss_buf = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc min_gss_buf = GSS_C_EMPTY_BUFFER;
+ char maj_buf[30], min_buf[30];
+ char *maj, *min;
+ u_int32_t msg_ctx = 0;
+
+ /* Get major status message */
+ maj_stat1 = gss_display_status(&min_stat1, major,
+ GSS_C_GSS_CODE, mech, &msg_ctx, &maj_gss_buf);
+
+ if (maj_stat1 != GSS_S_COMPLETE) {
+ snprintf(maj_buf, sizeof(maj_buf), "(0x%08x)", major);
+ maj = &maj_buf[0];
+ } else {
+ maj = maj_gss_buf.value;
+ }
+
+ /* Get minor status message */
+ maj_stat2 = gss_display_status(&min_stat2, minor,
+ GSS_C_MECH_CODE, mech, &msg_ctx, &min_gss_buf);
+
+ if (maj_stat2 != GSS_S_COMPLETE) {
+ snprintf(min_buf, sizeof(min_buf), "(0x%08x)", minor);
+ min = &min_buf[0];
+ } else {
+ min = min_gss_buf.value;
+ }
+
+ printerr(0, "ERROR: GSS-API: error in %s(): %s - %s\n",
+ m, maj, min);
+
+ if (maj_gss_buf.length != 0)
+ (void) gss_release_buffer(&min_stat1, &maj_gss_buf);
+ if (min_gss_buf.length != 0)
+ (void) gss_release_buffer(&min_stat2, &min_gss_buf);
+}
+
+void
+pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat, const gss_OID mech)
+{
+ display_status_2(msg, maj_stat, min_stat, mech);
+}
+
+int
+gssd_acquire_cred(char *server_name)
+{
+ gss_buffer_desc name;
+ gss_name_t target_name;
+ u_int32_t maj_stat, min_stat;
+ u_int32_t ignore_maj_stat, ignore_min_stat;
+
+ name.value = (void *)server_name;
+ name.length = strlen(server_name);
+
+ maj_stat = gss_import_name(&min_stat, &name,
+ (const gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
+ &target_name);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid);
+ return (FALSE);
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat, target_name, 0,
+ GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+ &gssd_creds, NULL, NULL);
+
+ ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ pgsserr("gss_acquire_cred", maj_stat, min_stat, g_mechOid);
+
+ return (maj_stat == GSS_S_COMPLETE);
+}
diff --git a/utils/gssd/gss_util.h b/utils/gssd/gss_util.h
new file mode 100644
index 0000000..9e480ac
--- /dev/null
+++ b/utils/gssd/gss_util.h
@@ -0,0 +1,44 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GSS_UTIL_H_
+#define _GSS_UTIL_H_
+
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include "write_bytes.h"
+
+extern gss_cred_id_t gssd_creds;
+
+int gssd_acquire_cred(char *server_name);
+void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat,
+ const gss_OID mech);
+
+#endif /* _GSS_UTIL_H_ */
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
new file mode 100644
index 0000000..0f7a7e5
--- /dev/null
+++ b/utils/gssd/gssd.c
@@ -0,0 +1,134 @@
+/*
+ gssd.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "krb5_util.h"
+
+char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
+
+void
+sig_die(int signal)
+{
+ /* destroy krb5 machine creds */
+ gssd_destroy_krb5_machine_creds();
+ printerr(1, "exiting on signal %d\n", signal);
+ exit(1);
+}
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s [-f] [-v] [-p pipefsdir] [-k keytab]\n",
+ progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fg = 0;
+ int verbosity = 0;
+ int opt;
+ extern char *optarg;
+ char *progname;
+
+ while ((opt = getopt(argc, argv, "fvmp:k:")) != -1) {
+ switch (opt) {
+ case 'f':
+ fg = 1;
+ break;
+ case 'm':
+ /* Accept but ignore this. Now the default. */
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'p':
+ strncpy(pipefsdir, optarg, sizeof(pipefsdir));
+ if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
+ errx(1, "pipefs path name too long");
+ break;
+ case 'k':
+ strncpy(keytabfile, optarg, sizeof(keytabfile));
+ if (keytabfile[sizeof(keytabfile)-1] != '\0')
+ errx(1, "keytab path name too long");
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+ strncat(pipefsdir + strlen(pipefsdir), "/" GSSD_SERVICE_NAME,
+ sizeof(pipefsdir)-strlen(pipefsdir));
+ if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
+ errx(1, "pipefs path name too long");
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+
+ if (!fg && daemon(0, 0) < 0)
+ errx(1, "fork");
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ signal(SIGHUP, sig_die);
+
+ /* Process keytab file and get machine credentials */
+ gssd_refresh_krb5_machine_creds();
+
+ gssd_run();
+ printerr(0, "gssd_run returned!\n");
+ abort();
+}
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
new file mode 100644
index 0000000..d590401
--- /dev/null
+++ b/utils/gssd/gssd.h
@@ -0,0 +1,89 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RPC_GSSD_H_
+#define _RPC_GSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+
+#define MAX_FILE_NAMELEN 32
+#define FD_ALLOC_BLOCK 32
+#ifndef GSSD_PIPEFS_DIR
+#define GSSD_PIPEFS_DIR "/var/lib/nfs/rpc_pipefs"
+#endif
+#define INFO "info"
+#define KRB5 "krb5"
+#define DNOTIFY_SIGNAL (SIGRTMIN + 3)
+
+#define GSSD_DEFAULT_CRED_DIR "/tmp"
+#define GSSD_DEFAULT_CRED_PREFIX "krb5cc_"
+#define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine"
+#define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab"
+#define GSSD_SERVICE_NAME "nfs"
+#define GSSD_SERVICE_NAME_LEN 3
+
+/*
+ * The gss mechanisms that we can handle
+ */
+enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUTHTYPE_LIPKEY};
+
+
+
+extern char pipefsdir[PATH_MAX];
+extern char keytabfile[PATH_MAX];
+
+TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
+
+struct clnt_info {
+ TAILQ_ENTRY(clnt_info) list;
+ char *dirname;
+ int dir_fd;
+ char *servicename;
+ char *servername;
+ int prog;
+ int vers;
+ char *protocol;
+ int krb5_fd;
+ int krb5_poll_index;
+ int spkm3_fd;
+ int spkm3_poll_index;
+};
+
+void init_client_list(void);
+int update_client_list(void);
+void handle_krb5_upcall(struct clnt_info *clp);
+void handle_spkm3_upcall(struct clnt_info *clp);
+int gssd_acquire_cred(char *server_name);
+void gssd_run(void);
+
+
+#endif /* _RPC_GSSD_H_ */
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
new file mode 100644
index 0000000..d8f9a0f
--- /dev/null
+++ b/utils/gssd/gssd.man
@@ -0,0 +1,63 @@
+.\"
+.\" rpc.gssd(8)
+.\"
+.\" Copyright (C) 2003 J. Bruce Fields <bfields@umich.edu>
+.TH rpc.gssd 8 "17 Mar 2003"
+.SH NAME
+rpc.gssd \- rpcsec_gss daemon
+.SH SYNOPSIS
+.B "rpc.gssd [-f] [-k keytab] [-p pipefsdir] [-v]"
+.SH DESCRIPTION
+The rpcsec_gss protocol gives a means of using the gss-api generic security
+api to provide security for protocols using rpc (in particular, nfs). Before
+exchanging any rpc requests using rpcsec_gss, the rpc client must first
+establish a security context. The linux kernel's implementation of rpcsec_gss
+depends on the userspace daemon
+.B rpc.gssd
+to establish security contexts. The
+.B rpc.gssd
+daemon uses files in the rpc_pipefs filesystem to communicate with the kernel.
+
+.SH OPTIONS
+.TP
+.B -f
+Runs
+.B rpc.gssd
+in the foreground and sends output to stderr (as opposed to syslogd)
+.TP
+.B -k keytab
+Tells
+.B rpc.gssd
+to use the keys for principals nfs/hostname in
+.I keytab
+to obtain machine credentials.
+The default value is "/etc/krb5.keytab".
+.\".TP
+.\".B -m
+.\"Ordinarily,
+.\".B rpc.gssd
+.\"looks for a cached ticket for user $UID in /tmp/krb5cc_$UID.
+.\"With the -m option, the user with uid 0 will be treated specially, and will
+.\"be mapped instead to the credentials for the principal nfs/hostname found in
+.\"the keytab file.
+.\"(This option is now the default and is ignored if specified.)
+.TP
+.B -p path
+Tells
+.B rpc.gssd
+where to look for the rpc_pipefs filesystem. The default value is
+"/var/lib/nfs/rpc_pipefs".
+.TP
+.B -v
+Increases the verbosity of the output (can be specified multiple times).
+.SH SEE ALSO
+.BR rpc.svcgssd(8)
+.SH AUTHORS
+.br
+Dug Song <dugsong@umich.edu>
+.br
+Andy Adamson <andros@umich.edu>
+.br
+Marius Aamodt Eriksen <marius@umich.edu>
+.br
+J. Bruce Fields <bfields@umich.edu>
diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c
new file mode 100644
index 0000000..a086bb3
--- /dev/null
+++ b/utils/gssd/gssd_main_loop.c
@@ -0,0 +1,144 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "gssd.h"
+#include "err_util.h"
+
+extern struct pollfd *pollarray;
+extern int pollsize;
+
+#define POLL_MILLISECS 500
+
+static volatile int dir_changed = 1;
+
+static void dir_notify_handler(int sig, siginfo_t *si, void *data)
+{
+ dir_changed = 1;
+}
+
+static void
+scan_poll_results(int ret)
+{
+ int i;
+ struct clnt_info *clp;
+
+ for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
+ {
+ i = clp->krb5_poll_index;
+ if (i >= 0 && pollarray[i].revents) {
+ if (pollarray[i].revents & POLLHUP)
+ dir_changed = 1;
+ if (pollarray[i].revents & POLLIN)
+ handle_krb5_upcall(clp);
+ pollarray[clp->krb5_poll_index].revents = 0;
+ ret--;
+ if (!ret)
+ break;
+ }
+ i = clp->spkm3_poll_index;
+ if (i >= 0 && pollarray[i].revents) {
+ if (pollarray[i].revents & POLLHUP)
+ dir_changed = 1;
+ if (pollarray[i].revents & POLLIN)
+ handle_spkm3_upcall(clp);
+ pollarray[clp->spkm3_poll_index].revents = 0;
+ ret--;
+ if (!ret)
+ break;
+ }
+ }
+};
+
+void
+gssd_run()
+{
+ int ret;
+ struct sigaction dn_act;
+ int fd;
+
+ /* Taken from linux/Documentation/dnotify.txt: */
+ dn_act.sa_sigaction = dir_notify_handler;
+ sigemptyset(&dn_act.sa_mask);
+ dn_act.sa_flags = SA_SIGINFO;
+ sigaction(DNOTIFY_SIGNAL, &dn_act, NULL);
+
+ if ((fd = open(pipefsdir, O_RDONLY)) == -1) {
+ printerr(0, "ERROR: failed to open %s: %s\n",
+ pipefsdir, strerror(errno));
+ exit(1);
+ }
+ fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL);
+ fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
+
+ init_client_list();
+
+ while (1) {
+ while (dir_changed) {
+ dir_changed = 0;
+ if (update_client_list()) {
+ printerr(0, "ERROR: couldn't update "
+ "client list\n");
+ exit(1);
+ }
+ }
+ /* race condition here: dir_changed could be set before we
+ * enter the poll, and we'd never notice if it weren't for the
+ * timeout. */
+ ret = poll(pollarray, pollsize, POLL_MILLISECS);
+ if (ret < 0) {
+ if (errno != EINTR)
+ printerr(0,
+ "WARNING: error return from poll\n");
+ } else if (ret == 0) {
+ /* timeout */
+ } else { /* ret > 0 */
+ scan_poll_results(ret);
+ }
+ }
+ close(fd);
+ return;
+}
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
new file mode 100644
index 0000000..1e7ebae
--- /dev/null
+++ b/utils/gssd/gssd_proc.c
@@ -0,0 +1,661 @@
+/*
+ gssd_proc.c
+
+ Copyright (c) 2000-2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2001 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+ Copyright (c) 2004 Kevin Coffman <kwc@umich.edu>
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <dirent.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include <netdb.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "krb5_util.h"
+#include "context.h"
+
+/*
+ * pollarray:
+ * array of struct pollfd suitable to pass to poll. initialized to
+ * zero - a zero struct is ignored by poll() because the events mask is 0.
+ *
+ * clnt_list:
+ * linked list of struct clnt_info which associates a clntXXX directory
+ * with an index into pollarray[], and other basic data about that client.
+ *
+ * Directory structure: created by the kernel nfs client
+ * /pipefsdir/clntXX : one per rpc_clnt struct in the kernel
+ * /pipefsdir/clntXX/krb5 : read uid for which kernel wants
+ * a context, write the resulting context
+ * /pipefsdir/clntXX/info : stores info such as server name
+ *
+ * Algorithm:
+ * Poll all /pipefsdir/clntXX/krb5 files. When ready, data read
+ * is a uid; performs rpcsec_gss context initialization protocol to
+ * get a cred for that user. Writes result to corresponding krb5 file
+ * in a form the kernel code will understand.
+ * In addition, we make sure we are notified whenever anything is
+ * created or destroyed in pipefsdir/ or in an of the clntXX directories,
+ * and rescan the whole pipefsdir when this happens.
+ */
+
+struct pollfd * pollarray;
+
+int pollsize; /* the size of pollaray (in pollfd's) */
+
+/* XXX buffer problems: */
+static int
+read_service_info(char *info_file_name, char **servicename, char **servername,
+ int *prog, int *vers, char **protocol) {
+#define INFOBUFLEN 256
+ char buf[INFOBUFLEN];
+ static char dummy[128];
+ int nbytes;
+ static char service[128];
+ static char address[128];
+ char program[16];
+ char version[16];
+ char protoname[16];
+ in_addr_t inaddr;
+ int fd = -1;
+ struct hostent *ent = NULL;
+ int numfields;
+
+ *servicename = *servername = *protocol = NULL;
+
+ if ((fd = open(info_file_name, O_RDONLY)) == -1) {
+ printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
+ strerror(errno));
+ goto fail;
+ }
+ if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
+ goto fail;
+ close(fd);
+
+ numfields = sscanf(buf,"RPC server: %s\n"
+ "service: %s %s version %s\n"
+ "address: %s\n"
+ "protocol: %s\n",
+ dummy,
+ service, program, version,
+ address,
+ protoname);
+
+ if (numfields == 5) {
+ strcpy(protoname, "tcp");
+ } else if (numfields != 6) {
+ goto fail;
+ }
+
+ /* check service, program, and version */
+ if(memcmp(service, "nfs", 3)) return -1;
+ *prog = atoi(program + 1); /* skip open paren */
+ *vers = atoi(version);
+ if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
+ goto fail;
+
+ /* create service name */
+ inaddr = inet_addr(address);
+ if (!(ent = gethostbyaddr(&inaddr, sizeof(inaddr), AF_INET))) {
+ printerr(0, "ERROR: can't resolve server %s name\n", address);
+ goto fail;
+ }
+ if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
+ goto fail;
+ memcpy(*servername, ent->h_name, strlen(ent->h_name));
+ snprintf(buf, INFOBUFLEN, "%s@%s", service, ent->h_name);
+ if (!(*servicename = calloc(strlen(buf) + 1, 1)))
+ goto fail;
+ memcpy(*servicename, buf, strlen(buf));
+
+ if (!(*protocol = strdup(protoname)))
+ goto fail;
+ return 0;
+fail:
+ printerr(0, "ERROR: failed to read service info\n");
+ if (fd != -1) close(fd);
+ if (*servername) free(*servername);
+ if (*servicename) free(*servicename);
+ if (*protocol) free(*protocol);
+ return -1;
+}
+
+static void
+destroy_client(struct clnt_info *clp)
+{
+ if (clp->dir_fd != -1) close(clp->dir_fd);
+ if (clp->krb5_fd != -1) close(clp->krb5_fd);
+ if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
+ if (clp->dirname) free(clp->dirname);
+ if (clp->servicename) free(clp->servicename);
+ if (clp->servername) free(clp->servername);
+ if (clp->protocol) free(clp->protocol);
+ free(clp);
+}
+
+static struct clnt_info *
+insert_new_clnt(void)
+{
+ struct clnt_info *clp = NULL;
+
+ if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
+ printerr(0, "ERROR: can't malloc clnt_info: %s\n",
+ strerror(errno));
+ goto out;
+ }
+ clp->krb5_poll_index = -1;
+ clp->spkm3_poll_index = -1;
+ clp->krb5_fd = -1;
+ clp->spkm3_fd = -1;
+ clp->dir_fd = -1;
+
+ TAILQ_INSERT_HEAD(&clnt_list, clp, list);
+out:
+ return clp;
+}
+
+static int
+process_clnt_dir_files(struct clnt_info * clp)
+{
+ char kname[32];
+ char sname[32];
+ char info_file_name[32];
+
+ snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
+ clp->krb5_fd = open(kname, O_RDWR);
+ snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
+ clp->spkm3_fd = open(sname, O_RDWR);
+ if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
+ return -1;
+ snprintf(info_file_name, sizeof(info_file_name), "%s/info",
+ clp->dirname);
+ if (read_service_info(info_file_name, &clp->servicename,
+ &clp->servername, &clp->prog, &clp->vers,
+ &clp->protocol))
+ return -1;
+ return 0;
+}
+
+static int
+get_poll_index(int *ind)
+{
+ int i;
+
+ *ind = -1;
+ for (i=0; i<FD_ALLOC_BLOCK; i++) {
+ if (pollarray[i].events == 0) {
+ *ind = i;
+ break;
+ }
+ }
+ if (*ind == -1) {
+ printerr(0, "ERROR: No pollarray slots open\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+process_clnt_dir(char *dir)
+{
+ struct clnt_info * clp;
+
+ if (!(clp = insert_new_clnt()))
+ goto fail_destroy_client;
+
+ if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
+ goto fail_destroy_client;
+ }
+ memcpy(clp->dirname, dir, strlen(dir));
+ if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
+ printerr(0, "ERROR: can't open %s: %s\n",
+ clp->dirname, strerror(errno));
+ goto fail_destroy_client;
+ }
+ fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
+ fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
+
+ if (process_clnt_dir_files(clp))
+ goto fail_keep_client;
+
+ if(clp->krb5_fd != -1) {
+ if (get_poll_index(&clp->krb5_poll_index)) {
+ printerr(0, "ERROR: Too many krb5 clients\n");
+ goto fail_destroy_client;
+ }
+ pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
+ pollarray[clp->krb5_poll_index].events |= POLLIN;
+ }
+
+ if(clp->spkm3_fd != -1) {
+ if (get_poll_index(&clp->spkm3_poll_index)) {
+ printerr(0, "ERROR: Too many spkm3 clients\n");
+ goto fail_destroy_client;
+ }
+ pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
+ pollarray[clp->spkm3_poll_index].events |= POLLIN;
+ }
+
+ return;
+
+fail_destroy_client:
+ if (clp) {
+ TAILQ_REMOVE(&clnt_list, clp, list);
+ destroy_client(clp);
+ }
+fail_keep_client:
+ /* We couldn't find some subdirectories, but we keep the client
+ * around in case we get a notification on the directory when the
+ * subdirectories are created. */
+ return;
+}
+
+void
+init_client_list(void)
+{
+ TAILQ_INIT(&clnt_list);
+ /* Eventually plan to grow/shrink poll array: */
+ pollsize = FD_ALLOC_BLOCK;
+ pollarray = calloc(pollsize, sizeof(struct pollfd));
+}
+
+static void
+destroy_client_list(void)
+{
+ struct clnt_info *clp;
+
+ printerr(1, "processing client list\n");
+
+ while (clnt_list.tqh_first != NULL) {
+ clp = clnt_list.tqh_first;
+ TAILQ_REMOVE(&clnt_list, clp, list);
+ destroy_client(clp);
+ }
+}
+
+/* Used to read (and re-read) list of clients, set up poll array. */
+int
+update_client_list(void)
+{
+ struct dirent **namelist;
+ int i,j;
+
+ destroy_client_list();
+
+ if (chdir(pipefsdir) < 0) {
+ printerr(0, "ERROR: can't chdir to %s: %s\n",
+ pipefsdir, strerror(errno));
+ return -1;
+ }
+
+ memset(pollarray, 0, pollsize * sizeof(struct pollfd));
+
+ j = scandir(pipefsdir, &namelist, NULL, alphasort);
+ if (j < 0) {
+ printerr(0, "ERROR: can't scandir %s: %s\n",
+ pipefsdir, strerror(errno));
+ return -1;
+ }
+ for (i=0; i < j; i++) {
+ if (i < FD_ALLOC_BLOCK
+ && !strncmp(namelist[i]->d_name, "clnt", 4))
+ process_clnt_dir(namelist[i]->d_name);
+ free(namelist[i]);
+ }
+
+ free(namelist);
+ return 0;
+}
+
+static int
+do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
+ gss_buffer_desc *context_token)
+{
+ char buf[2048];
+ char *p = buf, *end = buf + 2048;
+ unsigned int timeout = 0; /* XXX decide on a reasonable value */
+
+ printerr(1, "doing downcall\n");
+
+ if (WRITE_BYTES(&p, end, uid)) return -1;
+ /* Not setting any timeout for now: */
+ if (WRITE_BYTES(&p, end, timeout)) return -1;
+ if (WRITE_BYTES(&p, end, pd->pd_seq_win)) return -1;
+ if (write_buffer(&p, end, &pd->pd_ctx_hndl)) return -1;
+ if (write_buffer(&p, end, context_token)) return -1;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) return -1;
+ return 0;
+}
+
+static int
+do_error_downcall(int k5_fd, uid_t uid, int err)
+{
+ char buf[1024];
+ char *p = buf, *end = buf + 1024;
+ unsigned int timeout = 0;
+ int zero = 0;
+
+ printerr(1, "doing error downcall\n");
+
+ if (WRITE_BYTES(&p, end, uid)) return -1;
+ if (WRITE_BYTES(&p, end, timeout)) return -1;
+ /* use seq_win = 0 to indicate an error: */
+ if (WRITE_BYTES(&p, end, zero)) return -1;
+ if (WRITE_BYTES(&p, end, err)) return -1;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) return -1;
+ return 0;
+
+}
+
+/*
+ * Create an RPC connection and establish an authenticated
+ * gss context with a server.
+ */
+int create_auth_rpc_client(struct clnt_info *clp,
+ AUTH **auth_return,
+ uid_t uid,
+ int authtype)
+{
+ CLIENT *rpc_clnt = NULL;
+ struct rpc_gss_sec sec;
+ AUTH *auth = NULL;
+ uid_t save_uid = -1;
+ int retval = -1;
+ OM_uint32 min_stat;
+
+ sec.qop = GSS_C_QOP_DEFAULT;
+ sec.svc = RPCSEC_GSS_SVC_NONE;
+ sec.cred = GSS_C_NO_CREDENTIAL;
+ sec.req_flags = 0;
+ if (authtype == AUTHTYPE_KRB5) {
+ sec.mech = (gss_OID)&krb5oid;
+ sec.req_flags = GSS_C_MUTUAL_FLAG;
+ }
+ else if (authtype == AUTHTYPE_SPKM3) {
+ sec.mech = (gss_OID)&spkm3oid;
+ sec.req_flags = GSS_C_ANON_FLAG;
+ }
+ else {
+ printerr(0, "ERROR: Invalid authentication type (%d) "
+ "in create_auth_rpc_client\n", authtype);
+ goto out_fail;
+ }
+
+
+ if (authtype == AUTHTYPE_KRB5) {
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ /*
+ * Do this before creating rpc connection since we won't need
+ * rpc connection if it fails!
+ */
+ if (limit_krb5_enctypes(&sec, uid)) {
+ printerr(1, "WARNING: Failed while limiting krb5 "
+ "encryption types for user with uid %d\n",
+ uid);
+ goto out_fail;
+ }
+#endif
+ }
+
+ /* Create the context as the user (not as root) */
+ save_uid = geteuid();
+ if (seteuid(uid) != 0) {
+ printerr(0, "WARNING: Failed to seteuid for "
+ "user with uid %d\n", uid);
+ goto out_fail;
+ }
+ printerr(2, "creating context using euid %d (save_uid %d)\n",
+ geteuid(), save_uid);
+
+ /* create an rpc connection to the nfs server */
+
+ printerr(2, "creating %s client for server %s\n", clp->protocol,
+ clp->servername);
+ if ((rpc_clnt = clnt_create(clp->servername, clp->prog, clp->vers,
+ clp->protocol)) == NULL) {
+ printerr(0, "WARNING: can't create rpc_clnt for server "
+ "%s for user with uid %d\n",
+ clp->servername, uid);
+ goto out_fail;
+ }
+
+ printerr(2, "creating context with server %s\n", clp->servicename);
+ auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
+ if (!auth) {
+ /* Our caller should print appropriate message */
+ printerr(2, "WARNING: Failed to create krb5 context for "
+ "user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_fail;
+ }
+
+ /* Restore euid to original value */
+ if (seteuid(save_uid) != 0) {
+ printerr(0, "WARNING: Failed to restore euid"
+ " to uid %d\n", save_uid);
+ goto out_fail;
+ }
+ save_uid = -1;
+
+ /* Success !!! */
+ *auth_return = auth;
+ retval = 0;
+
+ out_fail:
+ if (sec.cred != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&min_stat, &sec.cred);
+ if (rpc_clnt) clnt_destroy(rpc_clnt);
+
+ return retval;
+}
+
+
+/*
+ * this code uses the userland rpcsec gss library to create a krb5
+ * context on behalf of the kernel
+ */
+void
+handle_krb5_upcall(struct clnt_info *clp)
+{
+ uid_t uid;
+ AUTH *auth;
+ struct authgss_private_data pd;
+ gss_buffer_desc token;
+ char **credlist = NULL;
+ char **ccname;
+
+ printerr(1, "handling krb5 upcall\n");
+
+ token.length = 0;
+ token.value = NULL;
+
+ if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+ printerr(0, "WARNING: failed reading uid from krb5 "
+ "upcall pipe: %s\n", strerror(errno));
+ goto out;
+ }
+
+ if (uid == 0) {
+ int success = 0;
+
+ /*
+ * Get a list of credential cache names and try each
+ * of them until one works or we've tried them all
+ */
+ if (gssd_get_krb5_machine_cred_list(&credlist)) {
+ printerr(0, "WARNING: Failed to obtain machine "
+ "credentials for connection to "
+ "server %s\n", clp->servername);
+ goto out_return_error;
+ }
+ for (ccname = credlist; ccname && *ccname; ccname++) {
+ gssd_setup_krb5_machine_gss_ccache(*ccname);
+ if ((create_auth_rpc_client(clp, &auth, uid,
+ AUTHTYPE_KRB5)) == 0) {
+ /* Success! */
+ success++;
+ break;
+ }
+ printerr(2, "WARNING: Failed to create krb5 context "
+ "for user with uid %d with credentials "
+ "cache %s for server %s\n",
+ uid, *ccname, clp->servername);
+ }
+ gssd_free_krb5_machine_cred_list(credlist);
+ if (!success) {
+ printerr(0, "WARNING: Failed to create krb5 context "
+ "for user with uid %d with any "
+ "credentials cache for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+ }
+ else {
+ /* Tell krb5 gss which credentials cache to use */
+ gssd_setup_krb5_user_gss_ccache(uid, clp->servername);
+
+ if (create_auth_rpc_client(clp, &auth, uid, AUTHTYPE_KRB5)) {
+ printerr(0, "WARNING: Failed to create krb5 context "
+ "for user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+ }
+
+ if (!authgss_get_private_data(auth, &pd)) {
+ printerr(0, "WARNING: Failed to obtain authentication "
+ "data for user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+
+ if (serialize_context_for_kernel(pd.pd_ctx, &token)) {
+ printerr(0, "WARNING: Failed to serialize krb5 context for "
+ "user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+
+ do_downcall(clp->krb5_fd, uid, &pd, &token);
+
+ if (token.value)
+ free(token.value);
+out:
+ return;
+
+out_return_error:
+ do_error_downcall(clp->krb5_fd, uid, -1);
+ return;
+}
+
+/*
+ * this code uses the userland rpcsec gss library to create an spkm3
+ * context on behalf of the kernel
+ */
+void
+handle_spkm3_upcall(struct clnt_info *clp)
+{
+ uid_t uid;
+ AUTH *auth;
+ struct authgss_private_data pd;
+ gss_buffer_desc token;
+
+ printerr(2, "handling spkm3 upcall\n");
+
+ token.length = 0;
+ token.value = NULL;
+
+ if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+ printerr(0, "WARNING: failed reading uid from spkm3 "
+ "upcall pipe: %s\n", strerror(errno));
+ goto out;
+ }
+
+ if (create_auth_rpc_client(clp, &auth, uid, AUTHTYPE_SPKM3)) {
+ printerr(0, "WARNING: Failed to create spkm3 context for "
+ "user with uid %d\n", uid);
+ goto out_return_error;
+ }
+
+ if (!authgss_get_private_data(auth, &pd)) {
+ printerr(0, "WARNING: Failed to obtain authentication "
+ "data for user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+
+ if (serialize_context_for_kernel(pd.pd_ctx, &token)) {
+ printerr(0, "WARNING: Failed to serialize spkm3 context for "
+ "user with uid %d for server\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+
+ do_downcall(clp->spkm3_fd, uid, &pd, &token);
+
+ if (token.value)
+ free(token.value);
+out:
+ return;
+
+out_return_error:
+ do_error_downcall(clp->spkm3_fd, uid, -1);
+ return;
+}
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
new file mode 100644
index 0000000..2dcc2ee
--- /dev/null
+++ b/utils/gssd/krb5_util.c
@@ -0,0 +1,809 @@
+/*
+ * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ * Copyright (c) 2002-2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ * Kevin Coffman <kwc@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ krb5_util.c
+
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <time.h>
+#include <gssapi/gssapi.h>
+#ifdef USE_PRIVATE_KRB5_FUNCTIONS
+#include <gssapi/gssapi_krb5.h>
+#endif
+#include <krb5.h>
+#include <rpc/auth_gss.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "krb5_util.h"
+
+/* Global list of principals/cache file names for machine credentials */
+struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
+
+/*==========================*/
+/*=== Internal routines ===*/
+/*==========================*/
+
+static int select_krb5_ccache(const struct dirent *d);
+static int gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d);
+static int gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt, struct gssd_k5_kt_princ *ple);
+static int gssd_have_realm_ple(krb5_data *realm);
+static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
+ char *kt_name);
+
+/*
+ * Called from the scandir function to weed out potential krb5
+ * credentials cache files
+ *
+ * Returns:
+ * 0 => don't select this one
+ * 1 => select this one
+ */
+static int
+select_krb5_ccache(const struct dirent *d)
+{
+ /* Don't consider anything but regular files. (No symlinks, etc.) */
+ if (d->d_type != DT_REG)
+ return 0;
+
+ if (strstr(d->d_name, GSSD_DEFAULT_CRED_PREFIX))
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Look in the GSSD_DEFAULT_CRED_DIR for files that look like they
+ * are Kerberos Credential Cache files for a given UID. Return
+ * non-zero and the dirent pointer for the entry most likely to be
+ * what we want. Otherwise, return zero and no dirent pointer.
+ * The caller is responsible for freeing the dirent if one is returned.
+ *
+ * Returns:
+ * 0 => could not find an existing entry
+ * 1 => found an existing entry
+ */
+static int
+gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d)
+{
+ struct dirent **namelist;
+ int n;
+ int i;
+ int found = 0;
+ struct dirent *best_match_dir = NULL;
+ struct stat best_match_stat, tmp_stat;
+
+ *d = NULL;
+ n = scandir(GSSD_DEFAULT_CRED_DIR, &namelist, select_krb5_ccache, 0);
+ if (n < 0) {
+ perror("scandir looking for krb5 credentials caches");
+ }
+ else if (n > 0) {
+ char substring[128];
+ char statname[1024];
+ snprintf(substring, sizeof(substring), "_%d", uid);
+ for (i = 0; i < n; i++) {
+ printerr(3, "CC file '%s' being considered\n",
+ namelist[i]->d_name);
+ if (strstr(namelist[i]->d_name, substring)) {
+ snprintf(statname, sizeof(statname),
+ "%s/%s", GSSD_DEFAULT_CRED_DIR,
+ namelist[i]->d_name);
+ if (stat(statname, &tmp_stat)) {
+ printerr(0, "Error doing stat "
+ "on file '%s'\n",
+ statname);
+ continue;
+ }
+ printerr(3, "CC file '%s' matches "
+ "name check and has "
+ "mtime of %u\n",
+ namelist[i]->d_name,
+ tmp_stat.st_mtime);
+ /* if more than one match is found,
+ * return the most recent (the one
+ * with the latest mtime),
+ * and don't free the dirent */
+ if (!found) {
+ best_match_dir = namelist[i];
+ best_match_stat = tmp_stat;
+ found++;
+ }
+ else {
+ /*
+ * If the current match has
+ * an mtime later than the
+ * one we are looking at,
+ * then use the current match.
+ * Otherwise, we still have
+ * the best match.
+ */
+ if (tmp_stat.st_mtime >
+ best_match_stat.st_mtime) {
+ free(best_match_dir);
+ best_match_dir = namelist[i];
+ best_match_stat = tmp_stat;
+ }
+ else {
+ free(namelist[i]);
+ }
+ printerr(3, "CC file '%s' is our "
+ "current best match "
+ "with mtime of %u\n",
+ best_match_dir->d_name,
+ best_match_stat.st_mtime);
+ }
+ }
+ else
+ free(namelist[i]);
+ }
+ free(namelist);
+ }
+ if (found)
+ {
+ *d = best_match_dir;
+ }
+ return found;
+}
+
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+/*
+ * this routine obtains a credentials handle via gss_acquire_cred()
+ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
+ * types negotiated.
+ *
+ * XXX Should call some function to determine the enctypes supported
+ * by the kernel. (Only need to do that once!)
+ *
+ * Returns:
+ * 0 => all went well
+ * -1 => there was an error
+ */
+
+int
+limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
+{
+ u_int maj_stat, min_stat;
+ gss_cred_id_t credh;
+/* krb5_enctype enctypes[] = {ENCTYPE_DES3_CBC_SHA1};
+ ENCTYPE_ARCFOUR_HMAC, */
+ krb5_enctype enctypes[] = {ENCTYPE_DES3_CBC_SHA1,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_CRC};
+ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
+
+ maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
+ GSS_C_NULL_OID_SET, GSS_C_INITIATE,
+ &credh, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "WARNING: error from gss_acquire_cred "
+ "for user with uid %d (%s)\n",
+ uid, error_message(min_stat));
+ return -1;
+ }
+
+ maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid,
+ num_enctypes, &enctypes);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "WARNING: error from gss_set_allowable_enctypes "
+ "for user with uid %d (%s)\n",
+ uid, error_message(min_stat));
+ return -1;
+ }
+ sec->cred = credh;
+
+ return 0;
+}
+#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
+
+/*
+ * Obtain credentials via a key in the keytab given
+ * a keytab handle and a gssd_k5_kt_princ structure.
+ * Checks to see if current credentials are expired,
+ * if not, uses the keytab to obtain new credentials.
+ *
+ * Returns:
+ * 0 => success (or credentials have not expired)
+ * nonzero => error
+ */
+static int
+gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt,
+ struct gssd_k5_kt_princ *ple)
+{
+ krb5_get_init_creds_opt options;
+ krb5_creds my_creds;
+ krb5_ccache ccache = NULL;
+ char kt_name[BUFSIZ];
+ char cc_name[BUFSIZ];
+ int code;
+ time_t now = time(0);
+
+ memset(&my_creds, 0, sizeof(my_creds));
+
+ if (ple->ccname && ple->endtime > now) {
+ printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
+ ple->ccname, ple->endtime);
+ code = 0;
+ goto out;
+ }
+
+ if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
+ printerr(0, "ERROR: Unable to get keytab name in "
+ "gssd_get_single_krb5_cred\n");
+ goto out;
+ }
+
+ krb5_get_init_creds_opt_init(&options);
+ krb5_get_init_creds_opt_set_address_list(&options, NULL);
+
+#ifdef TEST_SHORT_LIFETIME
+ /* set a short lifetime (for debugging only!) */
+ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
+ krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
+#endif
+ if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+ kt, 0, 0, &options))) {
+ char *pname;
+ if ((krb5_unparse_name(context, ple->princ, &pname))) {
+ pname = NULL;
+ }
+ printerr(0, "WARNING: %s while getting initial ticket for "
+ "principal '%s' from keytab '%s'\n",
+ error_message(code),
+ pname ? pname : "<unparsable>", kt_name);
+ if (pname) krb5_free_unparsed_name(context, pname);
+ goto out;
+ }
+
+ /*
+ * Initialize cache file which we're going to be using
+ */
+
+ snprintf(cc_name, sizeof(cc_name), "FILE:%s/%s%s_%s",
+ GSSD_DEFAULT_CRED_DIR, GSSD_DEFAULT_CRED_PREFIX,
+ GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
+ ple->endtime = my_creds.times.endtime;
+ ple->ccname = strdup(cc_name);
+ if (ple->ccname == NULL) {
+ printerr(0, "ERROR: no storage to duplicate credentials "
+ "cache name\n");
+ code = ENOMEM;
+ goto out;
+ }
+ if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
+ printerr(0, "ERROR: %s while opening credential cache '%s'\n",
+ error_message(code), cc_name);
+ goto out;
+ }
+ if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
+ printerr(0, "ERROR: %s while initializing credential "
+ "cache '%s'\n", error_message(code), cc_name);
+ goto out;
+ }
+ if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
+ printerr(0, "ERROR: %s while storing credentials in '%s'\n",
+ error_message(code), cc_name);
+ goto out;
+ }
+
+ code = 0;
+ printerr(1, "Using (machine) credentials cache: '%s'\n", cc_name);
+ out:
+ if (ccache)
+ krb5_cc_close(context, ccache);
+ krb5_free_cred_contents(context, &my_creds);
+ return (code);
+}
+
+/*
+ * Determine if we already have a ple for the given realm
+ *
+ * Returns:
+ * 0 => no ple found for given realm
+ * 1 => found ple for given realm
+ */
+static int
+gssd_have_realm_ple(krb5_data *realm)
+{
+ struct gssd_k5_kt_princ *ple;
+
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if ((realm->length == strlen(ple->realm)) &&
+ (strncmp(realm->data, ple->realm, realm->length) == 0)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Process the given keytab file and create a list of principals we
+ * might use to perform mount operations.
+ *
+ * Returns:
+ * 0 => Sucess
+ * nonzero => Error
+ */
+static int
+gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, char *kt_name)
+{
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry kte;
+ krb5_error_code code;
+ struct gssd_k5_kt_princ *ple;
+ int retval = -1;
+
+ /*
+ * Look through each entry in the keytab file and determine
+ * if we might want to use it later to do a mount. If so,
+ * save info in the global principal list
+ * (gssd_k5_kt_princ_list).
+ * Note: (ple == principal list entry)
+ */
+ if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
+ printerr(0, "ERROR: %s while beginning keytab scan "
+ "for keytab '%s'\n",
+ error_message(code), kt_name);
+ retval = code;
+ goto out;
+ }
+
+ while ((code = krb5_kt_next_entry(context, kt, &kte, &cursor)) == 0) {
+ char *pname;
+ if ((code = krb5_unparse_name(context, kte.principal,
+ &pname))) {
+ printerr(0, "WARNING: Skipping keytab entry because "
+ "we failed to unparse principal name: %s\n",
+ error_message(code));
+ continue;
+ }
+ printerr(2, "Processing keytab entry for principal '%s'\n",
+ pname);
+ if ( (kte.principal->data[0].length == GSSD_SERVICE_NAME_LEN) &&
+ (strncmp(kte.principal->data[0].data, GSSD_SERVICE_NAME,
+ GSSD_SERVICE_NAME_LEN) == 0) &&
+ (!gssd_have_realm_ple(&kte.principal->realm)) ) {
+ printerr(2, "We will use this entry (%s)\n", pname);
+ ple = malloc(sizeof(struct gssd_k5_kt_princ));
+ if (ple == NULL) {
+ printerr(0, "ERROR: could not allocate storage "
+ "for principal list entry\n");
+ krb5_free_unparsed_name(context, pname);
+ retval = ENOMEM;
+ goto out;
+ }
+ /* These will be filled in later */
+ ple->next = NULL;
+ ple->ccname = NULL;
+ ple->endtime = 0;
+ if ((ple->realm =
+ strndup(kte.principal->realm.data,
+ kte.principal->realm.length))
+ == NULL) {
+ printerr(0, "ERROR: %s while copying realm to "
+ "principal list entry\n",
+ "not enough memory");
+ krb5_free_unparsed_name(context, pname);
+ retval = ENOMEM;
+ goto out;
+ }
+ if ((code = krb5_copy_principal(context,
+ kte.principal, &ple->princ))) {
+ printerr(0, "ERROR: %s while copying principal "
+ "to principal list entry\n",
+ error_message(code));
+ krb5_free_unparsed_name(context, pname);
+ retval = code;
+ goto out;
+ }
+ if (gssd_k5_kt_princ_list == NULL)
+ gssd_k5_kt_princ_list = ple;
+ else {
+ ple->next = gssd_k5_kt_princ_list;
+ gssd_k5_kt_princ_list = ple;
+ }
+ }
+ else {
+ printerr(2, "We will NOT use this entry (%s)\n",
+ pname);
+ }
+ krb5_free_unparsed_name(context, pname);
+ }
+
+ if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
+ printerr(0, "WARNING: %s while ending keytab scan for "
+ "keytab '%s'\n",
+ error_message(code), kt_name);
+ }
+
+ retval = 0;
+ out:
+ return retval;
+}
+
+
+/*==========================*/
+/*=== External routines ===*/
+/*==========================*/
+
+/*
+ * Attempt to find the best match for a credentials cache file
+ * given only a UID. We really need more information, but we
+ * do the best we can.
+ *
+ * Returns:
+ * void
+ */
+void
+gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername)
+{
+ char buf[MAX_NETOBJ_SZ];
+#ifdef HAVE_GSS_KRB5_CCACHE_NAME
+ u_int min_stat;
+#endif
+ struct dirent *d;
+
+ printerr(2, "getting credentials for client with uid %u for "
+ "server %s\n", uid, servername);
+ memset(buf, 0, sizeof(buf));
+ if (gssd_find_existing_krb5_ccache(uid, &d)) {
+ snprintf(buf, sizeof(buf), "FILE:%s/%s",
+ GSSD_DEFAULT_CRED_DIR, d->d_name);
+ free(d);
+ }
+ else
+ snprintf(buf, sizeof(buf), "FILE:%s/%s%u",
+ GSSD_DEFAULT_CRED_DIR,
+ GSSD_DEFAULT_CRED_PREFIX, uid);
+ printerr(2, "using %s as credentials cache for client with "
+ "uid %u for server %s\n", buf, uid, servername);
+#ifdef HAVE_GSS_KRB5_CCACHE_NAME
+ gss_krb5_ccache_name(&min_stat, buf, NULL);
+#else
+ /*
+ * Set the KRB5CCNAME environment variable to tell the krb5 code
+ * which credentials cache to use. (Instead of using the private
+ * function above for which there is no generic gssapi
+ * equivalent.)
+ */
+ setenv("KRB5CCNAME", buf, 1);
+#endif
+}
+
+/*
+ * Let the gss code know where to find the machine credentials ccache.
+ *
+ * Returns:
+ * void
+ */
+void
+gssd_setup_krb5_machine_gss_ccache(char *ccname)
+{
+#ifdef HAVE_GSS_KRB5_CCACHE_NAME
+ u_int min_stat;
+#endif
+ printerr(2, "using %s as credentials cache for machine creds\n",
+ ccname);
+#ifdef HAVE_GSS_KRB5_CCACHE_NAME
+ gss_krb5_ccache_name(&min_stat, ccname, NULL);
+#else
+ /*
+ * Set the KRB5CCNAME environment variable to tell the krb5 code
+ * which credentials cache to use. (Instead of using the private
+ * function above for which there is no generic gssapi
+ * equivalent.)
+ */
+ setenv("KRB5CCNAME", ccname, 1);
+#endif
+}
+
+/*
+ * The first time through this routine, go through the keytab and
+ * determine which keys we will try to use as machine credentials.
+ * Every time through this routine, try to obtain credentials using
+ * the keytab entries selected the first time through.
+ *
+ * Returns:
+ * 0 => obtained one or more credentials
+ * nonzero => error
+ *
+ */
+
+int
+gssd_refresh_krb5_machine_creds(void)
+{
+ krb5_context context = NULL;
+ krb5_keytab kt = NULL;;
+ krb5_error_code code;
+ int retval = -1;
+ struct gssd_k5_kt_princ *ple;
+ int gotone = 0;
+ static int processed_keytab = 0;
+
+
+ code = krb5_init_context(&context);
+ if (code) {
+ printerr(0, "ERROR: %s while initializing krb5 in "
+ "gssd_refresh_krb5_machine_creds\n",
+ error_message(code));
+ retval = code;
+ goto out;
+ }
+
+ printerr(1, "Using keytab file '%s'\n", keytabfile);
+
+ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+ printerr(0, "ERROR: %s while resolving keytab '%s'\n",
+ error_message(code), keytabfile);
+ goto out;
+ }
+
+ /* Only go through the keytab file once. Only print messages once. */
+ if (gssd_k5_kt_princ_list == NULL && !processed_keytab) {
+ processed_keytab = 1;
+ gssd_process_krb5_keytab(context, kt, keytabfile);
+ if (gssd_k5_kt_princ_list == NULL) {
+ printerr(0, "ERROR: No usable keytab entries found in "
+ "keytab '%s'\n", keytabfile);
+ printerr(0, "Do you have a valid keytab entry for "
+ "%s/<your.host>@<YOUR.REALM> in "
+ "keytab file %s ?\n",
+ GSSD_SERVICE_NAME, keytabfile);
+ printerr(0, "Continuing without (machine) credentials "
+ "- nfs4 mounts with Kerberos will fail\n");
+ }
+ }
+
+ /*
+ * If we don't have any keytab entries we liked, then we have a problem
+ */
+ if (gssd_k5_kt_princ_list == NULL) {
+ retval = ENOENT;
+ goto out;
+ }
+
+ /*
+ * Now go through the list of saved entries and get initial
+ * credentials for them (We can't do this while making the
+ * list because it messes up the keytab iteration cursor
+ * when we use the keytab to get credentials.)
+ */
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if ((gssd_get_single_krb5_cred(context, kt, ple)) == 0) {
+ gotone++;
+ }
+ }
+ if (!gotone) {
+ printerr(0, "ERROR: No usable machine credentials obtained\n");
+ goto out;
+ }
+
+ retval = 0;
+ out:
+ if (kt) krb5_kt_close(context, kt);
+ krb5_free_context(context);
+
+ return retval;
+}
+
+
+/*
+ * Return an array of pointers to names of credential cache files
+ * which can be used to try to create gss contexts with a server.
+ *
+ * Returns:
+ * 0 => list is attached
+ * nonzero => error
+ */
+int
+gssd_get_krb5_machine_cred_list(char ***list)
+{
+ char **l;
+ int listinc = 10;
+ int listsize = listinc;
+ int i = 0;
+ int retval;
+ struct gssd_k5_kt_princ *ple;
+
+ /* Assume failure */
+ retval = -1;
+ *list = (char **) NULL;
+
+ /* Refresh machine credentials */
+ if ((retval = gssd_refresh_krb5_machine_creds())) {
+ goto out;
+ }
+
+ if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if (ple->ccname) {
+ if (i + 1 > listsize) {
+ listsize += listinc;
+ l = (char **)
+ realloc(l, listsize * sizeof(char *));
+ if (l == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ }
+ if ((l[i++] = strdup(ple->ccname)) == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ }
+ }
+ if (i > 0) {
+ l[i] = NULL;
+ *list = l;
+ retval = 0;
+ goto out;
+ }
+ out:
+ return retval;
+}
+
+/*
+ * Frees the list of names returned in get_krb5_machine_cred_list()
+ */
+void
+gssd_free_krb5_machine_cred_list(char **list)
+{
+ char **n;
+
+ if (list == NULL)
+ return;
+ for (n = list; n && *n; n++) {
+ free(*n);
+ }
+ free(list);
+}
+
+/*
+ * Called upon exit. Destroys machine credentials.
+ */
+void
+gssd_destroy_krb5_machine_creds(void)
+{
+ krb5_context context;
+ krb5_error_code code = 0;
+ krb5_ccache ccache;
+ struct gssd_k5_kt_princ *ple;
+
+ code = krb5_init_context(&context);
+ if (code) {
+ printerr(0, "ERROR: %s while initializing krb5\n",
+ error_message(code));
+ goto out;
+ }
+
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if (!ple->ccname)
+ continue;
+ if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
+ printerr(0, "WARNING: %s while resolving credential "
+ "cache '%s' for destruction\n",
+ error_message(code), ple->ccname);
+ continue;
+ }
+
+ if ((code = krb5_cc_destroy(context, ccache))) {
+ printerr(0, "WARNING: %s while destroying credential "
+ "cache '%s'\n",
+ error_message(code), ple->ccname);
+ }
+ }
+ out:
+ krb5_free_context(context);
+}
+
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
new file mode 100644
index 0000000..da04530
--- /dev/null
+++ b/utils/gssd/krb5_util.h
@@ -0,0 +1,30 @@
+#ifndef KRB5_UTIL_H
+#define KRB5_UTIL_H
+
+#include <krb5.h>
+
+/*
+ * List of principals from our keytab that we
+ * may try to get credentials for
+ */
+struct gssd_k5_kt_princ {
+ struct gssd_k5_kt_princ *next;
+ krb5_principal princ;
+ char *ccname;
+ char *realm;
+ krb5_timestamp endtime;
+};
+
+
+void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
+int gssd_get_krb5_machine_cred_list(char ***list);
+int gssd_refresh_krb5_machine_creds(void);
+void gssd_free_krb5_machine_cred_list(char **list);
+void gssd_setup_krb5_machine_gss_ccache(char *servername);
+void gssd_destroy_krb5_machine_creds(void);
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid);
+#endif
+
+#endif /* KRB5_UTIL_H */
diff --git a/utils/gssd/write_bytes.h b/utils/gssd/write_bytes.h
new file mode 100644
index 0000000..ba00598
--- /dev/null
+++ b/utils/gssd/write_bytes.h
@@ -0,0 +1,139 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _WRITE_BYTES_H_
+#define _WRITE_BYTES_H_
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h> /* for ntohl */
+
+inline static int
+write_bytes(char **ptr, const char *end, const void *arg, int arg_len)
+{
+ char *p = *ptr, *arg_end;
+
+ arg_end = p + arg_len;
+ if (arg_end > end || arg_end < p)
+ return -1;
+ memcpy(p, arg, arg_len);
+ *ptr = arg_end;
+ return 0;
+}
+
+#define WRITE_BYTES(p, end, arg) write_bytes(p, end, &arg, sizeof(arg))
+
+inline static int
+write_buffer(char **p, char *end, gss_buffer_desc *arg)
+{
+ if (WRITE_BYTES(p, end, arg->length))
+ return -1;
+ if (*p + arg->length > end)
+ return -1;
+ memcpy(*p, arg->value, arg->length);
+ *p += arg->length;
+ return 0;
+}
+
+static inline int
+get_bytes(char **ptr, const char *end, void *res, int len)
+{
+ char *p, *q;
+ p = *ptr;
+ q = p + len;
+ if (q > end || q < p)
+ return -1;
+ memcpy(res, p, len);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+get_buffer(char **ptr, const char *end, gss_buffer_desc *res)
+{
+ char *p, *q;
+ p = *ptr;
+ if (get_bytes(&p, end, &res->length, sizeof(res->length)))
+ return -1;
+ q = p + res->length;
+ if (q > end || q < p)
+ return -1;
+ if (!(res->value = malloc(res->length)))
+ return -1;
+ memcpy(res->value, p, res->length);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+xdr_get_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t *res)
+{
+ if (get_bytes((char **)ptr, (char *)end, res, sizeof(res)))
+ return -1;
+ *res = ntohl(*res);
+ return 0;
+}
+
+static inline int
+xdr_get_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *res)
+{
+ u_int32_t *p, *q;
+ p = *ptr;
+ if (xdr_get_u32(&p, end, &res->length))
+ return -1;
+ q = p + ((res->length + 3) >> 2);
+ if (q > end || q < p)
+ return -1;
+ if (!(res->value = malloc(res->length)))
+ return -1;
+ memcpy(res->value, p, res->length);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+xdr_write_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t arg)
+{
+ u_int32_t tmp;
+
+ tmp = htonl(arg);
+ return WRITE_BYTES((char **)ptr, (char *)end, tmp);
+}
+
+static inline int
+xdr_write_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *arg)
+{
+ if (xdr_write_u32(ptr, end, arg->length))
+ return -1;
+ return write_bytes((char **)ptr, (char *)end, arg->value,
+ (arg->length + 3) & ~3);
+}
+
+#endif /* _WRITE_BYTES_H_ */
diff --git a/utils/gssdestroycreds/Makefile b/utils/gssdestroycreds/Makefile
new file mode 100644
index 0000000..266e9de
--- /dev/null
+++ b/utils/gssdestroycreds/Makefile
@@ -0,0 +1,14 @@
+#
+# gss context destruction tool
+#
+
+PROGRAM = gss_clnt_send_err
+OBJS = gss_clnt_send_err.o
+
+include $(TOP)rules.mk
+
+gss_clnt_send_err.o: ../gssd/gss_clnt_send_err.c
+ $(CC) -c $(CFLAGS) $(?:.o=.c)
+
+install::
+ $(INSTALLBIN) ../gssd/gss_destroy_creds $(SBINDIR)/$(PREFIX)$kgss_destroy_creds
diff --git a/utils/svcgssd/Makefile b/utils/svcgssd/Makefile
new file mode 100644
index 0000000..ed3fa98
--- /dev/null
+++ b/utils/svcgssd/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for rpc.gssd
+#
+
+PROGRAM = svcgssd
+PREFIX = rpc.
+OBJS = svcgssd.o svcgssd_main_loop.o svcgssd_proc.o err_util.o gss_util.o \
+ gss_oids.o context.o context_heimdal.o cacheio.o svcgssd_mech2file.o
+LIBDEPS = $(TOP)support/lib/librpc.a $(TOP)support/lib/libgssapi.a
+LIBS = -Wl,-rpath=$(KRBDIR)/lib -lrpc -lgssapi -ldl $(KRBLIB)
+MAN8 = svcgssd
+
+predep ::
+ - ln ../gssd/err_util.c
+ - ln ../gssd/gss_util.c
+ - ln ../gssd/gss_oids.c
+ - ln ../gssd/context.c
+ - ln ../gssd/context_heimdal.c
+
+include $(TOP)rules.mk
+
+CFLAGS += -I../gssd -DKRB5_VERSION=$(KRB5_VERSION) \
+ -I$(TOP)support/rpc/include/ -I$(KRBDIR)/include
diff --git a/utils/svcgssd/cacheio.c b/utils/svcgssd/cacheio.c
new file mode 100644
index 0000000..ac76c06
--- /dev/null
+++ b/utils/svcgssd/cacheio.c
@@ -0,0 +1,289 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * support/nfs/cacheio.c
+ * support IO on the cache channel files in 2.5 and beyond.
+ * These use 'qwords' which are like words, but with a little quoting.
+ *
+ */
+
+
+/*
+ * Support routines for text-based upcalls.
+ * Fields are separated by spaces.
+ * Fields are either mangled to quote space tab newline slosh with slosh
+ * or a hexified with a leading \x
+ * Record is terminated with newline.
+ *
+ */
+
+#include "cacheio.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include "err_util.h"
+
+void qword_add(char **bpp, int *lp, char *str)
+{
+ char *bp = *bpp;
+ int len = *lp;
+ char c;
+
+ if (len < 0) return;
+
+ while ((c=*str++) && len)
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\\':
+ if (len >= 4) {
+ *bp++ = '\\';
+ *bp++ = '0' + ((c & 0300)>>6);
+ *bp++ = '0' + ((c & 0070)>>3);
+ *bp++ = '0' + ((c & 0007)>>0);
+ }
+ len -= 4;
+ break;
+ default:
+ *bp++ = c;
+ len--;
+ }
+ if (c || len <1) len = -1;
+ else {
+ *bp++ = ' ';
+ len--;
+ }
+ *bpp = bp;
+ *lp = len;
+}
+
+void qword_addhex(char **bpp, int *lp, char *buf, int blen)
+{
+ char *bp = *bpp;
+ int len = *lp;
+
+ if (len < 0) return;
+
+ if (len > 2) {
+ *bp++ = '\\';
+ *bp++ = 'x';
+ len -= 2;
+ while (blen && len >= 2) {
+ unsigned char c = *buf++;
+ *bp++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
+ *bp++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
+ len -= 2;
+ blen--;
+ }
+ }
+ if (blen || len<1) len = -1;
+ else {
+ *bp++ = ' ';
+ len--;
+ }
+ *bpp = bp;
+ *lp = len;
+}
+
+void qword_addint(char **bpp, int *lp, int n)
+{
+ int len;
+
+ len = snprintf(*bpp, *lp, "%d ", n);
+ if (len > *lp)
+ len = *lp;
+ *bpp += len;
+ *lp -= len;
+}
+
+void qword_addeol(char **bpp, int *lp)
+{
+ if (*lp <= 0)
+ return;
+ **bpp = '\n';
+ (*bpp)++;
+ (*lp)--;
+}
+
+static char qword_buf[8192];
+static char tmp_buf[8192];
+void qword_print(FILE *f, char *str)
+{
+ char *bp = qword_buf;
+ int len = sizeof(qword_buf);
+ qword_add(&bp, &len, str);
+ fwrite(qword_buf, bp-qword_buf, 1, f);
+ /* XXX: */
+ memcpy(tmp_buf, qword_buf, bp-qword_buf);
+ tmp_buf[bp-qword_buf] = '\0';
+ printerr(2, "%s", tmp_buf);
+}
+
+void qword_printhex(FILE *f, char *str, int slen)
+{
+ char *bp = qword_buf;
+ int len = sizeof(qword_buf);
+ qword_addhex(&bp, &len, str, slen);
+ fwrite(qword_buf, bp-qword_buf, 1, f);
+ /* XXX: */
+ memcpy(tmp_buf, qword_buf, bp-qword_buf);
+ tmp_buf[bp-qword_buf] = '\0';
+ printerr(2, "%s", tmp_buf);
+}
+
+void qword_printint(FILE *f, int num)
+{
+ fprintf(f, "%d ", num);
+ printerr(2, "%d ", num);
+}
+
+void qword_eol(FILE *f)
+{
+ fprintf(f,"\n");
+ fflush(f);
+ printerr(2, "\n");
+}
+
+
+
+#define isodigit(c) (isdigit(c) && c <= '7')
+int qword_get(char **bpp, char *dest, int bufsize)
+{
+ /* return bytes copied, or -1 on error */
+ char *bp = *bpp;
+ int len = 0;
+
+ while (*bp == ' ') bp++;
+
+ if (bp[0] == '\\' && bp[1] == 'x') {
+ /* HEX STRING */
+ bp += 2;
+ while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) {
+ int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
+ bp++;
+ byte <<= 4;
+ byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
+ *dest++ = byte;
+ bp++;
+ len++;
+ }
+ } else {
+ /* text with \nnn octal quoting */
+ while (*bp != ' ' && *bp != '\n' && *bp && len < bufsize-1) {
+ if (*bp == '\\' &&
+ isodigit(bp[1]) && (bp[1] <= '3') &&
+ isodigit(bp[2]) &&
+ isodigit(bp[3])) {
+ int byte = (*++bp -'0');
+ bp++;
+ byte = (byte << 3) | (*bp++ - '0');
+ byte = (byte << 3) | (*bp++ - '0');
+ *dest++ = byte;
+ len++;
+ } else {
+ *dest++ = *bp++;
+ len++;
+ }
+ }
+ }
+
+ if (*bp != ' ' && *bp != '\n' && *bp != '\0')
+ return -1;
+ while (*bp == ' ') bp++;
+ *bpp = bp;
+ *dest = '\0';
+ return len;
+}
+
+int qword_get_int(char **bpp, int *anint)
+{
+ char buf[50];
+ char *ep;
+ int rv;
+ int len = qword_get(bpp, buf, 50);
+ if (len < 0) return -1;
+ if (len ==0) return -1;
+ rv = strtol(buf, &ep, 0);
+ if (*ep) return -1;
+ *anint = rv;
+ return 0;
+}
+
+int readline(int fd, char **buf, int *lenp)
+{
+ /* read a line into *buf, which is malloced *len long
+ * realloc if needed until we find a \n
+ * nul out the \n and return
+ * 0 of eof, 1 of success
+ */
+ int len;
+
+ if (*lenp == 0) {
+ char *b = malloc(128);
+ if (b == NULL)
+ return 0;
+ *buf = b;
+ *lenp = 128;
+ }
+ len = read(fd, *buf, *lenp);
+ if (len <= 0) {
+ printerr(2, "read error in readline: %d\n", len);
+ return 0;
+ }
+ while ((*buf)[len-1] != '\n') {
+ /* now the less common case. There was no newline,
+ * so we have to keep reading after re-alloc
+ */
+ char *new;
+ int nl;
+ *lenp += 128;
+ new = realloc(*buf, *lenp);
+ if (new == NULL)
+ return 0;
+ *buf = new;
+ nl = read(fd, *buf +len, *lenp - len);
+ if (nl <= 0 ) {
+ printerr(2, "read error in readline: %d\n", nl);
+ return 0;
+ }
+ len += nl;
+ }
+ (*buf)[len-1] = 0;
+ printerr(1, "read line with %d characters:\n%s\n", *lenp, *buf);
+ return 1;
+}
diff --git a/utils/svcgssd/cacheio.h b/utils/svcgssd/cacheio.h
new file mode 100644
index 0000000..cc97b36
--- /dev/null
+++ b/utils/svcgssd/cacheio.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _CACHEIO_H_
+#define _CACHEIO_H_
+
+#include <stdio.h>
+
+void qword_add(char **bpp, int *lp, char *str);
+void qword_addhex(char **bpp, int *lp, char *buf, int blen);
+void qword_addint(char **bpp, int *lp, int n);
+void qword_addeol(char **bpp, int *lp);
+void qword_print(FILE *f, char *str);
+void qword_printhex(FILE *f, char *str, int slen);
+void qword_printint(FILE *f, int num);
+void qword_eol(FILE *f);
+int readline(int fd, char **buf, int *lenp);
+int qword_get(char **bpp, char *dest, int bufsize);
+int qword_get_int(char **bpp, int *anint);
+
+#endif /* _CACHEIO_H_ */
diff --git a/utils/svcgssd/svcgssd.c b/utils/svcgssd/svcgssd.c
new file mode 100644
index 0000000..9dd5a3a
--- /dev/null
+++ b/utils/svcgssd/svcgssd.c
@@ -0,0 +1,209 @@
+/*
+ gssd.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ Copyright (c) 2002 J. Bruce Fields <bfields@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+
+/*
+ * mydaemon creates a pipe between the partent and child
+ * process. The parent process will wait until the
+ * child dies or writes a '1' on the pipe signaling
+ * that it started successfully.
+ */
+int pipefds[2] = { -1, -1};
+
+static void
+mydaemon(int nochdir, int noclose)
+{
+ int pid, status, tempfd, fdmax, filedes;
+
+ if (pipe(pipefds) < 0) {
+ printerr(1, "mydaemon: pipe() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+ if ((pid = fork ()) < 0) {
+ printerr(1, "mydaemon: fork() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+
+ if (pid != 0) {
+ /*
+ * Parent. Wait for status from child.
+ */
+ close(pipefds[1]);
+ if (read(pipefds[0], &status, 1) != 1)
+ exit(1);
+ exit (0);
+ }
+ /* Child. */
+ close(pipefds[0]);
+ setsid ();
+ if (nochdir == 0) {
+ if (chdir ("/") == -1) {
+ printerr(1, "mydaemon: chdir() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+ }
+
+ while (pipefds[1] <= 2) {
+ pipefds[1] = dup(pipefds[1]);
+ if (pipefds[1] < 0) {
+ printerr(1, "mydaemon: dup() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (noclose == 0) {
+ tempfd = open("/dev/null", O_RDWR);
+ close(0); dup2(tempfd, 0);
+ close(1); dup2(tempfd, 1);
+ close(2); dup2(tempfd, 2);
+ fdmax = sysconf (_SC_OPEN_MAX);
+ for (filedes = 3; filedes < fdmax; filedes++)
+ if (filedes != pipefds[1])
+ close (filedes);
+ }
+
+ return;
+}
+
+static void
+release_parent()
+{
+ int status;
+
+ if (pipefds[1] > 0) {
+ write(pipefds[1], &status, 1);
+ close(pipefds[1]);
+ pipefds[1] = -1;
+ }
+}
+
+void
+sig_die(int signal)
+{
+ /* destroy krb5 machine creds */
+ printerr(1, "exiting on signal %d\n", signal);
+ exit(1);
+}
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s [-n] [-f] [-v]\n",
+ progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int get_creds = 1;
+ int fg = 0;
+ int verbosity = 0;
+ int opt;
+ extern char *optarg;
+ char *progname;
+
+ while ((opt = getopt(argc, argv, "fvnp:")) != -1) {
+ switch (opt) {
+ case 'f':
+ fg = 1;
+ break;
+ case 'n':
+ get_creds = 0;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+
+ if (!fg)
+ mydaemon(0, 0);
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ signal(SIGHUP, sig_die);
+
+ if (get_creds && !gssd_acquire_cred(GSSD_SERVICE_NAME)) {
+ printerr(0, "unable to obtain root (machine) credentials\n");
+ printerr(0, "do you have a keytab entry for "
+ "nfs/<your.host>@<YOUR.REALM> in "
+ "/etc/krb5.keytab?\n");
+ exit(1);
+ }
+
+ if (!fg)
+ release_parent();
+
+ gssd_run();
+ printerr(0, "gssd_run returned!\n");
+ abort();
+}
diff --git a/utils/svcgssd/svcgssd.h b/utils/svcgssd/svcgssd.h
new file mode 100644
index 0000000..9a2e2e8
--- /dev/null
+++ b/utils/svcgssd/svcgssd.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RPC_SVCGSSD_H_
+#define _RPC_SVCGSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+
+void handle_nullreq(FILE *f);
+void gssd_run(void);
+
+#define GSSD_SERVICE_NAME "nfs"
+
+#endif /* _RPC_SVCGSSD_H_ */
diff --git a/utils/svcgssd/svcgssd.man b/utils/svcgssd/svcgssd.man
new file mode 100644
index 0000000..f17f772
--- /dev/null
+++ b/utils/svcgssd/svcgssd.man
@@ -0,0 +1,41 @@
+.\"
+.\" rpc.svcgssd(8)
+.\"
+.\" Copyright (C) 2003 J. Bruce Fields <bfields@umich.edu>
+.TH rpc.svcgssd 8 "17 Mar 2003"
+.SH NAME
+rpc.svcgssd \- server-side rpcsec_gss daemon
+.SH SYNOPSIS
+.B "rpc.svcgssd [-v] [-f] [-p pipefsdir]"
+.SH DESCRIPTION
+The rpcsec_gss protocol gives a means of using the gss-api generic security
+api to provide security for protocols using rpc (in particular, nfs). Before
+exchanging any rpc requests using rpcsec_gss, the rpc client must first
+establish a security context with the rpc server. The linux kernel's
+implementation of rpcsec_gss depends on the userspace daemon
+.B rpc.svcgssd
+to handle context establishment on the rpc server. The
+daemon uses files in the proc filesystem to communicate with
+the kernel.
+
+.SH OPTIONS
+.TP
+.B -f
+Runs
+.B rpc.svcgssd
+in the foreground and sends output to stderr (as opposed to syslogd)
+.TP
+.B -v
+Increases the verbosity of the output (can be specified multiple times).
+
+.SH SEE ALSO
+.BR rpc.gssd(8),
+.SH AUTHORS
+.br
+Dug Song <dugsong@umich.edu>
+.br
+Andy Adamson <andros@umich.edu>
+.br
+Marius Aamodt Eriksen <marius@umich.edu>
+.br
+J. Bruce Fields <bfields@umich.edu>
diff --git a/utils/svcgssd/svcgssd_main_loop.c b/utils/svcgssd/svcgssd_main_loop.c
new file mode 100644
index 0000000..477a44c
--- /dev/null
+++ b/utils/svcgssd/svcgssd_main_loop.c
@@ -0,0 +1,86 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "svcgssd.h"
+#include "err_util.h"
+
+void
+gssd_run()
+{
+ int ret;
+ FILE *f;
+ struct pollfd pollfd;
+
+#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel"
+
+ f = fopen(NULLRPC_FILE, "rw");
+
+ if (!f) {
+ printerr(0, "failed to open %s\n", NULLRPC_FILE);
+ exit(1);
+ }
+ pollfd.fd = fileno(f);
+ pollfd.events = POLLIN;
+ while (1) {
+ pollfd.revents = 0;
+ printerr(1, "entering poll\n");
+ ret = poll(&pollfd, 1, -1);
+ printerr(1, "leaving poll\n");
+ if (ret < 0) {
+ if (errno != EINTR)
+ printerr(0, "error return from poll\n");
+ } else if (ret == 0) {
+ /* timeout; shouldn't happen. */
+ } else {
+ if (ret != 1) {
+ printerr(0, "bug: unexpected poll return %d\n",
+ ret);
+ exit(1);
+ }
+ if (pollfd.revents & POLLIN)
+ handle_nullreq(f);
+ }
+ }
+}
diff --git a/utils/svcgssd/svcgssd_mech2file.c b/utils/svcgssd/svcgssd_mech2file.c
new file mode 100644
index 0000000..f44f7c6
--- /dev/null
+++ b/utils/svcgssd/svcgssd_mech2file.c
@@ -0,0 +1,77 @@
+/*
+ linux_downcall.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2004 Andy Adamson <andros@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_KRB5
+#include <gssapi/gssapi_generic.h>
+#elif HAVE_HEIMDAL
+#include <gssapi.h>
+#endif
+#include <string.h>
+
+
+#define g_OID_equal(o1,o2) \
+ (((o1)->length == (o2)->length) && \
+ (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
+
+struct mech2file {
+ gss_OID_desc mech;
+ char filename[8];
+};
+
+struct mech2file m2f[] = {
+ {{9, "\052\206\110\206\367\022\001\002\002"}, "krb5"},
+ {{7, "\053\006\001\005\005\001\003"}, "spkm3"},
+ {{7, "\053\006\001\005\005\001\009"}, "lipkey"},
+ {{0,0},""},
+};
+
+/*
+ * Find the Linux svcgssd downcall file name given the mechanism
+ */
+char *
+mech2file(gss_OID mech)
+{
+ struct mech2file *m2fp = m2f;
+
+ while(m2fp->mech.length != 0) {
+ if (g_OID_equal(mech,&m2fp->mech))
+ return(m2fp->filename);
+ m2fp++;
+ }
+ return NULL;
+}
diff --git a/utils/svcgssd/svcgssd_proc.c b/utils/svcgssd/svcgssd_proc.c
new file mode 100644
index 0000000..c2470c6
--- /dev/null
+++ b/utils/svcgssd/svcgssd_proc.c
@@ -0,0 +1,344 @@
+/*
+ svc_in_gssd_proc.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+#include "context.h"
+#include "cacheio.h"
+
+/* XXX: ? */
+#ifndef NGROUPS
+#define NGROUPS 32
+#endif
+
+extern char * mech2file(gss_OID mech);
+#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
+#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.rpcsec.init/channel"
+
+struct svc_cred {
+ uid_t cr_uid;
+ gid_t cr_gid;
+ gid_t cr_groups[NGROUPS];
+};
+
+static int
+do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
+ gss_OID mech, gss_buffer_desc *context_token)
+{
+ FILE *f;
+ int i, ngroups;
+ char *fname = NULL;
+
+ printerr(1, "doing downcall\n");
+ if ((fname = mech2file(mech)) == NULL)
+ goto out_err;
+ f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
+ if (f == NULL) {
+ printerr(0, "WARNING: unable to open downcall channel "
+ "%s: %s\n",
+ SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
+ goto out_err;
+ }
+ qword_printhex(f, out_handle->value, out_handle->length);
+ /* XXX are types OK for the rest of this? */
+ qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
+ qword_printint(f, cred->cr_uid);
+ qword_printint(f, cred->cr_gid);
+ ngroups = NGROUPS;
+ for (i=0; i < NGROUPS; i++) {
+ if (cred->cr_groups[i] == NOGROUP) {
+ ngroups = i;
+ break;
+ }
+ }
+ qword_printint(f, ngroups);
+ for (i=0; i < ngroups; i++)
+ qword_printint(f, cred->cr_groups[i]);
+ qword_print(f, fname);
+ qword_printhex(f, context_token->value, context_token->length);
+ qword_eol(f);
+ fclose(f);
+ return 0;
+out_err:
+ printerr(0, "WARNING: downcall failed\n");
+ return -1;
+}
+
+struct gss_verifier {
+ u_int32_t flav;
+ gss_buffer_desc body;
+};
+
+#define RPCSEC_GSS_SEQ_WIN 5
+
+static int
+send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
+ u_int32_t maj_stat, u_int32_t min_stat,
+ gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
+{
+ char buf[2 * 4096];
+ char *bp = buf;
+ int blen = sizeof(buf);
+ /* XXXARG: */
+ int g;
+
+ printerr(1, "sending null reply\n");
+
+ qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
+ qword_addhex(&bp, &blen, in_token->value, in_token->length);
+ qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
+ qword_addint(&bp, &blen, maj_stat);
+ qword_addint(&bp, &blen, min_stat);
+ qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
+ qword_addhex(&bp, &blen, out_token->value, out_token->length);
+ qword_addeol(&bp, &blen);
+ if (blen <= 0) {
+ printerr(0, "WARNING: send_respsonse: message too long\n");
+ return -1;
+ }
+ g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
+ if (g == -1) {
+ printerr(0, "WARNING: open %s failed: %s\n",
+ SVCGSSD_INIT_CHANNEL, strerror(errno));
+ return -1;
+ }
+ *bp = '\0';
+ printerr(1, "writing message: %s", buf);
+ if (write(g, buf, bp - buf) == -1) {
+ printerr(0, "WARNING: failed to write message\n");
+ close(g);
+ return -1;
+ }
+ close(g);
+ return 0;
+}
+
+#define rpc_auth_ok 0
+#define rpc_autherr_badcred 1
+#define rpc_autherr_rejectedcred 2
+#define rpc_autherr_badverf 3
+#define rpc_autherr_rejectedverf 4
+#define rpc_autherr_tooweak 5
+#define rpcsec_gsserr_credproblem 13
+#define rpcsec_gsserr_ctxproblem 14
+
+/* XXX memory leaks everywhere: */
+static int
+get_ids(gss_name_t client_name, gss_OID *mech, struct svc_cred *cred)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc name;
+ char *sname;
+ int res = -1;
+ struct passwd *pw = NULL;
+ gss_OID name_type;
+ char *c;
+
+ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
+ if (maj_stat != GSS_S_COMPLETE)
+ goto out;
+ if (!(sname = calloc(name.length + 1, 1)))
+ goto out;
+ memcpy(sname, name.value, name.length);
+ printerr(1, "sname = %s\n", sname);
+ /* XXX: should use same mapping as idmapd? Or something; for now
+ * I'm just chopping off the domain. */
+ /* XXX: note that idmapd also does this! It doesn't check the domain
+ * name. */
+ if ((c = strchr(sname, '@')) != NULL)
+ *c = '\0';
+ /* XXX? mapping unknown users (including machine creds) to nobody: */
+ if ( !(pw = getpwnam(sname)) && !(pw = getpwnam("nobody")) )
+ goto out;
+ cred->cr_uid = pw->pw_uid;
+ cred->cr_gid = pw->pw_gid;
+ /* XXX Read password file? Use initgroups? I dunno...*/
+ cred->cr_groups[0] = NOGROUP;
+ res = 0;
+out:
+ if (res)
+ printerr(0, "WARNING: get_uid failed\n");
+ return res;
+}
+
+void
+print_hexl(int pri, unsigned char *cp, int length)
+{
+ int i, j, jm;
+ unsigned char c;
+
+ printerr(pri, "length %d\n",length);
+ printerr(pri, "\n");
+
+ for (i = 0; i < length; i += 0x10) {
+ printerr(pri, " %04x: ", (u_int)i);
+ jm = length - i;
+ jm = jm > 16 ? 16 : jm;
+
+ for (j = 0; j < jm; j++) {
+ if ((j % 2) == 1)
+ printerr(pri,"%02x ", (u_int)cp[i+j]);
+ else
+ printerr(pri,"%02x", (u_int)cp[i+j]);
+ }
+ for (; j < 16; j++) {
+ if ((j % 2) == 1)
+ printerr(pri," ");
+ else
+ printerr(pri," ");
+ }
+ printerr(pri," ");
+
+ for (j = 0; j < jm; j++) {
+ c = cp[i+j];
+ c = isprint(c) ? c : '.';
+ printerr(pri,"%c", c);
+ }
+ printerr(pri,"\n");
+ }
+}
+
+void
+handle_nullreq(FILE *f) {
+ /* XXX initialize to a random integer to reduce chances of unnecessary
+ * invalidation of existing ctx's on restarting svcgssd. */
+ static u_int32_t handle_seq = 0;
+ char in_tok_buf[1023];
+ char in_handle_buf[15];
+ char out_handle_buf[15];
+ gss_buffer_desc in_tok = {.value = in_tok_buf},
+ out_tok = {.value = NULL},
+ in_handle = {.value = in_handle_buf},
+ out_handle = {.value = out_handle_buf},
+ ctx_token = {.value = NULL},
+ /* XXX isn't there a define for this?: */
+ null_token = {.value = NULL};
+ u_int32_t ret_flags;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_name_t client_name;
+ gss_OID mech;
+ u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
+ struct svc_cred cred;
+ static char *lbuf = NULL;
+ static int lbuflen = 0;
+ static char *cp;
+
+ printerr(1, "handling null request\n");
+
+ if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "failed reading request\n");
+ return;
+ }
+
+ cp = lbuf;
+
+ in_handle.length
+ = qword_get(&cp, in_handle.value, sizeof(in_handle_buf));
+ printerr(2, "in_handle: \n");
+ print_hexl(2, in_handle.value, in_handle.length);
+ handle_seq++;
+ out_handle.length = sizeof(handle_seq);
+ memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
+
+ in_tok.length = qword_get(&cp, in_tok.value, sizeof(in_tok_buf));
+ printerr(2, "in_tok: \n");
+ print_hexl(2, in_tok.value, in_tok.length);
+
+ if (in_tok.length < 0) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "failed parsing request\n");
+ goto out_err;
+ }
+
+ if (in_handle.length != 0) { /* CONTINUE_INIT case */
+ printerr(0, "WARNING: handle_nullreq: "
+ "CONTINUE_INIT unsupported\n");
+ send_response(f, &in_handle, &in_tok, -1, -1, &null_token,
+ &null_token);
+ goto out_err;
+ }
+
+ maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
+ &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
+ &mech, &out_tok, &ret_flags, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "WARNING: gss_accept_sec_context failed\n");
+ pgsserr("handle_nullreq: gss_accept_sec_context",
+ maj_stat, min_stat, mech);
+ send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
+ &null_token, &null_token);
+ goto out_err;
+ }
+ if (get_ids(client_name, &mech, &cred)) {
+ printerr(0, "WARNING: handle_nullreq: get_uid failed\n");
+ send_response(f, &in_handle, &in_tok, GSS_S_BAD_NAME /* XXX? */,
+ 0, &null_token, &null_token);
+ goto out_err;
+ }
+
+ /* kernel needs ctx to calculate verifier on null response, so
+ * must give it context before doing null call: */
+ if (serialize_context_for_kernel(ctx, &ctx_token)) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "serialize_context_for_kernel failed\n");
+ send_response(f, &in_handle, &in_tok, -1, /* XXX? */
+ 0, &null_token, &null_token);
+ goto out_err;
+ }
+ do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
+ send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
+ &out_handle, &out_tok);
+ goto out;
+out_err:
+out:
+ if (ctx_token.value != NULL)
+ free(ctx_token.value);
+ printerr(1, "finished handling null request\n");
+ return;
+}