Whamcloud - gitweb
- make HEAD from b_post_cmd3
[fs/lustre-release.git] / lustre / utils / l_getidentity.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (C) 2004-2006 Cluster File Systems, Inc.
5  *
6  *   This file is part of Lustre, http://www.lustre.org.
7  *
8  *   Lustre is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   Lustre is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with Lustre; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22
23 #include <stdlib.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <pwd.h>
31 #include <grp.h>
32 #include <stdarg.h>
33 #include <stddef.h>
34 #include <libgen.h>
35 #include <syslog.h>
36
37 #include <liblustre.h>
38 #include <lustre/lustre_user.h>
39 #include <lustre/lustre_idl.h>
40 #include <libcfs/kp30.h>
41
42 #define SETXID_PATHNAME "/etc/lustre/setxid.conf"
43
44 /*
45  * setxid permission file format is like this:
46  * {nid} {uid} {perms}
47  *
48  * '*' nid means any nid
49  * '*' uid means any uid
50  * the valid values for perms are:
51  * setuid/setgid/setgrp         -- enable corresponding perm
52  * nosetuid/nosetgid/nosetgrp   -- disable corresponding perm
53  * they can be listed together, seperated by ',',
54  * when perm and noperm are in the same line (item), noperm is preferential,
55  * when they are in different lines (items), the latter is preferential,
56  * '*' nid is as default perm, and is not preferential.
57  */
58
59 static char *progname;
60
61 static void usage(void)
62 {
63         fprintf(stderr,
64                 "\nusage: %s {mdtname} {uid}\n"
65                 "Normally invoked as an upcall from Lustre, set via:\n"
66                 "  /proc/fs/lustre/mdt/{mdtname}/identity_upcall\n",
67                 progname);
68 }
69
70 static int compare_u32(const void *v1, const void *v2)
71 {
72         return (*(__u32 *)v1 - *(__u32 *)v2);
73 }
74
75 static void errlog(const char *fmt, ...)
76 {
77         va_list args;
78
79         openlog(progname, LOG_PERROR, LOG_AUTHPRIV);
80
81         va_start(args, fmt);
82         vsyslog(LOG_NOTICE, fmt, args);
83         fprintf(stderr, fmt, args);
84         va_end(args);
85
86         closelog();
87 }
88
89 int get_groups_local(struct identity_downcall_data *data)
90 {
91         int maxgroups;
92         gid_t *groups;
93         unsigned int ngroups = 0;
94         struct passwd *pw;
95         struct group *gr;
96         char *pw_name;
97         int namelen;
98         int i;
99
100         pw = getpwuid(data->idd_uid);
101         if (!pw) {
102                 errlog("no such user %u\n", data->idd_uid);
103                 data->idd_err = errno ? errno : EIDRM;
104                 return -1;
105         }
106         data->idd_gid = pw->pw_gid;
107
108         namelen = sysconf(_SC_LOGIN_NAME_MAX);
109         if (namelen < _POSIX_LOGIN_NAME_MAX)
110                 namelen = _POSIX_LOGIN_NAME_MAX;
111         pw_name = (char *)malloc(namelen);
112         if (!pw_name) {
113                 errlog("malloc error\n");
114                 data->idd_err = errno;
115                 return -1;
116         }
117         memset(pw_name, 0, namelen);
118         strncpy(pw_name, pw->pw_name, namelen - 1);
119
120         maxgroups = sysconf(_SC_NGROUPS_MAX);
121         if (maxgroups > NGROUPS_MAX)
122                 maxgroups = NGROUPS_MAX;
123         groups = data->idd_groups;
124
125         groups[ngroups++] = pw->pw_gid;
126         while ((gr = getgrent())) {
127                 if (gr->gr_gid == groups[0])
128                         continue;
129                 if (!gr->gr_mem)
130                         continue;
131                 for (i = 0; gr->gr_mem[i]; i++) {
132                         if (!strcmp(gr->gr_mem[i], pw_name)) {
133                                 groups[ngroups++] = gr->gr_gid;
134                                 break;
135                         }
136                 }
137                 if (ngroups == maxgroups)
138                         break;
139         }
140         endgrent();
141         qsort(groups, ngroups, sizeof(*groups), compare_u32);
142         data->idd_ngroups = ngroups;
143
144         free(pw_name);
145         return 0;
146 }
147
148 static inline int comment_line(char *line)
149 {
150         char *p = line;
151
152         while (*p && (*p == ' ' || *p == '\t')) p++;
153
154         if (!*p || *p == '\n' || *p == '#')
155                 return 1;
156         return 0;
157 }
158
159 static inline int match_uid(uid_t uid, const char *str)
160 {
161         char *end;
162         uid_t uid2;
163
164         if(!strcmp(str, "*"))
165                 return -1;
166
167         uid2 = strtoul(str, &end, 0);
168         if (*end)
169                 return 0;
170
171         return (uid == uid2);
172 }
173
174 typedef struct {
175         char   *name;
176         __u32   bit;
177 } setxid_perm_type_t;
178
179 static setxid_perm_type_t setxid_perm_types[] = {
180         { "setuid", LUSTRE_SETUID_PERM },
181         { "setgid", LUSTRE_SETGID_PERM },
182         { "setgrp", LUSTRE_SETGRP_PERM },
183         { 0 }
184 };
185
186 static setxid_perm_type_t setxid_noperm_types[] = {
187         { "nosetuid", LUSTRE_SETUID_PERM },
188         { "nosetgid", LUSTRE_SETGID_PERM },
189         { "nosetgrp", LUSTRE_SETGRP_PERM },
190         { 0 }
191 };
192
193 int parse_setxid_perm(__u32 *perm, __u32 *noperm, char *str)
194 {
195         char *start, *end;
196         char name[64];
197         setxid_perm_type_t *pt;
198
199         *perm = 0;
200         *noperm = 0;
201         start = str;
202         while (1) {
203                 memset(name, 0, sizeof(name));
204                 end = strchr(start, ',');
205                 if (!end)
206                         end = str + strlen(str);
207                 if (start >= end)
208                         break;
209                 strncpy(name, start, end - start);
210                 for (pt = setxid_perm_types; pt->name; pt++) {
211                         if (!strcasecmp(name, pt->name)) {
212                                 *perm |= pt->bit;
213                                 break;
214                         }
215                 }
216
217                 if (!pt->name) {
218                         for (pt = setxid_noperm_types; pt->name; pt++) {
219                                 if (!strcasecmp(name, pt->name)) {
220                                         *noperm |= pt->bit;
221                                         break;
222                                 }
223                         }
224
225                         if (!pt->name) {
226                                 printf("unkown type: %s\n", name);
227                                 return -1;
228                         }
229                 }
230
231                 start = end + 1;
232         }
233         return 0;
234 }
235
236 int parse_setxid_perm_line(struct identity_downcall_data *data, char *line)
237 {
238         char uid_str[256], nid_str[256], perm_str[256];
239         lnet_nid_t nid;
240         __u32 perm, noperm;
241         int rc, i;
242
243         if (data->idd_nperms >= N_SETXID_PERMS_MAX) {
244                 errlog("setxid permission count %d > max %d\n",
245                         data->idd_nperms, N_SETXID_PERMS_MAX);
246                 return -1;
247         }
248
249         rc = sscanf(line, "%s %s %s", nid_str, uid_str, perm_str);
250         if (rc != 3) {
251                 errlog("can't parse line %s\n", line);
252                 return -1;
253         }
254
255         if (!match_uid(data->idd_uid, uid_str))
256                 return 0;
257
258         if (!strcmp(nid_str, "*")) {
259                 nid = LNET_NID_ANY;
260         } else {
261                 nid = libcfs_str2nid(nid_str);
262                 if (nid == LNET_NID_ANY) {
263                         errlog("can't parse nid %s\n", nid_str);
264                         return -1;
265                 }
266         }
267
268         if (parse_setxid_perm(&perm, &noperm, perm_str)) {
269                 errlog("invalid perm %s\n", perm_str);
270                 return -1;
271         }
272
273         /* merge the perms with the same nid.
274          *
275          * If there is LNET_NID_ANY in data->idd_perms[i].pdd_nid,
276          * it must be data->idd_perms[0].pdd_nid, and act as default perm.
277          */
278         if (nid != LNET_NID_ANY) {
279                 int found = 0;
280
281                 /* search for the same nid */
282                 for (i = data->idd_nperms - 1; i >= 0; i--) {
283                         if (data->idd_perms[i].pdd_nid == nid) {
284                                 data->idd_perms[i].pdd_perm =
285                                         (data->idd_perms[i].pdd_perm | perm) &
286                                         ~noperm;
287                                 found = 1;
288                                 break;
289                         }
290                 }
291
292                 /* NOT found, add to tail */
293                 if (!found) {
294                         data->idd_perms[data->idd_nperms].pdd_nid = nid;
295                         data->idd_perms[data->idd_nperms].pdd_perm =
296                                 perm & ~noperm;
297                         data->idd_nperms++;
298                 }
299         } else {
300                 if (data->idd_nperms > 0) {
301                         /* the first one isn't LNET_NID_ANY, need exchange */
302                         if (data->idd_perms[0].pdd_nid != LNET_NID_ANY) {
303                                 data->idd_perms[data->idd_nperms].pdd_nid =
304                                         data->idd_perms[0].pdd_nid;
305                                 data->idd_perms[data->idd_nperms].pdd_perm =
306                                         data->idd_perms[0].pdd_perm;
307                                 data->idd_perms[0].pdd_nid = LNET_NID_ANY;
308                                 data->idd_perms[0].pdd_perm = perm & ~noperm;
309                                 data->idd_nperms++;
310                         } else {
311                                 /* only fix LNET_NID_ANY item */
312                                 data->idd_perms[0].pdd_perm =
313                                         (data->idd_perms[0].pdd_perm | perm) &
314                                         ~noperm;
315                         }
316                 } else {
317                         /* it is the first one, only add to head */
318                         data->idd_perms[0].pdd_nid = LNET_NID_ANY;
319                         data->idd_perms[0].pdd_perm = perm & ~noperm;
320                         data->idd_nperms = 1;
321                 }
322         }
323
324         return 0;
325 }
326
327 int get_setxid_perms(FILE *fp, struct identity_downcall_data *data)
328 {
329         char line[1024];
330
331         while (fgets(line, 1024, fp)) {
332                 if (comment_line(line))
333                         continue;
334
335                 if (parse_setxid_perm_line(data, line)) {
336                         errlog("parse line %s failed!\n", line);
337                         return -1;
338                 }
339         }
340
341         return 0;
342 }
343
344 static void show_result(struct identity_downcall_data *data)
345 {
346         int i;
347
348         if (data->idd_err) {
349                 errlog("failed to get identity for uid %d: %s\n",
350                        data->idd_uid, strerror(data->idd_err));
351                 return;
352         }
353
354         printf("uid=%d gid=", data->idd_uid);
355         for (i = 0; i < data->idd_ngroups; i++)
356                 printf("%s%u", i > 0 ? "," : "", data->idd_groups[i]);
357         printf("\n");
358         printf("setxid permissions:\n"
359                "  nid\t\t\tperm\n");
360         for (i = 0; i < data->idd_nperms; i++) {
361                 struct setxid_perm_downcall_data *pdd;
362
363                 pdd = &data->idd_perms[i];
364
365                 printf("  %#llx\t0x%x\n", pdd->pdd_nid, pdd->pdd_perm);
366         }
367         printf("\n");
368 }
369
370 int main(int argc, char **argv)
371 {
372         FILE *perms_fp;
373         char *end;
374         struct identity_downcall_data *data;
375         char procname[1024];
376         unsigned long uid;
377         int fd, rc;
378
379         progname = basename(argv[0]);
380
381         if (argc != 3) {
382                 usage();
383                 return 1;
384         }
385
386         uid = strtoul(argv[2], &end, 0);
387         if (*end) {
388                 errlog("%s: invalid uid '%s'\n", progname, argv[2]);
389                 usage();
390                 return 1;
391         }
392
393         data = malloc(sizeof(*data));
394         if (!data) {
395                 errlog("malloc identity downcall data(%d) failed!\n",
396                        sizeof(*data));
397                 return 1;
398         }
399         memset(data, 0, sizeof(*data));
400         data->idd_magic = IDENTITY_DOWNCALL_MAGIC;
401         data->idd_uid = uid;
402
403         /* get groups for uid */
404         rc = get_groups_local(data);
405         if (rc)
406                 goto downcall;
407
408         /* read permission database */
409         perms_fp = fopen(SETXID_PATHNAME, "r");
410         if (perms_fp) {
411                 get_setxid_perms(perms_fp, data);
412                 fclose(perms_fp);
413         } else if (errno != ENOENT) {
414                 errlog("open %s failed: %s\n",
415                        SETXID_PATHNAME, strerror(errno));
416         }
417
418 downcall:
419         if (getenv("L_GETIDENTITY_TEST")) {
420                 show_result(data);
421                 return 0;
422         }
423
424         snprintf(procname, sizeof(procname),
425                  "/proc/fs/lustre/mdt/%s/identity_info", argv[1]);
426         fd = open(procname, O_WRONLY);
427         if (fd < 0) {
428                 errlog("can't open file %s: %s\n", procname, strerror(errno));
429                 return 1;
430         }
431
432         rc = write(fd, data, sizeof(*data));
433         close(fd);
434         if (rc != sizeof(*data)) {
435                 errlog("partial write ret %d: %s\n", rc, strerror(errno));
436                 return 1;
437         }
438
439         return 0;
440 }