Whamcloud - gitweb
LU-19098 hsm: don't print progname twice with lhsmtool
[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 /* exclusive startup */
77
78 static struct __sem_s {
79         char           *name;
80         key_t           sem_key;
81         int             sem_id;
82 } sems[2] = {
83         [GSSD_CLI] = { "client",  0x3a92d473, 0 },
84         [GSSD_SVC] = { "server",  0x3b92d473, 0 },
85 };
86
87 void gssd_init_unique(int type)
88 {
89         struct __sem_s *sem = &sems[type];
90         struct sembuf   sembuf;
91
92         assert(type == GSSD_CLI || type == GSSD_SVC);
93
94 again:
95         sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
96         if (sem->sem_id == -1) {
97                 if (errno != EEXIST) {
98                         printerr(LL_ERR, "Create sem: %s\n", strerror(errno));
99                         exit(-1);
100                 }
101
102                 /* already exist. Note there's still a small window racing
103                  * with other processes, due to the stupid semaphore semantics.
104                  */
105                 sem->sem_id = semget(sem->sem_key, 0, 0700);
106                 if (sem->sem_id == -1) {
107                         if (errno == ENOENT) {
108                                 printerr(LL_ERR,
109                                          "another instance just exit, try again\n");
110                                 goto again;
111                         }
112
113                         printerr(LL_ERR, "Obtain sem: %s\n", strerror(errno));
114                         exit(-1);
115                 }
116         } else {
117                 int val = 1;
118
119                 if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
120                         printerr(LL_ERR, "Initialize sem: %s\n",
121                                  strerror(errno));
122                         exit(-1);
123                 }
124         }
125
126         sembuf.sem_num = 0;
127         sembuf.sem_op = -1;
128         sembuf.sem_flg = IPC_NOWAIT | SEM_UNDO;
129
130         if (semop(sem->sem_id, &sembuf, 1) != 0) {
131                 if (errno == EAGAIN) {
132                         printerr(LL_ERR, "Another instance is running, exit\n");
133                         exit(0);
134                 }
135                 printerr(LL_ERR, "Grab sem: %s\n", strerror(errno));
136                 exit(0);
137         }
138
139         printerr(LL_INFO, "Successfully created %s global identity\n",
140                  sem->name);
141 }
142
143 void gssd_exit_unique(int type)
144 {
145         assert(type == GSSD_CLI || type == GSSD_SVC);
146
147         /*
148          * do nothing. we can't remove the sem here, otherwise the race
149          * window would be much bigger. So it's sad we have to leave the
150          * sem in the system forever.
151          */
152 }
153
154 /****************************************
155  * client side resolvation:             *
156  *    lnd/netid/nid => hostname         *
157  ****************************************/
158
159 char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
160
161 typedef int lnd_nid2hostname_t(char *lnd, uint32_t net, uint32_t addr,
162                                char *buf, int buflen);
163
164 int getcanonname(const char *host, char *buf, int buflen)
165 {
166         struct addrinfo hints;
167         struct addrinfo *ai = NULL;
168         struct addrinfo *aip = NULL;
169         int err = 0;
170         int rc = 0;
171
172         if (!host || host[0] == '\0') {
173                 printerr(LL_ERR,
174                          "network address or hostname was not specified\n");
175                 return -1;
176         }
177
178         if (!buf) {
179                 printerr(LL_ERR,
180                          "canonical name buffer was not defined\n");
181                 return -1;
182         }
183
184         if (buflen <= 0) {
185                 printerr(LL_ERR,
186                          "invalid canonical name buffer length: %d\n", buflen);
187                 return -1;
188         }
189
190         memset(&hints, 0, sizeof(struct addrinfo));
191         hints.ai_family = AF_INET;
192         hints.ai_flags = AI_CANONNAME;
193
194         err = getaddrinfo(host, NULL, &hints, &ai);
195         if (err != 0) {
196                 printerr(LL_ERR,
197                          "failed to get addrinfo for %s: %s\n",
198                          host, gai_strerror(err));
199                 return -1;
200         }
201
202         for (aip = ai; aip; aip = aip->ai_next) {
203                 if (aip->ai_canonname && aip->ai_canonname[0] != '\0')
204                         break;
205         }
206
207         if (!aip) {
208                 printerr(LL_ERR, "failed to get canonical name of %s\n", host);
209                 rc = -1;
210                 goto out;
211         }
212
213         if (strlen(aip->ai_canonname) >= buflen) {
214                 printerr(LL_ERR, "canonical name is too long: %s\n",
215                          aip->ai_canonname);
216                 rc = -1;
217                 goto out;
218         }
219
220         strncpy(buf, aip->ai_canonname, buflen);
221
222 out:
223         if (ai != NULL)
224                 freeaddrinfo(ai);
225         return rc;
226 }
227
228 static int getaddrcanonname(const uint32_t addr, char *buf, int buflen)
229 {
230         struct sockaddr_in srcaddr;
231         char ipstr[INET_ADDRSTRLEN] = "\0";
232         int err = 0;
233         int rc = -1;
234
235         if (!buf) {
236                 printerr(LL_ERR,
237                          "canonical name buffer was not defined\n");
238                 goto out;
239         }
240
241         if (buflen <= 0) {
242                 printerr(LL_ERR,
243                          "invalid canonical name buffer length: %d\n", buflen);
244                 goto out;
245         }
246
247         memset(&srcaddr, 0, sizeof(srcaddr));
248         srcaddr.sin_family = AF_INET;
249         srcaddr.sin_addr.s_addr = (in_addr_t)addr;
250
251         err = getnameinfo((struct sockaddr *)&srcaddr, sizeof(srcaddr),
252                           buf, buflen, NULL, 0, NI_NAMEREQD);
253         if (err != 0) {
254                 if (inet_ntop(srcaddr.sin_family, &srcaddr.sin_addr, ipstr,
255                               INET_ADDRSTRLEN))
256                         printerr(LL_ERR, "failed to get name for %s: %s\n",
257                                  ipstr, gai_strerror(err));
258                 else
259                         printerr(LL_ERR, "failed to get name for 0x%x: %s\n",
260                                  addr, gai_strerror(err));
261                 goto out;
262         }
263
264         rc = 0;
265
266 out:
267         return rc;
268 }
269
270 /* FIXME what about IPv6? */
271 static
272 int ipv4_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
273                       char *buf, int buflen)
274 {
275         addr = htonl(addr);
276
277         if (getaddrcanonname(addr, buf, buflen) != 0) {
278                 printerr(LL_ERR, "%s: failed to get canonical name of 0x%x\n",
279                          lnd, addr);
280                 return -1;
281         }
282
283         printerr(LL_INFO, "%s: net 0x%x, addr 0x%x => %s\n",
284                  lnd, net, addr, buf);
285         return 0;
286 }
287
288 static
289 int lolnd_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
290                        char *buf, int buflen)
291 {
292         struct utsname uts;
293
294         if (addr) {
295                 printerr(LL_ERR, "%s: addr is 0x%x, we expect 0\n", lnd, addr);
296                 return -1;
297         }
298
299         if (uname(&uts)) {
300                 printerr(LL_ERR, "%s: failed obtain local machine name\n", lnd);
301                 return -1;
302         }
303
304         if (getcanonname(uts.nodename, buf, buflen) != 0) {
305                 printerr(LL_ERR, "%s: failed to obtain canonical name of %s\n",
306                          lnd, uts.nodename);
307                 return -1;
308         }
309
310         printerr(LL_DEBUG, "%s: addr 0x%x => %s\n", lnd, addr, buf);
311         return 0;
312 }
313
314 static int is_space(char c)
315 {
316         return (c == ' ' || c == '\t' || c == '\n');
317 }
318
319 static
320 int external_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
321                           char *namebuf, int namebuflen)
322 {
323         const int bufsize = PATH_MAX + 256;
324         char buf[bufsize], *head, *tail;
325         FILE *fghn;
326
327         sprintf(buf, "%s %s 0x%x 0x%x", gethostname_ex, lnd, net, addr);
328         printerr(LL_INFO, "cmd: %s\n", buf);
329
330         fghn = popen(buf, "r");
331         if (fghn == NULL) {
332                 printerr(LL_ERR, "failed to call %s\n", gethostname_ex);
333                 return -1;
334         }
335
336         head = fgets(buf, bufsize, fghn);
337         if (head == NULL) {
338                 printerr(LL_ERR, "can't read from %s\n", gethostname_ex);
339                 pclose(fghn);
340                 return -1;
341         }
342         if (pclose(fghn) == -1)
343                 printerr(LL_WARN, "pclose failed, continue\n");
344
345         /* trim head/tail space */
346         while (is_space(*head))
347                 head++;
348
349         tail = head + strlen(head);
350         if (tail <= head) {
351                 printerr(LL_ERR, "no output from %s\n", gethostname_ex);
352                 return -1;
353         }
354         while (is_space(*(tail - 1)))
355                 tail--;
356         if (tail <= head) {
357                 printerr(LL_ERR, "output are all space from %s\n",
358                          gethostname_ex);
359                 return -1;
360         }
361         *tail = '\0';
362
363         /* start with '@' means error msg */
364         if (head[0] == '@') {
365                 printerr(LL_ERR, "error from %s: %s\n",
366                          gethostname_ex, &head[1]);
367                 return -1;
368         }
369
370         if (tail - head > namebuflen) {
371                 printerr(LL_ERR, "external hostname too long: %s\n", head);
372                 return -1;
373         }
374
375         printerr(LL_INFO, "%s: net 0x%x, addr 0x%x => %s\n",
376                  lnd, net, addr, head);
377         strcpy(namebuf, head);
378         return 0;
379 }
380
381 struct convert_struct {
382         char                    *name;
383         lnd_nid2hostname_t      *nid2name;
384 };
385
386 static struct convert_struct converter[] = {
387         [0]       = { .name = "UNUSED0" },
388         [SOCKLND] = { .name = "SOCKLND", .nid2name = ipv4_nid2hostname },
389         [O2IBLND] = { .name = "O2IBLND", .nid2name = ipv4_nid2hostname },
390         [LOLND]   = { .name = "LOLND",   .nid2name = lolnd_nid2hostname },
391         [PTL4LND] = { .name = "PTL4LND", .nid2name = external_nid2hostname },
392         [KFILND]  = { .name = "KFILND",  .nid2name = ipv4_nid2hostname }
393 };
394
395 #define LND_MAX         (sizeof(converter) / sizeof(converter[0]))
396
397 int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen)
398 {
399         uint32_t lnd, net, addr;
400
401         addr = LNET_NIDADDR(nid);
402         net = LNET_NIDNET(nid);
403         lnd = LNET_NETTYP(net);
404
405         if (lnd >= LND_MAX) {
406                 printerr(LL_ERR, "ERROR: Unrecognized LND %u\n", lnd);
407                 return -1;
408         }
409
410         if (converter[lnd].nid2name == NULL) {
411                 printerr(LL_ERR, "ERROR: %s converter not ready\n",
412                         converter[lnd].name);
413                 return -1;
414         }
415
416         return converter[lnd].nid2name(converter[lnd].name, net, addr,
417                                        buf, buflen);
418 }
419
420 uid_t parse_uid(char *uidstr)
421 {
422         struct passwd *pw;
423         char *p = NULL;
424         long uid;
425
426         pw = getpwnam(uidstr);
427         if (pw)
428                 return pw->pw_uid;
429
430         uid = strtol(uidstr, &p, 0);
431         if (*p == '\0')
432                 return (uid_t) uid;
433
434         return -1;
435 }
436
437 /* realm of this node */
438 char *krb5_this_realm;
439
440 static int gss_get_provided_realm(char *realm)
441 {
442         if (krb5_this_realm)
443                 return 0;
444
445         if (!realm)
446                 return -1;
447
448         krb5_this_realm = strdup(realm);
449         return 0;
450 }
451
452 static int gss_get_local_realm(void)
453 {
454         krb5_context context = NULL;
455         krb5_error_code code;
456
457         if (krb5_this_realm != NULL)
458                 return 0;
459
460         code = krb5_init_context(&context);
461         if (code)
462                 return code;
463
464         code = krb5_get_default_realm(context, &krb5_this_realm);
465         krb5_free_context(context);
466
467         if (code)
468                 return code;
469
470         return 0;
471 }
472
473 int gss_get_realm(char *realm)
474 {
475         int rc;
476
477         /* Try to use provided realm first.
478          * If no provided realm, get default local realm.
479          */
480         rc = gss_get_provided_realm(realm);
481         if (rc)
482                 rc = gss_get_local_realm();
483
484         return rc;
485 }