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