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"
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.
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
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) */
107 * convert a presentation address string to a sockaddr_storage struct. Returns
108 * true on success and false on failure.
111 addrstr_to_sockaddr(struct sockaddr *sa, const char *addr, const int port)
113 struct sockaddr_in *s4 = (struct sockaddr_in *) sa;
115 if (inet_pton(AF_INET, addr, &s4->sin_addr)) {
116 s4->sin_family = AF_INET;
117 s4->sin_port = htons(port);
119 printerr(0, "ERROR: unable to convert %s to address\n", addr);
127 * convert a sockaddr to a hostname
130 sockaddr_to_hostname(const struct sockaddr *sa, const char *addr)
135 char hbuf[NI_MAXHOST];
137 switch (sa->sa_family) {
139 addrlen = sizeof(struct sockaddr_in);
142 printerr(0, "ERROR: unrecognized addr family %d\n",
147 err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0,
150 printerr(0, "ERROR: unable to resolve %s to hostname: %s\n",
151 addr, err == EAI_SYSTEM ? strerror(err) :
156 hostname = strdup(hbuf);
161 /* XXX buffer problems: */
163 read_service_info(char *info_file_name, char **servicename, char **servername,
164 int *prog, int *vers, char **protocol,
165 struct sockaddr *addr) {
166 #define INFOBUFLEN 256
167 char buf[INFOBUFLEN + 1];
168 static char dummy[128];
170 static char service[128];
171 static char address[128];
181 *servicename = *servername = *protocol = NULL;
183 if ((fd = open(info_file_name, O_RDONLY)) == -1) {
184 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
188 if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
193 numfields = sscanf(buf,"RPC server: %127s\n"
194 "service: %127s %15s version %15s\n"
198 service, program, version,
202 if (numfields == 5) {
203 strcpy(protoname, "tcp");
204 } else if (numfields != 6) {
209 if ((p = strstr(buf, "port")) != NULL)
210 sscanf(p, "port: %127s\n", cb_port);
212 /* check service, program, and version */
213 if(memcmp(service, "nfs", 3)) return -1;
214 *prog = atoi(program + 1); /* skip open paren */
215 *vers = atoi(version);
216 if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
219 if (cb_port[0] != '\0') {
220 port = atoi(cb_port);
221 if (port < 0 || port > 65535)
225 if (!addrstr_to_sockaddr(addr, address, port))
228 *servername = sockaddr_to_hostname(addr, address);
229 if (*servername == NULL)
232 nbytes = snprintf(buf, INFOBUFLEN, "%s@%s", service, *servername);
233 if (nbytes > INFOBUFLEN)
236 if (!(*servicename = calloc(strlen(buf) + 1, 1)))
238 memcpy(*servicename, buf, strlen(buf));
240 if (!(*protocol = strdup(protoname)))
244 printerr(0, "ERROR: failed to read service info\n");
245 if (fd != -1) close(fd);
249 *servicename = *servername = *protocol = NULL;
254 destroy_client(struct clnt_info *clp)
256 if (clp->krb5_poll_index != -1)
257 memset(&pollarray[clp->krb5_poll_index], 0,
258 sizeof(struct pollfd));
259 if (clp->spkm3_poll_index != -1)
260 memset(&pollarray[clp->spkm3_poll_index], 0,
261 sizeof(struct pollfd));
262 if (clp->dir_fd != -1) close(clp->dir_fd);
263 if (clp->krb5_fd != -1) close(clp->krb5_fd);
264 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
266 free(clp->servicename);
267 free(clp->servername);
272 static struct clnt_info *
273 insert_new_clnt(void)
275 struct clnt_info *clp = NULL;
277 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
278 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
282 clp->krb5_poll_index = -1;
283 clp->spkm3_poll_index = -1;
288 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
294 process_clnt_dir_files(struct clnt_info * clp)
298 char info_file_name[32];
300 if (clp->krb5_fd == -1) {
301 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
302 clp->krb5_fd = open(kname, O_RDWR);
304 if (clp->spkm3_fd == -1) {
305 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
306 clp->spkm3_fd = open(sname, O_RDWR);
308 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
310 snprintf(info_file_name, sizeof(info_file_name), "%s/info",
312 if ((clp->servicename == NULL) &&
313 read_service_info(info_file_name, &clp->servicename,
314 &clp->servername, &clp->prog, &clp->vers,
315 &clp->protocol, (struct sockaddr *) &clp->addr))
321 get_poll_index(int *ind)
326 for (i=0; i<FD_ALLOC_BLOCK; i++) {
327 if (pollarray[i].events == 0) {
333 printerr(0, "ERROR: No pollarray slots open\n");
341 insert_clnt_poll(struct clnt_info *clp)
343 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
344 if (get_poll_index(&clp->krb5_poll_index)) {
345 printerr(0, "ERROR: Too many krb5 clients\n");
348 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
349 pollarray[clp->krb5_poll_index].events |= POLLIN;
352 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
353 if (get_poll_index(&clp->spkm3_poll_index)) {
354 printerr(0, "ERROR: Too many spkm3 clients\n");
357 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
358 pollarray[clp->spkm3_poll_index].events |= POLLIN;
365 process_clnt_dir(char *dir)
367 struct clnt_info * clp;
369 if (!(clp = insert_new_clnt()))
370 goto fail_destroy_client;
372 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
373 goto fail_destroy_client;
375 memcpy(clp->dirname, dir, strlen(dir));
376 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
377 printerr(0, "ERROR: can't open %s: %s\n",
378 clp->dirname, strerror(errno));
379 goto fail_destroy_client;
381 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
382 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
384 if (process_clnt_dir_files(clp))
385 goto fail_keep_client;
387 if (insert_clnt_poll(clp))
388 goto fail_destroy_client;
394 TAILQ_REMOVE(&clnt_list, clp, list);
398 /* We couldn't find some subdirectories, but we keep the client
399 * around in case we get a notification on the directory when the
400 * subdirectories are created. */
405 init_client_list(void)
407 TAILQ_INIT(&clnt_list);
408 /* Eventually plan to grow/shrink poll array: */
409 pollsize = FD_ALLOC_BLOCK;
410 pollarray = calloc(pollsize, sizeof(struct pollfd));
414 * This is run after a DNOTIFY signal, and should clear up any
415 * directories that are no longer around, and re-scan any existing
416 * directories, since the DNOTIFY could have been in there.
419 update_old_clients(struct dirent **namelist, int size)
421 struct clnt_info *clp;
425 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
427 for (i=0; i < size; i++) {
428 if (!strcmp(clp->dirname, namelist[i]->d_name)) {
434 printerr(2, "destroying client %s\n", clp->dirname);
435 saveprev = clp->list.tqe_prev;
436 TAILQ_REMOVE(&clnt_list, clp, list);
441 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
442 if (!process_clnt_dir_files(clp))
443 insert_clnt_poll(clp);
447 /* Search for a client by directory name, return 1 if found, 0 otherwise */
449 find_client(char *dirname)
451 struct clnt_info *clp;
453 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
454 if (!strcmp(clp->dirname, dirname))
459 /* Used to read (and re-read) list of clients, set up poll array. */
461 update_client_list(void)
463 struct dirent **namelist;
466 if (chdir(pipefs_nfsdir) < 0) {
467 printerr(0, "ERROR: can't chdir to %s: %s\n",
468 pipefs_nfsdir, strerror(errno));
472 j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
474 printerr(0, "ERROR: can't scandir %s: %s\n",
475 pipefs_nfsdir, strerror(errno));
478 update_old_clients(namelist, j);
479 for (i=0; i < j; i++) {
480 if (i < FD_ALLOC_BLOCK
481 && !strncmp(namelist[i]->d_name, "clnt", 4)
482 && !find_client(namelist[i]->d_name))
483 process_clnt_dir(namelist[i]->d_name);
492 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
493 gss_buffer_desc *context_token)
495 char *buf = NULL, *p = NULL, *end = NULL;
496 unsigned int timeout = context_timeout;
497 unsigned int buf_size = 0;
499 printerr(1, "doing downcall\n");
500 buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
501 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
502 sizeof(context_token->length) + context_token->length;
503 p = buf = malloc(buf_size);
504 end = buf + buf_size;
506 if (WRITE_BYTES(&p, end, uid)) goto out_err;
507 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
508 if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
509 if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
510 if (write_buffer(&p, end, context_token)) goto out_err;
512 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
517 printerr(1, "Failed to write downcall!\n");
522 do_error_downcall(int k5_fd, uid_t uid, int err)
525 char *p = buf, *end = buf + 1024;
526 unsigned int timeout = 0;
529 printerr(1, "doing error downcall\n");
531 if (WRITE_BYTES(&p, end, uid)) goto out_err;
532 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
533 /* use seq_win = 0 to indicate an error: */
534 if (WRITE_BYTES(&p, end, zero)) goto out_err;
535 if (WRITE_BYTES(&p, end, err)) goto out_err;
537 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
540 printerr(1, "Failed to write error downcall!\n");
545 * If the port isn't already set, do an rpcbind query to the remote server
546 * using the program and version and get the port.
548 * Newer kernels send the value of the port= mount option in the "info"
549 * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
550 * of the port= option or '2049'. The port field in a new sockaddr should
551 * reflect the value that was sent by the kernel.
554 populate_port(struct sockaddr *sa, const socklen_t salen,
555 const rpcprog_t program, const rpcvers_t version,
556 const unsigned short protocol)
558 struct sockaddr_in *s4 = (struct sockaddr_in *) sa;
562 * Newer kernels send the port in the upcall. If we already have
563 * the port, there's no need to look it up.
565 switch (sa->sa_family) {
567 if (s4->sin_port != 0) {
568 printerr(2, "DEBUG: port already set to %d\n",
569 ntohs(s4->sin_port));
574 printerr(0, "ERROR: unsupported address family %d\n",
580 * Newer kernels that send the port in the upcall set the value to
581 * 2049 for NFSv4 mounts when one isn't specified. The check below is
582 * only for kernels that don't send the port in the upcall. For those
583 * we either have to do an rpcbind query or set it to the standard
584 * port. Doing a query could be problematic (firewalls, etc), so take
585 * the latter approach.
587 if (program == 100003 && version == 4) {
592 port = nfs_getport(sa, salen, program, version, protocol);
594 printerr(0, "ERROR: unable to obtain port for prog %ld "
595 "vers %ld\n", program, version);
600 printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port,
603 switch (sa->sa_family) {
605 s4->sin_port = htons(port);
613 * Create an RPC connection and establish an authenticated
614 * gss context with a server.
616 int create_auth_rpc_client(struct clnt_info *clp,
617 CLIENT **clnt_return,
622 CLIENT *rpc_clnt = NULL;
623 struct rpc_gss_sec sec;
628 char rpc_errmsg[1024];
630 struct timeval timeout = {5, 0};
631 struct sockaddr *addr = (struct sockaddr *) &clp->addr;
634 /* Create the context as the user (not as root) */
635 save_uid = geteuid();
636 if (setfsuid(uid) != 0) {
637 printerr(0, "WARNING: Failed to setfsuid for "
638 "user with uid %d\n", uid);
641 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
644 sec.qop = GSS_C_QOP_DEFAULT;
645 sec.svc = RPCSEC_GSS_SVC_NONE;
646 sec.cred = GSS_C_NO_CREDENTIAL;
648 if (authtype == AUTHTYPE_KRB5) {
649 sec.mech = (gss_OID)&krb5oid;
650 sec.req_flags = GSS_C_MUTUAL_FLAG;
652 else if (authtype == AUTHTYPE_SPKM3) {
653 sec.mech = (gss_OID)&spkm3oid;
654 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
655 * Need a way to switch....
657 sec.req_flags = GSS_C_MUTUAL_FLAG;
660 printerr(0, "ERROR: Invalid authentication type (%d) "
661 "in create_auth_rpc_client\n", authtype);
666 if (authtype == AUTHTYPE_KRB5) {
667 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
669 * Do this before creating rpc connection since we won't need
670 * rpc connection if it fails!
672 if (limit_krb5_enctypes(&sec, uid)) {
673 printerr(1, "WARNING: Failed while limiting krb5 "
674 "encryption types for user with uid %d\n",
681 /* create an rpc connection to the nfs server */
683 printerr(2, "creating %s client for server %s\n", clp->protocol,
686 if ((strcmp(clp->protocol, "tcp")) == 0) {
687 protocol = IPPROTO_TCP;
688 } else if ((strcmp(clp->protocol, "udp")) == 0) {
689 protocol = IPPROTO_UDP;
691 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
692 "for connection to server %s for user with uid %d\n",
693 clp->protocol, clp->servername, uid);
697 switch (addr->sa_family) {
699 salen = sizeof(struct sockaddr_in);
702 printerr(1, "ERROR: Unknown address family %d\n",
707 if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
710 rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
711 clp->vers, &timeout);
713 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
714 "WARNING: can't create %s rpc_clnt to server %s for "
716 protocol == IPPROTO_TCP ? "tcp" : "udp",
717 clp->servername, uid);
719 clnt_spcreateerror(rpc_errmsg));
723 printerr(2, "creating context with server %s\n", clp->servicename);
724 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
726 /* Our caller should print appropriate message */
727 printerr(2, "WARNING: Failed to create %s context for "
728 "user with uid %d for server %s\n",
729 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
730 uid, clp->servername);
735 rpc_clnt->cl_auth = auth;
736 *clnt_return = rpc_clnt;
741 if (sec.cred != GSS_C_NO_CREDENTIAL)
742 gss_release_cred(&min_stat, &sec.cred);
743 /* Restore euid to original value */
744 if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
745 printerr(0, "WARNING: Failed to restore fsuid"
746 " to uid %d from %d\n", save_uid, uid);
751 /* Only destroy here if failure. Otherwise, caller is responsible */
752 if (rpc_clnt) clnt_destroy(rpc_clnt);
759 * this code uses the userland rpcsec gss library to create a krb5
760 * context on behalf of the kernel
763 handle_krb5_upcall(struct clnt_info *clp)
766 CLIENT *rpc_clnt = NULL;
768 struct authgss_private_data pd;
769 gss_buffer_desc token;
770 char **credlist = NULL;
773 int create_resp = -1;
775 printerr(1, "handling krb5 upcall\n");
779 memset(&pd, 0, sizeof(struct authgss_private_data));
781 if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
782 printerr(0, "WARNING: failed reading uid from krb5 "
783 "upcall pipe: %s\n", strerror(errno));
787 if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
788 /* Tell krb5 gss which credentials cache to use */
789 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
790 if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
791 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
793 if (create_resp == 0)
797 if (create_resp != 0) {
798 if (uid == 0 && root_uses_machine_creds == 1) {
801 gssd_refresh_krb5_machine_credential(clp->servername,
804 * Get a list of credential cache names and try each
805 * of them until one works or we've tried them all
807 if (gssd_get_krb5_machine_cred_list(&credlist)) {
808 printerr(0, "ERROR: No credentials found "
809 "for connection to server %s\n",
811 goto out_return_error;
813 for (ccname = credlist; ccname && *ccname; ccname++) {
814 gssd_setup_krb5_machine_gss_ccache(*ccname);
815 if ((create_auth_rpc_client(clp, &rpc_clnt,
817 AUTHTYPE_KRB5)) == 0) {
822 printerr(2, "WARNING: Failed to create krb5 context "
823 "for user with uid %d with credentials "
824 "cache %s for server %s\n",
825 uid, *ccname, clp->servername);
827 gssd_free_krb5_machine_cred_list(credlist);
829 printerr(1, "WARNING: Failed to create krb5 context "
830 "for user with uid %d with any "
831 "credentials cache for server %s\n",
832 uid, clp->servername);
833 goto out_return_error;
836 printerr(1, "WARNING: Failed to create krb5 context "
837 "for user with uid %d for server %s\n",
838 uid, clp->servername);
839 goto out_return_error;
843 if (!authgss_get_private_data(auth, &pd)) {
844 printerr(1, "WARNING: Failed to obtain authentication "
845 "data for user with uid %d for server %s\n",
846 uid, clp->servername);
847 goto out_return_error;
850 if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
851 printerr(0, "WARNING: Failed to serialize krb5 context for "
852 "user with uid %d for server %s\n",
853 uid, clp->servername);
854 goto out_return_error;
857 do_downcall(clp->krb5_fd, uid, &pd, &token);
862 #ifndef HAVE_LIBTIRPC
863 if (pd.pd_ctx_hndl.length != 0)
864 authgss_free_private_data(&pd);
869 clnt_destroy(rpc_clnt);
873 do_error_downcall(clp->krb5_fd, uid, -1);
878 * this code uses the userland rpcsec gss library to create an spkm3
879 * context on behalf of the kernel
882 handle_spkm3_upcall(struct clnt_info *clp)
885 CLIENT *rpc_clnt = NULL;
887 struct authgss_private_data pd;
888 gss_buffer_desc token;
890 printerr(2, "handling spkm3 upcall\n");
895 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
896 printerr(0, "WARNING: failed reading uid from spkm3 "
897 "upcall pipe: %s\n", strerror(errno));
901 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
902 printerr(0, "WARNING: Failed to create spkm3 context for "
903 "user with uid %d\n", uid);
904 goto out_return_error;
907 if (!authgss_get_private_data(auth, &pd)) {
908 printerr(0, "WARNING: Failed to obtain authentication "
909 "data for user with uid %d for server %s\n",
910 uid, clp->servername);
911 goto out_return_error;
914 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) {
915 printerr(0, "WARNING: Failed to serialize spkm3 context for "
916 "user with uid %d for server\n",
917 uid, clp->servername);
918 goto out_return_error;
921 do_downcall(clp->spkm3_fd, uid, &pd, &token);
929 clnt_destroy(rpc_clnt);
933 do_error_downcall(clp->spkm3_fd, uid, -1);