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