4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.gnu.org/licenses/gpl-2.0.html
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Use is subject to license terms.
26 * Copyright (c) 2011, 2016, Intel Corporation.
29 * This file is part of Lustre, http://www.lustre.org/
46 #include <sys/types.h>
54 #include <libcfs/util/param.h>
55 #include <linux/lnet/nidstr.h>
56 #include <linux/lustre/lustre_user.h>
57 #include <linux/lustre/lustre_idl.h>
59 #define PERM_PATHNAME "/etc/lustre/perm.conf"
60 #define LUSTRE_PASSWD "/etc/lustre/passwd"
61 #define LUSTRE_GROUP "/etc/lustre/group"
63 #define L_GETIDENTITY_LOOKUP_CMD "lookup"
64 #define NSS_MODULES_MAX_NR 8
65 #define NSS_MODULE_NAME_SIZE 32
66 #define NSS_SYMBOL_NAME_LEN_MAX 256
68 static int nss_pw_buf_len;
69 static void *nss_pw_buf;
70 static int nss_grent_buf_len;
71 static void *nss_grent_buf;
72 static int g_n_nss_modules;
73 static int grent_mod_no = -1;
76 char name[NSS_MODULE_NAME_SIZE];
77 int (*getpwuid)(struct nss_module *mod, uid_t, struct passwd *pwd);
78 int (*getgrent)(struct nss_module *mod, struct group *result);
79 void (*endgrent)(struct nss_module *mod);
80 void (*fini)(struct nss_module *mod);
85 int (*l_getpwuid)(uid_t, struct passwd *pwd,
86 char *buffer, size_t buflen,
88 int (*l_getgrent)(struct group *result, char *buffer,
89 size_t buflen, int *errnop);
90 int (*l_endgrent)(void);
99 static struct nss_module g_nss_modules[NSS_MODULES_MAX_NR];
101 #define NSS_LIB_NAME_PATTERN "libnss_%s.so.2"
104 * permission file format is like this:
105 * {nid} {uid} {perms}
107 * '*' nid means any nid
108 * '*' uid means any uid
109 * the valid values for perms are:
110 * setuid/setgid/setgrp -- enable corresponding perm
111 * nosetuid/nosetgid/nosetgrp -- disable corresponding perm
112 * they can be listed together, separated by ',',
113 * when perm and noperm are in the same line (item), noperm is preferential,
114 * when they are in different lines (items), the latter is preferential,
115 * '*' nid is as default perm, and is not preferential.
118 static char *progname;
120 static void usage(void)
123 "\nusage: %s {-d|mdtname} {uid}\n"
124 "Normally invoked as an upcall from Lustre, set via:\n"
125 "lctl set_param mdt.${mdtname}.identity_upcall={path to upcall}\n"
126 "\t-d: debug, print values to stdout instead of Lustre\n"
127 "\tNSS support enabled\n",
131 static void errlog(const char *fmt, ...)
135 openlog(progname, LOG_PERROR | LOG_PID, LOG_AUTHPRIV);
138 vsyslog(LOG_WARNING, fmt, args);
144 static int compare_gids(const void *v1, const void *v2)
146 return (*(gid_t *)v1 - *(gid_t *)v2);
149 /** getpwuid() replacement */
150 static struct passwd *getpwuid_nss(uid_t uid)
152 static struct passwd pw;
155 for (i = 0; i < g_n_nss_modules; i++) {
156 struct nss_module *mod = g_nss_modules + i;
158 if (mod->getpwuid(mod, uid, &pw) == 0)
165 * getgrent() replacement.
166 * simulate getgrent(3) across nss modules
168 static struct group *getgrent_nss(void)
170 static struct group grp;
172 if (grent_mod_no < 0)
175 while (grent_mod_no < g_n_nss_modules) {
176 struct nss_module *mod = g_nss_modules + grent_mod_no;
178 if (mod->getgrent(mod, &grp) == 0)
186 /** endgrent() replacement */
187 static void endgrent_nss(void)
189 if (grent_mod_no < g_n_nss_modules
190 && grent_mod_no >= 0) {
191 struct nss_module *mod = g_nss_modules+grent_mod_no;
198 /** lookup symbol in dynamically loaded nss module */
199 static void *get_nss_sym(struct nss_module *mod, const char *op)
203 char symbuf[NSS_SYMBOL_NAME_LEN_MAX];
205 bytes = snprintf(symbuf, NSS_SYMBOL_NAME_LEN_MAX - 1, "_nss_%s_%s",
207 if (bytes >= NSS_SYMBOL_NAME_LEN_MAX - 1) {
208 errlog("symbol name too long\n");
211 res = dlsym(mod->u.lib.l_ptr, symbuf);
213 errlog("cannot find symbol %s in nss module \"%s\": %s\n",
214 symbuf, mod->name, dlerror());
218 /** allocate bigger buffer */
219 static void enlarge_nss_buffer(void **buf, int *bufsize)
222 *bufsize = *bufsize * 2;
223 *buf = malloc(*bufsize);
225 errlog("no memory to allocate bigger buffer of %d bytes\n",
231 static int getpwuid_nss_lib(struct nss_module *nss, uid_t uid,
237 err = nss->u.lib.l_getpwuid(uid, pw, nss_pw_buf,
238 nss_pw_buf_len, &tmp_errno);
239 if (err == NSS_STATUS_TRYAGAIN) {
240 if (tmp_errno == ERANGE) {
241 /* buffer too small */
242 enlarge_nss_buffer(&nss_pw_buf,
249 if (err == NSS_STATUS_SUCCESS)
254 static int getgrent_nss_lib(struct nss_module *nss, struct group *gr)
259 err = nss->u.lib.l_getgrent(gr, nss_grent_buf,
260 nss_grent_buf_len, &tmp_errno);
261 if (err == NSS_STATUS_TRYAGAIN) {
262 if (tmp_errno == ERANGE) {
263 /* buffer too small */
264 enlarge_nss_buffer(&nss_grent_buf,
271 if (err == NSS_STATUS_SUCCESS)
276 static void endgrent_nss_lib(struct nss_module *mod)
278 mod->u.lib.l_endgrent();
281 /** destroy a "shared lib" nss module */
282 static void fini_nss_lib_module(struct nss_module *mod)
284 if (mod->u.lib.l_ptr)
285 dlclose(mod->u.lib.l_ptr);
288 /** load and initialize a "shared lib" nss module */
289 static int init_nss_lib_module(struct nss_module *mod, char *name)
291 char lib_file_name[sizeof(NSS_LIB_NAME_PATTERN) + sizeof(mod->name)];
293 if (strlen(name) >= sizeof(mod->name)) {
294 errlog("module name (%s) too long\n", name);
298 strncpy(mod->name, name, sizeof(mod->name));
299 mod->name[sizeof(mod->name) - 1] = '\0';
301 snprintf(lib_file_name, sizeof(lib_file_name), NSS_LIB_NAME_PATTERN,
304 mod->getpwuid = getpwuid_nss_lib;
305 mod->getgrent = getgrent_nss_lib;
306 mod->endgrent = endgrent_nss_lib;
307 mod->fini = fini_nss_lib_module;
309 mod->u.lib.l_ptr = dlopen(lib_file_name, RTLD_NOW);
310 if (mod->u.lib.l_ptr == NULL) {
311 errlog("dl error %s\n", dlerror());
314 mod->u.lib.l_getpwuid = get_nss_sym(mod, "getpwuid_r");
315 if (mod->getpwuid == NULL)
318 mod->u.lib.l_getgrent = get_nss_sym(mod, "getgrent_r");
319 if (mod->getgrent == NULL)
322 mod->u.lib.l_endgrent = get_nss_sym(mod, "endgrent");
323 if (mod->endgrent == NULL)
329 static void fini_lustre_nss_module(struct nss_module *mod)
331 if (mod->u.files.f_passwd)
332 fclose(mod->u.files.f_passwd);
333 if (mod->u.files.f_group)
334 fclose(mod->u.files.f_group);
337 static int getpwuid_lustre_nss(struct nss_module *mod, uid_t uid,
342 while ((pos = fgetpwent(mod->u.files.f_passwd)) != NULL) {
343 if (pos->pw_uid == uid) {
351 static int getgrent_lustre_nss(struct nss_module *mod, struct group *gr)
355 pos = fgetgrent(mod->u.files.f_group);
363 static void endgrent_lustre_nss(struct nss_module *mod)
367 /** initialize module to access local /etc/lustre/passwd,group files */
368 static int init_lustre_module(struct nss_module *mod)
370 mod->fini = fini_lustre_nss_module;
371 mod->getpwuid = getpwuid_lustre_nss;
372 mod->getgrent = getgrent_lustre_nss;
373 mod->endgrent = endgrent_lustre_nss;
375 mod->u.files.f_passwd = fopen(LUSTRE_PASSWD, "r");
376 if (mod->u.files.f_passwd == NULL)
379 mod->u.files.f_group = fopen(LUSTRE_GROUP, "r");
380 if (mod->u.files.f_group == NULL)
383 snprintf(mod->name, sizeof(mod->name), "lustre");
387 /** load and initialize the "nss" system */
388 static void init_nss(void)
390 nss_pw_buf_len = sysconf(_SC_GETPW_R_SIZE_MAX);
391 if (nss_pw_buf_len == -1) {
395 nss_pw_buf = malloc(nss_pw_buf_len);
396 if (nss_pw_buf == NULL) {
397 perror("pw buffer allocation");
401 nss_grent_buf_len = sysconf(_SC_GETGR_R_SIZE_MAX);
402 if (nss_grent_buf_len == -1) {
406 nss_grent_buf = malloc(nss_grent_buf_len);
407 if (nss_grent_buf == NULL) {
408 perror("grent buffer allocation");
414 static void fini_nss(void)
418 for (i = 0; i < g_n_nss_modules; i++) {
419 struct nss_module *mod = g_nss_modules + i;
428 /** get supplementary group info and fill downcall data */
429 static int get_groups_nss(struct identity_downcall_data *data,
430 unsigned int maxgroups)
435 unsigned int ngroups = 0;
439 pw = getpwuid_nss(data->idd_uid);
441 data->idd_err = errno ? errno : EIDRM;
442 errlog("no such user %u\n", data->idd_uid);
446 data->idd_gid = pw->pw_gid;
447 pw_name = strdup(pw->pw_name);
448 groups = data->idd_groups;
450 while ((gr = getgrent_nss()) != NULL && ngroups < maxgroups) {
451 if (gr->gr_gid == pw->pw_gid)
455 for (i = 0; gr->gr_mem[i]; i++) {
456 if (!strcmp(gr->gr_mem[i], pw_name)) {
457 groups[ngroups++] = gr->gr_gid;
466 qsort(groups, ngroups, sizeof(*groups), compare_gids);
467 data->idd_ngroups = ngroups;
473 int get_groups_local(struct identity_downcall_data *data,
474 unsigned int maxgroups)
476 gid_t *groups, *groups_tmp = NULL;
477 unsigned int ngroups = 0;
482 pw = getpwuid(data->idd_uid);
484 errlog("no such user %u\n", data->idd_uid);
485 data->idd_err = errno ? errno : EIDRM;
489 data->idd_gid = pw->pw_gid;
491 groups = data->idd_groups;
494 * Allocate array of size maxgroups instead of handling two
495 * consecutive and potentially racy getgrouplist() calls.
497 groups_tmp = malloc(maxgroups * sizeof(gid_t));
499 data->idd_err = errno ? errno : ENOMEM;
500 errlog("malloc error=%u\n", data->idd_err);
504 ngroups_tmp = maxgroups;
505 if (getgrouplist(pw->pw_name, pw->pw_gid, groups_tmp, &ngroups_tmp) <
508 data->idd_err = errno ? errno : EIDRM;
509 errlog("getgrouplist() error for uid %u: error=%u\n",
510 data->idd_uid, data->idd_err);
514 /* Do not place user's group ID in to the resulting groups list */
515 for (i = 0; i < ngroups_tmp; i++)
516 if (pw->pw_gid != groups_tmp[i])
517 groups[ngroups++] = groups_tmp[i];
520 qsort(groups, ngroups, sizeof(*groups), compare_gids);
521 data->idd_ngroups = ngroups;
527 int get_groups_common(struct identity_downcall_data *data,
528 unsigned int maxgroups)
531 return get_groups_nss(data, maxgroups);
532 return get_groups_local(data, maxgroups);
535 static inline int comment_line(char *line)
539 while (*p && (*p == ' ' || *p == '\t'))
542 if (!*p || *p == '\n' || *p == '#')
547 static inline int match_uid(uid_t uid, const char *str)
552 if (!strcmp(str, "*"))
555 uid2 = strtoul(str, &end, 0);
558 return (uid == uid2);
566 static struct perm_type perm_types[] = {
567 { "setuid", CFS_SETUID_PERM },
568 { "setgid", CFS_SETGID_PERM },
569 { "setgrp", CFS_SETGRP_PERM },
575 static struct perm_type noperm_types[] = {
576 { "nosetuid", CFS_SETUID_PERM },
577 { "nosetgid", CFS_SETGID_PERM },
578 { "nosetgrp", CFS_SETGRP_PERM },
584 int parse_perm(__u32 *perm, __u32 *noperm, char *str)
588 struct perm_type *pt;
596 memset(name, 0, sizeof(name));
597 end = strchr(start, ',');
599 end = str + strlen(str);
603 if (len >= sizeof(name))
605 strncpy(name, start, len);
607 for (pt = perm_types; pt->name; pt++) {
608 if (!strcasecmp(name, pt->name)) {
615 for (pt = noperm_types; pt->name; pt++) {
616 if (!strcasecmp(name, pt->name)) {
623 printf("unkown type: %s\n", name);
634 parse_perm_line(struct identity_downcall_data *data, char *line, size_t size)
643 if (data->idd_nperms >= N_PERMS_MAX) {
644 errlog("permission count %d > max %d\n",
645 data->idd_nperms, N_PERMS_MAX);
649 rc = sscanf(line, "%s %s %s", nid_str, uid_str, perm_str);
651 errlog("can't parse line %s\n", line);
655 if (!match_uid(data->idd_uid, uid_str))
658 if (!strcmp(nid_str, "*")) {
661 nid = libcfs_str2nid(nid_str);
662 if (nid == LNET_NID_ANY) {
663 errlog("can't parse nid %s\n", nid_str);
668 if (parse_perm(&perm, &noperm, perm_str)) {
669 errlog("invalid perm %s\n", perm_str);
674 * merge the perms with the same nid.
676 * If there is LNET_NID_ANY in data->idd_perms[i].pdd_nid,
677 * it must be data->idd_perms[0].pdd_nid, and act as default perm.
679 if (nid != LNET_NID_ANY) {
682 /* search for the same nid */
683 for (i = data->idd_nperms - 1; i >= 0; i--) {
684 if (data->idd_perms[i].pdd_nid == nid) {
685 data->idd_perms[i].pdd_perm =
686 (data->idd_perms[i].pdd_perm | perm) &
693 /* NOT found, add to tail */
695 data->idd_perms[data->idd_nperms].pdd_nid = nid;
696 data->idd_perms[data->idd_nperms].pdd_perm =
701 if (data->idd_nperms > 0) {
702 /* the first one isn't LNET_NID_ANY, need exchange */
703 if (data->idd_perms[0].pdd_nid != LNET_NID_ANY) {
704 data->idd_perms[data->idd_nperms].pdd_nid =
705 data->idd_perms[0].pdd_nid;
706 data->idd_perms[data->idd_nperms].pdd_perm =
707 data->idd_perms[0].pdd_perm;
708 data->idd_perms[0].pdd_nid = LNET_NID_ANY;
709 data->idd_perms[0].pdd_perm = perm & ~noperm;
712 /* only fix LNET_NID_ANY item */
713 data->idd_perms[0].pdd_perm =
714 (data->idd_perms[0].pdd_perm | perm) &
718 /* it is the first one, only add to head */
719 data->idd_perms[0].pdd_nid = LNET_NID_ANY;
720 data->idd_perms[0].pdd_perm = perm & ~noperm;
721 data->idd_nperms = 1;
728 static char *striml(char *s)
735 static void check_new_nss_module(struct nss_module *mod)
739 for (i = 0; i < g_n_nss_modules; i++) {
740 struct nss_module *pos = g_nss_modules + i;
742 if (!strcmp(mod->name, pos->name)) {
743 errlog("attempt to initialize \"%s\" module twice\n",
750 #define ONE_DAY (24 * 60 * 60)
752 static void do_warn_interval(struct timeval *now)
754 bool write_warning = false;
755 bool show_warning = false;
756 const char *perm_warning = PERM_PATHNAME "-warning";
759 "Use 'lookup lustre'. The 'files' alias is deprecated.\n";
761 if (stat(perm_warning, &sbuf)) {
763 write_warning = true;
765 show_warning = (now->tv_sec - sbuf.st_mtim.tv_sec) > ONE_DAY;
768 if (write_warning || show_warning) {
769 const struct timespec times[] = {
770 {.tv_sec = UTIME_NOW},
771 {.tv_sec = UTIME_NOW},
774 int oflags = O_RDWR | O_CREAT;
775 int fd = open(perm_warning, oflags, mode);
777 /* if we cannot rate-limit ... better to be quiet */
781 ssize_t written = write(fd, msg, sizeof(msg));
783 /* unlikely, but rate-limiting may be broken */
787 errlog("WARNING: %s", msg);
789 /* rate limiting is working */
795 /** initialize module to access local /etc/lustre/passwd,group files */
796 static int init_lustre_files_module(struct nss_module *mod,
797 struct timeval *start)
799 do_warn_interval(start);
800 return init_lustre_module(mod);
804 * Check and parse lookup db config line.
805 * File should start with 'lookup' followed by the modules
806 * to be loaded, for example:
808 * [/etc/lustre/perm.conf]
811 * Should search, in order, first found wins:
812 * lustre [/etc/lustre/passwd and /etc/lustre/group]
815 * Other common nss modules: nis sss db files
816 * Since historically 'files' has been used exclusively
817 * to mean 'lustre auth files' and disabled using local auth
818 * via libnss_files users must select 'nss_files' to explicitly
819 * enable libnss_files, which is an uncommon configuration.
821 static int lookup_db_line_nss(char *line, struct timeval *start)
827 if (strncmp(p, L_GETIDENTITY_LOOKUP_CMD,
828 sizeof(L_GETIDENTITY_LOOKUP_CMD) - 1))
831 tok = strtok(p, " \t");
832 if (tok == NULL || strcmp(tok, L_GETIDENTITY_LOOKUP_CMD))
835 while ((tok = strtok(NULL, " \t\n")) != NULL) {
836 struct nss_module *newmod = NULL;
838 if (g_n_nss_modules < NSS_MODULES_MAX_NR)
839 newmod = &g_nss_modules[g_n_nss_modules];
843 if (!strcmp(tok, "files"))
844 ret = init_lustre_files_module(newmod, start);
845 else if (!strcmp(tok, "lustre"))
846 ret = init_lustre_module(newmod);
847 else if (!strcmp(tok, "nss_files"))
848 ret = init_nss_lib_module(newmod, "files");
850 ret = init_nss_lib_module(newmod, tok);
854 check_new_nss_module(newmod);
861 int get_perms(struct identity_downcall_data *data, struct timeval *start)
867 fp = fopen(PERM_PATHNAME, "r");
871 errlog("open %s failed: %s\n",
872 PERM_PATHNAME, strerror(errno));
873 data->idd_err = errno;
877 while (fgets(line, sizeof(line), fp)) {
878 if (comment_line(line))
880 ret = lookup_db_line_nss(line, start); /* lookup parsed */
883 if (parse_perm_line(data, line, sizeof(line))) {
884 errlog("parse line %s failed!\n", line);
885 data->idd_err = EINVAL;
895 static void show_result(struct identity_downcall_data *data)
900 errlog("failed to get identity for uid %d: %s\n",
901 data->idd_uid, strerror(data->idd_err));
905 printf("uid=%d gid=%d", data->idd_uid, data->idd_gid);
906 for (i = 0; i < data->idd_ngroups; i++)
907 printf(",%u", data->idd_groups[i]);
909 printf("permissions:\n"
911 for (i = 0; i < data->idd_nperms; i++) {
912 struct perm_downcall_data *pdd;
914 pdd = &data->idd_perms[i];
916 printf(" %#jx\t0x%x\n", (uintmax_t)pdd->pdd_nid,
922 #define difftime(a, b) \
923 ((a).tv_sec - (b).tv_sec + \
924 ((a).tv_usec - (b).tv_usec) / 1000000.0)
926 int main(int argc, char **argv)
929 struct identity_downcall_data *data = NULL;
932 struct timeval start, idgot, fini;
933 int fd, rc = -EINVAL, size, maxgroups;
934 bool alreadyfailed = false;
936 progname = basename(argv[0]);
943 uid = strtoul(argv[2], &end, 0);
944 if (*end != '\0' || end == argv[2] || errno != 0) {
945 errlog("%s: invalid uid '%s'\n", progname, argv[2]);
948 gettimeofday(&start, NULL);
950 maxgroups = sysconf(_SC_NGROUPS_MAX);
951 if (maxgroups > NGROUPS_MAX)
952 maxgroups = NGROUPS_MAX;
953 if (maxgroups == -1) {
959 size = offsetof(struct identity_downcall_data, idd_groups[maxgroups]);
962 errlog("malloc identity downcall data(%d) failed!\n", size);
963 if (!alreadyfailed) {
964 alreadyfailed = true;
971 memset(data, 0, size);
972 data->idd_magic = IDENTITY_DOWNCALL_MAGIC;
977 /* read permission database and/or load nss modules
978 * rc is -1 only when file exists and is not readable or
979 * content has format / syntax errors
981 rc = get_perms(data, &start);
985 /* get groups for uid */
986 rc = get_groups_common(data, maxgroups);
990 size = offsetof(struct identity_downcall_data,
991 idd_groups[data->idd_ngroups]);
993 gettimeofday(&idgot, NULL);
995 if (strcmp(argv[1], "-d") == 0 || getenv("L_GETIDENTITY_TEST")) {
1001 rc = cfs_get_param_paths(&path, "mdt/%s/identity_info", argv[1]);
1007 fd = open(path.gl_pathv[0], O_WRONLY);
1009 errlog("can't open file '%s':%s\n", path.gl_pathv[0],
1015 rc = write(fd, data, size);
1016 gettimeofday(&fini, NULL);
1019 errlog("partial write ret %d: %s\n", rc, strerror(errno));
1020 if (!alreadyfailed) {
1021 alreadyfailed = true;
1022 cfs_free_param_data(&path);
1031 /* log if it takes more than 20 second to avoid rate limite */
1032 if (rc || difftime(fini, start) > 20)
1033 errlog("get identity for uid %lu start time %ld.%06ld got time %ld.%06ld end time %ld.%06ld: rc = %d\n",
1034 uid, start.tv_sec, start.tv_usec, idgot.tv_sec,
1035 idgot.tv_usec, fini.tv_sec, fini.tv_usec, rc);
1038 cfs_free_param_data(&path);