Correct PPTP server firewall rules chain.
[tomato/davidwu.git] / release / src / router / nfs-utils / utils / gssd / gssd_proc.c
blobd0d3f7ffa219b03501e5079f85d759c619013a5f
1 /*
2 gssd_proc.c
4 Copyright (c) 2000-2004 The Regents of the University of Michigan.
5 All rights reserved.
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.
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions
16 are met:
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.
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.
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif /* HAVE_CONFIG_H */
45 #ifndef _GNU_SOURCE
46 #define _GNU_SOURCE
47 #endif
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>
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>
70 #include "gssd.h"
71 #include "err_util.h"
72 #include "gss_util.h"
73 #include "gss_oids.h"
74 #include "krb5_util.h"
75 #include "context.h"
78 * pollarray:
79 * array of struct pollfd suitable to pass to poll. initialized to
80 * zero - a zero struct is ignored by poll() because the events mask is 0.
82 * clnt_list:
83 * linked list of struct clnt_info which associates a clntXXX directory
84 * with an index into pollarray[], and other basic data about that client.
86 * Directory structure: created by the kernel nfs client
87 * {pipefs_nfsdir}/clntXX : one per rpc_clnt struct in the kernel
88 * {pipefs_nfsdir}/clntXX/krb5 : read uid for which kernel wants
89 * a context, write the resulting context
90 * {pipefs_nfsdir}/clntXX/info : stores info such as server name
92 * Algorithm:
93 * Poll all {pipefs_nfsdir}/clntXX/krb5 files. When ready, data read
94 * is a uid; performs rpcsec_gss context initialization protocol to
95 * get a cred for that user. Writes result to corresponding krb5 file
96 * in a form the kernel code will understand.
97 * In addition, we make sure we are notified whenever anything is
98 * created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories,
99 * and rescan the whole {pipefs_nfsdir} when this happens.
102 struct pollfd * pollarray;
104 int pollsize; /* the size of pollaray (in pollfd's) */
106 /* XXX buffer problems: */
107 static int
108 read_service_info(char *info_file_name, char **servicename, char **servername,
109 int *prog, int *vers, char **protocol, int *port) {
110 #define INFOBUFLEN 256
111 char buf[INFOBUFLEN];
112 static char dummy[128];
113 int nbytes;
114 static char service[128];
115 static char address[128];
116 char program[16];
117 char version[16];
118 char protoname[16];
119 char cb_port[128];
120 char *p;
121 in_addr_t inaddr;
122 int fd = -1;
123 struct hostent *ent = NULL;
124 int numfields;
126 *servicename = *servername = *protocol = NULL;
128 if ((fd = open(info_file_name, O_RDONLY)) == -1) {
129 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
130 strerror(errno));
131 goto fail;
133 if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
134 goto fail;
135 close(fd);
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);
146 if (numfields == 5) {
147 strcpy(protoname, "tcp");
148 } else if (numfields != 6) {
149 goto fail;
152 cb_port[0] = '\0';
153 if ((p = strstr(buf, "port")) != NULL)
154 sscanf(p, "port: %127s\n", cb_port);
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;
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;
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);
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 if (*servername) free(*servername);
186 if (*servicename) free(*servicename);
187 if (*protocol) free(*protocol);
188 return -1;
191 static void
192 destroy_client(struct clnt_info *clp)
194 if (clp->krb5_poll_index != -1)
195 memset(&pollarray[clp->krb5_poll_index], 0,
196 sizeof(struct pollfd));
197 if (clp->spkm3_poll_index != -1)
198 memset(&pollarray[clp->spkm3_poll_index], 0,
199 sizeof(struct pollfd));
200 if (clp->dir_fd != -1) close(clp->dir_fd);
201 if (clp->krb5_fd != -1) close(clp->krb5_fd);
202 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
203 if (clp->dirname) free(clp->dirname);
204 if (clp->servicename) free(clp->servicename);
205 if (clp->servername) free(clp->servername);
206 if (clp->protocol) free(clp->protocol);
207 free(clp);
210 static struct clnt_info *
211 insert_new_clnt(void)
213 struct clnt_info *clp = NULL;
215 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
216 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
217 strerror(errno));
218 goto out;
220 clp->krb5_poll_index = -1;
221 clp->spkm3_poll_index = -1;
222 clp->krb5_fd = -1;
223 clp->spkm3_fd = -1;
224 clp->dir_fd = -1;
226 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
227 out:
228 return clp;
231 static int
232 process_clnt_dir_files(struct clnt_info * clp)
234 char kname[32];
235 char sname[32];
236 char info_file_name[32];
238 if (clp->krb5_fd == -1) {
239 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
240 clp->krb5_fd = open(kname, O_RDWR);
242 if (clp->spkm3_fd == -1) {
243 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
244 clp->spkm3_fd = open(sname, O_RDWR);
246 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
247 return -1;
248 snprintf(info_file_name, sizeof(info_file_name), "%s/info",
249 clp->dirname);
250 if ((clp->servicename == NULL) &&
251 read_service_info(info_file_name, &clp->servicename,
252 &clp->servername, &clp->prog, &clp->vers,
253 &clp->protocol, &clp->port))
254 return -1;
255 return 0;
258 static int
259 get_poll_index(int *ind)
261 int i;
263 *ind = -1;
264 for (i=0; i<FD_ALLOC_BLOCK; i++) {
265 if (pollarray[i].events == 0) {
266 *ind = i;
267 break;
270 if (*ind == -1) {
271 printerr(0, "ERROR: No pollarray slots open\n");
272 return -1;
274 return 0;
278 static int
279 insert_clnt_poll(struct clnt_info *clp)
281 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
282 if (get_poll_index(&clp->krb5_poll_index)) {
283 printerr(0, "ERROR: Too many krb5 clients\n");
284 return -1;
286 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
287 pollarray[clp->krb5_poll_index].events |= POLLIN;
290 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
291 if (get_poll_index(&clp->spkm3_poll_index)) {
292 printerr(0, "ERROR: Too many spkm3 clients\n");
293 return -1;
295 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
296 pollarray[clp->spkm3_poll_index].events |= POLLIN;
299 return 0;
302 static void
303 process_clnt_dir(char *dir)
305 struct clnt_info * clp;
307 if (!(clp = insert_new_clnt()))
308 goto fail_destroy_client;
310 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
311 goto fail_destroy_client;
313 memcpy(clp->dirname, dir, strlen(dir));
314 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
315 printerr(0, "ERROR: can't open %s: %s\n",
316 clp->dirname, strerror(errno));
317 goto fail_destroy_client;
319 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
320 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
322 if (process_clnt_dir_files(clp))
323 goto fail_keep_client;
325 if (insert_clnt_poll(clp))
326 goto fail_destroy_client;
328 return;
330 fail_destroy_client:
331 if (clp) {
332 TAILQ_REMOVE(&clnt_list, clp, list);
333 destroy_client(clp);
335 fail_keep_client:
336 /* We couldn't find some subdirectories, but we keep the client
337 * around in case we get a notification on the directory when the
338 * subdirectories are created. */
339 return;
342 void
343 init_client_list(void)
345 TAILQ_INIT(&clnt_list);
346 /* Eventually plan to grow/shrink poll array: */
347 pollsize = FD_ALLOC_BLOCK;
348 pollarray = calloc(pollsize, sizeof(struct pollfd));
352 * This is run after a DNOTIFY signal, and should clear up any
353 * directories that are no longer around, and re-scan any existing
354 * directories, since the DNOTIFY could have been in there.
356 static void
357 update_old_clients(struct dirent **namelist, int size)
359 struct clnt_info *clp;
360 void *saveprev;
361 int i, stillhere;
363 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
364 stillhere = 0;
365 for (i=0; i < size; i++) {
366 if (!strcmp(clp->dirname, namelist[i]->d_name)) {
367 stillhere = 1;
368 break;
371 if (!stillhere) {
372 printerr(2, "destroying client %s\n", clp->dirname);
373 saveprev = clp->list.tqe_prev;
374 TAILQ_REMOVE(&clnt_list, clp, list);
375 destroy_client(clp);
376 clp = saveprev;
379 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
380 if (!process_clnt_dir_files(clp))
381 insert_clnt_poll(clp);
385 /* Search for a client by directory name, return 1 if found, 0 otherwise */
386 static int
387 find_client(char *dirname)
389 struct clnt_info *clp;
391 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
392 if (!strcmp(clp->dirname, dirname))
393 return 1;
394 return 0;
397 /* Used to read (and re-read) list of clients, set up poll array. */
399 update_client_list(void)
401 struct dirent **namelist;
402 int i, j;
404 if (chdir(pipefs_nfsdir) < 0) {
405 printerr(0, "ERROR: can't chdir to %s: %s\n",
406 pipefs_nfsdir, strerror(errno));
407 return -1;
410 j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
411 if (j < 0) {
412 printerr(0, "ERROR: can't scandir %s: %s\n",
413 pipefs_nfsdir, strerror(errno));
414 return -1;
416 update_old_clients(namelist, j);
417 for (i=0; i < j; i++) {
418 if (i < FD_ALLOC_BLOCK
419 && !strncmp(namelist[i]->d_name, "clnt", 4)
420 && !find_client(namelist[i]->d_name))
421 process_clnt_dir(namelist[i]->d_name);
422 free(namelist[i]);
425 free(namelist);
426 return 0;
429 static int
430 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
431 gss_buffer_desc *context_token)
433 char *buf = NULL, *p = NULL, *end = NULL;
434 unsigned int timeout = context_timeout;
435 unsigned int buf_size = 0;
437 printerr(1, "doing downcall\n");
438 buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
439 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
440 sizeof(context_token->length) + context_token->length;
441 p = buf = malloc(buf_size);
442 end = buf + buf_size;
444 if (WRITE_BYTES(&p, end, uid)) goto out_err;
445 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
446 if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
447 if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
448 if (write_buffer(&p, end, context_token)) goto out_err;
450 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
451 if (buf) free(buf);
452 return 0;
453 out_err:
454 if (buf) free(buf);
455 printerr(1, "Failed to write downcall!\n");
456 return -1;
459 static int
460 do_error_downcall(int k5_fd, uid_t uid, int err)
462 char buf[1024];
463 char *p = buf, *end = buf + 1024;
464 unsigned int timeout = 0;
465 int zero = 0;
467 printerr(1, "doing error downcall\n");
469 if (WRITE_BYTES(&p, end, uid)) goto out_err;
470 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
471 /* use seq_win = 0 to indicate an error: */
472 if (WRITE_BYTES(&p, end, zero)) goto out_err;
473 if (WRITE_BYTES(&p, end, err)) goto out_err;
475 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
476 return 0;
477 out_err:
478 printerr(1, "Failed to write error downcall!\n");
479 return -1;
483 * Create an RPC connection and establish an authenticated
484 * gss context with a server.
486 int create_auth_rpc_client(struct clnt_info *clp,
487 CLIENT **clnt_return,
488 AUTH **auth_return,
489 uid_t uid,
490 int authtype)
492 CLIENT *rpc_clnt = NULL;
493 struct rpc_gss_sec sec;
494 AUTH *auth = NULL;
495 uid_t save_uid = -1;
496 int retval = -1;
497 int errcode;
498 OM_uint32 min_stat;
499 char rpc_errmsg[1024];
500 int sockp = RPC_ANYSOCK;
501 int sendsz = 32768, recvsz = 32768;
502 struct addrinfo ai_hints, *a = NULL;
503 char service[64];
504 char *at_sign;
506 /* Create the context as the user (not as root) */
507 save_uid = geteuid();
508 if (setfsuid(uid) != 0) {
509 printerr(0, "WARNING: Failed to setfsuid for "
510 "user with uid %d\n", uid);
511 goto out_fail;
513 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
514 uid, save_uid);
516 sec.qop = GSS_C_QOP_DEFAULT;
517 sec.svc = RPCSEC_GSS_SVC_NONE;
518 sec.cred = GSS_C_NO_CREDENTIAL;
519 sec.req_flags = 0;
520 if (authtype == AUTHTYPE_KRB5) {
521 sec.mech = (gss_OID)&krb5oid;
522 sec.req_flags = GSS_C_MUTUAL_FLAG;
524 else if (authtype == AUTHTYPE_SPKM3) {
525 sec.mech = (gss_OID)&spkm3oid;
526 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
527 * Need a way to switch....
529 sec.req_flags = GSS_C_MUTUAL_FLAG;
531 else {
532 printerr(0, "ERROR: Invalid authentication type (%d) "
533 "in create_auth_rpc_client\n", authtype);
534 goto out_fail;
538 if (authtype == AUTHTYPE_KRB5) {
539 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
541 * Do this before creating rpc connection since we won't need
542 * rpc connection if it fails!
544 if (limit_krb5_enctypes(&sec, uid)) {
545 printerr(1, "WARNING: Failed while limiting krb5 "
546 "encryption types for user with uid %d\n",
547 uid);
548 goto out_fail;
550 #endif
553 /* create an rpc connection to the nfs server */
555 printerr(2, "creating %s client for server %s\n", clp->protocol,
556 clp->servername);
558 memset(&ai_hints, '\0', sizeof(ai_hints));
559 ai_hints.ai_family = PF_INET;
560 ai_hints.ai_flags |= AI_CANONNAME;
561 if ((strcmp(clp->protocol, "tcp")) == 0) {
562 ai_hints.ai_socktype = SOCK_STREAM;
563 ai_hints.ai_protocol = IPPROTO_TCP;
564 } else if ((strcmp(clp->protocol, "udp")) == 0) {
565 ai_hints.ai_socktype = SOCK_DGRAM;
566 ai_hints.ai_protocol = IPPROTO_UDP;
567 } else {
568 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
569 "for connection to server %s for user with uid %d\n",
570 clp->protocol, clp->servername, uid);
571 goto out_fail;
574 /* extract the service name from clp->servicename */
575 if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
576 printerr(0, "WARNING: servicename (%s) not formatted as "
577 "expected with service@host\n", clp->servicename);
578 goto out_fail;
580 if ((at_sign - clp->servicename) >= sizeof(service)) {
581 printerr(0, "WARNING: service portion of servicename (%s) "
582 "is too long!\n", clp->servicename);
583 goto out_fail;
585 strncpy(service, clp->servicename, at_sign - clp->servicename);
586 service[at_sign - clp->servicename] = '\0';
588 errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
589 if (errcode) {
590 printerr(0, "WARNING: Error from getaddrinfo for server "
591 "'%s': %s\n", clp->servername, gai_strerror(errcode));
592 goto out_fail;
595 if (a == NULL) {
596 printerr(0, "WARNING: No address information found for "
597 "connection to server %s for user with uid %d\n",
598 clp->servername, uid);
599 goto out_fail;
601 if (clp->port)
602 ((struct sockaddr_in *)a->ai_addr)->sin_port = htons(clp->port);
603 if (a->ai_protocol == IPPROTO_TCP) {
604 if ((rpc_clnt = clnttcp_create(
605 (struct sockaddr_in *) a->ai_addr,
606 clp->prog, clp->vers, &sockp,
607 sendsz, recvsz)) == NULL) {
608 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
609 "WARNING: can't create tcp 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;
616 } else if (a->ai_protocol == IPPROTO_UDP) {
617 const struct timeval timeout = {5, 0};
618 if ((rpc_clnt = clntudp_bufcreate(
619 (struct sockaddr_in *) a->ai_addr,
620 clp->prog, clp->vers, timeout,
621 &sockp, sendsz, recvsz)) == NULL) {
622 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
623 "WARNING: can't create udp rpc_clnt "
624 "for server %s for user with uid %d",
625 clp->servername, uid);
626 printerr(0, "%s\n",
627 clnt_spcreateerror(rpc_errmsg));
628 goto out_fail;
630 } else {
631 /* Shouldn't happen! */
632 printerr(0, "ERROR: requested protocol '%s', but "
633 "got addrinfo with protocol %d\n",
634 clp->protocol, a->ai_protocol);
635 goto out_fail;
637 /* We're done with this */
638 freeaddrinfo(a);
639 a = NULL;
641 printerr(2, "creating context with server %s\n", clp->servicename);
642 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
643 if (!auth) {
644 /* Our caller should print appropriate message */
645 printerr(2, "WARNING: Failed to create %s context for "
646 "user with uid %d for server %s\n",
647 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
648 uid, clp->servername);
649 goto out_fail;
652 /* Success !!! */
653 rpc_clnt->cl_auth = auth;
654 *clnt_return = rpc_clnt;
655 *auth_return = auth;
656 retval = 0;
658 out:
659 if (sec.cred != GSS_C_NO_CREDENTIAL)
660 gss_release_cred(&min_stat, &sec.cred);
661 if (a != NULL) freeaddrinfo(a);
662 /* Restore euid to original value */
663 if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
664 printerr(0, "WARNING: Failed to restore fsuid"
665 " to uid %d from %d\n", save_uid, uid);
667 return retval;
669 out_fail:
670 /* Only destroy here if failure. Otherwise, caller is responsible */
671 if (rpc_clnt) clnt_destroy(rpc_clnt);
673 goto out;
678 * this code uses the userland rpcsec gss library to create a krb5
679 * context on behalf of the kernel
681 void
682 handle_krb5_upcall(struct clnt_info *clp)
684 uid_t uid;
685 CLIENT *rpc_clnt = NULL;
686 AUTH *auth = NULL;
687 struct authgss_private_data pd;
688 gss_buffer_desc token;
689 char **credlist = NULL;
690 char **ccname;
691 char **dirname;
692 int create_resp = -1;
694 printerr(1, "handling krb5 upcall\n");
696 token.length = 0;
697 token.value = NULL;
698 memset(&pd, 0, sizeof(struct authgss_private_data));
700 if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
701 printerr(0, "WARNING: failed reading uid from krb5 "
702 "upcall pipe: %s\n", strerror(errno));
703 goto out;
706 if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
707 /* Tell krb5 gss which credentials cache to use */
708 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
709 if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
710 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
711 AUTHTYPE_KRB5);
712 if (create_resp == 0)
713 break;
716 if (create_resp != 0) {
717 if (uid == 0 && root_uses_machine_creds == 1) {
718 int success = 0;
720 gssd_refresh_krb5_machine_credential(clp->servername,
721 NULL);
723 * Get a list of credential cache names and try each
724 * of them until one works or we've tried them all
726 if (gssd_get_krb5_machine_cred_list(&credlist)) {
727 printerr(0, "ERROR: No credentials found "
728 "for connection to server %s\n",
729 clp->servername);
730 goto out_return_error;
732 for (ccname = credlist; ccname && *ccname; ccname++) {
733 gssd_setup_krb5_machine_gss_ccache(*ccname);
734 if ((create_auth_rpc_client(clp, &rpc_clnt,
735 &auth, uid,
736 AUTHTYPE_KRB5)) == 0) {
737 /* Success! */
738 success++;
739 break;
741 printerr(2, "WARNING: Failed to create krb5 context "
742 "for user with uid %d with credentials "
743 "cache %s for server %s\n",
744 uid, *ccname, clp->servername);
746 gssd_free_krb5_machine_cred_list(credlist);
747 if (!success) {
748 printerr(1, "WARNING: Failed to create krb5 context "
749 "for user with uid %d with any "
750 "credentials cache for server %s\n",
751 uid, clp->servername);
752 goto out_return_error;
754 } else {
755 printerr(1, "WARNING: Failed to create krb5 context "
756 "for user with uid %d for server %s\n",
757 uid, clp->servername);
758 goto out_return_error;
762 if (!authgss_get_private_data(auth, &pd)) {
763 printerr(1, "WARNING: Failed to obtain authentication "
764 "data for user with uid %d for server %s\n",
765 uid, clp->servername);
766 goto out_return_error;
769 if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
770 printerr(0, "WARNING: Failed to serialize krb5 context for "
771 "user with uid %d for server %s\n",
772 uid, clp->servername);
773 goto out_return_error;
776 do_downcall(clp->krb5_fd, uid, &pd, &token);
778 out:
779 if (token.value)
780 free(token.value);
781 if (pd.pd_ctx_hndl.length != 0)
782 authgss_free_private_data(&pd);
783 if (auth)
784 AUTH_DESTROY(auth);
785 if (rpc_clnt)
786 clnt_destroy(rpc_clnt);
787 return;
789 out_return_error:
790 do_error_downcall(clp->krb5_fd, uid, -1);
791 goto out;
795 * this code uses the userland rpcsec gss library to create an spkm3
796 * context on behalf of the kernel
798 void
799 handle_spkm3_upcall(struct clnt_info *clp)
801 uid_t uid;
802 CLIENT *rpc_clnt = NULL;
803 AUTH *auth = NULL;
804 struct authgss_private_data pd;
805 gss_buffer_desc token;
807 printerr(2, "handling spkm3 upcall\n");
809 token.length = 0;
810 token.value = NULL;
812 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
813 printerr(0, "WARNING: failed reading uid from spkm3 "
814 "upcall pipe: %s\n", strerror(errno));
815 goto out;
818 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
819 printerr(0, "WARNING: Failed to create spkm3 context for "
820 "user with uid %d\n", uid);
821 goto out_return_error;
824 if (!authgss_get_private_data(auth, &pd)) {
825 printerr(0, "WARNING: Failed to obtain authentication "
826 "data for user with uid %d for server %s\n",
827 uid, clp->servername);
828 goto out_return_error;
831 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) {
832 printerr(0, "WARNING: Failed to serialize spkm3 context for "
833 "user with uid %d for server\n",
834 uid, clp->servername);
835 goto out_return_error;
838 do_downcall(clp->spkm3_fd, uid, &pd, &token);
840 out:
841 if (token.value)
842 free(token.value);
843 if (auth)
844 AUTH_DESTROY(auth);
845 if (rpc_clnt)
846 clnt_destroy(rpc_clnt);
847 return;
849 out_return_error:
850 do_error_downcall(clp->spkm3_fd, uid, -1);
851 goto out;