]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/gssd_proc.c
4c3d85da766ebdef5f1352ada8c0c6f8f2fbd9b9
[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 #ifndef _GNU_SOURCE
42 #define _GNU_SOURCE
43 #endif
44 #include "config.h"
45 #include <sys/param.h>
46 #include <rpc/rpc.h>
47 #include <sys/stat.h>
48 #include <sys/socket.h>
49 #include <arpa/inet.h>
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <pwd.h>
54 #include <grp.h>
55 #include <string.h>
56 #include <dirent.h>
57 #include <poll.h>
58 #include <fcntl.h>
59 #include <signal.h>
60 #include <unistd.h>
61 #include <errno.h>
62 #include <gssapi/gssapi.h>
63 #include <netdb.h>
64
65 #include "gssd.h"
66 #include "err_util.h"
67 #include "gss_util.h"
68 #include "gss_oids.h"
69 #include "krb5_util.h"
70 #include "context.h"
71
72 /*
73  * pollarray:
74  *      array of struct pollfd suitable to pass to poll. initialized to
75  *      zero - a zero struct is ignored by poll() because the events mask is 0.
76  *
77  * clnt_list:
78  *      linked list of struct clnt_info which associates a clntXXX directory
79  *      with an index into pollarray[], and other basic data about that client.
80  *
81  * Directory structure: created by the kernel nfs client
82  *      /pipefsdir/clntXX             : one per rpc_clnt struct in the kernel
83  *      /pipefsdir/clntXX/krb5        : read uid for which kernel wants
84  *                                       a context, write the resulting context
85  *      /pipefsdir/clntXX/info        : stores info such as server name
86  *
87  * Algorithm:
88  *      Poll all /pipefsdir/clntXX/krb5 files.  When ready, data read
89  *      is a uid; performs rpcsec_gss context initialization protocol to
90  *      get a cred for that user.  Writes result to corresponding krb5 file
91  *      in a form the kernel code will understand.
92  *      In addition, we make sure we are notified whenever anything is
93  *      created or destroyed in pipefsdir/ or in an of the clntXX directories,
94  *      and rescan the whole pipefsdir when this happens.
95  */
96
97 struct pollfd * pollarray;
98
99 int pollsize;  /* the size of pollaray (in pollfd's) */
100
101 /* XXX buffer problems: */
102 static int
103 read_service_info(char *info_file_name, char **servicename, char **servername,
104                   int *prog, int *vers, char **protocol) {
105 #define INFOBUFLEN 256
106         char            buf[INFOBUFLEN];
107         static char     dummy[128];
108         int             nbytes;
109         static char     service[128];
110         static char     address[128];
111         char            program[16];
112         char            version[16];
113         char            protoname[16];
114         in_addr_t       inaddr;
115         int             fd = -1;
116         struct hostent  *ent = NULL;
117         int             numfields;
118
119         *servicename = *servername = *protocol = NULL;
120
121         if ((fd = open(info_file_name, O_RDONLY)) == -1) {
122                 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
123                          strerror(errno));
124                 goto fail;
125         }
126         if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
127                 goto fail;
128         close(fd);
129
130         numfields = sscanf(buf,"RPC server: %s\n"
131                    "service: %s %s version %s\n"
132                    "address: %s\n"
133                    "protocol: %s\n",
134                    dummy,
135                    service, program, version,
136                    address,
137                    protoname);
138
139         if (numfields == 5) {
140                 strcpy(protoname, "tcp");
141         } else if (numfields != 6) {
142                 goto fail;
143         }
144
145         /* check service, program, and version */
146         if(memcmp(service, "nfs", 3)) return -1;
147         *prog = atoi(program + 1); /* skip open paren */
148         *vers = atoi(version);
149         if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
150                 goto fail;
151
152         /* create service name */
153         inaddr = inet_addr(address);
154         if (!(ent = gethostbyaddr(&inaddr, sizeof(inaddr), AF_INET))) {
155                 printerr(0, "ERROR: can't resolve server %s name\n", address);
156                 goto fail;
157         }
158         if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
159                 goto fail;
160         memcpy(*servername, ent->h_name, strlen(ent->h_name));
161         snprintf(buf, INFOBUFLEN, "%s@%s", service, ent->h_name);
162         if (!(*servicename = calloc(strlen(buf) + 1, 1)))
163                 goto fail;
164         memcpy(*servicename, buf, strlen(buf));
165
166         if (!(*protocol = strdup(protoname)))
167                 goto fail;
168         return 0;
169 fail:
170         printerr(0, "ERROR: failed to read service info\n");
171         if (fd != -1) close(fd);
172         if (*servername) free(*servername);
173         if (*servicename) free(*servicename);
174         if (*protocol) free(*protocol);
175         return -1;
176 }
177
178 static void
179 destroy_client(struct clnt_info *clp)
180 {
181         if (clp->dir_fd != -1) close(clp->dir_fd);
182         if (clp->krb5_fd != -1) close(clp->krb5_fd);
183         if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
184         if (clp->dirname) free(clp->dirname);
185         if (clp->servicename) free(clp->servicename);
186         if (clp->servername) free(clp->servername);
187         if (clp->protocol) free(clp->protocol);
188         free(clp);
189 }
190
191 static struct clnt_info *
192 insert_new_clnt(void)
193 {
194         struct clnt_info        *clp = NULL;
195
196         if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
197                 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
198                          strerror(errno));
199                 goto out;
200         }
201         clp->krb5_poll_index = -1;
202         clp->spkm3_poll_index = -1;
203         clp->krb5_fd = -1;
204         clp->spkm3_fd = -1;
205         clp->dir_fd = -1;
206
207         TAILQ_INSERT_HEAD(&clnt_list, clp, list);
208 out:
209         return clp;
210 }
211
212 static int
213 process_clnt_dir_files(struct clnt_info * clp)
214 {
215         char    kname[32];
216         char    sname[32];
217         char    info_file_name[32];
218
219         snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
220         clp->krb5_fd = open(kname, O_RDWR);
221         snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
222         clp->spkm3_fd = open(sname, O_RDWR);
223         if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
224                 return -1;
225         snprintf(info_file_name, sizeof(info_file_name), "%s/info",
226                         clp->dirname);
227         if (read_service_info(info_file_name, &clp->servicename,
228                                 &clp->servername, &clp->prog, &clp->vers,
229                                 &clp->protocol))
230                 return -1;
231         return 0;
232 }
233
234 static int
235 get_poll_index(int *ind)
236 {
237         int i;
238
239         *ind = -1;
240         for (i=0; i<FD_ALLOC_BLOCK; i++) {
241                 if (pollarray[i].events == 0) {
242                         *ind = i;
243                         break;
244                 }
245         }
246         if (*ind == -1) {
247                 printerr(0, "ERROR: No pollarray slots open\n");
248                 return -1;
249         }
250         return 0;
251 }
252
253 static void
254 process_clnt_dir(char *dir)
255 {
256         struct clnt_info *      clp;
257
258         if (!(clp = insert_new_clnt()))
259                 goto fail_destroy_client;
260
261         if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
262                 goto fail_destroy_client;
263         }
264         memcpy(clp->dirname, dir, strlen(dir));
265         if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
266                 printerr(0, "ERROR: can't open %s: %s\n",
267                          clp->dirname, strerror(errno));
268                 goto fail_destroy_client;
269         }
270         fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
271         fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
272
273         if (process_clnt_dir_files(clp))
274                 goto fail_keep_client;
275
276         if(clp->krb5_fd != -1) {
277                 if (get_poll_index(&clp->krb5_poll_index)) {
278                         printerr(0, "ERROR: Too many krb5 clients\n");
279                         goto fail_destroy_client;
280                 }
281                 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
282                 pollarray[clp->krb5_poll_index].events |= POLLIN;
283         }
284
285         if(clp->spkm3_fd != -1) {
286                 if (get_poll_index(&clp->spkm3_poll_index)) {
287                         printerr(0, "ERROR: Too many spkm3 clients\n");
288                         goto fail_destroy_client;
289                 }
290                 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
291                 pollarray[clp->spkm3_poll_index].events |= POLLIN;
292         }
293
294         return;
295
296 fail_destroy_client:
297         if (clp) {
298                 TAILQ_REMOVE(&clnt_list, clp, list);
299                 destroy_client(clp);
300         }
301 fail_keep_client:
302         /* We couldn't find some subdirectories, but we keep the client
303          * around in case we get a notification on the directory when the
304          * subdirectories are created. */
305         return;
306 }
307
308 void
309 init_client_list(void)
310 {
311         TAILQ_INIT(&clnt_list);
312         /* Eventually plan to grow/shrink poll array: */
313         pollsize = FD_ALLOC_BLOCK;
314         pollarray = calloc(pollsize, sizeof(struct pollfd));
315 }
316
317 static void
318 destroy_client_list(void)
319 {
320         struct clnt_info        *clp;
321
322         printerr(1, "processing client list\n");
323
324         while (clnt_list.tqh_first != NULL) {
325                 clp = clnt_list.tqh_first;
326                 TAILQ_REMOVE(&clnt_list, clp, list);
327                 destroy_client(clp);
328         }
329 }
330
331 /* Used to read (and re-read) list of clients, set up poll array. */
332 int
333 update_client_list(void)
334 {
335         struct dirent **namelist;
336         int i,j;
337
338         destroy_client_list();
339
340         if (chdir(pipefsdir) < 0) {
341                 printerr(0, "ERROR: can't chdir to %s: %s\n",
342                          pipefsdir, strerror(errno));
343                 return -1;
344         }
345
346         memset(pollarray, 0, pollsize * sizeof(struct pollfd));
347
348         j = scandir(pipefsdir, &namelist, NULL, alphasort);
349         if (j < 0) {
350                 printerr(0, "ERROR: can't scandir %s: %s\n",
351                          pipefsdir, strerror(errno));
352                 return -1;
353         }
354         for (i=0; i < j; i++) {
355                 if (i < FD_ALLOC_BLOCK
356                                 && !strncmp(namelist[i]->d_name, "clnt", 4))
357                         process_clnt_dir(namelist[i]->d_name);
358                 free(namelist[i]);
359         }
360
361         free(namelist);
362         return 0;
363 }
364
365 static int
366 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
367             gss_buffer_desc *context_token)
368 {
369         char    *buf = NULL, *p = NULL, *end = NULL;
370         unsigned int timeout = 0; /* XXX decide on a reasonable value */
371         unsigned int buf_size = 0;
372
373         printerr(1, "doing downcall\n");
374         buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
375                 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
376                 sizeof(context_token->length) + context_token->length;
377         p = buf = malloc(buf_size);
378         end = buf + buf_size;
379
380         if (WRITE_BYTES(&p, end, uid)) goto out_err;
381         /* Not setting any timeout for now: */
382         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
383         if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
384         if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
385         if (write_buffer(&p, end, context_token)) goto out_err;
386
387         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
388         if (buf) free(buf);
389         return 0;
390 out_err:
391         if (buf) free(buf);
392         printerr(0, "Failed to write downcall!\n");
393         return -1;
394 }
395
396 static int
397 do_error_downcall(int k5_fd, uid_t uid, int err)
398 {
399         char    buf[1024];
400         char    *p = buf, *end = buf + 1024;
401         unsigned int timeout = 0;
402         int     zero = 0;
403
404         printerr(1, "doing error downcall\n");
405
406         if (WRITE_BYTES(&p, end, uid)) goto out_err;
407         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
408         /* use seq_win = 0 to indicate an error: */
409         if (WRITE_BYTES(&p, end, zero)) goto out_err;
410         if (WRITE_BYTES(&p, end, err)) goto out_err;
411
412         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
413         return 0;
414 out_err:
415         printerr(0, "Failed to write error downcall!\n");
416         return -1;
417 }
418
419 /*
420  * Create an RPC connection and establish an authenticated
421  * gss context with a server.
422  */
423 int create_auth_rpc_client(struct clnt_info *clp,
424                            AUTH **auth_return,
425                            uid_t uid,
426                            int authtype)
427 {
428         CLIENT                  *rpc_clnt = NULL;
429         struct rpc_gss_sec      sec;
430         AUTH                    *auth = NULL;
431         uid_t                   save_uid = -1;
432         int                     retval = -1;
433         int                     errcode;
434         OM_uint32               min_stat;
435         char                    rpc_errmsg[1024];
436         int                     sockp = RPC_ANYSOCK;
437         int                     sendsz = 32768, recvsz = 32768;
438         struct addrinfo         ai_hints, *a = NULL;
439         char                    service[64];
440         char                    *at_sign;
441
442         sec.qop = GSS_C_QOP_DEFAULT;
443         sec.svc = RPCSEC_GSS_SVC_NONE;
444         sec.cred = GSS_C_NO_CREDENTIAL;
445         sec.req_flags = 0;
446         if (authtype == AUTHTYPE_KRB5) {
447                 sec.mech = (gss_OID)&krb5oid;
448                 sec.req_flags = GSS_C_MUTUAL_FLAG;
449         }
450         else if (authtype == AUTHTYPE_SPKM3) {
451                 sec.mech = (gss_OID)&spkm3oid;
452                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
453                  * Need a way to switch....
454                  */
455                 sec.req_flags = GSS_C_MUTUAL_FLAG;
456         }
457         else {
458                 printerr(0, "ERROR: Invalid authentication type (%d) "
459                         "in create_auth_rpc_client\n", authtype);
460                 goto out_fail;
461         }
462
463
464         if (authtype == AUTHTYPE_KRB5) {
465 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
466                 /*
467                  * Do this before creating rpc connection since we won't need
468                  * rpc connection if it fails!
469                  */
470                 if (limit_krb5_enctypes(&sec, uid)) {
471                         printerr(1, "WARNING: Failed while limiting krb5 "
472                                     "encryption types for user with uid %d\n",
473                                  uid);
474                         goto out_fail;
475                 }
476 #endif
477         }
478
479         /* Create the context as the user (not as root) */
480         save_uid = geteuid();
481         if (seteuid(uid) != 0) {
482                 printerr(0, "WARNING: Failed to seteuid for "
483                             "user with uid %d\n", uid);
484                 goto out_fail;
485         }
486         printerr(2, "creating context using euid %d (save_uid %d)\n",
487                         geteuid(), save_uid);
488
489         /* create an rpc connection to the nfs server */
490
491         printerr(2, "creating %s client for server %s\n", clp->protocol,
492                         clp->servername);
493
494         memset(&ai_hints, '\0', sizeof(ai_hints));
495         ai_hints.ai_family = PF_INET;
496         ai_hints.ai_flags |= AI_CANONNAME;
497         if ((strcmp(clp->protocol, "tcp")) == 0) {
498                 ai_hints.ai_socktype = SOCK_STREAM;
499                 ai_hints.ai_protocol = IPPROTO_TCP;
500         } else if ((strcmp(clp->protocol, "udp")) == 0) {
501                 ai_hints.ai_socktype = SOCK_DGRAM;
502                 ai_hints.ai_protocol = IPPROTO_UDP;
503         } else {
504                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
505                          "for connection to server %s for user with uid %d",
506                          clp->protocol, clp->servername, uid);
507                 goto out_fail;
508         }
509
510         /* extract the service name from clp->servicename */
511         if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
512                 printerr(0, "WARNING: servicename (%s) not formatted as "
513                         "expected with service@host", clp->servicename);
514                 goto out_fail;
515         }
516         if ((at_sign - clp->servicename) >= sizeof(service)) {
517                 printerr(0, "WARNING: service portion of servicename (%s) "
518                         "is too long!", clp->servicename);
519                 goto out_fail;
520         }
521         strncpy(service, clp->servicename, at_sign - clp->servicename);
522         service[at_sign - clp->servicename] = '\0';
523
524         errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
525         if (errcode) {
526                 printerr(0, "WARNING: Error from getaddrinfo for server "
527                          "'%s': %s", clp->servername, gai_strerror(errcode));
528                 goto out_fail;
529         }
530
531         if (a == NULL) {
532                 printerr(0, "WARNING: No address information found for "
533                          "connection to server %s for user with uid %d",
534                          clp->servername, uid);
535                 goto out_fail;
536         }
537         if (a->ai_protocol == IPPROTO_TCP) {
538                 if ((rpc_clnt = clnttcp_create(
539                                         (struct sockaddr_in *) a->ai_addr,
540                                         clp->prog, clp->vers, &sockp,
541                                         sendsz, recvsz)) == NULL) {
542                         snprintf(rpc_errmsg, sizeof(rpc_errmsg),
543                                  "WARNING: can't create tcp rpc_clnt "
544                                  "for server %s for user with uid %d",
545                                  clp->servername, uid);
546                         printerr(0, "%s\n",
547                                  clnt_spcreateerror(rpc_errmsg));
548                         goto out_fail;
549                 }
550         } else if (a->ai_protocol == IPPROTO_UDP) {
551                 const struct timeval timeout = {5, 0};
552                 if ((rpc_clnt = clntudp_bufcreate(
553                                         (struct sockaddr_in *) a->ai_addr,
554                                         clp->prog, clp->vers, timeout,
555                                         &sockp, sendsz, recvsz)) == NULL) {
556                         snprintf(rpc_errmsg, sizeof(rpc_errmsg),
557                                  "WARNING: can't create udp rpc_clnt "
558                                  "for server %s for user with uid %d",
559                                  clp->servername, uid);
560                         printerr(0, "%s\n",
561                                  clnt_spcreateerror(rpc_errmsg));
562                         goto out_fail;
563                 }
564         } else {
565                 /* Shouldn't happen! */
566                 printerr(0, "ERROR: requested protocol '%s', but "
567                          "got addrinfo with protocol %d",
568                          clp->protocol, a->ai_protocol);
569                 goto out_fail;
570         }
571         /* We're done with this */
572         freeaddrinfo(a);
573         a = NULL;
574
575         printerr(2, "creating context with server %s\n", clp->servicename);
576         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
577         if (!auth) {
578                 /* Our caller should print appropriate message */
579                 printerr(2, "WARNING: Failed to create %s context for "
580                             "user with uid %d for server %s\n",
581                         (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
582                          uid, clp->servername);
583                 goto out_fail;
584         }
585
586         /* Restore euid to original value */
587         if (seteuid(save_uid) != 0) {
588                 printerr(0, "WARNING: Failed to restore euid"
589                             " to uid %d\n", save_uid);
590                 goto out_fail;
591         }
592         save_uid = -1;
593
594         /* Success !!! */
595         *auth_return = auth;
596         retval = 0;
597
598   out_fail:
599         if ((save_uid != -1) && (seteuid(save_uid) != 0)) {
600                 printerr(0, "WARNING: Failed to restore euid"
601                             " to uid %d (in error path)\n", save_uid);
602         }
603         if (sec.cred != GSS_C_NO_CREDENTIAL)
604                 gss_release_cred(&min_stat, &sec.cred);
605         if (rpc_clnt) clnt_destroy(rpc_clnt);
606         if (a != NULL) freeaddrinfo(a);
607
608         return retval;
609 }
610
611
612 /*
613  * this code uses the userland rpcsec gss library to create a krb5
614  * context on behalf of the kernel
615  */
616 void
617 handle_krb5_upcall(struct clnt_info *clp)
618 {
619         uid_t                   uid;
620         AUTH                    *auth;
621         struct authgss_private_data pd;
622         gss_buffer_desc         token;
623         char                    **credlist = NULL;
624         char                    **ccname;
625
626         printerr(1, "handling krb5 upcall\n");
627
628         token.length = 0;
629         token.value = NULL;
630
631         if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
632                 printerr(0, "WARNING: failed reading uid from krb5 "
633                             "upcall pipe: %s\n", strerror(errno));
634                 goto out;
635         }
636
637         if (uid == 0) {
638                 int success = 0;
639
640                 /*
641                  * Get a list of credential cache names and try each
642                  * of them until one works or we've tried them all
643                  */
644                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
645                         printerr(0, "WARNING: Failed to obtain machine "
646                                     "credentials for connection to "
647                                     "server %s\n", clp->servername);
648                                 goto out_return_error;
649                 }
650                 for (ccname = credlist; ccname && *ccname; ccname++) {
651                         gssd_setup_krb5_machine_gss_ccache(*ccname);
652                         if ((create_auth_rpc_client(clp, &auth, uid,
653                                                     AUTHTYPE_KRB5)) == 0) {
654                                 /* Success! */
655                                 success++;
656                                 break;
657                         }
658                         printerr(2, "WARNING: Failed to create krb5 context "
659                                     "for user with uid %d with credentials "
660                                     "cache %s for server %s\n",
661                                  uid, *ccname, clp->servername);
662                 }
663                 gssd_free_krb5_machine_cred_list(credlist);
664                 if (!success) {
665                         printerr(0, "WARNING: Failed to create krb5 context "
666                                     "for user with uid %d with any "
667                                     "credentials cache for server %s\n",
668                                  uid, clp->servername);
669                         goto out_return_error;
670                 }
671         }
672         else {
673                 /* Tell krb5 gss which credentials cache to use */
674                 gssd_setup_krb5_user_gss_ccache(uid, clp->servername);
675
676                 if (create_auth_rpc_client(clp, &auth, uid, AUTHTYPE_KRB5)) {
677                         printerr(0, "WARNING: Failed to create krb5 context "
678                                     "for user with uid %d for server %s\n",
679                                  uid, clp->servername);
680                         goto out_return_error;
681                 }
682         }
683
684         if (!authgss_get_private_data(auth, &pd)) {
685                 printerr(0, "WARNING: Failed to obtain authentication "
686                             "data for user with uid %d for server %s\n",
687                          uid, clp->servername);
688                 goto out_return_error;
689         }
690
691         if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid)) {
692                 printerr(0, "WARNING: Failed to serialize krb5 context for "
693                             "user with uid %d for server %s\n",
694                          uid, clp->servername);
695                 goto out_return_error;
696         }
697
698         do_downcall(clp->krb5_fd, uid, &pd, &token);
699
700         if (token.value)
701                 free(token.value);
702 out:
703         return;
704
705 out_return_error:
706         do_error_downcall(clp->krb5_fd, uid, -1);
707         return;
708 }
709
710 /*
711  * this code uses the userland rpcsec gss library to create an spkm3
712  * context on behalf of the kernel
713  */
714 void
715 handle_spkm3_upcall(struct clnt_info *clp)
716 {
717         uid_t                   uid;
718         AUTH                    *auth;
719         struct authgss_private_data pd;
720         gss_buffer_desc         token;
721
722         printerr(2, "handling spkm3 upcall\n");
723
724         token.length = 0;
725         token.value = NULL;
726
727         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
728                 printerr(0, "WARNING: failed reading uid from spkm3 "
729                          "upcall pipe: %s\n", strerror(errno));
730                 goto out;
731         }
732
733         if (create_auth_rpc_client(clp, &auth, uid, AUTHTYPE_SPKM3)) {
734                 printerr(0, "WARNING: Failed to create spkm3 context for "
735                             "user with uid %d\n", uid);
736                 goto out_return_error;
737         }
738
739         if (!authgss_get_private_data(auth, &pd)) {
740                 printerr(0, "WARNING: Failed to obtain authentication "
741                             "data for user with uid %d for server %s\n",
742                          uid, clp->servername);
743                 goto out_return_error;
744         }
745
746         if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
747                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
748                             "user with uid %d for server\n",
749                          uid, clp->servername);
750                 goto out_return_error;
751         }
752
753         do_downcall(clp->spkm3_fd, uid, &pd, &token);
754
755         if (token.value)
756                 free(token.value);
757 out:
758         return;
759
760 out_return_error:
761         do_error_downcall(clp->spkm3_fd, uid, -1);
762         return;
763 }