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>
69 #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) */
103 destroy_client(struct clnt_info *clp)
105 printerr(3, "clp %p: dirname %s, krb5fd %d\n", clp, clp->dirname, clp->krb5_fd);
106 if (clp->krb5_poll_index != -1)
107 memset(&pollarray[clp->krb5_poll_index], 0,
108 sizeof(struct pollfd));
109 if (clp->spkm3_poll_index != -1)
110 memset(&pollarray[clp->spkm3_poll_index], 0,
111 sizeof(struct pollfd));
112 if (clp->dir_fd != -1) close(clp->dir_fd);
113 if (clp->krb5_fd != -1) close(clp->krb5_fd);
114 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
115 if (clp->dirname) free(clp->dirname);
116 if (clp->servicename) free(clp->servicename);
120 static struct clnt_info *
121 insert_new_clnt(void)
123 struct clnt_info *clp = NULL;
125 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
126 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
130 clp->krb5_poll_index = -1;
131 clp->spkm3_poll_index = -1;
136 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
142 process_clnt_dir_files(struct clnt_info * clp)
147 if (clp->krb5_fd == -1) {
148 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
149 clp->krb5_fd = open(kname, O_RDWR);
151 if (clp->spkm3_fd == -1) {
152 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
153 clp->spkm3_fd = open(sname, O_RDWR);
155 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
161 get_poll_index(int *ind)
166 for (i=0; i<FD_ALLOC_BLOCK; i++) {
167 if (pollarray[i].events == 0) {
173 printerr(0, "ERROR: No pollarray slots open\n");
181 insert_clnt_poll(struct clnt_info *clp)
183 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
184 if (get_poll_index(&clp->krb5_poll_index)) {
185 printerr(0, "ERROR: Too many krb5 clients\n");
188 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
189 pollarray[clp->krb5_poll_index].events |= POLLIN;
190 printerr(2, "monitoring krb5 channel under %s\n",
194 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
195 if (get_poll_index(&clp->spkm3_poll_index)) {
196 printerr(0, "ERROR: Too many spkm3 clients\n");
199 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
200 pollarray[clp->spkm3_poll_index].events |= POLLIN;
207 process_clnt_dir(char *dir)
209 struct clnt_info * clp;
211 if (!(clp = insert_new_clnt()))
212 goto fail_destroy_client;
214 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
215 goto fail_destroy_client;
217 memcpy(clp->dirname, dir, strlen(dir));
218 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
219 printerr(0, "ERROR: can't open %s: %s\n",
220 clp->dirname, strerror(errno));
221 goto fail_destroy_client;
223 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
224 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
226 if (process_clnt_dir_files(clp))
227 goto fail_keep_client;
229 if (insert_clnt_poll(clp))
230 goto fail_destroy_client;
236 TAILQ_REMOVE(&clnt_list, clp, list);
240 /* We couldn't find some subdirectories, but we keep the client
241 * around in case we get a notification on the directory when the
242 * subdirectories are created. */
247 init_client_list(void)
249 TAILQ_INIT(&clnt_list);
250 /* Eventually plan to grow/shrink poll array: */
251 pollsize = FD_ALLOC_BLOCK;
252 pollarray = calloc(pollsize, sizeof(struct pollfd));
256 * This is run after a DNOTIFY signal, and should clear up any
257 * directories that are no longer around, and re-scan any existing
258 * directories, since the DNOTIFY could have been in there.
261 update_old_clients(struct dirent **namelist, int size)
263 struct clnt_info *clp;
267 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
269 for (i=0; i < size; i++) {
270 if (!strcmp(clp->dirname, namelist[i]->d_name)) {
276 printerr(2, "destroying client %s\n", clp->dirname);
277 saveprev = clp->list.tqe_prev;
278 TAILQ_REMOVE(&clnt_list, clp, list);
283 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
284 if (!process_clnt_dir_files(clp))
285 insert_clnt_poll(clp);
289 /* Search for a client by directory name, return 1 if found, 0 otherwise */
291 find_client(char *dirname)
293 struct clnt_info *clp;
295 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
296 if (!strcmp(clp->dirname, dirname))
301 /* Used to read (and re-read) list of clients, set up poll array. */
303 update_client_list(void)
305 char lustre_dir[PATH_MAX];
306 struct dirent lustre_dirent = { .d_name = "lustre" };
307 struct dirent *namelist[1];
311 if (chdir(pipefs_dir) < 0) {
312 printerr(0, "ERROR: can't chdir to %s: %s\n",
313 pipefs_dir, strerror(errno));
317 snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefs_dir, "lustre");
318 if (stat(lustre_dir, &statbuf) == 0) {
319 namelist[0] = &lustre_dirent;
321 printerr(2, "re-processing lustre directory\n");
325 printerr(2, "lustre directory not exist\n");
328 update_old_clients(namelist, j);
329 for (i=0; i < j; i++) {
330 if (i < FD_ALLOC_BLOCK && !find_client(namelist[i]->d_name))
331 process_clnt_dir(namelist[i]->d_name);
338 /* Context creation response. */
339 struct lustre_gss_init_res {
340 gss_buffer_desc gr_ctx; /* context handle */
341 u_int gr_major; /* major status */
342 u_int gr_minor; /* minor status */
343 u_int gr_win; /* sequence window */
344 gss_buffer_desc gr_token; /* token */
347 struct lustre_gss_data {
349 int lgd_lustre_svc; /* mds/oss */
350 int lgd_uid; /* uid */
351 char *lgd_uuid; /* client device uuid */
352 gss_name_t lgd_name; /* service name */
354 gss_OID lgd_mech; /* mech OID */
355 u_int lgd_req_flags; /* request flags */
356 gss_cred_id_t lgd_cred; /* credential */
357 gss_ctx_id_t lgd_ctx; /* session context */
358 gss_buffer_desc lgd_rmt_ctx; /* remote handle of context */
359 uint32_t lgd_seq_win; /* sequence window */
366 do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
367 struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
369 char *buf = NULL, *p = NULL, *end = NULL;
370 unsigned int timeout = 0; /* XXX decide on a reasonable value */
371 unsigned int buf_size = 0;
373 printerr(2, "doing downcall\n");
374 buf_size = sizeof(updata->seq) + sizeof(timeout) +
375 sizeof(lgd->lgd_seq_win) +
376 sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
377 sizeof(context_token->length) + context_token->length;
378 p = buf = malloc(buf_size);
379 end = buf + buf_size;
381 if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
382 /* Not setting any timeout for now: */
383 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
384 if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
385 if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
386 if (write_buffer(&p, end, context_token)) goto out_err;
388 lgssd_mutex_get(lgssd_mutex_downcall);
389 if (write(k5_fd, buf, p - buf) < p - buf) {
390 lgssd_mutex_put(lgssd_mutex_downcall);
393 lgssd_mutex_put(lgssd_mutex_downcall);
399 printerr(0, "ERROR: Failed to write downcall!\n");
404 do_error_downcall(int k5_fd, uint32_t seq, int rpc_err, int gss_err)
407 char *p = buf, *end = buf + 1024;
408 unsigned int timeout = 0;
411 printerr(1, "doing error downcall\n");
413 if (WRITE_BYTES(&p, end, seq)) goto out_err;
414 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
415 /* use seq_win = 0 to indicate an error: */
416 if (WRITE_BYTES(&p, end, zero)) goto out_err;
417 if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
418 if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
420 lgssd_mutex_get(lgssd_mutex_downcall);
421 if (write(k5_fd, buf, p - buf) < p - buf) {
422 lgssd_mutex_put(lgssd_mutex_downcall);
425 lgssd_mutex_put(lgssd_mutex_downcall);
428 printerr(0, "Failed to write error downcall!\n");
434 * Create an RPC connection and establish an authenticated
435 * gss context with a server.
437 int create_auth_rpc_client(struct clnt_info *clp,
438 CLIENT **clnt_return,
443 CLIENT *rpc_clnt = NULL;
444 struct rpc_gss_sec sec;
450 char rpc_errmsg[1024];
451 int sockp = RPC_ANYSOCK;
452 int sendsz = 32768, recvsz = 32768;
453 struct addrinfo ai_hints, *a = NULL;
457 /* Create the context as the user (not as root) */
458 save_uid = geteuid();
459 if (setfsuid(uid) != 0) {
460 printerr(0, "WARNING: Failed to setfsuid for "
461 "user with uid %d\n", uid);
464 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
467 sec.qop = GSS_C_QOP_DEFAULT;
468 sec.svc = RPCSEC_GSS_SVC_NONE;
469 sec.cred = GSS_C_NO_CREDENTIAL;
471 if (authtype == AUTHTYPE_KRB5) {
472 sec.mech = (gss_OID)&krb5oid;
473 sec.req_flags = GSS_C_MUTUAL_FLAG;
475 else if (authtype == AUTHTYPE_SPKM3) {
476 sec.mech = (gss_OID)&spkm3oid;
477 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
478 * Need a way to switch....
480 sec.req_flags = GSS_C_MUTUAL_FLAG;
483 printerr(0, "ERROR: Invalid authentication type (%d) "
484 "in create_auth_rpc_client\n", authtype);
489 if (authtype == AUTHTYPE_KRB5) {
490 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
492 * Do this before creating rpc connection since we won't need
493 * rpc connection if it fails!
495 if (limit_krb5_enctypes(&sec, uid)) {
496 printerr(1, "WARNING: Failed while limiting krb5 "
497 "encryption types for user with uid %d\n",
504 /* create an rpc connection to the nfs server */
506 printerr(2, "creating %s client for server %s\n", clp->protocol,
509 memset(&ai_hints, '\0', sizeof(ai_hints));
510 ai_hints.ai_family = PF_INET;
511 ai_hints.ai_flags |= AI_CANONNAME;
512 if ((strcmp(clp->protocol, "tcp")) == 0) {
513 ai_hints.ai_socktype = SOCK_STREAM;
514 ai_hints.ai_protocol = IPPROTO_TCP;
515 } else if ((strcmp(clp->protocol, "udp")) == 0) {
516 ai_hints.ai_socktype = SOCK_DGRAM;
517 ai_hints.ai_protocol = IPPROTO_UDP;
519 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
520 "for connection to server %s for user with uid %d",
521 clp->protocol, clp->servername, uid);
525 /* extract the service name from clp->servicename */
526 if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
527 printerr(0, "WARNING: servicename (%s) not formatted as "
528 "expected with service@host", clp->servicename);
531 if ((at_sign - clp->servicename) >= sizeof(service)) {
532 printerr(0, "WARNING: service portion of servicename (%s) "
533 "is too long!", clp->servicename);
536 strncpy(service, clp->servicename, at_sign - clp->servicename);
537 service[at_sign - clp->servicename] = '\0';
539 errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
541 printerr(0, "WARNING: Error from getaddrinfo for server "
542 "'%s': %s", clp->servername, gai_strerror(errcode));
547 printerr(0, "WARNING: No address information found for "
548 "connection to server %s for user with uid %d",
549 clp->servername, uid);
552 if (a->ai_protocol == IPPROTO_TCP) {
553 if ((rpc_clnt = clnttcp_create(
554 (struct sockaddr_in *) a->ai_addr,
555 clp->prog, clp->vers, &sockp,
556 sendsz, recvsz)) == NULL) {
557 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
558 "WARNING: can't create tcp rpc_clnt "
559 "for server %s for user with uid %d",
560 clp->servername, uid);
562 clnt_spcreateerror(rpc_errmsg));
565 } else if (a->ai_protocol == IPPROTO_UDP) {
566 const struct timeval timeout = {5, 0};
567 if ((rpc_clnt = clntudp_bufcreate(
568 (struct sockaddr_in *) a->ai_addr,
569 clp->prog, clp->vers, timeout,
570 &sockp, sendsz, recvsz)) == NULL) {
571 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
572 "WARNING: can't create udp rpc_clnt "
573 "for server %s for user with uid %d",
574 clp->servername, uid);
576 clnt_spcreateerror(rpc_errmsg));
580 /* Shouldn't happen! */
581 printerr(0, "ERROR: requested protocol '%s', but "
582 "got addrinfo with protocol %d",
583 clp->protocol, a->ai_protocol);
586 /* We're done with this */
590 printerr(2, "creating context with server %s\n", clp->servicename);
591 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
593 /* Our caller should print appropriate message */
594 printerr(2, "WARNING: Failed to create %s context for "
595 "user with uid %d for server %s\n",
596 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
597 uid, clp->servername);
602 rpc_clnt->cl_auth = auth;
603 *clnt_return = rpc_clnt;
608 if (sec.cred != GSS_C_NO_CREDENTIAL)
609 gss_release_cred(&min_stat, &sec.cred);
610 if (a != NULL) freeaddrinfo(a);
611 /* Restore euid to original value */
612 if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
613 printerr(0, "WARNING: Failed to restore fsuid"
614 " to uid %d from %d\n", save_uid, uid);
619 /* Only destroy here if failure. Otherwise, caller is responsible */
620 if (rpc_clnt) clnt_destroy(rpc_clnt);
627 int do_negotiation(struct lustre_gss_data *lgd,
628 gss_buffer_desc *gss_token,
629 struct lustre_gss_init_res *gr,
632 char *file = "/proc/fs/lustre/sptlrpc/gss/init_channel";
633 struct lgssd_ioctl_param param;
640 pw = getpwuid(lgd->lgd_uid);
642 printerr(0, "no uid %u in local user database\n",
647 param.version = GSSD_INTERFACE_VERSION;
648 param.uuid = lgd->lgd_uuid;
649 param.lustre_svc = lgd->lgd_lustre_svc;
650 param.uid = lgd->lgd_uid;
651 param.gid = pw->pw_gid;
652 param.send_token_size = gss_token->length;
653 param.send_token = (char *) gss_token->value;
654 param.reply_buf_size = sizeof(outbuf);
655 param.reply_buf = outbuf;
657 fd = open(file, O_RDWR);
659 printerr(0, "can't open file %s\n", file);
663 ret = write(fd, ¶m, sizeof(param));
665 if (ret != sizeof(param)) {
666 printerr(0, "lustre ioctl err: %d\n", strerror(errno));
672 printerr(0, "status: %d (%s)\n",
673 param.status, strerror((int)param.status));
674 if (param.status == -ETIMEDOUT) {
675 /* kernel return -ETIMEDOUT means the rpc timedout,
676 * we should notify the caller to reinitiate the
677 * gss negotiation, by return -ERESTART
679 lgd->lgd_rpc_err = -ERESTART;
680 lgd->lgd_gss_err = 0;
682 lgd->lgd_rpc_err = param.status;
683 lgd->lgd_gss_err = 0;
687 p = (unsigned int *)outbuf;
693 gr->gr_ctx.length = *p++;
694 gr->gr_ctx.value = malloc(gr->gr_ctx.length);
695 memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
696 p += (((gr->gr_ctx.length + 3) & ~3) / 4);
698 gr->gr_token.length = *p++;
699 gr->gr_token.value = malloc(gr->gr_token.length);
700 memcpy(gr->gr_token.value, p, gr->gr_token.length);
701 p += (((gr->gr_token.length + 3) & ~3) / 4);
703 printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
704 gr->gr_ctx.length, gr->gr_token.length);
710 int gssd_refresh_lgd(struct lustre_gss_data *lgd)
712 struct lustre_gss_init_res gr;
713 gss_buffer_desc *recv_tokenp, send_token;
714 OM_uint32 maj_stat, min_stat, call_stat, ret_flags;
716 /* GSS context establishment loop. */
717 memset(&gr, 0, sizeof(gr));
718 recv_tokenp = GSS_C_NO_BUFFER;
721 /* print the token we just received */
722 if (recv_tokenp != GSS_C_NO_BUFFER) {
723 printerr(3, "The received token length %d\n",
724 recv_tokenp->length);
725 print_hexl(3, recv_tokenp->value, recv_tokenp->length);
728 maj_stat = gss_init_sec_context(&min_stat,
737 NULL, /* used mech */
740 NULL); /* time rec */
742 if (recv_tokenp != GSS_C_NO_BUFFER) {
743 gss_release_buffer(&min_stat, &gr.gr_token);
744 recv_tokenp = GSS_C_NO_BUFFER;
746 if (maj_stat != GSS_S_COMPLETE &&
747 maj_stat != GSS_S_CONTINUE_NEEDED) {
748 pgsserr("gss_init_sec_context", maj_stat, min_stat,
752 if (send_token.length != 0) {
753 memset(&gr, 0, sizeof(gr));
755 /* print the token we are about to send */
756 printerr(3, "token being sent length %d\n",
758 print_hexl(3, send_token.value, send_token.length);
760 call_stat = do_negotiation(lgd, &send_token, &gr, 0);
761 gss_release_buffer(&min_stat, &send_token);
763 if (call_stat != 0 ||
764 (gr.gr_major != GSS_S_COMPLETE &&
765 gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
766 printerr(0, "call stat %d, major stat 0x%x\n",
767 (int)call_stat, gr.gr_major);
771 if (gr.gr_ctx.length != 0) {
772 if (lgd->lgd_rmt_ctx.value)
773 gss_release_buffer(&min_stat,
775 lgd->lgd_rmt_ctx = gr.gr_ctx;
777 if (gr.gr_token.length != 0) {
778 if (maj_stat != GSS_S_CONTINUE_NEEDED)
780 recv_tokenp = &gr.gr_token;
784 /* GSS_S_COMPLETE => check gss header verifier,
785 * usually checked in gss_validate
787 if (maj_stat == GSS_S_COMPLETE) {
788 lgd->lgd_established = 1;
789 lgd->lgd_seq_win = gr.gr_win;
793 /* End context negotiation loop. */
794 if (!lgd->lgd_established) {
795 if (gr.gr_token.length != 0)
796 gss_release_buffer(&min_stat, &gr.gr_token);
798 printerr(0, "context negotiation failed\n");
802 printerr(2, "successfully refreshed lgd\n");
807 int gssd_create_lgd(struct clnt_info *clp,
808 struct lustre_gss_data *lgd,
809 struct lgssd_upcall_data *updata,
812 gss_buffer_desc sname;
813 OM_uint32 maj_stat, min_stat;
816 lgd->lgd_established = 0;
817 lgd->lgd_lustre_svc = updata->svc;
818 lgd->lgd_uid = updata->uid;
819 lgd->lgd_uuid = updata->obd;
823 lgd->lgd_mech = (gss_OID) &krb5oid;
824 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
827 lgd->lgd_mech = (gss_OID) &spkm3oid;
828 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
829 * Need a way to switch....
831 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
834 printerr(0, "Invalid authentication type (%d)\n", authtype);
838 lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
839 lgd->lgd_ctx = GSS_C_NO_CONTEXT;
840 lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
841 lgd->lgd_seq_win = 0;
843 sname.value = clp->servicename;
844 sname.length = strlen(clp->servicename);
846 maj_stat = gss_import_name(&min_stat, &sname,
847 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
849 if (maj_stat != GSS_S_COMPLETE) {
850 pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
854 retval = gssd_refresh_lgd(lgd);
856 if (lgd->lgd_name != GSS_C_NO_NAME)
857 gss_release_name(&min_stat, &lgd->lgd_name);
859 if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
860 gss_release_cred(&min_stat, &lgd->lgd_cred);
867 void gssd_free_lgd(struct lustre_gss_data *lgd)
869 gss_buffer_t token = GSS_C_NO_BUFFER;
870 OM_uint32 maj_stat, min_stat;
872 if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
875 maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
879 int construct_service_name(struct clnt_info *clp,
880 struct lgssd_upcall_data *ud)
882 const int buflen = 256;
885 if (clp->servicename) {
886 free(clp->servicename);
887 clp->servicename = NULL;
890 if (lnet_nid2hostname(ud->nid, name, buflen))
893 clp->servicename = malloc(32 + strlen(name));
894 if (!clp->servicename) {
895 printerr(0, "can't alloc memory\n");
898 sprintf(clp->servicename, "%s@%s",
899 ud->svc == LUSTRE_GSS_SVC_MDS ?
900 GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
902 printerr(2, "constructed servicename: %s\n", clp->servicename);
907 * this code uses the userland rpcsec gss library to create a krb5
908 * context on behalf of the kernel
911 handle_krb5_upcall(struct clnt_info *clp)
914 gss_buffer_desc token = { 0, NULL };
915 struct lgssd_upcall_data updata;
916 struct lustre_gss_data lgd;
917 char **credlist = NULL;
921 printerr(2, "handling krb5 upcall\n");
923 memset(&lgd, 0, sizeof(lgd));
924 lgd.lgd_rpc_err = -EPERM; /* default error code */
926 read_rc = read(clp->krb5_fd, &updata, sizeof(updata));
928 printerr(0, "WARNING: failed reading from krb5 "
929 "upcall pipe: %s\n", strerror(errno));
931 } else if (read_rc != sizeof(updata)) {
932 printerr(0, "upcall data mismatch: length %d, expect %d\n",
933 read_rc, sizeof(updata));
935 /* the sequence number must be the first field. if read >= 4
936 * bytes then we know at least sequence is fine, try to send
937 * error notification nicely.
940 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
944 /* FIXME temporary fix, do this before fork.
945 * in case of errors could have memory leak!!!
947 if (updata.uid == 0) {
948 if (gssd_get_krb5_machine_cred_list(&credlist)) {
949 printerr(0, "ERROR: Failed to obtain machine "
951 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
956 /* fork child process */
959 printerr(0, "can't fork: %s\n", strerror(errno));
960 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
962 } else if (pid > 0) {
963 printerr(2, "forked child process: %d\n", pid);
967 printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
968 updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
970 if (updata.svc != LUSTRE_GSS_SVC_MDS &&
971 updata.svc != LUSTRE_GSS_SVC_OSS) {
972 printerr(0, "invalid svc %d\n", updata.svc);
973 lgd.lgd_rpc_err = -EPROTO;
974 goto out_return_error;
976 updata.obd[sizeof(updata.obd)-1] = '\0';
978 if (construct_service_name(clp, &updata)) {
979 printerr(0, "failed to construct service name\n");
980 goto out_return_error;
983 if (updata.uid == 0) {
987 * Get a list of credential cache names and try each
988 * of them until one works or we've tried them all
991 if (gssd_get_krb5_machine_cred_list(&credlist)) {
992 printerr(0, "ERROR: Failed to obtain machine "
993 "credentials for %s\n", clp->servicename);
994 goto out_return_error;
997 for (ccname = credlist; ccname && *ccname; ccname++) {
998 gssd_setup_krb5_machine_gss_ccache(*ccname);
999 if ((gssd_create_lgd(clp, &lgd, &updata,
1000 AUTHTYPE_KRB5)) == 0) {
1005 printerr(2, "WARNING: Failed to create krb5 context "
1006 "for user with uid %d with credentials "
1007 "cache %s for service %s\n",
1008 updata.uid, *ccname, clp->servicename);
1010 gssd_free_krb5_machine_cred_list(credlist);
1012 printerr(0, "ERROR: Failed to create krb5 context "
1013 "for user with uid %d with any "
1014 "credentials cache for service %s\n",
1015 updata.uid, clp->servicename);
1016 goto out_return_error;
1020 /* Tell krb5 gss which credentials cache to use */
1021 gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
1023 if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
1024 printerr(0, "WARNING: Failed to create krb5 context "
1025 "for user with uid %d for service %s\n",
1026 updata.uid, clp->servicename);
1027 goto out_return_error;
1031 if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
1032 printerr(0, "WARNING: Failed to serialize krb5 context for "
1033 "user with uid %d for service %s\n",
1034 updata.uid, clp->servicename);
1035 goto out_return_error;
1038 printerr(1, "refreshed: %u@%s for %s\n",
1039 updata.uid, updata.obd, clp->servicename);
1040 do_downcall(clp->krb5_fd, &updata, &lgd, &token);
1046 gssd_free_lgd(&lgd);
1047 exit(0); /* i'm child process */
1050 do_error_downcall(clp->krb5_fd, updata.seq,
1051 lgd.lgd_rpc_err, lgd.lgd_gss_err);
1056 * this code uses the userland rpcsec gss library to create an spkm3
1057 * context on behalf of the kernel
1060 handle_spkm3_upcall(struct clnt_info *clp)
1064 CLIENT *rpc_clnt = NULL;
1066 struct authgss_private_data pd;
1067 gss_buffer_desc token;
1069 printerr(2, "handling spkm3 upcall\n");
1074 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1075 printerr(0, "WARNING: failed reading uid from spkm3 "
1076 "upcall pipe: %s\n", strerror(errno));
1080 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
1081 printerr(0, "WARNING: Failed to create spkm3 context for "
1082 "user with uid %d\n", uid);
1083 goto out_return_error;
1086 if (!authgss_get_private_data(auth, &pd)) {
1087 printerr(0, "WARNING: Failed to obtain authentication "
1088 "data for user with uid %d for server %s\n",
1089 uid, clp->servername);
1090 goto out_return_error;
1093 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
1094 printerr(0, "WARNING: Failed to serialize spkm3 context for "
1095 "user with uid %d for server\n",
1096 uid, clp->servername);
1097 goto out_return_error;
1100 do_downcall(clp->spkm3_fd, uid, &pd, &token);
1108 clnt_destroy(rpc_clnt);
1112 do_error_downcall(clp->spkm3_fd, uid, -1);