diff options
-rw-r--r-- | ChangeLog | 45 | ||||
-rw-r--r-- | support/include/config.h.in | 9 | ||||
-rw-r--r-- | utils/exportfs/exports.man | 8 | ||||
-rw-r--r-- | utils/gssd/gss_util.c | 16 | ||||
-rw-r--r-- | utils/gssd/krb5_util.c | 88 | ||||
-rw-r--r-- | utils/idmapd/idmapd.c | 16 | ||||
-rw-r--r-- | utils/svcgssd/svcgssd_main_loop.c | 11 | ||||
-rw-r--r-- | utils/svcgssd/svcgssd_proc.c | 54 |
8 files changed, 177 insertions, 70 deletions
@@ -1,4 +1,49 @@ 2005-08-26 Kevin Coffman <kwc@citi.umich.edu> + * 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 <andrew.mahone@gmail.com> 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 <kwc@citi.umich.edu> * configure.in etc Consolidate some of the Kerberos checking instead of repeating the same things for MIT and Heimdal. 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); |