| diff --git a/aclocal/keyutils.m4 b/aclocal/keyutils.m4 |
| index a392c0e..16b225d 100644 |
| --- a/aclocal/keyutils.m4 |
| +++ b/aclocal/keyutils.m4 |
| @@ -8,4 +8,8 @@ AC_DEFUN([AC_KEYUTILS], [ |
| |
| AC_CHECK_HEADERS([keyutils.h]) |
| |
| + AC_CHECK_LIB([keyutils], [find_key_by_type_and_desc], |
| + [AC_DEFINE([HAVE_FIND_KEY_BY_TYPE_AND_DESC], [1], |
| + [Define to 1 if you have the `find_key_by_type_and_desc' function.])],) |
| + |
| ])dnl |
| diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c |
| index 10f69f9..9c49d42 100644 |
| --- a/utils/nfsidmap/nfsidmap.c |
| +++ b/utils/nfsidmap/nfsidmap.c |
| @@ -1,3 +1,4 @@ |
| +#include "config.h" |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| @@ -15,7 +16,7 @@ |
| #include "conffile.h" |
| |
| int verbose = 0; |
| -char *usage="Usage: %s [-v] [-c || [-u|-g|-r key] || [-t timeout] key desc]"; |
| +char *usage = "Usage: %s [-v] [-c || [-u|-g|-r key] || -d || -l || [-t timeout] key desc]"; |
| |
| #define MAX_ID_LEN 11 |
| #define IDMAP_NAMESZ 128 |
| @@ -31,15 +32,163 @@ char *usage="Usage: %s [-v] [-c || [-u|-g|-r key] || [-t timeout] key desc]"; |
| #define PATH_IDMAPDCONF "/etc/idmapd.conf" |
| #endif |
| |
| -static int keyring_clear(char *keyring); |
| - |
| #define UIDKEYS 0x1 |
| #define GIDKEYS 0x2 |
| |
| +#ifndef HAVE_FIND_KEY_BY_TYPE_AND_DESC |
| +static key_serial_t find_key_by_type_and_desc(const char *type, |
| + const char *desc, key_serial_t destringid) |
| +{ |
| + char buf[BUFSIZ]; |
| + key_serial_t key; |
| + FILE *fp; |
| + |
| + if ((fp = fopen(PROCKEYS, "r")) == NULL) { |
| + xlog_err("fopen(%s) failed: %m", PROCKEYS); |
| + return -1; |
| + } |
| + |
| + key = -1; |
| + while(fgets(buf, BUFSIZ, fp) != NULL) { |
| + unsigned int id; |
| + |
| + if (strstr(buf, type) == NULL) |
| + continue; |
| + if (strstr(buf, desc) == NULL) |
| + continue; |
| + if (sscanf(buf, "%x %*s", &id) != 1) { |
| + xlog_err("Unparsable keyring entry in %s", PROCKEYS); |
| + continue; |
| + } |
| + |
| + key = (key_serial_t)id; |
| + break; |
| + } |
| + |
| + fclose(fp); |
| + return key; |
| +} |
| +#endif |
| + |
| +/* |
| + * Clear all the keys on the given keyring |
| + */ |
| +static int keyring_clear(const char *keyring) |
| +{ |
| + key_serial_t key; |
| + |
| + key = find_key_by_type_and_desc("keyring", keyring, 0); |
| + if (key == -1) { |
| + if (verbose) |
| + xlog_warn("'%s' keyring was not found.", keyring); |
| + return EXIT_SUCCESS; |
| + } |
| + |
| + if (keyctl_clear(key) < 0) { |
| + xlog_err("keyctl_clear(0x%x) failed: %m", |
| + (unsigned int)key); |
| + return EXIT_FAILURE; |
| + } |
| + |
| + if (verbose) |
| + xlog_warn("'%s' cleared", keyring); |
| + return EXIT_SUCCESS; |
| +} |
| + |
| +static int display_default_domain(void) |
| +{ |
| + char domain[NFS4_MAX_DOMAIN_LEN]; |
| + int rc; |
| + |
| + rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN); |
| + if (rc) { |
| + xlog_errno(rc, "nfs4_get_default_domain failed: %m"); |
| + return EXIT_FAILURE; |
| + } |
| + |
| + printf("%s\n", domain); |
| + return EXIT_SUCCESS; |
| +} |
| + |
| +static void list_key(key_serial_t key) |
| +{ |
| + char *buffer, *c; |
| + int rc; |
| + |
| + rc = keyctl_describe_alloc(key, &buffer); |
| + if (rc < 0) { |
| + switch (errno) { |
| + case EKEYEXPIRED: |
| + printf("Expired key not displayed\n"); |
| + break; |
| + default: |
| + xlog_err("Failed to describe key: %m"); |
| + } |
| + return; |
| + } |
| + |
| + c = strrchr(buffer, ';'); |
| + if (!c) { |
| + xlog_err("Unparsable key not displayed\n"); |
| + goto out_free; |
| + } |
| + printf(" %s\n", ++c); |
| + |
| +out_free: |
| + free(buffer); |
| +} |
| + |
| +static void list_keys(const char *ring_name, key_serial_t ring_id) |
| +{ |
| + key_serial_t *key; |
| + void *keylist; |
| + int count; |
| + |
| + count = keyctl_read_alloc(ring_id, &keylist); |
| + if (count < 0) { |
| + xlog_err("Failed to read keyring %s: %m", ring_name); |
| + return; |
| + } |
| + count /= (int)sizeof(*key); |
| + |
| + switch (count) { |
| + case 0: |
| + printf("No %s keys found.\n", ring_name); |
| + break; |
| + case 1: |
| + printf("1 %s key found:\n", ring_name); |
| + break; |
| + default: |
| + printf("%u %s keys found:\n", count, ring_name); |
| + } |
| + |
| + for (key = keylist; count--; key++) |
| + list_key(*key); |
| + |
| + free(keylist); |
| +} |
| + |
| +/* |
| + * List all keys on a keyring |
| + */ |
| +static int list_keyring(const char *keyring) |
| +{ |
| + key_serial_t key; |
| + |
| + key = find_key_by_type_and_desc("keyring", keyring, 0); |
| + if (key == -1) { |
| + xlog_err("'%s' keyring was not found.", keyring); |
| + return EXIT_FAILURE; |
| + } |
| + |
| + list_keys(keyring, key); |
| + return EXIT_SUCCESS; |
| +} |
| + |
| /* |
| * Find either a user or group id based on the name@domain string |
| */ |
| -int id_lookup(char *name_at_domain, key_serial_t key, int type) |
| +static int id_lookup(char *name_at_domain, key_serial_t key, int type) |
| { |
| char id[MAX_ID_LEN]; |
| uid_t uid = 0; |
| @@ -53,30 +202,33 @@ int id_lookup(char *name_at_domain, key_serial_t key, int type) |
| rc = nfs4_group_owner_to_gid(name_at_domain, &gid); |
| sprintf(id, "%u", gid); |
| } |
| - if (rc < 0) |
| + if (rc < 0) { |
| xlog_errno(rc, "id_lookup: %s: failed: %m", |
| (type == USER ? "nfs4_owner_to_uid" : "nfs4_group_owner_to_gid")); |
| + return EXIT_FAILURE; |
| + } |
| |
| - if (rc == 0) { |
| - rc = keyctl_instantiate(key, id, strlen(id) + 1, 0); |
| - if (rc < 0) { |
| - switch(rc) { |
| - case -EDQUOT: |
| - case -ENFILE: |
| - case -ENOMEM: |
| - /* |
| - * The keyring is full. Clear the keyring and try again |
| - */ |
| - rc = keyring_clear(DEFAULT_KEYRING); |
| - if (rc == 0) |
| - rc = keyctl_instantiate(key, id, strlen(id) + 1, 0); |
| - break; |
| - default: |
| + rc = EXIT_SUCCESS; |
| + if (keyctl_instantiate(key, id, strlen(id) + 1, 0)) { |
| + switch (errno) { |
| + case EDQUOT: |
| + case ENFILE: |
| + case ENOMEM: |
| + /* |
| + * The keyring is full. Clear the keyring and try again |
| + */ |
| + rc = keyring_clear(DEFAULT_KEYRING); |
| + if (rc) |
| break; |
| + if (keyctl_instantiate(key, id, strlen(id) + 1, 0)) { |
| + rc = EXIT_FAILURE; |
| + xlog_err("id_lookup: keyctl_instantiate failed: %m"); |
| } |
| + break; |
| + default: |
| + rc = EXIT_FAILURE; |
| + break; |
| } |
| - if (rc < 0) |
| - xlog_err("id_lookup: keyctl_instantiate failed: %m"); |
| } |
| |
| return rc; |
| @@ -85,7 +237,7 @@ int id_lookup(char *name_at_domain, key_serial_t key, int type) |
| /* |
| * Find the name@domain string from either a user or group id |
| */ |
| -int name_lookup(char *id, key_serial_t key, int type) |
| +static int name_lookup(char *id, key_serial_t key, int type) |
| { |
| char name[IDMAP_NAMESZ]; |
| char domain[NFS4_MAX_DOMAIN_LEN]; |
| @@ -94,11 +246,10 @@ int name_lookup(char *id, key_serial_t key, int type) |
| int rc; |
| |
| rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN); |
| - if (rc != 0) { |
| + if (rc) { |
| xlog_errno(rc, |
| "name_lookup: nfs4_get_default_domain failed: %m"); |
| - rc = -1; |
| - goto out; |
| + return EXIT_FAILURE; |
| } |
| |
| if (type == USER) { |
| @@ -108,61 +259,21 @@ int name_lookup(char *id, key_serial_t key, int type) |
| gid = atoi(id); |
| rc = nfs4_gid_to_name(gid, domain, name, IDMAP_NAMESZ); |
| } |
| - if (rc < 0) |
| + if (rc) { |
| xlog_errno(rc, "name_lookup: %s: failed: %m", |
| (type == USER ? "nfs4_uid_to_name" : "nfs4_gid_to_name")); |
| - |
| - if (rc == 0) { |
| - rc = keyctl_instantiate(key, &name, strlen(name), 0); |
| - if (rc < 0) |
| - xlog_err("name_lookup: keyctl_instantiate failed: %m"); |
| + return EXIT_FAILURE; |
| } |
| -out: |
| - return rc; |
| -} |
| -/* |
| - * Clear all the keys on the given keyring |
| - */ |
| -static int keyring_clear(char *keyring) |
| -{ |
| - FILE *fp; |
| - char buf[BUFSIZ]; |
| - key_serial_t key; |
| |
| - if (keyring == NULL) |
| - keyring = DEFAULT_KEYRING; |
| - |
| - if ((fp = fopen(PROCKEYS, "r")) == NULL) { |
| - xlog_err("fopen(%s) failed: %m", PROCKEYS); |
| - return 1; |
| + rc = EXIT_SUCCESS; |
| + if (keyctl_instantiate(key, &name, strlen(name), 0)) { |
| + rc = EXIT_FAILURE; |
| + xlog_err("name_lookup: keyctl_instantiate failed: %m"); |
| } |
| |
| - while(fgets(buf, BUFSIZ, fp) != NULL) { |
| - if (strstr(buf, "keyring") == NULL) |
| - continue; |
| - if (strstr(buf, keyring) == NULL) |
| - continue; |
| - if (verbose) { |
| - *(strchr(buf, '\n')) = '\0'; |
| - xlog_warn("clearing '%s'", buf); |
| - } |
| - /* |
| - * The key is the first arugment in the string |
| - */ |
| - *(strchr(buf, ' ')) = '\0'; |
| - sscanf(buf, "%x", &key); |
| - if (keyctl_clear(key) < 0) { |
| - xlog_err("keyctl_clear(0x%x) failed: %m", key); |
| - fclose(fp); |
| - return 1; |
| - } |
| - fclose(fp); |
| - return 0; |
| - } |
| - xlog_err("'%s' keyring was not found.", keyring); |
| - fclose(fp); |
| - return 1; |
| + return rc; |
| } |
| + |
| /* |
| * Revoke a key |
| */ |
| @@ -177,7 +288,7 @@ static int key_invalidate(char *keystr, int keymask) |
| |
| if ((fp = fopen(PROCKEYS, "r")) == NULL) { |
| xlog_err("fopen(%s) failed: %m", PROCKEYS); |
| - return 1; |
| + return EXIT_FAILURE; |
| } |
| |
| while(fgets(buf, BUFSIZ, fp) != NULL) { |
| @@ -211,18 +322,18 @@ static int key_invalidate(char *keystr, int keymask) |
| if (keyctl_invalidate(key) < 0) { |
| xlog_err("keyctl_invalidate(0x%x) failed: %m", key); |
| fclose(fp); |
| - return 1; |
| + return EXIT_FAILURE; |
| } |
| |
| keymask &= ~mask; |
| if (keymask == 0) { |
| fclose(fp); |
| - return 0; |
| + return EXIT_SUCCESS; |
| } |
| } |
| xlog_err("'%s' key was not found.", keystr); |
| fclose(fp); |
| - return 1; |
| + return EXIT_FAILURE; |
| } |
| |
| int main(int argc, char **argv) |
| @@ -234,7 +345,7 @@ int main(int argc, char **argv) |
| int timeout = 600; |
| key_serial_t key; |
| char *progname, *keystr = NULL; |
| - int clearing = 0, keymask = 0; |
| + int clearing = 0, keymask = 0, display = 0, list = 0; |
| |
| /* Set the basename */ |
| if ((progname = strrchr(argv[0], '/')) != NULL) |
| @@ -244,8 +355,14 @@ int main(int argc, char **argv) |
| |
| xlog_open(progname); |
| |
| - while ((opt = getopt(argc, argv, "u:g:r:ct:v")) != -1) { |
| + while ((opt = getopt(argc, argv, "du:g:r:ct:vl")) != -1) { |
| switch (opt) { |
| + case 'd': |
| + display++; |
| + break; |
| + case 'l': |
| + list++; |
| + break; |
| case 'u': |
| keymask = UIDKEYS; |
| keystr = strdup(optarg); |
| @@ -273,28 +390,35 @@ int main(int argc, char **argv) |
| } |
| } |
| |
| + if (geteuid() != 0) { |
| + xlog_err("Must be run as root."); |
| + return EXIT_FAILURE; |
| + } |
| + |
| if ((rc = nfs4_init_name_mapping(PATH_IDMAPDCONF))) { |
| xlog_errno(rc, "Unable to create name to user id mappings."); |
| - return 1; |
| + return EXIT_FAILURE; |
| } |
| if (!verbose) |
| verbose = conf_get_num("General", "Verbosity", 0); |
| |
| + if (display) |
| + return display_default_domain(); |
| + if (list) |
| + return list_keyring(DEFAULT_KEYRING); |
| if (keystr) { |
| - rc = key_invalidate(keystr, keymask); |
| - return rc; |
| + return key_invalidate(keystr, keymask); |
| } |
| if (clearing) { |
| xlog_syslog(0); |
| - rc = keyring_clear(DEFAULT_KEYRING); |
| - return rc; |
| + return keyring_clear(DEFAULT_KEYRING); |
| } |
| |
| - xlog_stderr(0); |
| + xlog_stderr(verbose); |
| if ((argc - optind) != 2) { |
| - xlog_err("Bad arg count. Check /etc/request-key.conf"); |
| + xlog_warn("Bad arg count. Check /etc/request-key.conf"); |
| xlog_warn(usage, progname); |
| - return 1; |
| + return EXIT_FAILURE; |
| } |
| |
| if (verbose) |
| @@ -305,11 +429,15 @@ int main(int argc, char **argv) |
| arg = strdup(argv[optind]); |
| if (arg == NULL) { |
| xlog_err("strdup failed: %m"); |
| - return 1; |
| + return EXIT_FAILURE; |
| } |
| type = strtok(arg, ":"); |
| value = strtok(NULL, ":"); |
| - |
| + if (value == NULL) { |
| + free(arg); |
| + xlog_err("Error: Null uid/gid value."); |
| + return EXIT_FAILURE; |
| + } |
| if (verbose) { |
| xlog_warn("key: 0x%lx type: %s value: %s timeout %ld", |
| key, type, value, timeout); |
| @@ -328,7 +456,7 @@ int main(int argc, char **argv) |
| rc = name_lookup(value, key, GROUP); |
| |
| /* Set timeout to 10 (600 seconds) minutes */ |
| - if (rc == 0) |
| + if (rc == EXIT_SUCCESS) |
| keyctl_set_timeout(key, timeout); |
| |
| free(arg); |
| diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man |
| index 3a3a523..0275bdf 100644 |
| --- a/utils/nfsidmap/nfsidmap.man |
| +++ b/utils/nfsidmap/nfsidmap.man |
| @@ -11,30 +11,72 @@ nfsidmap \- The NFS idmapper upcall program |
| .B "nfsidmap [-v] [-c]" |
| .br |
| .B "nfsidmap [-v] [-u|-g|-r user]" |
| +.br |
| +.B "nfsidmap -d" |
| +.br |
| +.B "nfsidmap -l" |
| .SH DESCRIPTION |
| -The file |
| +The NFSv4 protocol represents the local system's UID and GID values |
| +on the wire as strings of the form |
| +.IR user@domain . |
| +The process of translating from UID to string and string to UID is |
| +referred to as "ID mapping." |
| +.PP |
| +The system derives the |
| +.I user |
| +part of the string by performing a password or group lookup. |
| +The lookup mechanism is configured in |
| +.IR /etc/idmapd.conf . |
| +.PP |
| +By default, the |
| +.I domain |
| +part of the string is the system's DNS domain name. |
| +It can also be specified in |
| +.I /etc/idmapd.conf |
| +if the system is multi-homed, |
| +or if the system's DNS domain name does |
| +not match the name of the system's Kerberos realm. |
| +.PP |
| +The |
| .I /usr/sbin/nfsidmap |
| -is used by the NFS idmapper to translate user and group ids into names, and to |
| -translate user and group names into ids. Idmapper uses request-key to perform |
| -the upcall and cache the result. |
| +program performs translations on behalf of the kernel. |
| +The kernel uses the request-key mechanism to perform |
| +an upcall. |
| .I /usr/sbin/nfsidmap |
| -is called by /sbin/request-key, and will perform the translation and |
| -initialize a key with the resulting information. |
| +is invoked by /sbin/request-key, performs the translation, |
| +and initializes a key with the resulting information. |
| +The kernel then caches the translation results in the key. |
| .PP |
| .I nfsidmap |
| -can also used to clear the keyring of all the keys or |
| -revoke one particular key. |
| -This is useful when the id mappings have failed to due |
| -to a lookup error resulting in all the cached uids/gids to be set |
| -to the user id nobody. |
| +can also clear cached ID map results in the kernel, |
| +or revoke one particular key. |
| +An incorrect cached key can result in file and directory ownership |
| +reverting to "nobody" on NFSv4 mount points. |
| +.PP |
| +In addition, the |
| +.B -d |
| +and |
| +.B -l |
| +options are available to help diagnose misconfigurations. |
| +They have no effect on the keyring containing ID mapping results. |
| .SH OPTIONS |
| .TP |
| .B -c |
| Clear the keyring of all the keys. |
| .TP |
| +.B -d |
| +Display the system's effective NFSv4 domain name on |
| +.IR stdout . |
| +.TP |
| .B -g user |
| Revoke the gid key of the given user. |
| .TP |
| +.B -l |
| +Display on |
| +.I stdout |
| +all keys currently in the keyring used to cache ID mapping results. |
| +These keys are visible only to the superuser. |
| +.TP |
| .B -r user |
| Revoke both the uid and gid key of the given user. |
| .TP |
| @@ -89,5 +131,15 @@ Notice that the new line was added above the line for the generic program. |
| request-key will find the first matching line and run the corresponding program. |
| In this case, /some/other/program will handle all uid lookups, and |
| /usr/sbin/nfsidmap will handle gid, user, and group lookups. |
| +.SH FILES |
| +.TP |
| +.I /etc/idmapd.conf |
| +ID mapping configuration file |
| +.TP |
| +.I /etc/request-key.conf |
| +Request key configuration file |
| +.SH "SEE ALSO" |
| +.BR idmapd.conf (5), |
| +.BR request-key (8) |
| .SH AUTHOR |
| Bryan Schumaker, <bjschuma@netapp.com> |