2861d060f0b97f35e714537f6f45dd80c879a274
[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 int 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         /* check service, program, and version */
254         if (memcmp(service, "nfs", 3) != 0)
255                 return -1;
256         *prog = atoi(program + 1); /* skip open paren */
257         *vers = atoi(version);
258
259         if (strlen(service) == 3 ) {
260                 if ((*prog != 100003) || ((*vers != 2) && (*vers != 3) &&
261                     (*vers != 4)))
262                         goto fail;
263         } else if (memcmp(service, "nfs4_cb", 7) == 0) {
264                 if (*vers != 1)
265                         goto fail;
266         }
267
268         if (!addrstr_to_sockaddr(addr, address, port))
269                 goto fail;
270
271         *servername = sockaddr_to_hostname(addr, address);
272         if (*servername == NULL)
273                 goto fail;
274
275         nbytes = snprintf(buf, INFOBUFLEN, "%s@%s", service, *servername);
276         if (nbytes > INFOBUFLEN)
277                 goto fail;
278
279         if (!(*servicename = calloc(strlen(buf) + 1, 1)))
280                 goto fail;
281         memcpy(*servicename, buf, strlen(buf));
282
283         if (!(*protocol = strdup(protoname)))
284                 goto fail;
285         return 0;
286 fail:
287         printerr(0, "ERROR: failed to read service info\n");
288         if (fd != -1) close(fd);
289         free(*servername);
290         free(*servicename);
291         free(*protocol);
292         *servicename = *servername = *protocol = NULL;
293         return -1;
294 }
295
296 static void
297 destroy_client(struct clnt_info *clp)
298 {
299         if (clp->krb5_poll_index != -1)
300                 memset(&pollarray[clp->krb5_poll_index], 0,
301                                         sizeof(struct pollfd));
302         if (clp->gssd_poll_index != -1)
303                 memset(&pollarray[clp->gssd_poll_index], 0,
304                                         sizeof(struct pollfd));
305         if (clp->dir_fd != -1) close(clp->dir_fd);
306         if (clp->krb5_fd != -1) close(clp->krb5_fd);
307         if (clp->gssd_fd != -1) close(clp->gssd_fd);
308         free(clp->dirname);
309         free(clp->servicename);
310         free(clp->servername);
311         free(clp->protocol);
312         free(clp);
313 }
314
315 static struct clnt_info *
316 insert_new_clnt(void)
317 {
318         struct clnt_info        *clp = NULL;
319
320         if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
321                 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
322                          strerror(errno));
323                 goto out;
324         }
325         clp->krb5_poll_index = -1;
326         clp->gssd_poll_index = -1;
327         clp->krb5_fd = -1;
328         clp->gssd_fd = -1;
329         clp->dir_fd = -1;
330
331         TAILQ_INSERT_HEAD(&clnt_list, clp, list);
332 out:
333         return clp;
334 }
335
336 static int
337 process_clnt_dir_files(struct clnt_info * clp)
338 {
339         char    name[PATH_MAX];
340         char    gname[PATH_MAX];
341         char    info_file_name[PATH_MAX];
342
343         if (clp->gssd_close_me) {
344                 printerr(2, "Closing 'gssd' pipe for %s\n", clp->dirname);
345                 close(clp->gssd_fd);
346                 memset(&pollarray[clp->gssd_poll_index], 0,
347                         sizeof(struct pollfd));
348                 clp->gssd_fd = -1;
349                 clp->gssd_poll_index = -1;
350                 clp->gssd_close_me = 0;
351         }
352         if (clp->krb5_close_me) {
353                 printerr(2, "Closing 'krb5' pipe for %s\n", clp->dirname);
354                 close(clp->krb5_fd);
355                 memset(&pollarray[clp->krb5_poll_index], 0,
356                         sizeof(struct pollfd));
357                 clp->krb5_fd = -1;
358                 clp->krb5_poll_index = -1;
359                 clp->krb5_close_me = 0;
360         }
361
362         if (clp->gssd_fd == -1) {
363                 snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname);
364                 clp->gssd_fd = open(gname, O_RDWR);
365         }
366         if (clp->gssd_fd == -1) {
367                 if (clp->krb5_fd == -1) {
368                         snprintf(name, sizeof(name), "%s/krb5", clp->dirname);
369                         clp->krb5_fd = open(name, O_RDWR);
370                 }
371
372                 /* If we opened a gss-specific pipe, let's try opening
373                  * the new upcall pipe again. If we succeed, close
374                  * gss-specific pipe(s).
375                  */
376                 if (clp->krb5_fd != -1) {
377                         clp->gssd_fd = open(gname, O_RDWR);
378                         if (clp->gssd_fd != -1) {
379                                 if (clp->krb5_fd != -1)
380                                         close(clp->krb5_fd);
381                                 clp->krb5_fd = -1;
382                         }
383                 }
384         }
385
386         if ((clp->krb5_fd == -1) && (clp->gssd_fd == -1))
387                 return -1;
388         snprintf(info_file_name, sizeof(info_file_name), "%s/info",
389                         clp->dirname);
390         if ((clp->servicename == NULL) &&
391              read_service_info(info_file_name, &clp->servicename,
392                                 &clp->servername, &clp->prog, &clp->vers,
393                                 &clp->protocol, (struct sockaddr *) &clp->addr))
394                 return -1;
395         return 0;
396 }
397
398 static int
399 get_poll_index(int *ind)
400 {
401         int i;
402
403         *ind = -1;
404         for (i=0; i<FD_ALLOC_BLOCK; i++) {
405                 if (pollarray[i].events == 0) {
406                         *ind = i;
407                         break;
408                 }
409         }
410         if (*ind == -1) {
411                 printerr(0, "ERROR: No pollarray slots open\n");
412                 return -1;
413         }
414         return 0;
415 }
416
417
418 static int
419 insert_clnt_poll(struct clnt_info *clp)
420 {
421         if ((clp->gssd_fd != -1) && (clp->gssd_poll_index == -1)) {
422                 if (get_poll_index(&clp->gssd_poll_index)) {
423                         printerr(0, "ERROR: Too many gssd clients\n");
424                         return -1;
425                 }
426                 pollarray[clp->gssd_poll_index].fd = clp->gssd_fd;
427                 pollarray[clp->gssd_poll_index].events |= POLLIN;
428         }
429
430         if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
431                 if (get_poll_index(&clp->krb5_poll_index)) {
432                         printerr(0, "ERROR: Too many krb5 clients\n");
433                         return -1;
434                 }
435                 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
436                 pollarray[clp->krb5_poll_index].events |= POLLIN;
437         }
438
439         return 0;
440 }
441
442 static void
443 process_clnt_dir(char *dir, char *pdir)
444 {
445         struct clnt_info *      clp;
446
447         if (!(clp = insert_new_clnt()))
448                 goto fail_destroy_client;
449
450         /* An extra for the '/', and an extra for the null */
451         if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) {
452                 goto fail_destroy_client;
453         }
454         sprintf(clp->dirname, "%s/%s", pdir, dir);
455         if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
456                 printerr(0, "ERROR: can't open %s: %s\n",
457                          clp->dirname, strerror(errno));
458                 goto fail_destroy_client;
459         }
460         fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
461         fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
462
463         if (process_clnt_dir_files(clp))
464                 goto fail_keep_client;
465
466         if (insert_clnt_poll(clp))
467                 goto fail_destroy_client;
468
469         return;
470
471 fail_destroy_client:
472         if (clp) {
473                 TAILQ_REMOVE(&clnt_list, clp, list);
474                 destroy_client(clp);
475         }
476 fail_keep_client:
477         /* We couldn't find some subdirectories, but we keep the client
478          * around in case we get a notification on the directory when the
479          * subdirectories are created. */
480         return;
481 }
482
483 void
484 init_client_list(void)
485 {
486         TAILQ_INIT(&clnt_list);
487         /* Eventually plan to grow/shrink poll array: */
488         pollsize = FD_ALLOC_BLOCK;
489         pollarray = calloc(pollsize, sizeof(struct pollfd));
490 }
491
492 /*
493  * This is run after a DNOTIFY signal, and should clear up any
494  * directories that are no longer around, and re-scan any existing
495  * directories, since the DNOTIFY could have been in there.
496  */
497 static void
498 update_old_clients(struct dirent **namelist, int size, char *pdir)
499 {
500         struct clnt_info *clp;
501         void *saveprev;
502         int i, stillhere;
503         char fname[PATH_MAX];
504
505         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
506                 /* only compare entries in the global list that are from the
507                  * same pipefs parent directory as "pdir"
508                  */
509                 if (strncmp(clp->dirname, pdir, strlen(pdir)) != 0) continue;
510
511                 stillhere = 0;
512                 for (i=0; i < size; i++) {
513                         snprintf(fname, sizeof(fname), "%s/%s",
514                                  pdir, namelist[i]->d_name);
515                         if (strcmp(clp->dirname, fname) == 0) {
516                                 stillhere = 1;
517                                 break;
518                         }
519                 }
520                 if (!stillhere) {
521                         printerr(2, "destroying client %s\n", clp->dirname);
522                         saveprev = clp->list.tqe_prev;
523                         TAILQ_REMOVE(&clnt_list, clp, list);
524                         destroy_client(clp);
525                         clp = saveprev;
526                 }
527         }
528         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
529                 if (!process_clnt_dir_files(clp))
530                         insert_clnt_poll(clp);
531         }
532 }
533
534 /* Search for a client by directory name, return 1 if found, 0 otherwise */
535 static int
536 find_client(char *dirname, char *pdir)
537 {
538         struct clnt_info        *clp;
539         char fname[PATH_MAX];
540
541         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
542                 snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname);
543                 if (strcmp(clp->dirname, fname) == 0)
544                         return 1;
545         }
546         return 0;
547 }
548
549 static int
550 process_pipedir(char *pipe_name)
551 {
552         struct dirent **namelist;
553         int i, j;
554
555         if (chdir(pipe_name) < 0) {
556                 printerr(0, "ERROR: can't chdir to %s: %s\n",
557                          pipe_name, strerror(errno));
558                 return -1;
559         }
560
561         j = scandir(pipe_name, &namelist, NULL, alphasort);
562         if (j < 0) {
563                 printerr(0, "ERROR: can't scandir %s: %s\n",
564                          pipe_name, strerror(errno));
565                 return -1;
566         }
567
568         update_old_clients(namelist, j, pipe_name);
569         for (i=0; i < j; i++) {
570                 if (i < FD_ALLOC_BLOCK
571                                 && !strncmp(namelist[i]->d_name, "clnt", 4)
572                                 && !find_client(namelist[i]->d_name, pipe_name))
573                         process_clnt_dir(namelist[i]->d_name, pipe_name);
574                 free(namelist[i]);
575         }
576
577         free(namelist);
578
579         return 0;
580 }
581
582 /* Used to read (and re-read) list of clients, set up poll array. */
583 int
584 update_client_list(void)
585 {
586         int retval = -1;
587         struct topdirs_info *tdi;
588
589         TAILQ_FOREACH(tdi, &topdirs_list, list) {
590                 retval = process_pipedir(tdi->dirname);
591                 if (retval)
592                         printerr(1, "WARNING: error processing %s\n",
593                                  tdi->dirname);
594
595         }
596         return retval;
597 }
598
599 /* Encryption types supported by the kernel rpcsec_gss code */
600 int num_krb5_enctypes = 0;
601 krb5_enctype *krb5_enctypes = NULL;
602
603 /*
604  * Parse the supported encryption type information
605  */
606 static int
607 parse_enctypes(char *enctypes)
608 {
609         int n = 0;
610         char *curr, *comma;
611         int i;
612         static char *cached_types;
613
614         if (cached_types && strcmp(cached_types, enctypes) == 0)
615                 return 0;
616         free(cached_types);
617
618         if (krb5_enctypes != NULL) {
619                 free(krb5_enctypes);
620                 krb5_enctypes = NULL;
621                 num_krb5_enctypes = 0;
622         }
623
624         /* count the number of commas */
625         for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
626                 comma = strchr(curr, ',');
627                 if (comma != NULL)
628                         n++;
629                 else
630                         break;
631         }
632         /* If no more commas and we're not at the end, there's one more value */
633         if (*curr != '\0')
634                 n++;
635
636         /* Empty string, return an error */
637         if (n == 0)
638                 return ENOENT;
639
640         /* Allocate space for enctypes array */
641         if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) {
642                 return ENOMEM;
643         }
644
645         /* Now parse each value into the array */
646         for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) {
647                 krb5_enctypes[i++] = atoi(curr);
648                 comma = strchr(curr, ',');
649                 if (comma == NULL)
650                         break;
651         }
652
653         num_krb5_enctypes = n;
654         if ((cached_types = malloc(strlen(enctypes)+1)))
655                 strcpy(cached_types, enctypes);
656
657         return 0;
658 }
659
660 static int
661 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
662             gss_buffer_desc *context_token)
663 {
664         char    *buf = NULL, *p = NULL, *end = NULL;
665         unsigned int timeout = context_timeout;
666         unsigned int buf_size = 0;
667
668         printerr(1, "doing downcall\n");
669         buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
670                 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
671                 sizeof(context_token->length) + context_token->length;
672         p = buf = malloc(buf_size);
673         end = buf + buf_size;
674
675         if (WRITE_BYTES(&p, end, uid)) goto out_err;
676         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
677         if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
678         if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
679         if (write_buffer(&p, end, context_token)) goto out_err;
680
681         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
682         if (buf) free(buf);
683         return 0;
684 out_err:
685         if (buf) free(buf);
686         printerr(1, "Failed to write downcall!\n");
687         return -1;
688 }
689
690 static int
691 do_error_downcall(int k5_fd, uid_t uid, int err)
692 {
693         char    buf[1024];
694         char    *p = buf, *end = buf + 1024;
695         unsigned int timeout = 0;
696         int     zero = 0;
697
698         printerr(1, "doing error downcall\n");
699
700         if (WRITE_BYTES(&p, end, uid)) goto out_err;
701         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
702         /* use seq_win = 0 to indicate an error: */
703         if (WRITE_BYTES(&p, end, zero)) goto out_err;
704         if (WRITE_BYTES(&p, end, err)) goto out_err;
705
706         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
707         return 0;
708 out_err:
709         printerr(1, "Failed to write error downcall!\n");
710         return -1;
711 }
712
713 /*
714  * If the port isn't already set, do an rpcbind query to the remote server
715  * using the program and version and get the port. 
716  *
717  * Newer kernels send the value of the port= mount option in the "info"
718  * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
719  * of the port= option or '2049'. The port field in a new sockaddr should
720  * reflect the value that was sent by the kernel.
721  */
722 static int
723 populate_port(struct sockaddr *sa, const socklen_t salen,
724               const rpcprog_t program, const rpcvers_t version,
725               const unsigned short protocol)
726 {
727         struct sockaddr_in      *s4 = (struct sockaddr_in *) sa;
728 #ifdef IPV6_SUPPORTED
729         struct sockaddr_in6     *s6 = (struct sockaddr_in6 *) sa;
730 #endif /* IPV6_SUPPORTED */
731         unsigned short          port;
732
733         /*
734          * Newer kernels send the port in the upcall. If we already have
735          * the port, there's no need to look it up.
736          */
737         switch (sa->sa_family) {
738         case AF_INET:
739                 if (s4->sin_port != 0) {
740                         printerr(2, "DEBUG: port already set to %d\n",
741                                  ntohs(s4->sin_port));
742                         return 1;
743                 }
744                 break;
745 #ifdef IPV6_SUPPORTED
746         case AF_INET6:
747                 if (s6->sin6_port != 0) {
748                         printerr(2, "DEBUG: port already set to %d\n",
749                                  ntohs(s6->sin6_port));
750                         return 1;
751                 }
752                 break;
753 #endif /* IPV6_SUPPORTED */
754         default:
755                 printerr(0, "ERROR: unsupported address family %d\n",
756                             sa->sa_family);
757                 return 0;
758         }
759
760         /*
761          * Newer kernels that send the port in the upcall set the value to
762          * 2049 for NFSv4 mounts when one isn't specified. The check below is
763          * only for kernels that don't send the port in the upcall. For those
764          * we either have to do an rpcbind query or set it to the standard
765          * port. Doing a query could be problematic (firewalls, etc), so take
766          * the latter approach.
767          */
768         if (program == 100003 && version == 4) {
769                 port = 2049;
770                 goto set_port;
771         }
772
773         port = nfs_getport(sa, salen, program, version, protocol);
774         if (!port) {
775                 printerr(0, "ERROR: unable to obtain port for prog %ld "
776                             "vers %ld\n", program, version);
777                 return 0;
778         }
779
780 set_port:
781         printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port,
782                  program, version);
783
784         switch (sa->sa_family) {
785         case AF_INET:
786                 s4->sin_port = htons(port);
787                 break;
788 #ifdef IPV6_SUPPORTED
789         case AF_INET6:
790                 s6->sin6_port = htons(port);
791                 break;
792 #endif /* IPV6_SUPPORTED */
793         }
794
795         return 1;
796 }
797
798 /*
799  * Create an RPC connection and establish an authenticated
800  * gss context with a server.
801  */
802 int create_auth_rpc_client(struct clnt_info *clp,
803                            CLIENT **clnt_return,
804                            AUTH **auth_return,
805                            uid_t uid,
806                            int authtype)
807 {
808         CLIENT                  *rpc_clnt = NULL;
809         struct rpc_gss_sec      sec;
810         AUTH                    *auth = NULL;
811         uid_t                   save_uid = -1;
812         int                     retval = -1;
813         OM_uint32               min_stat;
814         char                    rpc_errmsg[1024];
815         int                     protocol;
816         struct timeval          timeout = {5, 0};
817         struct sockaddr         *addr = (struct sockaddr *) &clp->addr;
818         socklen_t               salen;
819
820         /* Create the context as the user (not as root) */
821         save_uid = geteuid();
822         if (setfsuid(uid) != 0) {
823                 printerr(0, "WARNING: Failed to setfsuid for "
824                             "user with uid %d\n", uid);
825                 goto out_fail;
826         }
827         printerr(2, "creating context using fsuid %d (save_uid %d)\n",
828                         uid, save_uid);
829
830         sec.qop = GSS_C_QOP_DEFAULT;
831         sec.svc = RPCSEC_GSS_SVC_NONE;
832         sec.cred = GSS_C_NO_CREDENTIAL;
833         sec.req_flags = 0;
834         if (authtype == AUTHTYPE_KRB5) {
835                 sec.mech = (gss_OID)&krb5oid;
836                 sec.req_flags = GSS_C_MUTUAL_FLAG;
837         }
838         else {
839                 printerr(0, "ERROR: Invalid authentication type (%d) "
840                         "in create_auth_rpc_client\n", authtype);
841                 goto out_fail;
842         }
843
844
845         if (authtype == AUTHTYPE_KRB5) {
846 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
847                 /*
848                  * Do this before creating rpc connection since we won't need
849                  * rpc connection if it fails!
850                  */
851                 if (limit_krb5_enctypes(&sec)) {
852                         printerr(1, "WARNING: Failed while limiting krb5 "
853                                     "encryption types for user with uid %d\n",
854                                  uid);
855                         goto out_fail;
856                 }
857 #endif
858         }
859
860         /* create an rpc connection to the nfs server */
861
862         printerr(2, "creating %s client for server %s\n", clp->protocol,
863                         clp->servername);
864
865         if ((strcmp(clp->protocol, "tcp")) == 0) {
866                 protocol = IPPROTO_TCP;
867         } else if ((strcmp(clp->protocol, "udp")) == 0) {
868                 protocol = IPPROTO_UDP;
869         } else {
870                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
871                          "for connection to server %s for user with uid %d\n",
872                          clp->protocol, clp->servername, uid);
873                 goto out_fail;
874         }
875
876         switch (addr->sa_family) {
877         case AF_INET:
878                 salen = sizeof(struct sockaddr_in);
879                 break;
880 #ifdef IPV6_SUPPORTED
881         case AF_INET6:
882                 salen = sizeof(struct sockaddr_in6);
883                 break;
884 #endif /* IPV6_SUPPORTED */
885         default:
886                 printerr(1, "ERROR: Unknown address family %d\n",
887                          addr->sa_family);
888                 goto out_fail;
889         }
890
891         if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
892                 goto out_fail;
893
894         rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
895                                      clp->vers, &timeout);
896         if (!rpc_clnt) {
897                 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
898                          "WARNING: can't create %s rpc_clnt to server %s for "
899                          "user with uid %d",
900                          protocol == IPPROTO_TCP ? "tcp" : "udp",
901                          clp->servername, uid);
902                 printerr(0, "%s\n",
903                          clnt_spcreateerror(rpc_errmsg));
904                 goto out_fail;
905         }
906
907         printerr(2, "creating context with server %s\n", clp->servicename);
908         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
909         if (!auth) {
910                 /* Our caller should print appropriate message */
911                 printerr(2, "WARNING: Failed to create krb5 context for "
912                             "user with uid %d for server %s\n",
913                          uid, clp->servername);
914                 goto out_fail;
915         }
916
917         /* Success !!! */
918         rpc_clnt->cl_auth = auth;
919         *clnt_return = rpc_clnt;
920         *auth_return = auth;
921         retval = 0;
922
923   out:
924         if (sec.cred != GSS_C_NO_CREDENTIAL)
925                 gss_release_cred(&min_stat, &sec.cred);
926         /* Restore euid to original value */
927         if (((int)save_uid != -1) && (setfsuid(save_uid) != (int)uid)) {
928                 printerr(0, "WARNING: Failed to restore fsuid"
929                             " to uid %d from %d\n", save_uid, uid);
930         }
931         return retval;
932
933   out_fail:
934         /* Only destroy here if failure.  Otherwise, caller is responsible */
935         if (rpc_clnt) clnt_destroy(rpc_clnt);
936
937         goto out;
938 }
939
940 static char *
941 user_cachedir(char *dirname, uid_t uid)
942 {
943         struct passwd *pw;
944         char *ptr;
945
946         if ((pw = getpwuid(uid)) == NULL) {
947                 printerr(0, "user_cachedir: Failed to find '%d' uid"
948                             " for cache directory\n");
949                 return NULL;
950         }
951         ptr = malloc(strlen(dirname)+strlen(pw->pw_name)+2);
952         if (ptr)
953                 sprintf(ptr, "%s/%s", dirname, pw->pw_name);
954
955         return ptr;
956 }
957 /*
958  * this code uses the userland rpcsec gss library to create a krb5
959  * context on behalf of the kernel
960  */
961 static void
962 process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
963                     char *service)
964 {
965         CLIENT                  *rpc_clnt = NULL;
966         AUTH                    *auth = NULL;
967         struct authgss_private_data pd;
968         gss_buffer_desc         token;
969         char                    **credlist = NULL;
970         char                    **ccname;
971         char                    **dirname, *dir, *userdir;
972         int                     create_resp = -1;
973         int                     err, downcall_err = -EACCES;
974
975         printerr(1, "handling krb5 upcall (%s)\n", clp->dirname);
976
977         if (tgtname) {
978                 if (clp->servicename) {
979                         free(clp->servicename);
980                         clp->servicename = strdup(tgtname);
981                 }
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                 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
1014                         /* See if the user name is needed */
1015                         if (strncmp(*dirname, GSSD_USER_CRED_DIR, 
1016                                         strlen(GSSD_USER_CRED_DIR)) == 0) {
1017                                 userdir = user_cachedir(*dirname, uid);
1018                                 if (userdir == NULL) 
1019                                         continue;
1020                                 dir = userdir;
1021                         } else
1022                                 dir = *dirname;
1023
1024                         err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, dir);
1025
1026                         if (userdir) {
1027                                 free(userdir);
1028                                 userdir = NULL;
1029                         }
1030                         if (err == -EKEYEXPIRED)
1031                                 downcall_err = -EKEYEXPIRED;
1032                         else if (!err)
1033                                 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
1034                                                              AUTHTYPE_KRB5);
1035                         if (create_resp == 0)
1036                                 break;
1037                 }
1038         }
1039         if (create_resp != 0) {
1040                 if (uid == 0 && (root_uses_machine_creds == 1 ||
1041                                 service != NULL)) {
1042                         int nocache = 0;
1043                         int success = 0;
1044                         do {
1045                                 gssd_refresh_krb5_machine_credential(clp->servername,
1046                                                                      NULL, service);
1047                                 /*
1048                                  * Get a list of credential cache names and try each
1049                                  * of them until one works or we've tried them all
1050                                  */
1051                                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
1052                                         printerr(0, "ERROR: No credentials found "
1053                                                  "for connection to server %s\n",
1054                                                  clp->servername);
1055                                                 goto out_return_error;
1056                                 }
1057                                 for (ccname = credlist; ccname && *ccname; ccname++) {
1058                                         gssd_setup_krb5_machine_gss_ccache(*ccname);
1059                                         if ((create_auth_rpc_client(clp, &rpc_clnt,
1060                                                                     &auth, uid,
1061                                                                     AUTHTYPE_KRB5)) == 0) {
1062                                                 /* Success! */
1063                                                 success++;
1064                                                 break;
1065                                         } 
1066                                         printerr(2, "WARNING: Failed to create machine krb5 context "
1067                                                  "with credentials cache %s for server %s\n",
1068                                                  *ccname, clp->servername);
1069                                 }
1070                                 gssd_free_krb5_machine_cred_list(credlist);                     
1071                                 if (!success) {
1072                                         if(nocache == 0) {
1073                                                 nocache++;
1074                                                 printerr(2, "WARNING: Machine cache is prematurely expired or corrupted "
1075                                                             "trying to recreate cache for server %s\n", clp->servername);
1076                                         } else {
1077                                                 printerr(1, "WARNING: Failed to create machine krb5 context "
1078                                                  "with any credentials cache for server %s\n",
1079                                                  clp->servername);
1080                                                 goto out_return_error;
1081                                         }
1082                                 }
1083                         } while(!success);
1084                 } else {
1085                         printerr(1, "WARNING: Failed to create krb5 context "
1086                                  "for user with uid %d for server %s\n",
1087                                  uid, clp->servername);
1088                         goto out_return_error;
1089                 }
1090         }
1091
1092         if (!authgss_get_private_data(auth, &pd)) {
1093                 printerr(1, "WARNING: Failed to obtain authentication "
1094                             "data for user with uid %d for server %s\n",
1095                          uid, clp->servername);
1096                 goto out_return_error;
1097         }
1098
1099         if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
1100                 printerr(0, "WARNING: Failed to serialize krb5 context for "
1101                             "user with uid %d for server %s\n",
1102                          uid, clp->servername);
1103                 goto out_return_error;
1104         }
1105
1106         do_downcall(fd, uid, &pd, &token);
1107
1108 out:
1109         if (token.value)
1110                 free(token.value);
1111 #ifndef HAVE_LIBTIRPC
1112         if (pd.pd_ctx_hndl.length != 0)
1113                 authgss_free_private_data(&pd);
1114 #endif
1115         if (auth)
1116                 AUTH_DESTROY(auth);
1117         if (rpc_clnt)
1118                 clnt_destroy(rpc_clnt);
1119         return;
1120
1121 out_return_error:
1122         do_error_downcall(fd, uid, downcall_err);
1123         goto out;
1124 }
1125
1126 void
1127 handle_krb5_upcall(struct clnt_info *clp)
1128 {
1129         uid_t                   uid;
1130
1131         if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) {
1132                 printerr(0, "WARNING: failed reading uid from krb5 "
1133                             "upcall pipe: %s\n", strerror(errno));
1134                 return;
1135         }
1136
1137         return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL);
1138 }
1139
1140 void
1141 handle_gssd_upcall(struct clnt_info *clp)
1142 {
1143         uid_t                   uid;
1144         char                    *lbuf = NULL;
1145         int                     lbuflen = 0;
1146         char                    *p;
1147         char                    *mech = NULL;
1148         char                    *target = NULL;
1149         char                    *service = NULL;
1150         char                    *enctypes = NULL;
1151
1152         printerr(1, "handling gssd upcall (%s)\n", clp->dirname);
1153
1154         if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) {
1155                 printerr(0, "WARNING: handle_gssd_upcall: "
1156                             "failed reading request\n");
1157                 return;
1158         }
1159         printerr(2, "%s: '%s'\n", __func__, lbuf);
1160
1161         /* find the mechanism name */
1162         if ((p = strstr(lbuf, "mech=")) != NULL) {
1163                 mech = malloc(lbuflen);
1164                 if (!mech)
1165                         goto out;
1166                 if (sscanf(p, "mech=%s", mech) != 1) {
1167                         printerr(0, "WARNING: handle_gssd_upcall: "
1168                                     "failed to parse gss mechanism name "
1169                                     "in upcall string '%s'\n", lbuf);
1170                         goto out;
1171                 }
1172         } else {
1173                 printerr(0, "WARNING: handle_gssd_upcall: "
1174                             "failed to find gss mechanism name "
1175                             "in upcall string '%s'\n", lbuf);
1176                 goto out;
1177         }
1178
1179         /* read uid */
1180         if ((p = strstr(lbuf, "uid=")) != NULL) {
1181                 if (sscanf(p, "uid=%d", &uid) != 1) {
1182                         printerr(0, "WARNING: handle_gssd_upcall: "
1183                                     "failed to parse uid "
1184                                     "in upcall string '%s'\n", lbuf);
1185                         goto out;
1186                 }
1187         } else {
1188                 printerr(0, "WARNING: handle_gssd_upcall: "
1189                             "failed to find uid "
1190                             "in upcall string '%s'\n", lbuf);
1191                 goto out;
1192         }
1193
1194         /* read supported encryption types if supplied */
1195         if ((p = strstr(lbuf, "enctypes=")) != NULL) {
1196                 enctypes = malloc(lbuflen);
1197                 if (!enctypes)
1198                         goto out;
1199                 if (sscanf(p, "enctypes=%s", enctypes) != 1) {
1200                         printerr(0, "WARNING: handle_gssd_upcall: "
1201                                     "failed to parse encryption types "
1202                                     "in upcall string '%s'\n", lbuf);
1203                         goto out;
1204                 }
1205                 if (parse_enctypes(enctypes) != 0) {
1206                         printerr(0, "WARNING: handle_gssd_upcall: "
1207                                 "parsing encryption types failed: errno %d\n", errno);
1208                 }
1209         }
1210
1211         /* read target name */
1212         if ((p = strstr(lbuf, "target=")) != NULL) {
1213                 target = malloc(lbuflen);
1214                 if (!target)
1215                         goto out;
1216                 if (sscanf(p, "target=%s", target) != 1) {
1217                         printerr(0, "WARNING: handle_gssd_upcall: "
1218                                     "failed to parse target name "
1219                                     "in upcall string '%s'\n", lbuf);
1220                         goto out;
1221                 }
1222         }
1223
1224         /*
1225          * read the service name
1226          *
1227          * The presence of attribute "service=" indicates that machine
1228          * credentials should be used for this request.  If the value
1229          * is "*", then any machine credentials available can be used.
1230          * If the value is anything else, then machine credentials for
1231          * the specified service name (always "nfs" for now) should be
1232          * used.
1233          */
1234         if ((p = strstr(lbuf, "service=")) != NULL) {
1235                 service = malloc(lbuflen);
1236                 if (!service)
1237                         goto out;
1238                 if (sscanf(p, "service=%s", service) != 1) {
1239                         printerr(0, "WARNING: handle_gssd_upcall: "
1240                                     "failed to parse service type "
1241                                     "in upcall string '%s'\n", lbuf);
1242                         goto out;
1243                 }
1244         }
1245
1246         if (strcmp(mech, "krb5") == 0)
1247                 process_krb5_upcall(clp, uid, clp->gssd_fd, target, service);
1248         else
1249                 printerr(0, "WARNING: handle_gssd_upcall: "
1250                             "received unknown gss mech '%s'\n", mech);
1251
1252 out:
1253         free(lbuf);
1254         free(mech);
1255         free(enctypes);
1256         free(target);
1257         free(service);
1258         return; 
1259 }
1260