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, int *port) {
106 #define INFOBUFLEN 256
107 char buf[INFOBUFLEN];
108 static char dummy[128];
110 static char service[128];
111 static char address[128];
119 struct hostent *ent = NULL;
122 *servicename = *servername = *protocol = NULL;
124 if ((fd = open(info_file_name, O_RDONLY)) == -1) {
125 printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
129 if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
133 numfields = sscanf(buf,"RPC server: %127s\n"
134 "service: %127s %15s version %15s\n"
138 service, program, version,
142 if (numfields == 5) {
143 strcpy(protoname, "tcp");
144 } else if (numfields != 6) {
149 if ((p = strstr(buf, "port")) != NULL)
150 sscanf(p, "port: %127s\n", cb_port);
152 /* check service, program, and version */
153 if(memcmp(service, "nfs", 3)) return -1;
154 *prog = atoi(program + 1); /* skip open paren */
155 *vers = atoi(version);
156 if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
159 /* create service name */
160 inaddr = inet_addr(address);
161 if (!(ent = gethostbyaddr(&inaddr, sizeof(inaddr), AF_INET))) {
162 printerr(0, "ERROR: can't resolve server %s name\n", address);
165 if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
167 memcpy(*servername, ent->h_name, strlen(ent->h_name));
168 snprintf(buf, INFOBUFLEN, "%s@%s", service, ent->h_name);
169 if (!(*servicename = calloc(strlen(buf) + 1, 1)))
171 memcpy(*servicename, buf, strlen(buf));
172 if (cb_port[0] != '\0')
173 *port = atoi(cb_port);
175 if (!(*protocol = strdup(protoname)))
179 printerr(0, "ERROR: failed to read service info\n");
180 if (fd != -1) close(fd);
181 if (*servername) free(*servername);
182 if (*servicename) free(*servicename);
183 if (*protocol) free(*protocol);
188 destroy_client(struct clnt_info *clp)
190 if (clp->krb5_poll_index != -1)
191 memset(&pollarray[clp->krb5_poll_index], 0,
192 sizeof(struct pollfd));
193 if (clp->spkm3_poll_index != -1)
194 memset(&pollarray[clp->spkm3_poll_index], 0,
195 sizeof(struct pollfd));
196 if (clp->dir_fd != -1) close(clp->dir_fd);
197 if (clp->krb5_fd != -1) close(clp->krb5_fd);
198 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
199 if (clp->dirname) free(clp->dirname);
200 if (clp->servicename) free(clp->servicename);
201 if (clp->servername) free(clp->servername);
202 if (clp->protocol) free(clp->protocol);
206 static struct clnt_info *
207 insert_new_clnt(void)
209 struct clnt_info *clp = NULL;
211 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
212 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
216 clp->krb5_poll_index = -1;
217 clp->spkm3_poll_index = -1;
222 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
228 process_clnt_dir_files(struct clnt_info * clp)
232 char info_file_name[32];
234 if (clp->krb5_fd == -1) {
235 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
236 clp->krb5_fd = open(kname, O_RDWR);
238 if (clp->spkm3_fd == -1) {
239 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
240 clp->spkm3_fd = open(sname, O_RDWR);
242 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
244 snprintf(info_file_name, sizeof(info_file_name), "%s/info",
246 if ((clp->servicename == NULL) &&
247 read_service_info(info_file_name, &clp->servicename,
248 &clp->servername, &clp->prog, &clp->vers,
249 &clp->protocol, &clp->port))
255 get_poll_index(int *ind)
260 for (i=0; i<FD_ALLOC_BLOCK; i++) {
261 if (pollarray[i].events == 0) {
267 printerr(0, "ERROR: No pollarray slots open\n");
275 insert_clnt_poll(struct clnt_info *clp)
277 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
278 if (get_poll_index(&clp->krb5_poll_index)) {
279 printerr(0, "ERROR: Too many krb5 clients\n");
282 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
283 pollarray[clp->krb5_poll_index].events |= POLLIN;
286 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
287 if (get_poll_index(&clp->spkm3_poll_index)) {
288 printerr(0, "ERROR: Too many spkm3 clients\n");
291 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
292 pollarray[clp->spkm3_poll_index].events |= POLLIN;
299 process_clnt_dir(char *dir)
301 struct clnt_info * clp;
303 if (!(clp = insert_new_clnt()))
304 goto fail_destroy_client;
306 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
307 goto fail_destroy_client;
309 memcpy(clp->dirname, dir, strlen(dir));
310 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
311 printerr(0, "ERROR: can't open %s: %s\n",
312 clp->dirname, strerror(errno));
313 goto fail_destroy_client;
315 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
316 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
318 if (process_clnt_dir_files(clp))
319 goto fail_keep_client;
321 if (insert_clnt_poll(clp))
322 goto fail_destroy_client;
328 TAILQ_REMOVE(&clnt_list, clp, list);
332 /* We couldn't find some subdirectories, but we keep the client
333 * around in case we get a notification on the directory when the
334 * subdirectories are created. */
339 init_client_list(void)
341 TAILQ_INIT(&clnt_list);
342 /* Eventually plan to grow/shrink poll array: */
343 pollsize = FD_ALLOC_BLOCK;
344 pollarray = calloc(pollsize, sizeof(struct pollfd));
348 * This is run after a DNOTIFY signal, and should clear up any
349 * directories that are no longer around, and re-scan any existing
350 * directories, since the DNOTIFY could have been in there.
353 update_old_clients(struct dirent **namelist, int size)
355 struct clnt_info *clp;
359 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
361 for (i=0; i < size; i++) {
362 if (!strcmp(clp->dirname, namelist[i]->d_name)) {
368 printerr(2, "destroying client %s\n", clp->dirname);
369 saveprev = clp->list.tqe_prev;
370 TAILQ_REMOVE(&clnt_list, clp, list);
375 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
376 if (!process_clnt_dir_files(clp))
377 insert_clnt_poll(clp);
381 /* Search for a client by directory name, return 1 if found, 0 otherwise */
383 find_client(char *dirname)
385 struct clnt_info *clp;
387 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
388 if (!strcmp(clp->dirname, dirname))
393 /* Used to read (and re-read) list of clients, set up poll array. */
395 update_client_list(void)
397 struct dirent **namelist;
400 if (chdir(pipefs_nfsdir) < 0) {
401 printerr(0, "ERROR: can't chdir to %s: %s\n",
402 pipefs_nfsdir, strerror(errno));
406 j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
408 printerr(0, "ERROR: can't scandir %s: %s\n",
409 pipefs_nfsdir, strerror(errno));
412 update_old_clients(namelist, j);
413 for (i=0; i < j; i++) {
414 if (i < FD_ALLOC_BLOCK
415 && !strncmp(namelist[i]->d_name, "clnt", 4)
416 && !find_client(namelist[i]->d_name))
417 process_clnt_dir(namelist[i]->d_name);
426 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
427 gss_buffer_desc *context_token)
429 char *buf = NULL, *p = NULL, *end = NULL;
430 unsigned int timeout = context_timeout;
431 unsigned int buf_size = 0;
433 printerr(1, "doing downcall\n");
434 buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
435 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
436 sizeof(context_token->length) + context_token->length;
437 p = buf = malloc(buf_size);
438 end = buf + buf_size;
440 if (WRITE_BYTES(&p, end, uid)) goto out_err;
441 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
442 if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
443 if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
444 if (write_buffer(&p, end, context_token)) goto out_err;
446 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
451 printerr(0, "Failed to write downcall!\n");
456 do_error_downcall(int k5_fd, uid_t uid, int err)
459 char *p = buf, *end = buf + 1024;
460 unsigned int timeout = 0;
463 printerr(1, "doing error downcall\n");
465 if (WRITE_BYTES(&p, end, uid)) goto out_err;
466 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
467 /* use seq_win = 0 to indicate an error: */
468 if (WRITE_BYTES(&p, end, zero)) goto out_err;
469 if (WRITE_BYTES(&p, end, err)) goto out_err;
471 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
474 printerr(1, "Failed to write error downcall!\n");
479 * Create an RPC connection and establish an authenticated
480 * gss context with a server.
482 int create_auth_rpc_client(struct clnt_info *clp,
483 CLIENT **clnt_return,
488 CLIENT *rpc_clnt = NULL;
489 struct rpc_gss_sec sec;
495 char rpc_errmsg[1024];
496 int sockp = RPC_ANYSOCK;
497 int sendsz = 32768, recvsz = 32768;
498 struct addrinfo ai_hints, *a = NULL;
502 /* Create the context as the user (not as root) */
503 save_uid = geteuid();
504 if (setfsuid(uid) != 0) {
505 printerr(0, "WARNING: Failed to setfsuid for "
506 "user with uid %d\n", uid);
509 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
512 sec.qop = GSS_C_QOP_DEFAULT;
513 sec.svc = RPCSEC_GSS_SVC_NONE;
514 sec.cred = GSS_C_NO_CREDENTIAL;
516 if (authtype == AUTHTYPE_KRB5) {
517 sec.mech = (gss_OID)&krb5oid;
518 sec.req_flags = GSS_C_MUTUAL_FLAG;
520 else if (authtype == AUTHTYPE_SPKM3) {
521 sec.mech = (gss_OID)&spkm3oid;
522 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
523 * Need a way to switch....
525 sec.req_flags = GSS_C_MUTUAL_FLAG;
528 printerr(0, "ERROR: Invalid authentication type (%d) "
529 "in create_auth_rpc_client\n", authtype);
534 if (authtype == AUTHTYPE_KRB5) {
535 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
537 * Do this before creating rpc connection since we won't need
538 * rpc connection if it fails!
540 if (limit_krb5_enctypes(&sec, uid)) {
541 printerr(1, "WARNING: Failed while limiting krb5 "
542 "encryption types for user with uid %d\n",
549 /* create an rpc connection to the nfs server */
551 printerr(2, "creating %s client for server %s\n", clp->protocol,
554 memset(&ai_hints, '\0', sizeof(ai_hints));
555 ai_hints.ai_family = PF_INET;
556 ai_hints.ai_flags |= AI_CANONNAME;
557 if ((strcmp(clp->protocol, "tcp")) == 0) {
558 ai_hints.ai_socktype = SOCK_STREAM;
559 ai_hints.ai_protocol = IPPROTO_TCP;
560 } else if ((strcmp(clp->protocol, "udp")) == 0) {
561 ai_hints.ai_socktype = SOCK_DGRAM;
562 ai_hints.ai_protocol = IPPROTO_UDP;
564 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
565 "for connection to server %s for user with uid %d\n",
566 clp->protocol, clp->servername, uid);
570 /* extract the service name from clp->servicename */
571 if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
572 printerr(0, "WARNING: servicename (%s) not formatted as "
573 "expected with service@host\n", clp->servicename);
576 if ((at_sign - clp->servicename) >= sizeof(service)) {
577 printerr(0, "WARNING: service portion of servicename (%s) "
578 "is too long!\n", clp->servicename);
581 strncpy(service, clp->servicename, at_sign - clp->servicename);
582 service[at_sign - clp->servicename] = '\0';
584 errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
586 printerr(0, "WARNING: Error from getaddrinfo for server "
587 "'%s': %s\n", clp->servername, gai_strerror(errcode));
592 printerr(0, "WARNING: No address information found for "
593 "connection to server %s for user with uid %d\n",
594 clp->servername, uid);
598 ((struct sockaddr_in *)a->ai_addr)->sin_port = htons(clp->port);
599 if (a->ai_protocol == IPPROTO_TCP) {
600 if ((rpc_clnt = clnttcp_create(
601 (struct sockaddr_in *) a->ai_addr,
602 clp->prog, clp->vers, &sockp,
603 sendsz, recvsz)) == NULL) {
604 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
605 "WARNING: can't create tcp rpc_clnt "
606 "for server %s for user with uid %d",
607 clp->servername, uid);
609 clnt_spcreateerror(rpc_errmsg));
612 } else if (a->ai_protocol == IPPROTO_UDP) {
613 const struct timeval timeout = {5, 0};
614 if ((rpc_clnt = clntudp_bufcreate(
615 (struct sockaddr_in *) a->ai_addr,
616 clp->prog, clp->vers, timeout,
617 &sockp, sendsz, recvsz)) == NULL) {
618 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
619 "WARNING: can't create udp rpc_clnt "
620 "for server %s for user with uid %d",
621 clp->servername, uid);
623 clnt_spcreateerror(rpc_errmsg));
627 /* Shouldn't happen! */
628 printerr(0, "ERROR: requested protocol '%s', but "
629 "got addrinfo with protocol %d\n",
630 clp->protocol, a->ai_protocol);
633 /* We're done with this */
637 printerr(2, "creating context with server %s\n", clp->servicename);
638 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
640 /* Our caller should print appropriate message */
641 printerr(2, "WARNING: Failed to create %s context for "
642 "user with uid %d for server %s\n",
643 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
644 uid, clp->servername);
649 rpc_clnt->cl_auth = auth;
650 *clnt_return = rpc_clnt;
655 if (sec.cred != GSS_C_NO_CREDENTIAL)
656 gss_release_cred(&min_stat, &sec.cred);
657 if (a != NULL) freeaddrinfo(a);
658 /* Restore euid to original value */
659 if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
660 printerr(0, "WARNING: Failed to restore fsuid"
661 " to uid %d from %d\n", save_uid, uid);
666 /* Only destroy here if failure. Otherwise, caller is responsible */
667 if (rpc_clnt) clnt_destroy(rpc_clnt);
674 * this code uses the userland rpcsec gss library to create a krb5
675 * context on behalf of the kernel
678 handle_krb5_upcall(struct clnt_info *clp)
681 CLIENT *rpc_clnt = NULL;
683 struct authgss_private_data pd;
684 gss_buffer_desc token;
685 char **credlist = NULL;
688 int create_resp = -1;
690 printerr(1, "handling krb5 upcall\n");
694 memset(&pd, 0, sizeof(struct authgss_private_data));
696 if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
697 printerr(0, "WARNING: failed reading uid from krb5 "
698 "upcall pipe: %s\n", strerror(errno));
702 if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
703 /* Tell krb5 gss which credentials cache to use */
704 for (dirname = ccachesearch; *dirname != NULL; dirname++) {
705 if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
706 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
708 if (create_resp == 0)
712 if (create_resp != 0) {
713 if (uid == 0 && root_uses_machine_creds == 1) {
716 gssd_refresh_krb5_machine_credential(clp->servername,
719 * Get a list of credential cache names and try each
720 * of them until one works or we've tried them all
722 if (gssd_get_krb5_machine_cred_list(&credlist)) {
723 printerr(0, "ERROR: No credentials found "
724 "for connection to server %s\n",
726 goto out_return_error;
728 for (ccname = credlist; ccname && *ccname; ccname++) {
729 gssd_setup_krb5_machine_gss_ccache(*ccname);
730 if ((create_auth_rpc_client(clp, &rpc_clnt,
732 AUTHTYPE_KRB5)) == 0) {
737 printerr(2, "WARNING: Failed to create krb5 context "
738 "for user with uid %d with credentials "
739 "cache %s for server %s\n",
740 uid, *ccname, clp->servername);
742 gssd_free_krb5_machine_cred_list(credlist);
744 printerr(0, "WARNING: Failed to create krb5 context "
745 "for user with uid %d with any "
746 "credentials cache for server %s\n",
747 uid, clp->servername);
748 goto out_return_error;
751 printerr(0, "WARNING: Failed to create krb5 context "
752 "for user with uid %d for server %s\n",
753 uid, clp->servername);
754 goto out_return_error;
758 if (!authgss_get_private_data(auth, &pd)) {
759 printerr(0, "WARNING: Failed to obtain authentication "
760 "data for user with uid %d for server %s\n",
761 uid, clp->servername);
762 goto out_return_error;
765 if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid)) {
766 printerr(0, "WARNING: Failed to serialize krb5 context for "
767 "user with uid %d for server %s\n",
768 uid, clp->servername);
769 goto out_return_error;
772 do_downcall(clp->krb5_fd, uid, &pd, &token);
777 if (pd.pd_ctx_hndl.length != 0)
778 authgss_free_private_data(&pd);
782 clnt_destroy(rpc_clnt);
786 do_error_downcall(clp->krb5_fd, uid, -1);
791 * this code uses the userland rpcsec gss library to create an spkm3
792 * context on behalf of the kernel
795 handle_spkm3_upcall(struct clnt_info *clp)
798 CLIENT *rpc_clnt = NULL;
800 struct authgss_private_data pd;
801 gss_buffer_desc token;
803 printerr(2, "handling spkm3 upcall\n");
808 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
809 printerr(0, "WARNING: failed reading uid from spkm3 "
810 "upcall pipe: %s\n", strerror(errno));
814 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
815 printerr(0, "WARNING: Failed to create spkm3 context for "
816 "user with uid %d\n", uid);
817 goto out_return_error;
820 if (!authgss_get_private_data(auth, &pd)) {
821 printerr(0, "WARNING: Failed to obtain authentication "
822 "data for user with uid %d for server %s\n",
823 uid, clp->servername);
824 goto out_return_error;
827 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
828 printerr(0, "WARNING: Failed to serialize spkm3 context for "
829 "user with uid %d for server\n",
830 uid, clp->servername);
831 goto out_return_error;
834 do_downcall(clp->spkm3_fd, uid, &pd, &token);
842 clnt_destroy(rpc_clnt);
846 do_error_downcall(clp->spkm3_fd, uid, -1);