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/
52 #include <libcfs/util/param.h>
53 #include <linux/lnet/nidstr.h>
54 #include <linux/lustre/lustre_user.h>
55 #include <linux/lustre/lustre_idl.h>
57 #define PERM_PATHNAME "/etc/lustre/perm.conf"
58 #define LUSTRE_PASSWD "/etc/lustre/passwd"
59 #define LUSTRE_GROUP "/etc/lustre/group"
61 #define L_GETIDENTITY_LOOKUP_CMD "lookup"
62 #define NSS_MODULES_MAX_NR 8
63 #define NSS_MODULE_NAME_SIZE 32
64 #define NSS_SYMBOL_NAME_LEN_MAX 256
66 static int nss_pw_buf_len;
67 static void *nss_pw_buf;
68 static int nss_grent_buf_len;
69 static void *nss_grent_buf;
70 static int g_n_nss_modules;
71 static int grent_mod_no = -1;
74 char name[NSS_MODULE_NAME_SIZE];
75 int (*getpwuid)(struct nss_module *mod, uid_t, struct passwd *pwd);
76 int (*getgrent)(struct nss_module *mod, struct group *result);
77 void (*endgrent)(struct nss_module *mod);
78 void (*fini)(struct nss_module *mod);
83 int (*l_getpwuid)(uid_t, struct passwd *pwd,
84 char *buffer, size_t buflen,
86 int (*l_getgrent)(struct group *result, char *buffer,
87 size_t buflen, int *errnop);
88 int (*l_endgrent)(void);
97 static struct nss_module g_nss_modules[NSS_MODULES_MAX_NR];
99 #define NSS_LIB_NAME_PATTERN "libnss_%s.so.2"
102 * permission file format is like this:
103 * {nid} {uid} {perms}
105 * '*' nid means any nid
106 * '*' uid means any uid
107 * the valid values for perms are:
108 * setuid/setgid/setgrp -- enable corresponding perm
109 * nosetuid/nosetgid/nosetgrp -- disable corresponding perm
110 * they can be listed together, separated by ',',
111 * when perm and noperm are in the same line (item), noperm is preferential,
112 * when they are in different lines (items), the latter is preferential,
113 * '*' nid is as default perm, and is not preferential.
116 static char *progname;
118 static void usage(void)
121 "\nusage: %s {-d|mdtname} {uid}\n"
122 "Normally invoked as an upcall from Lustre, set via:\n"
123 "lctl set_param mdt.${mdtname}.identity_upcall={path to upcall}\n"
124 "\t-d: debug, print values to stdout instead of Lustre\n"
125 "\tNSS support enabled\n",
129 static void errlog(const char *fmt, ...)
133 openlog(progname, LOG_PERROR | LOG_PID, LOG_AUTHPRIV);
136 vsyslog(LOG_WARNING, fmt, args);
142 static int compare_gids(const void *v1, const void *v2)
144 return (*(gid_t *)v1 - *(gid_t *)v2);
147 /** getpwuid() replacement */
148 static struct passwd *getpwuid_nss(uid_t uid)
150 static struct passwd pw;
153 for (i = 0; i < g_n_nss_modules; i++) {
154 struct nss_module *mod = g_nss_modules + i;
156 if (mod->getpwuid(mod, uid, &pw) == 0)
163 * getgrent() replacement.
164 * simulate getgrent(3) across nss modules
166 static struct group *getgrent_nss(void)
168 static struct group grp;
170 if (grent_mod_no < 0)
173 while (grent_mod_no < g_n_nss_modules) {
174 struct nss_module *mod = g_nss_modules + grent_mod_no;
176 if (mod->getgrent(mod, &grp) == 0)
184 /** endgrent() replacement */
185 static void endgrent_nss(void)
187 if (grent_mod_no < g_n_nss_modules
188 && grent_mod_no >= 0) {
189 struct nss_module *mod = g_nss_modules+grent_mod_no;
196 /** lookup symbol in dynamically loaded nss module */
197 static void *get_nss_sym(struct nss_module *mod, const char *op)
201 char symbuf[NSS_SYMBOL_NAME_LEN_MAX];
203 bytes = snprintf(symbuf, NSS_SYMBOL_NAME_LEN_MAX - 1, "_nss_%s_%s",
205 if (bytes >= NSS_SYMBOL_NAME_LEN_MAX - 1) {
206 errlog("symbol name too long\n");
209 res = dlsym(mod->u.lib.l_ptr, symbuf);
211 errlog("cannot find symbol %s in nss module \"%s\": %s\n",
212 symbuf, mod->name, dlerror());
216 /** allocate bigger buffer */
217 static void enlarge_nss_buffer(void **buf, int *bufsize)
220 *bufsize = *bufsize * 2;
221 *buf = malloc(*bufsize);
223 errlog("no memory to allocate bigger buffer of %d bytes\n",
229 static int getpwuid_nss_lib(struct nss_module *nss, uid_t uid,
235 err = nss->u.lib.l_getpwuid(uid, pw, nss_pw_buf,
236 nss_pw_buf_len, &tmp_errno);
237 if (err == NSS_STATUS_TRYAGAIN) {
238 if (tmp_errno == ERANGE) {
239 /* buffer too small */
240 enlarge_nss_buffer(&nss_pw_buf,
247 if (err == NSS_STATUS_SUCCESS)
252 static int getgrent_nss_lib(struct nss_module *nss, struct group *gr)
257 err = nss->u.lib.l_getgrent(gr, nss_grent_buf,
258 nss_grent_buf_len, &tmp_errno);
259 if (err == NSS_STATUS_TRYAGAIN) {
260 if (tmp_errno == ERANGE) {
261 /* buffer too small */
262 enlarge_nss_buffer(&nss_grent_buf,
269 if (err == NSS_STATUS_SUCCESS)
274 static void endgrent_nss_lib(struct nss_module *mod)
276 mod->u.lib.l_endgrent();
279 /** destroy a "shared lib" nss module */
280 static void fini_nss_lib_module(struct nss_module *mod)
282 if (mod->u.lib.l_ptr)
283 dlclose(mod->u.lib.l_ptr);
286 /** load and initialize a "shared lib" nss module */
287 static int init_nss_lib_module(struct nss_module *mod, char *name)
289 char lib_file_name[sizeof(NSS_LIB_NAME_PATTERN) + sizeof(mod->name)];
291 if (strlen(name) >= sizeof(mod->name)) {
292 errlog("module name (%s) too long\n", name);
296 strncpy(mod->name, name, sizeof(mod->name));
297 mod->name[sizeof(mod->name) - 1] = '\0';
299 snprintf(lib_file_name, sizeof(lib_file_name), NSS_LIB_NAME_PATTERN,
302 mod->getpwuid = getpwuid_nss_lib;
303 mod->getgrent = getgrent_nss_lib;
304 mod->endgrent = endgrent_nss_lib;
305 mod->fini = fini_nss_lib_module;
307 mod->u.lib.l_ptr = dlopen(lib_file_name, RTLD_NOW);
308 if (mod->u.lib.l_ptr == NULL) {
309 errlog("dl error %s\n", dlerror());
312 mod->u.lib.l_getpwuid = get_nss_sym(mod, "getpwuid_r");
313 if (mod->getpwuid == NULL)
316 mod->u.lib.l_getgrent = get_nss_sym(mod, "getgrent_r");
317 if (mod->getgrent == NULL)
320 mod->u.lib.l_endgrent = get_nss_sym(mod, "endgrent");
321 if (mod->endgrent == NULL)
327 static void fini_lustre_nss_module(struct nss_module *mod)
329 if (mod->u.files.f_passwd)
330 fclose(mod->u.files.f_passwd);
331 if (mod->u.files.f_group)
332 fclose(mod->u.files.f_group);
335 static int getpwuid_lustre_nss(struct nss_module *mod, uid_t uid,
340 while ((pos = fgetpwent(mod->u.files.f_passwd)) != NULL) {
341 if (pos->pw_uid == uid) {
349 static int getgrent_lustre_nss(struct nss_module *mod, struct group *gr)
353 pos = fgetgrent(mod->u.files.f_group);
361 static void endgrent_lustre_nss(struct nss_module *mod)
365 /** initialize module to access local /etc/lustre/passwd,group files */
366 static int init_lustre_module(struct nss_module *mod)
368 mod->fini = fini_lustre_nss_module;
369 mod->getpwuid = getpwuid_lustre_nss;
370 mod->getgrent = getgrent_lustre_nss;
371 mod->endgrent = endgrent_lustre_nss;
373 mod->u.files.f_passwd = fopen(LUSTRE_PASSWD, "r");
374 if (mod->u.files.f_passwd == NULL)
377 mod->u.files.f_group = fopen(LUSTRE_GROUP, "r");
378 if (mod->u.files.f_group == NULL)
381 snprintf(mod->name, sizeof(mod->name), "lustre");
385 /** load and initialize the "nss" system */
386 static void init_nss(void)
388 nss_pw_buf_len = sysconf(_SC_GETPW_R_SIZE_MAX);
389 if (nss_pw_buf_len == -1) {
393 nss_pw_buf = malloc(nss_pw_buf_len);
394 if (nss_pw_buf == NULL) {
395 perror("pw buffer allocation");
399 nss_grent_buf_len = sysconf(_SC_GETGR_R_SIZE_MAX);
400 if (nss_grent_buf_len == -1) {
404 nss_grent_buf = malloc(nss_grent_buf_len);
405 if (nss_grent_buf == NULL) {
406 perror("grent buffer allocation");
412 static void fini_nss(void)
416 for (i = 0; i < g_n_nss_modules; i++) {
417 struct nss_module *mod = g_nss_modules + i;
426 /** get supplementary group info and fill downcall data */
427 static int get_groups_nss(struct identity_downcall_data *data,
428 unsigned int maxgroups)
433 unsigned int ngroups = 0;
437 pw = getpwuid_nss(data->idd_uid);
439 data->idd_err = errno ? errno : EIDRM;
440 errlog("no such user %u\n", data->idd_uid);
444 data->idd_gid = pw->pw_gid;
445 pw_name = strdup(pw->pw_name);
446 groups = data->idd_groups;
448 while ((gr = getgrent_nss()) != NULL && ngroups < maxgroups) {
449 if (gr->gr_gid == pw->pw_gid)
453 for (i = 0; gr->gr_mem[i]; i++) {
454 if (!strcmp(gr->gr_mem[i], pw_name)) {
455 groups[ngroups++] = gr->gr_gid;
464 qsort(groups, ngroups, sizeof(*groups), compare_gids);
465 data->idd_ngroups = ngroups;
471 int get_groups_local(struct identity_downcall_data *data,
472 unsigned int maxgroups)
474 gid_t *groups, *groups_tmp = NULL;
475 unsigned int ngroups = 0;
480 pw = getpwuid(data->idd_uid);
482 errlog("no such user %u\n", data->idd_uid);
483 data->idd_err = errno ? errno : EIDRM;
487 data->idd_gid = pw->pw_gid;
489 groups = data->idd_groups;
492 * Allocate array of size maxgroups instead of handling two
493 * consecutive and potentially racy getgrouplist() calls.
495 groups_tmp = malloc(maxgroups * sizeof(gid_t));
497 data->idd_err = errno ? errno : ENOMEM;
498 errlog("malloc error=%u\n", data->idd_err);
502 ngroups_tmp = maxgroups;
503 if (getgrouplist(pw->pw_name, pw->pw_gid, groups_tmp, &ngroups_tmp) <
506 data->idd_err = errno ? errno : EIDRM;
507 errlog("getgrouplist() error for uid %u: error=%u\n",
508 data->idd_uid, data->idd_err);
512 /* Do not place user's group ID in to the resulting groups list */
513 for (i = 0; i < ngroups_tmp; i++)
514 if (pw->pw_gid != groups_tmp[i])
515 groups[ngroups++] = groups_tmp[i];
518 qsort(groups, ngroups, sizeof(*groups), compare_gids);
519 data->idd_ngroups = ngroups;
525 int get_groups_common(struct identity_downcall_data *data,
526 unsigned int maxgroups)
529 return get_groups_nss(data, maxgroups);
530 return get_groups_local(data, maxgroups);
533 static inline int comment_line(char *line)
537 while (*p && (*p == ' ' || *p == '\t'))
540 if (!*p || *p == '\n' || *p == '#')
545 static inline int match_uid(uid_t uid, const char *str)
550 if (!strcmp(str, "*"))
553 uid2 = strtoul(str, &end, 0);
556 return (uid == uid2);
564 static struct perm_type perm_types[] = {
565 { "setuid", CFS_SETUID_PERM },
566 { "setgid", CFS_SETGID_PERM },
567 { "setgrp", CFS_SETGRP_PERM },
573 static struct perm_type noperm_types[] = {
574 { "nosetuid", CFS_SETUID_PERM },
575 { "nosetgid", CFS_SETGID_PERM },
576 { "nosetgrp", CFS_SETGRP_PERM },
582 int parse_perm(__u32 *perm, __u32 *noperm, char *str)
586 struct perm_type *pt;
594 memset(name, 0, sizeof(name));
595 end = strchr(start, ',');
597 end = str + strlen(str);
601 if (len >= sizeof(name))
603 strncpy(name, start, len);
605 for (pt = perm_types; pt->name; pt++) {
606 if (!strcasecmp(name, pt->name)) {
613 for (pt = noperm_types; pt->name; pt++) {
614 if (!strcasecmp(name, pt->name)) {
621 printf("unkown type: %s\n", name);
632 parse_perm_line(struct identity_downcall_data *data, char *line, size_t size)
641 if (data->idd_nperms >= N_PERMS_MAX) {
642 errlog("permission count %d > max %d\n",
643 data->idd_nperms, N_PERMS_MAX);
647 rc = sscanf(line, "%s %s %s", nid_str, uid_str, perm_str);
649 errlog("can't parse line %s\n", line);
653 if (!match_uid(data->idd_uid, uid_str))
656 if (!strcmp(nid_str, "*")) {
659 nid = libcfs_str2nid(nid_str);
660 if (nid == LNET_NID_ANY) {
661 errlog("can't parse nid %s\n", nid_str);
666 if (parse_perm(&perm, &noperm, perm_str)) {
667 errlog("invalid perm %s\n", perm_str);
672 * merge the perms with the same nid.
674 * If there is LNET_NID_ANY in data->idd_perms[i].pdd_nid,
675 * it must be data->idd_perms[0].pdd_nid, and act as default perm.
677 if (nid != LNET_NID_ANY) {
680 /* search for the same nid */
681 for (i = data->idd_nperms - 1; i >= 0; i--) {
682 if (data->idd_perms[i].pdd_nid == nid) {
683 data->idd_perms[i].pdd_perm =
684 (data->idd_perms[i].pdd_perm | perm) &
691 /* NOT found, add to tail */
693 data->idd_perms[data->idd_nperms].pdd_nid = nid;
694 data->idd_perms[data->idd_nperms].pdd_perm =
699 if (data->idd_nperms > 0) {
700 /* the first one isn't LNET_NID_ANY, need exchange */
701 if (data->idd_perms[0].pdd_nid != LNET_NID_ANY) {
702 data->idd_perms[data->idd_nperms].pdd_nid =
703 data->idd_perms[0].pdd_nid;
704 data->idd_perms[data->idd_nperms].pdd_perm =
705 data->idd_perms[0].pdd_perm;
706 data->idd_perms[0].pdd_nid = LNET_NID_ANY;
707 data->idd_perms[0].pdd_perm = perm & ~noperm;
710 /* only fix LNET_NID_ANY item */
711 data->idd_perms[0].pdd_perm =
712 (data->idd_perms[0].pdd_perm | perm) &
716 /* it is the first one, only add to head */
717 data->idd_perms[0].pdd_nid = LNET_NID_ANY;
718 data->idd_perms[0].pdd_perm = perm & ~noperm;
719 data->idd_nperms = 1;
726 static char *striml(char *s)
733 static void check_new_nss_module(struct nss_module *mod)
737 for (i = 0; i < g_n_nss_modules; i++) {
738 struct nss_module *pos = g_nss_modules + i;
740 if (!strcmp(mod->name, pos->name)) {
741 errlog("attempt to initialize \"%s\" module twice\n",
749 * Check and parse lookup db config line.
750 * File should start with 'lookup' followed by the modules
751 * to be loaded, for example:
753 * [/etc/lustre/perm.conf]
756 * Should search, in order, first found wins:
757 * lustre [/etc/lustre/passwd and /etc/lustre/group]
760 * Other common nss modules: nis sss db files
761 * Since historically 'files' has been used exclusively
762 * to mean 'lustre auth files' and disabled using local auth
763 * via libnss_files users must select 'nss_files' to explicitly
764 * enable libnss_files, which is an uncommon configuration.
766 static int lookup_db_line_nss(char *line)
772 if (strncmp(p, L_GETIDENTITY_LOOKUP_CMD,
773 sizeof(L_GETIDENTITY_LOOKUP_CMD) - 1))
776 tok = strtok(p, " \t");
777 if (tok == NULL || strcmp(tok, L_GETIDENTITY_LOOKUP_CMD))
780 while ((tok = strtok(NULL, " \t\n")) != NULL) {
781 struct nss_module *newmod = g_nss_modules + g_n_nss_modules;
783 if (g_n_nss_modules >= NSS_MODULES_MAX_NR)
786 if (!strcmp(tok, "files") || !strcmp(tok, "lustre"))
787 ret = init_lustre_module(newmod);
788 else if (!strcmp(tok, "nss_files"))
789 ret = init_nss_lib_module(newmod, "files");
791 ret = init_nss_lib_module(newmod, tok);
795 check_new_nss_module(newmod);
802 int get_perms(struct identity_downcall_data *data)
808 fp = fopen(PERM_PATHNAME, "r");
812 errlog("open %s failed: %s\n",
813 PERM_PATHNAME, strerror(errno));
814 data->idd_err = errno;
818 while (fgets(line, sizeof(line), fp)) {
819 if (comment_line(line))
821 ret = lookup_db_line_nss(line); /* lookup parsed */
824 if (parse_perm_line(data, line, sizeof(line))) {
825 errlog("parse line %s failed!\n", line);
826 data->idd_err = EINVAL;
836 static void show_result(struct identity_downcall_data *data)
841 errlog("failed to get identity for uid %d: %s\n",
842 data->idd_uid, strerror(data->idd_err));
846 printf("uid=%d gid=%d", data->idd_uid, data->idd_gid);
847 for (i = 0; i < data->idd_ngroups; i++)
848 printf(",%u", data->idd_groups[i]);
850 printf("permissions:\n"
852 for (i = 0; i < data->idd_nperms; i++) {
853 struct perm_downcall_data *pdd;
855 pdd = &data->idd_perms[i];
857 printf(" %#jx\t0x%x\n", (uintmax_t)pdd->pdd_nid,
863 #define difftime(a, b) \
864 ((a).tv_sec - (b).tv_sec + \
865 ((a).tv_usec - (b).tv_usec) / 1000000.0)
867 int main(int argc, char **argv)
870 struct identity_downcall_data *data = NULL;
873 struct timeval start, idgot, fini;
874 int fd, rc = -EINVAL, size, maxgroups;
875 bool alreadyfailed = false;
877 progname = basename(argv[0]);
884 uid = strtoul(argv[2], &end, 0);
885 if (*end != '\0' || end == argv[2] || errno != 0) {
886 errlog("%s: invalid uid '%s'\n", progname, argv[2]);
889 gettimeofday(&start, NULL);
891 maxgroups = sysconf(_SC_NGROUPS_MAX);
892 if (maxgroups > NGROUPS_MAX)
893 maxgroups = NGROUPS_MAX;
894 if (maxgroups == -1) {
900 size = offsetof(struct identity_downcall_data, idd_groups[maxgroups]);
903 errlog("malloc identity downcall data(%d) failed!\n", size);
904 if (!alreadyfailed) {
905 alreadyfailed = true;
912 memset(data, 0, size);
913 data->idd_magic = IDENTITY_DOWNCALL_MAGIC;
918 /* read permission database and/or load nss modules
919 * rc is -1 only when file exists and is not readable or
920 * content has format / syntax errors
922 rc = get_perms(data);
926 /* get groups for uid */
927 rc = get_groups_common(data, maxgroups);
931 size = offsetof(struct identity_downcall_data,
932 idd_groups[data->idd_ngroups]);
934 gettimeofday(&idgot, NULL);
936 if (strcmp(argv[1], "-d") == 0 || getenv("L_GETIDENTITY_TEST")) {
942 rc = cfs_get_param_paths(&path, "mdt/%s/identity_info", argv[1]);
948 fd = open(path.gl_pathv[0], O_WRONLY);
950 errlog("can't open file '%s':%s\n", path.gl_pathv[0],
956 rc = write(fd, data, size);
957 gettimeofday(&fini, NULL);
960 errlog("partial write ret %d: %s\n", rc, strerror(errno));
961 if (!alreadyfailed) {
962 alreadyfailed = true;
963 cfs_free_param_data(&path);
972 /* log if it takes more than 20 second to avoid rate limite */
973 if (rc || difftime(fini, start) > 20)
974 errlog("get identity for uid %lu start time %ld.%06ld got time %ld.%06ld end time %ld.%06ld: rc = %d\n",
975 uid, start.tv_sec, start.tv_usec, idgot.tv_sec,
976 idgot.tv_usec, fini.tv_sec, fini.tv_usec, rc);
979 cfs_free_param_data(&path);