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>
47 #include <sys/socket.h>
48 #include <arpa/inet.h>
61 #include <gssapi/gssapi.h>
68 #include "krb5_util.h"
74 * array of struct pollfd suitable to pass to poll. initialized to
75 * zero - a zero struct is ignored by poll() because the events mask is 0.
78 * linked list of struct clnt_info which associates a clntXXX directory
79 * with an index into pollarray[], and other basic data about that client.
81 * Directory structure: created by the kernel nfs client
82 * /pipefsdir/clntXX : one per rpc_clnt struct in the kernel
83 * /pipefsdir/clntXX/krb5 : read uid for which kernel wants
84 * a context, write the resulting context
85 * /pipefsdir/clntXX/info : stores info such as server name
88 * Poll all /pipefsdir/clntXX/krb5 files. When ready, data read
89 * is a uid; performs rpcsec_gss context initialization protocol to
90 * get a cred for that user. Writes result to corresponding krb5 file
91 * in a form the kernel code will understand.
92 * In addition, we make sure we are notified whenever anything is
93 * created or destroyed in pipefsdir/ or in an of the clntXX directories,
94 * and rescan the whole pipefsdir when this happens.
97 struct pollfd * pollarray;
99 int pollsize; /* the size of pollaray (in pollfd's) */
102 destroy_client(struct clnt_info *clp)
104 if (clp->krb5_poll_index != -1)
105 memset(&pollarray[clp->krb5_poll_index], 0,
106 sizeof(struct pollfd));
107 if (clp->spkm3_poll_index != -1)
108 memset(&pollarray[clp->spkm3_poll_index], 0,
109 sizeof(struct pollfd));
110 if (clp->dir_fd != -1) close(clp->dir_fd);
111 if (clp->krb5_fd != -1) close(clp->krb5_fd);
112 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
113 if (clp->dirname) free(clp->dirname);
114 if (clp->servicename) free(clp->servicename);
118 static struct clnt_info *
119 insert_new_clnt(void)
121 struct clnt_info *clp = NULL;
123 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
124 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
128 clp->krb5_poll_index = -1;
129 clp->spkm3_poll_index = -1;
134 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
140 process_clnt_dir_files(struct clnt_info * clp)
145 if (clp->krb5_fd == -1) {
146 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
147 clp->krb5_fd = open(kname, O_RDWR);
149 if (clp->spkm3_fd == -1) {
150 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
151 clp->spkm3_fd = open(sname, O_RDWR);
153 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
159 get_poll_index(int *ind)
164 for (i=0; i<FD_ALLOC_BLOCK; i++) {
165 if (pollarray[i].events == 0) {
171 printerr(0, "ERROR: No pollarray slots open\n");
179 insert_clnt_poll(struct clnt_info *clp)
181 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
182 if (get_poll_index(&clp->krb5_poll_index)) {
183 printerr(0, "ERROR: Too many krb5 clients\n");
186 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
187 pollarray[clp->krb5_poll_index].events |= POLLIN;
188 printerr(2, "monitoring krb5 channel under %s\n",
192 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
193 if (get_poll_index(&clp->spkm3_poll_index)) {
194 printerr(0, "ERROR: Too many spkm3 clients\n");
197 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
198 pollarray[clp->spkm3_poll_index].events |= POLLIN;
205 process_clnt_dir(char *dir)
207 struct clnt_info * clp;
209 if (!(clp = insert_new_clnt()))
210 goto fail_destroy_client;
212 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
213 goto fail_destroy_client;
215 memcpy(clp->dirname, dir, strlen(dir));
216 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
217 printerr(0, "ERROR: can't open %s: %s\n",
218 clp->dirname, strerror(errno));
219 goto fail_destroy_client;
221 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
222 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
224 if (process_clnt_dir_files(clp))
225 goto fail_keep_client;
227 if (insert_clnt_poll(clp))
228 goto fail_destroy_client;
234 TAILQ_REMOVE(&clnt_list, clp, list);
238 /* We couldn't find some subdirectories, but we keep the client
239 * around in case we get a notification on the directory when the
240 * subdirectories are created. */
245 init_client_list(void)
247 TAILQ_INIT(&clnt_list);
248 /* Eventually plan to grow/shrink poll array: */
249 pollsize = FD_ALLOC_BLOCK;
250 pollarray = calloc(pollsize, sizeof(struct pollfd));
254 * This is run after a DNOTIFY signal, and should clear up any
255 * directories that are no longer around, and re-scan any existing
256 * directories, since the DNOTIFY could have been in there.
259 update_old_clients(struct dirent **namelist, int size)
261 struct clnt_info *clp;
265 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
267 for (i=0; i < size; i++) {
268 if (!strcmp(clp->dirname, namelist[i]->d_name)) {
274 printerr(2, "destroying client %s\n", clp->dirname);
275 saveprev = clp->list.tqe_prev;
276 TAILQ_REMOVE(&clnt_list, clp, list);
281 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
282 if (!process_clnt_dir_files(clp))
283 insert_clnt_poll(clp);
287 /* Search for a client by directory name, return 1 if found, 0 otherwise */
289 find_client(char *dirname)
291 struct clnt_info *clp;
293 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
294 if (!strcmp(clp->dirname, dirname))
299 /* Used to read (and re-read) list of clients, set up poll array. */
301 update_client_list(void)
303 char lustre_dir[PATH_MAX];
304 struct dirent lustre_dirent = { .d_name = "lustre" };
305 struct dirent *namelist[1];
309 if (chdir(pipefsdir) < 0) {
310 printerr(0, "ERROR: can't chdir to %s: %s\n",
311 pipefsdir, strerror(errno));
315 snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefsdir, "lustre");
316 if (stat(lustre_dir, &statbuf) == 0) {
317 namelist[0] = &lustre_dirent;
319 printerr(2, "re-processing lustre directory\n");
323 printerr(2, "lustre directory not exist\n");
326 update_old_clients(namelist, j);
327 for (i=0; i < j; i++) {
328 if (i < FD_ALLOC_BLOCK &&
329 !find_client(namelist[i]->d_name))
330 process_clnt_dir(namelist[i]->d_name);
337 /* Context creation response. */
338 struct lustre_gss_init_res {
339 gss_buffer_desc gr_ctx; /* context handle */
340 u_int gr_major; /* major status */
341 u_int gr_minor; /* minor status */
342 u_int gr_win; /* sequence window */
343 gss_buffer_desc gr_token; /* token */
346 struct lustre_gss_data {
348 int lgd_lustre_svc; /* mds/oss */
349 int lgd_uid; /* uid */
350 char *lgd_uuid; /* client device uuid */
351 gss_name_t lgd_name; /* service name */
353 gss_OID lgd_mech; /* mech OID */
354 u_int lgd_req_flags; /* request flags */
355 gss_cred_id_t lgd_cred; /* credential */
356 gss_ctx_id_t lgd_ctx; /* session context */
357 gss_buffer_desc lgd_rmt_ctx; /* remote handle of context */
358 uint32_t lgd_seq_win; /* sequence window */
365 do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
366 struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
368 char *buf = NULL, *p = NULL, *end = NULL;
369 unsigned int timeout = 0; /* XXX decide on a reasonable value */
370 unsigned int buf_size = 0;
372 printerr(2, "doing downcall\n");
373 buf_size = sizeof(updata->seq) + sizeof(timeout) +
374 sizeof(lgd->lgd_seq_win) +
375 sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
376 sizeof(context_token->length) + context_token->length;
377 p = buf = malloc(buf_size);
378 end = buf + buf_size;
380 if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
381 /* Not setting any timeout for now: */
382 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
383 if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
384 if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
385 if (write_buffer(&p, end, context_token)) goto out_err;
387 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
392 printerr(0, "ERROR: Failed to write downcall!\n");
397 do_error_downcall(int k5_fd, struct lgssd_upcall_data *updata,
398 int rpc_err, int gss_err)
401 char *p = buf, *end = buf + 1024;
402 unsigned int timeout = 0;
405 printerr(1, "doing error downcall\n");
407 if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
408 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
409 /* use seq_win = 0 to indicate an error: */
410 if (WRITE_BYTES(&p, end, zero)) goto out_err;
411 if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
412 if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
414 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
417 printerr(0, "Failed to write error downcall!\n");
423 * Create an RPC connection and establish an authenticated
424 * gss context with a server.
426 int create_auth_rpc_client(struct clnt_info *clp,
427 CLIENT **clnt_return,
432 CLIENT *rpc_clnt = NULL;
433 struct rpc_gss_sec sec;
439 char rpc_errmsg[1024];
440 int sockp = RPC_ANYSOCK;
441 int sendsz = 32768, recvsz = 32768;
442 struct addrinfo ai_hints, *a = NULL;
446 /* Create the context as the user (not as root) */
447 save_uid = geteuid();
448 if (setfsuid(uid) != 0) {
449 printerr(0, "WARNING: Failed to setfsuid for "
450 "user with uid %d\n", uid);
453 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
456 sec.qop = GSS_C_QOP_DEFAULT;
457 sec.svc = RPCSEC_GSS_SVC_NONE;
458 sec.cred = GSS_C_NO_CREDENTIAL;
460 if (authtype == AUTHTYPE_KRB5) {
461 sec.mech = (gss_OID)&krb5oid;
462 sec.req_flags = GSS_C_MUTUAL_FLAG;
464 else if (authtype == AUTHTYPE_SPKM3) {
465 sec.mech = (gss_OID)&spkm3oid;
466 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
467 * Need a way to switch....
469 sec.req_flags = GSS_C_MUTUAL_FLAG;
472 printerr(0, "ERROR: Invalid authentication type (%d) "
473 "in create_auth_rpc_client\n", authtype);
478 if (authtype == AUTHTYPE_KRB5) {
479 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
481 * Do this before creating rpc connection since we won't need
482 * rpc connection if it fails!
484 if (limit_krb5_enctypes(&sec, uid)) {
485 printerr(1, "WARNING: Failed while limiting krb5 "
486 "encryption types for user with uid %d\n",
493 /* create an rpc connection to the nfs server */
495 printerr(2, "creating %s client for server %s\n", clp->protocol,
498 memset(&ai_hints, '\0', sizeof(ai_hints));
499 ai_hints.ai_family = PF_INET;
500 ai_hints.ai_flags |= AI_CANONNAME;
501 if ((strcmp(clp->protocol, "tcp")) == 0) {
502 ai_hints.ai_socktype = SOCK_STREAM;
503 ai_hints.ai_protocol = IPPROTO_TCP;
504 } else if ((strcmp(clp->protocol, "udp")) == 0) {
505 ai_hints.ai_socktype = SOCK_DGRAM;
506 ai_hints.ai_protocol = IPPROTO_UDP;
508 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
509 "for connection to server %s for user with uid %d",
510 clp->protocol, clp->servername, uid);
514 /* extract the service name from clp->servicename */
515 if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
516 printerr(0, "WARNING: servicename (%s) not formatted as "
517 "expected with service@host", clp->servicename);
520 if ((at_sign - clp->servicename) >= sizeof(service)) {
521 printerr(0, "WARNING: service portion of servicename (%s) "
522 "is too long!", clp->servicename);
525 strncpy(service, clp->servicename, at_sign - clp->servicename);
526 service[at_sign - clp->servicename] = '\0';
528 errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
530 printerr(0, "WARNING: Error from getaddrinfo for server "
531 "'%s': %s", clp->servername, gai_strerror(errcode));
536 printerr(0, "WARNING: No address information found for "
537 "connection to server %s for user with uid %d",
538 clp->servername, uid);
541 if (a->ai_protocol == IPPROTO_TCP) {
542 if ((rpc_clnt = clnttcp_create(
543 (struct sockaddr_in *) a->ai_addr,
544 clp->prog, clp->vers, &sockp,
545 sendsz, recvsz)) == NULL) {
546 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
547 "WARNING: can't create tcp rpc_clnt "
548 "for server %s for user with uid %d",
549 clp->servername, uid);
551 clnt_spcreateerror(rpc_errmsg));
554 } else if (a->ai_protocol == IPPROTO_UDP) {
555 const struct timeval timeout = {5, 0};
556 if ((rpc_clnt = clntudp_bufcreate(
557 (struct sockaddr_in *) a->ai_addr,
558 clp->prog, clp->vers, timeout,
559 &sockp, sendsz, recvsz)) == NULL) {
560 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
561 "WARNING: can't create udp rpc_clnt "
562 "for server %s for user with uid %d",
563 clp->servername, uid);
565 clnt_spcreateerror(rpc_errmsg));
569 /* Shouldn't happen! */
570 printerr(0, "ERROR: requested protocol '%s', but "
571 "got addrinfo with protocol %d",
572 clp->protocol, a->ai_protocol);
575 /* We're done with this */
579 printerr(2, "creating context with server %s\n", clp->servicename);
580 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
582 /* Our caller should print appropriate message */
583 printerr(2, "WARNING: Failed to create %s context for "
584 "user with uid %d for server %s\n",
585 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
586 uid, clp->servername);
591 rpc_clnt->cl_auth = auth;
592 *clnt_return = rpc_clnt;
597 if (sec.cred != GSS_C_NO_CREDENTIAL)
598 gss_release_cred(&min_stat, &sec.cred);
599 if (a != NULL) freeaddrinfo(a);
600 /* Restore euid to original value */
601 if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
602 printerr(0, "WARNING: Failed to restore fsuid"
603 " to uid %d from %d\n", save_uid, uid);
608 /* Only destroy here if failure. Otherwise, caller is responsible */
609 if (rpc_clnt) clnt_destroy(rpc_clnt);
616 int do_negotiation(struct lustre_gss_data *lgd,
617 gss_buffer_desc *gss_token,
618 struct lustre_gss_init_res *gr,
621 char *file = "/proc/fs/lustre/gss/init_channel";
622 struct lgssd_ioctl_param param;
629 pw = getpwuid(lgd->lgd_uid);
631 printerr(0, "no uid %u in local user database\n",
636 param.version = GSSD_INTERFACE_VERSION;
637 param.uuid = lgd->lgd_uuid;
638 param.lustre_svc = lgd->lgd_lustre_svc;
639 param.uid = lgd->lgd_uid;
640 param.gid = pw->pw_gid;
641 param.send_token_size = gss_token->length;
642 param.send_token = (char *) gss_token->value;
643 param.reply_buf_size = sizeof(outbuf);
644 param.reply_buf = outbuf;
646 fd = open(file, O_RDWR);
648 printerr(0, "can't open file %s\n", file);
652 ret = write(fd, ¶m, sizeof(param));
654 if (ret != sizeof(param)) {
655 printerr(0, "lustre ioctl err: %d\n", strerror(errno));
661 printerr(0, "status: %d (%s)\n",
662 param.status, strerror((int)param.status));
663 if (param.status == -ETIMEDOUT) {
664 /* kernel return -ETIMEDOUT means the rpc timedout,
665 * we should notify the caller to reinitiate the
666 * gss negotiation, by return -ERESTART
668 lgd->lgd_rpc_err = -ERESTART;
669 lgd->lgd_gss_err = 0;
671 lgd->lgd_rpc_err = param.status;
672 lgd->lgd_gss_err = 0;
676 p = (unsigned int *)outbuf;
682 gr->gr_ctx.length = *p++;
683 gr->gr_ctx.value = malloc(gr->gr_ctx.length);
684 memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
685 p += (((gr->gr_ctx.length + 3) & ~3) / 4);
687 gr->gr_token.length = *p++;
688 gr->gr_token.value = malloc(gr->gr_token.length);
689 memcpy(gr->gr_token.value, p, gr->gr_token.length);
690 p += (((gr->gr_token.length + 3) & ~3) / 4);
692 printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
693 gr->gr_ctx.length, gr->gr_token.length);
699 int gssd_refresh_lgd(struct lustre_gss_data *lgd)
701 struct lustre_gss_init_res gr;
702 gss_buffer_desc *recv_tokenp, send_token;
703 OM_uint32 maj_stat, min_stat, call_stat, ret_flags;
705 /* GSS context establishment loop. */
706 memset(&gr, 0, sizeof(gr));
707 recv_tokenp = GSS_C_NO_BUFFER;
710 /* print the token we just received */
711 if (recv_tokenp != GSS_C_NO_BUFFER) {
712 printerr(3, "The received token length %d\n",
713 recv_tokenp->length);
714 print_hexl(3, recv_tokenp->value, recv_tokenp->length);
717 maj_stat = gss_init_sec_context(&min_stat,
726 NULL, /* used mech */
729 NULL); /* time rec */
731 if (recv_tokenp != GSS_C_NO_BUFFER) {
732 gss_release_buffer(&min_stat, &gr.gr_token);
733 recv_tokenp = GSS_C_NO_BUFFER;
735 if (maj_stat != GSS_S_COMPLETE &&
736 maj_stat != GSS_S_CONTINUE_NEEDED) {
737 pgsserr("gss_init_sec_context", maj_stat, min_stat,
741 if (send_token.length != 0) {
742 memset(&gr, 0, sizeof(gr));
744 /* print the token we are about to send */
745 printerr(3, "token being sent length %d\n",
747 print_hexl(3, send_token.value, send_token.length);
749 call_stat = do_negotiation(lgd, &send_token, &gr, 0);
750 gss_release_buffer(&min_stat, &send_token);
752 if (call_stat != 0 ||
753 (gr.gr_major != GSS_S_COMPLETE &&
754 gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
755 printerr(0, "call stat %d, major stat 0x%x\n",
756 (int)call_stat, gr.gr_major);
760 if (gr.gr_ctx.length != 0) {
761 if (lgd->lgd_rmt_ctx.value)
762 gss_release_buffer(&min_stat,
764 lgd->lgd_rmt_ctx = gr.gr_ctx;
766 if (gr.gr_token.length != 0) {
767 if (maj_stat != GSS_S_CONTINUE_NEEDED)
769 recv_tokenp = &gr.gr_token;
773 /* GSS_S_COMPLETE => check gss header verifier,
774 * usually checked in gss_validate
776 if (maj_stat == GSS_S_COMPLETE) {
777 lgd->lgd_established = 1;
778 lgd->lgd_seq_win = gr.gr_win;
782 /* End context negotiation loop. */
783 if (!lgd->lgd_established) {
784 if (gr.gr_token.length != 0)
785 gss_release_buffer(&min_stat, &gr.gr_token);
787 printerr(0, "context negotiation failed\n");
791 printerr(2, "successfully refreshed lgd\n");
796 int gssd_create_lgd(struct clnt_info *clp,
797 struct lustre_gss_data *lgd,
798 struct lgssd_upcall_data *updata,
801 gss_buffer_desc sname;
802 OM_uint32 maj_stat, min_stat;
805 lgd->lgd_established = 0;
806 lgd->lgd_lustre_svc = updata->svc;
807 lgd->lgd_uid = updata->uid;
808 lgd->lgd_uuid = updata->obd;
812 lgd->lgd_mech = (gss_OID) &krb5oid;
813 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
816 lgd->lgd_mech = (gss_OID) &spkm3oid;
817 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
818 * Need a way to switch....
820 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
823 printerr(0, "Invalid authentication type (%d)\n", authtype);
827 lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
828 lgd->lgd_ctx = GSS_C_NO_CONTEXT;
829 lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
830 lgd->lgd_seq_win = 0;
832 sname.value = clp->servicename;
833 sname.length = strlen(clp->servicename);
835 maj_stat = gss_import_name(&min_stat, &sname,
836 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
838 if (maj_stat != GSS_S_COMPLETE) {
839 pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
843 retval = gssd_refresh_lgd(lgd);
845 if (lgd->lgd_name != GSS_C_NO_NAME)
846 gss_release_name(&min_stat, &lgd->lgd_name);
848 if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
849 gss_release_cred(&min_stat, &lgd->lgd_cred);
856 void gssd_free_lgd(struct lustre_gss_data *lgd)
858 gss_buffer_t token = GSS_C_NO_BUFFER;
859 OM_uint32 maj_stat, min_stat;
861 if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
864 maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
868 int construct_service_name(struct clnt_info *clp,
869 struct lgssd_upcall_data *ud)
871 const int buflen = 256;
874 if (clp->servicename) {
875 free(clp->servicename);
876 clp->servicename = NULL;
879 if (ptl_nid2hostname(ud->nid, name, buflen))
882 clp->servicename = malloc(32 + strlen(name));
883 if (!clp->servicename) {
884 printerr(0, "can't alloc memory\n");
887 sprintf(clp->servicename, "%s@%s",
888 ud->svc == LUSTRE_GSS_SVC_MDS ?
889 GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
891 printerr(2, "constructed servicename: %s\n", clp->servicename);
896 * this code uses the userland rpcsec gss library to create a krb5
897 * context on behalf of the kernel
900 handle_krb5_upcall(struct clnt_info *clp)
902 gss_buffer_desc token;
903 struct lgssd_upcall_data updata;
904 struct lustre_gss_data lgd;
905 char **credlist = NULL;
908 printerr(2, "handling krb5 upcall\n");
910 lgd.lgd_rpc_err = -EPERM; /* default error code */
915 if (read(clp->krb5_fd, &updata, sizeof(updata)) != sizeof(updata)) {
916 printerr(0, "WARNING: failed reading from krb5 "
917 "upcall pipe: %s\n", strerror(errno));
921 printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
922 updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
924 if (updata.svc != LUSTRE_GSS_SVC_MDS &&
925 updata.svc != LUSTRE_GSS_SVC_OSS) {
926 printerr(0, "invalid svc %d\n", updata.svc);
927 lgd.lgd_rpc_err = -EPROTO;
928 goto out_return_error;
930 updata.obd[sizeof(updata.obd)-1] = '\0';
932 if (construct_service_name(clp, &updata)) {
933 printerr(0, "failed to construct service name\n");
934 goto out_return_error;
937 if (updata.uid == 0) {
941 * Get a list of credential cache names and try each
942 * of them until one works or we've tried them all
944 if (gssd_get_krb5_machine_cred_list(&credlist)) {
945 printerr(0, "ERROR: Failed to obtain machine "
946 "credentials for %s\n", clp->servicename);
947 goto out_return_error;
949 for (ccname = credlist; ccname && *ccname; ccname++) {
950 gssd_setup_krb5_machine_gss_ccache(*ccname);
951 if ((gssd_create_lgd(clp, &lgd, &updata,
952 AUTHTYPE_KRB5)) == 0) {
957 printerr(2, "WARNING: Failed to create krb5 context "
958 "for user with uid %d with credentials "
959 "cache %s for service %s\n",
960 updata.uid, *ccname, clp->servicename);
962 gssd_free_krb5_machine_cred_list(credlist);
964 printerr(0, "ERROR: Failed to create krb5 context "
965 "for user with uid %d with any "
966 "credentials cache for service %s\n",
967 updata.uid, clp->servicename);
968 goto out_return_error;
972 /* Tell krb5 gss which credentials cache to use */
973 gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
975 if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
976 printerr(0, "WARNING: Failed to create krb5 context "
977 "for user with uid %d for service %s\n",
978 updata.uid, clp->servicename);
979 goto out_return_error;
983 if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
984 printerr(0, "WARNING: Failed to serialize krb5 context for "
985 "user with uid %d for service %s\n",
986 updata.uid, clp->servicename);
987 goto out_return_error;
990 printerr(1, "refreshed: %u@%s for %s\n",
991 updata.uid, updata.obd, clp->servicename);
992 do_downcall(clp->krb5_fd, &updata, &lgd, &token);
1002 do_error_downcall(clp->krb5_fd, &updata,
1003 lgd.lgd_rpc_err, lgd.lgd_gss_err);
1008 * this code uses the userland rpcsec gss library to create an spkm3
1009 * context on behalf of the kernel
1012 handle_spkm3_upcall(struct clnt_info *clp)
1016 CLIENT *rpc_clnt = NULL;
1018 struct authgss_private_data pd;
1019 gss_buffer_desc token;
1021 printerr(2, "handling spkm3 upcall\n");
1026 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1027 printerr(0, "WARNING: failed reading uid from spkm3 "
1028 "upcall pipe: %s\n", strerror(errno));
1032 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
1033 printerr(0, "WARNING: Failed to create spkm3 context for "
1034 "user with uid %d\n", uid);
1035 goto out_return_error;
1038 if (!authgss_get_private_data(auth, &pd)) {
1039 printerr(0, "WARNING: Failed to obtain authentication "
1040 "data for user with uid %d for server %s\n",
1041 uid, clp->servername);
1042 goto out_return_error;
1045 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
1046 printerr(0, "WARNING: Failed to serialize spkm3 context for "
1047 "user with uid %d for server\n",
1048 uid, clp->servername);
1049 goto out_return_error;
1052 do_downcall(clp->spkm3_fd, uid, &pd, &token);
1060 clnt_destroy(rpc_clnt);
1064 do_error_downcall(clp->spkm3_fd, uid, -1);