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.
43 #endif /* HAVE_CONFIG_H */
49 #include <sys/param.h>
52 #include <sys/socket.h>
53 #include <arpa/inet.h>
54 #include <sys/fsuid.h>
67 #include <gssapi/gssapi.h>
73 #include "krb5_util.h"
80 * array of struct pollfd suitable to pass to poll. initialized to
81 * zero - a zero struct is ignored by poll() because the events mask is 0.
84 * linked list of struct clnt_info which associates a clntXXX directory
85 * with an index into pollarray[], and other basic data about that client.
87 * Directory structure: created by the kernel
88 * {rpc_pipefs}/{dir}/clntXX : one per rpc_clnt struct in the kernel
89 * {rpc_pipefs}/{dir}/clntXX/krb5 : read uid for which kernel wants
90 * a context, write the resulting context
91 * {rpc_pipefs}/{dir}/clntXX/info : stores info such as server name
92 * {rpc_pipefs}/{dir}/clntXX/gssd : pipe for all gss mechanisms using
93 * a text-based string of parameters
96 * Poll all {rpc_pipefs}/{dir}/clntXX/YYYY files. When data is ready,
97 * read and process; performs rpcsec_gss context initialization protocol to
98 * get a cred for that user. Writes result to corresponding krb5 file
99 * in a form the kernel code will understand.
100 * In addition, we make sure we are notified whenever anything is
101 * created or destroyed in {rpc_pipefs} or in any of the clntXX directories,
102 * and rescan the whole {rpc_pipefs} when this happens.
105 struct pollfd * pollarray;
107 int pollsize; /* the size of pollaray (in pollfd's) */
110 * convert a presentation address string to a sockaddr_storage struct. Returns
111 * true on success and false on failure.
113 * Note that we do not populate the sin6_scope_id field here for IPv6 addrs.
114 * gssd nececessarily relies on hostname resolution and DNS AAAA records
115 * do not generally contain scope-id's. This means that GSSAPI auth really
116 * can't work with IPv6 link-local addresses.
118 * We *could* consider changing this if we did something like adopt the
119 * Microsoft "standard" of using the ipv6-literal.net domainname, but it's
120 * not really feasible at present.
123 addrstr_to_sockaddr(struct sockaddr *sa, const char *addr, const int port)
125 struct sockaddr_in *s4 = (struct sockaddr_in *) sa;
126 #ifdef IPV6_SUPPORTED
127 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
128 #endif /* IPV6_SUPPORTED */
130 if (inet_pton(AF_INET, addr, &s4->sin_addr)) {
131 s4->sin_family = AF_INET;
132 s4->sin_port = htons(port);
133 #ifdef IPV6_SUPPORTED
134 } else if (inet_pton(AF_INET6, addr, &s6->sin6_addr)) {
135 s6->sin6_family = AF_INET6;
136 s6->sin6_port = htons(port);
137 #endif /* IPV6_SUPPORTED */
139 printerr(0, "ERROR: unable to convert %s to address\n", addr);
147 * convert a sockaddr to a hostname
150 sockaddr_to_hostname(const struct sockaddr *sa, const char *addr)
155 char hbuf[NI_MAXHOST];
157 switch (sa->sa_family) {
159 addrlen = sizeof(struct sockaddr_in);
161 #ifdef IPV6_SUPPORTED
163 addrlen = sizeof(struct sockaddr_in6);
165 #endif /* IPV6_SUPPORTED */
167 printerr(0, "ERROR: unrecognized addr family %d\n",
172 err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0,
175 printerr(0, "ERROR: unable to resolve %s to hostname: %s\n",
176 addr, err == EAI_SYSTEM ? strerror(err) :
181 hostname = strdup(hbuf);
186 /* XXX buffer problems: */
188 read_service_info(char *info_file_name, char **servicename, char **servername,
189 int *prog, int *vers, char **protocol,
190 struct sockaddr *addr) {
191 #define INFOBUFLEN 256
192 char buf[INFOBUFLEN + 1];
193 static char dummy[128];
195 static char service[128];
196 static char address[128];
206 *servicename = *servername = *protocol = NULL;
208 if ((fd = open(info_file_name, O_RDONLY)) == -1) {
209 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
213 if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
218 numfields = sscanf(buf,"RPC server: %127s\n"
219 "service: %127s %15s version %15s\n"
223 service, program, version,
227 if (numfields == 5) {
228 strcpy(protoname, "tcp");
229 } else if (numfields != 6) {
234 if ((p = strstr(buf, "port")) != NULL)
235 sscanf(p, "port: %127s\n", cb_port);
237 /* check service, program, and version */
238 if (memcmp(service, "nfs", 3) != 0)
240 *prog = atoi(program + 1); /* skip open paren */
241 *vers = atoi(version);
243 if (strlen(service) == 3 ) {
244 if ((*prog != 100003) || ((*vers != 2) && (*vers != 3) &&
247 } else if (memcmp(service, "nfs4_cb", 7) == 0) {
252 if (cb_port[0] != '\0') {
253 port = atoi(cb_port);
254 if (port < 0 || port > 65535)
258 if (!addrstr_to_sockaddr(addr, address, port))
261 *servername = sockaddr_to_hostname(addr, address);
262 if (*servername == NULL)
265 nbytes = snprintf(buf, INFOBUFLEN, "%s@%s", service, *servername);
266 if (nbytes > INFOBUFLEN)
269 if (!(*servicename = calloc(strlen(buf) + 1, 1)))
271 memcpy(*servicename, buf, strlen(buf));
273 if (!(*protocol = strdup(protoname)))
277 printerr(0, "ERROR: failed to read service info\n");
278 if (fd != -1) close(fd);
282 *servicename = *servername = *protocol = NULL;
287 destroy_client(struct clnt_info *clp)
289 if (clp->krb5_poll_index != -1)
290 memset(&pollarray[clp->krb5_poll_index], 0,
291 sizeof(struct pollfd));
292 if (clp->spkm3_poll_index != -1)
293 memset(&pollarray[clp->spkm3_poll_index], 0,
294 sizeof(struct pollfd));
295 if (clp->gssd_poll_index != -1)
296 memset(&pollarray[clp->gssd_poll_index], 0,
297 sizeof(struct pollfd));
298 if (clp->dir_fd != -1) close(clp->dir_fd);
299 if (clp->krb5_fd != -1) close(clp->krb5_fd);
300 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
301 if (clp->gssd_fd != -1) close(clp->gssd_fd);
303 free(clp->servicename);
304 free(clp->servername);
309 static struct clnt_info *
310 insert_new_clnt(void)
312 struct clnt_info *clp = NULL;
314 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
315 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
319 clp->krb5_poll_index = -1;
320 clp->spkm3_poll_index = -1;
321 clp->gssd_poll_index = -1;
327 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
333 process_clnt_dir_files(struct clnt_info * clp)
336 char gname[PATH_MAX];
337 char info_file_name[PATH_MAX];
339 if (clp->gssd_fd == -1) {
340 snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname);
341 clp->gssd_fd = open(gname, O_RDWR);
343 if (clp->gssd_fd == -1) {
344 if (clp->krb5_fd == -1) {
345 snprintf(name, sizeof(name), "%s/krb5", clp->dirname);
346 clp->krb5_fd = open(name, O_RDWR);
348 if (clp->spkm3_fd == -1) {
349 snprintf(name, sizeof(name), "%s/spkm3", clp->dirname);
350 clp->spkm3_fd = open(name, O_RDWR);
353 /* If we opened a gss-specific pipe, let's try opening
354 * the new upcall pipe again. If we succeed, close
355 * gss-specific pipe(s).
357 if (clp->krb5_fd != -1 || clp->spkm3_fd != -1) {
358 clp->gssd_fd = open(gname, O_RDWR);
359 if (clp->gssd_fd != -1) {
360 if (clp->krb5_fd != -1)
363 if (clp->spkm3_fd != -1)
364 close(clp->spkm3_fd);
370 if ((clp->krb5_fd == -1) && (clp->spkm3_fd == -1) &&
371 (clp->gssd_fd == -1))
373 snprintf(info_file_name, sizeof(info_file_name), "%s/info",
375 if ((clp->servicename == NULL) &&
376 read_service_info(info_file_name, &clp->servicename,
377 &clp->servername, &clp->prog, &clp->vers,
378 &clp->protocol, (struct sockaddr *) &clp->addr))
384 get_poll_index(int *ind)
389 for (i=0; i<FD_ALLOC_BLOCK; i++) {
390 if (pollarray[i].events == 0) {
396 printerr(0, "ERROR: No pollarray slots open\n");
404 insert_clnt_poll(struct clnt_info *clp)
406 if ((clp->gssd_fd != -1) && (clp->gssd_poll_index == -1)) {
407 if (get_poll_index(&clp->gssd_poll_index)) {
408 printerr(0, "ERROR: Too many gssd clients\n");
411 pollarray[clp->gssd_poll_index].fd = clp->gssd_fd;
412 pollarray[clp->gssd_poll_index].events |= POLLIN;
415 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
416 if (get_poll_index(&clp->krb5_poll_index)) {
417 printerr(0, "ERROR: Too many krb5 clients\n");
420 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
421 pollarray[clp->krb5_poll_index].events |= POLLIN;
424 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
425 if (get_poll_index(&clp->spkm3_poll_index)) {
426 printerr(0, "ERROR: Too many spkm3 clients\n");
429 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
430 pollarray[clp->spkm3_poll_index].events |= POLLIN;
437 process_clnt_dir(char *dir, char *pdir)
439 struct clnt_info * clp;
441 if (!(clp = insert_new_clnt()))
442 goto fail_destroy_client;
444 /* An extra for the '/', and an extra for the null */
445 if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) {
446 goto fail_destroy_client;
448 sprintf(clp->dirname, "%s/%s", pdir, dir);
449 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
450 printerr(0, "ERROR: can't open %s: %s\n",
451 clp->dirname, strerror(errno));
452 goto fail_destroy_client;
454 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
455 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
457 if (process_clnt_dir_files(clp))
458 goto fail_keep_client;
460 if (insert_clnt_poll(clp))
461 goto fail_destroy_client;
467 TAILQ_REMOVE(&clnt_list, clp, list);
471 /* We couldn't find some subdirectories, but we keep the client
472 * around in case we get a notification on the directory when the
473 * subdirectories are created. */
478 init_client_list(void)
480 TAILQ_INIT(&clnt_list);
481 /* Eventually plan to grow/shrink poll array: */
482 pollsize = FD_ALLOC_BLOCK;
483 pollarray = calloc(pollsize, sizeof(struct pollfd));
487 * This is run after a DNOTIFY signal, and should clear up any
488 * directories that are no longer around, and re-scan any existing
489 * directories, since the DNOTIFY could have been in there.
492 update_old_clients(struct dirent **namelist, int size, char *pdir)
494 struct clnt_info *clp;
497 char fname[PATH_MAX];
499 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
500 /* only compare entries in the global list that are from the
501 * same pipefs parent directory as "pdir"
503 if (strncmp(clp->dirname, pdir, strlen(pdir)) != 0) continue;
506 for (i=0; i < size; i++) {
507 snprintf(fname, sizeof(fname), "%s/%s",
508 pdir, namelist[i]->d_name);
509 if (strcmp(clp->dirname, fname) == 0) {
515 printerr(2, "destroying client %s\n", clp->dirname);
516 saveprev = clp->list.tqe_prev;
517 TAILQ_REMOVE(&clnt_list, clp, list);
522 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
523 if (!process_clnt_dir_files(clp))
524 insert_clnt_poll(clp);
528 /* Search for a client by directory name, return 1 if found, 0 otherwise */
530 find_client(char *dirname, char *pdir)
532 struct clnt_info *clp;
533 char fname[PATH_MAX];
535 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
536 snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname);
537 if (strcmp(clp->dirname, fname) == 0)
544 process_pipedir(char *pipe_name)
546 struct dirent **namelist;
549 if (chdir(pipe_name) < 0) {
550 printerr(0, "ERROR: can't chdir to %s: %s\n",
551 pipe_name, strerror(errno));
555 j = scandir(pipe_name, &namelist, NULL, alphasort);
557 printerr(0, "ERROR: can't scandir %s: %s\n",
558 pipe_name, strerror(errno));
562 update_old_clients(namelist, j, pipe_name);
563 for (i=0; i < j; i++) {
564 if (i < FD_ALLOC_BLOCK
565 && !strncmp(namelist[i]->d_name, "clnt", 4)
566 && !find_client(namelist[i]->d_name, pipe_name))
567 process_clnt_dir(namelist[i]->d_name, pipe_name);
576 /* Used to read (and re-read) list of clients, set up poll array. */
578 update_client_list(void)
581 struct topdirs_info *tdi;
583 TAILQ_FOREACH(tdi, &topdirs_list, list) {
584 retval = process_pipedir(tdi->dirname);
586 printerr(1, "WARNING: error processing %s\n",
594 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
595 gss_buffer_desc *context_token)
597 char *buf = NULL, *p = NULL, *end = NULL;
598 unsigned int timeout = context_timeout;
599 unsigned int buf_size = 0;
601 printerr(1, "doing downcall\n");
602 buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
603 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
604 sizeof(context_token->length) + context_token->length;
605 p = buf = malloc(buf_size);
606 end = buf + buf_size;
608 if (WRITE_BYTES(&p, end, uid)) goto out_err;
609 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
610 if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
611 if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
612 if (write_buffer(&p, end, context_token)) goto out_err;
614 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
619 printerr(1, "Failed to write downcall!\n");
624 do_error_downcall(int k5_fd, uid_t uid, int err)
627 char *p = buf, *end = buf + 1024;
628 unsigned int timeout = 0;
631 printerr(1, "doing error downcall\n");
633 if (WRITE_BYTES(&p, end, uid)) goto out_err;
634 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
635 /* use seq_win = 0 to indicate an error: */
636 if (WRITE_BYTES(&p, end, zero)) goto out_err;
637 if (WRITE_BYTES(&p, end, err)) goto out_err;
639 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
642 printerr(1, "Failed to write error downcall!\n");
647 * If the port isn't already set, do an rpcbind query to the remote server
648 * using the program and version and get the port.
650 * Newer kernels send the value of the port= mount option in the "info"
651 * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
652 * of the port= option or '2049'. The port field in a new sockaddr should
653 * reflect the value that was sent by the kernel.
656 populate_port(struct sockaddr *sa, const socklen_t salen,
657 const rpcprog_t program, const rpcvers_t version,
658 const unsigned short protocol)
660 struct sockaddr_in *s4 = (struct sockaddr_in *) sa;
661 #ifdef IPV6_SUPPORTED
662 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
663 #endif /* IPV6_SUPPORTED */
667 * Newer kernels send the port in the upcall. If we already have
668 * the port, there's no need to look it up.
670 switch (sa->sa_family) {
672 if (s4->sin_port != 0) {
673 printerr(2, "DEBUG: port already set to %d\n",
674 ntohs(s4->sin_port));
678 #ifdef IPV6_SUPPORTED
680 if (s6->sin6_port != 0) {
681 printerr(2, "DEBUG: port already set to %d\n",
682 ntohs(s6->sin6_port));
686 #endif /* IPV6_SUPPORTED */
688 printerr(0, "ERROR: unsupported address family %d\n",
694 * Newer kernels that send the port in the upcall set the value to
695 * 2049 for NFSv4 mounts when one isn't specified. The check below is
696 * only for kernels that don't send the port in the upcall. For those
697 * we either have to do an rpcbind query or set it to the standard
698 * port. Doing a query could be problematic (firewalls, etc), so take
699 * the latter approach.
701 if (program == 100003 && version == 4) {
706 port = nfs_getport(sa, salen, program, version, protocol);
708 printerr(0, "ERROR: unable to obtain port for prog %ld "
709 "vers %ld\n", program, version);
714 printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port,
717 switch (sa->sa_family) {
719 s4->sin_port = htons(port);
721 #ifdef IPV6_SUPPORTED
723 s6->sin6_port = htons(port);
725 #endif /* IPV6_SUPPORTED */
732 * Create an RPC connection and establish an authenticated
733 * gss context with a server.
735 int create_auth_rpc_client(struct clnt_info *clp,
736 CLIENT **clnt_return,
741 CLIENT *rpc_clnt = NULL;
742 struct rpc_gss_sec sec;
747 char rpc_errmsg[1024];
749 struct timeval timeout = {5, 0};
750 struct sockaddr *addr = (struct sockaddr *) &clp->addr;
753 /* Create the context as the user (not as root) */
754 save_uid = geteuid();
755 if (setfsuid(uid) != 0) {
756 printerr(0, "WARNING: Failed to setfsuid for "
757 "user with uid %d\n", uid);
760 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
763 sec.qop = GSS_C_QOP_DEFAULT;
764 sec.svc = RPCSEC_GSS_SVC_NONE;
765 sec.cred = GSS_C_NO_CREDENTIAL;
767 if (authtype == AUTHTYPE_KRB5) {
768 sec.mech = (gss_OID)&krb5oid;
769 sec.req_flags = GSS_C_MUTUAL_FLAG;
771 else if (authtype == AUTHTYPE_SPKM3) {
772 sec.mech = (gss_OID)&spkm3oid;
773 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
774 * Need a way to switch....
776 sec.req_flags = GSS_C_MUTUAL_FLAG;
779 printerr(0, "ERROR: Invalid authentication type (%d) "
780 "in create_auth_rpc_client\n", authtype);
785 if (authtype == AUTHTYPE_KRB5) {
786 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
788 * Do this before creating rpc connection since we won't need
789 * rpc connection if it fails!
791 if (limit_krb5_enctypes(&sec, uid)) {
792 printerr(1, "WARNING: Failed while limiting krb5 "
793 "encryption types for user with uid %d\n",
800 /* create an rpc connection to the nfs server */
802 printerr(2, "creating %s client for server %s\n", clp->protocol,
805 if ((strcmp(clp->protocol, "tcp")) == 0) {
806 protocol = IPPROTO_TCP;
807 } else if ((strcmp(clp->protocol, "udp")) == 0) {
808 protocol = IPPROTO_UDP;
810 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
811 "for connection to server %s for user with uid %d\n",
812 clp->protocol, clp->servername, uid);
816 switch (addr->sa_family) {
818 salen = sizeof(struct sockaddr_in);
820 #ifdef IPV6_SUPPORTED
822 salen = sizeof(struct sockaddr_in6);
824 #endif /* IPV6_SUPPORTED */
826 printerr(1, "ERROR: Unknown address family %d\n",
831 if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
834 rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
835 clp->vers, &timeout);
837 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
838 "WARNING: can't create %s rpc_clnt to server %s for "
840 protocol == IPPROTO_TCP ? "tcp" : "udp",
841 clp->servername, uid);
843 clnt_spcreateerror(rpc_errmsg));
847 printerr(2, "creating context with server %s\n", clp->servicename);
848 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
850 /* Our caller should print appropriate message */
851 printerr(2, "WARNING: Failed to create %s context for "
852 "user with uid %d for server %s\n",
853 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
854 uid, clp->servername);
859 rpc_clnt->cl_auth = auth;
860 *clnt_return = rpc_clnt;
865 if (sec.cred != GSS_C_NO_CREDENTIAL)
866 gss_release_cred(&min_stat, &sec.cred);
867 /* Restore euid to original value */
868 if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
869 printerr(0, "WARNING: Failed to restore fsuid"
870 " to uid %d from %d\n", save_uid, uid);
875 /* Only destroy here if failure. Otherwise, caller is responsible */
876 if (rpc_clnt) clnt_destroy(rpc_clnt);
882 * this code uses the userland rpcsec gss library to create a krb5
883 * context on behalf of the kernel
886 process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
889 CLIENT *rpc_clnt = NULL;
891 struct authgss_private_data pd;
892 gss_buffer_desc token;
893 char **credlist = NULL;
896 int create_resp = -1;
898 printerr(1, "handling krb5 upcall (%s)\n", clp->dirname);
901 if (clp->servicename) {
902 free(clp->servicename);
903 clp->servicename = strdup(tgtname);
908 memset(&pd, 0, sizeof(struct authgss_private_data));
911 * If "service" is specified, then the kernel is indicating that
912 * we must use machine credentials for this request. (Regardless
913 * of the uid value or the setting of root_uses_machine_creds.)
914 * If the service value is "*", then any service name can be used.
915 * Otherwise, it specifies the service name that should be used.
916 * (For now, the values of service will only be "*" or "nfs".)
918 * Restricting gssd to use "nfs" service name is needed for when
919 * the NFS server is doing a callback to the NFS client. In this
920 * case, the NFS server has to authenticate itself as "nfs" --
921 * even if there are other service keys such as "host" or "root"
924 * Another case when the kernel may specify the service attribute
925 * is when gssd is being asked to create the context for a
926 * SETCLIENT_ID operation. In this case, machine credentials
927 * must be used for the authentication. However, the service name
928 * used for this case is not important.
931 printerr(2, "%s: service is '%s'\n", __func__,
932 service ? service : "<null>");
933 if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
935 /* Tell krb5 gss which credentials cache to use */
936 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
937 if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
938 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
940 if (create_resp == 0)
944 if (create_resp != 0) {
945 if (uid == 0 && (root_uses_machine_creds == 1 ||
950 gssd_refresh_krb5_machine_credential(clp->servername,
953 * Get a list of credential cache names and try each
954 * of them until one works or we've tried them all
956 if (gssd_get_krb5_machine_cred_list(&credlist)) {
957 printerr(0, "ERROR: No credentials found "
958 "for connection to server %s\n",
960 goto out_return_error;
962 for (ccname = credlist; ccname && *ccname; ccname++) {
963 gssd_setup_krb5_machine_gss_ccache(*ccname);
964 if ((create_auth_rpc_client(clp, &rpc_clnt,
966 AUTHTYPE_KRB5)) == 0) {
971 printerr(2, "WARNING: Failed to create machine krb5 context "
972 "with credentials cache %s for server %s\n",
973 *ccname, clp->servername);
975 gssd_free_krb5_machine_cred_list(credlist);
979 printerr(2, "WARNING: Machine cache is prematurely expired or corrupted "
980 "trying to recreate cache for server %s\n", clp->servername);
982 printerr(1, "WARNING: Failed to create machine krb5 context "
983 "with any credentials cache for server %s\n",
985 goto out_return_error;
990 printerr(1, "WARNING: Failed to create krb5 context "
991 "for user with uid %d for server %s\n",
992 uid, clp->servername);
993 goto out_return_error;
997 if (!authgss_get_private_data(auth, &pd)) {
998 printerr(1, "WARNING: Failed to obtain authentication "
999 "data for user with uid %d for server %s\n",
1000 uid, clp->servername);
1001 goto out_return_error;
1004 if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
1005 printerr(0, "WARNING: Failed to serialize krb5 context for "
1006 "user with uid %d for server %s\n",
1007 uid, clp->servername);
1008 goto out_return_error;
1011 do_downcall(fd, uid, &pd, &token);
1016 #ifndef HAVE_LIBTIRPC
1017 if (pd.pd_ctx_hndl.length != 0)
1018 authgss_free_private_data(&pd);
1023 clnt_destroy(rpc_clnt);
1027 do_error_downcall(fd, uid, -1);
1032 * this code uses the userland rpcsec gss library to create an spkm3
1033 * context on behalf of the kernel
1036 process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd)
1038 CLIENT *rpc_clnt = NULL;
1040 struct authgss_private_data pd;
1041 gss_buffer_desc token;
1043 printerr(2, "handling spkm3 upcall (%s)\n", clp->dirname);
1048 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
1049 printerr(0, "WARNING: Failed to create spkm3 context for "
1050 "user with uid %d\n", uid);
1051 goto out_return_error;
1054 if (!authgss_get_private_data(auth, &pd)) {
1055 printerr(0, "WARNING: Failed to obtain authentication "
1056 "data for user with uid %d for server %s\n",
1057 uid, clp->servername);
1058 goto out_return_error;
1061 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) {
1062 printerr(0, "WARNING: Failed to serialize spkm3 context for "
1063 "user with uid %d for server\n",
1064 uid, clp->servername);
1065 goto out_return_error;
1068 do_downcall(fd, uid, &pd, &token);
1076 clnt_destroy(rpc_clnt);
1080 do_error_downcall(fd, uid, -1);
1085 handle_krb5_upcall(struct clnt_info *clp)
1089 if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1090 printerr(0, "WARNING: failed reading uid from krb5 "
1091 "upcall pipe: %s\n", strerror(errno));
1095 return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL);
1099 handle_spkm3_upcall(struct clnt_info *clp)
1103 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1104 printerr(0, "WARNING: failed reading uid from spkm3 "
1105 "upcall pipe: %s\n", strerror(errno));
1109 return process_spkm3_upcall(clp, uid, clp->spkm3_fd);
1113 handle_gssd_upcall(struct clnt_info *clp)
1120 char *target = NULL;
1121 char *service = NULL;
1123 printerr(1, "handling gssd upcall (%s)\n", clp->dirname);
1125 if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) {
1126 printerr(0, "WARNING: handle_gssd_upcall: "
1127 "failed reading request\n");
1130 printerr(2, "%s: '%s'\n", __func__, lbuf);
1132 /* find the mechanism name */
1133 if ((p = strstr(lbuf, "mech=")) != NULL) {
1134 mech = malloc(lbuflen);
1137 if (sscanf(p, "mech=%s", mech) != 1) {
1138 printerr(0, "WARNING: handle_gssd_upcall: "
1139 "failed to parse gss mechanism name "
1140 "in upcall string '%s'\n", lbuf);
1144 printerr(0, "WARNING: handle_gssd_upcall: "
1145 "failed to find gss mechanism name "
1146 "in upcall string '%s'\n", lbuf);
1151 if ((p = strstr(lbuf, "uid=")) != NULL) {
1152 if (sscanf(p, "uid=%d", &uid) != 1) {
1153 printerr(0, "WARNING: handle_gssd_upcall: "
1154 "failed to parse uid "
1155 "in upcall string '%s'\n", lbuf);
1159 printerr(0, "WARNING: handle_gssd_upcall: "
1160 "failed to find uid "
1161 "in upcall string '%s'\n", lbuf);
1165 /* read target name */
1166 if ((p = strstr(lbuf, "target=")) != NULL) {
1167 target = malloc(lbuflen);
1170 if (sscanf(p, "target=%s", target) != 1) {
1171 printerr(0, "WARNING: handle_gssd_upcall: "
1172 "failed to parse target name "
1173 "in upcall string '%s'\n", lbuf);
1179 * read the service name
1181 * The presence of attribute "service=" indicates that machine
1182 * credentials should be used for this request. If the value
1183 * is "*", then any machine credentials available can be used.
1184 * If the value is anything else, then machine credentials for
1185 * the specified service name (always "nfs" for now) should be
1188 if ((p = strstr(lbuf, "service=")) != NULL) {
1189 service = malloc(lbuflen);
1192 if (sscanf(p, "service=%s", service) != 1) {
1193 printerr(0, "WARNING: handle_gssd_upcall: "
1194 "failed to parse service type "
1195 "in upcall string '%s'\n", lbuf);
1200 if (strcmp(mech, "krb5") == 0)
1201 process_krb5_upcall(clp, uid, clp->gssd_fd, target, service);
1202 else if (strcmp(mech, "spkm3") == 0)
1203 process_spkm3_upcall(clp, uid, clp->gssd_fd);
1205 printerr(0, "WARNING: handle_gssd_upcall: "
1206 "received unknown gss mech '%s'\n", mech);