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