]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/gssd_proc.c
be6f44040d0d373dadaa2c73c863fa51f67c4fab
[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 = 0; /* XXX decide on a reasonable value */
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         /* Not setting any timeout for now: */
442         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
443         if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
444         if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
445         if (write_buffer(&p, end, context_token)) goto out_err;
446
447         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
448         if (buf) free(buf);
449         return 0;
450 out_err:
451         if (buf) free(buf);
452         printerr(0, "Failed to write downcall!\n");
453         return -1;
454 }
455
456 static int
457 do_error_downcall(int k5_fd, uid_t uid, int err)
458 {
459         char    buf[1024];
460         char    *p = buf, *end = buf + 1024;
461         unsigned int timeout = 0;
462         int     zero = 0;
463
464         printerr(1, "doing error downcall\n");
465
466         if (WRITE_BYTES(&p, end, uid)) goto out_err;
467         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
468         /* use seq_win = 0 to indicate an error: */
469         if (WRITE_BYTES(&p, end, zero)) goto out_err;
470         if (WRITE_BYTES(&p, end, err)) goto out_err;
471
472         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
473         return 0;
474 out_err:
475         printerr(1, "Failed to write error downcall!\n");
476         return -1;
477 }
478
479 /*
480  * Create an RPC connection and establish an authenticated
481  * gss context with a server.
482  */
483 int create_auth_rpc_client(struct clnt_info *clp,
484                            CLIENT **clnt_return,
485                            AUTH **auth_return,
486                            uid_t uid,
487                            int authtype)
488 {
489         CLIENT                  *rpc_clnt = NULL;
490         struct rpc_gss_sec      sec;
491         AUTH                    *auth = NULL;
492         uid_t                   save_uid = -1;
493         int                     retval = -1;
494         int                     errcode;
495         OM_uint32               min_stat;
496         char                    rpc_errmsg[1024];
497         int                     sockp = RPC_ANYSOCK;
498         int                     sendsz = 32768, recvsz = 32768;
499         struct addrinfo         ai_hints, *a = NULL;
500         char                    service[64];
501         char                    *at_sign;
502
503         /* Create the context as the user (not as root) */
504         save_uid = geteuid();
505         if (setfsuid(uid) != 0) {
506                 printerr(0, "WARNING: Failed to setfsuid for "
507                             "user with uid %d\n", uid);
508                 goto out_fail;
509         }
510         printerr(2, "creating context using fsuid %d (save_uid %d)\n",
511                         uid, save_uid);
512
513         sec.qop = GSS_C_QOP_DEFAULT;
514         sec.svc = RPCSEC_GSS_SVC_NONE;
515         sec.cred = GSS_C_NO_CREDENTIAL;
516         sec.req_flags = 0;
517         if (authtype == AUTHTYPE_KRB5) {
518                 sec.mech = (gss_OID)&krb5oid;
519                 sec.req_flags = GSS_C_MUTUAL_FLAG;
520         }
521         else if (authtype == AUTHTYPE_SPKM3) {
522                 sec.mech = (gss_OID)&spkm3oid;
523                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
524                  * Need a way to switch....
525                  */
526                 sec.req_flags = GSS_C_MUTUAL_FLAG;
527         }
528         else {
529                 printerr(0, "ERROR: Invalid authentication type (%d) "
530                         "in create_auth_rpc_client\n", authtype);
531                 goto out_fail;
532         }
533
534
535         if (authtype == AUTHTYPE_KRB5) {
536 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
537                 /*
538                  * Do this before creating rpc connection since we won't need
539                  * rpc connection if it fails!
540                  */
541                 if (limit_krb5_enctypes(&sec, uid)) {
542                         printerr(1, "WARNING: Failed while limiting krb5 "
543                                     "encryption types for user with uid %d\n",
544                                  uid);
545                         goto out_fail;
546                 }
547 #endif
548         }
549
550         /* create an rpc connection to the nfs server */
551
552         printerr(2, "creating %s client for server %s\n", clp->protocol,
553                         clp->servername);
554
555         memset(&ai_hints, '\0', sizeof(ai_hints));
556         ai_hints.ai_family = PF_INET;
557         ai_hints.ai_flags |= AI_CANONNAME;
558         if ((strcmp(clp->protocol, "tcp")) == 0) {
559                 ai_hints.ai_socktype = SOCK_STREAM;
560                 ai_hints.ai_protocol = IPPROTO_TCP;
561         } else if ((strcmp(clp->protocol, "udp")) == 0) {
562                 ai_hints.ai_socktype = SOCK_DGRAM;
563                 ai_hints.ai_protocol = IPPROTO_UDP;
564         } else {
565                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
566                          "for connection to server %s for user with uid %d\n",
567                          clp->protocol, clp->servername, uid);
568                 goto out_fail;
569         }
570
571         /* extract the service name from clp->servicename */
572         if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
573                 printerr(0, "WARNING: servicename (%s) not formatted as "
574                         "expected with service@host\n", clp->servicename);
575                 goto out_fail;
576         }
577         if ((at_sign - clp->servicename) >= sizeof(service)) {
578                 printerr(0, "WARNING: service portion of servicename (%s) "
579                         "is too long!\n", clp->servicename);
580                 goto out_fail;
581         }
582         strncpy(service, clp->servicename, at_sign - clp->servicename);
583         service[at_sign - clp->servicename] = '\0';
584
585         errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
586         if (errcode) {
587                 printerr(0, "WARNING: Error from getaddrinfo for server "
588                          "'%s': %s\n", clp->servername, gai_strerror(errcode));
589                 goto out_fail;
590         }
591
592         if (a == NULL) {
593                 printerr(0, "WARNING: No address information found for "
594                          "connection to server %s for user with uid %d\n",
595                          clp->servername, uid);
596                 goto out_fail;
597         }
598         if (clp->port)
599                 ((struct sockaddr_in *)a->ai_addr)->sin_port = htons(clp->port);
600         if (a->ai_protocol == IPPROTO_TCP) {
601                 if ((rpc_clnt = clnttcp_create(
602                                         (struct sockaddr_in *) a->ai_addr,
603                                         clp->prog, clp->vers, &sockp,
604                                         sendsz, recvsz)) == NULL) {
605                         snprintf(rpc_errmsg, sizeof(rpc_errmsg),
606                                  "WARNING: can't create tcp rpc_clnt "
607                                  "for server %s for user with uid %d",
608                                  clp->servername, uid);
609                         printerr(0, "%s\n",
610                                  clnt_spcreateerror(rpc_errmsg));
611                         goto out_fail;
612                 }
613         } else if (a->ai_protocol == IPPROTO_UDP) {
614                 const struct timeval timeout = {5, 0};
615                 if ((rpc_clnt = clntudp_bufcreate(
616                                         (struct sockaddr_in *) a->ai_addr,
617                                         clp->prog, clp->vers, timeout,
618                                         &sockp, sendsz, recvsz)) == NULL) {
619                         snprintf(rpc_errmsg, sizeof(rpc_errmsg),
620                                  "WARNING: can't create udp rpc_clnt "
621                                  "for server %s for user with uid %d",
622                                  clp->servername, uid);
623                         printerr(0, "%s\n",
624                                  clnt_spcreateerror(rpc_errmsg));
625                         goto out_fail;
626                 }
627         } else {
628                 /* Shouldn't happen! */
629                 printerr(0, "ERROR: requested protocol '%s', but "
630                          "got addrinfo with protocol %d\n",
631                          clp->protocol, a->ai_protocol);
632                 goto out_fail;
633         }
634         /* We're done with this */
635         freeaddrinfo(a);
636         a = NULL;
637
638         printerr(2, "creating context with server %s\n", clp->servicename);
639         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
640         if (!auth) {
641                 /* Our caller should print appropriate message */
642                 printerr(2, "WARNING: Failed to create %s context for "
643                             "user with uid %d for server %s\n",
644                         (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
645                          uid, clp->servername);
646                 goto out_fail;
647         }
648
649         /* Success !!! */
650         rpc_clnt->cl_auth = auth;
651         *clnt_return = rpc_clnt;
652         *auth_return = auth;
653         retval = 0;
654
655   out:
656         if (sec.cred != GSS_C_NO_CREDENTIAL)
657                 gss_release_cred(&min_stat, &sec.cred);
658         if (a != NULL) freeaddrinfo(a);
659         /* Restore euid to original value */
660         if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
661                 printerr(0, "WARNING: Failed to restore fsuid"
662                             " to uid %d from %d\n", save_uid, uid);
663         }
664         return retval;
665
666   out_fail:
667         /* Only destroy here if failure.  Otherwise, caller is responsible */
668         if (rpc_clnt) clnt_destroy(rpc_clnt);
669
670         goto out;
671 }
672
673
674 /*
675  * this code uses the userland rpcsec gss library to create a krb5
676  * context on behalf of the kernel
677  */
678 void
679 handle_krb5_upcall(struct clnt_info *clp)
680 {
681         uid_t                   uid;
682         CLIENT                  *rpc_clnt = NULL;
683         AUTH                    *auth = NULL;
684         struct authgss_private_data pd;
685         gss_buffer_desc         token;
686         char                    **credlist = NULL;
687         char                    **ccname;
688         char                    **dirname;
689         int                     create_resp = -1;
690
691         printerr(1, "handling krb5 upcall\n");
692
693         token.length = 0;
694         token.value = NULL;
695         memset(&pd, 0, sizeof(struct authgss_private_data));
696
697         if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
698                 printerr(0, "WARNING: failed reading uid from krb5 "
699                             "upcall pipe: %s\n", strerror(errno));
700                 goto out;
701         }
702
703         if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
704                 /* Tell krb5 gss which credentials cache to use */
705                 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
706                         gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname);
707
708                         create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
709                                                              AUTHTYPE_KRB5);
710                         if (create_resp == 0)
711                                 break;
712                 }
713         }
714         if (create_resp != 0) {
715                 if (uid == 0 && root_uses_machine_creds == 1) {
716                         int success = 0;
717
718                         gssd_refresh_krb5_machine_credential(clp->servername,
719                                                              NULL);
720                         /*
721                          * Get a list of credential cache names and try each
722                          * of them until one works or we've tried them all
723                          */
724                         if (gssd_get_krb5_machine_cred_list(&credlist)) {
725                                 printerr(0, "ERROR: No credentials found "
726                                          "for connection to server %s\n",
727                                          clp->servername);
728                                         goto out_return_error;
729                         }
730                         for (ccname = credlist; ccname && *ccname; ccname++) {
731                                 gssd_setup_krb5_machine_gss_ccache(*ccname);
732                                 if ((create_auth_rpc_client(clp, &rpc_clnt,
733                                                             &auth, uid,
734                                                             AUTHTYPE_KRB5)) == 0) {
735                                         /* Success! */
736                                         success++;
737                                         break;
738                                 }
739                                 printerr(2, "WARNING: Failed to create krb5 context "
740                                          "for user with uid %d with credentials "
741                                          "cache %s for server %s\n",
742                                          uid, *ccname, clp->servername);
743                         }
744                         gssd_free_krb5_machine_cred_list(credlist);
745                         if (!success) {
746                                 printerr(0, "WARNING: Failed to create krb5 context "
747                                          "for user with uid %d with any "
748                                          "credentials cache for server %s\n",
749                                          uid, clp->servername);
750                                 goto out_return_error;
751                         }
752                 } else {
753                         printerr(0, "WARNING: Failed to create krb5 context "
754                                  "for user with uid %d for server %s\n",
755                                  uid, clp->servername);
756                         goto out_return_error;
757                 }
758         }
759
760         if (!authgss_get_private_data(auth, &pd)) {
761                 printerr(0, "WARNING: Failed to obtain authentication "
762                             "data for user with uid %d for server %s\n",
763                          uid, clp->servername);
764                 goto out_return_error;
765         }
766
767         if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid)) {
768                 printerr(0, "WARNING: Failed to serialize krb5 context for "
769                             "user with uid %d for server %s\n",
770                          uid, clp->servername);
771                 goto out_return_error;
772         }
773
774         do_downcall(clp->krb5_fd, uid, &pd, &token);
775
776 out:
777         if (token.value)
778                 free(token.value);
779         if (pd.pd_ctx_hndl.length != 0)
780                 authgss_free_private_data(&pd);
781         if (auth)
782                 AUTH_DESTROY(auth);
783         if (rpc_clnt)
784                 clnt_destroy(rpc_clnt);
785         return;
786
787 out_return_error:
788         do_error_downcall(clp->krb5_fd, uid, -1);
789         goto out;
790 }
791
792 /*
793  * this code uses the userland rpcsec gss library to create an spkm3
794  * context on behalf of the kernel
795  */
796 void
797 handle_spkm3_upcall(struct clnt_info *clp)
798 {
799         uid_t                   uid;
800         CLIENT                  *rpc_clnt = NULL;
801         AUTH                    *auth = NULL;
802         struct authgss_private_data pd;
803         gss_buffer_desc         token;
804
805         printerr(2, "handling spkm3 upcall\n");
806
807         token.length = 0;
808         token.value = NULL;
809
810         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
811                 printerr(0, "WARNING: failed reading uid from spkm3 "
812                          "upcall pipe: %s\n", strerror(errno));
813                 goto out;
814         }
815
816         if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
817                 printerr(0, "WARNING: Failed to create spkm3 context for "
818                             "user with uid %d\n", uid);
819                 goto out_return_error;
820         }
821
822         if (!authgss_get_private_data(auth, &pd)) {
823                 printerr(0, "WARNING: Failed to obtain authentication "
824                             "data for user with uid %d for server %s\n",
825                          uid, clp->servername);
826                 goto out_return_error;
827         }
828
829         if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
830                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
831                             "user with uid %d for server\n",
832                          uid, clp->servername);
833                 goto out_return_error;
834         }
835
836         do_downcall(clp->spkm3_fd, uid, &pd, &token);
837
838 out:
839         if (token.value)
840                 free(token.value);
841         if (auth)
842                 AUTH_DESTROY(auth);
843         if (rpc_clnt)
844                 clnt_destroy(rpc_clnt);
845         return;
846
847 out_return_error:
848         do_error_downcall(clp->spkm3_fd, uid, -1);
849         goto out;
850 }