]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/gssd_proc.c
Merge branch 'sid'
[nfs-utils.git] / utils / gssd / gssd_proc.c
1 /*
2   gssd_proc.c
3
4   Copyright (c) 2000-2004 The Regents of the University of Michigan.
5   All rights reserved.
6
7   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
8   Copyright (c) 2001 Andy Adamson <andros@UMICH.EDU>.
9   Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
10   Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
11   Copyright (c) 2004 Kevin Coffman <kwc@umich.edu>
12   All rights reserved, all wrongs reversed.
13
14   Redistribution and use in source and binary forms, with or without
15   modification, are permitted provided that the following conditions
16   are met:
17
18   1. Redistributions of source code must retain the above copyright
19      notice, this list of conditions and the following disclaimer.
20   2. Redistributions in binary form must reproduce the above copyright
21      notice, this list of conditions and the following disclaimer in the
22      documentation and/or other materials provided with the distribution.
23   3. Neither the name of the University nor the names of its
24      contributors may be used to endorse or promote products derived
25      from this software without specific prior written permission.
26
27   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
28   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
34   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39 */
40
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif  /* HAVE_CONFIG_H */
44
45 #ifndef _GNU_SOURCE
46 #define _GNU_SOURCE
47 #endif
48
49 #include <sys/param.h>
50 #include <rpc/rpc.h>
51 #include <sys/stat.h>
52 #include <sys/socket.h>
53 #include <arpa/inet.h>
54 #include <sys/fsuid.h>
55 #include <sys/resource.h>
56
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <pwd.h>
60 #include <grp.h>
61 #include <string.h>
62 #include <dirent.h>
63 #include <poll.h>
64 #include <fcntl.h>
65 #include <signal.h>
66 #include <unistd.h>
67 #include <errno.h>
68 #include <gssapi/gssapi.h>
69 #include <netdb.h>
70 #include <ctype.h>
71
72 #include "gssd.h"
73 #include "err_util.h"
74 #include "gss_util.h"
75 #include "krb5_util.h"
76 #include "context.h"
77 #include "nfsrpc.h"
78 #include "nfslib.h"
79
80 /*
81  * pollarray:
82  *      array of struct pollfd suitable to pass to poll. initialized to
83  *      zero - a zero struct is ignored by poll() because the events mask is 0.
84  *
85  * clnt_list:
86  *      linked list of struct clnt_info which associates a clntXXX directory
87  *      with an index into pollarray[], and other basic data about that client.
88  *
89  * Directory structure: created by the kernel
90  *      {rpc_pipefs}/{dir}/clntXX         : one per rpc_clnt struct in the kernel
91  *      {rpc_pipefs}/{dir}/clntXX/krb5    : read uid for which kernel wants
92  *                                          a context, write the resulting context
93  *      {rpc_pipefs}/{dir}/clntXX/info    : stores info such as server name
94  *      {rpc_pipefs}/{dir}/clntXX/gssd    : pipe for all gss mechanisms using
95  *                                          a text-based string of parameters
96  *
97  * Algorithm:
98  *      Poll all {rpc_pipefs}/{dir}/clntXX/YYYY files.  When data is ready,
99  *      read and process; performs rpcsec_gss context initialization protocol to
100  *      get a cred for that user.  Writes result to corresponding krb5 file
101  *      in a form the kernel code will understand.
102  *      In addition, we make sure we are notified whenever anything is
103  *      created or destroyed in {rpc_pipefs} or in any of the clntXX directories,
104  *      and rescan the whole {rpc_pipefs} when this happens.
105  */
106
107 struct pollfd * pollarray;
108
109 unsigned long pollsize;  /* the size of pollaray (in pollfd's) */
110
111 /* Avoid DNS reverse lookups on server names */
112 int avoid_dns = 1;
113
114 /*
115  * convert a presentation address string to a sockaddr_storage struct. Returns
116  * true on success or false on failure.
117  *
118  * Note that we do not populate the sin6_scope_id field here for IPv6 addrs.
119  * gssd nececessarily relies on hostname resolution and DNS AAAA records
120  * do not generally contain scope-id's. This means that GSSAPI auth really
121  * can't work with IPv6 link-local addresses.
122  *
123  * We *could* consider changing this if we did something like adopt the
124  * Microsoft "standard" of using the ipv6-literal.net domainname, but it's
125  * not really feasible at present.
126  */
127 static int
128 addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port)
129 {
130         int rc;
131         struct addrinfo *res;
132         struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV };
133
134 #ifndef IPV6_SUPPORTED
135         hints.ai_family = AF_INET;
136 #endif /* IPV6_SUPPORTED */
137
138         rc = getaddrinfo(node, port, &hints, &res);
139         if (rc) {
140                 printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n",
141                          node, port, rc == EAI_SYSTEM ? strerror(errno) :
142                                                 gai_strerror(rc));
143                 return 0;
144         }
145
146 #ifdef IPV6_SUPPORTED
147         /*
148          * getnameinfo ignores the scopeid. If the address turns out to have
149          * a non-zero scopeid, we can't use it -- the resolved host might be
150          * completely different from the one intended.
151          */
152         if (res->ai_addr->sa_family == AF_INET6) {
153                 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr;
154                 if (sin6->sin6_scope_id) {
155                         printerr(0, "ERROR: address %s has non-zero "
156                                     "sin6_scope_id!\n", node);
157                         freeaddrinfo(res);
158                         return 0;
159                 }
160         }
161 #endif /* IPV6_SUPPORTED */
162
163         memcpy(sa, res->ai_addr, res->ai_addrlen);
164         freeaddrinfo(res);
165         return 1;
166 }
167
168 /*
169  * convert a sockaddr to a hostname
170  */
171 static char *
172 get_servername(const char *name, const struct sockaddr *sa, const char *addr)
173 {
174         socklen_t               addrlen;
175         int                     err;
176         char                    *hostname;
177         char                    hbuf[NI_MAXHOST];
178         unsigned char           buf[sizeof(struct in6_addr)];
179         int                     servername = 0;
180
181         if (avoid_dns) {
182                 /*
183                  * Determine if this is a server name, or an IP address.
184                  * If it is an IP address, do the DNS lookup otherwise
185                  * skip the DNS lookup.
186                  */
187                 servername = 0;
188                 if (strchr(name, '.') && inet_pton(AF_INET, name, buf) == 1)
189                         servername = 1; /* IPv4 */
190                 else if (strchr(name, ':') && inet_pton(AF_INET6, name, buf) == 1)
191                         servername = 1; /* or IPv6 */
192
193                 if (servername) {
194                         return strdup(name);
195                 }
196         }
197
198         switch (sa->sa_family) {
199         case AF_INET:
200                 addrlen = sizeof(struct sockaddr_in);
201                 break;
202 #ifdef IPV6_SUPPORTED
203         case AF_INET6:
204                 addrlen = sizeof(struct sockaddr_in6);
205                 break;
206 #endif /* IPV6_SUPPORTED */
207         default:
208                 printerr(0, "ERROR: unrecognized addr family %d\n",
209                          sa->sa_family);
210                 return NULL;
211         }
212
213         err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0,
214                           NI_NAMEREQD);
215         if (err) {
216                 printerr(0, "ERROR: unable to resolve %s to hostname: %s\n",
217                          addr, err == EAI_SYSTEM ? strerror(err) :
218                                                    gai_strerror(err));
219                 return NULL;
220         }
221
222         hostname = strdup(hbuf);
223
224         return hostname;
225 }
226
227 /* XXX buffer problems: */
228 static int
229 read_service_info(char *info_file_name, char **servicename, char **servername,
230                   int *prog, int *vers, char **protocol,
231                   struct sockaddr *addr) {
232 #define INFOBUFLEN 256
233         char            buf[INFOBUFLEN + 1];
234         static char     server[128];
235         int             nbytes;
236         static char     service[128];
237         static char     address[128];
238         char            program[16];
239         char            version[16];
240         char            protoname[16];
241         char            port[128];
242         char            *p;
243         int             fd = -1;
244         int             numfields;
245
246         *servicename = *servername = *protocol = NULL;
247
248         if ((fd = open(info_file_name, O_RDONLY)) == -1) {
249                 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
250                          strerror(errno));
251                 goto fail;
252         }
253         if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
254                 goto fail;
255         close(fd);
256         buf[nbytes] = '\0';
257
258         numfields = sscanf(buf,"RPC server: %127s\n"
259                    "service: %127s %15s version %15s\n"
260                    "address: %127s\n"
261                    "protocol: %15s\n",
262                    server,
263                    service, program, version,
264                    address,
265                    protoname);
266
267         if (numfields == 5) {
268                 strcpy(protoname, "tcp");
269         } else if (numfields != 6) {
270                 goto fail;
271         }
272
273         port[0] = '\0';
274         if ((p = strstr(buf, "port")) != NULL)
275                 sscanf(p, "port: %127s\n", port);
276
277         /* get program, and version numbers */
278         *prog = atoi(program + 1); /* skip open paren */
279         *vers = atoi(version);
280
281         if (!addrstr_to_sockaddr(addr, address, port))
282                 goto fail;
283
284         *servername = get_servername(server, addr, address);
285         if (*servername == NULL)
286                 goto fail;
287
288         nbytes = snprintf(buf, INFOBUFLEN, "%s@%s", service, *servername);
289         if (nbytes > INFOBUFLEN)
290                 goto fail;
291
292         if (!(*servicename = calloc(strlen(buf) + 1, 1)))
293                 goto fail;
294         memcpy(*servicename, buf, strlen(buf));
295
296         if (!(*protocol = strdup(protoname)))
297                 goto fail;
298         return 0;
299 fail:
300         printerr(0, "ERROR: failed to read service info\n");
301         if (fd != -1) close(fd);
302         free(*servername);
303         free(*servicename);
304         free(*protocol);
305         *servicename = *servername = *protocol = NULL;
306         return -1;
307 }
308
309 static void
310 destroy_client(struct clnt_info *clp)
311 {
312         if (clp->krb5_poll_index != -1)
313                 memset(&pollarray[clp->krb5_poll_index], 0,
314                                         sizeof(struct pollfd));
315         if (clp->gssd_poll_index != -1)
316                 memset(&pollarray[clp->gssd_poll_index], 0,
317                                         sizeof(struct pollfd));
318         if (clp->dir_fd != -1) close(clp->dir_fd);
319         if (clp->krb5_fd != -1) close(clp->krb5_fd);
320         if (clp->gssd_fd != -1) close(clp->gssd_fd);
321         free(clp->dirname);
322         free(clp->servicename);
323         free(clp->servername);
324         free(clp->protocol);
325         free(clp);
326 }
327
328 static struct clnt_info *
329 insert_new_clnt(void)
330 {
331         struct clnt_info        *clp = NULL;
332
333         if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
334                 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
335                          strerror(errno));
336                 goto out;
337         }
338         clp->krb5_poll_index = -1;
339         clp->gssd_poll_index = -1;
340         clp->krb5_fd = -1;
341         clp->gssd_fd = -1;
342         clp->dir_fd = -1;
343
344         TAILQ_INSERT_HEAD(&clnt_list, clp, list);
345 out:
346         return clp;
347 }
348
349 static int
350 process_clnt_dir_files(struct clnt_info * clp)
351 {
352         char    name[PATH_MAX];
353         char    gname[PATH_MAX];
354         char    info_file_name[PATH_MAX];
355
356         if (clp->gssd_close_me) {
357                 printerr(2, "Closing 'gssd' pipe for %s\n", clp->dirname);
358                 close(clp->gssd_fd);
359                 memset(&pollarray[clp->gssd_poll_index], 0,
360                         sizeof(struct pollfd));
361                 clp->gssd_fd = -1;
362                 clp->gssd_poll_index = -1;
363                 clp->gssd_close_me = 0;
364         }
365         if (clp->krb5_close_me) {
366                 printerr(2, "Closing 'krb5' pipe for %s\n", clp->dirname);
367                 close(clp->krb5_fd);
368                 memset(&pollarray[clp->krb5_poll_index], 0,
369                         sizeof(struct pollfd));
370                 clp->krb5_fd = -1;
371                 clp->krb5_poll_index = -1;
372                 clp->krb5_close_me = 0;
373         }
374
375         if (clp->gssd_fd == -1) {
376                 snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname);
377                 clp->gssd_fd = open(gname, O_RDWR);
378         }
379         if (clp->gssd_fd == -1) {
380                 if (clp->krb5_fd == -1) {
381                         snprintf(name, sizeof(name), "%s/krb5", clp->dirname);
382                         clp->krb5_fd = open(name, O_RDWR);
383                 }
384
385                 /* If we opened a gss-specific pipe, let's try opening
386                  * the new upcall pipe again. If we succeed, close
387                  * gss-specific pipe(s).
388                  */
389                 if (clp->krb5_fd != -1) {
390                         clp->gssd_fd = open(gname, O_RDWR);
391                         if (clp->gssd_fd != -1) {
392                                 if (clp->krb5_fd != -1)
393                                         close(clp->krb5_fd);
394                                 clp->krb5_fd = -1;
395                         }
396                 }
397         }
398
399         if ((clp->krb5_fd == -1) && (clp->gssd_fd == -1))
400                 return -1;
401         snprintf(info_file_name, sizeof(info_file_name), "%s/info",
402                         clp->dirname);
403         if ((clp->servicename == NULL) &&
404              read_service_info(info_file_name, &clp->servicename,
405                                 &clp->servername, &clp->prog, &clp->vers,
406                                 &clp->protocol, (struct sockaddr *) &clp->addr))
407                 return -1;
408         return 0;
409 }
410
411 static int
412 get_poll_index(int *ind)
413 {
414         unsigned int i;
415
416         *ind = -1;
417         for (i=0; i<pollsize; i++) {
418                 if (pollarray[i].events == 0) {
419                         *ind = i;
420                         break;
421                 }
422         }
423         if (*ind == -1) {
424                 printerr(0, "ERROR: No pollarray slots open\n");
425                 return -1;
426         }
427         return 0;
428 }
429
430
431 static int
432 insert_clnt_poll(struct clnt_info *clp)
433 {
434         if ((clp->gssd_fd != -1) && (clp->gssd_poll_index == -1)) {
435                 if (get_poll_index(&clp->gssd_poll_index)) {
436                         printerr(0, "ERROR: Too many gssd clients\n");
437                         return -1;
438                 }
439                 pollarray[clp->gssd_poll_index].fd = clp->gssd_fd;
440                 pollarray[clp->gssd_poll_index].events |= POLLIN;
441         }
442
443         if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
444                 if (get_poll_index(&clp->krb5_poll_index)) {
445                         printerr(0, "ERROR: Too many krb5 clients\n");
446                         return -1;
447                 }
448                 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
449                 pollarray[clp->krb5_poll_index].events |= POLLIN;
450         }
451
452         return 0;
453 }
454
455 static void
456 process_clnt_dir(char *dir, char *pdir)
457 {
458         struct clnt_info *      clp;
459
460         if (!(clp = insert_new_clnt()))
461                 goto fail_destroy_client;
462
463         /* An extra for the '/', and an extra for the null */
464         if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) {
465                 goto fail_destroy_client;
466         }
467         sprintf(clp->dirname, "%s/%s", pdir, dir);
468         if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
469                 printerr(0, "ERROR: can't open %s: %s\n",
470                          clp->dirname, strerror(errno));
471                 goto fail_destroy_client;
472         }
473         fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
474         fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
475
476         if (process_clnt_dir_files(clp))
477                 goto fail_keep_client;
478
479         if (insert_clnt_poll(clp))
480                 goto fail_destroy_client;
481
482         return;
483
484 fail_destroy_client:
485         if (clp) {
486                 TAILQ_REMOVE(&clnt_list, clp, list);
487                 destroy_client(clp);
488         }
489 fail_keep_client:
490         /* We couldn't find some subdirectories, but we keep the client
491          * around in case we get a notification on the directory when the
492          * subdirectories are created. */
493         return;
494 }
495
496 void
497 init_client_list(void)
498 {
499         struct rlimit rlim;
500         TAILQ_INIT(&clnt_list);
501         /* Eventually plan to grow/shrink poll array: */
502         pollsize = FD_ALLOC_BLOCK;
503         if (getrlimit(RLIMIT_NOFILE, &rlim) == 0 &&
504             rlim.rlim_cur != RLIM_INFINITY)
505                 pollsize = rlim.rlim_cur;
506         pollarray = calloc(pollsize, sizeof(struct pollfd));
507 }
508
509 /*
510  * This is run after a DNOTIFY signal, and should clear up any
511  * directories that are no longer around, and re-scan any existing
512  * directories, since the DNOTIFY could have been in there.
513  */
514 static void
515 update_old_clients(struct dirent **namelist, int size, char *pdir)
516 {
517         struct clnt_info *clp;
518         void *saveprev;
519         int i, stillhere;
520         char fname[PATH_MAX];
521
522         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
523                 /* only compare entries in the global list that are from the
524                  * same pipefs parent directory as "pdir"
525                  */
526                 if (strncmp(clp->dirname, pdir, strlen(pdir)) != 0) continue;
527
528                 stillhere = 0;
529                 for (i=0; i < size; i++) {
530                         snprintf(fname, sizeof(fname), "%s/%s",
531                                  pdir, namelist[i]->d_name);
532                         if (strcmp(clp->dirname, fname) == 0) {
533                                 stillhere = 1;
534                                 break;
535                         }
536                 }
537                 if (!stillhere) {
538                         printerr(2, "destroying client %s\n", clp->dirname);
539                         saveprev = clp->list.tqe_prev;
540                         TAILQ_REMOVE(&clnt_list, clp, list);
541                         destroy_client(clp);
542                         clp = saveprev;
543                 }
544         }
545         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
546                 if (!process_clnt_dir_files(clp))
547                         insert_clnt_poll(clp);
548         }
549 }
550
551 /* Search for a client by directory name, return 1 if found, 0 otherwise */
552 static int
553 find_client(char *dirname, char *pdir)
554 {
555         struct clnt_info        *clp;
556         char fname[PATH_MAX];
557
558         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
559                 snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname);
560                 if (strcmp(clp->dirname, fname) == 0)
561                         return 1;
562         }
563         return 0;
564 }
565
566 static int
567 process_pipedir(char *pipe_name)
568 {
569         struct dirent **namelist;
570         int i, j;
571
572         if (chdir(pipe_name) < 0) {
573                 printerr(0, "ERROR: can't chdir to %s: %s\n",
574                          pipe_name, strerror(errno));
575                 return -1;
576         }
577
578         j = scandir(pipe_name, &namelist, NULL, alphasort);
579         if (j < 0) {
580                 printerr(0, "ERROR: can't scandir %s: %s\n",
581                          pipe_name, strerror(errno));
582                 return -1;
583         }
584
585         update_old_clients(namelist, j, pipe_name);
586         for (i=0; i < j; i++) {
587                 if (!strncmp(namelist[i]->d_name, "clnt", 4)
588                     && !find_client(namelist[i]->d_name, pipe_name))
589                         process_clnt_dir(namelist[i]->d_name, pipe_name);
590                 free(namelist[i]);
591         }
592
593         free(namelist);
594
595         return 0;
596 }
597
598 /* Used to read (and re-read) list of clients, set up poll array. */
599 int
600 update_client_list(void)
601 {
602         int retval = -1;
603         struct topdirs_info *tdi;
604
605         TAILQ_FOREACH(tdi, &topdirs_list, list) {
606                 retval = process_pipedir(tdi->dirname);
607                 if (retval)
608                         printerr(1, "WARNING: error processing %s\n",
609                                  tdi->dirname);
610
611         }
612         return retval;
613 }
614
615 /* Encryption types supported by the kernel rpcsec_gss code */
616 int num_krb5_enctypes = 0;
617 krb5_enctype *krb5_enctypes = NULL;
618
619 /*
620  * Parse the supported encryption type information
621  */
622 static int
623 parse_enctypes(char *enctypes)
624 {
625         int n = 0;
626         char *curr, *comma;
627         int i;
628         static char *cached_types;
629
630         if (cached_types && strcmp(cached_types, enctypes) == 0)
631                 return 0;
632         free(cached_types);
633
634         if (krb5_enctypes != NULL) {
635                 free(krb5_enctypes);
636                 krb5_enctypes = NULL;
637                 num_krb5_enctypes = 0;
638         }
639
640         /* count the number of commas */
641         for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
642                 comma = strchr(curr, ',');
643                 if (comma != NULL)
644                         n++;
645                 else
646                         break;
647         }
648         /* If no more commas and we're not at the end, there's one more value */
649         if (*curr != '\0')
650                 n++;
651
652         /* Empty string, return an error */
653         if (n == 0)
654                 return ENOENT;
655
656         /* Allocate space for enctypes array */
657         if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) {
658                 return ENOMEM;
659         }
660
661         /* Now parse each value into the array */
662         for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) {
663                 krb5_enctypes[i++] = atoi(curr);
664                 comma = strchr(curr, ',');
665                 if (comma == NULL)
666                         break;
667         }
668
669         num_krb5_enctypes = n;
670         if ((cached_types = malloc(strlen(enctypes)+1)))
671                 strcpy(cached_types, enctypes);
672
673         return 0;
674 }
675
676 static int
677 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
678             gss_buffer_desc *context_token, OM_uint32 lifetime_rec)
679 {
680         char    *buf = NULL, *p = NULL, *end = NULL;
681         unsigned int timeout = context_timeout;
682         unsigned int buf_size = 0;
683
684         printerr(1, "doing downcall lifetime_rec %u\n", lifetime_rec);
685         buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
686                 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
687                 sizeof(context_token->length) + context_token->length;
688         p = buf = malloc(buf_size);
689         end = buf + buf_size;
690
691         /* context_timeout set by -t option overrides context lifetime */
692         if (timeout == 0)
693                 timeout = lifetime_rec;
694         if (WRITE_BYTES(&p, end, uid)) goto out_err;
695         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
696         if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
697         if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
698         if (write_buffer(&p, end, context_token)) goto out_err;
699
700         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
701         if (buf) free(buf);
702         return 0;
703 out_err:
704         if (buf) free(buf);
705         printerr(1, "Failed to write downcall!\n");
706         return -1;
707 }
708
709 static int
710 do_error_downcall(int k5_fd, uid_t uid, int err)
711 {
712         char    buf[1024];
713         char    *p = buf, *end = buf + 1024;
714         unsigned int timeout = 0;
715         int     zero = 0;
716
717         printerr(1, "doing error downcall\n");
718
719         if (WRITE_BYTES(&p, end, uid)) goto out_err;
720         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
721         /* use seq_win = 0 to indicate an error: */
722         if (WRITE_BYTES(&p, end, zero)) goto out_err;
723         if (WRITE_BYTES(&p, end, err)) goto out_err;
724
725         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
726         return 0;
727 out_err:
728         printerr(1, "Failed to write error downcall!\n");
729         return -1;
730 }
731
732 /*
733  * If the port isn't already set, do an rpcbind query to the remote server
734  * using the program and version and get the port.
735  *
736  * Newer kernels send the value of the port= mount option in the "info"
737  * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
738  * of the port= option or '2049'. The port field in a new sockaddr should
739  * reflect the value that was sent by the kernel.
740  */
741 static int
742 populate_port(struct sockaddr *sa, const socklen_t salen,
743               const rpcprog_t program, const rpcvers_t version,
744               const unsigned short protocol)
745 {
746         struct sockaddr_in      *s4 = (struct sockaddr_in *) sa;
747 #ifdef IPV6_SUPPORTED
748         struct sockaddr_in6     *s6 = (struct sockaddr_in6 *) sa;
749 #endif /* IPV6_SUPPORTED */
750         unsigned short          port;
751
752         /*
753          * Newer kernels send the port in the upcall. If we already have
754          * the port, there's no need to look it up.
755          */
756         switch (sa->sa_family) {
757         case AF_INET:
758                 if (s4->sin_port != 0) {
759                         printerr(2, "DEBUG: port already set to %d\n",
760                                  ntohs(s4->sin_port));
761                         return 1;
762                 }
763                 break;
764 #ifdef IPV6_SUPPORTED
765         case AF_INET6:
766                 if (s6->sin6_port != 0) {
767                         printerr(2, "DEBUG: port already set to %d\n",
768                                  ntohs(s6->sin6_port));
769                         return 1;
770                 }
771                 break;
772 #endif /* IPV6_SUPPORTED */
773         default:
774                 printerr(0, "ERROR: unsupported address family %d\n",
775                             sa->sa_family);
776                 return 0;
777         }
778
779         /*
780          * Newer kernels that send the port in the upcall set the value to
781          * 2049 for NFSv4 mounts when one isn't specified. The check below is
782          * only for kernels that don't send the port in the upcall. For those
783          * we either have to do an rpcbind query or set it to the standard
784          * port. Doing a query could be problematic (firewalls, etc), so take
785          * the latter approach.
786          */
787         if (program == 100003 && version == 4) {
788                 port = 2049;
789                 goto set_port;
790         }
791
792         port = nfs_getport(sa, salen, program, version, protocol);
793         if (!port) {
794                 printerr(0, "ERROR: unable to obtain port for prog %ld "
795                             "vers %ld\n", program, version);
796                 return 0;
797         }
798
799 set_port:
800         printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port,
801                  program, version);
802
803         switch (sa->sa_family) {
804         case AF_INET:
805                 s4->sin_port = htons(port);
806                 break;
807 #ifdef IPV6_SUPPORTED
808         case AF_INET6:
809                 s6->sin6_port = htons(port);
810                 break;
811 #endif /* IPV6_SUPPORTED */
812         }
813
814         return 1;
815 }
816
817 /*
818  * Create an RPC connection and establish an authenticated
819  * gss context with a server.
820  */
821 static int
822 create_auth_rpc_client(struct clnt_info *clp,
823                        CLIENT **clnt_return,
824                        AUTH **auth_return,
825                        uid_t uid,
826                        int authtype,
827                        gss_cred_id_t cred)
828 {
829         CLIENT                  *rpc_clnt = NULL;
830         struct rpc_gss_sec      sec;
831         AUTH                    *auth = NULL;
832         uid_t                   save_uid = -1;
833         int                     retval = -1;
834         OM_uint32               min_stat;
835         char                    rpc_errmsg[1024];
836         int                     protocol;
837         struct timeval          timeout = {5, 0};
838         struct sockaddr         *addr = (struct sockaddr *) &clp->addr;
839         socklen_t               salen;
840
841         /* Create the context as the user (not as root) */
842         save_uid = geteuid();
843         if (setfsuid(uid) != 0) {
844                 printerr(0, "WARNING: Failed to setfsuid for "
845                             "user with uid %d\n", uid);
846                 goto out_fail;
847         }
848         printerr(2, "creating context using fsuid %d (save_uid %d)\n",
849                         uid, save_uid);
850
851         sec.qop = GSS_C_QOP_DEFAULT;
852         sec.svc = RPCSEC_GSS_SVC_NONE;
853         sec.cred = cred;
854         sec.req_flags = 0;
855         if (authtype == AUTHTYPE_KRB5) {
856                 sec.mech = (gss_OID)&krb5oid;
857                 sec.req_flags = GSS_C_MUTUAL_FLAG;
858         }
859         else {
860                 printerr(0, "ERROR: Invalid authentication type (%d) "
861                         "in create_auth_rpc_client\n", authtype);
862                 goto out_fail;
863         }
864
865
866         if (authtype == AUTHTYPE_KRB5) {
867 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
868                 /*
869                  * Do this before creating rpc connection since we won't need
870                  * rpc connection if it fails!
871                  */
872                 if (limit_krb5_enctypes(&sec)) {
873                         printerr(1, "WARNING: Failed while limiting krb5 "
874                                     "encryption types for user with uid %d\n",
875                                  uid);
876                         goto out_fail;
877                 }
878 #endif
879         }
880
881         /* create an rpc connection to the nfs server */
882
883         printerr(2, "creating %s client for server %s\n", clp->protocol,
884                         clp->servername);
885
886         if ((strcmp(clp->protocol, "tcp")) == 0) {
887                 protocol = IPPROTO_TCP;
888         } else if ((strcmp(clp->protocol, "udp")) == 0) {
889                 protocol = IPPROTO_UDP;
890         } else {
891                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
892                          "for connection to server %s for user with uid %d\n",
893                          clp->protocol, clp->servername, uid);
894                 goto out_fail;
895         }
896
897         switch (addr->sa_family) {
898         case AF_INET:
899                 salen = sizeof(struct sockaddr_in);
900                 break;
901 #ifdef IPV6_SUPPORTED
902         case AF_INET6:
903                 salen = sizeof(struct sockaddr_in6);
904                 break;
905 #endif /* IPV6_SUPPORTED */
906         default:
907                 printerr(1, "ERROR: Unknown address family %d\n",
908                          addr->sa_family);
909                 goto out_fail;
910         }
911
912         if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
913                 goto out_fail;
914
915         rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
916                                      clp->vers, &timeout);
917         if (!rpc_clnt) {
918                 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
919                          "WARNING: can't create %s rpc_clnt to server %s for "
920                          "user with uid %d",
921                          protocol == IPPROTO_TCP ? "tcp" : "udp",
922                          clp->servername, uid);
923                 printerr(0, "%s\n",
924                          clnt_spcreateerror(rpc_errmsg));
925                 goto out_fail;
926         }
927
928         printerr(2, "creating context with server %s\n", clp->servicename);
929         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
930         if (!auth) {
931                 /* Our caller should print appropriate message */
932                 printerr(2, "WARNING: Failed to create krb5 context for "
933                             "user with uid %d for server %s\n",
934                          uid, clp->servername);
935                 goto out_fail;
936         }
937
938         /* Success !!! */
939         rpc_clnt->cl_auth = auth;
940         *clnt_return = rpc_clnt;
941         *auth_return = auth;
942         retval = 0;
943
944   out:
945         if (sec.cred != GSS_C_NO_CREDENTIAL)
946                 gss_release_cred(&min_stat, &sec.cred);
947         /* Restore euid to original value */
948         if (((int)save_uid != -1) && (setfsuid(save_uid) != (int)uid)) {
949                 printerr(0, "WARNING: Failed to restore fsuid"
950                             " to uid %d from %d\n", save_uid, uid);
951         }
952         return retval;
953
954   out_fail:
955         /* Only destroy here if failure.  Otherwise, caller is responsible */
956         if (rpc_clnt) clnt_destroy(rpc_clnt);
957
958         goto out;
959 }
960
961 /*
962  * this code uses the userland rpcsec gss library to create a krb5
963  * context on behalf of the kernel
964  */
965 static void
966 process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
967                     char *service)
968 {
969         CLIENT                  *rpc_clnt = NULL;
970         AUTH                    *auth = NULL;
971         struct authgss_private_data pd;
972         gss_buffer_desc         token;
973         char                    **credlist = NULL;
974         char                    **ccname;
975         char                    **dirname;
976         int                     create_resp = -1;
977         int                     err, downcall_err = -EACCES;
978         gss_cred_id_t           gss_cred;
979         OM_uint32               maj_stat, min_stat, lifetime_rec;
980
981         printerr(1, "handling krb5 upcall (%s)\n", clp->dirname);
982
983         token.length = 0;
984         token.value = NULL;
985         memset(&pd, 0, sizeof(struct authgss_private_data));
986
987         /*
988          * If "service" is specified, then the kernel is indicating that
989          * we must use machine credentials for this request.  (Regardless
990          * of the uid value or the setting of root_uses_machine_creds.)
991          * If the service value is "*", then any service name can be used.
992          * Otherwise, it specifies the service name that should be used.
993          * (For now, the values of service will only be "*" or "nfs".)
994          *
995          * Restricting gssd to use "nfs" service name is needed for when
996          * the NFS server is doing a callback to the NFS client.  In this
997          * case, the NFS server has to authenticate itself as "nfs" --
998          * even if there are other service keys such as "host" or "root"
999          * in the keytab.
1000          *
1001          * Another case when the kernel may specify the service attribute
1002          * is when gssd is being asked to create the context for a
1003          * SETCLIENT_ID operation.  In this case, machine credentials
1004          * must be used for the authentication.  However, the service name
1005          * used for this case is not important.
1006          *
1007          */
1008         printerr(2, "%s: service is '%s'\n", __func__,
1009                  service ? service : "<null>");
1010         if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
1011                                 service == NULL)) {
1012                 /* Tell krb5 gss which credentials cache to use */
1013                 /* Try first to acquire credentials directly via GSSAPI */
1014                 err = gssd_acquire_user_cred(uid, &gss_cred);
1015                 if (!err)
1016                         create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
1017                                                              AUTHTYPE_KRB5, gss_cred);
1018                 /* if create_auth_rplc_client fails try the traditional method of
1019                  * trolling for credentials */
1020                 for (dirname = ccachesearch; create_resp != 0 && *dirname != NULL; dirname++) {
1021                         err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname);
1022                         if (err == -EKEYEXPIRED)
1023                                 downcall_err = -EKEYEXPIRED;
1024                         else if (!err)
1025                                 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
1026                                                              AUTHTYPE_KRB5, GSS_C_NO_CREDENTIAL);
1027                 }
1028         }
1029         if (create_resp != 0) {
1030                 if (uid == 0 && (root_uses_machine_creds == 1 ||
1031                                 service != NULL)) {
1032                         int nocache = 0;
1033                         int success = 0;
1034                         do {
1035                                 gssd_refresh_krb5_machine_credential(clp->servername,
1036                                                                      NULL, service,
1037                                                                      tgtname);
1038                                 /*
1039                                  * Get a list of credential cache names and try each
1040                                  * of them until one works or we've tried them all
1041                                  */
1042                                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
1043                                         printerr(0, "ERROR: No credentials found "
1044                                                  "for connection to server %s\n",
1045                                                  clp->servername);
1046                                         goto out_return_error;
1047                                 }
1048                                 for (ccname = credlist; ccname && *ccname; ccname++) {
1049                                         gssd_setup_krb5_machine_gss_ccache(*ccname);
1050                                         if ((create_auth_rpc_client(clp, &rpc_clnt,
1051                                                                     &auth, uid,
1052                                                                     AUTHTYPE_KRB5,
1053                                                                     GSS_C_NO_CREDENTIAL)) == 0) {
1054                                                 /* Success! */
1055                                                 success++;
1056                                                 break;
1057                                         }
1058                                         printerr(2, "WARNING: Failed to create machine krb5 context "
1059                                                  "with credentials cache %s for server %s\n",
1060                                                  *ccname, clp->servername);
1061                                 }
1062                                 gssd_free_krb5_machine_cred_list(credlist);
1063                                 if (!success) {
1064                                         if(nocache == 0) {
1065                                                 nocache++;
1066                                                 printerr(2, "WARNING: Machine cache is prematurely expired or corrupted "
1067                                                             "trying to recreate cache for server %s\n", clp->servername);
1068                                         } else {
1069                                                 printerr(1, "WARNING: Failed to create machine krb5 context "
1070                                                  "with any credentials cache for server %s\n",
1071                                                  clp->servername);
1072                                                 goto out_return_error;
1073                                         }
1074                                 }
1075                         } while(!success);
1076                 } else {
1077                         printerr(1, "WARNING: Failed to create krb5 context "
1078                                  "for user with uid %d for server %s\n",
1079                                  uid, clp->servername);
1080                         goto out_return_error;
1081                 }
1082         }
1083
1084         if (!authgss_get_private_data(auth, &pd)) {
1085                 printerr(1, "WARNING: Failed to obtain authentication "
1086                             "data for user with uid %d for server %s\n",
1087                          uid, clp->servername);
1088                 goto out_return_error;
1089         }
1090
1091         /* Grab the context lifetime to pass to the kernel. lifetime_rec
1092          * is set to zero on error */
1093         maj_stat = gss_inquire_context(&min_stat, pd.pd_ctx, NULL, NULL,
1094                                        &lifetime_rec, NULL, NULL, NULL, NULL);
1095
1096         if (maj_stat)
1097                 printerr(1, "WARNING: Failed to inquire context for lifetme "
1098                             "maj_stat %u\n", maj_stat);
1099
1100         if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) {
1101                 printerr(0, "WARNING: Failed to serialize krb5 context for "
1102                             "user with uid %d for server %s\n",
1103                          uid, clp->servername);
1104                 goto out_return_error;
1105         }
1106
1107         do_downcall(fd, uid, &pd, &token, lifetime_rec);
1108
1109 out:
1110         if (token.value)
1111                 free(token.value);
1112 #ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA
1113         if (pd.pd_ctx_hndl.length != 0 || pd.pd_ctx != 0)
1114                 authgss_free_private_data(&pd);
1115 #endif
1116         if (auth)
1117                 AUTH_DESTROY(auth);
1118         if (rpc_clnt)
1119                 clnt_destroy(rpc_clnt);
1120         return;
1121
1122 out_return_error:
1123         do_error_downcall(fd, uid, downcall_err);
1124         goto out;
1125 }
1126
1127 void
1128 handle_krb5_upcall(struct clnt_info *clp)
1129 {
1130         uid_t                   uid;
1131
1132         if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) {
1133                 printerr(0, "WARNING: failed reading uid from krb5 "
1134                             "upcall pipe: %s\n", strerror(errno));
1135                 return;
1136         }
1137
1138         process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL);
1139 }
1140
1141 void
1142 handle_gssd_upcall(struct clnt_info *clp)
1143 {
1144         uid_t                   uid;
1145         char                    *lbuf = NULL;
1146         int                     lbuflen = 0;
1147         char                    *p;
1148         char                    *mech = NULL;
1149         char                    *target = NULL;
1150         char                    *service = NULL;
1151         char                    *enctypes = NULL;
1152
1153         printerr(1, "handling gssd upcall (%s)\n", clp->dirname);
1154
1155         if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) {
1156                 printerr(0, "WARNING: handle_gssd_upcall: "
1157                             "failed reading request\n");
1158                 return;
1159         }
1160         printerr(2, "%s: '%s'\n", __func__, lbuf);
1161
1162         /* find the mechanism name */
1163         if ((p = strstr(lbuf, "mech=")) != NULL) {
1164                 mech = malloc(lbuflen);
1165                 if (!mech)
1166                         goto out;
1167                 if (sscanf(p, "mech=%s", mech) != 1) {
1168                         printerr(0, "WARNING: handle_gssd_upcall: "
1169                                     "failed to parse gss mechanism name "
1170                                     "in upcall string '%s'\n", lbuf);
1171                         goto out;
1172                 }
1173         } else {
1174                 printerr(0, "WARNING: handle_gssd_upcall: "
1175                             "failed to find gss mechanism name "
1176                             "in upcall string '%s'\n", lbuf);
1177                 goto out;
1178         }
1179
1180         /* read uid */
1181         if ((p = strstr(lbuf, "uid=")) != NULL) {
1182                 if (sscanf(p, "uid=%d", &uid) != 1) {
1183                         printerr(0, "WARNING: handle_gssd_upcall: "
1184                                     "failed to parse uid "
1185                                     "in upcall string '%s'\n", lbuf);
1186                         goto out;
1187                 }
1188         } else {
1189                 printerr(0, "WARNING: handle_gssd_upcall: "
1190                             "failed to find uid "
1191                             "in upcall string '%s'\n", lbuf);
1192                 goto out;
1193         }
1194
1195         /* read supported encryption types if supplied */
1196         if ((p = strstr(lbuf, "enctypes=")) != NULL) {
1197                 enctypes = malloc(lbuflen);
1198                 if (!enctypes)
1199                         goto out;
1200                 if (sscanf(p, "enctypes=%s", enctypes) != 1) {
1201                         printerr(0, "WARNING: handle_gssd_upcall: "
1202                                     "failed to parse encryption types "
1203                                     "in upcall string '%s'\n", lbuf);
1204                         goto out;
1205                 }
1206                 if (parse_enctypes(enctypes) != 0) {
1207                         printerr(0, "WARNING: handle_gssd_upcall: "
1208                                 "parsing encryption types failed: errno %d\n", errno);
1209                 }
1210         }
1211
1212         /* read target name */
1213         if ((p = strstr(lbuf, "target=")) != NULL) {
1214                 target = malloc(lbuflen);
1215                 if (!target)
1216                         goto out;
1217                 if (sscanf(p, "target=%s", target) != 1) {
1218                         printerr(0, "WARNING: handle_gssd_upcall: "
1219                                     "failed to parse target name "
1220                                     "in upcall string '%s'\n", lbuf);
1221                         goto out;
1222                 }
1223         }
1224
1225         /*
1226          * read the service name
1227          *
1228          * The presence of attribute "service=" indicates that machine
1229          * credentials should be used for this request.  If the value
1230          * is "*", then any machine credentials available can be used.
1231          * If the value is anything else, then machine credentials for
1232          * the specified service name (always "nfs" for now) should be
1233          * used.
1234          */
1235         if ((p = strstr(lbuf, "service=")) != NULL) {
1236                 service = malloc(lbuflen);
1237                 if (!service)
1238                         goto out;
1239                 if (sscanf(p, "service=%s", service) != 1) {
1240                         printerr(0, "WARNING: handle_gssd_upcall: "
1241                                     "failed to parse service type "
1242                                     "in upcall string '%s'\n", lbuf);
1243                         goto out;
1244                 }
1245         }
1246
1247         if (strcmp(mech, "krb5") == 0)
1248                 process_krb5_upcall(clp, uid, clp->gssd_fd, target, service);
1249         else
1250                 printerr(0, "WARNING: handle_gssd_upcall: "
1251                             "received unknown gss mech '%s'\n", mech);
1252
1253 out:
1254         free(lbuf);
1255         free(mech);
1256         free(enctypes);
1257         free(target);
1258         free(service);
1259         return;
1260 }
1261