Whamcloud - gitweb
LU-8648 all: remove all Sun license and URL references
[fs/lustre-release.git] / lustre / utils / l_getidentity.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
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.
9  *
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).
15  *
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
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2011, 2014, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  */
32
33 #include <stdbool.h>
34 #include <stdlib.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <fcntl.h>
41 #include <pwd.h>
42 #include <grp.h>
43 #include <stdarg.h>
44 #include <stddef.h>
45 #include <libgen.h>
46 #include <syslog.h>
47
48 #include <libcfs/util/param.h>
49 #include <libcfs/util/string.h>
50 #include <lnet/nidstr.h>
51 #include <lustre/lustre_user.h>
52 #include <lustre/lustre_idl.h>
53
54 #define PERM_PATHNAME "/etc/lustre/perm.conf"
55
56 /*
57  * permission file format is like this:
58  * {nid} {uid} {perms}
59  *
60  * '*' nid means any nid
61  * '*' uid means any uid
62  * the valid values for perms are:
63  * setuid/setgid/setgrp/rmtacl           -- enable corresponding perm
64  * nosetuid/nosetgid/nosetgrp/normtacl   -- disable corresponding perm
65  * they can be listed together, separated by ',',
66  * when perm and noperm are in the same line (item), noperm is preferential,
67  * when they are in different lines (items), the latter is preferential,
68  * '*' nid is as default perm, and is not preferential.
69  */
70
71 static char *progname;
72
73 static void usage(void)
74 {
75         fprintf(stderr,
76                 "\nusage: %s {mdtname} {uid}\n"
77                 "Normally invoked as an upcall from Lustre, set via:\n"
78                 "lctl set_param mdt.${mdtname}.identity_upcall={path to upcall}\n",
79                 progname);
80 }
81
82 static int compare_u32(const void *v1, const void *v2)
83 {
84         return (*(__u32 *)v1 - *(__u32 *)v2);
85 }
86
87 static void errlog(const char *fmt, ...)
88 {
89         va_list args;
90
91         openlog(progname, LOG_PERROR | LOG_PID, LOG_AUTHPRIV);
92
93         va_start(args, fmt);
94         vsyslog(LOG_NOTICE, fmt, args);
95         va_end(args);
96
97         closelog();
98 }
99
100 int get_groups_local(struct identity_downcall_data *data,
101                      unsigned int maxgroups)
102 {
103         gid_t *groups, *groups_tmp = NULL;
104         unsigned int ngroups = 0;
105         int ngroups_tmp;
106         struct passwd *pw;
107         char *pw_name;
108         int namelen;
109         int i;
110
111         pw = getpwuid(data->idd_uid);
112         if (!pw) {
113                 errlog("no such user %u\n", data->idd_uid);
114                 data->idd_err = errno ? errno : EIDRM;
115                 return -1;
116         }
117
118         data->idd_gid = pw->pw_gid;
119         namelen = sysconf(_SC_LOGIN_NAME_MAX);
120         if (namelen < _POSIX_LOGIN_NAME_MAX)
121                 namelen = _POSIX_LOGIN_NAME_MAX;
122
123         pw_name = malloc(namelen);
124         if (!pw_name) {
125                 errlog("malloc error\n");
126                 data->idd_err = errno;
127                 return -1;
128         }
129
130         strlcpy(pw_name, pw->pw_name, namelen);
131         groups = data->idd_groups;
132
133         /* Allocate array of size maxgroups instead of handling two
134          * consecutive and potentially racy getgrouplist() calls. */
135         groups_tmp = malloc(maxgroups * sizeof(gid_t));
136         if (groups_tmp == NULL) {
137                 free(pw_name);
138                 data->idd_err = errno ? errno : ENOMEM;
139                 errlog("malloc error=%u\n",data->idd_err);
140                 return -1;
141         }
142
143         ngroups_tmp = maxgroups;
144         if (getgrouplist(pw->pw_name, pw->pw_gid, groups_tmp, &ngroups_tmp) <
145             0) {
146                 free(pw_name);
147                 free(groups_tmp);
148                 data->idd_err = errno ? errno : EIDRM;
149                 errlog("getgrouplist() error for uid %u: error=%u\n",
150                         data->idd_uid, data->idd_err);
151                 return -1;
152         }
153
154         /* Do not place user's group ID in to the resulting groups list */
155         for (i = 0; i < ngroups_tmp; i++)
156                 if (pw->pw_gid != groups_tmp[i])
157                         groups[ngroups++] = groups_tmp[i];
158
159         if (ngroups > 0)
160                 qsort(groups, ngroups, sizeof(*groups), compare_u32);
161         data->idd_ngroups = ngroups;
162
163         free(pw_name);
164         free(groups_tmp);
165         return 0;
166 }
167
168 static inline int comment_line(char *line)
169 {
170         char *p = line;
171
172         while (*p && (*p == ' ' || *p == '\t')) p++;
173
174         if (!*p || *p == '\n' || *p == '#')
175                 return 1;
176         return 0;
177 }
178
179 static inline int match_uid(uid_t uid, const char *str)
180 {
181         char *end;
182         uid_t uid2;
183
184         if(!strcmp(str, "*"))
185                 return -1;
186
187         uid2 = strtoul(str, &end, 0);
188         if (*end)
189                 return 0;
190         return (uid == uid2);
191 }
192
193 typedef struct {
194         char   *name;
195         __u32   bit;
196 } perm_type_t;
197
198 static perm_type_t perm_types[] = {
199         { "setuid", CFS_SETUID_PERM },
200         { "setgid", CFS_SETGID_PERM },
201         { "setgrp", CFS_SETGRP_PERM },
202         { 0 }
203 };
204
205 static perm_type_t noperm_types[] = {
206         { "nosetuid", CFS_SETUID_PERM },
207         { "nosetgid", CFS_SETGID_PERM },
208         { "nosetgrp", CFS_SETGRP_PERM },
209         { 0 }
210 };
211
212 int parse_perm(__u32 *perm, __u32 *noperm, char *str)
213 {
214         char *start, *end;
215         char name[64];
216         perm_type_t *pt;
217
218         *perm = 0;
219         *noperm = 0;
220         start = str;
221         while (1) {
222                 size_t len;
223                 memset(name, 0, sizeof(name));
224                 end = strchr(start, ',');
225                 if (end == NULL)
226                         end = str + strlen(str);
227                 if (start >= end)
228                         break;
229                 len = end - start;
230                 if (len >= sizeof(name))
231                         return -E2BIG;
232                 strncpy(name, start, len);
233                 name[len] = '\0';
234                 for (pt = perm_types; pt->name; pt++) {
235                         if (!strcasecmp(name, pt->name)) {
236                                 *perm |= pt->bit;
237                                 break;
238                         }
239                 }
240
241                 if (!pt->name) {
242                         for (pt = noperm_types; pt->name; pt++) {
243                                 if (!strcasecmp(name, pt->name)) {
244                                         *noperm |= pt->bit;
245                                         break;
246                                 }
247                         }
248
249                         if (!pt->name) {
250                                 printf("unkown type: %s\n", name);
251                                 return -1;
252                         }
253                 }
254
255                 start = end + 1;
256         }
257         return 0;
258 }
259
260 static int
261 parse_perm_line(struct identity_downcall_data *data, char *line, size_t size)
262 {
263         char uid_str[size];
264         char nid_str[size];
265         char perm_str[size];
266         lnet_nid_t nid;
267         __u32 perm, noperm;
268         int rc, i;
269
270         if (data->idd_nperms >= N_PERMS_MAX) {
271                 errlog("permission count %d > max %d\n",
272                         data->idd_nperms, N_PERMS_MAX);
273                 return -1;
274         }
275
276         rc = sscanf(line, "%s %s %s", nid_str, uid_str, perm_str);
277         if (rc != 3) {
278                 errlog("can't parse line %s\n", line);
279                 return -1;
280         }
281
282         if (!match_uid(data->idd_uid, uid_str))
283                 return 0;
284
285         if (!strcmp(nid_str, "*")) {
286                 nid = LNET_NID_ANY;
287         } else {
288                 nid = libcfs_str2nid(nid_str);
289                 if (nid == LNET_NID_ANY) {
290                         errlog("can't parse nid %s\n", nid_str);
291                         return -1;
292                 }
293         }
294
295         if (parse_perm(&perm, &noperm, perm_str)) {
296                 errlog("invalid perm %s\n", perm_str);
297                 return -1;
298         }
299
300         /* merge the perms with the same nid.
301          *
302          * If there is LNET_NID_ANY in data->idd_perms[i].pdd_nid,
303          * it must be data->idd_perms[0].pdd_nid, and act as default perm.
304          */
305         if (nid != LNET_NID_ANY) {
306                 int found = 0;
307
308                 /* search for the same nid */
309                 for (i = data->idd_nperms - 1; i >= 0; i--) {
310                         if (data->idd_perms[i].pdd_nid == nid) {
311                                 data->idd_perms[i].pdd_perm =
312                                         (data->idd_perms[i].pdd_perm | perm) &
313                                         ~noperm;
314                                 found = 1;
315                                 break;
316                         }
317                 }
318
319                 /* NOT found, add to tail */
320                 if (!found) {
321                         data->idd_perms[data->idd_nperms].pdd_nid = nid;
322                         data->idd_perms[data->idd_nperms].pdd_perm =
323                                 perm & ~noperm;
324                         data->idd_nperms++;
325                 }
326         } else {
327                 if (data->idd_nperms > 0) {
328                         /* the first one isn't LNET_NID_ANY, need exchange */
329                         if (data->idd_perms[0].pdd_nid != LNET_NID_ANY) {
330                                 data->idd_perms[data->idd_nperms].pdd_nid =
331                                         data->idd_perms[0].pdd_nid;
332                                 data->idd_perms[data->idd_nperms].pdd_perm =
333                                         data->idd_perms[0].pdd_perm;
334                                 data->idd_perms[0].pdd_nid = LNET_NID_ANY;
335                                 data->idd_perms[0].pdd_perm = perm & ~noperm;
336                                 data->idd_nperms++;
337                         } else {
338                                 /* only fix LNET_NID_ANY item */
339                                 data->idd_perms[0].pdd_perm =
340                                         (data->idd_perms[0].pdd_perm | perm) &
341                                         ~noperm;
342                         }
343                 } else {
344                         /* it is the first one, only add to head */
345                         data->idd_perms[0].pdd_nid = LNET_NID_ANY;
346                         data->idd_perms[0].pdd_perm = perm & ~noperm;
347                         data->idd_nperms = 1;
348                 }
349         }
350
351         return 0;
352 }
353
354 int get_perms(struct identity_downcall_data *data)
355 {
356         FILE *fp;
357         char line[PATH_MAX];
358
359         fp = fopen(PERM_PATHNAME, "r");
360         if (fp == NULL) {
361                 if (errno == ENOENT) {
362                         return 0;
363                 } else {
364                         errlog("open %s failed: %s\n",
365                                PERM_PATHNAME, strerror(errno));
366                         data->idd_err = errno;
367                         return -1;
368                 }
369         }
370
371         while (fgets(line, sizeof(line), fp)) {
372                 if (comment_line(line))
373                         continue;
374
375                 if (parse_perm_line(data, line, sizeof(line))) {
376                         errlog("parse line %s failed!\n", line);
377                         data->idd_err = EINVAL;
378                         fclose(fp);
379                         return -1;
380                 }
381         }
382
383         fclose(fp);
384         return 0;
385 }
386
387 static void show_result(struct identity_downcall_data *data)
388 {
389         int i;
390
391         if (data->idd_err) {
392                 errlog("failed to get identity for uid %d: %s\n",
393                        data->idd_uid, strerror(data->idd_err));
394                 return;
395         }
396
397         printf("uid=%d gid=%d", data->idd_uid, data->idd_gid);
398         for (i = 0; i < data->idd_ngroups; i++)
399                 printf(",%u", data->idd_groups[i]);
400         printf("\n");
401         printf("permissions:\n"
402                "  nid\t\t\tperm\n");
403         for (i = 0; i < data->idd_nperms; i++) {
404                 struct perm_downcall_data *pdd;
405
406                 pdd = &data->idd_perms[i];
407
408                 printf("  %#jx\t0x%x\n", (uintmax_t)pdd->pdd_nid,
409                        pdd->pdd_perm);
410         }
411         printf("\n");
412 }
413
414 int main(int argc, char **argv)
415 {
416         char *end;
417         struct identity_downcall_data *data = NULL;
418         glob_t path;
419         unsigned long uid;
420         int fd, rc = -EINVAL, size, maxgroups;
421
422         progname = basename(argv[0]);
423         if (argc != 3) {
424                 usage();
425                 goto out;
426         }
427
428         uid = strtoul(argv[2], &end, 0);
429         if (*end) {
430                 errlog("%s: invalid uid '%s'\n", progname, argv[2]);
431                 goto out;
432         }
433
434         maxgroups = sysconf(_SC_NGROUPS_MAX);
435         if (maxgroups > NGROUPS_MAX)
436                 maxgroups = NGROUPS_MAX;
437         if (maxgroups == -1) {
438                 rc = -EINVAL;
439                 goto out;
440         }
441
442         size = offsetof(struct identity_downcall_data, idd_groups[maxgroups]);
443         data = malloc(size);
444         if (!data) {
445                 errlog("malloc identity downcall data(%d) failed!\n", size);
446                 rc = -ENOMEM;
447                 goto out;
448         }
449
450         memset(data, 0, size);
451         data->idd_magic = IDENTITY_DOWNCALL_MAGIC;
452         data->idd_uid = uid;
453         /* get groups for uid */
454         rc = get_groups_local(data, maxgroups);
455         if (rc)
456                 goto downcall;
457
458         size = offsetof(struct identity_downcall_data,
459                         idd_groups[data->idd_ngroups]);
460         /* read permission database */
461         rc = get_perms(data);
462
463 downcall:
464         if (getenv("L_GETIDENTITY_TEST")) {
465                 show_result(data);
466                 rc = 0;
467                 goto out;
468         }
469
470         rc = cfs_get_param_paths(&path, "mdt/%s/identity_info", argv[1]);
471         if (rc != 0) {
472                 rc = -errno;
473                 goto out;
474         }
475
476         fd = open(path.gl_pathv[0], O_WRONLY);
477         if (fd < 0) {
478                 errlog("can't open file '%s':%s\n", path.gl_pathv[0],
479                        strerror(errno));
480                 rc = -errno;
481                 goto out_params;
482         }
483
484         rc = write(fd, data, size);
485         close(fd);
486         if (rc != size) {
487                 errlog("partial write ret %d: %s\n", rc, strerror(errno));
488                 rc = -1;
489         } else {
490                 rc = 0;
491         }
492
493 out_params:
494         cfs_free_param_data(&path);
495 out:
496         if (data != NULL)
497                 free(data);
498         return rc;
499 }