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>
71 #include "krb5_util.h"
77 * array of struct pollfd suitable to pass to poll. initialized to
78 * zero - a zero struct is ignored by poll() because the events mask is 0.
81 * linked list of struct clnt_info which associates a clntXXX directory
82 * with an index into pollarray[], and other basic data about that client.
84 * Directory structure: created by the kernel nfs client
85 * {pipefs_nfsdir}/clntXX : one per rpc_clnt struct in the kernel
86 * {pipefs_nfsdir}/clntXX/krb5 : read uid for which kernel wants
87 * a context, write the resulting context
88 * {pipefs_nfsdir}/clntXX/info : stores info such as server name
91 * Poll all {pipefs_nfsdir}/clntXX/krb5 files. When ready, data read
92 * is a uid; performs rpcsec_gss context initialization protocol to
93 * get a cred for that user. Writes result to corresponding krb5 file
94 * in a form the kernel code will understand.
95 * In addition, we make sure we are notified whenever anything is
96 * created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories,
97 * and rescan the whole {pipefs_nfsdir} when this happens.
100 struct pollfd * pollarray;
102 int pollsize; /* the size of pollaray (in pollfd's) */
105 destroy_client(struct clnt_info *clp)
107 printerr(3, "clp %p: dirname %s, krb5fd %d\n", clp, clp->dirname, clp->krb5_fd);
108 if (clp->krb5_poll_index != -1)
109 memset(&pollarray[clp->krb5_poll_index], 0,
110 sizeof(struct pollfd));
111 if (clp->spkm3_poll_index != -1)
112 memset(&pollarray[clp->spkm3_poll_index], 0,
113 sizeof(struct pollfd));
114 if (clp->dir_fd != -1) close(clp->dir_fd);
115 if (clp->krb5_fd != -1) close(clp->krb5_fd);
116 if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
117 if (clp->dirname) free(clp->dirname);
118 if (clp->servicename) free(clp->servicename);
122 static struct clnt_info *
123 insert_new_clnt(void)
125 struct clnt_info *clp = NULL;
127 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
128 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
132 clp->krb5_poll_index = -1;
133 clp->spkm3_poll_index = -1;
138 TAILQ_INSERT_HEAD(&clnt_list, clp, list);
144 process_clnt_dir_files(struct clnt_info * clp)
149 if (clp->krb5_fd == -1) {
150 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
151 clp->krb5_fd = open(kname, O_RDWR);
153 if (clp->spkm3_fd == -1) {
154 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
155 clp->spkm3_fd = open(sname, O_RDWR);
157 if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
163 get_poll_index(int *ind)
168 for (i=0; i<FD_ALLOC_BLOCK; i++) {
169 if (pollarray[i].events == 0) {
175 printerr(0, "ERROR: No pollarray slots open\n");
183 insert_clnt_poll(struct clnt_info *clp)
185 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
186 if (get_poll_index(&clp->krb5_poll_index)) {
187 printerr(0, "ERROR: Too many krb5 clients\n");
190 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
191 pollarray[clp->krb5_poll_index].events |= POLLIN;
192 printerr(2, "monitoring krb5 channel under %s\n",
196 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
197 if (get_poll_index(&clp->spkm3_poll_index)) {
198 printerr(0, "ERROR: Too many spkm3 clients\n");
201 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
202 pollarray[clp->spkm3_poll_index].events |= POLLIN;
209 process_clnt_dir(char *dir)
211 struct clnt_info * clp;
213 if (!(clp = insert_new_clnt()))
214 goto fail_destroy_client;
216 if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
217 goto fail_destroy_client;
219 memcpy(clp->dirname, dir, strlen(dir));
220 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
221 printerr(0, "ERROR: can't open %s: %s\n",
222 clp->dirname, strerror(errno));
223 goto fail_destroy_client;
225 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
226 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
228 if (process_clnt_dir_files(clp))
229 goto fail_keep_client;
231 if (insert_clnt_poll(clp))
232 goto fail_destroy_client;
238 TAILQ_REMOVE(&clnt_list, clp, list);
242 /* We couldn't find some subdirectories, but we keep the client
243 * around in case we get a notification on the directory when the
244 * subdirectories are created. */
249 init_client_list(void)
251 TAILQ_INIT(&clnt_list);
252 /* Eventually plan to grow/shrink poll array: */
253 pollsize = FD_ALLOC_BLOCK;
254 pollarray = calloc(pollsize, sizeof(struct pollfd));
258 * This is run after a DNOTIFY signal, and should clear up any
259 * directories that are no longer around, and re-scan any existing
260 * directories, since the DNOTIFY could have been in there.
263 update_old_clients(struct dirent **namelist, int size)
265 struct clnt_info *clp;
269 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
271 for (i=0; i < size; i++) {
272 if (!strcmp(clp->dirname, namelist[i]->d_name)) {
278 printerr(2, "destroying client %s\n", clp->dirname);
279 saveprev = clp->list.tqe_prev;
280 TAILQ_REMOVE(&clnt_list, clp, list);
285 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
286 if (!process_clnt_dir_files(clp))
287 insert_clnt_poll(clp);
291 /* Search for a client by directory name, return 1 if found, 0 otherwise */
293 find_client(char *dirname)
295 struct clnt_info *clp;
297 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
298 if (!strcmp(clp->dirname, dirname))
303 /* Used to read (and re-read) list of clients, set up poll array. */
305 update_client_list(void)
307 char lustre_dir[PATH_MAX];
308 struct dirent lustre_dirent = { .d_name = "lustre" };
309 struct dirent *namelist[1];
313 if (chdir(pipefs_dir) < 0) {
314 printerr(0, "ERROR: can't chdir to %s: %s\n",
315 pipefs_dir, strerror(errno));
319 snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefs_dir, "lustre");
320 if (stat(lustre_dir, &statbuf) == 0) {
321 namelist[0] = &lustre_dirent;
323 printerr(2, "re-processing lustre directory\n");
327 printerr(2, "lustre directory not exist\n");
330 update_old_clients(namelist, j);
331 for (i=0; i < j; i++) {
332 if (i < FD_ALLOC_BLOCK && !find_client(namelist[i]->d_name))
333 process_clnt_dir(namelist[i]->d_name);
340 /* Context creation response. */
341 struct lustre_gss_init_res {
342 gss_buffer_desc gr_ctx; /* context handle */
343 unsigned int gr_major; /* major status */
344 unsigned int gr_minor; /* minor status */
345 unsigned int gr_win; /* sequence window */
346 gss_buffer_desc gr_token; /* token */
349 struct lustre_gss_data {
351 int lgd_lustre_svc; /* mds/oss */
352 int lgd_uid; /* uid */
353 char *lgd_uuid; /* client device uuid */
354 gss_name_t lgd_name; /* service name */
356 gss_OID lgd_mech; /* mech OID */
357 unsigned int lgd_req_flags; /* request flags */
358 gss_cred_id_t lgd_cred; /* credential */
359 gss_ctx_id_t lgd_ctx; /* session context */
360 gss_buffer_desc lgd_rmt_ctx; /* remote handle of context */
361 uint32_t lgd_seq_win; /* sequence window */
368 do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
369 struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
371 char *buf = NULL, *p = NULL, *end = NULL;
372 unsigned int timeout = 0; /* XXX decide on a reasonable value */
373 unsigned int buf_size = 0;
375 printerr(2, "doing downcall\n");
376 buf_size = sizeof(updata->seq) + sizeof(timeout) +
377 sizeof(lgd->lgd_seq_win) +
378 sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
379 sizeof(context_token->length) + context_token->length;
380 p = buf = malloc(buf_size);
381 end = buf + buf_size;
383 if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
384 /* Not setting any timeout for now: */
385 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
386 if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
387 if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
388 if (write_buffer(&p, end, context_token)) goto out_err;
390 lgssd_mutex_get(lgssd_mutex_downcall);
391 if (write(k5_fd, buf, p - buf) < p - buf) {
392 lgssd_mutex_put(lgssd_mutex_downcall);
395 lgssd_mutex_put(lgssd_mutex_downcall);
401 printerr(0, "ERROR: Failed to write downcall!\n");
406 do_error_downcall(int k5_fd, uint32_t seq, int rpc_err, int gss_err)
409 char *p = buf, *end = buf + 1024;
410 unsigned int timeout = 0;
413 printerr(1, "doing error downcall\n");
415 if (WRITE_BYTES(&p, end, seq)) goto out_err;
416 if (WRITE_BYTES(&p, end, timeout)) goto out_err;
417 /* use seq_win = 0 to indicate an error: */
418 if (WRITE_BYTES(&p, end, zero)) goto out_err;
419 if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
420 if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
422 lgssd_mutex_get(lgssd_mutex_downcall);
423 if (write(k5_fd, buf, p - buf) < p - buf) {
424 lgssd_mutex_put(lgssd_mutex_downcall);
427 lgssd_mutex_put(lgssd_mutex_downcall);
430 printerr(0, "Failed to write error downcall!\n");
436 * Create an RPC connection and establish an authenticated
437 * gss context with a server.
439 int create_auth_rpc_client(struct clnt_info *clp,
440 CLIENT **clnt_return,
445 CLIENT *rpc_clnt = NULL;
446 struct rpc_gss_sec sec;
452 char rpc_errmsg[1024];
453 int sockp = RPC_ANYSOCK;
454 int sendsz = 32768, recvsz = 32768;
455 struct addrinfo ai_hints, *a = NULL;
459 /* Create the context as the user (not as root) */
460 save_uid = geteuid();
461 if (setfsuid(uid) != 0) {
462 printerr(0, "WARNING: Failed to setfsuid for "
463 "user with uid %d\n", uid);
466 printerr(2, "creating context using fsuid %d (save_uid %d)\n",
469 sec.qop = GSS_C_QOP_DEFAULT;
470 sec.svc = RPCSEC_GSS_SVC_NONE;
471 sec.cred = GSS_C_NO_CREDENTIAL;
473 if (authtype == AUTHTYPE_KRB5) {
474 sec.mech = (gss_OID)&krb5oid;
475 sec.req_flags = GSS_C_MUTUAL_FLAG;
477 else if (authtype == AUTHTYPE_SPKM3) {
478 sec.mech = (gss_OID)&spkm3oid;
479 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
480 * Need a way to switch....
482 sec.req_flags = GSS_C_MUTUAL_FLAG;
485 printerr(0, "ERROR: Invalid authentication type (%d) "
486 "in create_auth_rpc_client\n", authtype);
491 if (authtype == AUTHTYPE_KRB5) {
492 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
494 * Do this before creating rpc connection since we won't need
495 * rpc connection if it fails!
497 if (limit_krb5_enctypes(&sec, uid)) {
498 printerr(1, "WARNING: Failed while limiting krb5 "
499 "encryption types for user with uid %d\n",
506 /* create an rpc connection to the nfs server */
508 printerr(2, "creating %s client for server %s\n", clp->protocol,
511 memset(&ai_hints, '\0', sizeof(ai_hints));
512 ai_hints.ai_family = PF_INET;
513 ai_hints.ai_flags |= AI_CANONNAME;
514 if ((strcmp(clp->protocol, "tcp")) == 0) {
515 ai_hints.ai_socktype = SOCK_STREAM;
516 ai_hints.ai_protocol = IPPROTO_TCP;
517 } else if ((strcmp(clp->protocol, "udp")) == 0) {
518 ai_hints.ai_socktype = SOCK_DGRAM;
519 ai_hints.ai_protocol = IPPROTO_UDP;
521 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
522 "for connection to server %s for user with uid %d",
523 clp->protocol, clp->servername, uid);
527 /* extract the service name from clp->servicename */
528 if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
529 printerr(0, "WARNING: servicename (%s) not formatted as "
530 "expected with service@host", clp->servicename);
533 if ((at_sign - clp->servicename) >= sizeof(service)) {
534 printerr(0, "WARNING: service portion of servicename (%s) "
535 "is too long!", clp->servicename);
538 strncpy(service, clp->servicename, at_sign - clp->servicename);
539 service[at_sign - clp->servicename] = '\0';
541 errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
543 printerr(0, "WARNING: Error from getaddrinfo for server "
544 "'%s': %s", clp->servername, gai_strerror(errcode));
549 printerr(0, "WARNING: No address information found for "
550 "connection to server %s for user with uid %d",
551 clp->servername, uid);
554 if (a->ai_protocol == IPPROTO_TCP) {
555 if ((rpc_clnt = clnttcp_create(
556 (struct sockaddr_in *) a->ai_addr,
557 clp->prog, clp->vers, &sockp,
558 sendsz, recvsz)) == NULL) {
559 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
560 "WARNING: can't create tcp rpc_clnt "
561 "for server %s for user with uid %d",
562 clp->servername, uid);
564 clnt_spcreateerror(rpc_errmsg));
567 } else if (a->ai_protocol == IPPROTO_UDP) {
568 const struct timeval timeout = {5, 0};
569 if ((rpc_clnt = clntudp_bufcreate(
570 (struct sockaddr_in *) a->ai_addr,
571 clp->prog, clp->vers, timeout,
572 &sockp, sendsz, recvsz)) == NULL) {
573 snprintf(rpc_errmsg, sizeof(rpc_errmsg),
574 "WARNING: can't create udp rpc_clnt "
575 "for server %s for user with uid %d",
576 clp->servername, uid);
578 clnt_spcreateerror(rpc_errmsg));
582 /* Shouldn't happen! */
583 printerr(0, "ERROR: requested protocol '%s', but "
584 "got addrinfo with protocol %d",
585 clp->protocol, a->ai_protocol);
588 /* We're done with this */
592 printerr(2, "creating context with server %s\n", clp->servicename);
593 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
595 /* Our caller should print appropriate message */
596 printerr(2, "WARNING: Failed to create %s context for "
597 "user with uid %d for server %s\n",
598 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
599 uid, clp->servername);
604 rpc_clnt->cl_auth = auth;
605 *clnt_return = rpc_clnt;
610 if (sec.cred != GSS_C_NO_CREDENTIAL)
611 gss_release_cred(&min_stat, &sec.cred);
612 if (a != NULL) freeaddrinfo(a);
613 /* Restore euid to original value */
614 if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
615 printerr(0, "WARNING: Failed to restore fsuid"
616 " to uid %d from %d\n", save_uid, uid);
621 /* Only destroy here if failure. Otherwise, caller is responsible */
622 if (rpc_clnt) clnt_destroy(rpc_clnt);
629 int do_negotiation(struct lustre_gss_data *lgd,
630 gss_buffer_desc *gss_token,
631 struct lustre_gss_init_res *gr,
634 char *file = "/proc/fs/lustre/sptlrpc/gss/init_channel";
635 struct lgssd_ioctl_param param;
642 pw = getpwuid(lgd->lgd_uid);
644 printerr(0, "no uid %u in local user database\n",
649 param.version = GSSD_INTERFACE_VERSION;
650 param.uuid = lgd->lgd_uuid;
651 param.lustre_svc = lgd->lgd_lustre_svc;
652 param.uid = lgd->lgd_uid;
653 param.gid = pw->pw_gid;
654 param.send_token_size = gss_token->length;
655 param.send_token = (char *) gss_token->value;
656 param.reply_buf_size = sizeof(outbuf);
657 param.reply_buf = outbuf;
659 fd = open(file, O_RDWR);
661 printerr(0, "can't open file %s\n", file);
665 ret = write(fd, ¶m, sizeof(param));
667 if (ret != sizeof(param)) {
668 printerr(0, "lustre ioctl err: %d\n", strerror(errno));
674 printerr(0, "status: %d (%s)\n",
675 param.status, strerror((int)param.status));
676 if (param.status == -ETIMEDOUT) {
677 /* kernel return -ETIMEDOUT means the rpc timedout,
678 * we should notify the caller to reinitiate the
679 * gss negotiation, by return -ERESTART
681 lgd->lgd_rpc_err = -ERESTART;
682 lgd->lgd_gss_err = 0;
684 lgd->lgd_rpc_err = param.status;
685 lgd->lgd_gss_err = 0;
689 p = (unsigned int *)outbuf;
695 gr->gr_ctx.length = *p++;
696 gr->gr_ctx.value = malloc(gr->gr_ctx.length);
697 memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
698 p += (((gr->gr_ctx.length + 3) & ~3) / 4);
700 gr->gr_token.length = *p++;
701 gr->gr_token.value = malloc(gr->gr_token.length);
702 memcpy(gr->gr_token.value, p, gr->gr_token.length);
703 p += (((gr->gr_token.length + 3) & ~3) / 4);
705 printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
706 gr->gr_ctx.length, gr->gr_token.length);
712 int gssd_refresh_lgd(struct lustre_gss_data *lgd)
714 struct lustre_gss_init_res gr;
715 gss_buffer_desc *recv_tokenp, send_token;
716 OM_uint32 maj_stat, min_stat, call_stat, ret_flags;
718 /* GSS context establishment loop. */
719 memset(&gr, 0, sizeof(gr));
720 recv_tokenp = GSS_C_NO_BUFFER;
723 /* print the token we just received */
724 if (recv_tokenp != GSS_C_NO_BUFFER) {
725 printerr(3, "The received token length %d\n",
726 recv_tokenp->length);
727 print_hexl(3, recv_tokenp->value, recv_tokenp->length);
730 maj_stat = gss_init_sec_context(&min_stat,
739 NULL, /* used mech */
742 NULL); /* time rec */
744 if (recv_tokenp != GSS_C_NO_BUFFER) {
745 gss_release_buffer(&min_stat, &gr.gr_token);
746 recv_tokenp = GSS_C_NO_BUFFER;
748 if (maj_stat != GSS_S_COMPLETE &&
749 maj_stat != GSS_S_CONTINUE_NEEDED) {
750 pgsserr("gss_init_sec_context", maj_stat, min_stat,
754 if (send_token.length != 0) {
755 memset(&gr, 0, sizeof(gr));
757 /* print the token we are about to send */
758 printerr(3, "token being sent length %d\n",
760 print_hexl(3, send_token.value, send_token.length);
762 call_stat = do_negotiation(lgd, &send_token, &gr, 0);
763 gss_release_buffer(&min_stat, &send_token);
765 if (call_stat != 0 ||
766 (gr.gr_major != GSS_S_COMPLETE &&
767 gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
768 printerr(0, "call stat %d, major stat 0x%x\n",
769 (int)call_stat, gr.gr_major);
773 if (gr.gr_ctx.length != 0) {
774 if (lgd->lgd_rmt_ctx.value)
775 gss_release_buffer(&min_stat,
777 lgd->lgd_rmt_ctx = gr.gr_ctx;
779 if (gr.gr_token.length != 0) {
780 if (maj_stat != GSS_S_CONTINUE_NEEDED)
782 recv_tokenp = &gr.gr_token;
786 /* GSS_S_COMPLETE => check gss header verifier,
787 * usually checked in gss_validate
789 if (maj_stat == GSS_S_COMPLETE) {
790 lgd->lgd_established = 1;
791 lgd->lgd_seq_win = gr.gr_win;
795 /* End context negotiation loop. */
796 if (!lgd->lgd_established) {
797 if (gr.gr_token.length != 0)
798 gss_release_buffer(&min_stat, &gr.gr_token);
800 printerr(0, "context negotiation failed\n");
804 printerr(2, "successfully refreshed lgd\n");
809 int gssd_create_lgd(struct clnt_info *clp,
810 struct lustre_gss_data *lgd,
811 struct lgssd_upcall_data *updata,
814 gss_buffer_desc sname;
815 OM_uint32 maj_stat, min_stat;
818 lgd->lgd_established = 0;
819 lgd->lgd_lustre_svc = updata->svc;
820 lgd->lgd_uid = updata->uid;
821 lgd->lgd_uuid = updata->obd;
825 lgd->lgd_mech = (gss_OID) &krb5oid;
826 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
829 lgd->lgd_mech = (gss_OID) &spkm3oid;
830 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
831 * Need a way to switch....
833 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
836 printerr(0, "Invalid authentication type (%d)\n", authtype);
840 lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
841 lgd->lgd_ctx = GSS_C_NO_CONTEXT;
842 lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
843 lgd->lgd_seq_win = 0;
845 sname.value = clp->servicename;
846 sname.length = strlen(clp->servicename);
848 maj_stat = gss_import_name(&min_stat, &sname,
849 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
851 if (maj_stat != GSS_S_COMPLETE) {
852 pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
856 retval = gssd_refresh_lgd(lgd);
858 if (lgd->lgd_name != GSS_C_NO_NAME)
859 gss_release_name(&min_stat, &lgd->lgd_name);
861 if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
862 gss_release_cred(&min_stat, &lgd->lgd_cred);
869 void gssd_free_lgd(struct lustre_gss_data *lgd)
871 gss_buffer_t token = GSS_C_NO_BUFFER;
872 OM_uint32 maj_stat, min_stat;
874 if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
877 maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
881 int construct_service_name(struct clnt_info *clp,
882 struct lgssd_upcall_data *ud)
884 const int buflen = 256;
887 if (clp->servicename) {
888 free(clp->servicename);
889 clp->servicename = NULL;
892 if (lnet_nid2hostname(ud->nid, name, buflen))
895 clp->servicename = malloc(32 + strlen(name));
896 if (!clp->servicename) {
897 printerr(0, "can't alloc memory\n");
900 sprintf(clp->servicename, "%s@%s",
901 ud->svc == LUSTRE_GSS_SVC_MDS ?
902 GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
904 printerr(2, "constructed servicename: %s\n", clp->servicename);
909 * this code uses the userland rpcsec gss library to create a krb5
910 * context on behalf of the kernel
913 handle_krb5_upcall(struct clnt_info *clp)
916 gss_buffer_desc token = { 0, NULL };
917 struct lgssd_upcall_data updata;
918 struct lustre_gss_data lgd;
919 char **credlist = NULL;
923 printerr(2, "handling krb5 upcall\n");
925 memset(&lgd, 0, sizeof(lgd));
926 lgd.lgd_rpc_err = -EPERM; /* default error code */
928 read_rc = read(clp->krb5_fd, &updata, sizeof(updata));
930 printerr(0, "WARNING: failed reading from krb5 "
931 "upcall pipe: %s\n", strerror(errno));
933 } else if (read_rc != sizeof(updata)) {
934 printerr(0, "upcall data mismatch: length %d, expect %d\n",
935 read_rc, sizeof(updata));
937 /* the sequence number must be the first field. if read >= 4
938 * bytes then we know at least sequence is fine, try to send
939 * error notification nicely.
942 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
946 /* FIXME temporary fix, do this before fork.
947 * in case of errors could have memory leak!!!
949 if (updata.uid == 0) {
950 if (gssd_get_krb5_machine_cred_list(&credlist)) {
951 printerr(0, "ERROR: Failed to obtain machine "
953 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
958 /* fork child process */
961 printerr(0, "can't fork: %s\n", strerror(errno));
962 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
964 } else if (pid > 0) {
965 printerr(2, "forked child process: %d\n", pid);
969 printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
970 updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
972 if (updata.svc != LUSTRE_GSS_SVC_MDS &&
973 updata.svc != LUSTRE_GSS_SVC_OSS) {
974 printerr(0, "invalid svc %d\n", updata.svc);
975 lgd.lgd_rpc_err = -EPROTO;
976 goto out_return_error;
978 updata.obd[sizeof(updata.obd)-1] = '\0';
980 if (construct_service_name(clp, &updata)) {
981 printerr(0, "failed to construct service name\n");
982 goto out_return_error;
985 if (updata.uid == 0) {
989 * Get a list of credential cache names and try each
990 * of them until one works or we've tried them all
993 if (gssd_get_krb5_machine_cred_list(&credlist)) {
994 printerr(0, "ERROR: Failed to obtain machine "
995 "credentials for %s\n", clp->servicename);
996 goto out_return_error;
999 for (ccname = credlist; ccname && *ccname; ccname++) {
1000 gssd_setup_krb5_machine_gss_ccache(*ccname);
1001 if ((gssd_create_lgd(clp, &lgd, &updata,
1002 AUTHTYPE_KRB5)) == 0) {
1007 printerr(2, "WARNING: Failed to create krb5 context "
1008 "for user with uid %d with credentials "
1009 "cache %s for service %s\n",
1010 updata.uid, *ccname, clp->servicename);
1012 gssd_free_krb5_machine_cred_list(credlist);
1014 printerr(0, "ERROR: Failed to create krb5 context "
1015 "for user with uid %d with any "
1016 "credentials cache for service %s\n",
1017 updata.uid, clp->servicename);
1018 goto out_return_error;
1022 /* Tell krb5 gss which credentials cache to use */
1023 gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
1025 if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
1026 printerr(0, "WARNING: Failed to create krb5 context "
1027 "for user with uid %d for service %s\n",
1028 updata.uid, clp->servicename);
1029 goto out_return_error;
1033 if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
1034 printerr(0, "WARNING: Failed to serialize krb5 context for "
1035 "user with uid %d for service %s\n",
1036 updata.uid, clp->servicename);
1037 goto out_return_error;
1040 printerr(1, "refreshed: %u@%s for %s\n",
1041 updata.uid, updata.obd, clp->servicename);
1042 do_downcall(clp->krb5_fd, &updata, &lgd, &token);
1048 gssd_free_lgd(&lgd);
1049 exit(0); /* i'm child process */
1052 do_error_downcall(clp->krb5_fd, updata.seq,
1053 lgd.lgd_rpc_err, lgd.lgd_gss_err);
1058 * this code uses the userland rpcsec gss library to create an spkm3
1059 * context on behalf of the kernel
1062 handle_spkm3_upcall(struct clnt_info *clp)
1066 CLIENT *rpc_clnt = NULL;
1068 struct authgss_private_data pd;
1069 gss_buffer_desc token;
1071 printerr(2, "handling spkm3 upcall\n");
1076 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1077 printerr(0, "WARNING: failed reading uid from spkm3 "
1078 "upcall pipe: %s\n", strerror(errno));
1082 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
1083 printerr(0, "WARNING: Failed to create spkm3 context for "
1084 "user with uid %d\n", uid);
1085 goto out_return_error;
1088 if (!authgss_get_private_data(auth, &pd)) {
1089 printerr(0, "WARNING: Failed to obtain authentication "
1090 "data for user with uid %d for server %s\n",
1091 uid, clp->servername);
1092 goto out_return_error;
1095 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
1096 printerr(0, "WARNING: Failed to serialize spkm3 context for "
1097 "user with uid %d for server\n",
1098 uid, clp->servername);
1099 goto out_return_error;
1102 do_downcall(clp->spkm3_fd, uid, &pd, &token);
1110 clnt_destroy(rpc_clnt);
1114 do_error_downcall(clp->spkm3_fd, uid, -1);