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