Whamcloud - gitweb
LU-12511 utils: fix regression for UAPI headers for native client
[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  */
31
32 #ifndef _GNU_SOURCE
33 #define _GNU_SOURCE
34 #endif
35 #include "config.h"
36 #include <sys/param.h>
37 #include <sys/utsname.h>
38 #include <sys/stat.h>
39 #include <sys/socket.h>
40 #include <arpa/inet.h>
41 #include <sys/types.h>
42 #include <sys/ipc.h>
43 #include <sys/sem.h>
44
45 #include <stdbool.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <pwd.h>
49 #include <grp.h>
50 #include <string.h>
51 #include <dirent.h>
52 #include <poll.h>
53 #include <fcntl.h>
54 #include <signal.h>
55 #include <unistd.h>
56 #include <errno.h>
57 #include <assert.h>
58 #ifdef HAVE_NETDB_H
59 # include <netdb.h>
60 #endif
61 #ifdef _NEW_BUILD_
62 # include "lgss_utils.h"
63 #else
64 # include "err_util.h"
65 # include "gssd.h"
66 #endif
67 #include "lsupport.h"
68
69 const char * lustre_svc_name[] =
70 {
71         [LUSTRE_GSS_SVC_MGS]    = "MGS",
72         [LUSTRE_GSS_SVC_MDS]    = "MDS",
73         [LUSTRE_GSS_SVC_OSS]    = "OSS",
74 };
75
76 /****************************************
77  * exclusive startup                    *
78  ****************************************/
79
80 static struct __sem_s {
81         char           *name;
82         key_t           sem_key;
83         int             sem_id;
84 } sems[2] = {
85         [GSSD_CLI] = { "client",  0x3a92d473, 0 },
86         [GSSD_SVC] = { "server",  0x3b92d473, 0 },
87 };
88
89 void gssd_init_unique(int type)
90 {
91         struct __sem_s *sem = &sems[type];
92         struct sembuf   sembuf;
93
94         assert(type == GSSD_CLI || type == GSSD_SVC);
95
96 again:
97         sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
98         if (sem->sem_id == -1) {
99                 if (errno != EEXIST) {
100                         printerr(0, "Create sem: %s\n", strerror(errno));
101                         exit(-1);
102                 }
103
104                 /* already exist. Note there's still a small window racing
105                  * with other processes, due to the stupid semaphore semantics.
106                  */
107                 sem->sem_id = semget(sem->sem_key, 0, 0700);
108                 if (sem->sem_id == -1) {
109                         if (errno == ENOENT) {
110                                 printerr(0, "another instance just exit, "
111                                          "try again\n");
112                                 goto again;
113                         }
114
115                         printerr(0, "Obtain sem: %s\n", strerror(errno));
116                         exit(-1);
117                 }
118         } else {
119                 int val = 1;
120
121                 if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
122                         printerr(0, "Initialize sem: %s\n",
123                                  strerror(errno));
124                         exit(-1);
125                 }
126         }
127
128         sembuf.sem_num = 0;
129         sembuf.sem_op = -1;
130         sembuf.sem_flg = IPC_NOWAIT | SEM_UNDO;
131
132         if (semop(sem->sem_id, &sembuf, 1) != 0) {
133                 if (errno == EAGAIN) {
134                         printerr(0, "Another instance is running, exit\n");
135                         exit(0);
136                 }
137                 printerr(0, "Grab sem: %s\n", strerror(errno));
138                 exit(0);
139         }
140
141         printerr(2, "Successfully created %s global identity\n", sem->name);
142 }
143
144 void gssd_exit_unique(int type)
145 {
146         assert(type == GSSD_CLI || type == GSSD_SVC);
147
148         /*
149          * do nothing. we can't remove the sem here, otherwise the race
150          * window would be much bigger. So it's sad we have to leave the
151          * sem in the system forever.
152          */
153 }
154
155 /****************************************
156  * client side resolvation:             *
157  *    lnd/netid/nid => hostname         *
158  ****************************************/
159
160 char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
161
162 typedef int lnd_nid2hostname_t(char *lnd, uint32_t net, uint32_t addr,
163                                char *buf, int buflen);
164
165 /* FIXME what about IPv6? */
166 static
167 int ipv4_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
168                       char *buf, int buflen)
169 {
170         struct hostent  *ent;
171
172         addr = htonl(addr);
173         ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
174         if (!ent) {
175                 printerr(0, "%s: can't resolve 0x%x\n", lnd, addr);
176                 return -1;
177         }
178         if (strlen(ent->h_name) >= buflen) {
179                 printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
180                 return -1;
181         }
182         strcpy(buf, ent->h_name);
183
184         printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
185                  lnd, net, addr, buf);
186         return 0;
187 }
188
189 static
190 int lolnd_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
191                        char *buf, int buflen)
192 {
193         struct utsname   uts;
194         struct hostent  *ent;
195
196         if (addr) {
197                 printerr(0, "%s: addr is 0x%x, we expect 0\n", lnd, addr);
198                 return -1;
199         }
200
201         if (uname(&uts)) {
202                 printerr(0, "%s: failed obtain local machine name\n", lnd);
203                 return -1;
204         }
205
206         ent = gethostbyname(uts.nodename);
207         if (!ent) {
208                 printerr(0, "%s: failed obtain canonical name of %s\n",
209                          lnd, uts.nodename);
210                 return -1;
211         }
212
213         if (strlen(ent->h_name) >= buflen) {
214                 printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
215                 return -1;
216         }
217         strcpy(buf, ent->h_name);
218
219         printerr(3, "%s: addr 0x%x => %s\n", lnd, addr, buf);
220         return 0;
221 }
222
223 static int is_space(char c)
224 {
225         return (c == ' ' || c == '\t' || c == '\n');
226 }
227
228 static
229 int external_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
230                           char *namebuf, int namebuflen)
231 {
232         const int bufsize = PATH_MAX + 256;
233         char buf[bufsize], *head, *tail;
234         FILE *fghn;
235
236         sprintf(buf, "%s %s 0x%x 0x%x", gethostname_ex, lnd, net, addr);
237         printerr(2, "cmd: %s\n", buf);
238
239         fghn = popen(buf, "r");
240         if (fghn == NULL) {
241                 printerr(0, "failed to call %s\n", gethostname_ex);
242                 return -1;
243         }
244
245         head = fgets(buf, bufsize, fghn);
246         if (head == NULL) {
247                 printerr(0, "can't read from %s\n", gethostname_ex);
248                 pclose(fghn);
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 }