4 Copyright (c) 2000 The Regents of the University of Michigan.
7 Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
13 1. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15 2. Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18 3. Neither the name of the University nor the names of its
19 contributors may be used to endorse or promote products derived
20 from this software without specific prior written permission.
22 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 #include <sys/param.h>
55 extern char * mech2file(gss_OID mech);
56 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.sptlrpc.context/channel"
57 #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.sptlrpc.init/channel"
59 #define TOKEN_BUF_SIZE 8192
71 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
72 gss_OID mech, gss_buffer_desc *context_token)
78 printerr(2, "doing downcall\n");
79 if ((fname = mech2file(mech)) == NULL)
81 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
83 printerr(0, "WARNING: unable to open downcall channel "
85 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
88 qword_printhex(f, out_handle->value, out_handle->length);
89 /* XXX are types OK for the rest of this? */
90 qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
91 qword_printint(f, cred->cr_remote);
92 qword_printint(f, cred->cr_usr_root);
93 qword_printint(f, cred->cr_usr_mds);
94 qword_printint(f, cred->cr_mapped_uid);
95 qword_printint(f, cred->cr_uid);
96 qword_printint(f, cred->cr_gid);
97 qword_print(f, fname);
98 qword_printhex(f, context_token->value, context_token->length);
103 printerr(0, "WARNING: downcall failed\n");
107 struct gss_verifier {
109 gss_buffer_desc body;
112 #define RPCSEC_GSS_SEQ_WIN 5
115 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
116 u_int32_t maj_stat, u_int32_t min_stat,
117 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
119 char buf[2 * TOKEN_BUF_SIZE];
121 int blen = sizeof(buf);
125 printerr(2, "sending null reply\n");
127 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
128 qword_addhex(&bp, &blen, in_token->value, in_token->length);
129 qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
130 qword_adduint(&bp, &blen, maj_stat);
131 qword_adduint(&bp, &blen, min_stat);
132 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
133 qword_addhex(&bp, &blen, out_token->value, out_token->length);
134 qword_addeol(&bp, &blen);
136 printerr(0, "WARNING: send_respsonse: message too long\n");
139 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
141 printerr(0, "WARNING: open %s failed: %s\n",
142 SVCGSSD_INIT_CHANNEL, strerror(errno));
146 printerr(3, "writing message: %s", buf);
147 if (write(g, buf, bp - buf) == -1) {
148 printerr(0, "WARNING: failed to write message\n");
156 #define rpc_auth_ok 0
157 #define rpc_autherr_badcred 1
158 #define rpc_autherr_rejectedcred 2
159 #define rpc_autherr_badverf 3
160 #define rpc_autherr_rejectedverf 4
161 #define rpc_autherr_tooweak 5
162 #define rpcsec_gsserr_credproblem 13
163 #define rpcsec_gsserr_ctxproblem 14
167 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
170 static gid_t *groups = NULL;
172 cred->cr_ngroups = NGROUPS;
173 ret = nfs4_gss_princ_to_grouplist(secname, name,
174 cred->cr_groups, &cred->cr_ngroups);
176 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
177 ret = nfs4_gss_princ_to_grouplist(secname, name,
178 groups, &cred->cr_ngroups);
180 cred->cr_ngroups = 0;
182 if (cred->cr_ngroups > NGROUPS)
183 cred->cr_ngroups = NGROUPS;
184 memcpy(cred->cr_groups, groups,
185 cred->cr_ngroups*sizeof(gid_t));
193 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
195 u_int32_t maj_stat, min_stat;
196 gss_buffer_desc name;
200 gss_OID name_type = GSS_C_NO_OID;
203 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
204 if (maj_stat != GSS_S_COMPLETE) {
205 pgsserr("get_ids: gss_display_name",
206 maj_stat, min_stat, mech);
209 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
210 !(sname = calloc(name.length + 1, 1))) {
211 printerr(0, "WARNING: get_ids: error allocating %d bytes "
212 "for sname\n", name.length + 1);
213 gss_release_buffer(&min_stat, &name);
216 memcpy(sname, name.value, name.length);
217 printerr(1, "sname = %s\n", sname);
218 gss_release_buffer(&min_stat, &name);
221 if ((secname = mech2file(mech)) == NULL) {
222 printerr(0, "WARNING: get_ids: error mapping mech to "
223 "file for name '%s'\n", sname);
226 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
227 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
230 * -ENOENT means there was no mapping, any other error
231 * value means there was an error trying to do the
233 * If there was no mapping, we send down the value -1
234 * to indicate that the anonuid/anongid for the export
237 if (res == -ENOENT) {
240 cred->cr_ngroups = 0;
244 printerr(0, "WARNING: get_ids: failed to map name '%s' "
245 "to uid/gid: %s\n", sname, strerror(-res));
250 add_supplementary_groups(secname, sname, cred);
261 print_hexl(int pri, unsigned char *cp, int length)
266 printerr(pri, "length %d\n",length);
269 for (i = 0; i < length; i += 0x10) {
270 printerr(pri, " %04x: ", (u_int)i);
272 jm = jm > 16 ? 16 : jm;
274 for (j = 0; j < jm; j++) {
276 printerr(pri,"%02x ", (u_int)cp[i+j]);
278 printerr(pri,"%02x", (u_int)cp[i+j]);
280 for (; j < 16; j++) {
288 for (j = 0; j < jm; j++) {
290 c = isprint(c) ? c : '.';
291 printerr(pri,"%c", c);
299 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
300 lnet_nid_t nid, uint32_t lustre_svc)
302 u_int32_t maj_stat, min_stat;
303 gss_buffer_desc name;
304 char *sname, *host, *realm;
305 const int namebuf_size = 512;
306 char namebuf[namebuf_size];
308 gss_OID name_type = GSS_C_NO_OID;
311 cred->cr_remote = cred->cr_usr_root = cred->cr_usr_mds = 0;
312 cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
314 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
315 if (maj_stat != GSS_S_COMPLETE) {
316 pgsserr("get_ids: gss_display_name",
317 maj_stat, min_stat, mech);
320 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
321 !(sname = calloc(name.length + 1, 1))) {
322 printerr(0, "WARNING: get_ids: error allocating %d bytes "
323 "for sname\n", name.length + 1);
324 gss_release_buffer(&min_stat, &name);
327 memcpy(sname, name.value, name.length);
328 gss_release_buffer(&min_stat, &name);
330 if (lustre_svc == LUSTRE_GSS_SVC_MDS)
331 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
333 cred->cr_mapped_uid = -1;
335 realm = strchr(sname, '@');
339 printerr(0, "ERROR: %s has no realm name\n", sname);
343 host = strchr(sname, '/');
347 if (strcmp(sname, GSSD_SERVICE_OSS) == 0 ||
348 strcmp(sname, GSSD_SERVICE_MGS) == 0) {
349 printerr(0, "forbid %s as user name\n", sname);
353 /* 1. check host part */
355 if (lnet_nid2hostname(nid, namebuf, namebuf_size)) {
356 printerr(0, "ERROR: failed to resolve hostname for "
357 "%s/%s@%s from %016llx\n",
358 sname, host, realm, nid);
362 if (strcasecmp(host, namebuf)) {
363 printerr(0, "ERROR: %s/%s@%s claimed hostname doesn't "
364 "match %s, nid %016llx\n", sname, host, realm,
369 if (!strcmp(sname, GSSD_SERVICE_MDS)) {
370 printerr(0, "ERROR: "GSSD_SERVICE_MDS"@%s from %016llx "
371 "doesn't bind with hostname\n", realm, nid);
377 if (!mds_local_realm || strcasecmp(mds_local_realm, realm)) {
380 /* Allow mapped user from remote realm */
381 if (cred->cr_mapped_uid != -1)
383 /* Allow OSS auth using client machine credential */
384 else if (lustre_svc == LUSTRE_GSS_SVC_OSS &&
385 !strcmp(sname, LUSTRE_ROOT_NAME))
387 /* Invalid remote user */
389 printerr(0, "ERROR: %s%s%s@%s from %016llx is remote "
390 "but without mapping\n", sname,
391 host ? "/" : "", host ? host : "", realm, nid);
393 /* skip local user check */
398 if (!(pw = getpwnam(sname))) {
399 /* map lustre_root/lustre_mds to root user, which is subject
400 * to further mapping by root-squash in kernel. */
401 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
403 cred->cr_usr_root = 1;
404 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
406 cred->cr_usr_mds = 1;
408 if (cred->cr_mapped_uid == -1) {
409 printerr(0, "ERROR: invalid user, %s/%s@%s "
410 "from %016llx\n", sname, host,
415 printerr(2, "user %s from %016llx is mapped to %u\n",
416 sname, nid, cred->cr_mapped_uid);
418 /* note: a mapped local user will go to here too */
419 cred->cr_uid = pw->pw_uid;
420 printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid);
426 printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n",
427 lustre_svc_name[lustre_svc], sname,
428 host ? "/" : "", host ? host : "", realm, nid);
433 typedef struct gss_union_ctx_id_t {
435 gss_ctx_id_t internal_ctx_id;
436 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
439 * return -1 only if we detect error during reading from upcall channel,
440 * all other cases return 0.
443 handle_nullreq(FILE *f) {
445 char in_tok_buf[TOKEN_BUF_SIZE];
446 char in_handle_buf[15];
447 char out_handle_buf[15];
448 gss_buffer_desc in_tok = {.value = in_tok_buf},
449 out_tok = {.value = NULL},
450 in_handle = {.value = in_handle_buf},
451 out_handle = {.value = out_handle_buf},
452 ctx_token = {.value = NULL},
453 ignore_out_tok = {.value = NULL},
454 /* XXX isn't there a define for this?: */
455 null_token = {.value = NULL};
459 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
460 gss_name_t client_name;
461 gss_OID mech = GSS_C_NO_OID;
462 gss_cred_id_t svc_cred;
463 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
464 u_int32_t ignore_min_stat;
465 struct svc_cred cred;
466 static char *lbuf = NULL;
467 static int lbuflen = 0;
470 printerr(2, "handling null request\n");
472 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
473 printerr(0, "WARNING: handle_nullreq: "
474 "failed reading request\n");
480 qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
481 qword_get(&cp, (char *) &nid, sizeof(nid));
482 qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
483 printerr(2, "handling req: svc %u, nid %016llx, idx %llx\n",
484 lustre_svc, nid, handle_seq);
486 in_handle.length = (size_t) qword_get(&cp, in_handle.value,
487 sizeof(in_handle_buf));
488 printerr(3, "in_handle: \n");
489 print_hexl(3, in_handle.value, in_handle.length);
491 in_tok.length = (size_t) qword_get(&cp, in_tok.value,
493 printerr(3, "in_tok: \n");
494 print_hexl(3, in_tok.value, in_tok.length);
496 if (in_tok.length < 0) {
497 printerr(0, "WARNING: handle_nullreq: "
498 "failed parsing request\n");
502 if (in_handle.length != 0) { /* CONTINUE_INIT case */
503 if (in_handle.length != sizeof(ctx)) {
504 printerr(0, "WARNING: handle_nullreq: "
505 "input handle has unexpected length %d\n",
509 /* in_handle is the context id stored in the out_handle
510 * for the GSS_S_CONTINUE_NEEDED case below. */
511 memcpy(&ctx, in_handle.value, in_handle.length);
514 svc_cred = gssd_select_svc_cred(lustre_svc);
516 printerr(0, "no service credential for svc %u\n", lustre_svc);
520 maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
521 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
522 &mech, &out_tok, &ret_flags, NULL, NULL);
524 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
525 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
527 /* Save the context handle for future calls */
528 out_handle.length = sizeof(ctx);
529 memcpy(out_handle.value, &ctx, sizeof(ctx));
530 goto continue_needed;
532 else if (maj_stat != GSS_S_COMPLETE) {
533 printerr(0, "WARNING: gss_accept_sec_context failed\n");
534 pgsserr("handle_nullreq: gss_accept_sec_context",
535 maj_stat, min_stat, mech);
539 if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
540 /* get_ids() prints error msg */
541 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
542 gss_release_name(&ignore_min_stat, &client_name);
545 gss_release_name(&ignore_min_stat, &client_name);
547 /* Context complete. Pass handle_seq in out_handle to use
548 * for context lookup in the kernel. */
549 out_handle.length = sizeof(handle_seq);
550 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
552 /* kernel needs ctx to calculate verifier on null response, so
553 * must give it context before doing null call: */
554 if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
555 printerr(0, "WARNING: handle_nullreq: "
556 "serialize_context_for_kernel failed\n");
557 maj_stat = GSS_S_FAILURE;
560 /* We no longer need the gss context */
561 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
563 do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
565 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
566 &out_handle, &out_tok);
568 if (ctx_token.value != NULL)
569 free(ctx_token.value);
570 if (out_tok.value != NULL)
571 gss_release_buffer(&ignore_min_stat, &out_tok);
575 if (ctx != GSS_C_NO_CONTEXT)
576 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
577 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
578 &null_token, &null_token);