Whamcloud - gitweb
Mass conversion of all copyright messages to Oracle.
[fs/lustre-release.git] / lustre / utils / gss / lsupport.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
30  * Use is subject to license terms.
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
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         [CIBLND]        = { "CIBLND",   external_nid2hostname },
305         [OPENIBLND]     = { "OPENIBLND",external_nid2hostname },
306         [IIBLND]        = { "IIBLND",   external_nid2hostname },
307         [LOLND]         = { "LOLND",    lolnd_nid2hostname },
308         [RALND]         = { "RALND",    external_nid2hostname },
309         [VIBLND]        = { "VIBLND",   external_nid2hostname },
310         [MXLND]         = { "MXLND",    external_nid2hostname },
311 };
312
313 #define LND_MAX         (sizeof(converter) / sizeof(converter[0]))
314
315 int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen)
316 {
317         uint32_t lnd, net, addr;
318
319         addr = LNET_NIDADDR(nid);
320         net = LNET_NIDNET(nid);
321         lnd = LNET_NETTYP(net);
322
323         if (lnd >= LND_MAX) {
324                 printerr(0, "ERROR: Unrecognized LND %u\n", lnd);
325                 return -1;
326         }
327
328         if (converter[lnd].nid2name == NULL) {
329                 printerr(0, "ERROR: %s converter not ready\n",
330                         converter[lnd].name);
331                 return -1;
332         }
333
334         return converter[lnd].nid2name(converter[lnd].name, net, addr,
335                                        buf, buflen);
336 }
337
338
339 /****************************************
340  * user mapping database handling       *
341  * (very rudiment)                      *
342  ****************************************/
343
344 #define MAPPING_GROW_SIZE       512
345 #define MAX_LINE_LEN            256
346
347 struct user_map_item {
348         char        *principal; /* NULL means match all */
349         lnet_nid_t   nid;
350         uid_t        uid;
351 };
352
353 struct user_mapping {
354         int                   nitems;
355         struct user_map_item *items;
356 };
357
358 static struct user_mapping mapping = {0, NULL};
359 /* FIXME to be finished: monitor change of mapping database */
360 static int mapping_mtime = 0;
361
362 void cleanup_mapping(void)
363 {
364         if (mapping.items) {
365                 for (; mapping.nitems > 0; mapping.nitems--)
366                         if (mapping.items[mapping.nitems-1].principal)
367                                 free(mapping.items[mapping.nitems-1].principal);
368
369                 free(mapping.items);
370                 mapping.items = NULL;
371         }
372 }
373
374 static int grow_mapping(int nitems)
375 {
376         struct user_map_item *new;
377         int oldsize, newsize;
378
379         oldsize = (mapping.nitems * sizeof(struct user_map_item) +
380                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
381         newsize = (nitems * sizeof(struct user_map_item) +
382                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
383         while (newsize <= oldsize)
384                 return 0;
385
386         newsize *= MAPPING_GROW_SIZE;
387         new = malloc(newsize);
388         if (!new) {
389                 printerr(0, "can't alloc mapping size %d\n", newsize);
390                 return -1;
391         }
392
393         if (mapping.items) {
394                 memcpy(new, mapping.items,
395                        mapping.nitems * sizeof(struct user_map_item));
396                 free(mapping.items);
397         }
398         mapping.items = new;
399         return 0;
400 }
401
402 uid_t parse_uid(char *uidstr)
403 {
404         struct passwd *pw;
405         char *p = NULL;
406         long uid;
407
408         pw = getpwnam(uidstr);
409         if (pw)
410                 return pw->pw_uid;
411
412         uid = strtol(uidstr, &p, 0);
413         if (*p == '\0')
414                 return (uid_t) uid;
415
416         return -1;
417 }
418
419 static int read_mapping_db(void)
420 {
421         char princ[MAX_LINE_LEN];
422         char nid_str[MAX_LINE_LEN];
423         char dest[MAX_LINE_LEN];
424         char linebuf[MAX_LINE_LEN];
425         char *line;
426         lnet_nid_t nid;
427         uid_t dest_uid;
428         FILE *f;
429
430         /* cleanup old mappings */
431         cleanup_mapping();
432
433         f = fopen(MAPPING_DATABASE_FILE, "r");
434         if (!f) {
435                 printerr(0, "can't open mapping database: %s\n",
436                          MAPPING_DATABASE_FILE);
437                 return -1;
438         }
439
440         while ((line = fgets(linebuf, MAX_LINE_LEN, f)) != NULL) {
441                 char *name;
442
443                 if (strlen(line) >= MAX_LINE_LEN) {
444                         printerr(0, "invalid mapping db: line too long (%d)\n",
445                                  strlen(line));
446                         continue;
447                 }
448
449                 if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
450                         printerr(0, "mapping db: syntax error\n");
451                         continue;
452                 }
453
454                 if (!strcmp(princ, "*")) {
455                         name = NULL;
456                 } else {
457                         name = strdup(princ);
458                         if (!name) {
459                                 printerr(0, "fail to dup str %s\n", princ);
460                                 continue;
461                         }
462                 }
463
464                 if (!strcmp(nid_str, "*")) {
465                         nid = LNET_NID_ANY;
466                 } else {
467                         nid = libcfs_str2nid(nid_str);
468                         if (nid == LNET_NID_ANY) {
469                                 printerr(0, "fail to parse nid %s\n", nid_str);
470                                 if (name)
471                                 free(name);
472                                 continue;
473                         }
474                 }
475
476                 dest_uid = parse_uid(dest);
477                 if (dest_uid == -1) {
478                         printerr(0, "no valid user: %s\n", dest);
479                         if (name)
480                         free(name);
481                         continue;
482                 }
483
484                 if (grow_mapping(mapping.nitems + 1)) {
485                         printerr(0, "fail to grow mapping to %d\n",
486                                  mapping.nitems + 1);
487                         if (name)
488                         free(name);
489                         fclose(f);
490                         return -1;
491                 }
492
493                 mapping.items[mapping.nitems].principal = name;
494                 mapping.items[mapping.nitems].nid = nid;
495                 mapping.items[mapping.nitems].uid = dest_uid;
496                 mapping.nitems++;
497                 printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
498                          name, nid_str, nid, dest_uid);
499         }
500
501         fclose(f);
502         return 0;
503 }
504
505 static inline int mapping_changed(void)
506 {
507         struct stat st;
508
509         if (stat(MAPPING_DATABASE_FILE, &st) == -1) {
510                 /* stat failed, treat it like doesn't exist or be removed */
511                 if (mapping_mtime == 0) {
512                         return 0;
513                 } else {
514                         printerr(0, "Warning: stat %s failed: %s\n",
515                                  MAPPING_DATABASE_FILE, strerror(errno));
516
517                         mapping_mtime = 0;
518                         return 1;
519                 }
520         }
521
522         if (st.st_mtime != mapping_mtime) {
523                 mapping_mtime = st.st_mtime;
524                 return 1;
525         }
526
527         return 0;
528 }
529
530 int lookup_mapping(char *princ, lnet_nid_t nid, uid_t *uid)
531 {
532         int n;
533
534         *uid = -1;
535
536         /* FIXME race condition here */
537         if (mapping_changed()) {
538                 if (read_mapping_db())
539                         printerr(0, "all remote users will be denied\n");
540         }
541
542         for (n = 0; n < mapping.nitems; n++) {
543                 struct user_map_item *entry = &mapping.items[n];
544
545                 if (entry->nid != LNET_NID_ANY && entry->nid != nid)
546                         continue;
547                 if (!entry->principal || !strcasecmp(entry->principal, princ)) {
548                         printerr(1, "found mapping: %s ==> %d\n",
549                                  princ, entry->uid);
550                         *uid = entry->uid;
551                         return 0;
552                 }
553         }
554
555         printerr(2, "no mapping for %s/%#Lx\n", princ, nid);
556         return -1;
557 }