]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/gssd_proc.c
gssd: process service= attribute in new 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, char *tgtname,
887                     char *service)
888 {
889         CLIENT                  *rpc_clnt = NULL;
890         AUTH                    *auth = NULL;
891         struct authgss_private_data pd;
892         gss_buffer_desc         token;
893         char                    **credlist = NULL;
894         char                    **ccname;
895         char                    **dirname;
896         int                     create_resp = -1;
897
898         printerr(1, "handling krb5 upcall (%s)\n", clp->dirname);
899
900         if (tgtname) {
901                 if (clp->servicename) {
902                         free(clp->servicename);
903                         clp->servicename = strdup(tgtname);
904                 }
905         }
906         token.length = 0;
907         token.value = NULL;
908         memset(&pd, 0, sizeof(struct authgss_private_data));
909
910         /*
911          * If "service" is specified, then the kernel is indicating that
912          * we must use machine credentials for this request.  (Regardless
913          * of the uid value or the setting of root_uses_machine_creds.)
914          * If the service value is "*", then any service name can be used.
915          * Otherwise, it specifies the service name that should be used.
916          * (For now, the values of service will only be "*" or "nfs".)
917          *
918          * Restricting gssd to use "nfs" service name is needed for when
919          * the NFS server is doing a callback to the NFS client.  In this
920          * case, the NFS server has to authenticate itself as "nfs" --
921          * even if there are other service keys such as "host" or "root"
922          * in the keytab.
923          *
924          * Another case when the kernel may specify the service attribute
925          * is when gssd is being asked to create the context for a
926          * SETCLIENT_ID operation.  In this case, machine credentials
927          * must be used for the authentication.  However, the service name
928          * used for this case is not important.
929          *
930          */
931         printerr(2, "%s: service is '%s'\n", __func__,
932                  service ? service : "<null>");
933         if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
934                                 service == NULL)) {
935                 /* Tell krb5 gss which credentials cache to use */
936                 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
937                         if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
938                                 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
939                                                              AUTHTYPE_KRB5);
940                         if (create_resp == 0)
941                                 break;
942                 }
943         }
944         if (create_resp != 0) {
945                 if (uid == 0 && (root_uses_machine_creds == 1 ||
946                                 service != NULL)) {
947                         int nocache = 0;
948                         int success = 0;
949                         do {
950                                 gssd_refresh_krb5_machine_credential(clp->servername,
951                                                                      NULL, service);
952                                 /*
953                                  * Get a list of credential cache names and try each
954                                  * of them until one works or we've tried them all
955                                  */
956                                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
957                                         printerr(0, "ERROR: No credentials found "
958                                                  "for connection to server %s\n",
959                                                  clp->servername);
960                                                 goto out_return_error;
961                                 }
962                                 for (ccname = credlist; ccname && *ccname; ccname++) {
963                                         gssd_setup_krb5_machine_gss_ccache(*ccname);
964                                         if ((create_auth_rpc_client(clp, &rpc_clnt,
965                                                                     &auth, uid,
966                                                                     AUTHTYPE_KRB5)) == 0) {
967                                                 /* Success! */
968                                                 success++;
969                                                 break;
970                                         } 
971                                         printerr(2, "WARNING: Failed to create machine krb5 context "
972                                                  "with credentials cache %s for server %s\n",
973                                                  *ccname, clp->servername);
974                                 }
975                                 gssd_free_krb5_machine_cred_list(credlist);                     
976                                 if (!success) {
977                                         if(nocache == 0) {
978                                                 nocache++;
979                                                 printerr(2, "WARNING: Machine cache is prematurely expired or corrupted "
980                                                             "trying to recreate cache for server %s\n", clp->servername);
981                                         } else {
982                                                 printerr(1, "WARNING: Failed to create machine krb5 context "
983                                                  "with any credentials cache for server %s\n",
984                                                  clp->servername);
985                                                 goto out_return_error;
986                                         }
987                                 }
988                         } while(!success);
989                 } else {
990                         printerr(1, "WARNING: Failed to create krb5 context "
991                                  "for user with uid %d for server %s\n",
992                                  uid, clp->servername);
993                         goto out_return_error;
994                 }
995         }
996
997         if (!authgss_get_private_data(auth, &pd)) {
998                 printerr(1, "WARNING: Failed to obtain authentication "
999                             "data for user with uid %d for server %s\n",
1000                          uid, clp->servername);
1001                 goto out_return_error;
1002         }
1003
1004         if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
1005                 printerr(0, "WARNING: Failed to serialize krb5 context for "
1006                             "user with uid %d for server %s\n",
1007                          uid, clp->servername);
1008                 goto out_return_error;
1009         }
1010
1011         do_downcall(fd, uid, &pd, &token);
1012
1013 out:
1014         if (token.value)
1015                 free(token.value);
1016 #ifndef HAVE_LIBTIRPC
1017         if (pd.pd_ctx_hndl.length != 0)
1018                 authgss_free_private_data(&pd);
1019 #endif
1020         if (auth)
1021                 AUTH_DESTROY(auth);
1022         if (rpc_clnt)
1023                 clnt_destroy(rpc_clnt);
1024         return;
1025
1026 out_return_error:
1027         do_error_downcall(fd, uid, -1);
1028         goto out;
1029 }
1030
1031 /*
1032  * this code uses the userland rpcsec gss library to create an spkm3
1033  * context on behalf of the kernel
1034  */
1035 static void
1036 process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd)
1037 {
1038         CLIENT                  *rpc_clnt = NULL;
1039         AUTH                    *auth = NULL;
1040         struct authgss_private_data pd;
1041         gss_buffer_desc         token;
1042
1043         printerr(2, "handling spkm3 upcall (%s)\n", clp->dirname);
1044
1045         token.length = 0;
1046         token.value = NULL;
1047
1048         if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
1049                 printerr(0, "WARNING: Failed to create spkm3 context for "
1050                             "user with uid %d\n", uid);
1051                 goto out_return_error;
1052         }
1053
1054         if (!authgss_get_private_data(auth, &pd)) {
1055                 printerr(0, "WARNING: Failed to obtain authentication "
1056                             "data for user with uid %d for server %s\n",
1057                          uid, clp->servername);
1058                 goto out_return_error;
1059         }
1060
1061         if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) {
1062                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
1063                             "user with uid %d for server\n",
1064                          uid, clp->servername);
1065                 goto out_return_error;
1066         }
1067
1068         do_downcall(fd, uid, &pd, &token);
1069
1070 out:
1071         if (token.value)
1072                 free(token.value);
1073         if (auth)
1074                 AUTH_DESTROY(auth);
1075         if (rpc_clnt)
1076                 clnt_destroy(rpc_clnt);
1077         return;
1078
1079 out_return_error:
1080         do_error_downcall(fd, uid, -1);
1081         goto out;
1082 }
1083
1084 void
1085 handle_krb5_upcall(struct clnt_info *clp)
1086 {
1087         uid_t                   uid;
1088
1089         if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1090                 printerr(0, "WARNING: failed reading uid from krb5 "
1091                             "upcall pipe: %s\n", strerror(errno));
1092                 return;
1093         }
1094
1095         return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL);
1096 }
1097
1098 void
1099 handle_spkm3_upcall(struct clnt_info *clp)
1100 {
1101         uid_t                   uid;
1102
1103         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1104                 printerr(0, "WARNING: failed reading uid from spkm3 "
1105                          "upcall pipe: %s\n", strerror(errno));
1106                 return;
1107         }
1108
1109         return process_spkm3_upcall(clp, uid, clp->spkm3_fd);
1110 }
1111
1112 void
1113 handle_gssd_upcall(struct clnt_info *clp)
1114 {
1115         uid_t                   uid;
1116         char                    *lbuf = NULL;
1117         int                     lbuflen = 0;
1118         char                    *p;
1119         char                    *mech = NULL;
1120         char                    *target = NULL;
1121         char                    *service = NULL;
1122
1123         printerr(1, "handling gssd upcall (%s)\n", clp->dirname);
1124
1125         if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) {
1126                 printerr(0, "WARNING: handle_gssd_upcall: "
1127                             "failed reading request\n");
1128                 return;
1129         }
1130         printerr(2, "%s: '%s'\n", __func__, lbuf);
1131
1132         /* find the mechanism name */
1133         if ((p = strstr(lbuf, "mech=")) != NULL) {
1134                 mech = malloc(lbuflen);
1135                 if (!mech)
1136                         goto out;
1137                 if (sscanf(p, "mech=%s", mech) != 1) {
1138                         printerr(0, "WARNING: handle_gssd_upcall: "
1139                                     "failed to parse gss mechanism name "
1140                                     "in upcall string '%s'\n", lbuf);
1141                         goto out;
1142                 }
1143         } else {
1144                 printerr(0, "WARNING: handle_gssd_upcall: "
1145                             "failed to find gss mechanism name "
1146                             "in upcall string '%s'\n", lbuf);
1147                 goto out;
1148         }
1149
1150         /* read uid */
1151         if ((p = strstr(lbuf, "uid=")) != NULL) {
1152                 if (sscanf(p, "uid=%d", &uid) != 1) {
1153                         printerr(0, "WARNING: handle_gssd_upcall: "
1154                                     "failed to parse uid "
1155                                     "in upcall string '%s'\n", lbuf);
1156                         goto out;
1157                 }
1158         } else {
1159                 printerr(0, "WARNING: handle_gssd_upcall: "
1160                             "failed to find uid "
1161                             "in upcall string '%s'\n", lbuf);
1162                 goto out;
1163         }
1164
1165         /* read target name */
1166         if ((p = strstr(lbuf, "target=")) != NULL) {
1167                 target = malloc(lbuflen);
1168                 if (!target)
1169                         goto out;
1170                 if (sscanf(p, "target=%s", target) != 1) {
1171                         printerr(0, "WARNING: handle_gssd_upcall: "
1172                                     "failed to parse target name "
1173                                     "in upcall string '%s'\n", lbuf);
1174                         goto out;
1175                 }
1176         }
1177
1178         /*
1179          * read the service name
1180          *
1181          * The presence of attribute "service=" indicates that machine
1182          * credentials should be used for this request.  If the value
1183          * is "*", then any machine credentials available can be used.
1184          * If the value is anything else, then machine credentials for
1185          * the specified service name (always "nfs" for now) should be
1186          * used.
1187          */
1188         if ((p = strstr(lbuf, "service=")) != NULL) {
1189                 service = malloc(lbuflen);
1190                 if (!service)
1191                         goto out;
1192                 if (sscanf(p, "service=%s", service) != 1) {
1193                         printerr(0, "WARNING: handle_gssd_upcall: "
1194                                     "failed to parse service type "
1195                                     "in upcall string '%s'\n", lbuf);
1196                         goto out;
1197                 }
1198         }
1199
1200         if (strcmp(mech, "krb5") == 0)
1201                 process_krb5_upcall(clp, uid, clp->gssd_fd, target, service);
1202         else if (strcmp(mech, "spkm3") == 0)
1203                 process_spkm3_upcall(clp, uid, clp->gssd_fd);
1204         else
1205                 printerr(0, "WARNING: handle_gssd_upcall: "
1206                             "received unknown gss mech '%s'\n", mech);
1207
1208 out:
1209         free(lbuf);
1210         free(mech);
1211         free(target);
1212         free(service);
1213         return; 
1214 }
1215