Changes since 1.0.10-asci-1: * Added a new function, nfskrutil_destroy_ctx_keys(), to libnfskrutil to revoke and unlink rpcsec_gss_ctx keys * Removed the unused "flags" parameter from the nfskrutil_delete_gss_cred() function. * nfslogout calls the new function to stop using NFS (It does not destroy credentials, it only revokes and unlinks the ctx keys and the keys used to tell gssd where the credentials are.) * Two changes from Bruce to 1) send down the initiate flag in the new context format, and 2) look for the Kerberos enctype information at the top of the rpc_pipefs instead of within the nfs directory --- nfs-utils-1.0.10-asci-kwc/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/aclocal.m4 | 74 nfs-utils-1.0.10-asci-kwc/aclocal/keyring.m4 | 9 nfs-utils-1.0.10-asci-kwc/config.guess | 664 ++---- nfs-utils-1.0.10-asci-kwc/config.sub | 214 -- nfs-utils-1.0.10-asci-kwc/configure | 363 ++- nfs-utils-1.0.10-asci-kwc/configure.in | 6 nfs-utils-1.0.10-asci-kwc/linux-nfs/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/ltmain.sh | 12 nfs-utils-1.0.10-asci-kwc/support/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/support/export/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/support/include/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/support/include/config.h.in | 3 nfs-utils-1.0.10-asci-kwc/support/include/exportfs.h | 7 nfs-utils-1.0.10-asci-kwc/support/include/nfs/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/support/include/nfslib.h | 8 nfs-utils-1.0.10-asci-kwc/support/include/rpcsvc/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/support/include/sys/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/support/include/sys/fs/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/support/misc/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/support/nfs/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/support/nfs/exports.c | 177 + nfs-utils-1.0.10-asci-kwc/tools/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/tools/getiversion/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/tools/getkversion/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/tools/locktest/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/tools/nlmtest/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/tools/rpcdebug/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/tools/rpcgen/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/exportfs/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/exportfs/exportfs.c | 17 nfs-utils-1.0.10-asci-kwc/utils/exportfs/exports.man | 13 nfs-utils-1.0.10-asci-kwc/utils/gssd/Makefile.am | 25 nfs-utils-1.0.10-asci-kwc/utils/gssd/Makefile.in | 235 +- nfs-utils-1.0.10-asci-kwc/utils/gssd/context.c | 7 nfs-utils-1.0.10-asci-kwc/utils/gssd/context.h | 8 nfs-utils-1.0.10-asci-kwc/utils/gssd/context_heimdal.c | 19 nfs-utils-1.0.10-asci-kwc/utils/gssd/context_mit.c | 522 ++++- nfs-utils-1.0.10-asci-kwc/utils/gssd/context_spkm3.c | 44 nfs-utils-1.0.10-asci-kwc/utils/gssd/gss_clnt_send_err.c | 4 nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd.c | 22 nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd.h | 49 nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd_keyring.c | 507 ++++ nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd_main_loop.c | 15 nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd_proc.c | 166 + nfs-utils-1.0.10-asci-kwc/utils/gssd/krb5_util.c | 284 ++ nfs-utils-1.0.10-asci-kwc/utils/gssd/krb5_util.h | 4 nfs-utils-1.0.10-asci-kwc/utils/gssd/libnfskrutil_symbol_versions | 20 nfs-utils-1.0.10-asci-kwc/utils/gssd/nfskrutil-int.h | 112 + nfs-utils-1.0.10-asci-kwc/utils/gssd/nfskrutil-test.c | 348 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/nfskrutil.c | 1039 ++++++++++ nfs-utils-1.0.10-asci-kwc/utils/gssd/nfskrutil.h | 131 + nfs-utils-1.0.10-asci-kwc/utils/gssd/nfslogin.c | 134 + nfs-utils-1.0.10-asci-kwc/utils/gssd/nfslogout.c | 135 + nfs-utils-1.0.10-asci-kwc/utils/gssd/sessionsh.c | 38 nfs-utils-1.0.10-asci-kwc/utils/gssd/svcgssd_proc.c | 2 nfs-utils-1.0.10-asci-kwc/utils/idmapd/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/lockd/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/mount/Makefile.am | 5 nfs-utils-1.0.10-asci-kwc/utils/mount/Makefile.in | 78 nfs-utils-1.0.10-asci-kwc/utils/mount/nfs4mount.c | 311 ++ nfs-utils-1.0.10-asci-kwc/utils/mountd/Makefile.am | 2 nfs-utils-1.0.10-asci-kwc/utils/mountd/Makefile.in | 20 nfs-utils-1.0.10-asci-kwc/utils/mountd/cache.c | 42 nfs-utils-1.0.10-asci-kwc/utils/mountd/fsloc.c | 193 + nfs-utils-1.0.10-asci-kwc/utils/mountd/fsloc.h | 20 nfs-utils-1.0.10-asci-kwc/utils/nfsd/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/nfsstat/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/nhfsstone/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/rquotad/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/showmount/Makefile.in | 1 nfs-utils-1.0.10-asci-kwc/utils/statd/Makefile.in | 1 73 files changed, 5248 insertions(+), 888 deletions(-) diff -puN configure.in~CITI_NFS4_ALL configure.in --- nfs-utils-1.0.10-asci/configure.in~CITI_NFS4_ALL 2006-09-23 18:39:12.752304000 -0400 +++ nfs-utils-1.0.10-asci-kwc/configure.in 2006-09-23 18:40:06.227401000 -0400 @@ -159,9 +159,7 @@ if test "$enable_nfsv4" = yes; then dnl but we need to make sure we get the right version if test "$enable_gss" = yes; then PKG_CHECK_MODULES(RPCSECGSS, librpcsecgss >= 0.10, , - [AC_MSG_ERROR([Unable to locate information required to use librpcsecgss.]) - AC_MSG_ERROR([If you have pkgconfig installed, you might try setting environment]) - AC_MSG_ERROR([variable PKG_CONFIG_PATH to /usr/local/lib/pkgconfig]) + [AC_MSG_ERROR([Unable to locate information required to use librpcsecgss. If you have pkgconfig installed, you might try setting environment variable PKG_CONFIG_PATH to /usr/local/lib/pkgconfig]) ] ) PKG_CHECK_MODULES(GSSAPI, libgssapi >= 0.9) @@ -194,6 +192,8 @@ if test "$enable_gss" = yes; then AC_CHECK_LIB(rpcsecgss, authgss_set_debug_level, AC_DEFINE(HAVE_AUTHGSS_SET_DEBUG_LEVEL, 1, [Define this if the rpcsec_gss library has the function authgss_set_debug_level]),, -lgssapi -ldl) + dnl Check for Keyring library support + AC_KEYRING_CHECK fi dnl ************************************************************* diff -puN utils/gssd/context_mit.c~CITI_NFS4_ALL utils/gssd/context_mit.c --- nfs-utils-1.0.10-asci/utils/gssd/context_mit.c~CITI_NFS4_ALL 2006-09-23 18:39:13.897853000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/context_mit.c 2006-09-23 18:40:15.752898000 -0400 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -43,9 +44,53 @@ #ifdef HAVE_KRB5 #include +/* for 3DES */ +#define KG_USAGE_SEAL 22 +#define KG_USAGE_SIGN 23 +#define KG_USAGE_SEQ 24 + +/* for rfc???? */ +#define KG_USAGE_ACCEPTOR_SEAL 22 +#define KG_USAGE_ACCEPTOR_SIGN 23 +#define KG_USAGE_INITIATOR_SEAL 24 +#define KG_USAGE_INITIATOR_SIGN 25 + +/* Lifted from mit src/lib/gssapi/krb5/gssapiP_krb5.h */ +enum seal_alg { + SEAL_ALG_NONE = 0xffff, + SEAL_ALG_DES = 0x0000, + SEAL_ALG_1 = 0x0001, /* not published */ + SEAL_ALG_MICROSOFT_RC4 = 0x0010, /* microsoft w2k; */ + SEAL_ALG_DES3KD = 0x0002 +}; + +#define KEY_USAGE_SEED_ENCRYPTION 0xAA +#define KEY_USAGE_SEED_INTEGRITY 0x55 +#define KEY_USAGE_SEED_CHECKSUM 0x99 +#define K5CLENGTH 5 + +/* Flags for version 2 context flags */ +#define KRB5_CTX_FLAG_INITIATOR 0x00000001 +#define KRB5_CTX_FLAG_CFX 0x00000002 +#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + +/* + * XXX Hack alert. We don't have "legal" access to these + * structures located in libk5crypto + */ +extern void krb5int_enc_arcfour; +extern void krb5int_enc_des3; +extern void krb5int_enc_aes128; +extern void krb5int_enc_aes256; +extern int krb5_derive_key(); + +void *get_enc_provider(); + /* XXX spkm3 seems to actually want it this big, yipes. */ #define MAX_CTX_LEN 4096 + + #ifdef HAVE_LUCID_CONTEXT_SUPPORT /* Don't use the private structure, use the exported lucid structure */ @@ -144,9 +189,99 @@ write_lucid_keyblock(char **p, char *end return 0; } +static void +key_lucid_to_krb5(const gss_krb5_lucid_key_t *lin, krb5_keyblock *kout) +{ + memset(kout, '\0', sizeof(kout)); + kout->enctype = lin->type; + kout->length = lin->length; + kout->contents = lin->data; +} + +static void +key_krb5_to_lucid(const krb5_keyblock *kin, gss_krb5_lucid_key_t *lout) +{ + memset(lout, '\0', sizeof(lout)); + lout->type = kin->enctype; + lout->length = kin->length; + lout->data = kin->contents; +} + +/* + * Function to derive a new key from a given key and given constant data. + */ +static krb5_error_code +derive_key_lucid(const gss_krb5_lucid_key_t *in, gss_krb5_lucid_key_t *out, + int usage, char extra) +{ + krb5_error_code code; + unsigned char constant_data[K5CLENGTH]; + krb5_data datain; + int keylength; + void *enc; + krb5_keyblock kin, kout; /* must send krb5_keyblock, not lucid! */ + + /* + * XXX Hack alert. We don't have "legal" access to these + * values and structures located in libk5crypto + */ + switch (in->type) { + case ENCTYPE_DES3_CBC_RAW: + keylength = 24; + enc = &krb5int_enc_des3; + break; + case ENCTYPE_AES128_CTS_HMAC_SHA1_96: + keylength = 16; + enc = &krb5int_enc_aes128; + break; + case ENCTYPE_AES256_CTS_HMAC_SHA1_96: + keylength = 32; + enc = &krb5int_enc_aes256; + break; + default: + code = KRB5_BAD_ENCTYPE; + goto out; + } + + /* allocate memory for output key */ + if ((out->data = malloc(keylength)) == NULL) { + code = ENOMEM; + goto out; + } + out->length = keylength; + out->type = in->type; + + /* Convert to correct format for call to krb5_derive_key */ + key_lucid_to_krb5(in, &kin); + key_lucid_to_krb5(out, &kout); + + datain.data = (char *) constant_data; + datain.length = K5CLENGTH; + + datain.data[0] = (usage>>24)&0xff; + datain.data[1] = (usage>>16)&0xff; + datain.data[2] = (usage>>8)&0xff; + datain.data[3] = usage&0xff; + + datain.data[4] = (char) extra; + + if ((code = krb5_derive_key(enc, &kin, &kout, &datain))) { + free(out->data); + out->data = NULL; + goto out; + } + key_krb5_to_lucid(&kout, out); + + out: + if (code) + printerr(0, "ERROR: derive_key_lucid returning error %d (%s)\n", + code, error_message(code)); + return (code); +} + static int prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx, - gss_buffer_desc *buf) + gss_buffer_desc *buf, uint32_t *endtime) { char *p, *end; static int constant_zero = 0; @@ -181,9 +316,11 @@ prepare_krb5_rfc1964_buffer(gss_krb5_luc 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; + if (endtime) + *endtime = lctx->endtime; 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; + if (write_oid(&p, end, &krb5oid)) goto out_err; printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with " "enctype %d and length %d\n", @@ -212,23 +349,187 @@ prepare_krb5_rfc1964_buffer(gss_krb5_luc return 0; out_err: printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); - if (buf->value) free(buf->value); + if (buf->value) { + free(buf->value); + buf->value = NULL; + } buf->length = 0; - if (enc_key.data) free(enc_key.data); + if (enc_key.data) { + free(enc_key.data); + enc_key.data = NULL; + } return -1; } +/* + * Prepare a new-style buffer to send to the kernel for newer encryption + * types -- or for DES3. + * + * The new format is: + * + * u32 initiate; ( whether we are the initiator or not ) + * s32 endtime; + * u32 flags; + * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 + * #define KRB5_CTX_FLAG_CFX 0x00000002 + * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + * u64 seq_send; + * u32 enctype; ( encrption type of keys ) + * u32 size_of_each_key; ( size of each key in bytes ) + * u32 number_of_keys; ( N -- should always be 3 for now ) + * keydata-1; ( Ke ) + * keydata-2; ( Ki ) + * keydata-3; ( Kc ) + * + */ static int -prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx, - gss_buffer_desc *buf) +prepare_krb5_ctx_v2_buffer(gss_krb5_lucid_context_v1_t *lctx, + gss_buffer_desc *buf, uint32_t *endtime) { - printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n"); + char *p, *end; + uint32_t v2_flags = 0; + gss_krb5_lucid_key_t enc_key; + gss_krb5_lucid_key_t derived_key; + gss_buffer_desc fakeoid; + uint32_t enctype; + uint32_t keysize; + uint32_t numkeys; + + 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; + + /* Version 2 */ + if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err; + if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err; + if (endtime) + *endtime = lctx->endtime; + + if (lctx->initiate) + v2_flags |= KRB5_CTX_FLAG_INITIATOR; + if (lctx->protocol != 0) + v2_flags |= KRB5_CTX_FLAG_CFX; + if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1) + v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; + + if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; + + if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err; + + /* Protocol 0 here implies DES3 or RC4 */ + if (lctx->protocol == 0) { + enctype = lctx->rfc1964_kd.ctx_key.type; + keysize = lctx->rfc1964_kd.ctx_key.length; + numkeys = 3; /* XXX is always gonna be three? */ + } else { + if (lctx->cfx_kd.have_acceptor_subkey) { + enctype = lctx->cfx_kd.acceptor_subkey.type; + keysize = lctx->cfx_kd.acceptor_subkey.length; + } else { + enctype = lctx->cfx_kd.ctx_key.type; + keysize = lctx->cfx_kd.ctx_key.length; + } + numkeys = 3; + } + printerr(2, "prepare_krb5_ctx_v2_buffer: serializing %d keys with " + "enctype %d and size %d\n", numkeys, enctype, keysize); + if (WRITE_BYTES(&p, end, enctype)) goto out_err; + if (WRITE_BYTES(&p, end, keysize)) goto out_err; + if (WRITE_BYTES(&p, end, numkeys)) goto out_err; + + if (lctx->protocol == 0) { + /* derive and send down: Ke, Ki, and Kc */ + /* Ke */ + if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data, + lctx->rfc1964_kd.ctx_key.length)) + goto out_err; + + /* Ki */ + if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data, + lctx->rfc1964_kd.ctx_key.length)) + goto out_err; + + /* Kc */ + if (derive_key_lucid(&lctx->rfc1964_kd.ctx_key, + &derived_key, + KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM)) + goto out_err; + if (write_bytes(&p, end, derived_key.data, + derived_key.length)) + goto out_err; + free(derived_key.data); + } else { + gss_krb5_lucid_key_t *keyptr; + uint32_t sign_usage, seal_usage; + + if (lctx->cfx_kd.have_acceptor_subkey) + keyptr = &lctx->cfx_kd.acceptor_subkey; + else + keyptr = &lctx->cfx_kd.ctx_key; + + if (lctx->initiate == 1) { + sign_usage = KG_USAGE_INITIATOR_SIGN; + seal_usage = KG_USAGE_INITIATOR_SEAL; + } else { + sign_usage = KG_USAGE_ACCEPTOR_SIGN; + seal_usage = KG_USAGE_ACCEPTOR_SEAL; + } + + /* derive and send down: Ke, Ki, and Kc */ + + /* Ke */ + if (derive_key_lucid(keyptr, &derived_key, + seal_usage, KEY_USAGE_SEED_ENCRYPTION)) + goto out_err; + if (write_bytes(&p, end, derived_key.data, + derived_key.length)) + goto out_err; + free(derived_key.data); + + /* Ki */ + if (derive_key_lucid(keyptr, &derived_key, + seal_usage, KEY_USAGE_SEED_INTEGRITY)) + goto out_err; + if (write_bytes(&p, end, derived_key.data, + derived_key.length)) + goto out_err; + free(derived_key.data); + + /* Kc */ + if (derive_key_lucid(keyptr, &derived_key, + sign_usage, KEY_USAGE_SEED_CHECKSUM)) + goto out_err; + if (write_bytes(&p, end, derived_key.data, + derived_key.length)) + goto out_err; + free(derived_key.data); + } + + buf->length = p - (char *)buf->value; + return 0; + +out_err: + printerr(0, "ERROR: prepare_krb5_ctx_v2_buffer: " + "failed serializing krb5 context for kernel\n"); + if (buf->value) { + free(buf->value); + buf->value = NULL; + } + buf->length = 0; + if (enc_key.data) { + free(enc_key.data); + enc_key.data = NULL; + } return -1; } int -serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf) +serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, uint32_t *endtime) { OM_uint32 maj_stat, min_stat; void *return_ctx = 0; @@ -258,11 +559,21 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss 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); + /* + * Now lctx points to a lucid context that we can send down to kernel + * + * Note: we send down different information to the kernel depending + * on the protocol version and the enctyption type. + * For protocol version 0 with all enctypes besides DES3, we use + * the original format. For protocol version != 0 or DES3, we + * send down the new style information. + */ + + if (lctx->protocol == 0 && + lctx->rfc1964_kd.ctx_key.type == ENCTYPE_DES_CBC_RAW) + retcode = prepare_krb5_rfc1964_buffer(lctx, buf, endtime); else - retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf); + retcode = prepare_krb5_ctx_v2_buffer(lctx, buf, endtime); maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx); if (maj_stat != GSS_S_COMPLETE) { @@ -300,6 +611,66 @@ write_keyblock(char **p, char *end, stru } /* + * Function to derive a new key from a given key and given constant data. + */ +static krb5_error_code +derive_key(const krb5_keyblock *in, krb5_keyblock *out, int usage, char extra) +{ + krb5_error_code code; + unsigned char constant_data[K5CLENGTH]; + krb5_data datain; + int keylength; + void *enc; + + /* + * XXX Hack alert. We don't have "legal" access to these + * values and structures located in libk5crypto + */ + switch (in->enctype) { + case ENCTYPE_DES3_CBC_RAW: + keylength = 24; + enc = &krb5int_enc_des3; + break; + case ENCTYPE_ARCFOUR_HMAC: + keylength = 16; + enc = &krb5int_enc_arcfour; + break; + default: + code = KRB5_BAD_ENCTYPE; + goto out; + } + + /* allocate memory for output key */ + if ((out->contents = malloc(keylength)) == NULL) { + code = ENOMEM; + goto out; + } + out->length = keylength; + out->enctype = in->enctype; + + datain.data = (char *) constant_data; + datain.length = K5CLENGTH; + + datain.data[0] = (usage>>24)&0xff; + datain.data[1] = (usage>>16)&0xff; + datain.data[2] = (usage>>8)&0xff; + datain.data[3] = usage&0xff; + + datain.data[4] = (char) extra; + + if ((code = krb5_derive_key(enc, in, out, &datain))) { + free(out->contents); + out->contents = NULL; + } + + out: + if (code) + printerr(0, "ERROR: derive_key returning error %d (%s)\n", + code, error_message(code)); + return (code); +} + +/* * We really shouldn't know about glue-layer context structure, but * we need to get at the real krb5 context pointer. This should be * removed as soon as we say there is no support for MIT Kerberos @@ -311,49 +682,120 @@ typedef struct gss_union_ctx_id_t { } gss_union_ctx_id_desc, *gss_union_ctx_id_t; int -serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf) +serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, uint32_t *endtime) { krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id; char *p, *end; - static int constant_one = 1; static int constant_zero = 0; + static int constant_one = 1; + static int constant_two = 2; uint32_t word_seq_send; + u_int64_t seq_send_64bit; + uint32_t v2_flags = 0; + krb5_keyblock derived_key; + uint32_t numkeys; 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_oid(&p, end, kctx->mech_used)) goto out_err; - - printerr(2, "serialize_krb5_ctx: serializing keys with " - "enctype %d and length %d\n", - kctx->enc->enctype, kctx->enc->length); + switch (kctx->sealalg) { + case SEAL_ALG_DES: + /* Versions 0 and 1 */ + 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; + if (endtime) + *endtime = kctx->endtime; + word_seq_send = kctx->seq_send; + if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; + if (write_oid(&p, end, kctx->mech_used)) goto out_err; + + printerr(2, "serialize_krb5_ctx: serializing keys with " + "enctype %d and length %d\n", + kctx->enc->enctype, kctx->enc->length); - if (write_keyblock(&p, end, kctx->enc)) goto out_err; - if (write_keyblock(&p, end, kctx->seq)) goto out_err; + if (write_keyblock(&p, end, kctx->enc)) goto out_err; + if (write_keyblock(&p, end, kctx->seq)) goto out_err; + break; + case SEAL_ALG_MICROSOFT_RC4: + case SEAL_ALG_DES3KD: + /* u32 version; ( 2 ) + * s32 endtime; + * u32 flags; + * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 + * #define KRB5_CTX_FLAG_CFX 0x00000002 + * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + * u64 seq_send; + * u32 enctype; + * u32 size_of_each_key; ( size in bytes ) + * u32 number_of_keys; ( N (assumed to be 3 for now) ) + * keydata-1; ( Ke (Kenc for DES3) ) + * keydata-2; ( Ki (Kseq for DES3) ) + * keydata-3; ( Kc (derived checksum key) ) + */ + /* Version 2 */ + if (WRITE_BYTES(&p, end , constant_two)) goto out_err; + if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; + + /* Only applicable flag for is initiator */ + if (kctx->initiate) v2_flags |= KRB5_CTX_FLAG_INITIATOR; + if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; + + seq_send_64bit = kctx->seq_send; + if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err; + + if (WRITE_BYTES(&p, end, kctx->enc->enctype)) goto out_err; + if (WRITE_BYTES(&p, end, kctx->enc->length)) goto out_err; + numkeys = 3; + if (WRITE_BYTES(&p, end, numkeys)) goto out_err; + printerr(2, "serialize_krb5_ctx: serializing %d keys with " + "enctype %d and size %d\n", + numkeys, kctx->enc->enctype, kctx->enc->length); + + /* Ke */ + if (write_bytes(&p, end, kctx->enc->contents, + kctx->enc->length)) + goto out_err; + + /* Ki */ + if (write_bytes(&p, end, kctx->enc->contents, + kctx->enc->length)) + goto out_err; + + /* Kc */ + if (derive_key(kctx->seq, &derived_key, + KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM)) + goto out_err; + if (write_bytes(&p, end, derived_key.contents, + derived_key.length)) + goto out_err; + free(derived_key.contents); + break; + default: + printerr(0, "ERROR: serialize_krb5_ctx: unsupported seal " + "algorithm %d\n", kctx->sealalg); + 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); diff -puN utils/gssd/gssd_proc.c~CITI_NFS4_ALL utils/gssd/gssd_proc.c --- nfs-utils-1.0.10-asci/utils/gssd/gssd_proc.c~CITI_NFS4_ALL 2006-09-23 18:39:15.785951000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd_proc.c 2006-09-23 18:40:27.022930000 -0400 @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -57,10 +58,12 @@ #include #include #include +#include #include #include #include #include +#include #include "gssd.h" #include "err_util.h" @@ -79,19 +82,19 @@ * 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 + * {pipefs_nfsdir}/clntXX : one per rpc_clnt struct in the kernel + * {pipefs_nfsdir}/clntXX/krb5 : read uid for which kernel wants + * a context, write the resulting context + * {pipefs_nfsdir}/clntXX/info : stores info such as server name * * Algorithm: - * Poll all /pipefsdir/clntXX/krb5 files. When ready, data read + * Poll all {pipefs_nfsdir}/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. + * created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories, + * and rescan the whole {pipefs_nfsdir} when this happens. */ struct pollfd * pollarray; @@ -102,7 +105,6 @@ int pollsize; /* the size of pollaray ( 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; @@ -178,6 +180,9 @@ fail: static void destroy_client(struct clnt_info *clp) { + if (clp->keyring_poll_index != -1) + memset(&pollarray[clp->keyring_poll_index], 0, + sizeof(struct pollfd)); if (clp->krb5_poll_index != -1) memset(&pollarray[clp->krb5_poll_index], 0, sizeof(struct pollfd)); @@ -204,8 +209,10 @@ insert_new_clnt(void) strerror(errno)); goto out; } + clp->keyring_poll_index = -1; clp->krb5_poll_index = -1; clp->spkm3_poll_index = -1; + clp->keyring_fd = -1; clp->krb5_fd = -1; clp->spkm3_fd = -1; clp->dir_fd = -1; @@ -218,26 +225,31 @@ out: static int process_clnt_dir_files(struct clnt_info * clp) { - char kname[32]; - char sname[32]; + char name[32]; char info_file_name[32]; + if (clp->keyring_fd == -1) { + snprintf(name, sizeof(name), "%s/keyring", clp->dirname); + clp->keyring_fd = open(name, O_RDWR); + } if (clp->krb5_fd == -1) { - snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname); - clp->krb5_fd = open(kname, O_RDWR); + snprintf(name, sizeof(name), "%s/krb5", clp->dirname); + clp->krb5_fd = open(name, O_RDWR); } if (clp->spkm3_fd == -1) { - snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname); - clp->spkm3_fd = open(sname, O_RDWR); + snprintf(name, sizeof(name), "%s/spkm3", clp->dirname); + clp->spkm3_fd = open(name, O_RDWR); } - if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1)) + if ((clp->keyring_fd == -1) && + (clp->krb5_fd == -1) && + (clp->spkm3_fd == -1)) return -1; snprintf(info_file_name, sizeof(info_file_name), "%s/info", - clp->dirname); + clp->dirname); if ((clp->servicename == NULL) && - read_service_info(info_file_name, &clp->servicename, - &clp->servername, &clp->prog, &clp->vers, - &clp->protocol)) + read_service_info(info_file_name, &clp->servicename, + &clp->servername, &clp->prog, &clp->vers, + &clp->protocol)) return -1; return 0; } @@ -265,6 +277,15 @@ get_poll_index(int *ind) static int insert_clnt_poll(struct clnt_info *clp) { + if ((clp->keyring_fd != -1) && (clp->keyring_poll_index == -1)) { + if (get_poll_index(&clp->keyring_poll_index)) { + printerr(0, "ERROR: Too many keyring clients\n"); + return -1; + } + pollarray[clp->keyring_poll_index].fd = clp->keyring_fd; + pollarray[clp->keyring_poll_index].events |= POLLIN; + } + if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) { if (get_poll_index(&clp->krb5_poll_index)) { printerr(0, "ERROR: Too many krb5 clients\n"); @@ -388,16 +409,16 @@ update_client_list(void) struct dirent **namelist; int i, j; - if (chdir(pipefsdir) < 0) { + if (chdir(pipefs_nfsdir) < 0) { printerr(0, "ERROR: can't chdir to %s: %s\n", - pipefsdir, strerror(errno)); + pipefs_nfsdir, strerror(errno)); return -1; } - j = scandir(pipefsdir, &namelist, NULL, alphasort); + j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort); if (j < 0) { printerr(0, "ERROR: can't scandir %s: %s\n", - pipefsdir, strerror(errno)); + pipefs_nfsdir, strerror(errno)); return -1; } update_old_clients(namelist, j); @@ -413,8 +434,8 @@ update_client_list(void) return 0; } -static int -do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, +int +do_downcall(int fd, uid_t uid, struct authgss_private_data *pd, gss_buffer_desc *context_token) { char *buf = NULL, *p = NULL, *end = NULL; @@ -435,7 +456,7 @@ do_downcall(int k5_fd, uid_t uid, struct if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err; if (write_buffer(&p, end, context_token)) goto out_err; - if (write(k5_fd, buf, p - buf) < p - buf) goto out_err; + if (write(fd, buf, p - buf) < p - buf) goto out_err; if (buf) free(buf); return 0; out_err: @@ -444,8 +465,8 @@ out_err: return -1; } -static int -do_error_downcall(int k5_fd, uid_t uid, int err) +int +do_error_downcall(int fd, uid_t uid, int err) { char buf[1024]; char *p = buf, *end = buf + 1024; @@ -460,13 +481,36 @@ do_error_downcall(int k5_fd, uid_t uid, if (WRITE_BYTES(&p, end, zero)) goto out_err; if (WRITE_BYTES(&p, end, err)) goto out_err; - if (write(k5_fd, buf, p - buf) < p - buf) goto out_err; + if (write(fd, buf, p - buf) < p - buf) goto out_err; return 0; out_err: printerr(0, "Failed to write error downcall!\n"); return -1; } +inline int +set_user_fsuid(struct clnt_info *clp) +{ + clp->saved_uid = geteuid(); + if (setfsuid(clp->uid) != 0) { + printerr(0, "WARNING: Failed to setfsuid for " + "user with uid %d\n", clp->uid); + return -1; + } + return 0; +} + +inline int +restore_our_fsuid(struct clnt_info *clp) +{ + if (setfsuid(clp->saved_uid) != clp->uid) { + printerr(0, "WARNING: Failed to restore fsuid" + " to uid %d from %d\n", clp->saved_uid, clp->uid); + return -1; + } + return 0; +} + /* * Create an RPC connection and establish an authenticated * gss context with a server. @@ -480,7 +524,6 @@ int create_auth_rpc_client(struct clnt_i CLIENT *rpc_clnt = NULL; struct rpc_gss_sec sec; AUTH *auth = NULL; - uid_t save_uid = -1; int retval = -1; int errcode; OM_uint32 min_stat; @@ -491,16 +534,6 @@ int create_auth_rpc_client(struct clnt_i char service[64]; char *at_sign; - /* Create the context as the user (not as root) */ - save_uid = geteuid(); - if (setfsuid(uid) != 0) { - printerr(0, "WARNING: Failed to setfsuid for " - "user with uid %d\n", uid); - goto out_fail; - } - printerr(2, "creating context using fsuid %d (save_uid %d)\n", - uid, save_uid); - sec.qop = GSS_C_QOP_DEFAULT; sec.svc = RPCSEC_GSS_SVC_NONE; sec.cred = GSS_C_NO_CREDENTIAL; @@ -645,11 +678,6 @@ int create_auth_rpc_client(struct clnt_i if (sec.cred != GSS_C_NO_CREDENTIAL) gss_release_cred(&min_stat, &sec.cred); if (a != NULL) freeaddrinfo(a); - /* Restore euid to original value */ - if ((save_uid != -1) && (setfsuid(save_uid) != uid)) { - printerr(0, "WARNING: Failed to restore fsuid" - " to uid %d from %d\n", save_uid, uid); - } return retval; out_fail: @@ -659,6 +687,40 @@ int create_auth_rpc_client(struct clnt_i goto out; } +/* + * A wrapper function around create_auth_rpc_client() that + * changes and then restores the fsuid. + * This allows create_auth_rpc_client() to be called + * directly if fsuid has already been changed elsewhere. + */ +int create_auth_rpc_client_wrap(struct clnt_info *clp, + CLIENT **clnt_return, + AUTH **auth_return, + uid_t uid, + int authtype) +{ + int retval = -1; + + /* Set up uid in the clnt_info! */ + clp->uid = uid; + + /* Create the context with the fsuid of the user (not as root) */ + if (set_user_fsuid(clp) != 0) + return -1; + + printerr(2, "creating context using fsuid %d (save_uid %d)\n", + clp->uid, clp->saved_uid); + + retval = create_auth_rpc_client(clp, clnt_return, auth_return, + clp->uid, authtype); + + /* Restore fsuid to our original value */ + if (restore_our_fsuid(clp) != 0) + return -1; + + return retval; +} + /* * this code uses the userland rpcsec gss library to create a krb5 @@ -702,8 +764,8 @@ handle_krb5_upcall(struct clnt_info *clp } for (ccname = credlist; ccname && *ccname; ccname++) { gssd_setup_krb5_machine_gss_ccache(*ccname); - if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, - AUTHTYPE_KRB5)) == 0) { + if ((create_auth_rpc_client_wrap(clp, &rpc_clnt, &auth, uid, + AUTHTYPE_KRB5)) == 0) { /* Success! */ success++; break; @@ -726,8 +788,8 @@ handle_krb5_upcall(struct clnt_info *clp /* Tell krb5 gss which credentials cache to use */ gssd_setup_krb5_user_gss_ccache(uid, clp->servername); - if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, - AUTHTYPE_KRB5)) != 0) { + if ((create_auth_rpc_client_wrap(clp, &rpc_clnt, &auth, uid, + AUTHTYPE_KRB5)) != 0) { printerr(0, "WARNING: Failed to create krb5 context " "for user with uid %d for server %s\n", uid, clp->servername); @@ -742,7 +804,7 @@ handle_krb5_upcall(struct clnt_info *clp goto out_return_error; } - if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid)) { + if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) { printerr(0, "WARNING: Failed to serialize krb5 context for " "user with uid %d for server %s\n", uid, clp->servername); @@ -791,7 +853,7 @@ handle_spkm3_upcall(struct clnt_info *cl goto out; } - if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) { + if (create_auth_rpc_client_wrap(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) { printerr(0, "WARNING: Failed to create spkm3 context for " "user with uid %d\n", uid); goto out_return_error; @@ -804,7 +866,7 @@ handle_spkm3_upcall(struct clnt_info *cl goto out_return_error; } - if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) { + if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) { printerr(0, "WARNING: Failed to serialize spkm3 context for " "user with uid %d for server\n", uid, clp->servername); diff -puN utils/gssd/krb5_util.c~CITI_NFS4_ALL utils/gssd/krb5_util.c --- nfs-utils-1.0.10-asci/utils/gssd/krb5_util.c~CITI_NFS4_ALL 2006-09-23 18:39:17.945049000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/krb5_util.c 2006-09-23 18:40:27.412833000 -0400 @@ -97,6 +97,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -105,6 +106,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +125,10 @@ /* Global list of principals/cache file names for machine credentials */ struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; +/* Encryption types supported by the kernel rpcsec_gss code */ +int num_krb5_enctypes = 0; +krb5_enctype *krb5_enctypes = NULL; + /*==========================*/ /*=== Internal routines ===*/ /*==========================*/ @@ -178,6 +184,7 @@ gssd_find_existing_krb5_ccache(uid_t uid struct dirent *best_match_dir = NULL; struct stat best_match_stat, tmp_stat; + memset(&best_match_stat, 0, sizeof(best_match_stat)); *d = NULL; n = scandir(ccachedir, &namelist, select_krb5_ccache, 0); if (n < 0) { @@ -261,56 +268,6 @@ gssd_find_existing_krb5_ccache(uid_t uid } -#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; - gss_OID_set_desc desired_mechs; - krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC }; - int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); - - /* We only care about getting a krb5 cred */ - desired_mechs.count = 1; - desired_mechs.elements = &krb5oid; - - maj_stat = gss_acquire_cred(&min_stat, NULL, 0, - &desired_mechs, GSS_C_INITIATE, - &credh, NULL, NULL); - - if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_acquire_cred", - maj_stat, min_stat, &krb5oid); - return -1; - } - - maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid, - num_enctypes, &enctypes); - if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_set_allowable_enctypes", - maj_stat, min_stat, &krb5oid); - 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. @@ -581,7 +538,7 @@ gssd_process_krb5_keytab(krb5_context co * Depending on the version of Kerberos, we either need to use * a private function, or simply set the environment variable. */ -static void +void gssd_set_krb5_ccache_name(char *ccname) { #ifdef USE_GSS_KRB5_CCACHE_NAME @@ -608,6 +565,56 @@ gssd_set_krb5_ccache_name(char *ccname) #endif } +/* + * Parse the supported encryption type information + */ +static int +parse_enctypes(char *enctypes) +{ + int n = 0; + char *curr, *comma; + int i; + + /* Just in case this ever gets called more than once */ + if (krb5_enctypes != NULL) { + free(krb5_enctypes); + krb5_enctypes = NULL; + num_krb5_enctypes = 0; + } + + /* count the number of commas */ + for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { + comma = strchr(curr, ','); + if (comma != NULL) + n++; + else + break; + } + /* If no more commas and we're not at the end, there's one more value */ + if (*curr != '\0') + n++; + + /* Empty string, return an error */ + if (n == 0) + return ENOENT; + + /* Allocate space for enctypes array */ + if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) { + return ENOMEM; + } + + /* Now parse each value into the array */ + for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) { + krb5_enctypes[i++] = atoi(curr); + comma = strchr(curr, ','); + if (comma == NULL) + break; + } + + num_krb5_enctypes = n; + return 0; +} + /*==========================*/ /*=== External routines ===*/ /*==========================*/ @@ -859,3 +866,178 @@ gssd_destroy_krb5_machine_creds(void) krb5_free_context(context); } +#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. + * + * 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; + gss_OID_set_desc desired_mechs; + krb5_enctype enctypes[] = {ENCTYPE_DES_CBC_CRC}; + int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); + + /* We only care about getting a krb5 cred */ + desired_mechs.count = 1; + desired_mechs.elements = &krb5oid; + + maj_stat = gss_acquire_cred(&min_stat, NULL, 0, + &desired_mechs, GSS_C_INITIATE, + &credh, NULL, NULL); + + if (maj_stat != GSS_S_COMPLETE) { + pgsserr("gss_acquire_cred", + maj_stat, min_stat, &krb5oid); + return -1; + } + + /* + * If we failed for any reason to produce global + * list of supported enctypes, use local default here. + */ + if (krb5_enctypes == NULL) + maj_stat = gss_set_allowable_enctypes(&min_stat, credh, + &krb5oid, num_enctypes, &enctypes); + else + maj_stat = gss_set_allowable_enctypes(&min_stat, credh, + &krb5oid, num_krb5_enctypes, + krb5_enctypes); + if (maj_stat != GSS_S_COMPLETE) { + pgsserr("gss_set_allowable_enctypes", + maj_stat, min_stat, &krb5oid); + return -1; + } + sec->cred = credh; + + return 0; +} +#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ + +/* + * Obtain supported enctypes from kernel. + * Set defaults if info is not available. + */ +void +gssd_obtain_kernel_krb5_info(void) +{ + char enctype_file_name[128]; + char buf[1024]; + char enctypes[128]; + char extrainfo[1024]; + int fd; + int use_default_enctypes = 0; + int nbytes, numfields; + char default_enctypes[] = "1,3,2"; + int code; + + snprintf(enctype_file_name, sizeof(enctype_file_name), + "%s/%s", pipefs_dir, "krb5_info"); + + if ((fd = open(enctype_file_name, O_RDONLY)) == -1) { + printerr(1, "WARNING: gssd_obtain_kernel_krb5_info: " + "Unable to open '%s'. Unable to determine " + "Kerberos encryption types supported by the " + "kernel; using defaults (%s).\n", + enctype_file_name, default_enctypes); + use_default_enctypes = 1; + goto do_the_parse; + } + if ((nbytes = read(fd, buf, sizeof(buf))) == -1) { + printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: " + "Error reading Kerberos encryption type " + "information file '%s'; using defaults (%s).\n", + enctype_file_name, default_enctypes); + use_default_enctypes = 1; + goto do_the_parse; + } + numfields = sscanf(buf, "enctypes: %s\n%s", enctypes, extrainfo); + if (numfields < 1) { + printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: " + "error parsing Kerberos encryption type " + "information from file '%s'; using defaults (%s).\n", + enctype_file_name, default_enctypes); + use_default_enctypes = 1; + goto do_the_parse; + } + if (numfields > 1) { + printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: " + "Extra information, '%s', from '%s' is ignored\n", + enctype_file_name, extrainfo); + use_default_enctypes = 1; + goto do_the_parse; + } + do_the_parse: + if (use_default_enctypes) + strcpy(enctypes, default_enctypes); + + if ((code = parse_enctypes(enctypes)) != 0) { + printerr(0, "ERROR: gssd_obtain_kernel_krb5_info: " + "parse_enctypes%s failed with code %d\n", + use_default_enctypes ? " (with default enctypes)" : "", + code); + } +} + +/* + * Determine the principal name associated with the credentials cache + * and copy it into the client information. + */ +int +gssd_krb5_get_principal(struct clnt_info *clp, char *ccname) +{ + krb5_context context; + krb5_ccache ccache; + krb5_error_code code; + krb5_principal princ; + int retval; + + + if ((code = krb5_init_context(&context))) { + printerr(0, "ERROR: %s while initializing krb5 in %s\n", + error_message(code), __FUNCTION__); + retval = code; + goto out; + } + + if ((code = krb5_cc_resolve(context, ccname, &ccache))) { + printerr(0, "ERROR: %s while resolving ccache '%s' in %s\n", + error_message(code), ccname, __FUNCTION__); + retval = code; + goto out_fc; + } + + if ((code = krb5_cc_get_principal(context, ccache, &princ))) { + printerr(0, "ERROR: %s while getting cc principal info in %s\n", + error_message(code), __FUNCTION__); + retval = code; + goto out_fc; + } + + if ((code = krb5_unparse_name(context, princ, &clp->principal))) { + printerr(0, "ERROR: %s while unparsing principal name in %s\n", + error_message(code), __FUNCTION__); + retval = code; + goto out_fp; + } + + printerr(2, "%s: principal name is %s\n", __FUNCTION__, clp->principal); + + /* Success */ + retval = 0; + + out_fp: + krb5_free_principal(context, princ); + out_fc: + krb5_free_context(context); + out: + return retval; +} diff -puN utils/gssd/context_heimdal.c~CITI_NFS4_ALL utils/gssd/context_heimdal.c --- nfs-utils-1.0.10-asci/utils/gssd/context_heimdal.c~CITI_NFS4_ALL 2006-09-23 18:39:22.021245000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/context_heimdal.c 2006-09-23 18:40:15.183342000 -0400 @@ -46,6 +46,20 @@ #include "gss_oids.h" #include "write_bytes.h" +typedef struct gss_ctx_id_t_desc_struct { + struct krb5_auth_context_data *auth_context; + gss_name_t source, target; + OM_uint32 flags; + enum { LOCAL = 1, OPEN = 2, + COMPAT_OLD_DES3 = 4, + COMPAT_OLD_DES3_SELECTED = 8, + } more_flags; + struct krb5_ticket *ticket; + OM_uint32 lifetime; + pthread_mutex_t ctx_id_mutex; + struct gss_msg_order *order; +} gss_ctx_id_t_desc; + #define MAX_CTX_LEN 4096 int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key) @@ -199,7 +213,7 @@ int write_heimdal_seq_key(char **p, char */ int -serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf) +serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, uint32_t *endtime) { char *p, *end; @@ -240,6 +254,9 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss /* endtime */ if (WRITE_BYTES(&p, end, ctx->lifetime)) goto out_err; + if (endtime) + *endtime = ctx->lifetime; + /* seq_send */ if (WRITE_BYTES(&p, end, ctx->auth_context->local_seqnumber)) goto out_err; diff -puN utils/gssd/context_spkm3.c~CITI_NFS4_ALL utils/gssd/context_spkm3.c --- nfs-utils-1.0.10-asci/utils/gssd/context_spkm3.c~CITI_NFS4_ALL 2006-09-23 18:39:25.760441000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/context_spkm3.c 2006-09-23 18:40:15.814891000 -0400 @@ -51,6 +51,7 @@ * * buf->length should be: * + * version 4 * ctx_id 4 + 12 * qop 4 * mech_used 4 + 7 @@ -70,60 +71,58 @@ prepare_spkm3_ctx_buffer(gss_spkm3_lucid char *p, *end; unsigned int buf_size = 0; - buf_size = lctx->ctx_id.length + - sizeof(lctx->ctx_id.length) + sizeof(lctx->qop) + + buf_size = sizeof(lctx->version) + + lctx->ctx_id.length + sizeof(lctx->ctx_id.length) + + sizeof(lctx->endtime) + sizeof(lctx->mech_used.length) + lctx->mech_used.length + - sizeof(lctx->ret_flags) + sizeof(lctx->req_flags) + - sizeof(lctx->share_key.length) + lctx->share_key.length + + sizeof(lctx->ret_flags) + sizeof(lctx->conf_alg.length) + lctx->conf_alg.length + sizeof(lctx->derived_conf_key.length) + lctx->derived_conf_key.length + sizeof(lctx->intg_alg.length) + lctx->intg_alg.length + sizeof(lctx->derived_integ_key.length) + - lctx->derived_integ_key.length + - sizeof(lctx->keyestb_alg.length) + lctx->keyestb_alg.length + - sizeof(lctx->owf_alg.length) + lctx->owf_alg.length; + lctx->derived_integ_key.length; if (!(buf->value = calloc(1, buf_size))) goto out_err; p = buf->value; end = buf->value + buf_size; + if (WRITE_BYTES(&p, end, lctx->version)) + goto out_err; + printerr(2, "DEBUG: exporting version = %d\n", lctx->version); + if (write_buffer(&p, end, &lctx->ctx_id)) goto out_err; + printerr(2, "DEBUG: exporting ctx_id(%d)\n", lctx->ctx_id.length); - if (WRITE_BYTES(&p, end, lctx->qop)) + if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err; + printerr(2, "DEBUG: exporting endtime = %d\n", lctx->endtime); if (write_buffer(&p, end, &lctx->mech_used)) goto out_err; + printerr(2, "DEBUG: exporting mech oid (%d)\n", lctx->mech_used.length); if (WRITE_BYTES(&p, end, lctx->ret_flags)) goto out_err; - - if (WRITE_BYTES(&p, end, lctx->req_flags)) - goto out_err; - - if (write_buffer(&p, end, &lctx->share_key)) - goto out_err; + printerr(2, "DEBUG: exporting ret_flags = %d\n", lctx->ret_flags); if (write_buffer(&p, end, &lctx->conf_alg)) goto out_err; + printerr(2, "DEBUG: exporting conf_alg oid (%d)\n", lctx->conf_alg.length); if (write_buffer(&p, end, &lctx->derived_conf_key)) goto out_err; + printerr(2, "DEBUG: exporting conf key (%d)\n", lctx->derived_conf_key.length); if (write_buffer(&p, end, &lctx->intg_alg)) goto out_err; + printerr(2, "DEBUG: exporting intg_alg oid (%d)\n", lctx->intg_alg.length); if (write_buffer(&p, end, &lctx->derived_integ_key)) goto out_err; - - if (write_buffer(&p, end, &lctx->keyestb_alg)) - goto out_err; - - if (write_buffer(&p, end, &lctx->owf_alg)) - goto out_err; + printerr(2, "DEBUG: exporting intg key (%d)\n", lctx->derived_integ_key.length); buf->length = p - (char *)buf->value; return 0; @@ -140,7 +139,7 @@ out_err: * and only export those fields to the kernel. */ int -serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf) +serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, uint32_t *endtime) { OM_uint32 vers, ret, maj_stat, min_stat; void *ret_ctx = 0; @@ -163,6 +162,9 @@ serialize_spkm3_ctx(gss_ctx_id_t ctx, gs } ret = prepare_spkm3_ctx_buffer(lctx, buf); + if (endtime) + *endtime = lctx->endtime; + maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, ret_ctx); if (maj_stat != GSS_S_COMPLETE) diff -puN support/include/exportfs.h~CITI_NFS4_ALL support/include/exportfs.h --- nfs-utils-1.0.10-asci/support/include/exportfs.h~CITI_NFS4_ALL 2006-09-23 18:39:28.305517000 -0400 +++ nfs-utils-1.0.10-asci-kwc/support/include/exportfs.h 2006-09-23 18:39:41.105676000 -0400 @@ -23,6 +23,13 @@ enum { MCL_MAXTYPES }; +enum { + FSLOC_NONE = 0, + FSLOC_REFER, + FSLOC_REPLICA, + FSLOC_STUB +}; + typedef struct mclient { struct mclient * m_next; char m_hostname[NFSCLNT_IDMAX+1]; diff -puN support/include/nfslib.h~CITI_NFS4_ALL support/include/nfslib.h --- nfs-utils-1.0.10-asci/support/include/nfslib.h~CITI_NFS4_ALL 2006-09-23 18:39:29.388983000 -0400 +++ nfs-utils-1.0.10-asci-kwc/support/include/nfslib.h 2006-09-23 18:39:46.731982000 -0400 @@ -57,6 +57,9 @@ enum cle_maptypes { CLE_MAP_UGIDD, }; +extern char *secflavor_name[]; +#define SECFLAVOR_COUNT 4 + /* * Data related to a single exports entry as returned by getexportent. * FIXME: export options should probably be parsed at a later time to @@ -80,6 +83,10 @@ struct exportent { int e_nsqgids; int e_fsid; char * e_mountpoint; + int e_fslocmethod; + char * e_fslocdata; + int e_secinfo_order[SECFLAVOR_COUNT+1]; + int e_secinfo_flags[SECFLAVOR_COUNT]; }; struct rmtabent { @@ -93,6 +100,7 @@ struct rmtabent { */ void setexportent(char *fname, char *type); struct exportent * getexportent(int,int); +void secinfo_show(FILE *fp, struct exportent *ep); void putexportent(struct exportent *xep); void endexportent(void); struct exportent * mkexportent(char *hname, char *path, char *opts); diff -puN support/nfs/exports.c~CITI_NFS4_ALL support/nfs/exports.c --- nfs-utils-1.0.10-asci/support/nfs/exports.c~CITI_NFS4_ALL 2006-09-23 18:39:30.812186000 -0400 +++ nfs-utils-1.0.10-asci-kwc/support/nfs/exports.c 2006-09-23 18:39:46.834970000 -0400 @@ -50,6 +50,10 @@ static int parsenum(char **cpp); static int parsemaptype(char *type); static void freesquash(void); static void syntaxerr(char *msg); +static unsigned int parse_flavors(char *str, struct exportent *ep); +static int secinfo_default(struct exportent *ep); +static void setflags(int mask, unsigned int *ap, struct exportent *ep); +static void clearflags(int mask, unsigned int *ap, struct exportent *ep); void setexportent(char *fname, char *type) @@ -94,6 +98,9 @@ getexportent(int fromkernel, int fromexp ee.e_squids = NULL; ee.e_sqgids = NULL; ee.e_mountpoint = NULL; + ee.e_fslocmethod = FSLOC_NONE; + ee.e_fslocdata = NULL; + ee.e_secinfo_order[0] = -1; ee.e_nsquids = 0; ee.e_nsqgids = 0; @@ -156,6 +163,19 @@ getexportent(int fromkernel, int fromexp } void +secinfo_show(FILE *fp, struct exportent *ep) +{ + int *p1, *p2; + for (p1=ep->e_secinfo_order; *p1>=0; p1=p2) { + fprintf(fp, ",sec=%s", secflavor_name[*p1]); + for (p2=p1+1; (*p2>=0) && (ep->e_secinfo_flags[*p1]==ep->e_secinfo_flags[*p2]); p2++) { + fprintf(fp, ":%s", secflavor_name[*p2]); + } + fprintf(fp, ",%s", (ep->e_secinfo_flags[*p1] & NFSEXP_READONLY)? "ro" : "rw"); + } +} + +void putexportent(struct exportent *ep) { FILE *fp; @@ -173,7 +193,6 @@ putexportent(struct exportent *ep) fprintf(fp, "%c", esc[i]); fprintf(fp, "\t%s(", ep->e_hostname); - fprintf(fp, "%s,", (ep->e_flags & NFSEXP_READONLY)? "ro" : "rw"); fprintf(fp, "%ssync,", (ep->e_flags & NFSEXP_ASYNC)? "a" : ""); fprintf(fp, "%swdelay,", (ep->e_flags & NFSEXP_GATHERED_WRITES)? "" : "no_"); @@ -199,7 +218,22 @@ putexportent(struct exportent *ep) if (ep->e_mountpoint) fprintf(fp, "mountpoint%s%s,", ep->e_mountpoint[0]?"=":"", ep->e_mountpoint); - + switch (ep->e_fslocmethod) { + case FSLOC_NONE: + break; + case FSLOC_REFER: + fprintf(fp, "refer=%s,", ep->e_fslocdata); + break; + case FSLOC_REPLICA: + fprintf(fp, "replicas=%s,", ep->e_fslocdata); + break; + case FSLOC_STUB: + fprintf(fp, "fsloc=stub,"); + break; + default: + xlog(L_ERROR, "unknown fsloc method for %s:%s", + ep->e_hostname, ep->e_path); + } fprintf(fp, "mapping="); switch (ep->e_maptype) { case CLE_MAP_IDENT: @@ -231,7 +265,9 @@ putexportent(struct exportent *ep) else fprintf(fp, "%d,", id[i]); } - fprintf(fp, "anonuid=%d,anongid=%d)\n", ep->e_anonuid, ep->e_anongid); + fprintf(fp, "anonuid=%d,anongid=%d", ep->e_anonuid, ep->e_anongid); + secinfo_show(fp, ep); + fprintf(fp, ")\n"); } void @@ -262,6 +298,8 @@ dupexportent(struct exportent *dst, stru } if (src->e_mountpoint) dst->e_mountpoint = strdup(src->e_mountpoint); + if (src->e_fslocdata) + dst->e_fslocdata = strdup(src->e_fslocdata); } struct exportent * @@ -276,6 +314,7 @@ mkexportent(char *hname, char *path, cha ee.e_squids = NULL; ee.e_sqgids = NULL; ee.e_mountpoint = NULL; + ee.e_secinfo_order[0] = -1; ee.e_nsquids = 0; ee.e_nsqgids = 0; @@ -315,6 +354,9 @@ parseopts(char *cp, struct exportent *ep int had_subtree_opt = 0; char *flname = efname?efname:"command line"; int flline = efp?efp->x_line:0; + int *p; + unsigned int active = 0; + int secmask = NFSEXP_READONLY; /* options that can vary per flavor */ squids = ep->e_squids; nsquids = ep->e_nsquids; sqgids = ep->e_sqgids; nsqgids = ep->e_nsqgids; @@ -337,9 +379,9 @@ parseopts(char *cp, struct exportent *ep /* process keyword */ if (strcmp(opt, "ro") == 0) - ep->e_flags |= NFSEXP_READONLY; + setflags(NFSEXP_READONLY, &active, ep); else if (strcmp(opt, "rw") == 0) - ep->e_flags &= ~NFSEXP_READONLY; + clearflags(NFSEXP_READONLY, &active, ep); else if (!strcmp(opt, "secure")) ep->e_flags &= ~NFSEXP_INSECURE_PORT; else if (!strcmp(opt, "insecure")) @@ -437,6 +479,24 @@ bad_option: ep->e_mountpoint = strdup(mp+1); else ep->e_mountpoint = strdup(""); + } else if (strncmp(opt, "fsloc=", 6) == 0) { + if (strcmp(opt+6, "stub") == 0) + ep->e_fslocmethod = FSLOC_STUB; + else { + xlog(L_ERROR, "%s:%d: bad option %s\n", + flname, flline, opt); + goto bad_option; + } + } else if (strncmp(opt, "refer=", 6) == 0) { + ep->e_fslocmethod = FSLOC_REFER; + ep->e_fslocdata = strdup(opt+6); + } else if (strncmp(opt, "replicas=", 9) == 0) { + ep->e_fslocmethod = FSLOC_REPLICA; + ep->e_fslocdata = strdup(opt+9); + } else if (strncmp(opt, "sec=", 4) == 0) { + active = parse_flavors(opt+4, ep); + if (!active) + goto bad_option; } else { xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n", flname, flline, opt); @@ -448,6 +508,12 @@ bad_option: cp++; } + if (!active) + active = secinfo_default(ep); + for (p=ep->e_secinfo_order; *p>=0; p++) + ep->e_secinfo_flags[*p] |= (ep->e_flags & ~secmask); + /* If did not use sec= option, ensure e_flags is backward compatible */ + ep->e_flags = ep->e_secinfo_flags[ep->e_secinfo_order[0]]; ep->e_squids = squids; ep->e_sqgids = sqgids; ep->e_nsquids = nsquids; @@ -576,3 +642,104 @@ syntaxerr(char *msg) efname, efp?efp->x_line:0, msg); } +char *secflavor_name[SECFLAVOR_COUNT] = { "sys", + "krb5", + "krb5i", + "krb5p" +}; + +static void +secinfo_addflavor(int bit, struct exportent *ep) +{ + int *p; + for (p=ep->e_secinfo_order; *p>=0; p++) { + if (*p == bit) + return; + } + *p++ = bit; + *p = -1; + ep->e_secinfo_flags[bit] = 0; +} + +static int +secinfo_nameindex(char *name) +{ + int i; + for (i=0; ie_hostname. */ +static int +secinfo_default(struct exportent *ep) +{ + int i=-1; + if (strncmp(ep->e_hostname, "gss/", 4) == 0) { + i = secinfo_nameindex(ep->e_hostname + 4); + if (i < 0) + xlog(L_WARNING, "unknown flavor %s\n", ep->e_hostname); + } + /* Default to auth_sys */ + if (i < 0) + i = secinfo_nameindex("sys"); + secinfo_addflavor(i, ep); + return 1<e_secinfo_flags[flavor] |= mask; + flavor++; + active >>= 1; + } +} + +/* Clears the bits in @mask for the appropriate security flavor flags. */ +static void +clearflags(int mask, unsigned int *ap, struct exportent *ep) +{ + int active, flavor=0; + if (!*ap) + *ap = secinfo_default(ep); + active = *ap; + while (active) { + if (active & 1) + ep->e_secinfo_flags[flavor] &= ~mask; + flavor++; + active >>= 1; + } +} diff -puN utils/exportfs/exportfs.c~CITI_NFS4_ALL utils/exportfs/exportfs.c --- nfs-utils-1.0.10-asci/utils/exportfs/exportfs.c~CITI_NFS4_ALL 2006-09-23 18:39:33.268284000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/exportfs/exportfs.c 2006-09-23 18:39:47.444819000 -0400 @@ -376,10 +376,12 @@ dump(int verbose) continue; } c = '('; + /* if (ep->e_flags & NFSEXP_READONLY) c = dumpopt(c, "ro"); else c = dumpopt(c, "rw"); + */ if (ep->e_flags & NFSEXP_ASYNC) c = dumpopt(c, "async"); if (ep->e_flags & NFSEXP_GATHERED_WRITES) @@ -416,7 +418,20 @@ dump(int verbose) c = dumpopt(c, "anonuid=%d", ep->e_anonuid); if (ep->e_anongid != -2) c = dumpopt(c, "anongid=%d", ep->e_anongid); - + switch(ep->e_fslocmethod) { + case FSLOC_NONE: + break; + case FSLOC_REFER: + c = dumpopt(c, "refer=%s", ep->e_fslocdata); + break; + case FSLOC_REPLICA: + c = dumpopt(c, "replicas=%s", ep->e_fslocdata); + break; + case FSLOC_STUB: + c = dumpopt(c, "fsloc=stub"); + break; + } + secinfo_show(stdout, ep); printf("%c\n", (c != '(')? ')' : ' '); } } diff -puN utils/exportfs/exports.man~CITI_NFS4_ALL utils/exportfs/exports.man --- nfs-utils-1.0.10-asci/utils/exportfs/exports.man~CITI_NFS4_ALL 2006-09-23 18:39:35.101382000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/exportfs/exports.man 2006-09-23 18:39:43.033774000 -0400 @@ -322,6 +322,19 @@ The value 0 has a special meaning when concept of a root of the overall exported filesystem. The export point exported with fsid=0 will be used as this root. +.TP +.IR refer= path@host[+host][:path@host[+host]] +A client referencing the export point will be directed to choose from +the given list an alternative location for the filesystem. +(Note that the server currently needs to have a filesystem mounted here, +generally using mount --bind, although it is not actually exported.) + +.TP +.IR replicas= path@host[+host][:path@host[+host]] +If the client asks for alternative locations for the export point, it +will be given this list of alternatives. (Note that actual replication +of the filesystem must be handled elsewhere.) + .SS User ID Mapping .PP .I nfsd diff -puN utils/mountd/cache.c~CITI_NFS4_ALL utils/mountd/cache.c --- nfs-utils-1.0.10-asci/utils/mountd/cache.c~CITI_NFS4_ALL 2006-09-23 18:39:37.257480000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/mountd/cache.c 2006-09-23 18:39:47.934519000 -0400 @@ -26,6 +26,7 @@ #include "exportfs.h" #include "mountd.h" #include "xmalloc.h" +#include "fsloc.h" /* * Support routines for text-based upcalls. @@ -239,6 +240,43 @@ void nfsd_fh(FILE *f) return; } +static void write_fsloc(FILE *f, struct exportent *ep, char *path) +{ + struct servers *servers; + + if (ep->e_fslocmethod == FSLOC_NONE) + return; + + servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata, path); + if (!servers) + return; + qword_print(f, "fsloc"); + qword_printint(f, servers->h_num); + if (servers->h_num >= 0) { + int i; + for (i=0; ih_num; i++) { + qword_print(f, servers->h_mp[i]->h_host); + qword_print(f, servers->h_mp[i]->h_path); + } + } + qword_printint(f, servers->h_referral); + release_replicas(servers); +} + +static void write_secinfo(FILE *f, struct exportent *ep) +{ + int *p; + qword_print(f, "secinfo"); + for (p=ep->e_secinfo_order; *p>=0; p++) + ; /* Do nothing */ + qword_printint(f, p - ep->e_secinfo_order); + for (p=ep->e_secinfo_order; *p>=0; p++) { + qword_print(f, secflavor_name[*p]); + qword_printint(f, ep->e_secinfo_flags[*p]); + } + +} + void nfsd_export(FILE *f) { /* requests are: @@ -295,6 +333,8 @@ void nfsd_export(FILE *f) qword_printint(f, found->m_export.e_anonuid); qword_printint(f, found->m_export.e_anongid); qword_printint(f, found->m_export.e_fsid); + write_fsloc(f, &found->m_export, path); + write_secinfo(f, &found->m_export); mountlist_add(dom, path); } qword_eol(f); @@ -370,6 +410,8 @@ void cache_export_ent(char *domain, stru qword_printint(f, exp->e_anonuid); qword_printint(f, exp->e_anongid); qword_printint(f, exp->e_fsid); + write_fsloc(f, exp, exp->e_path); + write_secinfo(f, exp); qword_eol(f); fclose(f); diff -puN /dev/null utils/mountd/fsloc.c --- /dev/null 2006-09-22 08:14:07.867789970 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/mountd/fsloc.c 2006-09-23 18:39:43.978323000 -0400 @@ -0,0 +1,193 @@ +#include +#include +#include + +#include "fsloc.h" +#include "exportfs.h" + +/* Debugging tool: prints out @servers info to syslog */ +static void replicas_print(struct servers *sp) +{ + int i; + if (!sp) { + syslog(LOG_INFO, "NULL replicas pointer"); + return; + } + syslog(LOG_INFO, "replicas listsize=%i", sp->h_num); + for (i=0; ih_num; i++) { + syslog(LOG_INFO, "%s:/%s", + sp->h_mp[i]->h_host, sp->h_mp[i]->h_path); + } +} + +/* Called by setting 'Method = stub' in config file. Just returns + * some syntactically correct gibberish for testing purposes. + */ +static struct servers *method_stub(char *key) +{ + struct servers *sp; + struct mount_point *mp; + + syslog(LOG_INFO, "called method_stub"); + sp = malloc(sizeof(struct servers)); + if (!sp) + return NULL; + mp = calloc(1, sizeof(struct mount_point)); + if (!mp) { + free(sp); + return NULL; + } + sp->h_num = 1; + sp->h_mp[0] = mp; + mp->h_host = strdup("stub_server"); + mp->h_path = strdup("/my/test/path"); + sp->h_referral = 1; + return sp; +} + +/* Scan @list, which is a NULL-terrminated array of strings of the + * form host[:host]:/path, and return corresponding servers structure. + */ +static struct servers *parse_list(char **list) +{ + int i; + struct servers *res; + struct mount_point *mp; + char *cp; + + res = malloc(sizeof(struct servers)); + if (!res) + return NULL; + res->h_num = 0; + + /* parse each of the answers in sucession. */ + for (i=0; list[i] && ih_mp[i] = mp; + res->h_num++; + mp->h_host = strndup(list[i], cp - list[i]); + cp++; + mp->h_path = strdup(cp); + } + return res; +} + +/* Converts from path@host[+host][:path@host[+host]] to + * host[:host]:path[@host[:host]:path] + * + * XXX Once the interface is stabilized, we can put the kernel and + * userland formats into agreement, so this won't be necessary. + */ +static char *strconvert(const char *in) +{ + char *path, *ptr, *copy, *rv, *rvptr, *next; + next = copy = strdup(in); + rvptr = rv = malloc(strlen(in) + 1); + if (!copy || !rv) + goto error; + while (next) { + ptr = strsep(&next, ":"); + path = strsep(&ptr, "@"); + if (!ptr) + goto error; + while (*ptr) { + if (*ptr == '+') { + *rvptr++ = ':'; + ptr++; + } + else + *rvptr++ = *ptr++; + } + *rvptr++ = ':'; + while (*path) { + *rvptr++ = *path++; + } + if (next) + *rvptr++ = '@'; + else + *rvptr = '\0'; + } + free(copy); + return rv; +error: + free(copy); + free(rv); + return NULL; +} + +/* @data is a string of form path@host[+host][:path@host[+host]] + */ +static struct servers *method_list(char *data) +{ + char *copy, *ptr=data; + char **list; + int i, listsize; + struct servers *rv=NULL; + + syslog(LOG_INFO, "method_list(%s)\n", data); + for (ptr--, listsize=1; ptr; ptr=index(ptr, ':'), listsize++) + ptr++; + list = malloc(listsize * sizeof(char *)); + copy = strconvert(data); + syslog(LOG_INFO, "converted to %s\n", copy); + if (list && copy) { + ptr = copy; + for (i=0; ih_referral = 1; + break; + case FSLOC_REPLICA: + sp = method_list(data); + if (sp) + sp->h_referral = 0; + break; + case FSLOC_STUB: + sp = method_stub(data); + break; + default: + syslog(LOG_WARNING, "Unknown method = %i", method); + } + replicas_print(sp); + return sp; +} + +void release_replicas(struct servers *server) +{ + int i; + + if (!server) return; + for (i = 0; i < server->h_num; i++) { + free(server->h_mp[i]->h_host); + free(server->h_mp[i]->h_path); + free(server->h_mp[i]); + } + free(server); +} diff -puN /dev/null utils/mountd/fsloc.h --- /dev/null 2006-09-22 08:14:07.867789970 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/mountd/fsloc.h 2006-09-23 18:39:44.061323000 -0400 @@ -0,0 +1,20 @@ +#ifndef FSLOC_H +#define FSLOC_H + +#define FSLOC_MAX_LIST 40 + +struct mount_point { + char *h_host; + char *h_path; +}; + +struct servers { + int h_num; + struct mount_point *h_mp[FSLOC_MAX_LIST]; + int h_referral; /* 0=replica, 1=referral */ +}; + +struct servers *replicas_lookup(int method, char *data, char *key); +void release_replicas(struct servers *server); + +#endif /* FSLOC_H */ diff -puN utils/mountd/Makefile.am~CITI_NFS4_ALL utils/mountd/Makefile.am --- nfs-utils-1.0.10-asci/utils/mountd/Makefile.am~CITI_NFS4_ALL 2006-09-23 18:39:40.342075000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/mountd/Makefile.am 2006-09-23 18:39:43.087774000 -0400 @@ -8,7 +8,7 @@ KPREFIX = @kprefix@ sbin_PROGRAMS = mountd mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \ - svc_run.c mountd.h + svc_run.c fsloc.c mountd.h mountd_LDADD = ../../support/export/libexport.a \ ../../support/nfs/libnfs.a \ ../../support/misc/libmisc.a \ diff -puN utils/gssd/gssd.c~CITI_NFS4_ALL utils/gssd/gssd.c --- nfs-utils-1.0.10-asci/utils/gssd/gssd.c~CITI_NFS4_ALL 2006-09-23 18:39:49.920617000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd.c 2006-09-23 18:40:25.221832000 -0400 @@ -52,8 +52,10 @@ #include "err_util.h" #include "gss_util.h" #include "krb5_util.h" +#include -char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR; +char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR; +char pipefs_nfsdir[PATH_MAX] = GSSD_PIPEFS_DIR; char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE; char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR; @@ -107,8 +109,8 @@ main(int argc, char *argv[]) rpc_verbosity++; break; case 'p': - strncpy(pipefsdir, optarg, sizeof(pipefsdir)); - if (pipefsdir[sizeof(pipefsdir)-1] != '\0') + strncpy(pipefs_dir, optarg, sizeof(pipefs_dir)); + if (pipefs_dir[sizeof(pipefs_dir)-1] != '\0') errx(1, "pipefs path name too long"); break; case 'k': @@ -126,10 +128,10 @@ main(int argc, char *argv[]) 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"); + snprintf(pipefs_nfsdir, sizeof(pipefs_nfsdir), "%s/%s", + pipefs_dir, GSSD_SERVICE_NAME); + if (pipefs_nfsdir[sizeof(pipefs_nfsdir)-1] != '\0') + errx(1, "pipefs_nfsdir path name too long"); if ((progname = strrchr(argv[0], '/'))) progname++; @@ -157,6 +159,12 @@ main(int argc, char *argv[]) /* Process keytab file and get machine credentials */ gssd_refresh_krb5_machine_creds(); + /* Determine Kerberos information from the kernel */ + gssd_obtain_kernel_krb5_info(); + /* Create our own anonymous session */ + if (nfskrutil_newsession(NULL, NULL)) + printerr(0, "Warning: unable to join new session\n"); + gssd_run(); printerr(0, "gssd_run returned!\n"); diff -puN utils/gssd/gssd.h~CITI_NFS4_ALL utils/gssd/gssd.h --- nfs-utils-1.0.10-asci/utils/gssd/gssd.h~CITI_NFS4_ALL 2006-09-23 18:39:50.898166000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd.h 2006-09-23 18:40:25.703443000 -0400 @@ -34,6 +34,8 @@ #include #include #include +#include +#include #define MAX_FILE_NAMELEN 32 #define FD_ALLOC_BLOCK 32 @@ -51,6 +53,8 @@ #define GSSD_SERVICE_NAME "nfs" #define GSSD_SERVICE_NAME_LEN 3 +#define INFOBUFLEN 256 + /* * The gss mechanisms that we can handle */ @@ -58,7 +62,8 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUT -extern char pipefsdir[PATH_MAX]; +extern char pipefs_dir[PATH_MAX]; +extern char pipefs_nfsdir[PATH_MAX]; extern char keytabfile[PATH_MAX]; extern char ccachedir[PATH_MAX]; @@ -72,19 +77,57 @@ struct clnt_info { char *servername; int prog; int vers; + uid_t uid; + uid_t saved_uid; + gid_t gid; char *protocol; + char *principal; int krb5_fd; int krb5_poll_index; int spkm3_fd; int spkm3_poll_index; + int keyring_fd; + int keyring_poll_index; + char *mechanism; + char *ip_string; + int port; + uint32_t lifetime; + key_serial_t key; + key_serial_t authkey; + key_serial_t thread_ring; + key_serial_t process_ring; + key_serial_t session_ring; + key_serial_t ids_ring; + key_serial_t ids_key; }; +/* gssd_main_loop.c */ +void gssd_run(void); + +/* gssd_proc.c */ 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); +inline int set_user_fsuid(struct clnt_info *clp); +inline int restore_our_fsuid(struct clnt_info *clp); + +int do_downcall(int fd, uid_t uid, struct authgss_private_data *pd, + gss_buffer_desc *context_token); +int do_error_downcall(int fd, uid_t uid, int err); + +int create_auth_rpc_client(struct clnt_info *clp, + CLIENT **clnt_return, + AUTH **auth_return, + uid_t uid, + int authtype); + +/* gssd_keyring.c */ +int serialize_ctx_key_data(char *buf, int *buflen, + struct clnt_info *clp, + struct authgss_private_data *pd, + gss_buffer_desc *context_token); +void handle_keyring_upcall(struct clnt_info *clp); #endif /* _RPC_GSSD_H_ */ diff -puN utils/gssd/gssd_main_loop.c~CITI_NFS4_ALL utils/gssd/gssd_main_loop.c --- nfs-utils-1.0.10-asci/utils/gssd/gssd_main_loop.c~CITI_NFS4_ALL 2006-09-23 18:39:52.464547000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd_main_loop.c 2006-09-23 18:40:26.482214000 -0400 @@ -68,6 +68,17 @@ scan_poll_results(int ret) for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { + i = clp->keyring_poll_index; + if (i >= 0 && pollarray[i].revents) { + if (pollarray[i].revents & POLLHUP) + dir_changed = 1; + if (pollarray[i].revents & POLLIN) + handle_keyring_upcall(clp); + pollarray[clp->keyring_poll_index].revents = 0; + ret--; + if (!ret) + break; + } i = clp->krb5_poll_index; if (i >= 0 && pollarray[i].revents) { if (pollarray[i].revents & POLLHUP) @@ -106,9 +117,9 @@ gssd_run() dn_act.sa_flags = SA_SIGINFO; sigaction(DNOTIFY_SIGNAL, &dn_act, NULL); - if ((fd = open(pipefsdir, O_RDONLY)) == -1) { + if ((fd = open(pipefs_nfsdir, O_RDONLY)) == -1) { printerr(0, "ERROR: failed to open %s: %s\n", - pipefsdir, strerror(errno)); + pipefs_nfsdir, strerror(errno)); exit(1); } fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL); diff -puN utils/gssd/krb5_util.h~CITI_NFS4_ALL utils/gssd/krb5_util.h --- nfs-utils-1.0.10-asci/utils/gssd/krb5_util.h~CITI_NFS4_ALL 2006-09-23 18:39:55.589071000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/krb5_util.h 2006-09-23 18:40:28.116479000 -0400 @@ -22,6 +22,10 @@ int gssd_refresh_krb5_machine_creds(voi 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); +void gssd_obtain_kernel_krb5_info(void); +int gssd_krb5_get_principal(struct clnt_info *clp, char *ccname); +void gssd_set_krb5_ccache_name(char *ccname); + #ifdef HAVE_SET_ALLOWABLE_ENCTYPES int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid); diff -puN /dev/null aclocal/keyring.m4 --- /dev/null 2006-09-22 08:14:07.867789970 -0400 +++ nfs-utils-1.0.10-asci-kwc/aclocal/keyring.m4 2006-09-23 18:40:06.150401000 -0400 @@ -0,0 +1,9 @@ +dnl Checks for keyring utility library support +dnl +dnl +dnl Check to see if libkeyutils exists +AC_DEFUN([AC_KEYRING_CHECK], [ + AC_MSG_CHECKING(for keyring utility library support) + AC_CHECK_LIB(keyutils, add_key) +]) + diff -puN /dev/null utils/gssd/libnfskrutil_symbol_versions --- /dev/null 2006-09-22 08:14:07.867789970 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/libnfskrutil_symbol_versions 2006-09-23 18:40:07.865499000 -0400 @@ -0,0 +1,20 @@ +libnfskrutil_CITI_0 { + nfskrutil_add_gss_cred; + nfskrutil_delete_gss_cred; + nfskrutil_destroy_ctx_keys; + nfskrutil_newsession; + nfskrutil_set_default_gss_cred; + nfskrutil_get_default_gss_cred; + nfskrutil_get_gss_cred_list; + nfskrutil_free_gss_cred_list; +}; + +HIDDEN { +local: + __*; + _rest*; + _save*; + generic_*; + internal_*; + locate_symbol; +}; diff -puN utils/gssd/Makefile.am~CITI_NFS4_ALL utils/gssd/Makefile.am --- nfs-utils-1.0.10-asci/utils/gssd/Makefile.am~CITI_NFS4_ALL 2006-09-23 18:40:02.710796000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/Makefile.am 2006-09-23 18:40:35.668973000 -0400 @@ -7,6 +7,15 @@ KPREFIX = @kprefix@ sbin_PREFIXED = gssd svcgssd sbin_PROGRAMS = $(sbin_PREFIXED) gss_clnt_send_err sbin_SCRIPTS = gss_destroy_creds +bin_PROGRAMS = nfslogin nfslogout sessionsh +noinst_PROGRAMS = nfskrutil-test +lib_LTLIBRARIES = libnfskrutil.la +libnfskrutil_la_SOURCES = nfskrutil.c +libnfskrutil_la_LDFLAGS = -version-info 0:0:0 \ + -Wl,--version-script,$(srcdir)/libnfskrutil_symbol_versions +libnfskrutil_la_LIBADD = -lkeyutils +include_HEADERS = nfskrutil.h + EXTRA_DIST = \ gss_destroy_creds \ @@ -29,6 +38,7 @@ COMMON_SRCS = \ gssd_SOURCES = \ $(COMMON_SRCS) \ gssd.c \ + gssd_keyring.c \ gssd_main_loop.c \ gssd_proc.c \ krb5_util.c \ @@ -37,7 +47,7 @@ gssd_SOURCES = \ krb5_util.h \ write_bytes.h -gssd_LDADD = $(RPCSECGSS_LIBS) $(KRBLIBS) +gssd_LDADD = $(RPCSECGSS_LIBS) $(KRBLIBS) -lnfskrutil gssd_LDFLAGS = $(KRBLDFLAGS) gssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \ @@ -69,6 +79,19 @@ gss_clnt_send_err_SOURCES = gss_clnt_sen gss_clnt_send_err_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \ $(RPCSECGSS_CFLAGS) $(KRBCFLAGS) +nfskrutil_test_SOURCES = nfskrutil-test.c +nfskrutil_test_LDADD = -lnfskrutil + +nfslogin_SOURCES = nfslogin.c +nfslogin_LDADD = $(KRBLIBS) -lnfskrutil +nfslogin_LDFLAGS = $(KRBLDFLAGS) +nfslogout_SOURCES = nfslogout.c +nfslogout_LDADD = $(KRBLIBS) -lnfskrutil +nfslogout_LDFLAGS = $(KRBLDFLAGS) +sessionsh_SOURCES = sessionsh.c +sessionsh_LDADD = $(KRBLIBS) -lnfskrutil +sessionsh_LDFLAGS = $(KRBLDFLAGS) + MAINTAINERCLEANFILES = Makefile.in ####################################################################### diff -puN /dev/null utils/gssd/nfskrutil.c --- /dev/null 2006-09-22 08:14:07.867789970 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/nfskrutil.c 2006-09-23 18:40:10.532371000 -0400 @@ -0,0 +1,1039 @@ +/* + * nfskrutil.c + * + * Copyright (c) 2006 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_LIBKEYUTILS + + +#include +#include +#include +#include +#include +#include +#include +#include "nfskrutil-int.h" + + +/********************************************************************* + * Internally definitions + *********************************************************************/ +/*-------------------------------------------------------------------* + * + * The problem(s) with locking: + * + * - Don't want a global lock + * - Can't do create/unlink more than once in a process and + * get the same file each time. (Recreating the same name + * after unlink gets a new inode.) + * - Don't want to keep around all these files, so we really + * need to do unlink + * - How to do all this with unrelated processes? + * + *-------------------------------------------------------------------*/ +#define NFSKRUTIL_LOCK_NAME_LEN 128 +#define NFSKRUTIL_LOCK_NAME_FMT "/tmp/.krutil_lock_%d" + +typedef enum _nfskrutil_locktype { + KRL_LOCKTYPE_EXCLUSIVE = 1, + KRL_LOCKTYPE_SHARED = 2, + KRL_LOCKTYPE_UNLOCK = 3, +} nfskrutil_locktype_t; + +typedef struct _nfskrutil_lock { + int krl_valid; + int krl_fd; + char krl_filename[NFSKRUTIL_LOCK_NAME_LEN]; + nfskrutil_locktype_t krl_locktype; +} nfskrutil_lock_t; + + +/********************************************************************* + * Internally visible (static) functions + *********************************************************************/ +/* + * Locking routines. Given a keyring serial number, use fcntl to + * lock "that keyring". We don't want a global lock that becomes + * a bottleneck. + */ + +/* Prepare a lock to be used */ +static inline int +nfskrlock_init(key_serial_t key, nfskrutil_lock_t *lock) +{ + if (!key || !lock) + return EINVAL; + lock->krl_valid = 0; + + snprintf(lock->krl_filename, NFSKRUTIL_LOCK_NAME_LEN, + NFSKRUTIL_LOCK_NAME_FMT, key); + lock->krl_fd = open(lock->krl_filename, O_RDWR|O_CREAT, + S_IRWXU|S_IRWXG|S_IRWXO); + if (lock->krl_fd < 0) { + return errno; + } + lock->krl_valid = 1; + + return 0; +} + +/* Indicate we are done with a lock */ +static inline int +nfskrlock_fini(nfskrutil_lock_t *lock) +{ + int err; + if (lock->krl_fd < 0) { + return EINVAL; + } + + if ((err = close(lock->krl_fd))) { + return err; + } + + /* Ignore errors from unlink */ + if ((err = unlink(lock->krl_filename))) { + } + + lock->krl_fd = -1; + lock->krl_valid = 0; + + return 0; +} + +/* Get the lock of type "type" on filename */ +static inline int +nfskrlock_lock(nfskrutil_lock_t *lock, nfskrutil_locktype_t locktype) +{ + struct flock krl_flock; + char debug_string[40]; + int retval; + + if (!lock || !lock->krl_valid) + return EINVAL; + + switch (locktype) { + case KRL_LOCKTYPE_EXCLUSIVE: + krl_flock.l_type = F_WRLCK; + strcpy(debug_string, "Lock EXCLUSIVE"); + break; + case KRL_LOCKTYPE_SHARED: + krl_flock.l_type = F_RDLCK; + strcpy(debug_string, "Lock SHARED"); + break; + case KRL_LOCKTYPE_UNLOCK: + krl_flock.l_type = F_UNLCK; + strcpy(debug_string, "UNLOCK"); + break; + default: + return EINVAL; + } + + krl_flock.l_whence = SEEK_SET; + krl_flock.l_start = 0; + krl_flock.l_len = 0; + + do { + retval = fcntl(lock->krl_fd, F_SETLKW, &krl_flock); + } while (retval == -1 && errno == EINTR); + if (retval == -1) + return errno; + + return 0; +} + +/* + * Return 1 if "scope" is a valid scope value. Return 0 otherwise. + */ +static inline int +valid_scope(const nfskrutil_scope_t scope) +{ + if ((scope == NFSKRUTIL_SCOPE_SESSION) || + (scope == NFSKRUTIL_SCOPE_PROCESS) || + (scope == NFSKRUTIL_SCOPE_THREAD)) + return 1; + else + return 0; +} + +/* + * Return 1 if "mech" is a valid mechanism value. Return 0 otherwise. + */ +static inline int +valid_mech(const nfskrutil_mech_t mech) +{ + if ((mech == NFSKRUTIL_MECH_KRB5) || + (mech == NFSKRUTIL_MECH_SPKM3) || + (mech == NFSKRUTIL_MECH_LIPKEY)) + return 1; + else + return 0; +} + +/* + * Given a mechanism value, "mech" return the associated name. + */ +static inline const char * +mech2string(const nfskrutil_mech_t mech) +{ + switch (mech) { + case NFSKRUTIL_MECH_KRB5: + return "krb5"; + break; + case NFSKRUTIL_MECH_SPKM3: + return "spkm3"; + break; + case NFSKRUTIL_MECH_LIPKEY: + return "lipkey"; + break; + default: + return "invalid"; + break; + } +} + +/* + * Given a scope value, "scope" return the associated special keyring value. + * + * This should only be used by the "user" side routines. The routines + * used by gssd should have the explicit serial numbers. + */ +static inline key_serial_t +scope2ring(nfskrutil_scope_t scope) +{ + key_serial_t id; + + switch (scope) { + case NFSKRUTIL_SCOPE_THREAD: + id = KEY_SPEC_THREAD_KEYRING; + break; + case NFSKRUTIL_SCOPE_PROCESS: + id = KEY_SPEC_PROCESS_KEYRING; + break; + case NFSKRUTIL_SCOPE_SESSION: + default: + id = KEY_SPEC_SESSION_KEYRING; + break; + } + return id; +} + +/* + * Retrieve the mechanism default credential for mechanism "mech" in + * the keyring "keyring" + * + * Locking: shared lock on keyring should be held + */ +static key_serial_t +get_default_key(const key_serial_t keyring, const nfskrutil_mech_t mech) +{ + char dfltname[NFSKRUTIL_MAX_DESCRIPTION]; + + snprintf(dfltname, sizeof(dfltname), "%s:%s", + mech2string(mech), "default"); + + return keyctl_search(keyring, NFSKRUTIL_KEYTYPE_USER, dfltname, 0); +} + +/* + * Get a lock on the specified keyring + */ +static int +lock_ring(key_serial_t ring, nfskrutil_lock_t *lock, + nfskrutil_locktype_t locktype) +{ +#ifdef DO_LOCKING + if (nfskrlock_init(ring, lock)) + return -1; + if (nfskrlock_lock(lock, locktype)) { + return -1; + } +#endif + return 0; +} + +/* + * Release a lock + */ +static int +unlock_ring(nfskrutil_lock_t *lock) +{ +#ifdef DO_LOCKING + if(!lock) { + return -1; + } + + if (nfskrlock_lock(lock, KRL_LOCKTYPE_UNLOCK)) { + return -1; + } + if (nfskrlock_fini(lock)) { + return -1; + } +#endif + return 0; +} + +/* + * Return the serial number of the keyring holding + * the gss credential information. + * + * If "ring" is zero, then use request_key() to find the most + * relevant credential information by using the normal search + * order (thread, process, session, user, user session). + * + * If "ring" is non-zero, then look only in the specified ring + * using the keyctl_search() method. In this case, if the + * credentials keyring doesn't already exist, and "create" is + * non-zero, then the credentials keyring is created. + * + * Locking: If successful, returns with the cred_ring locked; + * lock->krl_locktype should contain the type of + * lock requested. + */ +static key_serial_t +get_cred_ring(key_serial_t ring, int create, + nfskrutil_lock_t *lock, nfskrutil_locktype_t locktype) +{ + key_serial_t retring; + + if (!lock || (locktype != KRL_LOCKTYPE_SHARED && + locktype != KRL_LOCKTYPE_EXCLUSIVE )) + return -1; + + if (ring == 0) { + retring = request_key(NFSKRUTIL_KEYTYPE_KEYRING, + NFSKRUTIL_GSSCREDS_RING_NAME, NULL, 0); + } else { + retring = keyctl_search(ring, NFSKRUTIL_KEYTYPE_KEYRING, + NFSKRUTIL_GSSCREDS_RING_NAME, 0); + if ((retring == -1) && (errno == ENOKEY) && create) { + retring = add_key(NFSKRUTIL_KEYTYPE_KEYRING, + NFSKRUTIL_GSSCREDS_RING_NAME, + NULL, 0, ring); + } + } +#ifdef DO_LOCKING + if (retring >= 0) { + if (nfskrlock_init(retring, lock)) + return -1; + if (nfskrlock_lock(lock, locktype)) { + return -1; + } + } +#endif + return retring; +} + +/* + * Releases the lock on the cred_ring + */ +static int +put_cred_ring(nfskrutil_lock_t *lock) +{ + return (unlock_ring(lock)); +} + +/* + * Make "newname" the default credential for mechanism "mech" + * + * Locking: The parent of "keyring" should be exclusive locked + */ +static int +make_default_cred(const key_serial_t keyring, + const nfskrutil_mech_t mech, + const char *newname, + char **oldname) +{ + char dfltname[NFSKRUTIL_MAX_DESCRIPTION]; + key_serial_t default_key; + int code; + int retval = 0; + + if (oldname) + *oldname = NULL; + + default_key = get_default_key(keyring, mech); + if (default_key >= 0) { + /* key already exists, try to get old value and then update it */ + if (oldname) { + code = keyctl_read_alloc(default_key, (void **)oldname); + if (code == -1) { + *oldname = NULL; + } + } + code = keyctl_update(default_key, newname, strlen(newname) + 1); + if (code) { + retval = errno; + goto out; + } + } else if (errno == ENOKEY) { + /* key doesn't exist, create it */ + snprintf(dfltname, sizeof(dfltname), "%s:%s", + mech2string(mech), "default"); + + default_key = add_key(NFSKRUTIL_KEYTYPE_USER, dfltname, newname, + strlen(newname) + 1, keyring); + if (default_key == -1) { + retval = errno; + goto out; + } + } else { + /* error from keyctl_search */ + retval = errno; + } + + out: + return retval; +} + +/********************************************************************* + * Externally visible functions + *********************************************************************/ + +/* XXX Need locking? XXX races? XXX*/ + + +/* Valid flag values for the add function */ +#define NFSKRUTIL_VALID_ADD_FLAGS (NFSKRUTIL_ADDFLAG_DEFAULT) + +/* + * Create a new "anonymous" session (if no name supplied) + * or join a named session (creating it if it doesn't + * already exist). + * + * Returns the new sesion keyring serial number if requested. + * + * Locking: None required + */ +int +nfskrutil_newsession(const char *name, key_serial_t *retkey) +{ + key_serial_t session_ring; + int retval; + + session_ring = keyctl_join_session_keyring(name); + if (session_ring < 0) + retval = errno; + else + retval = 0; + + if (retkey) + *retkey = session_ring; + + return retval; +} + + +/* + * Add a credential that may be used by gssd in negotiating + * contexts. If flags has NFSKRUTIL_ADDFLAG_DEFAULT set, this + * new credential becomes the default for this mechanism. + * + * Locking: Obtains and releases exclusive lock on parent keyring + */ +int +nfskrutil_add_gss_cred(const nfskrutil_scope_t scope, + const nfskrutil_mech_t mech, + const char *name, + u_int32_t flags) +{ + key_serial_t parent_ring, cred_ring, retkey; + char full_name[NFSKRUTIL_MAX_DESCRIPTION]; + int retval; + char dummy_data[2] = "C"; + nfskrutil_lock_t lock; + + /* Check input values */ + if (!valid_scope(scope)) + return NFSKRUTIL_ERR_BAD_SCOPE; + if (!valid_mech(mech)) + return NFSKRUTIL_ERR_BAD_MECH; + if (!name) + return NFSKRUTIL_ERR_NAME_MISSING; + if (flags & ~(NFSKRUTIL_VALID_ADD_FLAGS)) + return NFSKRUTIL_ERR_FLAG_NOSUPP; + + /* Now add the credential */ + parent_ring = scope2ring(scope); + + cred_ring = get_cred_ring(parent_ring, 1, &lock, KRL_LOCKTYPE_EXCLUSIVE); + if (cred_ring < 0) + return ENOENT; + + snprintf(full_name, sizeof(full_name), "%s:%s", mech2string(mech), name); + + retkey = keyctl_search(cred_ring, NFSKRUTIL_KEYTYPE_USER, full_name, 0); + if (retkey == -1) { +#if 0 + retkey = add_key(NFSKRUTIL_KEYTYPE_USER, full_name, NULL, 0, cred_ring); +#else + /* We cannot create an empty key, so we create it with dummy data */ + retkey = add_key(NFSKRUTIL_KEYTYPE_USER, full_name, + dummy_data, sizeof(dummy_data), cred_ring); +#endif + if (retkey == -1) { + retval = errno; + goto out_unlock; + } + } + + /* Make it the default if requested */ + if (flags & NFSKRUTIL_ADDFLAG_DEFAULT) { + retval = make_default_cred(cred_ring, mech, name, NULL); + if (retval) { + goto out_unlock; + } + } + retval = 0; +out_unlock: + if (put_cred_ring(&lock)) + assert(0); + return retval; +} + +/* + * Set the default credential to be used by gssd in negotiating + * contexts. Return the old default, if requested, in the + * user-supplied buffer pointed to by oldname. + * + * Locking: Obtains and releases exclusive lock on parent keyring + */ +int +nfskrutil_set_default_gss_cred(const nfskrutil_scope_t scope, + const nfskrutil_mech_t mech, + const char *newname, + char **oldname) +{ + key_serial_t parent_ring, cred_ring; + nfskrutil_lock_t lock; + int retval; + + /* Check input values */ + if (!valid_scope(scope)) + return NFSKRUTIL_ERR_BAD_SCOPE; + if (!valid_mech(mech)) + return NFSKRUTIL_ERR_BAD_MECH; + if (!newname) + return NFSKRUTIL_ERR_NAME_MISSING; + + parent_ring = scope2ring(scope); + + cred_ring = get_cred_ring(parent_ring, 1, &lock, KRL_LOCKTYPE_EXCLUSIVE); + if (cred_ring < 0) { + return ENOENT; + } + + retval = make_default_cred(cred_ring, mech, newname, oldname); + + if (put_cred_ring(&lock)) + assert(0); + return retval; +} + +/* + * Remove a credential from the list of credentials that may + * be used by gssd in nogotiating contexts. + * + * If name points to "*", then the entire cred_ring is emptied + * and removed. + * + * Locking: Obtains and releases exclusive lock on parent keyring + */ +int +nfskrutil_delete_gss_cred(const nfskrutil_scope_t scope, + const nfskrutil_mech_t mech, + const char *name) +{ + key_serial_t parent_ring, cred_ring, delete_key, default_key; + char full_name[NFSKRUTIL_MAX_DESCRIPTION]; + void *default_name; + long code; + int delete_default, retval; + nfskrutil_lock_t lock; + int size; + + /* Check input values */ + if (!valid_scope(scope)) + return NFSKRUTIL_ERR_BAD_SCOPE; + if (!valid_mech(mech) && (mech != NFSKRUTIL_MECH_ALL)) + return NFSKRUTIL_ERR_BAD_MECH; + if (!name) + return NFSKRUTIL_ERR_NAME_MISSING; + /* Must choose everything or something specific */ + if (((strcmp(name, "*") == 0) && (mech != NFSKRUTIL_MECH_ALL)) || + ((mech == NFSKRUTIL_MECH_ALL) && (strcmp(name, "*") != 0))) + return EINVAL; + + parent_ring = scope2ring(scope); + + retval = ENOENT; + if ((cred_ring = get_cred_ring(parent_ring, 0, &lock, + KRL_LOCKTYPE_EXCLUSIVE)) < 0) { + return retval; + } + + /* + * If mech is NFSKRUTIL_MECH_ALL and name is "*" then + * do the special case of deleting all pointers and + * the cred_ring + */ + if ((strcmp(name, "*") == 0) && (mech == NFSKRUTIL_MECH_ALL)) { + if ((code = keyctl_clear(cred_ring)) == -1) { + retval = errno; + goto out_unlock; + } + if ((code = keyctl_unlink(cred_ring, parent_ring)) == -1) { + retval = errno; + goto out_unlock; + } + retval = 0; + goto out_unlock; + } + + snprintf(full_name, sizeof(full_name), "%s:%s", mech2string(mech), name); + + if ((delete_key = keyctl_search(cred_ring, NFSKRUTIL_KEYTYPE_USER, + full_name, 0)) == -1) { + if (errno == ENOKEY) + retval = ENOENT; + goto out_unlock; + } + + /* Determine if we are removing the default */ + delete_default = 0; + if ((default_key = get_default_key(cred_ring, mech)) >= 0) { + if (keyctl_read_alloc(default_key, &default_name) != -1) { + if (strcmp(default_name, name) == 0) + delete_default = 1; + free(default_name); + } + } + + /* Remove the cred requested and the default if necessary */ + if ((code = keyctl_unlink(delete_key, cred_ring)) == -1) { + retval = errno; + goto out_unlock; + } + if (delete_default) { + if ((code = keyctl_unlink(default_key, cred_ring)) == -1) { + retval = errno; + goto out_unlock; + } + } + + /* If the cred_ring is now empty, remove the cred_ring */ + size = keyctl_read(cred_ring, NULL, 0); + if (size == 0) { + if ((code = keyctl_unlink(cred_ring, parent_ring)) == -1) { + retval = errno; + goto out_unlock; + } + } + + retval = 0; +out_unlock: + if (put_cred_ring(&lock)) + assert(0); + return retval; +} + +/* + * Destroy rpcsec_gss context key(s) matching the input criteria. + * A value of NFSKRUTIL_MECH_ALL for 'mech' means to match any + * mechanism value. + * A value of "*" for the hostname means match any hostname. + * + * Returns zero if successful. + * Returns error values as positive values + * Returns number of keys that could not be unlinked as a negative value. + */ +int +nfskrutil_destroy_ctx_keys(const nfskrutil_scope_t scope, + const nfskrutil_mech_t mech, + const char *hostname) +{ + key_serial_t key, parent_ring, *keyarray; + nfskrutil_lock_t lock; + void *ringdata; + int ringsize, keycount; + int retval, i; + int errcount = 0; + + /* Check input values */ + if (!valid_scope(scope)) + return NFSKRUTIL_ERR_BAD_SCOPE; + if (!valid_mech(mech) && (mech != NFSKRUTIL_MECH_ALL)) + return NFSKRUTIL_ERR_BAD_MECH; + if (!hostname) + return NFSKRUTIL_ERR_NAME_MISSING; + + parent_ring = KEY_SPEC_SESSION_KEYRING; + + retval = ENOENT; + if ((retval = lock_ring(parent_ring, &lock, KRL_LOCKTYPE_EXCLUSIVE)) < 0) { + return retval; + } + + if ((ringsize = keyctl_read_alloc(parent_ring, &ringdata)) < 0) { + retval = errno; + goto out_unlock; + } + if (ringsize == 0) { + /* no data in session keyring, we're done! */ + retval = 0; + goto out_unlock; + } + + /* + * Loop through each key within the keyring + */ + keycount = ringsize / sizeof(key_serial_t); + keyarray = (key_serial_t *) ringdata; + + for (i = 0; i < keycount; i++) { + key_perm_t perm; + char *description, *keyname; + char type[256]; + uid_t uid; + gid_t gid; + int count, tlen, npos, n, ret, res; + + char mechanism[128]; + char pseudoflavor[128]; + char service[128]; + char host[128]; + key_serial_t skey, pkey, tkey, check_key; + const char name_format[] = "mechanism=\"%128[^\"]\" pseudoflavor=\"%128[^\"]\" service=\"%128[^\"]\" host=\"%128[^\"]\" skey=\"%u\" pkey=\"%u\" tkey=\"%u\""; + int ncount = 7; /* number of expected name fields */ + + key = keyarray[i]; + ret = keyctl_describe_alloc(key, &description); + if (ret < 0) + continue; /* just ignore keys we can't access */ + + /* Make sure not to use left-over junk */ + uid = 0; + gid = 0; + perm = 0; + type[0] = '\0'; + npos = -1; + /* Parse the description */ + n = sscanf((char *) description, "%[^;];%d;%d;%x;%n", + type, &uid, &gid, &perm, &npos); + if (n != 4) + goto next; /* just ignore keys whose description we can't parse */ + + if (strcmp(type, "rpcsec_gss_ctx") != 0) + goto next; /* ignore any keys that aren't gss ctx keys */ + + keyname = description + npos; + + /* Make sure not to use left-over junk */ + mechanism[0] = '\0'; + pseudoflavor[0] = '\0'; + service[0] = '\0'; + host[0] = '\0'; + skey = pkey = tkey = check_key = -1; + + /* Parse the name */ + n = sscanf(keyname, name_format, mechanism, pseudoflavor, + service, host, &skey, &pkey, &tkey); + if (n != ncount) + goto next; /* XXX ignore keys with names we can't parse? */ + + /* + * Check the key value for the requested scope level + */ + switch (scope) { + case NFSKRUTIL_SCOPE_THREAD: + check_key = keyctl_get_keyring_ID(KEY_SPEC_THREAD_KEYRING, 0); +/*XXX*/ fprintf(stderr, "check_key %d key's tkey %d\n", check_key, tkey); + if ((check_key != -1) && (check_key != tkey)) + goto next; + case NFSKRUTIL_SCOPE_PROCESS: + check_key = keyctl_get_keyring_ID(KEY_SPEC_PROCESS_KEYRING, 0); +/*XXX*/ fprintf(stderr, "check_key %d key's pkey %d\n", check_key, pkey); + if ((check_key != -1) && (check_key != pkey)) + goto next; + case NFSKRUTIL_SCOPE_SESSION: + check_key = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0); +/*XXX*/ fprintf(stderr, "check_key %d key's skey %d\n", check_key, skey); + if ((check_key != -1) && (check_key != skey)) + goto next; + } + /* + * If they specified a specific mechanism, only consider keys + * for that mechanism + */ + switch (mech) { + case NFSKRUTIL_MECH_KRB5: + case NFSKRUTIL_MECH_SPKM3: + case NFSKRUTIL_MECH_LIPKEY: +/*XXX*/ fprintf(stderr, "desired mech '%s' key's mech '%s'\n", + mech2string(mech), mechanism); + if (strcmp(mech2string(mech), mechanism) != 0) + goto next; + break; + case NFSKRUTIL_MECH_ALL: + /* don't take mechanism into consideration */ + break; + } + /* + * If they specified a specific hostname, only consider keys + * with that hostname + */ + if ((strcmp(hostname, "*")) != 0) { +/*XXX*/ fprintf(stderr, "desired hostname '%s' key's host '%s'\n", + hostname, host); + if ((strcmp(hostname, host)) != 0) + goto next; + } + + res = keyctl_revoke(key); + if (res == -1) { +/*XXX*/ fprintf(stderr, "Error revoking key %d (%s): %s\n", + key, keyname, strerror(errno)); + errcount++; + goto next; + } + + res = keyctl_unlink(key, KEY_SPEC_SESSION_KEYRING); + if (res == -1) { +/*XXX*/ fprintf(stderr, "Error unlinking key %d (%s): %s\n", + key, keyname, strerror(errno)); + errcount++; + } +next: + free(description); + } + + retval = -errcount; +out_unlock: + if (unlock_ring(&lock)) + assert(0); + return retval; +} + +/* + * Get the name of the default credential to be used by + * gssd in negotiating contexts for the given machanism. + * + * Locking: XXX + * should have shared lock on parent, but we don't know its value + */ +int +nfskrutil_get_default_gss_cred(const nfskrutil_mech_t mech, char **retname) +{ + key_serial_t cred_ring, default_key; + int retval, code; + nfskrutil_lock_t lock; + + /* Check input values */ + if (!valid_mech(mech)) + return NFSKRUTIL_ERR_BAD_MECH; + + if ((cred_ring = get_cred_ring(0, 0, &lock, KRL_LOCKTYPE_SHARED)) < 0) { + return ENOENT; + } + + if ((default_key = get_default_key(cred_ring, mech)) == -1) { + retval = errno; + goto out_unlock; + } + if ((code = keyctl_read_alloc(default_key, (void **)retname)) == -1) { + retval = errno; + goto out_unlock; + } + + retval = 0; +out_unlock: + if (put_cred_ring(&lock)) + assert(0); + return retval; +} + + +/* + * Get a list of the credentials for the specified mechanism. + * If there is a default, it will be returned first in the list. + * + * Locking: XXX + */ +int +nfskrutil_get_gss_cred_list(const nfskrutil_mech_t mech, + char ***namelist, + int *listlength) +{ + key_serial_t cred_ring, default_key; + key_serial_t cred_key, *kp_list; + void *vp; + int credcount, code, i, retval, namecount, found_default, mechprefix_len; + char description[1024], mechprefix[32], *name, **retnames; + nfskrutil_lock_t lock; + + /* Check input values */ + if (!valid_mech(mech)) + return NFSKRUTIL_ERR_BAD_MECH; + if (namelist == NULL || listlength == NULL) + return EINVAL; + + /* Initialize output values */ + *namelist = NULL; + *listlength = 0; + + if ((cred_ring = get_cred_ring(0, 0, &lock, KRL_LOCKTYPE_SHARED)) < 0) { + retval = ENOENT; + goto out; + } + + if ((code = keyctl_read_alloc(cred_ring, &vp)) <= 0) { + retval = ENOENT; + goto out_unlock; + } + + kp_list = (key_serial_t *)vp; + credcount = code / sizeof(key_serial_t); + + /* We can return at most, credcount+1 (including terminating NULL) */ + namecount = 0; + retnames = calloc(credcount+1, sizeof(char *)); + if (retnames == NULL) { + retval = ENOMEM; + goto err_free_keylist; + } + + mechprefix_len = snprintf(mechprefix, sizeof(mechprefix), + "%s:", mech2string(mech)); + + /* Check if there is a default we need to return first on the list */ + found_default = 0; + default_key = get_default_key(cred_ring, mech); + if (default_key >= 0) { + found_default = 1; + code = keyctl_read_alloc(default_key, (void **)(&retnames[namecount++])); + if (code == -1) { + retval = errno; + goto err_free_array; + } + } + + /* Go through all the creds and make a list to be returned */ + for (i = 0; i < credcount; i++) { + cred_key = kp_list[i]; + + /* We already handled this above */ + if (cred_key == default_key) + continue; + + code = keyctl_describe(cred_key, description, sizeof(description)); + if (code < 0 || code > sizeof(description)) { + retval = EIO; + goto err_free_entries; + } + + /* Search backwards for last semi-colon to find the name */ + while(description[code] != ';') + code--; + name = &description[code + 1]; + + /* Skip entries for other mechanisms */ + if ((strncmp(name, mechprefix, mechprefix_len)) != 0) + continue; + + /* Point past the mechanism prefix */ + name += mechprefix_len; + + /* If we found a default, don't include it twice */ + if ((found_default == 1) && (strcmp(name, retnames[0]) == 0)) + continue; + + /* It is the right mechanism and is not the default, add it! */ + if ((retnames[namecount++] = strdup(name)) == NULL) { + retval = ENOMEM; + goto err_free_entries; + } + } + if (namecount > 0) { + retnames[namecount] = NULL; /* redundant because of calloc */ + *namelist = retnames; + *listlength = namecount; + } + retval= 0; + +out_unlock: + if (put_cred_ring(&lock)) + assert(0); +out: + return retval; + +err_free_entries: + for (i = 0; i < namecount; i++) + free(retnames[i]); +err_free_array: + free(retnames); +err_free_keylist: + free(kp_list); + goto out_unlock; +} + +/* + * The list that we return always has a NULL pointer at the end that + * we don't tell the caller about. Make sure that this looks like + * a legitimate list and free all the storage. + * + * Locking: XXX + */ +int +nfskrutil_free_gss_cred_list(char **list, int listlength) +{ + char **names; + int i; + + if (list == NULL || listlength <= 0) + return EINVAL; + + names = list; + if (names[listlength] != NULL) + return EINVAL; + + /* Free each name */ + for (i = 0; i < listlength; i++) + free(names[i]); + + /* Free the pointer array */ + free(list); + + return 0; +} + +#endif /* HAVE_LIBKEYUTILS */ diff -puN /dev/null utils/gssd/nfskrutil.h --- /dev/null 2006-09-22 08:14:07.867789970 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/nfskrutil.h 2006-09-23 18:40:10.601302000 -0400 @@ -0,0 +1,131 @@ +/* + * nfskrutil.h -- Declarations for use with NFS keyring utility library + * + * Copyright (c) 2006 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 _NFSKRUTIL_H_ +#define _NFSKRUTIL_H_ + +#include + +/* + * The scope at which a credential is added + */ +typedef enum { + NFSKRUTIL_SCOPE_SESSION = 1, + NFSKRUTIL_SCOPE_PROCESS = 2, + NFSKRUTIL_SCOPE_THREAD = 3, +} nfskrutil_scope_t; + +/* + * The mechanism for which a credential is valid + */ +typedef enum { + NFSKRUTIL_MECH_ALL = -1, + NFSKRUTIL_MECH_KRB5 = 1, + NFSKRUTIL_MECH_SPKM3 = 2, + NFSKRUTIL_MECH_LIPKEY = 3, +} nfskrutil_mech_t; + +/* + * Flags used in the add function + */ +#define NFSKRUTIL_ADDFLAG_DEFAULT 0x00000001 + +/* + * Create a new "anonymous" session (if no name supplied) + * or join a named session (creating it if it doesn't + * already exist). + * + * Returns the new sesion keyring serial number if requested. + */ +int +nfskrutil_newsession(const char *name, key_serial_t *retkey); + +/* + * Add a credential that may be used by gssd in negotiating + * contexts. If flags has NFSKRUTIL_ADDFLAG_DEFAULT set, this + * new credential becomes the default for this mechanism. + */ +int +nfskrutil_add_gss_cred(const nfskrutil_scope_t scope, + const nfskrutil_mech_t mech, + const char *name, + u_int32_t flags); + +/* + * Set the default credential to be used by gssd in negotiating + * contexts. Return the old default, if requested, in the + * user-supplied buffer pointed to by oldname. + */ +int +nfskrutil_set_default_gss_cred(const nfskrutil_scope_t scope, + const nfskrutil_mech_t mech, + const char *newname, + char **oldname); + +/* + * Remove a credential from the list of credentials that may + * be used by gssd in nogotiating contexts. + */ +int +nfskrutil_delete_gss_cred(const nfskrutil_scope_t scope, + const nfskrutil_mech_t mech, + const char *name); + +/* + * Destroy rpcsec_gss context key(s) matching the input criteria. + * A value of NFSKRUTIL_MECH_ALL for 'mech' means to match any + * mechanism value. + * A value of "*" for the hostname means match any hostname. + * + * Returns zero if successful. + * Returns error values as positive values + * Returns number of keys that could not be unlinked as a negative value. + */ +int +nfskrutil_destroy_ctx_keys(const nfskrutil_scope_t scope, + const nfskrutil_mech_t mech, + const char *hostname); + +/* + * Error values that might be returned + */ +#define NFSKRUTIL_ERR_BAD_SCOPE 0xffff0000 + /* Requested scope not recognized */ +#define NFSKRUTIL_ERR_BAD_MECH 0xffff0001 + /* Requested mechanism not recognized */ +#define NFSKRUTIL_ERR_NAME_MISSING 0xffff0002 + /* No name was supplied */ +#define NFSKRUTIL_ERR_FLAG_NOSUPP 0xffff0003 + /* Flags contained unrecognized flag */ + +#endif /* _NFSKRUTIL_H_ */ diff -puN /dev/null utils/gssd/nfskrutil-int.h --- /dev/null 2006-09-22 08:14:07.867789970 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/nfskrutil-int.h 2006-09-23 18:40:07.964499000 -0400 @@ -0,0 +1,112 @@ +/* + * nfskrutil-int.h -- Internal definitions for libnfskrutil and gssd + * + * Copyright (c) 2006 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. + * + */ + +/* Internal definitions for libnfskrutil */ + +#ifndef _NFSKRUTIL_INT_H_ +#define _NFSKRUTIL_INT_H_ + +#include "config.h" + +#ifdef HAVE_LIBKEYUTILS + +/* Include public interface information */ +#include "nfskrutil.h" + +/* Maximum length for key description (name) */ +#define NFSKRUTIL_MAX_DESCRIPTION 1024 + +#define NFSKRUTIL_KEYTYPE_USER "user" +#define NFSKRUTIL_KEYTYPE_KEYRING "keyring" + +/* + * Name of special keyring that lists the gss credentials + * that may be used by gssd when negotiating contexts. + */ +#define NFSKRUTIL_GSSCREDS_RING_NAME "_gss_creds_" + +/* + * Now define internal-use-only information + */ + +typedef struct { + int version; /* Must be one (1) */ + int entry_count; /* Number of cred entries */ + int default_entry; /* Entry number of default entry */ + /* XXX Need default for each mech! */ +} nfskrutil_cred_header_t; + +typedef struct { + int entry_len; /* Length of entire entry */ + nfskrutil_mech_t mech; /* Mechanism this cred applied to */ + int name_len; /* Name length, including nul terminator */ + char *name; /* The nul-terminated name */ +} nfskrutil_cred_entry_t; + + + +/* + * Functions called by gssd to get credentials that should be used + * when negotiating an rpcsec_gss context. + */ +int +nfskrutil_get_default_gss_cred(const nfskrutil_mech_t mech, + char **retname); + +int +nfskrutil_get_gss_cred_list(const nfskrutil_mech_t mech, + char ***namelist, + int *listlength); + +int +nfskrutil_free_gss_cred_list(char **list, + int listlength); + + +/* XXX These values are platform-specific and should not be here! */ +/* XXX There is a bug in FC5 where these are not included in errno.h */ +#ifndef ENOKEY +#define ENOKEY 126 /* Required key not available */ +#endif +#ifndef EKEYEXPIRED +#define EKEYEXPIRED 127 /* Key has expired */ +#endif +#ifndef EKEYREVOKED +#define EKEYREVOKED 128 /* Key has been revoked */ +#endif +#ifndef EKEYREJECTED +#define EKEYREJECTED 129 /* Key was rejected by service */ +#endif + +#endif /* HAVE_LIBKEYUTILS */ +#endif /* _NFSKRUTIL_INT_H_ */ diff -puN /dev/null utils/gssd/nfskrutil-test.c --- /dev/null 2006-09-22 08:14:07.867789970 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/nfskrutil-test.c 2006-09-23 18:40:08.809048000 -0400 @@ -0,0 +1,348 @@ +/* + nfskrutil-test.c + + Copyright (c) 2000 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_LIBKEYUTILS + + +#include +#include +#include +#include +#include +#include +#include "nfskrutil-int.h" + + +int do_looptest = 0; +int do_multiadd = 0; +int do_ctx_destroy = 0; +int verbosity = 0; + +/* + * Given a mechanism value, "mech" return the associated name. + */ +static inline const char * +mech2string(const nfskrutil_mech_t mech) +{ + switch (mech) { + case NFSKRUTIL_MECH_KRB5: + return "krb5"; + break; + case NFSKRUTIL_MECH_SPKM3: + return "spkm3"; + break; + case NFSKRUTIL_MECH_LIPKEY: + return "lipkey"; + break; + default: + return "invalid"; + break; + } +} + +void usage(char *pgmname) +{ + fprintf(stderr, "Usage: %s [-a][-l][-m][-x][-v]\n", pgmname); +} + +int +process_args(int argc, char *argv[]) +{ + int opt; + while ((opt = getopt(argc, argv, "almxv")) != -1) { + switch (opt) { + case 'a': + do_looptest = 1; + do_multiadd = 1; + do_ctx_destroy = 1; + break; + case 'l': + do_looptest = 1; + break; + case 'm': + do_multiadd = 1; + break; + case 'x': + do_ctx_destroy = 1; + break; + case 'v': + verbosity++; + break; + default: + fprintf(stderr, "%s: ignoring unknown option '%c'\n", argv[0], opt); + usage(argv[0]); + break; + } + } + return 0; +} + +int print_current_default(nfskrutil_mech_t mech) +{ + int code; + char *dfltname; + code = nfskrutil_get_default_gss_cred(mech, &dfltname); + if (code) { + fprintf(stderr, "Failed to get default: %s\n", + strerror(code)); + } else { + fprintf(stdout, "The current default for mech %s is '%s'\n", mech2string(mech), dfltname); +#if 0 /* Check valgrind detection of leaks... */ + free(dfltname); +#endif + } + return 0; +} + +int print_current_list(nfskrutil_mech_t mech) +{ + int code, i; + char **namelist; + int numnames; + + code = nfskrutil_get_gss_cred_list(mech, &namelist, &numnames); + if (code && code != ENOENT) { + fprintf(stderr, "Failed to get current list: %s\n", + strerror(code)); + return -1; + } else { + fprintf(stdout, "There are now %d creds for mech %s:\n", numnames, mech2string(mech)); + for (i = 0; i < numnames; i++) { + fprintf(stdout, "\t%02d\t'%s'\n", i+1, namelist[i]); + } +#if 0 /* Check valgrind detection of leaks... */ + if (numnames) { + code = nfskrutil_free_gss_cred_list(namelist, numnames); + if (code) { + fprintf(stderr, "Error freeing returned cred list!: %s\n", strerror(code)); + return -1; + } + } +#endif + } + return 0; +} + +int +looptest() +{ + nfskrutil_mech_t mech; + nfskrutil_scope_t scope; + int i, code; + + char *cred_names[] = { + "AAAAAAAA", + "BBBBBBBB", + "CCCCCCCC", + "DDDDDDDD", + "EEEEEEEE", + NULL, + "FFFFFFFF", + "GGGGGGGG", + "HHHHHHHH", + "IIIIIIII", + "JJJJJJJJ", + "KKKKKKKK", + "LLLLLLLL", + "MMMMMMMM", + "NNNNNNNN", + "OOOOOOOO", + "PPPPPPPP", + "QQQQQQQQ", + "RRRRRRRR", + "SSSSSSSS", + "TTTTTTTT", + "UUUUUUUU", + "VVVVVVVV", + "WWWWWWWW", + "XXXXXXXX", + "YYYYYYYY", + "ZZZZZZZZ", + NULL, + }; + + fprintf(stdout, "=========================== looptest ===========================\n"); + /* Add a bunch of creds */ + for (scope = NFSKRUTIL_SCOPE_SESSION; scope <= NFSKRUTIL_SCOPE_THREAD; scope++) { + for (mech = NFSKRUTIL_MECH_KRB5; mech <= NFSKRUTIL_MECH_LIPKEY; mech++) { + for (i = 0; ; i++) { + int addflags = (i % 2) ? NFSKRUTIL_ADDFLAG_DEFAULT : 0; + + if (cred_names[i] == NULL) { + break; + } + + fprintf(stdout, "Adding '%s' (and %smaking it default)\n", + cred_names[i], + addflags & NFSKRUTIL_ADDFLAG_DEFAULT ? "" : "NOT "); + code = nfskrutil_add_gss_cred(scope, mech, cred_names[i], + addflags); + if (code) { + fprintf(stderr, "Failed to add name '%s': %s\n", + cred_names[i], strerror(code)); + return -1; + } + + if (print_current_default(mech)) + return -1; + if (print_current_list(mech)) + return -1; + } + } + } + for (scope = NFSKRUTIL_SCOPE_SESSION; scope <= NFSKRUTIL_SCOPE_THREAD; scope++) { + for (mech = NFSKRUTIL_MECH_KRB5; mech <= NFSKRUTIL_MECH_LIPKEY; mech++) { + for (i = 0; ; i++) { + if (cred_names[i] == NULL) { + break; + } + fprintf(stdout, "Deleting '%s'\n", cred_names[i]); + code = nfskrutil_delete_gss_cred(scope, mech, cred_names[i]); + if (code) { + fprintf(stderr, "Failed to delete name '%s': %s\n", + cred_names[i], strerror(code)); + } + } + } + } + + return 0; +} + +int +multiadd() +{ + nfskrutil_mech_t mech; + nfskrutil_scope_t scope; + int i, code; + + char *cred_names[] = { + "Testing 1", + "Testing 2", + "Testing 3", + "Testing 4", + NULL, + }; + + fprintf(stdout, "=========================== multiadd ===========================\n"); + for (scope = NFSKRUTIL_SCOPE_SESSION; scope <= NFSKRUTIL_SCOPE_THREAD; scope++) { + for (mech = NFSKRUTIL_MECH_KRB5; mech <= NFSKRUTIL_MECH_LIPKEY; mech++) { + for (i = 0; i < 4; i++) { + code = nfskrutil_add_gss_cred(scope, mech, cred_names[i], NFSKRUTIL_ADDFLAG_DEFAULT); + if (code) { + fprintf(stderr, "Failed to add name '%s': %s\n", + cred_names[i], strerror(code)); + return -1; + } + if (print_current_default(mech)) + return -1; + if (print_current_list(mech)) + return -1; + } + for (i = 3; i >= 0; i--) { + code = nfskrutil_delete_gss_cred(scope, mech, cred_names[i]); + if (code) { + fprintf(stderr, "Failed to delete cred '%s': %s\n", cred_names[i], strerror(code)); + } + if (print_current_default(mech)) + return -1; + if (print_current_list(mech)) + return -1; + } + } + } + + return 0; +} + +int +ctx_destroy(void) +{ + int code; + + fprintf(stdout, "=========================== ctx_destroy ===========================\n"); + code = nfskrutil_destroy_ctx_keys(0, NFSKRUTIL_MECH_ALL, "chubby"); + if (code != NFSKRUTIL_ERR_BAD_SCOPE) { + fprintf(stderr, "ERROR: expected error NFSKRUTIL_ERR_BAD_SCOPE " + "from nfskrutil_destroy_ctx_keys() but got %d\n", code); + return -1; + } + code = nfskrutil_destroy_ctx_keys(NFSKRUTIL_SCOPE_SESSION, 0, "chubby"); + if (code != NFSKRUTIL_ERR_BAD_MECH) { + fprintf(stderr, "ERROR: expected error NFSKRUTIL_ERR_BAD_MECH " + "from nfskrutil_destroy_ctx_keys() but got %d\n", code); + return -1; + } + code = nfskrutil_destroy_ctx_keys(NFSKRUTIL_SCOPE_SESSION, + NFSKRUTIL_MECH_ALL, NULL); + if (code != NFSKRUTIL_ERR_NAME_MISSING) { + fprintf(stderr, "ERROR: expected error NFSKRUTIL_ERR_NAME_MISSING " + "from nfskrutil_destroy_ctx_keys() but got %d\n", code); + return -1; + } + code = nfskrutil_destroy_ctx_keys(NFSKRUTIL_SCOPE_SESSION, + NFSKRUTIL_MECH_ALL, "*"); + if (code != 0) { + fprintf(stderr, "ERROR: got code %d " + "from nfskrutil_destroy_ctx_keys()\n", code); + return -1; + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + if (process_args(argc, argv)) { + fprintf(stderr, "Try again...\n"); + return 1; + } + if (do_looptest && looptest()) { + fprintf(stderr, "looptest failed...\n"); + return 1; + } + if (do_multiadd && multiadd()) { + fprintf(stderr, "multiadd test failed...\n"); + return 1; + } + if (do_ctx_destroy && ctx_destroy()) { + fprintf(stderr, "ctx_destroy test failed...\n"); + return 1; + } + return 0; +} + +#endif /* HAVE_LIBKEYUTILS */ diff -puN utils/gssd/context.c~CITI_NFS4_ALL utils/gssd/context.c --- nfs-utils-1.0.10-asci/utils/gssd/context.c~CITI_NFS4_ALL 2006-09-23 18:40:11.947695000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/context.c 2006-09-23 18:40:14.300793000 -0400 @@ -43,13 +43,14 @@ int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, - gss_OID mech) + gss_OID mech, + uint32_t *endtime) { if (g_OID_equal(&krb5oid, mech)) - return serialize_krb5_ctx(ctx, buf); + return serialize_krb5_ctx(ctx, buf, endtime); #ifdef HAVE_SPKM3_H else if (g_OID_equal(&spkm3oid, mech)) - return serialize_spkm3_ctx(ctx, buf); + return serialize_spkm3_ctx(ctx, buf, endtime); #endif else { printerr(0, "ERROR: attempting to serialize context with " diff -puN utils/gssd/context.h~CITI_NFS4_ALL utils/gssd/context.h --- nfs-utils-1.0.10-asci/utils/gssd/context.h~CITI_NFS4_ALL 2006-09-23 18:40:13.472080000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/context.h 2006-09-23 18:40:15.118342000 -0400 @@ -34,8 +34,10 @@ #include int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, - gss_OID mech); -int serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf); -int serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf); + gss_OID mech, uint32_t *endtime); +int serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, + uint32_t *endtime); +int serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, + uint32_t *endtime); #endif /* _CONTEXT_H_ */ diff -puN utils/gssd/svcgssd_proc.c~CITI_NFS4_ALL utils/gssd/svcgssd_proc.c --- nfs-utils-1.0.10-asci/utils/gssd/svcgssd_proc.c~CITI_NFS4_ALL 2006-09-23 18:40:14.220793000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/svcgssd_proc.c 2006-09-23 18:40:17.657093000 -0400 @@ -386,7 +386,7 @@ handle_nullreq(FILE *f) { /* 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, mech)) { + if (serialize_context_for_kernel(ctx, &ctx_token, mech, NULL)) { printerr(0, "WARNING: handle_nullreq: " "serialize_context_for_kernel failed\n"); maj_stat = GSS_S_FAILURE; diff -puN utils/gssd/gss_clnt_send_err.c~CITI_NFS4_ALL utils/gssd/gss_clnt_send_err.c --- nfs-utils-1.0.10-asci/utils/gssd/gss_clnt_send_err.c~CITI_NFS4_ALL 2006-09-23 18:40:21.679269000 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/gss_clnt_send_err.c 2006-09-23 18:40:25.164832000 -0400 @@ -57,7 +57,7 @@ usage(char *progname) } static int -do_error_downcall(int k5_fd, uid_t uid, int err) +do_local_error_downcall(int k5_fd, uid_t uid, int err) { char buf[1024]; char *p = buf, *end = buf + 1024; @@ -97,7 +97,7 @@ main(int argc, char *argv[]) err(1, "unknown user %s", argv[i]); uid = pw->pw_uid; } - if (do_error_downcall(fd, uid, -1)) + if (do_local_error_downcall(fd, uid, -1)) err(1, "failed to destroy cred for user %s", argv[i]); } exit(0); diff -puN /dev/null utils/gssd/gssd_keyring.c --- /dev/null 2006-09-22 08:14:07.867789970 -0400 +++ nfs-utils-1.0.10-asci-kwc/utils/gssd/gssd_keyring.c 2006-09-23 18:40:25.842381000 -0400 @@ -0,0 +1,507 @@ +/* + Copyright (c) 2006 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gssd.h" +#include "err_util.h" +#include "gss_util.h" +#include "gss_oids.h" +#include "context.h" +#include "nfskrutil-int.h" +#include "krb5_util.h" + +static int +read_authkey_service_info(struct clnt_info *clp) +{ + char buf[INFOBUFLEN]; + int retval = EINVAL; + char mechanism[128]; + char