From: neilbrown Date: Fri, 26 Aug 2005 01:27:17 +0000 (+0000) Subject: 2005-08-26 Kevin Coffman X-Git-Tag: nfs-utils-1-0-7-post2~3 X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=commitdiff_plain;h=a980156c122e975cc185a6c41ef705f166a5765f 2005-08-26 Kevin Coffman * utils/exportfs/exports.man: Document the "crossmnt" export export option * utils/gssd/krb5_util.c: Add better debugging and partially revert the function check for gss_krb5_ccache_name. For MIT Kerberos releases up to and including 1.3.1, we *must* use the routine gss_krb5_ccache_name to get the K5 gssapi code to use a different credentials cache. For releases 1.3.2 and on, we want to use the KRB5CCNAME environment variable to tell it what to use. (A problem was reported where 1.3.5 was being used, our code was using gss_krb5_ccache_name, but the underlying code continued to use the first (or default?) credentials cache. Switching to using the env variable fixed the problem. I cannot recreate this problem. *utils/gssd/krb5_util.c: Andrew Mahone reported that reiser4 always has DT_UNKNOWN. He supplied patch to move the check for regular files after the stat() call to correctly find ccache files in reiser4 filesystem. Also change the name comparison so that the wrong file is not selected when the substring comparison is done. *utils/gssd/krb5_util.c: Limit the set of encryption types that can be negotiated by the Kerberos library to those that the kernel code currently supports. This should eventually query the kernel for the list of supported enctypes. *utils/gssd/gss_util.c, utils/svcgssd/svcgssd_main_loop.c: Print more information in error messages to help debugging failures. *utils/svcgssd/svcgssd_proc.c: Increase token buffer size and update error handling so that a response is always sent. *utils/svcgssd/svcgssd_proc.c: Add support to retrieve supplementary groups. --- diff --git a/ChangeLog b/ChangeLog index c7068a2..6cff0f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,48 @@ +2005-08-26 Kevin Coffman + * utils/exportfs/exports.man: Document the "crossmnt" export export option + * utils/gssd/krb5_util.c: + Add better debugging and partially revert the function + check for gss_krb5_ccache_name. + + For MIT Kerberos releases up to and including 1.3.1, we *must* + use the routine gss_krb5_ccache_name to get the K5 gssapi code + to use a different credentials cache. + + For releases 1.3.2 and on, we want to use the KRB5CCNAME + environment variable to tell it what to use. + (A problem was reported where 1.3.5 was being used, our + code was using gss_krb5_ccache_name, but the underlying + code continued to use the first (or default?) credentials + cache. Switching to using the env variable fixed the problem. + I cannot recreate this problem. + + *utils/gssd/krb5_util.c: + Andrew Mahone reported that reiser4 + always has DT_UNKNOWN. He supplied patch to move the check + for regular files after the stat() call to correctly find + ccache files in reiser4 filesystem. + + Also change the name comparison so that the wrong file is + not selected when the substring comparison is done. + + *utils/gssd/krb5_util.c: + Limit the set of encryption types that can be negotiated by + the Kerberos library to those that the kernel code currently + supports. + + This should eventually query the kernel for the list of + supported enctypes. + + *utils/gssd/gss_util.c, utils/svcgssd/svcgssd_main_loop.c: + Print more information in error messages to help debugging failures. + + *utils/svcgssd/svcgssd_proc.c: Increase token buffer size and + update error handling so that a response is always sent. + + *utils/svcgssd/svcgssd_proc.c: Add support to retrieve + supplementary groups. + + 2005-08-26 Kevin Coffman * configure.in etc Consolidate some of the Kerberos checking instead of repeating diff --git a/support/include/config.h.in b/support/include/config.h.in index 4be7b04..efda975 100644 --- a/support/include/config.h.in +++ b/support/include/config.h.in @@ -50,6 +50,15 @@ * gss_krb5_cache_name */ #undef HAVE_GSS_KRB5_CCACHE_NAME +/* Define this if we want to use the private Kerberos + * gssapi library function, gss_krb5_cache_name, to + * specify the credentials cache file to be used by + * the gssapi library. + * (For MIT releases 1.3.1 and before, this must + * be defined. For later releases we can simply + * set the KRB5CCNAME environment variable.) */ +#undef USE_GSS_KRB5_CCACHE_NAME + /* The size of a `int', as computed by sizeof. */ #undef SIZEOF_INT diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man index 2b316f5..f420a20 100644 --- a/utils/exportfs/exports.man +++ b/utils/exportfs/exports.man @@ -168,6 +168,14 @@ copes with the situation effectively. The option can be explicitly disabled with .IR hide . .TP +.IR crossmnt +This option is similar to +.I nohide +but it makes it possible for clients to move from the filesystem marked +with crossmnt to exported filesystems mounted on it. Thus when a child +filesystem "B" is mounted on a parent "A", setting crossmnt on "A" has +the same effect as setting "nohide" on B. +.TP .IR no_subtree_check This option disables subtree checking, which has mild security implications, but can improve reliability in some circumstances. diff --git a/utils/gssd/gss_util.c b/utils/gssd/gss_util.c index cf240ac..f62a87b 100644 --- a/utils/gssd/gss_util.c +++ b/utils/gssd/gss_util.c @@ -190,6 +190,7 @@ gssd_acquire_cred(char *server_name) gss_name_t target_name; u_int32_t maj_stat, min_stat; u_int32_t ignore_maj_stat, ignore_min_stat; + gss_buffer_desc pbuf; name.value = (void *)server_name; name.length = strlen(server_name); @@ -207,10 +208,19 @@ gssd_acquire_cred(char *server_name) GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &gssd_creds, NULL, NULL); - ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name); - - if (maj_stat != GSS_S_COMPLETE) + if (maj_stat != GSS_S_COMPLETE) { pgsserr("gss_acquire_cred", maj_stat, min_stat, g_mechOid); + ignore_maj_stat = gss_display_name(&ignore_min_stat, + target_name, &pbuf, NULL); + if (ignore_maj_stat == GSS_S_COMPLETE) { + printerr(0, "Unable to obtain credentials for '%.*s'\n", + pbuf.length, pbuf.value); + ignore_maj_stat = gss_release_buffer(&ignore_min_stat, + &pbuf); + } + } + + ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name); return (maj_stat == GSS_S_COMPLETE); } diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c index 2dcc2ee..d29b839 100644 --- a/utils/gssd/krb5_util.c +++ b/utils/gssd/krb5_util.c @@ -146,10 +146,11 @@ static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, static int select_krb5_ccache(const struct dirent *d) { - /* Don't consider anything but regular files. (No symlinks, etc.) */ - if (d->d_type != DT_REG) - return 0; - + /* + * Note: We used to check d->d_type for DT_REG here, + * but apparenlty reiser4 always has DT_UNKNOWN. + * Check for IS_REG after stat() call instead. + */ if (strstr(d->d_name, GSSD_DEFAULT_CRED_PREFIX)) return 1; else @@ -184,12 +185,15 @@ gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d) } else if (n > 0) { char substring[128]; + char fullstring[128]; char statname[1024]; - snprintf(substring, sizeof(substring), "_%d", uid); + snprintf(substring, sizeof(substring), "_%d_", uid); + snprintf(fullstring, sizeof(fullstring), "_%d", uid); for (i = 0; i < n; i++) { printerr(3, "CC file '%s' being considered\n", namelist[i]->d_name); - if (strstr(namelist[i]->d_name, substring)) { + if (strstr(namelist[i]->d_name, substring) || + !strcmp(namelist[i]->d_name, fullstring)) { snprintf(statname, sizeof(statname), "%s/%s", GSSD_DEFAULT_CRED_DIR, namelist[i]->d_name); @@ -199,6 +203,12 @@ gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d) statname); continue; } + if (!S_ISREG(tmp_stat.st_mode)) { + printerr(3, "File '%s' is not " + "a regular file\n", + statname); + continue; + } printerr(3, "CC file '%s' matches " "name check and has " "mtime of %u\n", @@ -270,11 +280,7 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) { u_int maj_stat, min_stat; gss_cred_id_t credh; -/* krb5_enctype enctypes[] = {ENCTYPE_DES3_CBC_SHA1}; - ENCTYPE_ARCFOUR_HMAC, */ - krb5_enctype enctypes[] = {ENCTYPE_DES3_CBC_SHA1, - ENCTYPE_DES_CBC_MD5, - ENCTYPE_DES_CBC_CRC}; + krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC }; int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); maj_stat = gss_acquire_cred(&min_stat, NULL, 0, @@ -528,6 +534,36 @@ gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, char *kt_name) return retval; } +/* + * Depending on the version of Kerberos, we either need to use + * a private function, or simply set the environment variable. + */ +static void +gssd_set_krb5_ccache_name(char *ccname) +{ +#ifdef USE_GSS_KRB5_CCACHE_NAME + u_int maj_stat, min_stat; + + printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n", + ccname); + maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL); + if (maj_stat != GSS_S_COMPLETE) { + printerr(0, "WARNING: gss_krb5_ccache_name with " + "name '%s' failed (%s)\n", + ccname, error_message(min_stat)); + } +#else + /* + * Set the KRB5CCNAME environment variable to tell the krb5 code + * which credentials cache to use. (Instead of using the private + * function above for which there is no generic gssapi + * equivalent.) + */ + printerr(2, "using environment variable to select krb5 ccache %s\n", + ccname); + setenv("KRB5CCNAME", ccname, 1); +#endif +} /*==========================*/ /*=== External routines ===*/ @@ -545,9 +581,6 @@ void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername) { char buf[MAX_NETOBJ_SZ]; -#ifdef HAVE_GSS_KRB5_CCACHE_NAME - u_int min_stat; -#endif struct dirent *d; printerr(2, "getting credentials for client with uid %u for " @@ -564,17 +597,7 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername) GSSD_DEFAULT_CRED_PREFIX, uid); printerr(2, "using %s as credentials cache for client with " "uid %u for server %s\n", buf, uid, servername); -#ifdef HAVE_GSS_KRB5_CCACHE_NAME - gss_krb5_ccache_name(&min_stat, buf, NULL); -#else - /* - * Set the KRB5CCNAME environment variable to tell the krb5 code - * which credentials cache to use. (Instead of using the private - * function above for which there is no generic gssapi - * equivalent.) - */ - setenv("KRB5CCNAME", buf, 1); -#endif + gssd_set_krb5_ccache_name(buf); } /* @@ -586,22 +609,9 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername) void gssd_setup_krb5_machine_gss_ccache(char *ccname) { -#ifdef HAVE_GSS_KRB5_CCACHE_NAME - u_int min_stat; -#endif printerr(2, "using %s as credentials cache for machine creds\n", ccname); -#ifdef HAVE_GSS_KRB5_CCACHE_NAME - gss_krb5_ccache_name(&min_stat, ccname, NULL); -#else - /* - * Set the KRB5CCNAME environment variable to tell the krb5 code - * which credentials cache to use. (Instead of using the private - * function above for which there is no generic gssapi - * equivalent.) - */ - setenv("KRB5CCNAME", ccname, 1); -#endif + gssd_set_krb5_ccache_name(ccname); } /* diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c index b1da786..80819d4 100644 --- a/utils/idmapd/idmapd.c +++ b/utils/idmapd/idmapd.c @@ -714,9 +714,7 @@ idtonameres(struct idmap_msg *im) sizeof(im->im_name)); break; } - /* XXX Hack? would rather return failure instead of writing nobody - * as above, but kernel seems not to deal well with that as of - * 2.6.8-rc3. */ + /* XXX Hack? */ im->im_status = IDMAP_STATUS_SUCCESS; } @@ -725,6 +723,14 @@ nametoidres(struct idmap_msg *im) { int ret = 0; + /* XXX: nobody fallbacks shouldn't always happen: + * server id -> name should be OK + * client name -> id should be OK + * but not otherwise */ + /* XXX: move nobody stuff to library calls + * (nfs4_get_nobody_user(domain), nfs4_get_nobody_group(domain)) */ + /* XXX: should make this call higher up in the call chain (so we'd + * have a chance on looking up server/whatever. */ switch (im->im_type) { case IDMAP_TYPE_USER: ret = nfs4_name_to_uid(im->im_name, &im->im_id); @@ -737,9 +743,7 @@ nametoidres(struct idmap_msg *im) im->im_id = nobodygid; break; } - /* XXX Hack? would rather return failure instead of writing nobody - * as above, but kernel seems not to deal well with that as of - * 2.6.8-rc3. */ + /* XXX? */ im->im_status = IDMAP_STATUS_SUCCESS; } diff --git a/utils/svcgssd/svcgssd_main_loop.c b/utils/svcgssd/svcgssd_main_loop.c index 477a44c..280816d 100644 --- a/utils/svcgssd/svcgssd_main_loop.c +++ b/utils/svcgssd/svcgssd_main_loop.c @@ -58,19 +58,24 @@ gssd_run() f = fopen(NULLRPC_FILE, "rw"); if (!f) { - printerr(0, "failed to open %s\n", NULLRPC_FILE); + printerr(0, "failed to open %s: %s\n", + NULLRPC_FILE, strerror(errno)); exit(1); } pollfd.fd = fileno(f); pollfd.events = POLLIN; while (1) { + int save_err; + pollfd.revents = 0; printerr(1, "entering poll\n"); ret = poll(&pollfd, 1, -1); + save_err = errno; printerr(1, "leaving poll\n"); if (ret < 0) { - if (errno != EINTR) - printerr(0, "error return from poll\n"); + if (save_err != EINTR) + printerr(0, "error return from poll: %s\n", + strerror(save_err)); } else if (ret == 0) { /* timeout; shouldn't happen. */ } else { diff --git a/utils/svcgssd/svcgssd_proc.c b/utils/svcgssd/svcgssd_proc.c index 8faddc1..dfa3c4c 100644 --- a/utils/svcgssd/svcgssd_proc.c +++ b/utils/svcgssd/svcgssd_proc.c @@ -52,11 +52,6 @@ #include "context.h" #include "cacheio.h" -/* XXX: ? */ -#ifndef NGROUPS -#define NGROUPS 32 -#endif - extern char * mech2file(gss_OID mech); #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel" #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.rpcsec.init/channel" @@ -162,6 +157,30 @@ send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token, #define rpcsec_gsserr_credproblem 13 #define rpcsec_gsserr_ctxproblem 14 +static void +add_supplementary_groups(char *secname, char *name, struct svc_cred *cred) +{ + int ret; + static gid_t *groups = NULL; + + cred->cr_ngroups = NGROUPS; + ret = nfs4_gss_princ_to_grouplist(secname, name, + cred->cr_groups, &cred->cr_ngroups); + if (ret < 0) { + groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t)); + ret = nfs4_gss_princ_to_grouplist(secname, name, + groups, &cred->cr_ngroups); + if (ret < 0) + cred->cr_ngroups = 0; + else { + if (cred->cr_ngroups > NGROUPS) + cred->cr_ngroups = NGROUPS; + memcpy(cred->cr_groups, groups, + cred->cr_ngroups*sizeof(gid_t)); + } + } +} + static int get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred) { @@ -172,6 +191,7 @@ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred) uid_t uid, gid; gss_OID name_type; char *secname; + gid_t *groups; maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type); if (maj_stat != GSS_S_COMPLETE) @@ -190,8 +210,7 @@ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred) goto out_free; cred->cr_uid = uid; cred->cr_gid = gid; - /*XXX: want add_supplementary_groups(secname, sname, cred)? */ - cred->cr_ngroups = 0; + add_supplementary_groups(secname, sname, cred); res = 0; out_free: free(sname); @@ -243,7 +262,7 @@ handle_nullreq(FILE *f) { /* XXX initialize to a random integer to reduce chances of unnecessary * invalidation of existing ctx's on restarting svcgssd. */ static u_int32_t handle_seq = 0; - char in_tok_buf[1023]; + char in_tok_buf[8192]; char in_handle_buf[15]; char out_handle_buf[15]; gss_buffer_desc in_tok = {.value = in_tok_buf}, @@ -273,15 +292,16 @@ handle_nullreq(FILE *f) { cp = lbuf; - in_handle.length - = qword_get(&cp, in_handle.value, sizeof(in_handle_buf)); + in_handle.length = (size_t) qword_get(&cp, in_handle.value, + sizeof(in_handle_buf)); printerr(2, "in_handle: \n"); print_hexl(2, in_handle.value, in_handle.length); handle_seq++; out_handle.length = sizeof(handle_seq); memcpy(out_handle.value, &handle_seq, sizeof(handle_seq)); - in_tok.length = qword_get(&cp, in_tok.value, sizeof(in_tok_buf)); + in_tok.length = (size_t) qword_get(&cp, in_tok.value, + sizeof(in_tok_buf)); printerr(2, "in_tok: \n"); print_hexl(2, in_tok.value, in_tok.length); @@ -294,8 +314,6 @@ handle_nullreq(FILE *f) { if (in_handle.length != 0) { /* CONTINUE_INIT case */ printerr(0, "WARNING: handle_nullreq: " "CONTINUE_INIT unsupported\n"); - send_response(f, &in_handle, &in_tok, -1, -1, &null_token, - &null_token); goto out_err; } @@ -306,14 +324,11 @@ handle_nullreq(FILE *f) { printerr(0, "WARNING: gss_accept_sec_context failed\n"); pgsserr("handle_nullreq: gss_accept_sec_context", maj_stat, min_stat, mech); - send_response(f, &in_handle, &in_tok, maj_stat, min_stat, - &null_token, &null_token); goto out_err; } if (get_ids(client_name, mech, &cred)) { printerr(0, "WARNING: handle_nullreq: get_uid failed\n"); - send_response(f, &in_handle, &in_tok, GSS_S_BAD_NAME /* XXX? */, - 0, &null_token, &null_token); + maj_stat = GSS_S_BAD_NAME; /* XXX ? */ goto out_err; } @@ -322,8 +337,7 @@ handle_nullreq(FILE *f) { if (serialize_context_for_kernel(ctx, &ctx_token)) { printerr(0, "WARNING: handle_nullreq: " "serialize_context_for_kernel failed\n"); - send_response(f, &in_handle, &in_tok, -1, /* XXX? */ - 0, &null_token, &null_token); + maj_stat = GSS_S_FAILURE; goto out_err; } do_svc_downcall(&out_handle, &cred, mech, &ctx_token); @@ -331,6 +345,8 @@ handle_nullreq(FILE *f) { &out_handle, &out_tok); goto out; out_err: + send_response(f, &in_handle, &in_tok, maj_stat, min_stat, + &null_token, &null_token); out: if (ctx_token.value != NULL) free(ctx_token.value);