Whamcloud - gitweb
LU-5710 all: second batch of corrected typos and grammar errors
[fs/lustre-release.git] / lustre / utils / gss / lsupport.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.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2014, Intel Corporation.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  */
36
37 #ifndef _GNU_SOURCE
38 #define _GNU_SOURCE
39 #endif
40 #include "config.h"
41 #include <sys/param.h>
42 #include <sys/utsname.h>
43 #include <sys/stat.h>
44 #include <sys/socket.h>
45 #include <arpa/inet.h>
46 #include <sys/types.h>
47 #include <sys/ipc.h>
48 #include <sys/sem.h>
49
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <pwd.h>
53 #include <grp.h>
54 #include <string.h>
55 #include <dirent.h>
56 #include <poll.h>
57 #include <fcntl.h>
58 #include <signal.h>
59 #include <unistd.h>
60 #include <errno.h>
61 #include <assert.h>
62 #ifdef HAVE_GETHOSTBYNAME
63 # include <netdb.h>
64 #endif
65 #include <lnet/nidstr.h>
66 #ifdef _NEW_BUILD_
67 # include "lgss_utils.h"
68 #else
69 # include "err_util.h"
70 # include "gssd.h"
71 #endif
72 #include "lsupport.h"
73
74 const char * lustre_svc_name[] =
75 {
76         [LUSTRE_GSS_SVC_MGS]    = "MGS",
77         [LUSTRE_GSS_SVC_MDS]    = "MDS",
78         [LUSTRE_GSS_SVC_OSS]    = "OSS",
79 };
80
81 /****************************************
82  * exclusive startup                    *
83  ****************************************/
84
85 static struct __sem_s {
86         char           *name;
87         key_t           sem_key;
88         int             sem_id;
89 } sems[2] = {
90         [GSSD_CLI] = { "client",  0x3a92d473, 0 },
91         [GSSD_SVC] = { "server",  0x3b92d473, 0 },
92 };
93
94 void gssd_init_unique(int type)
95 {
96         struct __sem_s *sem = &sems[type];
97         struct sembuf   sembuf;
98
99         assert(type == GSSD_CLI || type == GSSD_SVC);
100
101 again:
102         sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
103         if (sem->sem_id == -1) {
104                 if (errno != EEXIST) {
105                         printerr(0, "Create sem: %s\n", strerror(errno));
106                         exit(-1);
107                 }
108
109                 /* already exist. Note there's still a small window racing
110                  * with other processes, due to the stupid semaphore semantics.
111                  */
112                 sem->sem_id = semget(sem->sem_key, 0, 0700);
113                 if (sem->sem_id == -1) {
114                         if (errno == ENOENT) {
115                                 printerr(0, "another instance just exit, "
116                                          "try again\n");
117                                 goto again;
118                         }
119
120                         printerr(0, "Obtain sem: %s\n", strerror(errno));
121                         exit(-1);
122                 }
123         } else {
124                 int val = 1;
125
126                 if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
127                         printerr(0, "Initialize sem: %s\n",
128                                  strerror(errno));
129                         exit(-1);
130                 }
131         }
132
133         sembuf.sem_num = 0;
134         sembuf.sem_op = -1;
135         sembuf.sem_flg = IPC_NOWAIT | SEM_UNDO;
136
137         if (semop(sem->sem_id, &sembuf, 1) != 0) {
138                 if (errno == EAGAIN) {
139                         printerr(0, "Another instance is running, exit\n");
140                         exit(0);
141                 }
142                 printerr(0, "Grab sem: %s\n", strerror(errno));
143                 exit(0);
144         }
145
146         printerr(2, "Successfully created %s global identity\n", sem->name);
147 }
148
149 void gssd_exit_unique(int type)
150 {
151         assert(type == GSSD_CLI || type == GSSD_SVC);
152
153         /*
154          * do nothing. we can't remove the sem here, otherwise the race
155          * window would be much bigger. So it's sad we have to leave the
156          * sem in the system forever.
157          */
158 }
159
160 /****************************************
161  * client side resolvation:             *
162  *    lnd/netid/nid => hostname         *
163  ****************************************/
164
165 char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
166
167 typedef int lnd_nid2hostname_t(char *lnd, uint32_t net, uint32_t addr,
168                                char *buf, int buflen);
169
170 /* FIXME what about IPv6? */
171 static
172 int ipv4_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
173                       char *buf, int buflen)
174 {
175         struct hostent  *ent;
176
177         addr = htonl(addr);
178         ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
179         if (!ent) {
180                 printerr(0, "%s: can't resolve 0x%x\n", lnd, addr);
181                 return -1;
182         }
183         if (strlen(ent->h_name) >= buflen) {
184                 printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
185                 return -1;
186         }
187         strcpy(buf, ent->h_name);
188
189         printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
190                  lnd, net, addr, buf);
191         return 0;
192 }
193
194 static
195 int lolnd_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
196                        char *buf, int buflen)
197 {
198         struct utsname   uts;
199         struct hostent  *ent;
200
201         if (addr) {
202                 printerr(0, "%s: addr is 0x%x, we expect 0\n", lnd, addr);
203                 return -1;
204         }
205
206         if (uname(&uts)) {
207                 printerr(0, "%s: failed obtain local machine name\n", lnd);
208                 return -1;
209         }
210
211         ent = gethostbyname(uts.nodename);
212         if (!ent) {
213                 printerr(0, "%s: failed obtain canonical name of %s\n",
214                          lnd, uts.nodename);
215                 return -1;
216         }
217
218         if (strlen(ent->h_name) >= buflen) {
219                 printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
220                 return -1;
221         }
222         strcpy(buf, ent->h_name);
223
224         printerr(3, "%s: addr 0x%x => %s\n", lnd, addr, buf);
225         return 0;
226 }
227
228 static int is_space(char c)
229 {
230         return (c == ' ' || c == '\t' || c == '\n');
231 }
232
233 static
234 int external_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
235                           char *namebuf, int namebuflen)
236 {
237         const int bufsize = PATH_MAX + 256;
238         char buf[bufsize], *head, *tail;
239         FILE *fghn;
240
241         sprintf(buf, "%s %s 0x%x 0x%x", gethostname_ex, lnd, net, addr);
242         printerr(2, "cmd: %s\n", buf);
243
244         fghn = popen(buf, "r");
245         if (fghn == NULL) {
246                 printerr(0, "failed to call %s\n", gethostname_ex);
247                 return -1;
248         }
249
250         head = fgets(buf, bufsize, fghn);
251         if (head == NULL) {
252                 printerr(0, "can't read from %s\n", gethostname_ex);
253                 return -1;
254         }
255         if (pclose(fghn) == -1)
256                 printerr(1, "pclose failed, continue\n");
257
258         /* trim head/tail space */
259         while (is_space(*head))
260                 head++;
261
262         tail = head + strlen(head);
263         if (tail <= head) {
264                 printerr(0, "no output from %s\n", gethostname_ex);
265                 return -1;
266         }
267         while (is_space(*(tail - 1)))
268                 tail--;
269         if (tail <= head) {
270                 printerr(0, "output are all space from %s\n", gethostname_ex);
271                 return -1;
272         }
273         *tail = '\0';
274
275         /* start with '@' means error msg */
276         if (head[0] == '@') {
277                 printerr(0, "error from %s: %s\n", gethostname_ex, &head[1]);
278                 return -1;
279         }
280
281         if (tail - head > namebuflen) {
282                 printerr(0, "external hostname too long: %s\n", head);
283                 return -1;
284         }
285
286         printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
287                  lnd, net, addr, head);
288         strcpy(namebuf, head);
289         return 0;
290 }
291
292 struct convert_struct {
293         char                    *name;
294         lnd_nid2hostname_t      *nid2name;
295 };
296
297 static struct convert_struct converter[] = {
298         [0]             = { "UNUSED0",  NULL},
299         [QSWLND]        = { "QSWLND",   external_nid2hostname},
300         [SOCKLND]       = { "SOCKLND",  ipv4_nid2hostname },
301         [GMLND]         = { "GMLND",    external_nid2hostname},
302         [PTLLND]        = { "PTLLND",   external_nid2hostname },
303         [O2IBLND]       = { "O2IBLND",  ipv4_nid2hostname },
304         [LOLND]         = { "LOLND",    lolnd_nid2hostname },
305         [RALND]         = { "RALND",    external_nid2hostname },
306         [MXLND]         = { "MXLND",    external_nid2hostname },
307 };
308
309 #define LND_MAX         (sizeof(converter) / sizeof(converter[0]))
310
311 int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen)
312 {
313         uint32_t lnd, net, addr;
314
315         addr = LNET_NIDADDR(nid);
316         net = LNET_NIDNET(nid);
317         lnd = LNET_NETTYP(net);
318
319         if (lnd >= LND_MAX) {
320                 printerr(0, "ERROR: Unrecognized LND %u\n", lnd);
321                 return -1;
322         }
323
324         if (converter[lnd].nid2name == NULL) {
325                 printerr(0, "ERROR: %s converter not ready\n",
326                         converter[lnd].name);
327                 return -1;
328         }
329
330         return converter[lnd].nid2name(converter[lnd].name, net, addr,
331                                        buf, buflen);
332 }
333
334
335 /****************************************
336  * user mapping database handling       *
337  * (very rudiment)                      *
338  ****************************************/
339
340 #define MAPPING_GROW_SIZE       512
341 #define MAX_LINE_LEN            256
342
343 struct user_map_item {
344         char        *principal; /* NULL means match all */
345         lnet_nid_t   nid;
346         uid_t        uid;
347 };
348
349 struct user_mapping {
350         int                   nitems;
351         struct user_map_item *items;
352 };
353
354 static struct user_mapping mapping = {0, NULL};
355 /* FIXME to be finished: monitor change of mapping database */
356 static int mapping_mtime = 0;
357
358 void cleanup_mapping(void)
359 {
360         if (mapping.items) {
361                 for (; mapping.nitems > 0; mapping.nitems--)
362                         if (mapping.items[mapping.nitems-1].principal)
363                                 free(mapping.items[mapping.nitems-1].principal);
364
365                 free(mapping.items);
366                 mapping.items = NULL;
367         }
368 }
369
370 static int grow_mapping(int nitems)
371 {
372         struct user_map_item *new;
373         int oldsize, newsize;
374
375         oldsize = (mapping.nitems * sizeof(struct user_map_item) +
376                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
377         newsize = (nitems * sizeof(struct user_map_item) +
378                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
379         while (newsize <= oldsize)
380                 return 0;
381
382         newsize *= MAPPING_GROW_SIZE;
383         new = malloc(newsize);
384         if (!new) {
385                 printerr(0, "can't alloc mapping size %d\n", newsize);
386                 return -1;
387         }
388
389         if (mapping.items) {
390                 memcpy(new, mapping.items,
391                        mapping.nitems * sizeof(struct user_map_item));
392                 free(mapping.items);
393         }
394         mapping.items = new;
395         return 0;
396 }
397
398 uid_t parse_uid(char *uidstr)
399 {
400         struct passwd *pw;
401         char *p = NULL;
402         long uid;
403
404         pw = getpwnam(uidstr);
405         if (pw)
406                 return pw->pw_uid;
407
408         uid = strtol(uidstr, &p, 0);
409         if (*p == '\0')
410                 return (uid_t) uid;
411
412         return -1;
413 }
414
415 static int read_mapping_db(void)
416 {
417         char princ[MAX_LINE_LEN];
418         char nid_str[MAX_LINE_LEN];
419         char dest[MAX_LINE_LEN];
420         char linebuf[MAX_LINE_LEN];
421         char *line;
422         lnet_nid_t nid;
423         uid_t dest_uid;
424         FILE *f;
425
426         /* cleanup old mappings */
427         cleanup_mapping();
428
429         f = fopen(MAPPING_DATABASE_FILE, "r");
430         if (!f) {
431                 printerr(0, "can't open mapping database: %s\n",
432                          MAPPING_DATABASE_FILE);
433                 return -1;
434         }
435
436         while ((line = fgets(linebuf, MAX_LINE_LEN, f)) != NULL) {
437                 char *name;
438
439                 if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
440                         printerr(0, "mapping db: syntax error\n");
441                         continue;
442                 }
443
444                 if (!strcmp(princ, "*")) {
445                         name = NULL;
446                 } else {
447                         name = strdup(princ);
448                         if (!name) {
449                                 printerr(0, "fail to dup str %s\n", princ);
450                                 continue;
451                         }
452                 }
453
454                 if (!strcmp(nid_str, "*")) {
455                         nid = LNET_NID_ANY;
456                 } else {
457                         nid = libcfs_str2nid(nid_str);
458                         if (nid == LNET_NID_ANY) {
459                                 printerr(0, "fail to parse nid %s\n", nid_str);
460                                 if (name)
461                                 free(name);
462                                 continue;
463                         }
464                 }
465
466                 dest_uid = parse_uid(dest);
467                 if (dest_uid == -1) {
468                         printerr(0, "no valid user: %s\n", dest);
469                         if (name)
470                         free(name);
471                         continue;
472                 }
473
474                 if (grow_mapping(mapping.nitems + 1)) {
475                         printerr(0, "fail to grow mapping to %d\n",
476                                  mapping.nitems + 1);
477                         if (name)
478                         free(name);
479                         fclose(f);
480                         return -1;
481                 }
482
483                 mapping.items[mapping.nitems].principal = name;
484                 mapping.items[mapping.nitems].nid = nid;
485                 mapping.items[mapping.nitems].uid = dest_uid;
486                 mapping.nitems++;
487                 printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
488                          name, nid_str, nid, dest_uid);
489         }
490
491         fclose(f);
492         return 0;
493 }
494
495 static inline int mapping_changed(void)
496 {
497         struct stat st;
498
499         if (stat(MAPPING_DATABASE_FILE, &st) == -1) {
500                 /* stat failed, treat it like doesn't exist or be removed */
501                 if (mapping_mtime == 0) {
502                         return 0;
503                 } else {
504                         printerr(0, "Warning: stat %s failed: %s\n",
505                                  MAPPING_DATABASE_FILE, strerror(errno));
506
507                         mapping_mtime = 0;
508                         return 1;
509                 }
510         }
511
512         if (st.st_mtime != mapping_mtime) {
513                 mapping_mtime = st.st_mtime;
514                 return 1;
515         }
516
517         return 0;
518 }
519
520 int lookup_mapping(char *princ, lnet_nid_t nid, uid_t *uid)
521 {
522         int n;
523
524         *uid = -1;
525
526         /* FIXME race condition here */
527         if (mapping_changed()) {
528                 if (read_mapping_db())
529                         printerr(0, "all remote users will be denied\n");
530         }
531
532         for (n = 0; n < mapping.nitems; n++) {
533                 struct user_map_item *entry = &mapping.items[n];
534
535                 if (entry->nid != LNET_NID_ANY && entry->nid != nid)
536                         continue;
537                 if (!entry->principal || !strcasecmp(entry->principal, princ)) {
538                         printerr(1, "found mapping: %s ==> %d\n",
539                                  princ, entry->uid);
540                         *uid = entry->uid;
541                         return 0;
542                 }
543         }
544
545         printerr(2, "no mapping for %s/%#Lx\n", princ, nid);
546         return -1;
547 }