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