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>
50 #include <sys/fsuid.h>
63 #include <gssapi/gssapi.h>
70 #include "krb5_util.h"
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.
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.
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
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.
98 struct pollfd * pollarray;
100 int pollsize; /* the size of pollaray (in pollfd's) */
102 /* XXX buffer problems: */
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];
110 static char service[128];
111 static char address[128];
117 struct hostent *ent = NULL;
120 *servicename = *servername = *protocol = NULL;
122 if ((fd = open(info_file_name, O_RDONLY)) == -1) {
123 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
127 if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
131 numfields = sscanf(buf,"RPC server: %127s\n"
132 "service: %127s %15s version %15s\n"
136 service, program, version,
140 if (numfields == 5) {
141 strcpy(protoname, "tcp");
142 } else if (numfields != 6) {
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)))
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);
159 if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
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)))
165 memcpy(*servicename, buf, strlen(buf));
167 if (!(*protocol = strdup(protoname)))
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);
180 destroy_client(struct clnt_info *clp)
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);
198 static struct clnt_info *
199 insert_new_clnt(void)
201 struct clnt_info *clp = NULL;
203 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
204 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
208 clp->krb5_poll_index = -1;
209 clp->spkm3_poll_index = -1;
214 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
220 process_clnt_dir_files(struct clnt_info * clp)
224 char info_file_name[32];
226 if (clp->krb5_fd == -1) {
227 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
228 clp->krb5_fd = open(kname, O_RDWR);
230 if (clp->spkm3_fd == -1) {
231 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
232 clp->spkm3_fd = open(sname, O_RDWR);
234 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
236 snprintf(info_file_name, sizeof(info_file_name), "%s/info",
238 if ((clp->servicename == NULL) &&
239 read_service_info(info_file_name, &clp->servicename,
240 &clp->servername, &clp->prog, &clp->vers,
247 get_poll_index(int *ind)
252 for (i=0; i<FD_ALLOC_BLOCK; i++) {
253 if (pollarray[i].events == 0) {
259 printerr(0, "ERROR: No pollarray slots open\n");
267 insert_clnt_poll(struct clnt_info *clp)
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");
274 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
275 pollarray[clp->krb5_poll_index].events |= POLLIN;
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");
283 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
284 pollarray[clp->spkm3_poll_index].events |= POLLIN;
291 process_clnt_dir(char *dir)
293 struct clnt_info * clp;
295 if (!(clp = insert_new_clnt()))
296 goto fail_destroy_client;
298 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
299 goto fail_destroy_client;
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;
307 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
308 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
310 if (process_clnt_dir_files(clp))
311 goto fail_keep_client;
313 if (insert_clnt_poll(clp))
314 goto fail_destroy_client;
320 TAILQ_REMOVE(&clnt_list, clp, list);
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. */
331 init_client_list(void)
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));
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.
345 update_old_clients(struct dirent **namelist, int size)
347 struct clnt_info *clp;
351 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
353 for (i=0; i < size; i++) {
354 if (!strcmp(clp->dirname, namelist[i]->d_name)) {
360 printerr(2, "destroying client %s\n", clp->dirname);
361 saveprev = clp->list.tqe_prev;
362 TAILQ_REMOVE(&clnt_list, clp, list);
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);
373 /* Search for a client by directory name, return 1 if found, 0 otherwise */
375 find_client(char *dirname)
377 struct clnt_info *clp;
379 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
380 if (!strcmp(clp->dirname, dirname))
385 /* Used to read (and re-read) list of clients, set up poll array. */
387 update_client_list(void)
389 struct dirent **namelist;
392 if (chdir(pipefs_nfsdir) < 0) {
393 printerr(0, "ERROR: can't chdir to %s: %s\n",
394 pipefs_nfsdir, strerror(errno));
398 j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
400 printerr(0, "ERROR: can't scandir %s: %s\n",
401 pipefs_nfsdir, strerror(errno));
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);
418 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
419 gss_buffer_desc *context_token)
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;
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;
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;
439 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
444 printerr(0, "Failed to write downcall!\n");
449 do_error_downcall(int k5_fd, uid_t uid, int err)
452 char *p = buf, *end = buf + 1024;
453 unsigned int timeout = 0;
456 printerr(1, "doing error downcall\n");
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;
464 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
467 printerr(0, "Failed to write error downcall!\n");
472 * Create an RPC connection and establish an authenticated
473 * gss context with a server.
475 int create_auth_rpc_client(struct clnt_info *clp,
476 CLIENT **clnt_return,
481 CLIENT *rpc_clnt = NULL;
482 struct rpc_gss_sec sec;
488 char rpc_errmsg[1024];
489 int sockp = RPC_ANYSOCK;
490 int sendsz = 32768, recvsz = 32768;
491 struct addrinfo ai_hints, *a = NULL;
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);
502 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
505 sec.qop = GSS_C_QOP_DEFAULT;
506 sec.svc = RPCSEC_GSS_SVC_NONE;
507 sec.cred = GSS_C_NO_CREDENTIAL;
509 if (authtype == AUTHTYPE_KRB5) {
510 sec.mech = (gss_OID)&krb5oid;
511 sec.req_flags = GSS_C_MUTUAL_FLAG;
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....
518 sec.req_flags = GSS_C_MUTUAL_FLAG;
521 printerr(0, "ERROR: Invalid authentication type (%d) "
522 "in create_auth_rpc_client\n", authtype);
527 if (authtype == AUTHTYPE_KRB5) {
528 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
530 * Do this before creating rpc connection since we won't need
531 * rpc connection if it fails!
533 if (limit_krb5_enctypes(&sec, uid)) {
534 printerr(1, "WARNING: Failed while limiting krb5 "
535 "encryption types for user with uid %d\n",
542 /* create an rpc connection to the nfs server */
544 printerr(2, "creating %s client for server %s\n", clp->protocol,
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;
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);
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);
569 if ((at_sign - clp->servicename) >= sizeof(service)) {
570 printerr(0, "WARNING: service portion of servicename (%s) "
571 "is too long!\n", clp->servicename);
574 strncpy(service, clp->servicename, at_sign - clp->servicename);
575 service[at_sign - clp->servicename] = '\0';
577 errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
579 printerr(0, "WARNING: Error from getaddrinfo for server "
580 "'%s': %s\n", clp->servername, gai_strerror(errcode));
585 printerr(0, "WARNING: No address information found for "
586 "connection to server %s for user with uid %d\n",
587 clp->servername, uid);
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);
600 clnt_spcreateerror(rpc_errmsg));
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);
614 clnt_spcreateerror(rpc_errmsg));
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);
624 /* We're done with this */
628 printerr(2, "creating context with server %s\n", clp->servicename);
629 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
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);
640 rpc_clnt->cl_auth = auth;
641 *clnt_return = rpc_clnt;
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);
657 /* Only destroy here if failure. Otherwise, caller is responsible */
658 if (rpc_clnt) clnt_destroy(rpc_clnt);
665 * this code uses the userland rpcsec gss library to create a krb5
666 * context on behalf of the kernel
669 handle_krb5_upcall(struct clnt_info *clp)
672 CLIENT *rpc_clnt = NULL;
674 struct authgss_private_data pd;
675 gss_buffer_desc token;
676 char **credlist = NULL;
678 int create_resp = -1;
680 printerr(1, "handling krb5 upcall\n");
684 memset(&pd, 0, sizeof(struct authgss_private_data));
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));
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);
696 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
699 if (create_resp != 0) {
700 if (uid == 0 && root_uses_machine_creds == 1) {
703 gssd_refresh_krb5_machine_credential(clp->servername,
706 * Get a list of credential cache names and try each
707 * of them until one works or we've tried them all
709 if (gssd_get_krb5_machine_cred_list(&credlist)) {
710 printerr(0, "ERROR: No credentials found "
711 "for connection to server %s\n",
713 goto out_return_error;
715 for (ccname = credlist; ccname && *ccname; ccname++) {
716 gssd_setup_krb5_machine_gss_ccache(*ccname);
717 if ((create_auth_rpc_client(clp, &rpc_clnt,
719 AUTHTYPE_KRB5)) == 0) {
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);
729 gssd_free_krb5_machine_cred_list(credlist);
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;
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;
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;
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;
759 do_downcall(clp->krb5_fd, uid, &pd, &token);
764 if (pd.pd_ctx_hndl.length != 0)
765 authgss_free_private_data(&pd);
769 clnt_destroy(rpc_clnt);
773 do_error_downcall(clp->krb5_fd, uid, -1);
778 * this code uses the userland rpcsec gss library to create an spkm3
779 * context on behalf of the kernel
782 handle_spkm3_upcall(struct clnt_info *clp)
785 CLIENT *rpc_clnt = NULL;
787 struct authgss_private_data pd;
788 gss_buffer_desc token;
790 printerr(2, "handling spkm3 upcall\n");
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));
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;
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;
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;
821 do_downcall(clp->spkm3_fd, uid, &pd, &token);
829 clnt_destroy(rpc_clnt);
833 do_error_downcall(clp->spkm3_fd, uid, -1);