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>
59 extern char * mech2file(gss_OID mech);
60 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.sptlrpc.context/channel"
61 #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.sptlrpc.init/channel"
63 #define TOKEN_BUF_SIZE 8192
76 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
77 gss_OID mech, gss_buffer_desc *context_token)
83 printerr(2, "doing downcall\n");
84 if ((fname = mech2file(mech)) == NULL)
86 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
88 printerr(0, "WARNING: unable to open downcall channel "
90 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
93 qword_printhex(f, out_handle->value, out_handle->length);
94 /* XXX are types OK for the rest of this? */
95 qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
96 qword_printint(f, cred->cr_remote);
97 qword_printint(f, cred->cr_usr_root);
98 qword_printint(f, cred->cr_usr_mds);
99 qword_printint(f, cred->cr_usr_oss);
100 qword_printint(f, cred->cr_mapped_uid);
101 qword_printint(f, cred->cr_uid);
102 qword_printint(f, cred->cr_gid);
103 qword_print(f, fname);
104 qword_printhex(f, context_token->value, context_token->length);
109 printerr(0, "WARNING: downcall failed\n");
113 struct gss_verifier {
115 gss_buffer_desc body;
118 #define RPCSEC_GSS_SEQ_WIN 5
121 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
122 u_int32_t maj_stat, u_int32_t min_stat,
123 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
125 char buf[2 * TOKEN_BUF_SIZE];
127 int blen = sizeof(buf);
131 printerr(2, "sending null reply\n");
133 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
134 qword_addhex(&bp, &blen, in_token->value, in_token->length);
135 qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
136 qword_adduint(&bp, &blen, maj_stat);
137 qword_adduint(&bp, &blen, min_stat);
138 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
139 qword_addhex(&bp, &blen, out_token->value, out_token->length);
140 qword_addeol(&bp, &blen);
142 printerr(0, "WARNING: send_respsonse: message too long\n");
145 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
147 printerr(0, "WARNING: open %s failed: %s\n",
148 SVCGSSD_INIT_CHANNEL, strerror(errno));
152 printerr(3, "writing message: %s", buf);
153 if (write(g, buf, bp - buf) == -1) {
154 printerr(0, "WARNING: failed to write message\n");
162 #define rpc_auth_ok 0
163 #define rpc_autherr_badcred 1
164 #define rpc_autherr_rejectedcred 2
165 #define rpc_autherr_badverf 3
166 #define rpc_autherr_rejectedverf 4
167 #define rpc_autherr_tooweak 5
168 #define rpcsec_gsserr_credproblem 13
169 #define rpcsec_gsserr_ctxproblem 14
173 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
176 static gid_t *groups = NULL;
178 cred->cr_ngroups = NGROUPS;
179 ret = nfs4_gss_princ_to_grouplist(secname, name,
180 cred->cr_groups, &cred->cr_ngroups);
182 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
183 ret = nfs4_gss_princ_to_grouplist(secname, name,
184 groups, &cred->cr_ngroups);
186 cred->cr_ngroups = 0;
188 if (cred->cr_ngroups > NGROUPS)
189 cred->cr_ngroups = NGROUPS;
190 memcpy(cred->cr_groups, groups,
191 cred->cr_ngroups*sizeof(gid_t));
199 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
201 u_int32_t maj_stat, min_stat;
202 gss_buffer_desc name;
206 gss_OID name_type = GSS_C_NO_OID;
209 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
210 if (maj_stat != GSS_S_COMPLETE) {
211 pgsserr("get_ids: gss_display_name",
212 maj_stat, min_stat, mech);
215 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
216 !(sname = calloc(name.length + 1, 1))) {
217 printerr(0, "WARNING: get_ids: error allocating %d bytes "
218 "for sname\n", name.length + 1);
219 gss_release_buffer(&min_stat, &name);
222 memcpy(sname, name.value, name.length);
223 printerr(1, "sname = %s\n", sname);
224 gss_release_buffer(&min_stat, &name);
227 if ((secname = mech2file(mech)) == NULL) {
228 printerr(0, "WARNING: get_ids: error mapping mech to "
229 "file for name '%s'\n", sname);
232 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
233 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
236 * -ENOENT means there was no mapping, any other error
237 * value means there was an error trying to do the
239 * If there was no mapping, we send down the value -1
240 * to indicate that the anonuid/anongid for the export
243 if (res == -ENOENT) {
246 cred->cr_ngroups = 0;
250 printerr(0, "WARNING: get_ids: failed to map name '%s' "
251 "to uid/gid: %s\n", sname, strerror(-res));
256 add_supplementary_groups(secname, sname, cred);
267 print_hexl(int pri, unsigned char *cp, int length)
272 printerr(pri, "length %d\n",length);
275 for (i = 0; i < length; i += 0x10) {
276 printerr(pri, " %04x: ", (unsigned int)i);
278 jm = jm > 16 ? 16 : jm;
280 for (j = 0; j < jm; j++) {
282 printerr(pri, "%02x ", (unsigned int)cp[i+j]);
284 printerr(pri, "%02x", (unsigned int)cp[i+j]);
286 for (; j < 16; j++) {
294 for (j = 0; j < jm; j++) {
296 c = isprint(c) ? c : '.';
297 printerr(pri,"%c", c);
305 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
306 lnet_nid_t nid, uint32_t lustre_svc)
308 u_int32_t maj_stat, min_stat;
309 gss_buffer_desc name;
310 char *sname, *host, *realm;
311 const int namebuf_size = 512;
312 char namebuf[namebuf_size];
314 gss_OID name_type = GSS_C_NO_OID;
318 cred->cr_usr_root = cred->cr_usr_mds = cred->cr_usr_oss = 0;
319 cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
321 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
322 if (maj_stat != GSS_S_COMPLETE) {
323 pgsserr("get_ids: gss_display_name",
324 maj_stat, min_stat, mech);
327 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
328 !(sname = calloc(name.length + 1, 1))) {
329 printerr(0, "WARNING: get_ids: error allocating %zu bytes "
330 "for sname\n", name.length + 1);
331 gss_release_buffer(&min_stat, &name);
334 memcpy(sname, name.value, name.length);
335 sname[name.length] = '\0';
336 gss_release_buffer(&min_stat, &name);
338 if (lustre_svc == LUSTRE_GSS_SVC_MDS)
339 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
341 cred->cr_mapped_uid = -1;
343 realm = strchr(sname, '@');
347 printerr(0, "ERROR: %s has no realm name\n", sname);
351 host = strchr(sname, '/');
355 if (strcmp(sname, GSSD_SERVICE_MGS) == 0) {
356 printerr(0, "forbid %s as a user name\n", sname);
360 /* 1. check host part */
362 if (lnet_nid2hostname(nid, namebuf, namebuf_size)) {
363 printerr(0, "ERROR: failed to resolve hostname for "
364 "%s/%s@%s from %016llx\n",
365 sname, host, realm, nid);
369 if (strcasecmp(host, namebuf)) {
370 printerr(0, "ERROR: %s/%s@%s claimed hostname doesn't "
371 "match %s, nid %016llx\n", sname, host, realm,
376 if (!strcmp(sname, GSSD_SERVICE_MDS) ||
377 !strcmp(sname, GSSD_SERVICE_OSS)) {
378 printerr(0, "ERROR: %s@%s from %016llx doesn't "
379 "bind with hostname\n", sname, realm, nid);
384 /* 2. check realm and user */
385 switch (lustre_svc) {
386 case LUSTRE_GSS_SVC_MDS:
387 if (strcasecmp(mds_local_realm, realm)) {
390 /* only allow mapped user from remote realm */
391 if (cred->cr_mapped_uid == -1) {
392 printerr(0, "ERROR: %s%s%s@%s from %016llx "
393 "is remote but without mapping\n",
394 sname, host ? "/" : "",
395 host ? host : "", realm, nid);
399 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
401 cred->cr_usr_root = 1;
402 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
404 cred->cr_usr_mds = 1;
405 } else if (!strcmp(sname, GSSD_SERVICE_OSS)) {
407 cred->cr_usr_oss = 1;
409 pw = getpwnam(sname);
411 cred->cr_uid = pw->pw_uid;
412 printerr(2, "%s resolve to uid %u\n",
413 sname, cred->cr_uid);
414 } else if (cred->cr_mapped_uid != -1) {
415 printerr(2, "user %s from %016llx is "
416 "mapped to %u\n", sname, nid,
417 cred->cr_mapped_uid);
419 printerr(0, "ERROR: invalid user, "
420 "%s/%s@%s from %016llx\n",
421 sname, host, realm, nid);
429 case LUSTRE_GSS_SVC_MGS:
430 if (!strcmp(sname, GSSD_SERVICE_OSS)) {
432 cred->cr_usr_oss = 1;
435 case LUSTRE_GSS_SVC_OSS:
436 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
438 cred->cr_usr_root = 1;
439 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
441 cred->cr_usr_mds = 1;
443 if (cred->cr_uid == -1) {
444 printerr(0, "ERROR: svc %d doesn't accept user %s "
445 "from %016llx\n", lustre_svc, sname, nid);
456 printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n",
457 lustre_svc_name[lustre_svc], sname,
458 host ? "/" : "", host ? host : "", realm, nid);
463 typedef struct gss_union_ctx_id_t {
465 gss_ctx_id_t internal_ctx_id;
466 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
469 * return -1 only if we detect error during reading from upcall channel,
470 * all other cases return 0.
473 handle_nullreq(FILE *f) {
475 char in_tok_buf[TOKEN_BUF_SIZE];
476 char in_handle_buf[15];
477 char out_handle_buf[15];
478 gss_buffer_desc in_tok = {.value = in_tok_buf},
479 out_tok = {.value = NULL},
480 in_handle = {.value = in_handle_buf},
481 out_handle = {.value = out_handle_buf},
482 ctx_token = {.value = NULL},
483 ignore_out_tok = {.value = NULL},
484 /* XXX isn't there a define for this?: */
485 null_token = {.value = NULL};
489 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
490 gss_name_t client_name;
491 gss_OID mech = GSS_C_NO_OID;
492 gss_cred_id_t svc_cred;
493 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
494 u_int32_t ignore_min_stat;
496 struct svc_cred cred;
497 static char *lbuf = NULL;
498 static int lbuflen = 0;
501 printerr(2, "handling null request\n");
503 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
504 printerr(0, "WARNING: handle_nullreq: "
505 "failed reading request\n");
511 qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
512 qword_get(&cp, (char *) &nid, sizeof(nid));
513 qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
514 printerr(2, "handling req: svc %u, nid %016llx, idx %"PRIx64"\n",
515 lustre_svc, nid, handle_seq);
517 get_len = qword_get(&cp, in_handle.value, sizeof(in_handle_buf));
519 printerr(0, "WARNING: handle_nullreq: "
520 "failed parsing request\n");
523 in_handle.length = (size_t)get_len;
525 printerr(3, "in_handle:\n");
526 print_hexl(3, in_handle.value, in_handle.length);
528 get_len = qword_get(&cp, in_tok.value, sizeof(in_tok_buf));
530 printerr(0, "WARNING: handle_nullreq: "
531 "failed parsing request\n");
534 in_tok.length = (size_t)get_len;
536 printerr(3, "in_tok:\n");
537 print_hexl(3, in_tok.value, in_tok.length);
539 if (in_handle.length != 0) { /* CONTINUE_INIT case */
540 if (in_handle.length != sizeof(ctx)) {
541 printerr(0, "WARNING: handle_nullreq: "
542 "input handle has unexpected length %zu\n",
546 /* in_handle is the context id stored in the out_handle
547 * for the GSS_S_CONTINUE_NEEDED case below. */
548 memcpy(&ctx, in_handle.value, in_handle.length);
551 svc_cred = gssd_select_svc_cred(lustre_svc);
553 printerr(0, "no service credential for svc %u\n", lustre_svc);
557 maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
558 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
559 &mech, &out_tok, &ret_flags, NULL, NULL);
561 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
562 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
564 /* Save the context handle for future calls */
565 out_handle.length = sizeof(ctx);
566 memcpy(out_handle.value, &ctx, sizeof(ctx));
567 goto continue_needed;
569 else if (maj_stat != GSS_S_COMPLETE) {
570 printerr(0, "WARNING: gss_accept_sec_context failed\n");
571 pgsserr("handle_nullreq: gss_accept_sec_context",
572 maj_stat, min_stat, mech);
576 if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
577 /* get_ids() prints error msg */
578 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
579 gss_release_name(&ignore_min_stat, &client_name);
582 gss_release_name(&ignore_min_stat, &client_name);
584 /* Context complete. Pass handle_seq in out_handle to use
585 * for context lookup in the kernel. */
586 out_handle.length = sizeof(handle_seq);
587 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
589 /* kernel needs ctx to calculate verifier on null response, so
590 * must give it context before doing null call: */
591 if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
592 printerr(0, "WARNING: handle_nullreq: "
593 "serialize_context_for_kernel failed\n");
594 maj_stat = GSS_S_FAILURE;
597 /* We no longer need the gss context */
598 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
600 do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
602 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
603 &out_handle, &out_tok);
605 if (ctx_token.value != NULL)
606 free(ctx_token.value);
607 if (out_tok.value != NULL)
608 gss_release_buffer(&ignore_min_stat, &out_tok);
612 if (ctx != GSS_C_NO_CONTEXT)
613 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
614 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
615 &null_token, &null_token);