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