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>
60 extern char * mech2file(gss_OID mech);
61 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.sptlrpc.context/channel"
62 #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.sptlrpc.init/channel"
64 #define TOKEN_BUF_SIZE 8192
77 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
78 gss_OID mech, gss_buffer_desc *context_token)
84 printerr(2, "doing downcall\n");
85 if ((fname = mech2file(mech)) == NULL)
87 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
89 printerr(0, "WARNING: unable to open downcall channel "
91 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
94 qword_printhex(f, out_handle->value, out_handle->length);
95 /* XXX are types OK for the rest of this? */
96 qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
97 qword_printint(f, cred->cr_remote);
98 qword_printint(f, cred->cr_usr_root);
99 qword_printint(f, cred->cr_usr_mds);
100 qword_printint(f, cred->cr_usr_oss);
101 qword_printint(f, cred->cr_mapped_uid);
102 qword_printint(f, cred->cr_uid);
103 qword_printint(f, cred->cr_gid);
104 qword_print(f, fname);
105 qword_printhex(f, context_token->value, context_token->length);
110 printerr(0, "WARNING: downcall failed\n");
114 struct gss_verifier {
116 gss_buffer_desc body;
119 #define RPCSEC_GSS_SEQ_WIN 5
122 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
123 u_int32_t maj_stat, u_int32_t min_stat,
124 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
126 char buf[2 * TOKEN_BUF_SIZE];
128 int blen = sizeof(buf);
132 printerr(2, "sending null reply\n");
134 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
135 qword_addhex(&bp, &blen, in_token->value, in_token->length);
136 qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
137 qword_adduint(&bp, &blen, maj_stat);
138 qword_adduint(&bp, &blen, min_stat);
139 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
140 qword_addhex(&bp, &blen, out_token->value, out_token->length);
141 qword_addeol(&bp, &blen);
143 printerr(0, "WARNING: send_respsonse: message too long\n");
146 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
148 printerr(0, "WARNING: open %s failed: %s\n",
149 SVCGSSD_INIT_CHANNEL, strerror(errno));
153 printerr(3, "writing message: %s", buf);
154 if (write(g, buf, bp - buf) == -1) {
155 printerr(0, "WARNING: failed to write message\n");
163 #define rpc_auth_ok 0
164 #define rpc_autherr_badcred 1
165 #define rpc_autherr_rejectedcred 2
166 #define rpc_autherr_badverf 3
167 #define rpc_autherr_rejectedverf 4
168 #define rpc_autherr_tooweak 5
169 #define rpcsec_gsserr_credproblem 13
170 #define rpcsec_gsserr_ctxproblem 14
174 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
177 static gid_t *groups = NULL;
179 cred->cr_ngroups = NGROUPS;
180 ret = nfs4_gss_princ_to_grouplist(secname, name,
181 cred->cr_groups, &cred->cr_ngroups);
183 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
184 ret = nfs4_gss_princ_to_grouplist(secname, name,
185 groups, &cred->cr_ngroups);
187 cred->cr_ngroups = 0;
189 if (cred->cr_ngroups > NGROUPS)
190 cred->cr_ngroups = NGROUPS;
191 memcpy(cred->cr_groups, groups,
192 cred->cr_ngroups*sizeof(gid_t));
200 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
202 u_int32_t maj_stat, min_stat;
203 gss_buffer_desc name;
207 gss_OID name_type = GSS_C_NO_OID;
210 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
211 if (maj_stat != GSS_S_COMPLETE) {
212 pgsserr("get_ids: gss_display_name",
213 maj_stat, min_stat, mech);
216 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
217 !(sname = calloc(name.length + 1, 1))) {
218 printerr(0, "WARNING: get_ids: error allocating %d bytes "
219 "for sname\n", name.length + 1);
220 gss_release_buffer(&min_stat, &name);
223 memcpy(sname, name.value, name.length);
224 printerr(1, "sname = %s\n", sname);
225 gss_release_buffer(&min_stat, &name);
228 if ((secname = mech2file(mech)) == NULL) {
229 printerr(0, "WARNING: get_ids: error mapping mech to "
230 "file for name '%s'\n", sname);
233 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
234 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
237 * -ENOENT means there was no mapping, any other error
238 * value means there was an error trying to do the
240 * If there was no mapping, we send down the value -1
241 * to indicate that the anonuid/anongid for the export
244 if (res == -ENOENT) {
247 cred->cr_ngroups = 0;
251 printerr(0, "WARNING: get_ids: failed to map name '%s' "
252 "to uid/gid: %s\n", sname, strerror(-res));
257 add_supplementary_groups(secname, sname, cred);
268 print_hexl(int pri, unsigned char *cp, int length)
273 printerr(pri, "length %d\n",length);
276 for (i = 0; i < length; i += 0x10) {
277 printerr(pri, " %04x: ", (unsigned int)i);
279 jm = jm > 16 ? 16 : jm;
281 for (j = 0; j < jm; j++) {
283 printerr(pri, "%02x ", (unsigned int)cp[i+j]);
285 printerr(pri, "%02x", (unsigned int)cp[i+j]);
287 for (; j < 16; j++) {
295 for (j = 0; j < jm; j++) {
297 c = isprint(c) ? c : '.';
298 printerr(pri,"%c", c);
306 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
307 lnet_nid_t nid, uint32_t lustre_svc)
309 u_int32_t maj_stat, min_stat;
310 gss_buffer_desc name;
311 char *sname, *host, *realm;
312 const int namebuf_size = 512;
313 char namebuf[namebuf_size];
315 gss_OID name_type = GSS_C_NO_OID;
319 cred->cr_usr_root = cred->cr_usr_mds = cred->cr_usr_oss = 0;
320 cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
322 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
323 if (maj_stat != GSS_S_COMPLETE) {
324 pgsserr("get_ids: gss_display_name",
325 maj_stat, min_stat, mech);
328 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
329 !(sname = calloc(name.length + 1, 1))) {
330 printerr(0, "WARNING: get_ids: error allocating %zu bytes "
331 "for sname\n", name.length + 1);
332 gss_release_buffer(&min_stat, &name);
335 memcpy(sname, name.value, name.length);
336 sname[name.length] = '\0';
337 gss_release_buffer(&min_stat, &name);
339 if (lustre_svc == LUSTRE_GSS_SVC_MDS)
340 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
342 cred->cr_mapped_uid = -1;
344 realm = strchr(sname, '@');
348 printerr(0, "ERROR: %s has no realm name\n", sname);
352 host = strchr(sname, '/');
356 if (strcmp(sname, GSSD_SERVICE_MGS) == 0) {
357 printerr(0, "forbid %s as a user name\n", sname);
361 /* 1. check host part */
363 if (lnet_nid2hostname(nid, namebuf, namebuf_size)) {
364 printerr(0, "ERROR: failed to resolve hostname for "
365 "%s/%s@%s from %016llx\n",
366 sname, host, realm, nid);
370 if (strcasecmp(host, namebuf)) {
371 printerr(0, "ERROR: %s/%s@%s claimed hostname doesn't "
372 "match %s, nid %016llx\n", sname, host, realm,
377 if (!strcmp(sname, GSSD_SERVICE_MDS) ||
378 !strcmp(sname, GSSD_SERVICE_OSS)) {
379 printerr(0, "ERROR: %s@%s from %016llx doesn't "
380 "bind with hostname\n", sname, realm, nid);
385 /* 2. check realm and user */
386 switch (lustre_svc) {
387 case LUSTRE_GSS_SVC_MDS:
388 if (strcasecmp(mds_local_realm, realm)) {
391 /* only allow mapped user from remote realm */
392 if (cred->cr_mapped_uid == -1) {
393 printerr(0, "ERROR: %s%s%s@%s from %016llx "
394 "is remote but without mapping\n",
395 sname, host ? "/" : "",
396 host ? host : "", realm, nid);
400 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
402 cred->cr_usr_root = 1;
403 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
405 cred->cr_usr_mds = 1;
406 } else if (!strcmp(sname, GSSD_SERVICE_OSS)) {
408 cred->cr_usr_oss = 1;
410 pw = getpwnam(sname);
412 cred->cr_uid = pw->pw_uid;
413 printerr(2, "%s resolve to uid %u\n",
414 sname, cred->cr_uid);
415 } else if (cred->cr_mapped_uid != -1) {
416 printerr(2, "user %s from %016llx is "
417 "mapped to %u\n", sname, nid,
418 cred->cr_mapped_uid);
420 printerr(0, "ERROR: invalid user, "
421 "%s/%s@%s from %016llx\n",
422 sname, host, realm, nid);
430 case LUSTRE_GSS_SVC_MGS:
431 if (!strcmp(sname, GSSD_SERVICE_OSS)) {
433 cred->cr_usr_oss = 1;
436 case LUSTRE_GSS_SVC_OSS:
437 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
439 cred->cr_usr_root = 1;
440 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
442 cred->cr_usr_mds = 1;
444 if (cred->cr_uid == -1) {
445 printerr(0, "ERROR: svc %d doesn't accept user %s "
446 "from %016llx\n", lustre_svc, sname, nid);
457 printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n",
458 lustre_svc_name[lustre_svc], sname,
459 host ? "/" : "", host ? host : "", realm, nid);
464 typedef struct gss_union_ctx_id_t {
466 gss_ctx_id_t internal_ctx_id;
467 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
470 * return -1 only if we detect error during reading from upcall channel,
471 * all other cases return 0.
474 handle_nullreq(FILE *f) {
476 char in_tok_buf[TOKEN_BUF_SIZE];
477 char in_handle_buf[15];
478 char out_handle_buf[15];
479 gss_buffer_desc in_tok = {.value = in_tok_buf},
480 out_tok = {.value = NULL},
481 in_handle = {.value = in_handle_buf},
482 out_handle = {.value = out_handle_buf},
483 ctx_token = {.value = NULL},
484 ignore_out_tok = {.value = NULL},
485 /* XXX isn't there a define for this?: */
486 null_token = {.value = NULL};
490 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
491 gss_name_t client_name;
492 gss_OID mech = GSS_C_NO_OID;
493 gss_cred_id_t svc_cred;
494 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
495 u_int32_t ignore_min_stat;
497 struct svc_cred cred;
498 static char *lbuf = NULL;
499 static int lbuflen = 0;
502 printerr(2, "handling null request\n");
504 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
505 printerr(0, "WARNING: handle_nullreq: "
506 "failed reading request\n");
512 qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
513 qword_get(&cp, (char *) &nid, sizeof(nid));
514 qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
515 printerr(2, "handling req: svc %u, nid %016llx, idx %"PRIx64"\n",
516 lustre_svc, nid, handle_seq);
518 get_len = qword_get(&cp, in_handle.value, sizeof(in_handle_buf));
520 printerr(0, "WARNING: handle_nullreq: "
521 "failed parsing request\n");
524 in_handle.length = (size_t)get_len;
526 printerr(3, "in_handle:\n");
527 print_hexl(3, in_handle.value, in_handle.length);
529 get_len = qword_get(&cp, in_tok.value, sizeof(in_tok_buf));
531 printerr(0, "WARNING: handle_nullreq: "
532 "failed parsing request\n");
535 in_tok.length = (size_t)get_len;
537 printerr(3, "in_tok:\n");
538 print_hexl(3, in_tok.value, in_tok.length);
540 if (in_handle.length != 0) { /* CONTINUE_INIT case */
541 if (in_handle.length != sizeof(ctx)) {
542 printerr(0, "WARNING: handle_nullreq: "
543 "input handle has unexpected length %zu\n",
547 /* in_handle is the context id stored in the out_handle
548 * for the GSS_S_CONTINUE_NEEDED case below. */
549 memcpy(&ctx, in_handle.value, in_handle.length);
552 svc_cred = gssd_select_svc_cred(lustre_svc);
554 printerr(0, "no service credential for svc %u\n", lustre_svc);
558 maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
559 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
560 &mech, &out_tok, &ret_flags, NULL, NULL);
562 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
563 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
565 /* Save the context handle for future calls */
566 out_handle.length = sizeof(ctx);
567 memcpy(out_handle.value, &ctx, sizeof(ctx));
568 goto continue_needed;
570 else if (maj_stat != GSS_S_COMPLETE) {
571 printerr(0, "WARNING: gss_accept_sec_context failed\n");
572 pgsserr("handle_nullreq: gss_accept_sec_context",
573 maj_stat, min_stat, mech);
577 if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
578 /* get_ids() prints error msg */
579 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
580 gss_release_name(&ignore_min_stat, &client_name);
583 gss_release_name(&ignore_min_stat, &client_name);
585 /* Context complete. Pass handle_seq in out_handle to use
586 * for context lookup in the kernel. */
587 out_handle.length = sizeof(handle_seq);
588 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
590 /* kernel needs ctx to calculate verifier on null response, so
591 * must give it context before doing null call: */
592 if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
593 printerr(0, "WARNING: handle_nullreq: "
594 "serialize_context_for_kernel failed\n");
595 maj_stat = GSS_S_FAILURE;
598 /* We no longer need the gss context */
599 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
601 do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
603 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
604 &out_handle, &out_tok);
606 if (ctx_token.value != NULL)
607 free(ctx_token.value);
608 if (out_tok.value != NULL)
609 gss_release_buffer(&ignore_min_stat, &out_tok);
613 if (ctx != GSS_C_NO_CONTEXT)
614 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
615 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
616 &null_token, &null_token);