]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/gssd_proc.c
gssd: handle new client upcall
[nfs-utils.git] / utils / gssd / gssd_proc.c
1 /*
2   gssd_proc.c
3
4   Copyright (c) 2000-2004 The Regents of the University of Michigan.
5   All rights reserved.
6
7   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
8   Copyright (c) 2001 Andy Adamson <andros@UMICH.EDU>.
9   Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
10   Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
11   Copyright (c) 2004 Kevin Coffman <kwc@umich.edu>
12   All rights reserved, all wrongs reversed.
13
14   Redistribution and use in source and binary forms, with or without
15   modification, are permitted provided that the following conditions
16   are met:
17
18   1. Redistributions of source code must retain the above copyright
19      notice, this list of conditions and the following disclaimer.
20   2. Redistributions in binary form must reproduce the above copyright
21      notice, this list of conditions and the following disclaimer in the
22      documentation and/or other materials provided with the distribution.
23   3. Neither the name of the University nor the names of its
24      contributors may be used to endorse or promote products derived
25      from this software without specific prior written permission.
26
27   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
28   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
34   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39 */
40
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif  /* HAVE_CONFIG_H */
44
45 #ifndef _GNU_SOURCE
46 #define _GNU_SOURCE
47 #endif
48
49 #include <sys/param.h>
50 #include <rpc/rpc.h>
51 #include <sys/stat.h>
52 #include <sys/socket.h>
53 #include <arpa/inet.h>
54 #include <sys/fsuid.h>
55
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <pwd.h>
59 #include <grp.h>
60 #include <string.h>
61 #include <dirent.h>
62 #include <poll.h>
63 #include <fcntl.h>
64 #include <signal.h>
65 #include <unistd.h>
66 #include <errno.h>
67 #include <gssapi/gssapi.h>
68 #include <netdb.h>
69
70 #include "gssd.h"
71 #include "err_util.h"
72 #include "gss_util.h"
73 #include "krb5_util.h"
74 #include "context.h"
75 #include "nfsrpc.h"
76 #include "nfslib.h"
77
78 /*
79  * pollarray:
80  *      array of struct pollfd suitable to pass to poll. initialized to
81  *      zero - a zero struct is ignored by poll() because the events mask is 0.
82  *
83  * clnt_list:
84  *      linked list of struct clnt_info which associates a clntXXX directory
85  *      with an index into pollarray[], and other basic data about that client.
86  *
87  * Directory structure: created by the kernel
88  *      {rpc_pipefs}/{dir}/clntXX         : one per rpc_clnt struct in the kernel
89  *      {rpc_pipefs}/{dir}/clntXX/krb5    : read uid for which kernel wants
90  *                                          a context, write the resulting context
91  *      {rpc_pipefs}/{dir}/clntXX/info    : stores info such as server name
92  *      {rpc_pipefs}/{dir}/clntXX/gssd    : pipe for all gss mechanisms using
93  *                                          a text-based string of parameters
94  *
95  * Algorithm:
96  *      Poll all {rpc_pipefs}/{dir}/clntXX/YYYY files.  When data is ready,
97  *      read and process; performs rpcsec_gss context initialization protocol to
98  *      get a cred for that user.  Writes result to corresponding krb5 file
99  *      in a form the kernel code will understand.
100  *      In addition, we make sure we are notified whenever anything is
101  *      created or destroyed in {rpc_pipefs} or in any of the clntXX directories,
102  *      and rescan the whole {rpc_pipefs} when this happens.
103  */
104
105 struct pollfd * pollarray;
106
107 int pollsize;  /* the size of pollaray (in pollfd's) */
108
109 /*
110  * convert a presentation address string to a sockaddr_storage struct. Returns
111  * true on success and 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 *addr, const int port)
124 {
125         struct sockaddr_in      *s4 = (struct sockaddr_in *) sa;
126 #ifdef IPV6_SUPPORTED
127         struct sockaddr_in6     *s6 = (struct sockaddr_in6 *) sa;
128 #endif /* IPV6_SUPPORTED */
129
130         if (inet_pton(AF_INET, addr, &s4->sin_addr)) {
131                 s4->sin_family = AF_INET;
132                 s4->sin_port = htons(port);
133 #ifdef IPV6_SUPPORTED
134         } else if (inet_pton(AF_INET6, addr, &s6->sin6_addr)) {
135                 s6->sin6_family = AF_INET6;
136                 s6->sin6_port = htons(port);
137 #endif /* IPV6_SUPPORTED */
138         } else {
139                 printerr(0, "ERROR: unable to convert %s to address\n", addr);
140                 return 0;
141         }
142
143         return 1;
144 }
145
146 /*
147  * convert a sockaddr to a hostname
148  */
149 static char *
150 sockaddr_to_hostname(const struct sockaddr *sa, const char *addr)
151 {
152         socklen_t               addrlen;
153         int                     err;
154         char                    *hostname;
155         char                    hbuf[NI_MAXHOST];
156
157         switch (sa->sa_family) {
158         case AF_INET:
159                 addrlen = sizeof(struct sockaddr_in);
160                 break;
161 #ifdef IPV6_SUPPORTED
162         case AF_INET6:
163                 addrlen = sizeof(struct sockaddr_in6);
164                 break;
165 #endif /* IPV6_SUPPORTED */
166         default:
167                 printerr(0, "ERROR: unrecognized addr family %d\n",
168                          sa->sa_family);
169                 return NULL;
170         }
171
172         err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0,
173                           NI_NAMEREQD);
174         if (err) {
175                 printerr(0, "ERROR: unable to resolve %s to hostname: %s\n",
176                          addr, err == EAI_SYSTEM ? strerror(err) :
177                                                    gai_strerror(err));
178                 return NULL;
179         }
180
181         hostname = strdup(hbuf);
182
183         return hostname;
184 }
185
186 /* XXX buffer problems: */
187 static int
188 read_service_info(char *info_file_name, char **servicename, char **servername,
189                   int *prog, int *vers, char **protocol,
190                   struct sockaddr *addr) {
191 #define INFOBUFLEN 256
192         char            buf[INFOBUFLEN + 1];
193         static char     dummy[128];
194         int             nbytes;
195         static char     service[128];
196         static char     address[128];
197         char            program[16];
198         char            version[16];
199         char            protoname[16];
200         char            cb_port[128];
201         char            *p;
202         int             fd = -1;
203         int             numfields;
204         int             port = 0;
205
206         *servicename = *servername = *protocol = NULL;
207
208         if ((fd = open(info_file_name, O_RDONLY)) == -1) {
209                 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
210                          strerror(errno));
211                 goto fail;
212         }
213         if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
214                 goto fail;
215         close(fd);
216         buf[nbytes] = '\0';
217
218         numfields = sscanf(buf,"RPC server: %127s\n"
219                    "service: %127s %15s version %15s\n"
220                    "address: %127s\n"
221                    "protocol: %15s\n",
222                    dummy,
223                    service, program, version,
224                    address,
225                    protoname);
226
227         if (numfields == 5) {
228                 strcpy(protoname, "tcp");
229         } else if (numfields != 6) {
230                 goto fail;
231         }
232
233         cb_port[0] = '\0';
234         if ((p = strstr(buf, "port")) != NULL)
235                 sscanf(p, "port: %127s\n", cb_port);
236
237         /* check service, program, and version */
238         if (memcmp(service, "nfs", 3) != 0)
239                 return -1;
240         *prog = atoi(program + 1); /* skip open paren */
241         *vers = atoi(version);
242
243         if (strlen(service) == 3 ) {
244                 if ((*prog != 100003) || ((*vers != 2) && (*vers != 3) &&
245                     (*vers != 4)))
246                         goto fail;
247         } else if (memcmp(service, "nfs4_cb", 7) == 0) {
248                 if (*vers != 1)
249                         goto fail;
250         }
251
252         if (cb_port[0] != '\0') {
253                 port = atoi(cb_port);
254                 if (port < 0 || port > 65535)
255                         goto fail;
256         }
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->spkm3_poll_index != -1)
293                 memset(&pollarray[clp->spkm3_poll_index], 0,
294                                         sizeof(struct pollfd));
295         if (clp->gssd_poll_index != -1)
296                 memset(&pollarray[clp->gssd_poll_index], 0,
297                                         sizeof(struct pollfd));
298         if (clp->dir_fd != -1) close(clp->dir_fd);
299         if (clp->krb5_fd != -1) close(clp->krb5_fd);
300         if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
301         if (clp->gssd_fd != -1) close(clp->gssd_fd);
302         free(clp->dirname);
303         free(clp->servicename);
304         free(clp->servername);
305         free(clp->protocol);
306         free(clp);
307 }
308
309 static struct clnt_info *
310 insert_new_clnt(void)
311 {
312         struct clnt_info        *clp = NULL;
313
314         if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
315                 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
316                          strerror(errno));
317                 goto out;
318         }
319         clp->krb5_poll_index = -1;
320         clp->spkm3_poll_index = -1;
321         clp->gssd_poll_index = -1;
322         clp->krb5_fd = -1;
323         clp->spkm3_fd = -1;
324         clp->gssd_fd = -1;
325         clp->dir_fd = -1;
326
327         TAILQ_INSERT_HEAD(&clnt_list, clp, list);
328 out:
329         return clp;
330 }
331
332 static int
333 process_clnt_dir_files(struct clnt_info * clp)
334 {
335         char    name[PATH_MAX];
336         char    gname[PATH_MAX];
337         char    info_file_name[PATH_MAX];
338
339         if (clp->gssd_fd == -1) {
340                 snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname);
341                 clp->gssd_fd = open(gname, O_RDWR);
342         }
343         if (clp->gssd_fd == -1) {
344                 if (clp->krb5_fd == -1) {
345                         snprintf(name, sizeof(name), "%s/krb5", clp->dirname);
346                         clp->krb5_fd = open(name, O_RDWR);
347                 }
348                 if (clp->spkm3_fd == -1) {
349                         snprintf(name, sizeof(name), "%s/spkm3", clp->dirname);
350                         clp->spkm3_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 || clp->spkm3_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                                 if (clp->spkm3_fd != -1)
364                                         close(clp->spkm3_fd);
365                                 clp->spkm3_fd = -1;
366                         }
367                 }
368         }
369
370         if ((clp->krb5_fd == -1) && (clp->spkm3_fd == -1) &&
371                         (clp->gssd_fd == -1))
372                 return -1;
373         snprintf(info_file_name, sizeof(info_file_name), "%s/info",
374                         clp->dirname);
375         if ((clp->servicename == NULL) &&
376              read_service_info(info_file_name, &clp->servicename,
377                                 &clp->servername, &clp->prog, &clp->vers,
378                                 &clp->protocol, (struct sockaddr *) &clp->addr))
379                 return -1;
380         return 0;
381 }
382
383 static int
384 get_poll_index(int *ind)
385 {
386         int i;
387
388         *ind = -1;
389         for (i=0; i<FD_ALLOC_BLOCK; i++) {
390                 if (pollarray[i].events == 0) {
391                         *ind = i;
392                         break;
393                 }
394         }
395         if (*ind == -1) {
396                 printerr(0, "ERROR: No pollarray slots open\n");
397                 return -1;
398         }
399         return 0;
400 }
401
402
403 static int
404 insert_clnt_poll(struct clnt_info *clp)
405 {
406         if ((clp->gssd_fd != -1) && (clp->gssd_poll_index == -1)) {
407                 if (get_poll_index(&clp->gssd_poll_index)) {
408                         printerr(0, "ERROR: Too many gssd clients\n");
409                         return -1;
410                 }
411                 pollarray[clp->gssd_poll_index].fd = clp->gssd_fd;
412                 pollarray[clp->gssd_poll_index].events |= POLLIN;
413         }
414
415         if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
416                 if (get_poll_index(&clp->krb5_poll_index)) {
417                         printerr(0, "ERROR: Too many krb5 clients\n");
418                         return -1;
419                 }
420                 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
421                 pollarray[clp->krb5_poll_index].events |= POLLIN;
422         }
423
424         if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
425                 if (get_poll_index(&clp->spkm3_poll_index)) {
426                         printerr(0, "ERROR: Too many spkm3 clients\n");
427                         return -1;
428                 }
429                 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
430                 pollarray[clp->spkm3_poll_index].events |= POLLIN;
431         }
432
433         return 0;
434 }
435
436 static void
437 process_clnt_dir(char *dir, char *pdir)
438 {
439         struct clnt_info *      clp;
440
441         if (!(clp = insert_new_clnt()))
442                 goto fail_destroy_client;
443
444         /* An extra for the '/', and an extra for the null */
445         if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) {
446                 goto fail_destroy_client;
447         }
448         sprintf(clp->dirname, "%s/%s", pdir, dir);
449         if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
450                 printerr(0, "ERROR: can't open %s: %s\n",
451                          clp->dirname, strerror(errno));
452                 goto fail_destroy_client;
453         }
454         fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
455         fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
456
457         if (process_clnt_dir_files(clp))
458                 goto fail_keep_client;
459
460         if (insert_clnt_poll(clp))
461                 goto fail_destroy_client;
462
463         return;
464
465 fail_destroy_client:
466         if (clp) {
467                 TAILQ_REMOVE(&clnt_list, clp, list);
468                 destroy_client(clp);
469         }
470 fail_keep_client:
471         /* We couldn't find some subdirectories, but we keep the client
472          * around in case we get a notification on the directory when the
473          * subdirectories are created. */
474         return;
475 }
476
477 void
478 init_client_list(void)
479 {
480         TAILQ_INIT(&clnt_list);
481         /* Eventually plan to grow/shrink poll array: */
482         pollsize = FD_ALLOC_BLOCK;
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 (i < FD_ALLOC_BLOCK
565                                 && !strncmp(namelist[i]->d_name, "clnt", 4)
566                                 && !find_client(namelist[i]->d_name, pipe_name))
567                         process_clnt_dir(namelist[i]->d_name, pipe_name);
568                 free(namelist[i]);
569         }
570
571         free(namelist);
572
573         return 0;
574 }
575
576 /* Used to read (and re-read) list of clients, set up poll array. */
577 int
578 update_client_list(void)
579 {
580         int retval = -1;
581         struct topdirs_info *tdi;
582
583         TAILQ_FOREACH(tdi, &topdirs_list, list) {
584                 retval = process_pipedir(tdi->dirname);
585                 if (retval)
586                         printerr(1, "WARNING: error processing %s\n",
587                                  tdi->dirname);
588
589         }
590         return retval;
591 }
592
593 static int
594 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
595             gss_buffer_desc *context_token)
596 {
597         char    *buf = NULL, *p = NULL, *end = NULL;
598         unsigned int timeout = context_timeout;
599         unsigned int buf_size = 0;
600
601         printerr(1, "doing downcall\n");
602         buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
603                 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
604                 sizeof(context_token->length) + context_token->length;
605         p = buf = malloc(buf_size);
606         end = buf + buf_size;
607
608         if (WRITE_BYTES(&p, end, uid)) goto out_err;
609         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
610         if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
611         if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
612         if (write_buffer(&p, end, context_token)) goto out_err;
613
614         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
615         if (buf) free(buf);
616         return 0;
617 out_err:
618         if (buf) free(buf);
619         printerr(1, "Failed to write downcall!\n");
620         return -1;
621 }
622
623 static int
624 do_error_downcall(int k5_fd, uid_t uid, int err)
625 {
626         char    buf[1024];
627         char    *p = buf, *end = buf + 1024;
628         unsigned int timeout = 0;
629         int     zero = 0;
630
631         printerr(1, "doing error downcall\n");
632
633         if (WRITE_BYTES(&p, end, uid)) goto out_err;
634         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
635         /* use seq_win = 0 to indicate an error: */
636         if (WRITE_BYTES(&p, end, zero)) goto out_err;
637         if (WRITE_BYTES(&p, end, err)) goto out_err;
638
639         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
640         return 0;
641 out_err:
642         printerr(1, "Failed to write error downcall!\n");
643         return -1;
644 }
645
646 /*
647  * If the port isn't already set, do an rpcbind query to the remote server
648  * using the program and version and get the port. 
649  *
650  * Newer kernels send the value of the port= mount option in the "info"
651  * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
652  * of the port= option or '2049'. The port field in a new sockaddr should
653  * reflect the value that was sent by the kernel.
654  */
655 static int
656 populate_port(struct sockaddr *sa, const socklen_t salen,
657               const rpcprog_t program, const rpcvers_t version,
658               const unsigned short protocol)
659 {
660         struct sockaddr_in      *s4 = (struct sockaddr_in *) sa;
661 #ifdef IPV6_SUPPORTED
662         struct sockaddr_in6     *s6 = (struct sockaddr_in6 *) sa;
663 #endif /* IPV6_SUPPORTED */
664         unsigned short          port;
665
666         /*
667          * Newer kernels send the port in the upcall. If we already have
668          * the port, there's no need to look it up.
669          */
670         switch (sa->sa_family) {
671         case AF_INET:
672                 if (s4->sin_port != 0) {
673                         printerr(2, "DEBUG: port already set to %d\n",
674                                  ntohs(s4->sin_port));
675                         return 1;
676                 }
677                 break;
678 #ifdef IPV6_SUPPORTED
679         case AF_INET6:
680                 if (s6->sin6_port != 0) {
681                         printerr(2, "DEBUG: port already set to %d\n",
682                                  ntohs(s6->sin6_port));
683                         return 1;
684                 }
685                 break;
686 #endif /* IPV6_SUPPORTED */
687         default:
688                 printerr(0, "ERROR: unsupported address family %d\n",
689                             sa->sa_family);
690                 return 0;
691         }
692
693         /*
694          * Newer kernels that send the port in the upcall set the value to
695          * 2049 for NFSv4 mounts when one isn't specified. The check below is
696          * only for kernels that don't send the port in the upcall. For those
697          * we either have to do an rpcbind query or set it to the standard
698          * port. Doing a query could be problematic (firewalls, etc), so take
699          * the latter approach.
700          */
701         if (program == 100003 && version == 4) {
702                 port = 2049;
703                 goto set_port;
704         }
705
706         port = nfs_getport(sa, salen, program, version, protocol);
707         if (!port) {
708                 printerr(0, "ERROR: unable to obtain port for prog %ld "
709                             "vers %ld\n", program, version);
710                 return 0;
711         }
712
713 set_port:
714         printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port,
715                  program, version);
716
717         switch (sa->sa_family) {
718         case AF_INET:
719                 s4->sin_port = htons(port);
720                 break;
721 #ifdef IPV6_SUPPORTED
722         case AF_INET6:
723                 s6->sin6_port = htons(port);
724                 break;
725 #endif /* IPV6_SUPPORTED */
726         }
727
728         return 1;
729 }
730
731 /*
732  * Create an RPC connection and establish an authenticated
733  * gss context with a server.
734  */
735 int create_auth_rpc_client(struct clnt_info *clp,
736                            CLIENT **clnt_return,
737                            AUTH **auth_return,
738                            uid_t uid,
739                            int authtype)
740 {
741         CLIENT                  *rpc_clnt = NULL;
742         struct rpc_gss_sec      sec;
743         AUTH                    *auth = NULL;
744         uid_t                   save_uid = -1;
745         int                     retval = -1;
746         OM_uint32               min_stat;
747         char                    rpc_errmsg[1024];
748         int                     protocol;
749         struct timeval          timeout = {5, 0};
750         struct sockaddr         *addr = (struct sockaddr *) &clp->addr;
751         socklen_t               salen;
752
753         /* Create the context as the user (not as root) */
754         save_uid = geteuid();
755         if (setfsuid(uid) != 0) {
756                 printerr(0, "WARNING: Failed to setfsuid for "
757                             "user with uid %d\n", uid);
758                 goto out_fail;
759         }
760         printerr(2, "creating context using fsuid %d (save_uid %d)\n",
761                         uid, save_uid);
762
763         sec.qop = GSS_C_QOP_DEFAULT;
764         sec.svc = RPCSEC_GSS_SVC_NONE;
765         sec.cred = GSS_C_NO_CREDENTIAL;
766         sec.req_flags = 0;
767         if (authtype == AUTHTYPE_KRB5) {
768                 sec.mech = (gss_OID)&krb5oid;
769                 sec.req_flags = GSS_C_MUTUAL_FLAG;
770         }
771         else if (authtype == AUTHTYPE_SPKM3) {
772                 sec.mech = (gss_OID)&spkm3oid;
773                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
774                  * Need a way to switch....
775                  */
776                 sec.req_flags = GSS_C_MUTUAL_FLAG;
777         }
778         else {
779                 printerr(0, "ERROR: Invalid authentication type (%d) "
780                         "in create_auth_rpc_client\n", authtype);
781                 goto out_fail;
782         }
783
784
785         if (authtype == AUTHTYPE_KRB5) {
786 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
787                 /*
788                  * Do this before creating rpc connection since we won't need
789                  * rpc connection if it fails!
790                  */
791                 if (limit_krb5_enctypes(&sec, uid)) {
792                         printerr(1, "WARNING: Failed while limiting krb5 "
793                                     "encryption types for user with uid %d\n",
794                                  uid);
795                         goto out_fail;
796                 }
797 #endif
798         }
799
800         /* create an rpc connection to the nfs server */
801
802         printerr(2, "creating %s client for server %s\n", clp->protocol,
803                         clp->servername);
804
805         if ((strcmp(clp->protocol, "tcp")) == 0) {
806                 protocol = IPPROTO_TCP;
807         } else if ((strcmp(clp->protocol, "udp")) == 0) {
808                 protocol = IPPROTO_UDP;
809         } else {
810                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
811                          "for connection to server %s for user with uid %d\n",
812                          clp->protocol, clp->servername, uid);
813                 goto out_fail;
814         }
815
816         switch (addr->sa_family) {
817         case AF_INET:
818                 salen = sizeof(struct sockaddr_in);
819                 break;
820 #ifdef IPV6_SUPPORTED
821         case AF_INET6:
822                 salen = sizeof(struct sockaddr_in6);
823                 break;
824 #endif /* IPV6_SUPPORTED */
825         default:
826                 printerr(1, "ERROR: Unknown address family %d\n",
827                          addr->sa_family);
828                 goto out_fail;
829         }
830
831         if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
832                 goto out_fail;
833
834         rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
835                                      clp->vers, &timeout);
836         if (!rpc_clnt) {
837                 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
838                          "WARNING: can't create %s rpc_clnt to server %s for "
839                          "user with uid %d",
840                          protocol == IPPROTO_TCP ? "tcp" : "udp",
841                          clp->servername, uid);
842                 printerr(0, "%s\n",
843                          clnt_spcreateerror(rpc_errmsg));
844                 goto out_fail;
845         }
846
847         printerr(2, "creating context with server %s\n", clp->servicename);
848         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
849         if (!auth) {
850                 /* Our caller should print appropriate message */
851                 printerr(2, "WARNING: Failed to create %s context for "
852                             "user with uid %d for server %s\n",
853                         (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
854                          uid, clp->servername);
855                 goto out_fail;
856         }
857
858         /* Success !!! */
859         rpc_clnt->cl_auth = auth;
860         *clnt_return = rpc_clnt;
861         *auth_return = auth;
862         retval = 0;
863
864   out:
865         if (sec.cred != GSS_C_NO_CREDENTIAL)
866                 gss_release_cred(&min_stat, &sec.cred);
867         /* Restore euid to original value */
868         if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
869                 printerr(0, "WARNING: Failed to restore fsuid"
870                             " to uid %d from %d\n", save_uid, uid);
871         }
872         return retval;
873
874   out_fail:
875         /* Only destroy here if failure.  Otherwise, caller is responsible */
876         if (rpc_clnt) clnt_destroy(rpc_clnt);
877
878         goto out;
879 }
880
881 /*
882  * this code uses the userland rpcsec gss library to create a krb5
883  * context on behalf of the kernel
884  */
885 static void
886 process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd)
887 {
888         CLIENT                  *rpc_clnt = NULL;
889         AUTH                    *auth = NULL;
890         struct authgss_private_data pd;
891         gss_buffer_desc         token;
892         char                    **credlist = NULL;
893         char                    **ccname;
894         char                    **dirname;
895         int                     create_resp = -1;
896
897         printerr(1, "handling krb5 upcall (%s)\n", clp->dirname);
898
899         token.length = 0;
900         token.value = NULL;
901         memset(&pd, 0, sizeof(struct authgss_private_data));
902
903         if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
904                 /* Tell krb5 gss which credentials cache to use */
905                 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
906                         if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
907                                 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
908                                                              AUTHTYPE_KRB5);
909                         if (create_resp == 0)
910                                 break;
911                 }
912         }
913         if (create_resp != 0) {
914                 if (uid == 0 && root_uses_machine_creds == 1) {
915                         int nocache = 0;
916                         int success = 0;
917                         do {
918                                 gssd_refresh_krb5_machine_credential(clp->servername,
919                                                                      NULL, nocache);
920                                 /*
921                                  * Get a list of credential cache names and try each
922                                  * of them until one works or we've tried them all
923                                  */
924                                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
925                                         printerr(0, "ERROR: No credentials found "
926                                                  "for connection to server %s\n",
927                                                  clp->servername);
928                                                 goto out_return_error;
929                                 }
930                                 for (ccname = credlist; ccname && *ccname; ccname++) {
931                                         gssd_setup_krb5_machine_gss_ccache(*ccname);
932                                         if ((create_auth_rpc_client(clp, &rpc_clnt,
933                                                                     &auth, uid,
934                                                                     AUTHTYPE_KRB5)) == 0) {
935                                                 /* Success! */
936                                                 success++;
937                                                 break;
938                                         } 
939                                         printerr(2, "WARNING: Failed to create machine krb5 context "
940                                                  "with credentials cache %s for server %s\n",
941                                                  *ccname, clp->servername);
942                                 }
943                                 gssd_free_krb5_machine_cred_list(credlist);                     
944                                 if (!success) {
945                                         if(nocache == 0) {
946                                                 nocache++;
947                                                 printerr(2, "WARNING: Machine cache is prematurely expired or corrupted "
948                                                             "trying to recreate cache for server %s\n", clp->servername);
949                                         } else {
950                                                 printerr(1, "WARNING: Failed to create machine krb5 context "
951                                                  "with any credentials cache for server %s\n",
952                                                  clp->servername);
953                                                 goto out_return_error;
954                                         }
955                                 }
956                         } while(!success);
957                 } else {
958                         printerr(1, "WARNING: Failed to create krb5 context "
959                                  "for user with uid %d for server %s\n",
960                                  uid, clp->servername);
961                         goto out_return_error;
962                 }
963         }
964
965         if (!authgss_get_private_data(auth, &pd)) {
966                 printerr(1, "WARNING: Failed to obtain authentication "
967                             "data for user with uid %d for server %s\n",
968                          uid, clp->servername);
969                 goto out_return_error;
970         }
971
972         if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
973                 printerr(0, "WARNING: Failed to serialize krb5 context for "
974                             "user with uid %d for server %s\n",
975                          uid, clp->servername);
976                 goto out_return_error;
977         }
978
979         do_downcall(fd, uid, &pd, &token);
980
981 out:
982         if (token.value)
983                 free(token.value);
984 #ifndef HAVE_LIBTIRPC
985         if (pd.pd_ctx_hndl.length != 0)
986                 authgss_free_private_data(&pd);
987 #endif
988         if (auth)
989                 AUTH_DESTROY(auth);
990         if (rpc_clnt)
991                 clnt_destroy(rpc_clnt);
992         return;
993
994 out_return_error:
995         do_error_downcall(fd, uid, -1);
996         goto out;
997 }
998
999 /*
1000  * this code uses the userland rpcsec gss library to create an spkm3
1001  * context on behalf of the kernel
1002  */
1003 static void
1004 process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd)
1005 {
1006         CLIENT                  *rpc_clnt = NULL;
1007         AUTH                    *auth = NULL;
1008         struct authgss_private_data pd;
1009         gss_buffer_desc         token;
1010
1011         printerr(2, "handling spkm3 upcall (%s)\n", clp->dirname);
1012
1013         token.length = 0;
1014         token.value = NULL;
1015
1016         if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
1017                 printerr(0, "WARNING: Failed to create spkm3 context for "
1018                             "user with uid %d\n", uid);
1019                 goto out_return_error;
1020         }
1021
1022         if (!authgss_get_private_data(auth, &pd)) {
1023                 printerr(0, "WARNING: Failed to obtain authentication "
1024                             "data for user with uid %d for server %s\n",
1025                          uid, clp->servername);
1026                 goto out_return_error;
1027         }
1028
1029         if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) {
1030                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
1031                             "user with uid %d for server\n",
1032                          uid, clp->servername);
1033                 goto out_return_error;
1034         }
1035
1036         do_downcall(fd, uid, &pd, &token);
1037
1038 out:
1039         if (token.value)
1040                 free(token.value);
1041         if (auth)
1042                 AUTH_DESTROY(auth);
1043         if (rpc_clnt)
1044                 clnt_destroy(rpc_clnt);
1045         return;
1046
1047 out_return_error:
1048         do_error_downcall(fd, uid, -1);
1049         goto out;
1050 }
1051
1052 void
1053 handle_krb5_upcall(struct clnt_info *clp)
1054 {
1055         uid_t                   uid;
1056
1057         if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1058                 printerr(0, "WARNING: failed reading uid from krb5 "
1059                             "upcall pipe: %s\n", strerror(errno));
1060                 return;
1061         }
1062
1063         return process_krb5_upcall(clp, uid, clp->krb5_fd);
1064 }
1065
1066 void
1067 handle_spkm3_upcall(struct clnt_info *clp)
1068 {
1069         uid_t                   uid;
1070
1071         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1072                 printerr(0, "WARNING: failed reading uid from spkm3 "
1073                          "upcall pipe: %s\n", strerror(errno));
1074                 return;
1075         }
1076
1077         return process_spkm3_upcall(clp, uid, clp->spkm3_fd);
1078 }
1079
1080 void
1081 handle_gssd_upcall(struct clnt_info *clp)
1082 {
1083         uid_t                   uid;
1084         char                    *lbuf = NULL;
1085         int                     lbuflen = 0;
1086         char                    *p;
1087         char                    *mech = NULL;
1088
1089         printerr(1, "handling gssd upcall (%s)\n", clp->dirname);
1090
1091         if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) {
1092                 printerr(0, "WARNING: handle_gssd_upcall: "
1093                             "failed reading request\n");
1094                 return;
1095         }
1096         printerr(2, "%s: '%s'\n", __func__, lbuf);
1097
1098         /* find the mechanism name */
1099         if ((p = strstr(lbuf, "mech=")) != NULL) {
1100                 mech = malloc(lbuflen);
1101                 if (!mech)
1102                         goto out;
1103                 if (sscanf(p, "mech=%s", mech) != 1) {
1104                         printerr(0, "WARNING: handle_gssd_upcall: "
1105                                     "failed to parse gss mechanism name "
1106                                     "in upcall string '%s'\n", lbuf);
1107                         goto out;
1108                 }
1109         } else {
1110                 printerr(0, "WARNING: handle_gssd_upcall: "
1111                             "failed to find gss mechanism name "
1112                             "in upcall string '%s'\n", lbuf);
1113                 goto out;
1114         }
1115
1116         /* read uid */
1117         if ((p = strstr(lbuf, "uid=")) != NULL) {
1118                 if (sscanf(p, "uid=%d", &uid) != 1) {
1119                         printerr(0, "WARNING: handle_gssd_upcall: "
1120                                     "failed to parse uid "
1121                                     "in upcall string '%s'\n", lbuf);
1122                         goto out;
1123                 }
1124         } else {
1125                 printerr(0, "WARNING: handle_gssd_upcall: "
1126                             "failed to find uid "
1127                             "in upcall string '%s'\n", lbuf);
1128                 goto out;
1129         }
1130
1131
1132         if (strcmp(mech, "krb5") == 0)
1133                 process_krb5_upcall(clp, uid, clp->gssd_fd);
1134         else if (strcmp(mech, "spkm3") == 0)
1135                 process_spkm3_upcall(clp, uid, clp->gssd_fd);
1136         else
1137                 printerr(0, "WARNING: handle_gssd_upcall: "
1138                             "received unknown gss mech '%s'\n", mech);
1139
1140 out:
1141         free(lbuf);
1142         free(mech);
1143         return; 
1144 }
1145