]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/gssd_proc.c
Gssd blindly caches machine credentials
[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
77 /*
78  * pollarray:
79  *      array of struct pollfd suitable to pass to poll. initialized to
80  *      zero - a zero struct is ignored by poll() because the events mask is 0.
81  *
82  * clnt_list:
83  *      linked list of struct clnt_info which associates a clntXXX directory
84  *      with an index into pollarray[], and other basic data about that client.
85  *
86  * Directory structure: created by the kernel nfs client
87  *      {pipefs_nfsdir}/clntXX             : one per rpc_clnt struct in the kernel
88  *      {pipefs_nfsdir}/clntXX/krb5        : read uid for which kernel wants
89  *                                          a context, write the resulting context
90  *      {pipefs_nfsdir}/clntXX/info        : stores info such as server name
91  *
92  * Algorithm:
93  *      Poll all {pipefs_nfsdir}/clntXX/krb5 files.  When ready, data read
94  *      is a uid; performs rpcsec_gss context initialization protocol to
95  *      get a cred for that user.  Writes result to corresponding krb5 file
96  *      in a form the kernel code will understand.
97  *      In addition, we make sure we are notified whenever anything is
98  *      created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories,
99  *      and rescan the whole {pipefs_nfsdir} when this happens.
100  */
101
102 struct pollfd * pollarray;
103
104 int pollsize;  /* the size of pollaray (in pollfd's) */
105
106 /*
107  * convert a presentation address string to a sockaddr_storage struct. Returns
108  * true on success and false on failure.
109  *
110  * Note that we do not populate the sin6_scope_id field here for IPv6 addrs.
111  * gssd nececessarily relies on hostname resolution and DNS AAAA records
112  * do not generally contain scope-id's. This means that GSSAPI auth really
113  * can't work with IPv6 link-local addresses.
114  *
115  * We *could* consider changing this if we did something like adopt the
116  * Microsoft "standard" of using the ipv6-literal.net domainname, but it's
117  * not really feasible at present.
118  */
119 static int
120 addrstr_to_sockaddr(struct sockaddr *sa, const char *addr, const int port)
121 {
122         struct sockaddr_in      *s4 = (struct sockaddr_in *) sa;
123 #ifdef IPV6_SUPPORTED
124         struct sockaddr_in6     *s6 = (struct sockaddr_in6 *) sa;
125 #endif /* IPV6_SUPPORTED */
126
127         if (inet_pton(AF_INET, addr, &s4->sin_addr)) {
128                 s4->sin_family = AF_INET;
129                 s4->sin_port = htons(port);
130 #ifdef IPV6_SUPPORTED
131         } else if (inet_pton(AF_INET6, addr, &s6->sin6_addr)) {
132                 s6->sin6_family = AF_INET6;
133                 s6->sin6_port = htons(port);
134 #endif /* IPV6_SUPPORTED */
135         } else {
136                 printerr(0, "ERROR: unable to convert %s to address\n", addr);
137                 return 0;
138         }
139
140         return 1;
141 }
142
143 /*
144  * convert a sockaddr to a hostname
145  */
146 static char *
147 sockaddr_to_hostname(const struct sockaddr *sa, const char *addr)
148 {
149         socklen_t               addrlen;
150         int                     err;
151         char                    *hostname;
152         char                    hbuf[NI_MAXHOST];
153
154         switch (sa->sa_family) {
155         case AF_INET:
156                 addrlen = sizeof(struct sockaddr_in);
157                 break;
158 #ifdef IPV6_SUPPORTED
159         case AF_INET6:
160                 addrlen = sizeof(struct sockaddr_in6);
161                 break;
162 #endif /* IPV6_SUPPORTED */
163         default:
164                 printerr(0, "ERROR: unrecognized addr family %d\n",
165                          sa->sa_family);
166                 return NULL;
167         }
168
169         err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0,
170                           NI_NAMEREQD);
171         if (err) {
172                 printerr(0, "ERROR: unable to resolve %s to hostname: %s\n",
173                          addr, err == EAI_SYSTEM ? strerror(err) :
174                                                    gai_strerror(err));
175                 return NULL;
176         }
177
178         hostname = strdup(hbuf);
179
180         return hostname;
181 }
182
183 /* XXX buffer problems: */
184 static int
185 read_service_info(char *info_file_name, char **servicename, char **servername,
186                   int *prog, int *vers, char **protocol,
187                   struct sockaddr *addr) {
188 #define INFOBUFLEN 256
189         char            buf[INFOBUFLEN + 1];
190         static char     dummy[128];
191         int             nbytes;
192         static char     service[128];
193         static char     address[128];
194         char            program[16];
195         char            version[16];
196         char            protoname[16];
197         char            cb_port[128];
198         char            *p;
199         int             fd = -1;
200         int             numfields;
201         int             port = 0;
202
203         *servicename = *servername = *protocol = NULL;
204
205         if ((fd = open(info_file_name, O_RDONLY)) == -1) {
206                 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
207                          strerror(errno));
208                 goto fail;
209         }
210         if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
211                 goto fail;
212         close(fd);
213         buf[nbytes] = '\0';
214
215         numfields = sscanf(buf,"RPC server: %127s\n"
216                    "service: %127s %15s version %15s\n"
217                    "address: %127s\n"
218                    "protocol: %15s\n",
219                    dummy,
220                    service, program, version,
221                    address,
222                    protoname);
223
224         if (numfields == 5) {
225                 strcpy(protoname, "tcp");
226         } else if (numfields != 6) {
227                 goto fail;
228         }
229
230         cb_port[0] = '\0';
231         if ((p = strstr(buf, "port")) != NULL)
232                 sscanf(p, "port: %127s\n", cb_port);
233
234         /* check service, program, and version */
235         if(memcmp(service, "nfs", 3)) return -1;
236         *prog = atoi(program + 1); /* skip open paren */
237         *vers = atoi(version);
238         if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
239                 goto fail;
240
241         if (cb_port[0] != '\0') {
242                 port = atoi(cb_port);
243                 if (port < 0 || port > 65535)
244                         goto fail;
245         }
246
247         if (!addrstr_to_sockaddr(addr, address, port))
248                 goto fail;
249
250         *servername = sockaddr_to_hostname(addr, address);
251         if (*servername == NULL)
252                 goto fail;
253
254         nbytes = snprintf(buf, INFOBUFLEN, "%s@%s", service, *servername);
255         if (nbytes > INFOBUFLEN)
256                 goto fail;
257
258         if (!(*servicename = calloc(strlen(buf) + 1, 1)))
259                 goto fail;
260         memcpy(*servicename, buf, strlen(buf));
261
262         if (!(*protocol = strdup(protoname)))
263                 goto fail;
264         return 0;
265 fail:
266         printerr(0, "ERROR: failed to read service info\n");
267         if (fd != -1) close(fd);
268         free(*servername);
269         free(*servicename);
270         free(*protocol);
271         *servicename = *servername = *protocol = NULL;
272         return -1;
273 }
274
275 static void
276 destroy_client(struct clnt_info *clp)
277 {
278         if (clp->krb5_poll_index != -1)
279                 memset(&pollarray[clp->krb5_poll_index], 0,
280                                         sizeof(struct pollfd));
281         if (clp->spkm3_poll_index != -1)
282                 memset(&pollarray[clp->spkm3_poll_index], 0,
283                                         sizeof(struct pollfd));
284         if (clp->dir_fd != -1) close(clp->dir_fd);
285         if (clp->krb5_fd != -1) close(clp->krb5_fd);
286         if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
287         free(clp->dirname);
288         free(clp->servicename);
289         free(clp->servername);
290         free(clp->protocol);
291         free(clp);
292 }
293
294 static struct clnt_info *
295 insert_new_clnt(void)
296 {
297         struct clnt_info        *clp = NULL;
298
299         if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
300                 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
301                          strerror(errno));
302                 goto out;
303         }
304         clp->krb5_poll_index = -1;
305         clp->spkm3_poll_index = -1;
306         clp->krb5_fd = -1;
307         clp->spkm3_fd = -1;
308         clp->dir_fd = -1;
309
310         TAILQ_INSERT_HEAD(&clnt_list, clp, list);
311 out:
312         return clp;
313 }
314
315 static int
316 process_clnt_dir_files(struct clnt_info * clp)
317 {
318         char    kname[32];
319         char    sname[32];
320         char    info_file_name[32];
321
322         if (clp->krb5_fd == -1) {
323                 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
324                 clp->krb5_fd = open(kname, O_RDWR);
325         }
326         if (clp->spkm3_fd == -1) {
327                 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
328                 clp->spkm3_fd = open(sname, O_RDWR);
329         }
330         if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
331                 return -1;
332         snprintf(info_file_name, sizeof(info_file_name), "%s/info",
333                         clp->dirname);
334         if ((clp->servicename == NULL) &&
335              read_service_info(info_file_name, &clp->servicename,
336                                 &clp->servername, &clp->prog, &clp->vers,
337                                 &clp->protocol, (struct sockaddr *) &clp->addr))
338                 return -1;
339         return 0;
340 }
341
342 static int
343 get_poll_index(int *ind)
344 {
345         int i;
346
347         *ind = -1;
348         for (i=0; i<FD_ALLOC_BLOCK; i++) {
349                 if (pollarray[i].events == 0) {
350                         *ind = i;
351                         break;
352                 }
353         }
354         if (*ind == -1) {
355                 printerr(0, "ERROR: No pollarray slots open\n");
356                 return -1;
357         }
358         return 0;
359 }
360
361
362 static int
363 insert_clnt_poll(struct clnt_info *clp)
364 {
365         if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
366                 if (get_poll_index(&clp->krb5_poll_index)) {
367                         printerr(0, "ERROR: Too many krb5 clients\n");
368                         return -1;
369                 }
370                 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
371                 pollarray[clp->krb5_poll_index].events |= POLLIN;
372         }
373
374         if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
375                 if (get_poll_index(&clp->spkm3_poll_index)) {
376                         printerr(0, "ERROR: Too many spkm3 clients\n");
377                         return -1;
378                 }
379                 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
380                 pollarray[clp->spkm3_poll_index].events |= POLLIN;
381         }
382
383         return 0;
384 }
385
386 static void
387 process_clnt_dir(char *dir)
388 {
389         struct clnt_info *      clp;
390
391         if (!(clp = insert_new_clnt()))
392                 goto fail_destroy_client;
393
394         if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
395                 goto fail_destroy_client;
396         }
397         memcpy(clp->dirname, dir, strlen(dir));
398         if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
399                 printerr(0, "ERROR: can't open %s: %s\n",
400                          clp->dirname, strerror(errno));
401                 goto fail_destroy_client;
402         }
403         fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
404         fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
405
406         if (process_clnt_dir_files(clp))
407                 goto fail_keep_client;
408
409         if (insert_clnt_poll(clp))
410                 goto fail_destroy_client;
411
412         return;
413
414 fail_destroy_client:
415         if (clp) {
416                 TAILQ_REMOVE(&clnt_list, clp, list);
417                 destroy_client(clp);
418         }
419 fail_keep_client:
420         /* We couldn't find some subdirectories, but we keep the client
421          * around in case we get a notification on the directory when the
422          * subdirectories are created. */
423         return;
424 }
425
426 void
427 init_client_list(void)
428 {
429         TAILQ_INIT(&clnt_list);
430         /* Eventually plan to grow/shrink poll array: */
431         pollsize = FD_ALLOC_BLOCK;
432         pollarray = calloc(pollsize, sizeof(struct pollfd));
433 }
434
435 /*
436  * This is run after a DNOTIFY signal, and should clear up any
437  * directories that are no longer around, and re-scan any existing
438  * directories, since the DNOTIFY could have been in there.
439  */
440 static void
441 update_old_clients(struct dirent **namelist, int size)
442 {
443         struct clnt_info *clp;
444         void *saveprev;
445         int i, stillhere;
446
447         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
448                 stillhere = 0;
449                 for (i=0; i < size; i++) {
450                         if (!strcmp(clp->dirname, namelist[i]->d_name)) {
451                                 stillhere = 1;
452                                 break;
453                         }
454                 }
455                 if (!stillhere) {
456                         printerr(2, "destroying client %s\n", clp->dirname);
457                         saveprev = clp->list.tqe_prev;
458                         TAILQ_REMOVE(&clnt_list, clp, list);
459                         destroy_client(clp);
460                         clp = saveprev;
461                 }
462         }
463         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
464                 if (!process_clnt_dir_files(clp))
465                         insert_clnt_poll(clp);
466         }
467 }
468
469 /* Search for a client by directory name, return 1 if found, 0 otherwise */
470 static int
471 find_client(char *dirname)
472 {
473         struct clnt_info        *clp;
474
475         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
476                 if (!strcmp(clp->dirname, dirname))
477                         return 1;
478         return 0;
479 }
480
481 /* Used to read (and re-read) list of clients, set up poll array. */
482 int
483 update_client_list(void)
484 {
485         struct dirent **namelist;
486         int i, j;
487
488         if (chdir(pipefs_nfsdir) < 0) {
489                 printerr(0, "ERROR: can't chdir to %s: %s\n",
490                          pipefs_nfsdir, strerror(errno));
491                 return -1;
492         }
493
494         j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
495         if (j < 0) {
496                 printerr(0, "ERROR: can't scandir %s: %s\n",
497                          pipefs_nfsdir, strerror(errno));
498                 return -1;
499         }
500         update_old_clients(namelist, j);
501         for (i=0; i < j; i++) {
502                 if (i < FD_ALLOC_BLOCK
503                                 && !strncmp(namelist[i]->d_name, "clnt", 4)
504                                 && !find_client(namelist[i]->d_name))
505                         process_clnt_dir(namelist[i]->d_name);
506                 free(namelist[i]);
507         }
508
509         free(namelist);
510         return 0;
511 }
512
513 static int
514 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
515             gss_buffer_desc *context_token)
516 {
517         char    *buf = NULL, *p = NULL, *end = NULL;
518         unsigned int timeout = context_timeout;
519         unsigned int buf_size = 0;
520
521         printerr(1, "doing downcall\n");
522         buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
523                 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
524                 sizeof(context_token->length) + context_token->length;
525         p = buf = malloc(buf_size);
526         end = buf + buf_size;
527
528         if (WRITE_BYTES(&p, end, uid)) goto out_err;
529         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
530         if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
531         if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
532         if (write_buffer(&p, end, context_token)) goto out_err;
533
534         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
535         if (buf) free(buf);
536         return 0;
537 out_err:
538         if (buf) free(buf);
539         printerr(1, "Failed to write downcall!\n");
540         return -1;
541 }
542
543 static int
544 do_error_downcall(int k5_fd, uid_t uid, int err)
545 {
546         char    buf[1024];
547         char    *p = buf, *end = buf + 1024;
548         unsigned int timeout = 0;
549         int     zero = 0;
550
551         printerr(1, "doing error downcall\n");
552
553         if (WRITE_BYTES(&p, end, uid)) goto out_err;
554         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
555         /* use seq_win = 0 to indicate an error: */
556         if (WRITE_BYTES(&p, end, zero)) goto out_err;
557         if (WRITE_BYTES(&p, end, err)) goto out_err;
558
559         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
560         return 0;
561 out_err:
562         printerr(1, "Failed to write error downcall!\n");
563         return -1;
564 }
565
566 /*
567  * If the port isn't already set, do an rpcbind query to the remote server
568  * using the program and version and get the port. 
569  *
570  * Newer kernels send the value of the port= mount option in the "info"
571  * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
572  * of the port= option or '2049'. The port field in a new sockaddr should
573  * reflect the value that was sent by the kernel.
574  */
575 static int
576 populate_port(struct sockaddr *sa, const socklen_t salen,
577               const rpcprog_t program, const rpcvers_t version,
578               const unsigned short protocol)
579 {
580         struct sockaddr_in      *s4 = (struct sockaddr_in *) sa;
581 #ifdef IPV6_SUPPORTED
582         struct sockaddr_in6     *s6 = (struct sockaddr_in6 *) sa;
583 #endif /* IPV6_SUPPORTED */
584         unsigned short          port;
585
586         /*
587          * Newer kernels send the port in the upcall. If we already have
588          * the port, there's no need to look it up.
589          */
590         switch (sa->sa_family) {
591         case AF_INET:
592                 if (s4->sin_port != 0) {
593                         printerr(2, "DEBUG: port already set to %d\n",
594                                  ntohs(s4->sin_port));
595                         return 1;
596                 }
597                 break;
598 #ifdef IPV6_SUPPORTED
599         case AF_INET6:
600                 if (s6->sin6_port != 0) {
601                         printerr(2, "DEBUG: port already set to %d\n",
602                                  ntohs(s6->sin6_port));
603                         return 1;
604                 }
605                 break;
606 #endif /* IPV6_SUPPORTED */
607         default:
608                 printerr(0, "ERROR: unsupported address family %d\n",
609                             sa->sa_family);
610                 return 0;
611         }
612
613         /*
614          * Newer kernels that send the port in the upcall set the value to
615          * 2049 for NFSv4 mounts when one isn't specified. The check below is
616          * only for kernels that don't send the port in the upcall. For those
617          * we either have to do an rpcbind query or set it to the standard
618          * port. Doing a query could be problematic (firewalls, etc), so take
619          * the latter approach.
620          */
621         if (program == 100003 && version == 4) {
622                 port = 2049;
623                 goto set_port;
624         }
625
626         port = nfs_getport(sa, salen, program, version, protocol);
627         if (!port) {
628                 printerr(0, "ERROR: unable to obtain port for prog %ld "
629                             "vers %ld\n", program, version);
630                 return 0;
631         }
632
633 set_port:
634         printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port,
635                  program, version);
636
637         switch (sa->sa_family) {
638         case AF_INET:
639                 s4->sin_port = htons(port);
640                 break;
641 #ifdef IPV6_SUPPORTED
642         case AF_INET6:
643                 s6->sin6_port = htons(port);
644                 break;
645 #endif /* IPV6_SUPPORTED */
646         }
647
648         return 1;
649 }
650
651 /*
652  * Create an RPC connection and establish an authenticated
653  * gss context with a server.
654  */
655 int create_auth_rpc_client(struct clnt_info *clp,
656                            CLIENT **clnt_return,
657                            AUTH **auth_return,
658                            uid_t uid,
659                            int authtype)
660 {
661         CLIENT                  *rpc_clnt = NULL;
662         struct rpc_gss_sec      sec;
663         AUTH                    *auth = NULL;
664         uid_t                   save_uid = -1;
665         int                     retval = -1;
666         OM_uint32               min_stat;
667         char                    rpc_errmsg[1024];
668         int                     protocol;
669         struct timeval          timeout = {5, 0};
670         struct sockaddr         *addr = (struct sockaddr *) &clp->addr;
671         socklen_t               salen;
672
673         /* Create the context as the user (not as root) */
674         save_uid = geteuid();
675         if (setfsuid(uid) != 0) {
676                 printerr(0, "WARNING: Failed to setfsuid for "
677                             "user with uid %d\n", uid);
678                 goto out_fail;
679         }
680         printerr(2, "creating context using fsuid %d (save_uid %d)\n",
681                         uid, save_uid);
682
683         sec.qop = GSS_C_QOP_DEFAULT;
684         sec.svc = RPCSEC_GSS_SVC_NONE;
685         sec.cred = GSS_C_NO_CREDENTIAL;
686         sec.req_flags = 0;
687         if (authtype == AUTHTYPE_KRB5) {
688                 sec.mech = (gss_OID)&krb5oid;
689                 sec.req_flags = GSS_C_MUTUAL_FLAG;
690         }
691         else if (authtype == AUTHTYPE_SPKM3) {
692                 sec.mech = (gss_OID)&spkm3oid;
693                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
694                  * Need a way to switch....
695                  */
696                 sec.req_flags = GSS_C_MUTUAL_FLAG;
697         }
698         else {
699                 printerr(0, "ERROR: Invalid authentication type (%d) "
700                         "in create_auth_rpc_client\n", authtype);
701                 goto out_fail;
702         }
703
704
705         if (authtype == AUTHTYPE_KRB5) {
706 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
707                 /*
708                  * Do this before creating rpc connection since we won't need
709                  * rpc connection if it fails!
710                  */
711                 if (limit_krb5_enctypes(&sec, uid)) {
712                         printerr(1, "WARNING: Failed while limiting krb5 "
713                                     "encryption types for user with uid %d\n",
714                                  uid);
715                         goto out_fail;
716                 }
717 #endif
718         }
719
720         /* create an rpc connection to the nfs server */
721
722         printerr(2, "creating %s client for server %s\n", clp->protocol,
723                         clp->servername);
724
725         if ((strcmp(clp->protocol, "tcp")) == 0) {
726                 protocol = IPPROTO_TCP;
727         } else if ((strcmp(clp->protocol, "udp")) == 0) {
728                 protocol = IPPROTO_UDP;
729         } else {
730                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
731                          "for connection to server %s for user with uid %d\n",
732                          clp->protocol, clp->servername, uid);
733                 goto out_fail;
734         }
735
736         switch (addr->sa_family) {
737         case AF_INET:
738                 salen = sizeof(struct sockaddr_in);
739                 break;
740 #ifdef IPV6_SUPPORTED
741         case AF_INET6:
742                 salen = sizeof(struct sockaddr_in6);
743                 break;
744 #endif /* IPV6_SUPPORTED */
745         default:
746                 printerr(1, "ERROR: Unknown address family %d\n",
747                          addr->sa_family);
748                 goto out_fail;
749         }
750
751         if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
752                 goto out_fail;
753
754         rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
755                                      clp->vers, &timeout);
756         if (!rpc_clnt) {
757                 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
758                          "WARNING: can't create %s rpc_clnt to server %s for "
759                          "user with uid %d",
760                          protocol == IPPROTO_TCP ? "tcp" : "udp",
761                          clp->servername, uid);
762                 printerr(0, "%s\n",
763                          clnt_spcreateerror(rpc_errmsg));
764                 goto out_fail;
765         }
766
767         printerr(2, "creating context with server %s\n", clp->servicename);
768         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
769         if (!auth) {
770                 /* Our caller should print appropriate message */
771                 printerr(2, "WARNING: Failed to create %s context for "
772                             "user with uid %d for server %s\n",
773                         (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
774                          uid, clp->servername);
775                 goto out_fail;
776         }
777
778         /* Success !!! */
779         rpc_clnt->cl_auth = auth;
780         *clnt_return = rpc_clnt;
781         *auth_return = auth;
782         retval = 0;
783
784   out:
785         if (sec.cred != GSS_C_NO_CREDENTIAL)
786                 gss_release_cred(&min_stat, &sec.cred);
787         /* Restore euid to original value */
788         if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
789                 printerr(0, "WARNING: Failed to restore fsuid"
790                             " to uid %d from %d\n", save_uid, uid);
791         }
792         return retval;
793
794   out_fail:
795         /* Only destroy here if failure.  Otherwise, caller is responsible */
796         if (rpc_clnt) clnt_destroy(rpc_clnt);
797
798         goto out;
799 }
800
801
802 /*
803  * this code uses the userland rpcsec gss library to create a krb5
804  * context on behalf of the kernel
805  */
806 void
807 handle_krb5_upcall(struct clnt_info *clp)
808 {
809         uid_t                   uid;
810         CLIENT                  *rpc_clnt = NULL;
811         AUTH                    *auth = NULL;
812         struct authgss_private_data pd;
813         gss_buffer_desc         token;
814         char                    **credlist = NULL;
815         char                    **ccname;
816         char                    **dirname;
817         int                     create_resp = -1;
818
819         printerr(1, "handling krb5 upcall\n");
820
821         token.length = 0;
822         token.value = NULL;
823         memset(&pd, 0, sizeof(struct authgss_private_data));
824
825         if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
826                 printerr(0, "WARNING: failed reading uid from krb5 "
827                             "upcall pipe: %s\n", strerror(errno));
828                 goto out;
829         }
830
831         if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
832                 /* Tell krb5 gss which credentials cache to use */
833                 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
834                         if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
835                                 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
836                                                              AUTHTYPE_KRB5);
837                         if (create_resp == 0)
838                                 break;
839                 }
840         }
841         if (create_resp != 0) {
842                 if (uid == 0 && root_uses_machine_creds == 1) {
843                         int nocache = 0;
844                         int success = 0;
845                         do {
846                                 gssd_refresh_krb5_machine_credential(clp->servername,
847                                                                      NULL, nocache);
848                                 /*
849                                  * Get a list of credential cache names and try each
850                                  * of them until one works or we've tried them all
851                                  */
852                                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
853                                         printerr(0, "ERROR: No credentials found "
854                                                  "for connection to server %s\n",
855                                                  clp->servername);
856                                                 goto out_return_error;
857                                 }
858                                 for (ccname = credlist; ccname && *ccname; ccname++) {
859                                         gssd_setup_krb5_machine_gss_ccache(*ccname);
860                                         if ((create_auth_rpc_client(clp, &rpc_clnt,
861                                                                     &auth, uid,
862                                                                     AUTHTYPE_KRB5)) == 0) {
863                                                 /* Success! */
864                                                 success++;
865                                                 break;
866                                         } 
867                                         printerr(2, "WARNING: Failed to create machine krb5 context "
868                                                  "with credentials cache %s for server %s\n",
869                                                  *ccname, clp->servername);
870                                 }
871                                 gssd_free_krb5_machine_cred_list(credlist);                     
872                                 if (!success) {
873                                         if(nocache == 0) {
874                                                 nocache++;
875                                                 printerr(2, "WARNING: Machine cache is prematurely expired or corrupted "
876                                                             "trying to recreate cache for server %s\n", clp->servername);
877                                         } else {
878                                                 printerr(1, "WARNING: Failed to create machine krb5 context "
879                                                  "with any credentials cache for server %s\n",
880                                                  clp->servername);
881                                                 goto out_return_error;
882                                         }
883                                 }
884                         } while(!success);
885                 } else {
886                         printerr(1, "WARNING: Failed to create krb5 context "
887                                  "for user with uid %d for server %s\n",
888                                  uid, clp->servername);
889                         goto out_return_error;
890                 }
891         }
892
893         if (!authgss_get_private_data(auth, &pd)) {
894                 printerr(1, "WARNING: Failed to obtain authentication "
895                             "data for user with uid %d for server %s\n",
896                          uid, clp->servername);
897                 goto out_return_error;
898         }
899
900         if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
901                 printerr(0, "WARNING: Failed to serialize krb5 context for "
902                             "user with uid %d for server %s\n",
903                          uid, clp->servername);
904                 goto out_return_error;
905         }
906
907         do_downcall(clp->krb5_fd, uid, &pd, &token);
908
909 out:
910         if (token.value)
911                 free(token.value);
912 #ifndef HAVE_LIBTIRPC
913         if (pd.pd_ctx_hndl.length != 0)
914                 authgss_free_private_data(&pd);
915 #endif
916         if (auth)
917                 AUTH_DESTROY(auth);
918         if (rpc_clnt)
919                 clnt_destroy(rpc_clnt);
920         return;
921
922 out_return_error:
923         do_error_downcall(clp->krb5_fd, uid, -1);
924         goto out;
925 }
926
927 /*
928  * this code uses the userland rpcsec gss library to create an spkm3
929  * context on behalf of the kernel
930  */
931 void
932 handle_spkm3_upcall(struct clnt_info *clp)
933 {
934         uid_t                   uid;
935         CLIENT                  *rpc_clnt = NULL;
936         AUTH                    *auth = NULL;
937         struct authgss_private_data pd;
938         gss_buffer_desc         token;
939
940         printerr(2, "handling spkm3 upcall\n");
941
942         token.length = 0;
943         token.value = NULL;
944
945         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
946                 printerr(0, "WARNING: failed reading uid from spkm3 "
947                          "upcall pipe: %s\n", strerror(errno));
948                 goto out;
949         }
950
951         if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
952                 printerr(0, "WARNING: Failed to create spkm3 context for "
953                             "user with uid %d\n", uid);
954                 goto out_return_error;
955         }
956
957         if (!authgss_get_private_data(auth, &pd)) {
958                 printerr(0, "WARNING: Failed to obtain authentication "
959                             "data for user with uid %d for server %s\n",
960                          uid, clp->servername);
961                 goto out_return_error;
962         }
963
964         if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) {
965                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
966                             "user with uid %d for server\n",
967                          uid, clp->servername);
968                 goto out_return_error;
969         }
970
971         do_downcall(clp->spkm3_fd, uid, &pd, &token);
972
973 out:
974         if (token.value)
975                 free(token.value);
976         if (auth)
977                 AUTH_DESTROY(auth);
978         if (rpc_clnt)
979                 clnt_destroy(rpc_clnt);
980         return;
981
982 out_return_error:
983         do_error_downcall(clp->spkm3_fd, uid, -1);
984         goto out;
985 }