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>
49 #include <sys/fsuid.h>
62 #include <gssapi/gssapi.h>
67 #include <libcfs/util/param.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 destroy_client(struct clnt_info *clp)
109 printerr(3, "clp %p: dirname %s, krb5fd %d\n", clp, clp->dirname, clp->krb5_fd);
110 if (clp->krb5_poll_index != -1)
111 memset(&pollarray[clp->krb5_poll_index], 0,
112 sizeof(struct pollfd));
113 if (clp->spkm3_poll_index != -1)
114 memset(&pollarray[clp->spkm3_poll_index], 0,
115 sizeof(struct pollfd));
116 if (clp->dir_fd != -1) close(clp->dir_fd);
117 if (clp->krb5_fd != -1) close(clp->krb5_fd);
118 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
119 if (clp->dirname) free(clp->dirname);
120 if (clp->servicename) free(clp->servicename);
124 static struct clnt_info *
125 insert_new_clnt(void)
127 struct clnt_info *clp = NULL;
129 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
130 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
134 clp->krb5_poll_index = -1;
135 clp->spkm3_poll_index = -1;
140 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
146 process_clnt_dir_files(struct clnt_info * clp)
151 if (clp->krb5_fd == -1) {
152 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
153 clp->krb5_fd = open(kname, O_RDWR);
155 if (clp->spkm3_fd == -1) {
156 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
157 clp->spkm3_fd = open(sname, O_RDWR);
159 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
165 get_poll_index(int *ind)
170 for (i=0; i<FD_ALLOC_BLOCK; i++) {
171 if (pollarray[i].events == 0) {
177 printerr(0, "ERROR: No pollarray slots open\n");
185 insert_clnt_poll(struct clnt_info *clp)
187 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
188 if (get_poll_index(&clp->krb5_poll_index)) {
189 printerr(0, "ERROR: Too many krb5 clients\n");
192 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
193 pollarray[clp->krb5_poll_index].events |= POLLIN;
194 printerr(2, "monitoring krb5 channel under %s\n",
198 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
199 if (get_poll_index(&clp->spkm3_poll_index)) {
200 printerr(0, "ERROR: Too many spkm3 clients\n");
203 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
204 pollarray[clp->spkm3_poll_index].events |= POLLIN;
211 process_clnt_dir(char *dir)
213 struct clnt_info * clp;
215 if (!(clp = insert_new_clnt()))
216 goto fail_destroy_client;
218 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
219 goto fail_destroy_client;
221 memcpy(clp->dirname, dir, strlen(dir));
222 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
223 printerr(0, "ERROR: can't open %s: %s\n",
224 clp->dirname, strerror(errno));
225 goto fail_destroy_client;
227 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
228 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
230 if (process_clnt_dir_files(clp))
231 goto fail_keep_client;
233 if (insert_clnt_poll(clp))
234 goto fail_destroy_client;
240 TAILQ_REMOVE(&clnt_list, clp, list);
244 /* We couldn't find some subdirectories, but we keep the client
245 * around in case we get a notification on the directory when the
246 * subdirectories are created. */
251 init_client_list(void)
253 TAILQ_INIT(&clnt_list);
254 /* Eventually plan to grow/shrink poll array: */
255 pollsize = FD_ALLOC_BLOCK;
256 pollarray = calloc(pollsize, sizeof(struct pollfd));
260 * This is run after a DNOTIFY signal, and should clear up any
261 * directories that are no longer around, and re-scan any existing
262 * directories, since the DNOTIFY could have been in there.
265 update_old_clients(struct dirent **namelist, int size)
267 struct clnt_info *clp;
271 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
273 for (i=0; i < size; i++) {
274 if (!strcmp(clp->dirname, namelist[i]->d_name)) {
280 printerr(2, "destroying client %s\n", clp->dirname);
281 saveprev = clp->list.tqe_prev;
282 TAILQ_REMOVE(&clnt_list, clp, list);
287 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
288 if (!process_clnt_dir_files(clp))
289 insert_clnt_poll(clp);
293 /* Search for a client by directory name, return 1 if found, 0 otherwise */
295 find_client(char *dirname)
297 struct clnt_info *clp;
299 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
300 if (!strcmp(clp->dirname, dirname))
305 /* Used to read (and re-read) list of clients, set up poll array. */
307 update_client_list(void)
309 char lustre_dir[PATH_MAX];
310 struct dirent lustre_dirent = { .d_name = "lustre" };
311 struct dirent *namelist[1];
315 if (chdir(pipefs_dir) < 0) {
316 printerr(0, "ERROR: can't chdir to %s: %s\n",
317 pipefs_dir, strerror(errno));
321 snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefs_dir, "lustre");
322 if (stat(lustre_dir, &statbuf) == 0) {
323 namelist[0] = &lustre_dirent;
325 printerr(2, "re-processing lustre directory\n");
329 printerr(2, "lustre directory not exist\n");
332 update_old_clients(namelist, j);
333 for (i=0; i < j; i++) {
334 if (i < FD_ALLOC_BLOCK && !find_client(namelist[i]->d_name))
335 process_clnt_dir(namelist[i]->d_name);
342 /* Context creation response. */
343 struct lustre_gss_init_res {
344 gss_buffer_desc gr_ctx; /* context handle */
345 unsigned int gr_major; /* major status */
346 unsigned int gr_minor; /* minor status */
347 unsigned int gr_win; /* sequence window */
348 gss_buffer_desc gr_token; /* token */
351 struct lustre_gss_data {
353 int lgd_lustre_svc; /* mds/oss */
354 int lgd_uid; /* uid */
355 char *lgd_uuid; /* client device uuid */
356 gss_name_t lgd_name; /* service name */
358 gss_OID lgd_mech; /* mech OID */
359 unsigned int lgd_req_flags; /* request flags */
360 gss_cred_id_t lgd_cred; /* credential */
361 gss_ctx_id_t lgd_ctx; /* session context */
362 gss_buffer_desc lgd_rmt_ctx; /* remote handle of context */
363 uint32_t lgd_seq_win; /* sequence window */
370 do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
371 struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
373 char *buf = NULL, *p = NULL, *end = NULL;
374 unsigned int timeout = 0; /* XXX decide on a reasonable value */
375 unsigned int buf_size = 0;
377 printerr(2, "doing downcall\n");
378 buf_size = sizeof(updata->seq) + sizeof(timeout) +
379 sizeof(lgd->lgd_seq_win) +
380 sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
381 sizeof(context_token->length) + context_token->length;
382 p = buf = malloc(buf_size);
383 end = buf + buf_size;
385 if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
386 /* Not setting any timeout for now: */
387 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
388 if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
389 if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
390 if (write_buffer(&p, end, context_token)) goto out_err;
392 lgssd_mutex_get(lgssd_mutex_downcall);
393 if (write(k5_fd, buf, p - buf) < p - buf) {
394 lgssd_mutex_put(lgssd_mutex_downcall);
397 lgssd_mutex_put(lgssd_mutex_downcall);
403 printerr(0, "ERROR: Failed to write downcall!\n");
408 do_error_downcall(int k5_fd, uint32_t seq, int rpc_err, int gss_err)
411 char *p = buf, *end = buf + 1024;
412 unsigned int timeout = 0;
415 printerr(1, "doing error downcall\n");
417 if (WRITE_BYTES(&p, end, seq)) goto out_err;
418 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
419 /* use seq_win = 0 to indicate an error: */
420 if (WRITE_BYTES(&p, end, zero)) goto out_err;
421 if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
422 if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
424 lgssd_mutex_get(lgssd_mutex_downcall);
425 if (write(k5_fd, buf, p - buf) < p - buf) {
426 lgssd_mutex_put(lgssd_mutex_downcall);
429 lgssd_mutex_put(lgssd_mutex_downcall);
432 printerr(0, "Failed to write error downcall!\n");
438 * Create an RPC connection and establish an authenticated
439 * gss context with a server.
441 int create_auth_rpc_client(struct clnt_info *clp,
442 CLIENT **clnt_return,
447 CLIENT *rpc_clnt = NULL;
448 struct rpc_gss_sec sec;
454 char rpc_errmsg[1024];
455 int sockp = RPC_ANYSOCK;
456 int sendsz = 32768, recvsz = 32768;
457 struct addrinfo ai_hints, *a = NULL;
461 /* Create the context as the user (not as root) */
462 save_uid = geteuid();
463 if (setfsuid(uid) != 0) {
464 printerr(0, "WARNING: Failed to setfsuid for "
465 "user with uid %d\n", uid);
468 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
471 sec.qop = GSS_C_QOP_DEFAULT;
472 sec.svc = RPCSEC_GSS_SVC_NONE;
473 sec.cred = GSS_C_NO_CREDENTIAL;
475 if (authtype == AUTHTYPE_KRB5) {
476 sec.mech = (gss_OID)&krb5oid;
477 sec.req_flags = GSS_C_MUTUAL_FLAG;
479 else if (authtype == AUTHTYPE_SPKM3) {
480 sec.mech = (gss_OID)&spkm3oid;
481 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
482 * Need a way to switch....
484 sec.req_flags = GSS_C_MUTUAL_FLAG;
487 printerr(0, "ERROR: Invalid authentication type (%d) "
488 "in create_auth_rpc_client\n", authtype);
493 if (authtype == AUTHTYPE_KRB5) {
494 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
496 * Do this before creating rpc connection since we won't need
497 * rpc connection if it fails!
499 if (limit_krb5_enctypes(&sec, uid)) {
500 printerr(1, "WARNING: Failed while limiting krb5 "
501 "encryption types for user with uid %d\n",
508 /* create an rpc connection to the nfs server */
510 printerr(2, "creating %s client for server %s\n", clp->protocol,
513 memset(&ai_hints, '\0', sizeof(ai_hints));
514 ai_hints.ai_family = PF_INET;
515 ai_hints.ai_flags |= AI_CANONNAME;
516 if ((strcmp(clp->protocol, "tcp")) == 0) {
517 ai_hints.ai_socktype = SOCK_STREAM;
518 ai_hints.ai_protocol = IPPROTO_TCP;
519 } else if ((strcmp(clp->protocol, "udp")) == 0) {
520 ai_hints.ai_socktype = SOCK_DGRAM;
521 ai_hints.ai_protocol = IPPROTO_UDP;
523 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
524 "for connection to server %s for user with uid %d",
525 clp->protocol, clp->servername, uid);
529 /* extract the service name from clp->servicename */
530 if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
531 printerr(0, "WARNING: servicename (%s) not formatted as "
532 "expected with service@host", clp->servicename);
535 if ((at_sign - clp->servicename) >= sizeof(service)) {
536 printerr(0, "WARNING: service portion of servicename (%s) "
537 "is too long!", clp->servicename);
540 strncpy(service, clp->servicename, at_sign - clp->servicename);
541 service[at_sign - clp->servicename] = '\0';
543 errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
545 printerr(0, "WARNING: Error from getaddrinfo for server "
546 "'%s': %s", clp->servername, gai_strerror(errcode));
551 printerr(0, "WARNING: No address information found for "
552 "connection to server %s for user with uid %d",
553 clp->servername, uid);
556 if (a->ai_protocol == IPPROTO_TCP) {
557 if ((rpc_clnt = clnttcp_create(
558 (struct sockaddr_in *) a->ai_addr,
559 clp->prog, clp->vers, &sockp,
560 sendsz, recvsz)) == NULL) {
561 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
562 "WARNING: can't create tcp rpc_clnt "
563 "for server %s for user with uid %d",
564 clp->servername, uid);
566 clnt_spcreateerror(rpc_errmsg));
569 } else if (a->ai_protocol == IPPROTO_UDP) {
570 const struct timeval timeout = {5, 0};
571 if ((rpc_clnt = clntudp_bufcreate(
572 (struct sockaddr_in *) a->ai_addr,
573 clp->prog, clp->vers, timeout,
574 &sockp, sendsz, recvsz)) == NULL) {
575 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
576 "WARNING: can't create udp rpc_clnt "
577 "for server %s for user with uid %d",
578 clp->servername, uid);
580 clnt_spcreateerror(rpc_errmsg));
584 /* Shouldn't happen! */
585 printerr(0, "ERROR: requested protocol '%s', but "
586 "got addrinfo with protocol %d",
587 clp->protocol, a->ai_protocol);
590 /* We're done with this */
594 printerr(2, "creating context with server %s\n", clp->servicename);
595 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
597 /* Our caller should print appropriate message */
598 printerr(2, "WARNING: Failed to create %s context for "
599 "user with uid %d for server %s\n",
600 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
601 uid, clp->servername);
606 rpc_clnt->cl_auth = auth;
607 *clnt_return = rpc_clnt;
612 if (sec.cred != GSS_C_NO_CREDENTIAL)
613 gss_release_cred(&min_stat, &sec.cred);
614 if (a != NULL) freeaddrinfo(a);
615 /* Restore euid to original value */
616 if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
617 printerr(0, "WARNING: Failed to restore fsuid"
618 " to uid %d from %d\n", save_uid, uid);
623 /* Only destroy here if failure. Otherwise, caller is responsible */
624 if (rpc_clnt) clnt_destroy(rpc_clnt);
631 int do_negotiation(struct lustre_gss_data *lgd,
632 gss_buffer_desc *gss_token,
633 struct lustre_gss_init_res *gr,
636 struct lgssd_ioctl_param param;
644 pw = getpwuid(lgd->lgd_uid);
646 printerr(0, "no uid %u in local user database\n",
651 param.version = GSSD_INTERFACE_VERSION_V1;
652 param.uuid = lgd->lgd_uuid;
653 param.lustre_svc = lgd->lgd_lustre_svc;
654 param.uid = lgd->lgd_uid;
655 param.gid = pw->pw_gid;
656 param.send_token_size = gss_token->length;
657 param.send_token = (char *) gss_token->value;
658 param.reply_buf_size = sizeof(outbuf);
659 param.reply_buf = outbuf;
661 if (cfs_get_param_paths(&path, "sptlrpc/gss/init_channel") != 0)
664 fd = open(path.gl_pathv[0], O_RDWR);
666 printerr(0, "can't open file %s\n", path.gl_pathv[0]);
671 rc = write(fd, ¶m, sizeof(param));
672 if (rc != sizeof(param)) {
673 printerr(0, "lustre ioctl err: %d\n", strerror(errno));
678 printerr(0, "status: %d (%s)\n",
679 param.status, strerror((int)param.status));
680 if (param.status == -ETIMEDOUT) {
681 /* kernel return -ETIMEDOUT means the rpc timedout,
682 * we should notify the caller to reinitiate the
683 * gss negotiation, by return -ERESTART
685 lgd->lgd_rpc_err = -ERESTART;
686 lgd->lgd_gss_err = 0;
688 lgd->lgd_rpc_err = param.status;
689 lgd->lgd_gss_err = 0;
694 p = (unsigned int *)outbuf;
699 gr->gr_ctx.length = *p++;
700 gr->gr_ctx.value = malloc(gr->gr_ctx.length);
701 memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
702 p += (((gr->gr_ctx.length + 3) & ~3) / 4);
704 gr->gr_token.length = *p++;
705 gr->gr_token.value = malloc(gr->gr_token.length);
706 memcpy(gr->gr_token.value, p, gr->gr_token.length);
707 p += (((gr->gr_token.length + 3) & ~3) / 4);
709 printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
710 gr->gr_ctx.length, gr->gr_token.length);
715 cfs_free_param_data(&path);
720 int gssd_refresh_lgd(struct lustre_gss_data *lgd)
722 struct lustre_gss_init_res gr;
723 gss_buffer_desc *recv_tokenp, send_token;
724 OM_uint32 maj_stat, min_stat, call_stat, ret_flags;
726 /* GSS context establishment loop. */
727 memset(&gr, 0, sizeof(gr));
728 recv_tokenp = GSS_C_NO_BUFFER;
731 /* print the token we just received */
732 if (recv_tokenp != GSS_C_NO_BUFFER) {
733 printerr(3, "The received token length %d\n",
734 recv_tokenp->length);
735 print_hexl(3, recv_tokenp->value, recv_tokenp->length);
738 maj_stat = gss_init_sec_context(&min_stat,
747 NULL, /* used mech */
750 NULL); /* time rec */
752 if (recv_tokenp != GSS_C_NO_BUFFER) {
753 gss_release_buffer(&min_stat, &gr.gr_token);
754 recv_tokenp = GSS_C_NO_BUFFER;
756 if (maj_stat != GSS_S_COMPLETE &&
757 maj_stat != GSS_S_CONTINUE_NEEDED) {
758 pgsserr("gss_init_sec_context", maj_stat, min_stat,
762 if (send_token.length != 0) {
763 memset(&gr, 0, sizeof(gr));
765 /* print the token we are about to send */
766 printerr(3, "token being sent length %d\n",
768 print_hexl(3, send_token.value, send_token.length);
770 call_stat = do_negotiation(lgd, &send_token, &gr, 0);
771 gss_release_buffer(&min_stat, &send_token);
773 if (call_stat != 0 ||
774 (gr.gr_major != GSS_S_COMPLETE &&
775 gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
776 printerr(0, "call stat %d, major stat 0x%x\n",
777 (int)call_stat, gr.gr_major);
781 if (gr.gr_ctx.length != 0) {
782 if (lgd->lgd_rmt_ctx.value)
783 gss_release_buffer(&min_stat,
785 lgd->lgd_rmt_ctx = gr.gr_ctx;
787 if (gr.gr_token.length != 0) {
788 if (maj_stat != GSS_S_CONTINUE_NEEDED)
790 recv_tokenp = &gr.gr_token;
794 /* GSS_S_COMPLETE => check gss header verifier,
795 * usually checked in gss_validate
797 if (maj_stat == GSS_S_COMPLETE) {
798 lgd->lgd_established = 1;
799 lgd->lgd_seq_win = gr.gr_win;
803 /* End context negotiation loop. */
804 if (!lgd->lgd_established) {
805 if (gr.gr_token.length != 0)
806 gss_release_buffer(&min_stat, &gr.gr_token);
808 printerr(0, "context negotiation failed\n");
812 printerr(2, "successfully refreshed lgd\n");
817 int gssd_create_lgd(struct clnt_info *clp,
818 struct lustre_gss_data *lgd,
819 struct lgssd_upcall_data *updata,
822 gss_buffer_desc sname;
823 OM_uint32 maj_stat, min_stat;
826 lgd->lgd_established = 0;
827 lgd->lgd_lustre_svc = updata->svc;
828 lgd->lgd_uid = updata->uid;
829 lgd->lgd_uuid = updata->obd;
833 lgd->lgd_mech = (gss_OID) &krb5oid;
834 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
837 lgd->lgd_mech = (gss_OID) &spkm3oid;
838 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
839 * Need a way to switch....
841 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
844 printerr(0, "Invalid authentication type (%d)\n", authtype);
848 lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
849 lgd->lgd_ctx = GSS_C_NO_CONTEXT;
850 lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
851 lgd->lgd_seq_win = 0;
853 sname.value = clp->servicename;
854 sname.length = strlen(clp->servicename);
856 maj_stat = gss_import_name(&min_stat, &sname,
857 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
859 if (maj_stat != GSS_S_COMPLETE) {
860 pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
864 retval = gssd_refresh_lgd(lgd);
866 if (lgd->lgd_name != GSS_C_NO_NAME)
867 gss_release_name(&min_stat, &lgd->lgd_name);
869 if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
870 gss_release_cred(&min_stat, &lgd->lgd_cred);
877 void gssd_free_lgd(struct lustre_gss_data *lgd)
879 gss_buffer_t token = GSS_C_NO_BUFFER;
880 OM_uint32 maj_stat, min_stat;
882 if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
885 maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
889 int construct_service_name(struct clnt_info *clp,
890 struct lgssd_upcall_data *ud)
892 const int buflen = 256;
895 if (clp->servicename) {
896 free(clp->servicename);
897 clp->servicename = NULL;
900 if (lnet_nid2hostname(ud->nid, name, buflen))
903 clp->servicename = malloc(32 + strlen(name));
904 if (!clp->servicename) {
905 printerr(0, "can't alloc memory\n");
908 sprintf(clp->servicename, "%s@%s",
909 ud->svc == LUSTRE_GSS_SVC_MDS ?
910 GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
912 printerr(2, "constructed servicename: %s\n", clp->servicename);
917 * this code uses the userland rpcsec gss library to create a krb5
918 * context on behalf of the kernel
921 handle_krb5_upcall(struct clnt_info *clp)
924 gss_buffer_desc token = { 0, NULL };
925 struct lgssd_upcall_data updata;
926 struct lustre_gss_data lgd;
927 char **credlist = NULL;
931 printerr(2, "handling krb5 upcall\n");
933 memset(&lgd, 0, sizeof(lgd));
934 lgd.lgd_rpc_err = -EPERM; /* default error code */
936 read_rc = read(clp->krb5_fd, &updata, sizeof(updata));
938 printerr(0, "WARNING: failed reading from krb5 "
939 "upcall pipe: %s\n", strerror(errno));
941 } else if (read_rc != sizeof(updata)) {
942 printerr(0, "upcall data mismatch: length %d, expect %d\n",
943 read_rc, sizeof(updata));
945 /* the sequence number must be the first field. if read >= 4
946 * bytes then we know at least sequence is fine, try to send
947 * error notification nicely.
950 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
954 /* FIXME temporary fix, do this before fork.
955 * in case of errors could have memory leak!!!
957 if (updata.uid == 0) {
958 if (gssd_get_krb5_machine_cred_list(&credlist)) {
959 printerr(0, "ERROR: Failed to obtain machine "
961 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
966 /* fork child process */
969 printerr(0, "can't fork: %s\n", strerror(errno));
970 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
972 } else if (pid > 0) {
973 printerr(2, "forked child process: %d\n", pid);
977 printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
978 updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
980 if (updata.svc != LUSTRE_GSS_SVC_MDS &&
981 updata.svc != LUSTRE_GSS_SVC_OSS) {
982 printerr(0, "invalid svc %d\n", updata.svc);
983 lgd.lgd_rpc_err = -EPROTO;
984 goto out_return_error;
986 updata.obd[sizeof(updata.obd)-1] = '\0';
988 if (construct_service_name(clp, &updata)) {
989 printerr(0, "failed to construct service name\n");
990 goto out_return_error;
993 if (updata.uid == 0) {
997 * Get a list of credential cache names and try each
998 * of them until one works or we've tried them all
1001 if (gssd_get_krb5_machine_cred_list(&credlist)) {
1002 printerr(0, "ERROR: Failed to obtain machine "
1003 "credentials for %s\n", clp->servicename);
1004 goto out_return_error;
1007 for (ccname = credlist; ccname && *ccname; ccname++) {
1008 gssd_setup_krb5_machine_gss_ccache(*ccname);
1009 if ((gssd_create_lgd(clp, &lgd, &updata,
1010 AUTHTYPE_KRB5)) == 0) {
1015 printerr(2, "WARNING: Failed to create krb5 context "
1016 "for user with uid %d with credentials "
1017 "cache %s for service %s\n",
1018 updata.uid, *ccname, clp->servicename);
1020 gssd_free_krb5_machine_cred_list(credlist);
1022 printerr(0, "ERROR: Failed to create krb5 context "
1023 "for user with uid %d with any "
1024 "credentials cache for service %s\n",
1025 updata.uid, clp->servicename);
1026 goto out_return_error;
1030 /* Tell krb5 gss which credentials cache to use */
1031 gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
1033 if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
1034 printerr(0, "WARNING: Failed to create krb5 context "
1035 "for user with uid %d for service %s\n",
1036 updata.uid, clp->servicename);
1037 goto out_return_error;
1041 if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
1042 printerr(0, "WARNING: Failed to serialize krb5 context for "
1043 "user with uid %d for service %s\n",
1044 updata.uid, clp->servicename);
1045 goto out_return_error;
1048 printerr(1, "refreshed: %u@%s for %s\n",
1049 updata.uid, updata.obd, clp->servicename);
1050 do_downcall(clp->krb5_fd, &updata, &lgd, &token);
1056 gssd_free_lgd(&lgd);
1057 exit(0); /* i'm child process */
1060 do_error_downcall(clp->krb5_fd, updata.seq,
1061 lgd.lgd_rpc_err, lgd.lgd_gss_err);
1066 * this code uses the userland rpcsec gss library to create an spkm3
1067 * context on behalf of the kernel
1070 handle_spkm3_upcall(struct clnt_info *clp)
1074 CLIENT *rpc_clnt = NULL;
1076 struct authgss_private_data pd;
1077 gss_buffer_desc token;
1079 printerr(2, "handling spkm3 upcall\n");
1084 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1085 printerr(0, "WARNING: failed reading uid from spkm3 "
1086 "upcall pipe: %s\n", strerror(errno));
1090 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
1091 printerr(0, "WARNING: Failed to create spkm3 context for "
1092 "user with uid %d\n", uid);
1093 goto out_return_error;
1096 if (!authgss_get_private_data(auth, &pd)) {
1097 printerr(0, "WARNING: Failed to obtain authentication "
1098 "data for user with uid %d for server %s\n",
1099 uid, clp->servername);
1100 goto out_return_error;
1103 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
1104 printerr(0, "WARNING: Failed to serialize spkm3 context for "
1105 "user with uid %d for server\n",
1106 uid, clp->servername);
1107 goto out_return_error;
1110 do_downcall(clp->spkm3_fd, uid, &pd, &token);
1118 clnt_destroy(rpc_clnt);
1122 do_error_downcall(clp->spkm3_fd, uid, -1);