]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/gssd_proc.c
Check the info file nfs/rpc_pipefs/nfs/clnt?/info to
[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         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                 gssd_setup_krb5_user_gss_ccache(uid, clp->servername);
705
706                 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
707                                                      AUTHTYPE_KRB5);
708         }
709         if (create_resp != 0) {
710                 if (uid == 0 && root_uses_machine_creds == 1) {
711                         int success = 0;
712
713                         gssd_refresh_krb5_machine_credential(clp->servername,
714                                                              NULL);
715                         /*
716                          * Get a list of credential cache names and try each
717                          * of them until one works or we've tried them all
718                          */
719                         if (gssd_get_krb5_machine_cred_list(&credlist)) {
720                                 printerr(0, "ERROR: No credentials found "
721                                          "for connection to server %s\n",
722                                          clp->servername);
723                                         goto out_return_error;
724                         }
725                         for (ccname = credlist; ccname && *ccname; ccname++) {
726                                 gssd_setup_krb5_machine_gss_ccache(*ccname);
727                                 if ((create_auth_rpc_client(clp, &rpc_clnt,
728                                                             &auth, uid,
729                                                             AUTHTYPE_KRB5)) == 0) {
730                                         /* Success! */
731                                         success++;
732                                         break;
733                                 }
734                                 printerr(2, "WARNING: Failed to create krb5 context "
735                                          "for user with uid %d with credentials "
736                                          "cache %s for server %s\n",
737                                          uid, *ccname, clp->servername);
738                         }
739                         gssd_free_krb5_machine_cred_list(credlist);
740                         if (!success) {
741                                 printerr(0, "WARNING: Failed to create krb5 context "
742                                          "for user with uid %d with any "
743                                          "credentials cache for server %s\n",
744                                          uid, clp->servername);
745                                 goto out_return_error;
746                         }
747                 } else {
748                         printerr(0, "WARNING: Failed to create krb5 context "
749                                  "for user with uid %d for server %s\n",
750                                  uid, clp->servername);
751                         goto out_return_error;
752                 }
753         }
754
755         if (!authgss_get_private_data(auth, &pd)) {
756                 printerr(0, "WARNING: Failed to obtain authentication "
757                             "data for user with uid %d for server %s\n",
758                          uid, clp->servername);
759                 goto out_return_error;
760         }
761
762         if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid)) {
763                 printerr(0, "WARNING: Failed to serialize krb5 context for "
764                             "user with uid %d for server %s\n",
765                          uid, clp->servername);
766                 goto out_return_error;
767         }
768
769         do_downcall(clp->krb5_fd, uid, &pd, &token);
770
771 out:
772         if (token.value)
773                 free(token.value);
774         if (pd.pd_ctx_hndl.length != 0)
775                 authgss_free_private_data(&pd);
776         if (auth)
777                 AUTH_DESTROY(auth);
778         if (rpc_clnt)
779                 clnt_destroy(rpc_clnt);
780         return;
781
782 out_return_error:
783         do_error_downcall(clp->krb5_fd, uid, -1);
784         goto out;
785 }
786
787 /*
788  * this code uses the userland rpcsec gss library to create an spkm3
789  * context on behalf of the kernel
790  */
791 void
792 handle_spkm3_upcall(struct clnt_info *clp)
793 {
794         uid_t                   uid;
795         CLIENT                  *rpc_clnt = NULL;
796         AUTH                    *auth = NULL;
797         struct authgss_private_data pd;
798         gss_buffer_desc         token;
799
800         printerr(2, "handling spkm3 upcall\n");
801
802         token.length = 0;
803         token.value = NULL;
804
805         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
806                 printerr(0, "WARNING: failed reading uid from spkm3 "
807                          "upcall pipe: %s\n", strerror(errno));
808                 goto out;
809         }
810
811         if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
812                 printerr(0, "WARNING: Failed to create spkm3 context for "
813                             "user with uid %d\n", uid);
814                 goto out_return_error;
815         }
816
817         if (!authgss_get_private_data(auth, &pd)) {
818                 printerr(0, "WARNING: Failed to obtain authentication "
819                             "data for user with uid %d for server %s\n",
820                          uid, clp->servername);
821                 goto out_return_error;
822         }
823
824         if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
825                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
826                             "user with uid %d for server\n",
827                          uid, clp->servername);
828                 goto out_return_error;
829         }
830
831         do_downcall(clp->spkm3_fd, uid, &pd, &token);
832
833 out:
834         if (token.value)
835                 free(token.value);
836         if (auth)
837                 AUTH_DESTROY(auth);
838         if (rpc_clnt)
839                 clnt_destroy(rpc_clnt);
840         return;
841
842 out_return_error:
843         do_error_downcall(clp->spkm3_fd, uid, -1);
844         goto out;
845 }