diff options
Diffstat (limited to 'utils')
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; +} |