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