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