Whamcloud - gitweb
LU-16977 utils: access_log_reader accesses beyond batch array
[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 <gssapi/gssapi.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(LL_ERR, "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(LL_ERR,
111                                          "another instance just exit, try again\n");
112                                 goto again;
113                         }
114
115                         printerr(LL_ERR, "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(LL_ERR, "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(LL_ERR, "Another instance is running, exit\n");
135                         exit(0);
136                 }
137                 printerr(LL_ERR, "Grab sem: %s\n", strerror(errno));
138                 exit(0);
139         }
140
141         printerr(LL_INFO, "Successfully created %s global identity\n",
142                  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(LL_ERR, "%s: can't resolve 0x%x\n", lnd, addr);
177                 return -1;
178         }
179         if (strlen(ent->h_name) >= buflen) {
180                 printerr(LL_ERR, "%s: name too long: %s\n", lnd, ent->h_name);
181                 return -1;
182         }
183         strcpy(buf, ent->h_name);
184
185         printerr(LL_INFO, "%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(LL_ERR, "%s: addr is 0x%x, we expect 0\n", lnd, addr);
199                 return -1;
200         }
201
202         if (uname(&uts)) {
203                 printerr(LL_ERR, "%s: failed obtain local machine name\n", lnd);
204                 return -1;
205         }
206
207         ent = gethostbyname(uts.nodename);
208         if (!ent) {
209                 printerr(LL_ERR, "%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(LL_ERR, "%s: name too long: %s\n", lnd, ent->h_name);
216                 return -1;
217         }
218         strcpy(buf, ent->h_name);
219
220         printerr(LL_DEBUG, "%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(LL_INFO, "cmd: %s\n", buf);
239
240         fghn = popen(buf, "r");
241         if (fghn == NULL) {
242                 printerr(LL_ERR, "failed to call %s\n", gethostname_ex);
243                 return -1;
244         }
245
246         head = fgets(buf, bufsize, fghn);
247         if (head == NULL) {
248                 printerr(LL_ERR, "can't read from %s\n", gethostname_ex);
249                 pclose(fghn);
250                 return -1;
251         }
252         if (pclose(fghn) == -1)
253                 printerr(LL_WARN, "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(LL_ERR, "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(LL_ERR, "output are all space from %s\n",
268                          gethostname_ex);
269                 return -1;
270         }
271         *tail = '\0';
272
273         /* start with '@' means error msg */
274         if (head[0] == '@') {
275                 printerr(LL_ERR, "error from %s: %s\n",
276                          gethostname_ex, &head[1]);
277                 return -1;
278         }
279
280         if (tail - head > namebuflen) {
281                 printerr(LL_ERR, "external hostname too long: %s\n", head);
282                 return -1;
283         }
284
285         printerr(LL_INFO, "%s: net 0x%x, addr 0x%x => %s\n",
286                  lnd, net, addr, head);
287         strcpy(namebuf, head);
288         return 0;
289 }
290
291 struct convert_struct {
292         char                    *name;
293         lnd_nid2hostname_t      *nid2name;
294 };
295
296 static struct convert_struct converter[] = {
297         [0]       = { .name = "UNUSED0" },
298         [SOCKLND] = { .name = "SOCKLND", .nid2name = ipv4_nid2hostname },
299         [O2IBLND] = { .name = "O2IBLND", .nid2name = ipv4_nid2hostname },
300         [LOLND]   = { .name = "LOLND",   .nid2name = lolnd_nid2hostname },
301         [PTL4LND] = { .name = "PTL4LND", .nid2name = external_nid2hostname },
302         [KFILND]  = { .name = "KFILND",  .nid2name = ipv4_nid2hostname }
303 };
304
305 #define LND_MAX         (sizeof(converter) / sizeof(converter[0]))
306
307 int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen)
308 {
309         uint32_t lnd, net, addr;
310
311         addr = LNET_NIDADDR(nid);
312         net = LNET_NIDNET(nid);
313         lnd = LNET_NETTYP(net);
314
315         if (lnd >= LND_MAX) {
316                 printerr(LL_ERR, "ERROR: Unrecognized LND %u\n", lnd);
317                 return -1;
318         }
319
320         if (converter[lnd].nid2name == NULL) {
321                 printerr(LL_ERR, "ERROR: %s converter not ready\n",
322                         converter[lnd].name);
323                 return -1;
324         }
325
326         return converter[lnd].nid2name(converter[lnd].name, net, addr,
327                                        buf, buflen);
328 }
329
330
331 /****************************************
332  * user mapping database handling       *
333  * (very rudiment)                      *
334  ****************************************/
335
336 #define MAPPING_GROW_SIZE       512
337 #define MAX_LINE_LEN            256
338
339 struct user_map_item {
340         char        *principal; /* NULL means match all */
341         lnet_nid_t   nid;
342         uid_t        uid;
343 };
344
345 struct user_mapping {
346         int                   nitems;
347         struct user_map_item *items;
348 };
349
350 static struct user_mapping mapping;
351 /* FIXME to be finished: monitor change of mapping database */
352 static int mapping_mtime = 0;
353
354 void cleanup_mapping(void)
355 {
356         if (mapping.items) {
357                 for (; mapping.nitems > 0; mapping.nitems--)
358                         if (mapping.items[mapping.nitems-1].principal)
359                                 free(mapping.items[mapping.nitems-1].principal);
360
361                 free(mapping.items);
362                 mapping.items = NULL;
363         }
364 }
365
366 static int grow_mapping(int nitems)
367 {
368         struct user_map_item *new;
369         int oldsize, newsize;
370
371         oldsize = (mapping.nitems * sizeof(struct user_map_item) +
372                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
373         newsize = (nitems * sizeof(struct user_map_item) +
374                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
375         while (newsize <= oldsize)
376                 return 0;
377
378         newsize *= MAPPING_GROW_SIZE;
379         new = malloc(newsize);
380         if (!new) {
381                 printerr(LL_ERR, "can't alloc mapping size %d\n", newsize);
382                 return -1;
383         }
384
385         if (mapping.items) {
386                 memcpy(new, mapping.items,
387                        mapping.nitems * sizeof(struct user_map_item));
388                 free(mapping.items);
389         }
390         mapping.items = new;
391         return 0;
392 }
393
394 uid_t parse_uid(char *uidstr)
395 {
396         struct passwd *pw;
397         char *p = NULL;
398         long uid;
399
400         pw = getpwnam(uidstr);
401         if (pw)
402                 return pw->pw_uid;
403
404         uid = strtol(uidstr, &p, 0);
405         if (*p == '\0')
406                 return (uid_t) uid;
407
408         return -1;
409 }
410
411 static int read_mapping_db(void)
412 {
413         char princ[MAX_LINE_LEN];
414         char nid_str[MAX_LINE_LEN];
415         char dest[MAX_LINE_LEN];
416         char linebuf[MAX_LINE_LEN];
417         char *line;
418         lnet_nid_t nid;
419         uid_t dest_uid;
420         FILE *f;
421
422         /* cleanup old mappings */
423         cleanup_mapping();
424
425         f = fopen(MAPPING_DATABASE_FILE, "r");
426         if (!f) {
427                 printerr(LL_ERR, "can't open mapping database: %s\n",
428                          MAPPING_DATABASE_FILE);
429                 return -1;
430         }
431
432         while ((line = fgets(linebuf, MAX_LINE_LEN, f)) != NULL) {
433                 char *name;
434
435                 if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
436                         printerr(LL_ERR, "mapping db: syntax error\n");
437                         continue;
438                 }
439
440                 if (!strcmp(princ, "*")) {
441                         name = NULL;
442                 } else {
443                         name = strdup(princ);
444                         if (!name) {
445                                 printerr(LL_ERR, "fail to dup str %s\n", princ);
446                                 continue;
447                         }
448                 }
449
450                 if (!strcmp(nid_str, "*")) {
451                         nid = LNET_NID_ANY;
452                 } else {
453                         nid = libcfs_str2nid(nid_str);
454                         if (nid == LNET_NID_ANY) {
455                                 printerr(LL_ERR, "fail to parse nid %s\n",
456                                          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(LL_ERR, "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(LL_ERR, "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(LL_WARN, "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
501                 printerr(LL_ERR, "stat %s failed: %s\n",
502                          MAPPING_DATABASE_FILE, strerror(errno));
503
504                 mapping_mtime = 0;
505                 return 1;
506         } else {
507                 printerr(LL_WARN,
508                          "Use of idmap.conf is deprecated.\nPlease consider switching to auth_to_local or equivalent as provided by Kerberos for cross-realm trust remapping.\n");
509         }
510
511         if (st.st_mtime != mapping_mtime) {
512                 mapping_mtime = st.st_mtime;
513                 return 1;
514         }
515
516         return 0;
517 }
518
519 void load_mapping(void)
520 {
521         if (mapping_changed())
522                 (void)read_mapping_db();
523 }
524
525 int mapping_empty(void)
526 {
527         return !mapping.nitems;
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(LL_ERR, "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(LL_WARN, "found mapping: %s ==> %d\n",
549                                  princ, entry->uid);
550                         *uid = entry->uid;
551                         return 0;
552                 }
553         }
554
555         printerr(LL_INFO, "no mapping for %s/%#Lx\n", princ, nid);
556         return -1;
557 }