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