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