]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/gssd_proc.c
gssd: refactor update_client_list()
[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 static int
482 process_pipedir(char *pipe_name)
483 {
484         struct dirent **namelist;
485         int i, j;
486
487         if (chdir(pipe_name) < 0) {
488                 printerr(0, "ERROR: can't chdir to %s: %s\n",
489                          pipe_name, strerror(errno));
490                 return -1;
491         }
492
493         j = scandir(pipe_name, &namelist, NULL, alphasort);
494         if (j < 0) {
495                 printerr(0, "ERROR: can't scandir %s: %s\n",
496                          pipe_name, strerror(errno));
497                 return -1;
498         }
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
511         return 0;
512 }
513
514 /* Used to read (and re-read) list of clients, set up poll array. */
515 int
516 update_client_list(void)
517 {
518         int retval = -1;
519
520         retval = process_pipedir(pipefs_nfsdir);
521         if (retval)
522                 printerr(0, "ERROR: processing %s\n", pipefs_nfsdir);
523
524         return retval;
525 }
526
527 static int
528 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
529             gss_buffer_desc *context_token)
530 {
531         char    *buf = NULL, *p = NULL, *end = NULL;
532         unsigned int timeout = context_timeout;
533         unsigned int buf_size = 0;
534
535         printerr(1, "doing downcall\n");
536         buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
537                 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
538                 sizeof(context_token->length) + context_token->length;
539         p = buf = malloc(buf_size);
540         end = buf + buf_size;
541
542         if (WRITE_BYTES(&p, end, uid)) goto out_err;
543         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
544         if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
545         if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
546         if (write_buffer(&p, end, context_token)) goto out_err;
547
548         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
549         if (buf) free(buf);
550         return 0;
551 out_err:
552         if (buf) free(buf);
553         printerr(1, "Failed to write downcall!\n");
554         return -1;
555 }
556
557 static int
558 do_error_downcall(int k5_fd, uid_t uid, int err)
559 {
560         char    buf[1024];
561         char    *p = buf, *end = buf + 1024;
562         unsigned int timeout = 0;
563         int     zero = 0;
564
565         printerr(1, "doing error downcall\n");
566
567         if (WRITE_BYTES(&p, end, uid)) goto out_err;
568         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
569         /* use seq_win = 0 to indicate an error: */
570         if (WRITE_BYTES(&p, end, zero)) goto out_err;
571         if (WRITE_BYTES(&p, end, err)) goto out_err;
572
573         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
574         return 0;
575 out_err:
576         printerr(1, "Failed to write error downcall!\n");
577         return -1;
578 }
579
580 /*
581  * If the port isn't already set, do an rpcbind query to the remote server
582  * using the program and version and get the port. 
583  *
584  * Newer kernels send the value of the port= mount option in the "info"
585  * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
586  * of the port= option or '2049'. The port field in a new sockaddr should
587  * reflect the value that was sent by the kernel.
588  */
589 static int
590 populate_port(struct sockaddr *sa, const socklen_t salen,
591               const rpcprog_t program, const rpcvers_t version,
592               const unsigned short protocol)
593 {
594         struct sockaddr_in      *s4 = (struct sockaddr_in *) sa;
595 #ifdef IPV6_SUPPORTED
596         struct sockaddr_in6     *s6 = (struct sockaddr_in6 *) sa;
597 #endif /* IPV6_SUPPORTED */
598         unsigned short          port;
599
600         /*
601          * Newer kernels send the port in the upcall. If we already have
602          * the port, there's no need to look it up.
603          */
604         switch (sa->sa_family) {
605         case AF_INET:
606                 if (s4->sin_port != 0) {
607                         printerr(2, "DEBUG: port already set to %d\n",
608                                  ntohs(s4->sin_port));
609                         return 1;
610                 }
611                 break;
612 #ifdef IPV6_SUPPORTED
613         case AF_INET6:
614                 if (s6->sin6_port != 0) {
615                         printerr(2, "DEBUG: port already set to %d\n",
616                                  ntohs(s6->sin6_port));
617                         return 1;
618                 }
619                 break;
620 #endif /* IPV6_SUPPORTED */
621         default:
622                 printerr(0, "ERROR: unsupported address family %d\n",
623                             sa->sa_family);
624                 return 0;
625         }
626
627         /*
628          * Newer kernels that send the port in the upcall set the value to
629          * 2049 for NFSv4 mounts when one isn't specified. The check below is
630          * only for kernels that don't send the port in the upcall. For those
631          * we either have to do an rpcbind query or set it to the standard
632          * port. Doing a query could be problematic (firewalls, etc), so take
633          * the latter approach.
634          */
635         if (program == 100003 && version == 4) {
636                 port = 2049;
637                 goto set_port;
638         }
639
640         port = nfs_getport(sa, salen, program, version, protocol);
641         if (!port) {
642                 printerr(0, "ERROR: unable to obtain port for prog %ld "
643                             "vers %ld\n", program, version);
644                 return 0;
645         }
646
647 set_port:
648         printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port,
649                  program, version);
650
651         switch (sa->sa_family) {
652         case AF_INET:
653                 s4->sin_port = htons(port);
654                 break;
655 #ifdef IPV6_SUPPORTED
656         case AF_INET6:
657                 s6->sin6_port = htons(port);
658                 break;
659 #endif /* IPV6_SUPPORTED */
660         }
661
662         return 1;
663 }
664
665 /*
666  * Create an RPC connection and establish an authenticated
667  * gss context with a server.
668  */
669 int create_auth_rpc_client(struct clnt_info *clp,
670                            CLIENT **clnt_return,
671                            AUTH **auth_return,
672                            uid_t uid,
673                            int authtype)
674 {
675         CLIENT                  *rpc_clnt = NULL;
676         struct rpc_gss_sec      sec;
677         AUTH                    *auth = NULL;
678         uid_t                   save_uid = -1;
679         int                     retval = -1;
680         OM_uint32               min_stat;
681         char                    rpc_errmsg[1024];
682         int                     protocol;
683         struct timeval          timeout = {5, 0};
684         struct sockaddr         *addr = (struct sockaddr *) &clp->addr;
685         socklen_t               salen;
686
687         /* Create the context as the user (not as root) */
688         save_uid = geteuid();
689         if (setfsuid(uid) != 0) {
690                 printerr(0, "WARNING: Failed to setfsuid for "
691                             "user with uid %d\n", uid);
692                 goto out_fail;
693         }
694         printerr(2, "creating context using fsuid %d (save_uid %d)\n",
695                         uid, save_uid);
696
697         sec.qop = GSS_C_QOP_DEFAULT;
698         sec.svc = RPCSEC_GSS_SVC_NONE;
699         sec.cred = GSS_C_NO_CREDENTIAL;
700         sec.req_flags = 0;
701         if (authtype == AUTHTYPE_KRB5) {
702                 sec.mech = (gss_OID)&krb5oid;
703                 sec.req_flags = GSS_C_MUTUAL_FLAG;
704         }
705         else if (authtype == AUTHTYPE_SPKM3) {
706                 sec.mech = (gss_OID)&spkm3oid;
707                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
708                  * Need a way to switch....
709                  */
710                 sec.req_flags = GSS_C_MUTUAL_FLAG;
711         }
712         else {
713                 printerr(0, "ERROR: Invalid authentication type (%d) "
714                         "in create_auth_rpc_client\n", authtype);
715                 goto out_fail;
716         }
717
718
719         if (authtype == AUTHTYPE_KRB5) {
720 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
721                 /*
722                  * Do this before creating rpc connection since we won't need
723                  * rpc connection if it fails!
724                  */
725                 if (limit_krb5_enctypes(&sec, uid)) {
726                         printerr(1, "WARNING: Failed while limiting krb5 "
727                                     "encryption types for user with uid %d\n",
728                                  uid);
729                         goto out_fail;
730                 }
731 #endif
732         }
733
734         /* create an rpc connection to the nfs server */
735
736         printerr(2, "creating %s client for server %s\n", clp->protocol,
737                         clp->servername);
738
739         if ((strcmp(clp->protocol, "tcp")) == 0) {
740                 protocol = IPPROTO_TCP;
741         } else if ((strcmp(clp->protocol, "udp")) == 0) {
742                 protocol = IPPROTO_UDP;
743         } else {
744                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
745                          "for connection to server %s for user with uid %d\n",
746                          clp->protocol, clp->servername, uid);
747                 goto out_fail;
748         }
749
750         switch (addr->sa_family) {
751         case AF_INET:
752                 salen = sizeof(struct sockaddr_in);
753                 break;
754 #ifdef IPV6_SUPPORTED
755         case AF_INET6:
756                 salen = sizeof(struct sockaddr_in6);
757                 break;
758 #endif /* IPV6_SUPPORTED */
759         default:
760                 printerr(1, "ERROR: Unknown address family %d\n",
761                          addr->sa_family);
762                 goto out_fail;
763         }
764
765         if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
766                 goto out_fail;
767
768         rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
769                                      clp->vers, &timeout);
770         if (!rpc_clnt) {
771                 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
772                          "WARNING: can't create %s rpc_clnt to server %s for "
773                          "user with uid %d",
774                          protocol == IPPROTO_TCP ? "tcp" : "udp",
775                          clp->servername, uid);
776                 printerr(0, "%s\n",
777                          clnt_spcreateerror(rpc_errmsg));
778                 goto out_fail;
779         }
780
781         printerr(2, "creating context with server %s\n", clp->servicename);
782         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
783         if (!auth) {
784                 /* Our caller should print appropriate message */
785                 printerr(2, "WARNING: Failed to create %s context for "
786                             "user with uid %d for server %s\n",
787                         (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
788                          uid, clp->servername);
789                 goto out_fail;
790         }
791
792         /* Success !!! */
793         rpc_clnt->cl_auth = auth;
794         *clnt_return = rpc_clnt;
795         *auth_return = auth;
796         retval = 0;
797
798   out:
799         if (sec.cred != GSS_C_NO_CREDENTIAL)
800                 gss_release_cred(&min_stat, &sec.cred);
801         /* Restore euid to original value */
802         if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
803                 printerr(0, "WARNING: Failed to restore fsuid"
804                             " to uid %d from %d\n", save_uid, uid);
805         }
806         return retval;
807
808   out_fail:
809         /* Only destroy here if failure.  Otherwise, caller is responsible */
810         if (rpc_clnt) clnt_destroy(rpc_clnt);
811
812         goto out;
813 }
814
815
816 /*
817  * this code uses the userland rpcsec gss library to create a krb5
818  * context on behalf of the kernel
819  */
820 void
821 handle_krb5_upcall(struct clnt_info *clp)
822 {
823         uid_t                   uid;
824         CLIENT                  *rpc_clnt = NULL;
825         AUTH                    *auth = NULL;
826         struct authgss_private_data pd;
827         gss_buffer_desc         token;
828         char                    **credlist = NULL;
829         char                    **ccname;
830         char                    **dirname;
831         int                     create_resp = -1;
832
833         printerr(1, "handling krb5 upcall\n");
834
835         token.length = 0;
836         token.value = NULL;
837         memset(&pd, 0, sizeof(struct authgss_private_data));
838
839         if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
840                 printerr(0, "WARNING: failed reading uid from krb5 "
841                             "upcall pipe: %s\n", strerror(errno));
842                 goto out;
843         }
844
845         if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
846                 /* Tell krb5 gss which credentials cache to use */
847                 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
848                         if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
849                                 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
850                                                              AUTHTYPE_KRB5);
851                         if (create_resp == 0)
852                                 break;
853                 }
854         }
855         if (create_resp != 0) {
856                 if (uid == 0 && root_uses_machine_creds == 1) {
857                         int nocache = 0;
858                         int success = 0;
859                         do {
860                                 gssd_refresh_krb5_machine_credential(clp->servername,
861                                                                      NULL, nocache);
862                                 /*
863                                  * Get a list of credential cache names and try each
864                                  * of them until one works or we've tried them all
865                                  */
866                                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
867                                         printerr(0, "ERROR: No credentials found "
868                                                  "for connection to server %s\n",
869                                                  clp->servername);
870                                                 goto out_return_error;
871                                 }
872                                 for (ccname = credlist; ccname && *ccname; ccname++) {
873                                         gssd_setup_krb5_machine_gss_ccache(*ccname);
874                                         if ((create_auth_rpc_client(clp, &rpc_clnt,
875                                                                     &auth, uid,
876                                                                     AUTHTYPE_KRB5)) == 0) {
877                                                 /* Success! */
878                                                 success++;
879                                                 break;
880                                         } 
881                                         printerr(2, "WARNING: Failed to create machine krb5 context "
882                                                  "with credentials cache %s for server %s\n",
883                                                  *ccname, clp->servername);
884                                 }
885                                 gssd_free_krb5_machine_cred_list(credlist);                     
886                                 if (!success) {
887                                         if(nocache == 0) {
888                                                 nocache++;
889                                                 printerr(2, "WARNING: Machine cache is prematurely expired or corrupted "
890                                                             "trying to recreate cache for server %s\n", clp->servername);
891                                         } else {
892                                                 printerr(1, "WARNING: Failed to create machine krb5 context "
893                                                  "with any credentials cache for server %s\n",
894                                                  clp->servername);
895                                                 goto out_return_error;
896                                         }
897                                 }
898                         } while(!success);
899                 } else {
900                         printerr(1, "WARNING: Failed to create krb5 context "
901                                  "for user with uid %d for server %s\n",
902                                  uid, clp->servername);
903                         goto out_return_error;
904                 }
905         }
906
907         if (!authgss_get_private_data(auth, &pd)) {
908                 printerr(1, "WARNING: Failed to obtain authentication "
909                             "data for user with uid %d for server %s\n",
910                          uid, clp->servername);
911                 goto out_return_error;
912         }
913
914         if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
915                 printerr(0, "WARNING: Failed to serialize krb5 context for "
916                             "user with uid %d for server %s\n",
917                          uid, clp->servername);
918                 goto out_return_error;
919         }
920
921         do_downcall(clp->krb5_fd, uid, &pd, &token);
922
923 out:
924         if (token.value)
925                 free(token.value);
926 #ifndef HAVE_LIBTIRPC
927         if (pd.pd_ctx_hndl.length != 0)
928                 authgss_free_private_data(&pd);
929 #endif
930         if (auth)
931                 AUTH_DESTROY(auth);
932         if (rpc_clnt)
933                 clnt_destroy(rpc_clnt);
934         return;
935
936 out_return_error:
937         do_error_downcall(clp->krb5_fd, uid, -1);
938         goto out;
939 }
940
941 /*
942  * this code uses the userland rpcsec gss library to create an spkm3
943  * context on behalf of the kernel
944  */
945 void
946 handle_spkm3_upcall(struct clnt_info *clp)
947 {
948         uid_t                   uid;
949         CLIENT                  *rpc_clnt = NULL;
950         AUTH                    *auth = NULL;
951         struct authgss_private_data pd;
952         gss_buffer_desc         token;
953
954         printerr(2, "handling spkm3 upcall\n");
955
956         token.length = 0;
957         token.value = NULL;
958
959         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
960                 printerr(0, "WARNING: failed reading uid from spkm3 "
961                          "upcall pipe: %s\n", strerror(errno));
962                 goto out;
963         }
964
965         if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
966                 printerr(0, "WARNING: Failed to create spkm3 context for "
967                             "user with uid %d\n", uid);
968                 goto out_return_error;
969         }
970
971         if (!authgss_get_private_data(auth, &pd)) {
972                 printerr(0, "WARNING: Failed to obtain authentication "
973                             "data for user with uid %d for server %s\n",
974                          uid, clp->servername);
975                 goto out_return_error;
976         }
977
978         if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) {
979                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
980                             "user with uid %d for server\n",
981                          uid, clp->servername);
982                 goto out_return_error;
983         }
984
985         do_downcall(clp->spkm3_fd, uid, &pd, &token);
986
987 out:
988         if (token.value)
989                 free(token.value);
990         if (auth)
991                 AUTH_DESTROY(auth);
992         if (rpc_clnt)
993                 clnt_destroy(rpc_clnt);
994         return;
995
996 out_return_error:
997         do_error_downcall(clp->spkm3_fd, uid, -1);
998         goto out;
999 }