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"
78 * array of struct pollfd suitable to pass to poll. initialized to
79 * zero - a zero struct is ignored by poll() because the events mask is 0.
82 * linked list of struct clnt_info which associates a clntXXX directory
83 * with an index into pollarray[], and other basic data about that client.
85 * Directory structure: created by the kernel nfs client
86 * {pipefs_nfsdir}/clntXX : one per rpc_clnt struct in the kernel
87 * {pipefs_nfsdir}/clntXX/krb5 : read uid for which kernel wants
88 * a context, write the resulting context
89 * {pipefs_nfsdir}/clntXX/info : stores info such as server name
92 * Poll all {pipefs_nfsdir}/clntXX/krb5 files. When ready, data read
93 * is a uid; performs rpcsec_gss context initialization protocol to
94 * get a cred for that user. Writes result to corresponding krb5 file
95 * in a form the kernel code will understand.
96 * In addition, we make sure we are notified whenever anything is
97 * created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories,
98 * and rescan the whole {pipefs_nfsdir} when this happens.
101 struct pollfd * pollarray;
103 int pollsize; /* the size of pollaray (in pollfd's) */
105 /* XXX buffer problems: */
107 read_service_info(char *info_file_name, char **servicename, char **servername,
108 int *prog, int *vers, char **protocol, int *port) {
109 #define INFOBUFLEN 256
110 char buf[INFOBUFLEN];
111 static char dummy[128];
113 static char service[128];
114 static char address[128];
122 struct hostent *ent = NULL;
125 *servicename = *servername = *protocol = NULL;
127 if ((fd = open(info_file_name, O_RDONLY)) == -1) {
128 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
132 if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
136 numfields = sscanf(buf,"RPC server: %127s\n"
137 "service: %127s %15s version %15s\n"
141 service, program, version,
145 if (numfields == 5) {
146 strcpy(protoname, "tcp");
147 } else if (numfields != 6) {
152 if ((p = strstr(buf, "port")) != NULL)
153 sscanf(p, "port: %127s\n", cb_port);
155 /* check service, program, and version */
156 if(memcmp(service, "nfs", 3)) return -1;
157 *prog = atoi(program + 1); /* skip open paren */
158 *vers = atoi(version);
159 if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
162 /* create service name */
163 inaddr = inet_addr(address);
164 if (!(ent = gethostbyaddr(&inaddr, sizeof(inaddr), AF_INET))) {
165 printerr(0, "ERROR: can't resolve server %s name\n", address);
168 if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
170 memcpy(*servername, ent->h_name, strlen(ent->h_name));
171 snprintf(buf, INFOBUFLEN, "%s@%s", service, ent->h_name);
172 if (!(*servicename = calloc(strlen(buf) + 1, 1)))
174 memcpy(*servicename, buf, strlen(buf));
175 if (cb_port[0] != '\0')
176 *port = atoi(cb_port);
178 if (!(*protocol = strdup(protoname)))
182 printerr(0, "ERROR: failed to read service info\n");
183 if (fd != -1) close(fd);
184 if (*servername) free(*servername);
185 if (*servicename) free(*servicename);
186 if (*protocol) free(*protocol);
191 destroy_client(struct clnt_info *clp)
193 if (clp->krb5_poll_index != -1)
194 memset(&pollarray[clp->krb5_poll_index], 0,
195 sizeof(struct pollfd));
196 if (clp->spkm3_poll_index != -1)
197 memset(&pollarray[clp->spkm3_poll_index], 0,
198 sizeof(struct pollfd));
199 if (clp->dir_fd != -1) close(clp->dir_fd);
200 if (clp->krb5_fd != -1) close(clp->krb5_fd);
201 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
202 if (clp->dirname) free(clp->dirname);
203 if (clp->servicename) free(clp->servicename);
204 if (clp->servername) free(clp->servername);
205 if (clp->protocol) free(clp->protocol);
209 static struct clnt_info *
210 insert_new_clnt(void)
212 struct clnt_info *clp = NULL;
214 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
215 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
219 clp->krb5_poll_index = -1;
220 clp->spkm3_poll_index = -1;
225 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
231 process_clnt_dir_files(struct clnt_info * clp)
235 char info_file_name[32];
237 if (clp->krb5_fd == -1) {
238 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
239 clp->krb5_fd = open(kname, O_RDWR);
241 if (clp->spkm3_fd == -1) {
242 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
243 clp->spkm3_fd = open(sname, O_RDWR);
245 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
247 snprintf(info_file_name, sizeof(info_file_name), "%s/info",
249 if ((clp->servicename == NULL) &&
250 read_service_info(info_file_name, &clp->servicename,
251 &clp->servername, &clp->prog, &clp->vers,
252 &clp->protocol, &clp->port))
258 get_poll_index(int *ind)
263 for (i=0; i<FD_ALLOC_BLOCK; i++) {
264 if (pollarray[i].events == 0) {
270 printerr(0, "ERROR: No pollarray slots open\n");
278 insert_clnt_poll(struct clnt_info *clp)
280 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
281 if (get_poll_index(&clp->krb5_poll_index)) {
282 printerr(0, "ERROR: Too many krb5 clients\n");
285 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
286 pollarray[clp->krb5_poll_index].events |= POLLIN;
289 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
290 if (get_poll_index(&clp->spkm3_poll_index)) {
291 printerr(0, "ERROR: Too many spkm3 clients\n");
294 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
295 pollarray[clp->spkm3_poll_index].events |= POLLIN;
302 process_clnt_dir(char *dir)
304 struct clnt_info * clp;
306 if (!(clp = insert_new_clnt()))
307 goto fail_destroy_client;
309 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
310 goto fail_destroy_client;
312 memcpy(clp->dirname, dir, strlen(dir));
313 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
314 printerr(0, "ERROR: can't open %s: %s\n",
315 clp->dirname, strerror(errno));
316 goto fail_destroy_client;
318 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
319 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
321 if (process_clnt_dir_files(clp))
322 goto fail_keep_client;
324 if (insert_clnt_poll(clp))
325 goto fail_destroy_client;
331 TAILQ_REMOVE(&clnt_list, clp, list);
335 /* We couldn't find some subdirectories, but we keep the client
336 * around in case we get a notification on the directory when the
337 * subdirectories are created. */
342 init_client_list(void)
344 TAILQ_INIT(&clnt_list);
345 /* Eventually plan to grow/shrink poll array: */
346 pollsize = FD_ALLOC_BLOCK;
347 pollarray = calloc(pollsize, sizeof(struct pollfd));
351 * This is run after a DNOTIFY signal, and should clear up any
352 * directories that are no longer around, and re-scan any existing
353 * directories, since the DNOTIFY could have been in there.
356 update_old_clients(struct dirent **namelist, int size)
358 struct clnt_info *clp;
362 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
364 for (i=0; i < size; i++) {
365 if (!strcmp(clp->dirname, namelist[i]->d_name)) {
371 printerr(2, "destroying client %s\n", clp->dirname);
372 saveprev = clp->list.tqe_prev;
373 TAILQ_REMOVE(&clnt_list, clp, list);
378 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
379 if (!process_clnt_dir_files(clp))
380 insert_clnt_poll(clp);
384 /* Search for a client by directory name, return 1 if found, 0 otherwise */
386 find_client(char *dirname)
388 struct clnt_info *clp;
390 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
391 if (!strcmp(clp->dirname, dirname))
396 /* Used to read (and re-read) list of clients, set up poll array. */
398 update_client_list(void)
400 struct dirent **namelist;
403 if (chdir(pipefs_nfsdir) < 0) {
404 printerr(0, "ERROR: can't chdir to %s: %s\n",
405 pipefs_nfsdir, strerror(errno));
409 j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
411 printerr(0, "ERROR: can't scandir %s: %s\n",
412 pipefs_nfsdir, strerror(errno));
415 update_old_clients(namelist, j);
416 for (i=0; i < j; i++) {
417 if (i < FD_ALLOC_BLOCK
418 && !strncmp(namelist[i]->d_name, "clnt", 4)
419 && !find_client(namelist[i]->d_name))
420 process_clnt_dir(namelist[i]->d_name);
429 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
430 gss_buffer_desc *context_token)
432 char *buf = NULL, *p = NULL, *end = NULL;
433 unsigned int timeout = context_timeout;
434 unsigned int buf_size = 0;
436 printerr(1, "doing downcall\n");
437 buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
438 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
439 sizeof(context_token->length) + context_token->length;
440 p = buf = malloc(buf_size);
441 end = buf + buf_size;
443 if (WRITE_BYTES(&p, end, uid)) goto out_err;
444 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
445 if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
446 if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
447 if (write_buffer(&p, end, context_token)) goto out_err;
449 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
454 printerr(1, "Failed to write downcall!\n");
459 do_error_downcall(int k5_fd, uid_t uid, int err)
462 char *p = buf, *end = buf + 1024;
463 unsigned int timeout = 0;
466 printerr(1, "doing error downcall\n");
468 if (WRITE_BYTES(&p, end, uid)) goto out_err;
469 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
470 /* use seq_win = 0 to indicate an error: */
471 if (WRITE_BYTES(&p, end, zero)) goto out_err;
472 if (WRITE_BYTES(&p, end, err)) goto out_err;
474 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
477 printerr(1, "Failed to write error downcall!\n");
482 * Create an RPC connection and establish an authenticated
483 * gss context with a server.
485 int create_auth_rpc_client(struct clnt_info *clp,
486 CLIENT **clnt_return,
491 CLIENT *rpc_clnt = NULL;
492 struct rpc_gss_sec sec;
498 char rpc_errmsg[1024];
499 int sockp = RPC_ANYSOCK;
500 int sendsz = 32768, recvsz = 32768;
501 struct addrinfo ai_hints, *a = NULL;
505 /* Create the context as the user (not as root) */
506 save_uid = geteuid();
507 if (setfsuid(uid) != 0) {
508 printerr(0, "WARNING: Failed to setfsuid for "
509 "user with uid %d\n", uid);
512 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
515 sec.qop = GSS_C_QOP_DEFAULT;
516 sec.svc = RPCSEC_GSS_SVC_NONE;
517 sec.cred = GSS_C_NO_CREDENTIAL;
519 if (authtype == AUTHTYPE_KRB5) {
520 sec.mech = (gss_OID)&krb5oid;
521 sec.req_flags = GSS_C_MUTUAL_FLAG;
523 else if (authtype == AUTHTYPE_SPKM3) {
524 sec.mech = (gss_OID)&spkm3oid;
525 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
526 * Need a way to switch....
528 sec.req_flags = GSS_C_MUTUAL_FLAG;
531 printerr(0, "ERROR: Invalid authentication type (%d) "
532 "in create_auth_rpc_client\n", authtype);
537 if (authtype == AUTHTYPE_KRB5) {
538 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
540 * Do this before creating rpc connection since we won't need
541 * rpc connection if it fails!
543 if (limit_krb5_enctypes(&sec, uid)) {
544 printerr(1, "WARNING: Failed while limiting krb5 "
545 "encryption types for user with uid %d\n",
552 /* create an rpc connection to the nfs server */
554 printerr(2, "creating %s client for server %s\n", clp->protocol,
557 memset(&ai_hints, '\0', sizeof(ai_hints));
558 ai_hints.ai_family = PF_INET;
559 ai_hints.ai_flags |= AI_CANONNAME;
560 if ((strcmp(clp->protocol, "tcp")) == 0) {
561 ai_hints.ai_socktype = SOCK_STREAM;
562 ai_hints.ai_protocol = IPPROTO_TCP;
563 } else if ((strcmp(clp->protocol, "udp")) == 0) {
564 ai_hints.ai_socktype = SOCK_DGRAM;
565 ai_hints.ai_protocol = IPPROTO_UDP;
567 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
568 "for connection to server %s for user with uid %d\n",
569 clp->protocol, clp->servername, uid);
573 /* extract the service name from clp->servicename */
574 if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
575 printerr(0, "WARNING: servicename (%s) not formatted as "
576 "expected with service@host\n", clp->servicename);
579 if ((at_sign - clp->servicename) >= sizeof(service)) {
580 printerr(0, "WARNING: service portion of servicename (%s) "
581 "is too long!\n", clp->servicename);
584 strncpy(service, clp->servicename, at_sign - clp->servicename);
585 service[at_sign - clp->servicename] = '\0';
587 errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
589 printerr(0, "WARNING: Error from getaddrinfo for server "
590 "'%s': %s\n", clp->servername, gai_strerror(errcode));
595 printerr(0, "WARNING: No address information found for "
596 "connection to server %s for user with uid %d\n",
597 clp->servername, uid);
601 ((struct sockaddr_in *)a->ai_addr)->sin_port = htons(clp->port);
602 if (a->ai_protocol == IPPROTO_TCP) {
603 if ((rpc_clnt = clnttcp_create(
604 (struct sockaddr_in *) a->ai_addr,
605 clp->prog, clp->vers, &sockp,
606 sendsz, recvsz)) == NULL) {
607 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
608 "WARNING: can't create tcp rpc_clnt "
609 "for server %s for user with uid %d",
610 clp->servername, uid);
612 clnt_spcreateerror(rpc_errmsg));
615 } else if (a->ai_protocol == IPPROTO_UDP) {
616 const struct timeval timeout = {5, 0};
617 if ((rpc_clnt = clntudp_bufcreate(
618 (struct sockaddr_in *) a->ai_addr,
619 clp->prog, clp->vers, timeout,
620 &sockp, sendsz, recvsz)) == NULL) {
621 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
622 "WARNING: can't create udp rpc_clnt "
623 "for server %s for user with uid %d",
624 clp->servername, uid);
626 clnt_spcreateerror(rpc_errmsg));
630 /* Shouldn't happen! */
631 printerr(0, "ERROR: requested protocol '%s', but "
632 "got addrinfo with protocol %d\n",
633 clp->protocol, a->ai_protocol);
636 /* We're done with this */
640 printerr(2, "creating context with server %s\n", clp->servicename);
641 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
643 /* Our caller should print appropriate message */
644 printerr(2, "WARNING: Failed to create %s context for "
645 "user with uid %d for server %s\n",
646 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
647 uid, clp->servername);
652 rpc_clnt->cl_auth = auth;
653 *clnt_return = rpc_clnt;
658 if (sec.cred != GSS_C_NO_CREDENTIAL)
659 gss_release_cred(&min_stat, &sec.cred);
660 if (a != NULL) freeaddrinfo(a);
661 /* Restore euid to original value */
662 if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
663 printerr(0, "WARNING: Failed to restore fsuid"
664 " to uid %d from %d\n", save_uid, uid);
669 /* Only destroy here if failure. Otherwise, caller is responsible */
670 if (rpc_clnt) clnt_destroy(rpc_clnt);
677 * this code uses the userland rpcsec gss library to create a krb5
678 * context on behalf of the kernel
681 handle_krb5_upcall(struct clnt_info *clp)
684 CLIENT *rpc_clnt = NULL;
686 struct authgss_private_data pd;
687 gss_buffer_desc token;
688 char **credlist = NULL;
691 int create_resp = -1;
693 printerr(1, "handling krb5 upcall\n");
697 memset(&pd, 0, sizeof(struct authgss_private_data));
699 if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
700 printerr(0, "WARNING: failed reading uid from krb5 "
701 "upcall pipe: %s\n", strerror(errno));
705 if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
706 /* Tell krb5 gss which credentials cache to use */
707 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
708 if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
709 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
711 if (create_resp == 0)
715 if (create_resp != 0) {
716 if (uid == 0 && root_uses_machine_creds == 1) {
719 gssd_refresh_krb5_machine_credential(clp->servername,
722 * Get a list of credential cache names and try each
723 * of them until one works or we've tried them all
725 if (gssd_get_krb5_machine_cred_list(&credlist)) {
726 printerr(0, "ERROR: No credentials found "
727 "for connection to server %s\n",
729 goto out_return_error;
731 for (ccname = credlist; ccname && *ccname; ccname++) {
732 gssd_setup_krb5_machine_gss_ccache(*ccname);
733 if ((create_auth_rpc_client(clp, &rpc_clnt,
735 AUTHTYPE_KRB5)) == 0) {
740 printerr(2, "WARNING: Failed to create krb5 context "
741 "for user with uid %d with credentials "
742 "cache %s for server %s\n",
743 uid, *ccname, clp->servername);
745 gssd_free_krb5_machine_cred_list(credlist);
747 printerr(1, "WARNING: Failed to create krb5 context "
748 "for user with uid %d with any "
749 "credentials cache for server %s\n",
750 uid, clp->servername);
751 goto out_return_error;
754 printerr(1, "WARNING: Failed to create krb5 context "
755 "for user with uid %d for server %s\n",
756 uid, clp->servername);
757 goto out_return_error;
761 if (!authgss_get_private_data(auth, &pd)) {
762 printerr(1, "WARNING: Failed to obtain authentication "
763 "data for user with uid %d for server %s\n",
764 uid, clp->servername);
765 goto out_return_error;
768 if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) {
769 printerr(0, "WARNING: Failed to serialize krb5 context for "
770 "user with uid %d for server %s\n",
771 uid, clp->servername);
772 goto out_return_error;
775 do_downcall(clp->krb5_fd, uid, &pd, &token);
780 #ifndef HAVE_LIBTIRPC
781 if (pd.pd_ctx_hndl.length != 0)
782 authgss_free_private_data(&pd);
787 clnt_destroy(rpc_clnt);
791 do_error_downcall(clp->krb5_fd, uid, -1);
796 * this code uses the userland rpcsec gss library to create an spkm3
797 * context on behalf of the kernel
800 handle_spkm3_upcall(struct clnt_info *clp)
803 CLIENT *rpc_clnt = NULL;
805 struct authgss_private_data pd;
806 gss_buffer_desc token;
808 printerr(2, "handling spkm3 upcall\n");
813 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
814 printerr(0, "WARNING: failed reading uid from spkm3 "
815 "upcall pipe: %s\n", strerror(errno));
819 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
820 printerr(0, "WARNING: Failed to create spkm3 context for "
821 "user with uid %d\n", uid);
822 goto out_return_error;
825 if (!authgss_get_private_data(auth, &pd)) {
826 printerr(0, "WARNING: Failed to obtain authentication "
827 "data for user with uid %d for server %s\n",
828 uid, clp->servername);
829 goto out_return_error;
832 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) {
833 printerr(0, "WARNING: Failed to serialize spkm3 context for "
834 "user with uid %d for server\n",
835 uid, clp->servername);
836 goto out_return_error;
839 do_downcall(clp->spkm3_fd, uid, &pd, &token);
847 clnt_destroy(rpc_clnt);
851 do_error_downcall(clp->spkm3_fd, uid, -1);