+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
* 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
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.
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);
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);
}
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
}
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);
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",
{
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,
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 ===*/
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 "
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);
}
/*
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);
}
/*
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;
}
{
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);
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;
}
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 {
#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"
#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)
{
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)
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);
/* 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},
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);
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;
}
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;
}
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);
&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);