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