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