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