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