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