4 Copyright (c) 2000 The Regents of the University of Michigan.
7 Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
9 Copyright (c) 2014, Intel Corporation.
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
15 1. Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17 2. Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20 3. Neither the name of the University nor the names of its
21 contributors may be used to endorse or promote products derived
22 from this software without specific prior written permission.
24 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/param.h>
54 #include <lnet/nidstr.h>
63 #include <lustre/lustre_idl.h>
65 extern const char *gss_OID_mech_name(gss_OID mech);
67 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.sptlrpc.context/channel"
68 #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.sptlrpc.init/channel"
70 #define TOKEN_BUF_SIZE 8192
82 struct svc_nego_data {
87 char nm_name[LUSTRE_NODEMAP_NAME_LENGTH + 1];
88 gss_buffer_desc in_tok;
89 gss_buffer_desc out_tok;
90 gss_buffer_desc in_handle;
91 gss_buffer_desc out_handle;
98 gss_buffer_desc ctx_token;
102 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
103 gss_OID mechoid, gss_buffer_desc *context_token)
106 const char *mechname;
109 printerr(2, "doing downcall\n");
110 mechname = gss_OID_mech_name(mechoid);
111 if (mechname == NULL)
113 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
115 printerr(0, "WARNING: unable to open downcall channel "
117 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
120 qword_printhex(f, out_handle->value, out_handle->length);
121 /* XXX are types OK for the rest of this? */
122 qword_printint(f, 3600); /* an hour should be sufficient */
123 qword_printint(f, cred->cr_remote);
124 qword_printint(f, cred->cr_usr_root);
125 qword_printint(f, cred->cr_usr_mds);
126 qword_printint(f, cred->cr_usr_oss);
127 qword_printint(f, cred->cr_mapped_uid);
128 qword_printint(f, cred->cr_uid);
129 qword_printint(f, cred->cr_gid);
130 qword_print(f, mechname);
131 qword_printhex(f, context_token->value, context_token->length);
136 printerr(0, "WARNING: downcall failed\n");
140 struct gss_verifier {
142 gss_buffer_desc body;
145 #define RPCSEC_GSS_SEQ_WIN 5
148 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
149 u_int32_t maj_stat, u_int32_t min_stat,
150 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
152 char buf[2 * TOKEN_BUF_SIZE];
154 int blen = sizeof(buf);
158 printerr(2, "sending null reply\n");
160 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
161 qword_addhex(&bp, &blen, in_token->value, in_token->length);
162 qword_addint(&bp, &blen, 3600); /* an hour should be sufficient */
163 qword_adduint(&bp, &blen, maj_stat);
164 qword_adduint(&bp, &blen, min_stat);
165 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
166 qword_addhex(&bp, &blen, out_token->value, out_token->length);
167 qword_addeol(&bp, &blen);
169 printerr(0, "WARNING: send_respsonse: message too long\n");
172 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
174 printerr(0, "WARNING: open %s failed: %s\n",
175 SVCGSSD_INIT_CHANNEL, strerror(errno));
179 printerr(3, "writing message: %s", buf);
180 if (write(g, buf, bp - buf) == -1) {
181 printerr(0, "WARNING: failed to write message\n");
189 #define rpc_auth_ok 0
190 #define rpc_autherr_badcred 1
191 #define rpc_autherr_rejectedcred 2
192 #define rpc_autherr_badverf 3
193 #define rpc_autherr_rejectedverf 4
194 #define rpc_autherr_tooweak 5
195 #define rpcsec_gsserr_credproblem 13
196 #define rpcsec_gsserr_ctxproblem 14
200 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
203 static gid_t *groups = NULL;
205 cred->cr_ngroups = NGROUPS;
206 ret = nfs4_gss_princ_to_grouplist(secname, name,
207 cred->cr_groups, &cred->cr_ngroups);
209 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
210 ret = nfs4_gss_princ_to_grouplist(secname, name,
211 groups, &cred->cr_ngroups);
213 cred->cr_ngroups = 0;
215 if (cred->cr_ngroups > NGROUPS)
216 cred->cr_ngroups = NGROUPS;
217 memcpy(cred->cr_groups, groups,
218 cred->cr_ngroups*sizeof(gid_t));
226 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
228 u_int32_t maj_stat, min_stat;
229 gss_buffer_desc name;
233 gss_OID name_type = GSS_C_NO_OID;
236 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
237 if (maj_stat != GSS_S_COMPLETE) {
238 pgsserr("get_ids: gss_display_name",
239 maj_stat, min_stat, mech);
242 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
243 !(sname = calloc(name.length + 1, 1))) {
244 printerr(0, "WARNING: get_ids: error allocating %d bytes "
245 "for sname\n", name.length + 1);
246 gss_release_buffer(&min_stat, &name);
249 memcpy(sname, name.value, name.length);
250 printerr(1, "sname = %s\n", sname);
251 gss_release_buffer(&min_stat, &name);
254 if ((secname = mech2file(mech)) == NULL) {
255 printerr(0, "WARNING: get_ids: error mapping mech to "
256 "file for name '%s'\n", sname);
259 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
260 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
263 * -ENOENT means there was no mapping, any other error
264 * value means there was an error trying to do the
266 * If there was no mapping, we send down the value -1
267 * to indicate that the anonuid/anongid for the export
270 if (res == -ENOENT) {
273 cred->cr_ngroups = 0;
277 printerr(0, "WARNING: get_ids: failed to map name '%s' "
278 "to uid/gid: %s\n", sname, strerror(-res));
283 add_supplementary_groups(secname, sname, cred);
294 print_hexl(int pri, unsigned char *cp, int length)
299 printerr(pri, "length %d\n",length);
302 for (i = 0; i < length; i += 0x10) {
303 printerr(pri, " %04x: ", (unsigned int)i);
305 jm = jm > 16 ? 16 : jm;
307 for (j = 0; j < jm; j++) {
309 printerr(pri, "%02x ", (unsigned int)cp[i+j]);
311 printerr(pri, "%02x", (unsigned int)cp[i+j]);
313 for (; j < 16; j++) {
321 for (j = 0; j < jm; j++) {
323 c = isprint(c) ? c : '.';
324 printerr(pri,"%c", c);
332 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
333 lnet_nid_t nid, uint32_t lustre_svc)
335 u_int32_t maj_stat, min_stat;
336 gss_buffer_desc name;
337 char *sname, *host, *realm;
338 const int namebuf_size = 512;
339 char namebuf[namebuf_size];
341 gss_OID name_type = GSS_C_NO_OID;
345 cred->cr_usr_root = cred->cr_usr_mds = cred->cr_usr_oss = 0;
346 cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
348 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
349 if (maj_stat != GSS_S_COMPLETE) {
350 pgsserr("get_ids: gss_display_name",
351 maj_stat, min_stat, mech);
354 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
355 !(sname = calloc(name.length + 1, 1))) {
356 printerr(0, "WARNING: get_ids: error allocating %zu bytes "
357 "for sname\n", name.length + 1);
358 gss_release_buffer(&min_stat, &name);
361 memcpy(sname, name.value, name.length);
362 sname[name.length] = '\0';
363 gss_release_buffer(&min_stat, &name);
365 if (lustre_svc == LUSTRE_GSS_SVC_MDS)
366 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
368 cred->cr_mapped_uid = -1;
370 realm = strchr(sname, '@');
374 printerr(0, "ERROR: %s has no realm name\n", sname);
378 host = strchr(sname, '/');
382 if (strcmp(sname, GSSD_SERVICE_MGS) == 0) {
383 printerr(0, "forbid %s as a user name\n", sname);
387 /* 1. check host part */
389 if (lnet_nid2hostname(nid, namebuf, namebuf_size)) {
390 printerr(0, "ERROR: failed to resolve hostname for "
391 "%s/%s@%s from %016llx\n",
392 sname, host, realm, nid);
396 if (strcasecmp(host, namebuf)) {
397 printerr(0, "ERROR: %s/%s@%s claimed hostname doesn't "
398 "match %s, nid %016llx\n", sname, host, realm,
403 if (!strcmp(sname, GSSD_SERVICE_MDS) ||
404 !strcmp(sname, GSSD_SERVICE_OSS)) {
405 printerr(0, "ERROR: %s@%s from %016llx doesn't "
406 "bind with hostname\n", sname, realm, nid);
411 /* 2. check realm and user */
412 switch (lustre_svc) {
413 case LUSTRE_GSS_SVC_MDS:
414 if (strcasecmp(mds_local_realm, realm)) {
417 /* only allow mapped user from remote realm */
418 if (cred->cr_mapped_uid == -1) {
419 printerr(0, "ERROR: %s%s%s@%s from %016llx "
420 "is remote but without mapping\n",
421 sname, host ? "/" : "",
422 host ? host : "", realm, nid);
426 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
428 cred->cr_usr_root = 1;
429 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
431 cred->cr_usr_mds = 1;
432 } else if (!strcmp(sname, GSSD_SERVICE_OSS)) {
434 cred->cr_usr_oss = 1;
436 pw = getpwnam(sname);
438 cred->cr_uid = pw->pw_uid;
439 printerr(2, "%s resolve to uid %u\n",
440 sname, cred->cr_uid);
441 } else if (cred->cr_mapped_uid != -1) {
442 printerr(2, "user %s from %016llx is "
443 "mapped to %u\n", sname, nid,
444 cred->cr_mapped_uid);
446 printerr(0, "ERROR: invalid user, "
447 "%s/%s@%s from %016llx\n",
448 sname, host, realm, nid);
456 case LUSTRE_GSS_SVC_MGS:
457 if (!strcmp(sname, GSSD_SERVICE_OSS)) {
459 cred->cr_usr_oss = 1;
462 case LUSTRE_GSS_SVC_OSS:
463 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
465 cred->cr_usr_root = 1;
466 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
468 cred->cr_usr_mds = 1;
470 if (cred->cr_uid == -1) {
471 printerr(0, "ERROR: svc %d doesn't accept user %s "
472 "from %016llx\n", lustre_svc, sname, nid);
483 printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n",
484 lustre_svc_name[lustre_svc], sname,
485 host ? "/" : "", host ? host : "", realm, nid);
490 typedef struct gss_union_ctx_id_t {
492 gss_ctx_id_t internal_ctx_id;
493 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
495 static int handle_krb(struct svc_nego_data *snd)
498 gss_name_t client_name;
499 gss_buffer_desc ignore_out_tok = {.value = NULL};
500 gss_OID mech = GSS_C_NO_OID;
501 gss_cred_id_t svc_cred;
502 u_int32_t ignore_min_stat;
503 struct svc_cred cred;
505 svc_cred = gssd_select_svc_cred(snd->lustre_svc);
507 printerr(0, "no service credential for svc %u\n",
512 snd->maj_stat = gss_accept_sec_context(&snd->min_stat, &snd->ctx,
513 svc_cred, &snd->in_tok,
514 GSS_C_NO_CHANNEL_BINDINGS,
516 &snd->out_tok, &ret_flags, NULL,
519 if (snd->maj_stat == GSS_S_CONTINUE_NEEDED) {
520 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
522 /* Save the context handle for future calls */
523 snd->out_handle.length = sizeof(snd->ctx);
524 memcpy(snd->out_handle.value, &snd->ctx, sizeof(snd->ctx));
526 } else if (snd->maj_stat != GSS_S_COMPLETE) {
527 printerr(0, "WARNING: gss_accept_sec_context failed\n");
528 pgsserr("handle_krb: gss_accept_sec_context",
529 snd->maj_stat, snd->min_stat, mech);
533 if (get_ids(client_name, mech, &cred, snd->nid, snd->lustre_svc)) {
534 /* get_ids() prints error msg */
535 snd->maj_stat = GSS_S_BAD_NAME; /* XXX ? */
536 gss_release_name(&ignore_min_stat, &client_name);
539 gss_release_name(&ignore_min_stat, &client_name);
541 /* Context complete. Pass handle_seq in out_handle to use
542 * for context lookup in the kernel. */
543 snd->out_handle.length = sizeof(snd->handle_seq);
544 memcpy(snd->out_handle.value, &snd->handle_seq,
545 sizeof(snd->handle_seq));
547 /* kernel needs ctx to calculate verifier on null response, so
548 * must give it context before doing null call: */
549 if (serialize_context_for_kernel(snd->ctx, &snd->ctx_token, mech)) {
550 printerr(0, "WARNING: handle_krb: "
551 "serialize_context_for_kernel failed\n");
552 snd->maj_stat = GSS_S_FAILURE;
555 /* We no longer need the gss context */
556 gss_delete_sec_context(&ignore_min_stat, &snd->ctx, &ignore_out_tok);
557 do_svc_downcall(&snd->out_handle, &cred, mech, &snd->ctx_token);
562 if (snd->ctx != GSS_C_NO_CONTEXT)
563 gss_delete_sec_context(&ignore_min_stat, &snd->ctx,
570 * return -1 only if we detect error during reading from upcall channel,
571 * all other cases return 0.
573 int handle_channel_request(FILE *f)
575 char in_tok_buf[TOKEN_BUF_SIZE];
576 char in_handle_buf[15];
577 char out_handle_buf[15];
578 gss_buffer_desc ctx_token = {.value = NULL},
579 null_token = {.value = NULL};
580 uint32_t lustre_mech;
586 u_int32_t ignore_min_stat;
587 struct svc_nego_data snd = {
588 .in_tok.value = in_tok_buf,
589 .in_handle.value = in_handle_buf,
590 .out_handle.value = out_handle_buf,
591 .maj_stat = GSS_S_FAILURE,
592 .ctx = GSS_C_NO_CONTEXT,
595 printerr(2, "handling request\n");
596 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
597 printerr(0, "WARNING: handle_req: failed reading request\n");
603 /* see rsi_request() for the format of data being input here */
604 qword_get(&cp, (char *)&snd.lustre_svc, sizeof(snd.lustre_svc));
606 /* lustre_svc is the svc and gss subflavor */
607 lustre_mech = (snd.lustre_svc & LUSTRE_GSS_MECH_MASK) >>
608 LUSTRE_GSS_MECH_SHIFT;
609 snd.lustre_svc = snd.lustre_svc & LUSTRE_GSS_SVC_MASK;
610 switch (lustre_mech) {
613 printerr(1, "WARNING: Request for kerberos but service "
614 "support not enabled\n");
620 printerr(0, "WARNING: invalid mechanism recevied: %d\n",
626 qword_get(&cp, (char *)&snd.nid, sizeof(snd.nid));
627 qword_get(&cp, (char *)&snd.handle_seq, sizeof(snd.handle_seq));
628 qword_get(&cp, snd.nm_name, sizeof(snd.nm_name));
629 printerr(2, "handling req: svc %u, nid %016llx, idx %"PRIx64" nodemap "
630 "%s\n", snd.lustre_svc, snd.nid, snd.handle_seq, snd.nm_name);
632 get_len = qword_get(&cp, snd.in_handle.value, sizeof(in_handle_buf));
634 printerr(0, "WARNING: handle_req: failed parsing request\n");
637 snd.in_handle.length = (size_t)get_len;
639 printerr(3, "in_handle:\n");
640 print_hexl(3, snd.in_handle.value, snd.in_handle.length);
642 get_len = qword_get(&cp, snd.in_tok.value, sizeof(in_tok_buf));
644 printerr(0, "WARNING: handle_req: failed parsing request\n");
647 snd.in_tok.length = (size_t)get_len;
649 printerr(3, "in_tok:\n");
650 print_hexl(3, snd.in_tok.value, snd.in_tok.length);
652 if (snd.in_handle.length != 0) { /* CONTINUE_INIT case */
653 if (snd.in_handle.length != sizeof(snd.ctx)) {
654 printerr(0, "WARNING: handle_req: "
655 "input handle has unexpected length %zu\n",
656 snd.in_handle.length);
659 /* in_handle is the context id stored in the out_handle
660 * for the GSS_S_CONTINUE_NEEDED case below. */
661 memcpy(&snd.ctx, snd.in_handle.value, snd.in_handle.length);
664 if (lustre_mech == LGSS_MECH_KRB5)
665 rc = handle_krb(&snd);
667 printerr(0, "WARNING: Received or request for"
668 "subflavor that is not enabled: %d\n", lustre_mech);
671 /* Failures send a null token */
673 send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat,
674 snd.min_stat, &snd.out_handle, &snd.out_tok);
676 send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat,
677 snd.min_stat, &null_token, &null_token);
679 /* cleanup buffers */
680 if (snd.ctx_token.value != NULL)
681 free(ctx_token.value);
682 if (snd.out_tok.value != NULL)
683 gss_release_buffer(&ignore_min_stat, &snd.out_tok);
685 /* For junk wire data just ignore */