4 Copyright (c) 2000-2004 The Regents of the University of Michigan.
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
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.
45 #include <sys/param.h>
48 #include <sys/socket.h>
49 #include <arpa/inet.h>
62 #include <gssapi/gssapi.h>
69 #include "krb5_util.h"
74 * array of struct pollfd suitable to pass to poll. initialized to
75 * zero - a zero struct is ignored by poll() because the events mask is 0.
78 * linked list of struct clnt_info which associates a clntXXX directory
79 * with an index into pollarray[], and other basic data about that client.
81 * Directory structure: created by the kernel nfs client
82 * /pipefsdir/clntXX : one per rpc_clnt struct in the kernel
83 * /pipefsdir/clntXX/krb5 : read uid for which kernel wants
84 * a context, write the resulting context
85 * /pipefsdir/clntXX/info : stores info such as server name
88 * Poll all /pipefsdir/clntXX/krb5 files. When ready, data read
89 * is a uid; performs rpcsec_gss context initialization protocol to
90 * get a cred for that user. Writes result to corresponding krb5 file
91 * in a form the kernel code will understand.
92 * In addition, we make sure we are notified whenever anything is
93 * created or destroyed in pipefsdir/ or in an of the clntXX directories,
94 * and rescan the whole pipefsdir when this happens.
97 struct pollfd * pollarray;
99 int pollsize; /* the size of pollaray (in pollfd's) */
101 /* XXX buffer problems: */
103 read_service_info(char *info_file_name, char **servicename, char **servername,
104 int *prog, int *vers, char **protocol) {
105 #define INFOBUFLEN 256
106 char buf[INFOBUFLEN];
107 static char dummy[128];
109 static char service[128];
110 static char address[128];
116 struct hostent *ent = NULL;
119 *servicename = *servername = *protocol = NULL;
121 if ((fd = open(info_file_name, O_RDONLY)) == -1) {
122 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
126 if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
130 numfields = sscanf(buf,"RPC server: %s\n"
131 "service: %s %s version %s\n"
135 service, program, version,
139 if (numfields == 5) {
140 strcpy(protoname, "tcp");
141 } else if (numfields != 6) {
145 /* check service, program, and version */
146 if(memcmp(service, "nfs", 3)) return -1;
147 *prog = atoi(program + 1); /* skip open paren */
148 *vers = atoi(version);
149 if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
152 /* create service name */
153 inaddr = inet_addr(address);
154 if (!(ent = gethostbyaddr(&inaddr, sizeof(inaddr), AF_INET))) {
155 printerr(0, "ERROR: can't resolve server %s name\n", address);
158 if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
160 memcpy(*servername, ent->h_name, strlen(ent->h_name));
161 snprintf(buf, INFOBUFLEN, "%s@%s", service, ent->h_name);
162 if (!(*servicename = calloc(strlen(buf) + 1, 1)))
164 memcpy(*servicename, buf, strlen(buf));
166 if (!(*protocol = strdup(protoname)))
170 printerr(0, "ERROR: failed to read service info\n");
171 if (fd != -1) close(fd);
172 if (*servername) free(*servername);
173 if (*servicename) free(*servicename);
174 if (*protocol) free(*protocol);
179 destroy_client(struct clnt_info *clp)
181 if (clp->dir_fd != -1) close(clp->dir_fd);
182 if (clp->krb5_fd != -1) close(clp->krb5_fd);
183 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
184 if (clp->dirname) free(clp->dirname);
185 if (clp->servicename) free(clp->servicename);
186 if (clp->servername) free(clp->servername);
187 if (clp->protocol) free(clp->protocol);
191 static struct clnt_info *
192 insert_new_clnt(void)
194 struct clnt_info *clp = NULL;
196 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
197 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
201 clp->krb5_poll_index = -1;
202 clp->spkm3_poll_index = -1;
207 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
213 process_clnt_dir_files(struct clnt_info * clp)
217 char info_file_name[32];
219 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
220 clp->krb5_fd = open(kname, O_RDWR);
221 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
222 clp->spkm3_fd = open(sname, O_RDWR);
223 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
225 snprintf(info_file_name, sizeof(info_file_name), "%s/info",
227 if (read_service_info(info_file_name, &clp->servicename,
228 &clp->servername, &clp->prog, &clp->vers,
235 get_poll_index(int *ind)
240 for (i=0; i<FD_ALLOC_BLOCK; i++) {
241 if (pollarray[i].events == 0) {
247 printerr(0, "ERROR: No pollarray slots open\n");
254 process_clnt_dir(char *dir)
256 struct clnt_info * clp;
258 if (!(clp = insert_new_clnt()))
259 goto fail_destroy_client;
261 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
262 goto fail_destroy_client;
264 memcpy(clp->dirname, dir, strlen(dir));
265 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
266 printerr(0, "ERROR: can't open %s: %s\n",
267 clp->dirname, strerror(errno));
268 goto fail_destroy_client;
270 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
271 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
273 if (process_clnt_dir_files(clp))
274 goto fail_keep_client;
276 if(clp->krb5_fd != -1) {
277 if (get_poll_index(&clp->krb5_poll_index)) {
278 printerr(0, "ERROR: Too many krb5 clients\n");
279 goto fail_destroy_client;
281 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
282 pollarray[clp->krb5_poll_index].events |= POLLIN;
285 if(clp->spkm3_fd != -1) {
286 if (get_poll_index(&clp->spkm3_poll_index)) {
287 printerr(0, "ERROR: Too many spkm3 clients\n");
288 goto fail_destroy_client;
290 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
291 pollarray[clp->spkm3_poll_index].events |= POLLIN;
298 TAILQ_REMOVE(&clnt_list, clp, list);
302 /* We couldn't find some subdirectories, but we keep the client
303 * around in case we get a notification on the directory when the
304 * subdirectories are created. */
309 init_client_list(void)
311 TAILQ_INIT(&clnt_list);
312 /* Eventually plan to grow/shrink poll array: */
313 pollsize = FD_ALLOC_BLOCK;
314 pollarray = calloc(pollsize, sizeof(struct pollfd));
318 destroy_client_list(void)
320 struct clnt_info *clp;
322 printerr(1, "processing client list\n");
324 while (clnt_list.tqh_first != NULL) {
325 clp = clnt_list.tqh_first;
326 TAILQ_REMOVE(&clnt_list, clp, list);
331 /* Used to read (and re-read) list of clients, set up poll array. */
333 update_client_list(void)
335 struct dirent **namelist;
338 destroy_client_list();
340 if (chdir(pipefsdir) < 0) {
341 printerr(0, "ERROR: can't chdir to %s: %s\n",
342 pipefsdir, strerror(errno));
346 memset(pollarray, 0, pollsize * sizeof(struct pollfd));
348 j = scandir(pipefsdir, &namelist, NULL, alphasort);
350 printerr(0, "ERROR: can't scandir %s: %s\n",
351 pipefsdir, strerror(errno));
354 for (i=0; i < j; i++) {
355 if (i < FD_ALLOC_BLOCK
356 && !strncmp(namelist[i]->d_name, "clnt", 4))
357 process_clnt_dir(namelist[i]->d_name);
366 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
367 gss_buffer_desc *context_token)
369 char *buf = NULL, *p = NULL, *end = NULL;
370 unsigned int timeout = 0; /* XXX decide on a reasonable value */
371 unsigned int buf_size = 0;
373 printerr(1, "doing downcall\n");
374 buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
375 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
376 sizeof(context_token->length) + context_token->length;
377 p = buf = malloc(buf_size);
378 end = buf + buf_size;
380 if (WRITE_BYTES(&p, end, uid)) goto out_err;
381 /* Not setting any timeout for now: */
382 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
383 if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
384 if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
385 if (write_buffer(&p, end, context_token)) goto out_err;
387 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
392 printerr(0, "Failed to write downcall!\n");
397 do_error_downcall(int k5_fd, uid_t uid, int err)
400 char *p = buf, *end = buf + 1024;
401 unsigned int timeout = 0;
404 printerr(1, "doing error downcall\n");
406 if (WRITE_BYTES(&p, end, uid)) goto out_err;
407 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
408 /* use seq_win = 0 to indicate an error: */
409 if (WRITE_BYTES(&p, end, zero)) goto out_err;
410 if (WRITE_BYTES(&p, end, err)) goto out_err;
412 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
415 printerr(0, "Failed to write error downcall!\n");
420 * Create an RPC connection and establish an authenticated
421 * gss context with a server.
423 int create_auth_rpc_client(struct clnt_info *clp,
428 CLIENT *rpc_clnt = NULL;
429 struct rpc_gss_sec sec;
435 char rpc_errmsg[1024];
436 int sockp = RPC_ANYSOCK;
437 int sendsz = 32768, recvsz = 32768;
438 struct addrinfo ai_hints, *a = NULL;
442 sec.qop = GSS_C_QOP_DEFAULT;
443 sec.svc = RPCSEC_GSS_SVC_NONE;
444 sec.cred = GSS_C_NO_CREDENTIAL;
446 if (authtype == AUTHTYPE_KRB5) {
447 sec.mech = (gss_OID)&krb5oid;
448 sec.req_flags = GSS_C_MUTUAL_FLAG;
450 else if (authtype == AUTHTYPE_SPKM3) {
451 sec.mech = (gss_OID)&spkm3oid;
452 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
453 * Need a way to switch....
455 sec.req_flags = GSS_C_MUTUAL_FLAG;
458 printerr(0, "ERROR: Invalid authentication type (%d) "
459 "in create_auth_rpc_client\n", authtype);
464 if (authtype == AUTHTYPE_KRB5) {
465 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
467 * Do this before creating rpc connection since we won't need
468 * rpc connection if it fails!
470 if (limit_krb5_enctypes(&sec, uid)) {
471 printerr(1, "WARNING: Failed while limiting krb5 "
472 "encryption types for user with uid %d\n",
479 /* Create the context as the user (not as root) */
480 save_uid = geteuid();
481 if (seteuid(uid) != 0) {
482 printerr(0, "WARNING: Failed to seteuid for "
483 "user with uid %d\n", uid);
486 printerr(2, "creating context using euid %d (save_uid %d)\n",
487 geteuid(), save_uid);
489 /* create an rpc connection to the nfs server */
491 printerr(2, "creating %s client for server %s\n", clp->protocol,
494 memset(&ai_hints, '\0', sizeof(ai_hints));
495 ai_hints.ai_family = PF_INET;
496 ai_hints.ai_flags |= AI_CANONNAME;
497 if ((strcmp(clp->protocol, "tcp")) == 0) {
498 ai_hints.ai_socktype = SOCK_STREAM;
499 ai_hints.ai_protocol = IPPROTO_TCP;
500 } else if ((strcmp(clp->protocol, "udp")) == 0) {
501 ai_hints.ai_socktype = SOCK_DGRAM;
502 ai_hints.ai_protocol = IPPROTO_UDP;
504 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
505 "for connection to server %s for user with uid %d",
506 clp->protocol, clp->servername, uid);
510 /* extract the service name from clp->servicename */
511 if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
512 printerr(0, "WARNING: servicename (%s) not formatted as "
513 "expected with service@host", clp->servicename);
516 if ((at_sign - clp->servicename) >= sizeof(service)) {
517 printerr(0, "WARNING: service portion of servicename (%s) "
518 "is too long!", clp->servicename);
521 strncpy(service, clp->servicename, at_sign - clp->servicename);
522 service[at_sign - clp->servicename] = '\0';
524 errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
526 printerr(0, "WARNING: Error from getaddrinfo for server "
527 "'%s': %s", clp->servername, gai_strerror(errcode));
532 printerr(0, "WARNING: No address information found for "
533 "connection to server %s for user with uid %d",
534 clp->servername, uid);
537 if (a->ai_protocol == IPPROTO_TCP) {
538 if ((rpc_clnt = clnttcp_create(
539 (struct sockaddr_in *) a->ai_addr,
540 clp->prog, clp->vers, &sockp,
541 sendsz, recvsz)) == NULL) {
542 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
543 "WARNING: can't create tcp rpc_clnt "
544 "for server %s for user with uid %d",
545 clp->servername, uid);
547 clnt_spcreateerror(rpc_errmsg));
550 } else if (a->ai_protocol == IPPROTO_UDP) {
551 const struct timeval timeout = {5, 0};
552 if ((rpc_clnt = clntudp_bufcreate(
553 (struct sockaddr_in *) a->ai_addr,
554 clp->prog, clp->vers, timeout,
555 &sockp, sendsz, recvsz)) == NULL) {
556 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
557 "WARNING: can't create udp rpc_clnt "
558 "for server %s for user with uid %d",
559 clp->servername, uid);
561 clnt_spcreateerror(rpc_errmsg));
565 /* Shouldn't happen! */
566 printerr(0, "ERROR: requested protocol '%s', but "
567 "got addrinfo with protocol %d",
568 clp->protocol, a->ai_protocol);
571 /* We're done with this */
575 printerr(2, "creating context with server %s\n", clp->servicename);
576 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
578 /* Our caller should print appropriate message */
579 printerr(2, "WARNING: Failed to create %s context for "
580 "user with uid %d for server %s\n",
581 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
582 uid, clp->servername);
586 /* Restore euid to original value */
587 if (seteuid(save_uid) != 0) {
588 printerr(0, "WARNING: Failed to restore euid"
589 " to uid %d\n", save_uid);
599 if ((save_uid != -1) && (seteuid(save_uid) != 0)) {
600 printerr(0, "WARNING: Failed to restore euid"
601 " to uid %d (in error path)\n", save_uid);
603 if (sec.cred != GSS_C_NO_CREDENTIAL)
604 gss_release_cred(&min_stat, &sec.cred);
605 if (rpc_clnt) clnt_destroy(rpc_clnt);
606 if (a != NULL) freeaddrinfo(a);
613 * this code uses the userland rpcsec gss library to create a krb5
614 * context on behalf of the kernel
617 handle_krb5_upcall(struct clnt_info *clp)
621 struct authgss_private_data pd;
622 gss_buffer_desc token;
623 char **credlist = NULL;
626 printerr(1, "handling krb5 upcall\n");
631 if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
632 printerr(0, "WARNING: failed reading uid from krb5 "
633 "upcall pipe: %s\n", strerror(errno));
641 * Get a list of credential cache names and try each
642 * of them until one works or we've tried them all
644 if (gssd_get_krb5_machine_cred_list(&credlist)) {
645 printerr(0, "WARNING: Failed to obtain machine "
646 "credentials for connection to "
647 "server %s\n", clp->servername);
648 goto out_return_error;
650 for (ccname = credlist; ccname && *ccname; ccname++) {
651 gssd_setup_krb5_machine_gss_ccache(*ccname);
652 if ((create_auth_rpc_client(clp, &auth, uid,
653 AUTHTYPE_KRB5)) == 0) {
658 printerr(2, "WARNING: Failed to create krb5 context "
659 "for user with uid %d with credentials "
660 "cache %s for server %s\n",
661 uid, *ccname, clp->servername);
663 gssd_free_krb5_machine_cred_list(credlist);
665 printerr(0, "WARNING: Failed to create krb5 context "
666 "for user with uid %d with any "
667 "credentials cache for server %s\n",
668 uid, clp->servername);
669 goto out_return_error;
673 /* Tell krb5 gss which credentials cache to use */
674 gssd_setup_krb5_user_gss_ccache(uid, clp->servername);
676 if (create_auth_rpc_client(clp, &auth, uid, AUTHTYPE_KRB5)) {
677 printerr(0, "WARNING: Failed to create krb5 context "
678 "for user with uid %d for server %s\n",
679 uid, clp->servername);
680 goto out_return_error;
684 if (!authgss_get_private_data(auth, &pd)) {
685 printerr(0, "WARNING: Failed to obtain authentication "
686 "data for user with uid %d for server %s\n",
687 uid, clp->servername);
688 goto out_return_error;
691 if (serialize_context_for_kernel(pd.pd_ctx, &token)) {
692 printerr(0, "WARNING: Failed to serialize krb5 context for "
693 "user with uid %d for server %s\n",
694 uid, clp->servername);
695 goto out_return_error;
698 do_downcall(clp->krb5_fd, uid, &pd, &token);
706 do_error_downcall(clp->krb5_fd, uid, -1);
711 * this code uses the userland rpcsec gss library to create an spkm3
712 * context on behalf of the kernel
715 handle_spkm3_upcall(struct clnt_info *clp)
719 struct authgss_private_data pd;
720 gss_buffer_desc token;
722 printerr(2, "handling spkm3 upcall\n");
727 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
728 printerr(0, "WARNING: failed reading uid from spkm3 "
729 "upcall pipe: %s\n", strerror(errno));
733 if (create_auth_rpc_client(clp, &auth, uid, AUTHTYPE_SPKM3)) {
734 printerr(0, "WARNING: Failed to create spkm3 context for "
735 "user with uid %d\n", uid);
736 goto out_return_error;
739 if (!authgss_get_private_data(auth, &pd)) {
740 printerr(0, "WARNING: Failed to obtain authentication "
741 "data for user with uid %d for server %s\n",
742 uid, clp->servername);
743 goto out_return_error;
746 if (serialize_context_for_kernel(pd.pd_ctx, &token)) {
747 printerr(0, "WARNING: Failed to serialize spkm3 context for "
748 "user with uid %d for server\n",
749 uid, clp->servername);
750 goto out_return_error;
753 do_downcall(clp->spkm3_fd, uid, &pd, &token);
761 do_error_downcall(clp->spkm3_fd, uid, -1);