]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/gssd_proc.c
nfs-utils: switch gssd to use standard function for getting an RPC client
[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                     protocol;
630         struct timeval          timeout = {5, 0};
631         struct sockaddr         *addr = (struct sockaddr *) &clp->addr;
632         socklen_t               salen;
633
634         /* Create the context as the user (not as root) */
635         save_uid = geteuid();
636         if (setfsuid(uid) != 0) {
637                 printerr(0, "WARNING: Failed to setfsuid for "
638                             "user with uid %d\n", uid);
639                 goto out_fail;
640         }
641         printerr(2, "creating context using fsuid %d (save_uid %d)\n",
642                         uid, save_uid);
643
644         sec.qop = GSS_C_QOP_DEFAULT;
645         sec.svc = RPCSEC_GSS_SVC_NONE;
646         sec.cred = GSS_C_NO_CREDENTIAL;
647         sec.req_flags = 0;
648         if (authtype == AUTHTYPE_KRB5) {
649                 sec.mech = (gss_OID)&krb5oid;
650                 sec.req_flags = GSS_C_MUTUAL_FLAG;
651         }
652         else if (authtype == AUTHTYPE_SPKM3) {
653                 sec.mech = (gss_OID)&spkm3oid;
654                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
655                  * Need a way to switch....
656                  */
657                 sec.req_flags = GSS_C_MUTUAL_FLAG;
658         }
659         else {
660                 printerr(0, "ERROR: Invalid authentication type (%d) "
661                         "in create_auth_rpc_client\n", authtype);
662                 goto out_fail;
663         }
664
665
666         if (authtype == AUTHTYPE_KRB5) {
667 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
668                 /*
669                  * Do this before creating rpc connection since we won't need
670                  * rpc connection if it fails!
671                  */
672                 if (limit_krb5_enctypes(&sec, uid)) {
673                         printerr(1, "WARNING: Failed while limiting krb5 "
674                                     "encryption types for user with uid %d\n",
675                                  uid);
676                         goto out_fail;
677                 }
678 #endif
679         }
680
681         /* create an rpc connection to the nfs server */
682
683         printerr(2, "creating %s client for server %s\n", clp->protocol,
684                         clp->servername);
685
686         if ((strcmp(clp->protocol, "tcp")) == 0) {
687                 protocol = IPPROTO_TCP;
688         } else if ((strcmp(clp->protocol, "udp")) == 0) {
689                 protocol = IPPROTO_UDP;
690         } else {
691                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
692                          "for connection to server %s for user with uid %d\n",
693                          clp->protocol, clp->servername, uid);
694                 goto out_fail;
695         }
696
697         switch (addr->sa_family) {
698         case AF_INET:
699                 salen = sizeof(struct sockaddr_in);
700                 break;
701         default:
702                 printerr(1, "ERROR: Unknown address family %d\n",
703                          addr->sa_family);
704                 goto out_fail;
705         }
706
707         if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
708                 goto out_fail;
709
710         rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
711                                      clp->vers, &timeout);
712         if (!rpc_clnt) {
713                 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
714                          "WARNING: can't create %s rpc_clnt to server %s for "
715                          "user with uid %d",
716                          protocol == IPPROTO_TCP ? "tcp" : "udp",
717                          clp->servername, uid);
718                 printerr(0, "%s\n",
719                          clnt_spcreateerror(rpc_errmsg));
720                 goto out_fail;
721         }
722
723         printerr(2, "creating context with server %s\n", clp->servicename);
724         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
725         if (!auth) {
726                 /* Our caller should print appropriate message */
727                 printerr(2, "WARNING: Failed to create %s context for "
728                             "user with uid %d for server %s\n",
729                         (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
730                          uid, clp->servername);
731                 goto out_fail;
732         }
733
734         /* Success !!! */
735         rpc_clnt->cl_auth = auth;
736         *clnt_return = rpc_clnt;
737         *auth_return = auth;
738         retval = 0;
739
740   out:
741         if (sec.cred != GSS_C_NO_CREDENTIAL)
742                 gss_release_cred(&min_stat, &sec.cred);
743         /* Restore euid to original value */
744         if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
745                 printerr(0, "WARNING: Failed to restore fsuid"
746                             " to uid %d from %d\n", save_uid, uid);
747         }
748         return retval;
749
750   out_fail:
751         /* Only destroy here if failure.  Otherwise, caller is responsible */
752         if (rpc_clnt) clnt_destroy(rpc_clnt);
753
754         goto out;
755 }
756
757
758 /*
759  * this code uses the userland rpcsec gss library to create a krb5
760  * context on behalf of the kernel
761  */
762 void
763 handle_krb5_upcall(struct clnt_info *clp)
764 {
765         uid_t                   uid;
766         CLIENT                  *rpc_clnt = NULL;
767         AUTH                    *auth = NULL;
768         struct authgss_private_data pd;
769         gss_buffer_desc         token;
770         char                    **credlist = NULL;
771         char                    **ccname;
772         char                    **dirname;
773         int                     create_resp = -1;
774
775         printerr(1, "handling krb5 upcall\n");
776
777         token.length = 0;
778         token.value = NULL;
779         memset(&pd, 0, sizeof(struct authgss_private_data));
780
781         if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
782                 printerr(0, "WARNING: failed reading uid from krb5 "
783                             "upcall pipe: %s\n", strerror(errno));
784                 goto out;
785         }
786
787         if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
788                 /* Tell krb5 gss which credentials cache to use */
789                 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
790                         if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
791                                 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
792                                                              AUTHTYPE_KRB5);
793                         if (create_resp == 0)
794                                 break;
795                 }
796         }
797         if (create_resp != 0) {
798                 if (uid == 0 && root_uses_machine_creds == 1) {
799                         int success = 0;
800
801                         gssd_refresh_krb5_machine_credential(clp->servername,
802                                                              NULL);
803                         /*
804                          * Get a list of credential cache names and try each
805                          * of them until one works or we've tried them all
806                          */
807                         if (gssd_get_krb5_machine_cred_list(&credlist)) {
808                                 printerr(0, "ERROR: No credentials found "
809                                          "for connection to server %s\n",
810                                          clp->servername);
811                                         goto out_return_error;
812                         }
813                         for (ccname = credlist; ccname && *ccname; ccname++) {
814                                 gssd_setup_krb5_machine_gss_ccache(*ccname);
815                                 if ((create_auth_rpc_client(clp, &rpc_clnt,
816                                                             &auth, uid,
817                                                             AUTHTYPE_KRB5)) == 0) {
818                                         /* Success! */
819                                         success++;
820                                         break;
821                                 }
822                                 printerr(2, "WARNING: Failed to create krb5 context "
823                                          "for user with uid %d with credentials "
824                                          "cache %s for server %s\n",
825                                          uid, *ccname, clp->servername);
826                         }
827                         gssd_free_krb5_machine_cred_list(credlist);
828                         if (!success) {
829                                 printerr(1, "WARNING: Failed to create krb5 context "
830                                          "for user with uid %d with any "
831                                          "credentials cache for server %s\n",
832                                          uid, clp->servername);
833                                 goto out_return_error;
834                         }
835                 } else {
836                         printerr(1, "WARNING: Failed to create krb5 context "
837                                  "for user with uid %d for server %s\n",
838                                  uid, clp->servername);
839                         goto out_return_error;
840                 }
841         }
842
843         if (!authgss_get_private_data(auth, &pd)) {
844                 printerr(1, "WARNING: Failed to obtain authentication "
845                             "data for user with uid %d for server %s\n",
846                          uid, clp->servername);
847                 goto out_return_error;
848         }
849
850         if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
851                 printerr(0, "WARNING: Failed to serialize krb5 context for "
852                             "user with uid %d for server %s\n",
853                          uid, clp->servername);
854                 goto out_return_error;
855         }
856
857         do_downcall(clp->krb5_fd, uid, &pd, &token);
858
859 out:
860         if (token.value)
861                 free(token.value);
862 #ifndef HAVE_LIBTIRPC
863         if (pd.pd_ctx_hndl.length != 0)
864                 authgss_free_private_data(&pd);
865 #endif
866         if (auth)
867                 AUTH_DESTROY(auth);
868         if (rpc_clnt)
869                 clnt_destroy(rpc_clnt);
870         return;
871
872 out_return_error:
873         do_error_downcall(clp->krb5_fd, uid, -1);
874         goto out;
875 }
876
877 /*
878  * this code uses the userland rpcsec gss library to create an spkm3
879  * context on behalf of the kernel
880  */
881 void
882 handle_spkm3_upcall(struct clnt_info *clp)
883 {
884         uid_t                   uid;
885         CLIENT                  *rpc_clnt = NULL;
886         AUTH                    *auth = NULL;
887         struct authgss_private_data pd;
888         gss_buffer_desc         token;
889
890         printerr(2, "handling spkm3 upcall\n");
891
892         token.length = 0;
893         token.value = NULL;
894
895         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
896                 printerr(0, "WARNING: failed reading uid from spkm3 "
897                          "upcall pipe: %s\n", strerror(errno));
898                 goto out;
899         }
900
901         if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
902                 printerr(0, "WARNING: Failed to create spkm3 context for "
903                             "user with uid %d\n", uid);
904                 goto out_return_error;
905         }
906
907         if (!authgss_get_private_data(auth, &pd)) {
908                 printerr(0, "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, &spkm3oid, NULL)) {
915                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
916                             "user with uid %d for server\n",
917                          uid, clp->servername);
918                 goto out_return_error;
919         }
920
921         do_downcall(clp->spkm3_fd, uid, &pd, &token);
922
923 out:
924         if (token.value)
925                 free(token.value);
926         if (auth)
927                 AUTH_DESTROY(auth);
928         if (rpc_clnt)
929                 clnt_destroy(rpc_clnt);
930         return;
931
932 out_return_error:
933         do_error_downcall(clp->spkm3_fd, uid, -1);
934         goto out;
935 }