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.ptlrpcs.context/channel"
57 #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.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)
77 printerr(2, "doing downcall\n");
78 if ((fname = mech2file(mech)) == NULL)
80 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
82 printerr(0, "WARNING: unable to open downcall channel "
84 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
87 qword_printhex(f, out_handle->value, out_handle->length);
88 /* XXX are types OK for the rest of this? */
89 qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
90 qword_printint(f, cred->cr_remote);
91 qword_printint(f, cred->cr_usr_root);
92 qword_printint(f, cred->cr_usr_mds);
93 qword_printint(f, cred->cr_mapped_uid);
94 qword_printint(f, cred->cr_uid);
95 qword_printint(f, cred->cr_gid);
96 qword_print(f, fname);
97 qword_printhex(f, context_token->value, context_token->length);
102 printerr(0, "WARNING: downcall failed\n");
106 struct gss_verifier {
108 gss_buffer_desc body;
111 #define RPCSEC_GSS_SEQ_WIN 5
114 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
115 u_int32_t maj_stat, u_int32_t min_stat,
116 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
118 char buf[2 * TOKEN_BUF_SIZE];
120 int blen = sizeof(buf);
124 printerr(2, "sending null reply\n");
126 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
127 qword_addhex(&bp, &blen, in_token->value, in_token->length);
128 qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
129 qword_addint(&bp, &blen, maj_stat);
130 qword_addint(&bp, &blen, min_stat);
131 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
132 qword_addhex(&bp, &blen, out_token->value, out_token->length);
133 qword_addeol(&bp, &blen);
135 printerr(0, "WARNING: send_respsonse: message too long\n");
138 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
140 printerr(0, "WARNING: open %s failed: %s\n",
141 SVCGSSD_INIT_CHANNEL, strerror(errno));
145 printerr(3, "writing message: %s", buf);
146 if (write(g, buf, bp - buf) == -1) {
147 printerr(0, "WARNING: failed to write message\n");
155 #define rpc_auth_ok 0
156 #define rpc_autherr_badcred 1
157 #define rpc_autherr_rejectedcred 2
158 #define rpc_autherr_badverf 3
159 #define rpc_autherr_rejectedverf 4
160 #define rpc_autherr_tooweak 5
161 #define rpcsec_gsserr_credproblem 13
162 #define rpcsec_gsserr_ctxproblem 14
166 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
169 static gid_t *groups = NULL;
171 cred->cr_ngroups = NGROUPS;
172 ret = nfs4_gss_princ_to_grouplist(secname, name,
173 cred->cr_groups, &cred->cr_ngroups);
175 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
176 ret = nfs4_gss_princ_to_grouplist(secname, name,
177 groups, &cred->cr_ngroups);
179 cred->cr_ngroups = 0;
181 if (cred->cr_ngroups > NGROUPS)
182 cred->cr_ngroups = NGROUPS;
183 memcpy(cred->cr_groups, groups,
184 cred->cr_ngroups*sizeof(gid_t));
192 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
194 u_int32_t maj_stat, min_stat;
195 gss_buffer_desc name;
199 gss_OID name_type = GSS_C_NO_OID;
202 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
203 if (maj_stat != GSS_S_COMPLETE) {
204 pgsserr("get_ids: gss_display_name",
205 maj_stat, min_stat, mech);
208 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
209 !(sname = calloc(name.length + 1, 1))) {
210 printerr(0, "WARNING: get_ids: error allocating %d bytes "
211 "for sname\n", name.length + 1);
212 gss_release_buffer(&min_stat, &name);
215 memcpy(sname, name.value, name.length);
216 printerr(1, "sname = %s\n", sname);
217 gss_release_buffer(&min_stat, &name);
220 if ((secname = mech2file(mech)) == NULL) {
221 printerr(0, "WARNING: get_ids: error mapping mech to "
222 "file for name '%s'\n", sname);
225 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
226 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
229 * -ENOENT means there was no mapping, any other error
230 * value means there was an error trying to do the
232 * If there was no mapping, we send down the value -1
233 * to indicate that the anonuid/anongid for the export
236 if (res == -ENOENT) {
239 cred->cr_ngroups = 0;
243 printerr(0, "WARNING: get_ids: failed to map name '%s' "
244 "to uid/gid: %s\n", sname, strerror(-res));
249 add_supplementary_groups(secname, sname, cred);
260 print_hexl(int pri, unsigned char *cp, int length)
265 printerr(pri, "length %d\n",length);
268 for (i = 0; i < length; i += 0x10) {
269 printerr(pri, " %04x: ", (u_int)i);
271 jm = jm > 16 ? 16 : jm;
273 for (j = 0; j < jm; j++) {
275 printerr(pri,"%02x ", (u_int)cp[i+j]);
277 printerr(pri,"%02x", (u_int)cp[i+j]);
279 for (; j < 16; j++) {
287 for (j = 0; j < jm; j++) {
289 c = isprint(c) ? c : '.';
290 printerr(pri,"%c", c);
298 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
299 lnet_nid_t nid, uint32_t lustre_svc)
301 u_int32_t maj_stat, min_stat;
302 gss_buffer_desc name;
303 char *sname, *realm, *slash;
305 gss_OID name_type = GSS_C_NO_OID;
308 cred->cr_remote = cred->cr_usr_root = cred->cr_usr_mds = 0;
309 cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
311 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
312 if (maj_stat != GSS_S_COMPLETE) {
313 pgsserr("get_ids: gss_display_name",
314 maj_stat, min_stat, mech);
317 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
318 !(sname = calloc(name.length + 1, 1))) {
319 printerr(0, "WARNING: get_ids: error allocating %d bytes "
320 "for sname\n", name.length + 1);
321 gss_release_buffer(&min_stat, &name);
324 memcpy(sname, name.value, name.length);
325 printerr(1, "authenticated %s from %016llx\n", sname, nid);
326 gss_release_buffer(&min_stat, &name);
328 if (lustre_svc == LUSTRE_GSS_SVC_MDS)
329 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
331 cred->cr_mapped_uid = -1;
333 realm = strchr(sname, '@');
335 printerr(0, "WARNNING: principal %s contains no realm name\n",
337 cred->cr_remote = (mds_local_realm != NULL);
340 if (!mds_local_realm)
344 (strcasecmp(mds_local_realm, realm) != 0);
347 if (cred->cr_remote) {
348 if (cred->cr_mapped_uid != -1)
350 else if (lustre_svc == LUSTRE_GSS_SVC_OSS &&
351 strcmp(sname, "lustre_root") == 0)
354 printerr(0, "principal %s is remote without mapping\n",
359 slash = strchr(sname, '/');
363 if (!(pw = getpwnam(sname))) {
364 /* If client use machine credential, we map it to root, which
365 * will subject to further mapping by root-squash in kernel.
367 * MDS service keytab is treated as special user, also mapped
368 * to root. OSS service keytab can't be used as a user.
370 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
371 printerr(2, "lustre_root principal, resolve to uid 0\n");
373 cred->cr_usr_root = 1;
374 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
375 printerr(2, "mds service principal, resolve to uid 0\n");
377 cred->cr_usr_mds = 1;
380 if (cred->cr_mapped_uid == -1) {
381 printerr(0, "invalid user %s\n", sname);
384 printerr(2, "user %s mapped to %u\n",
385 sname, cred->cr_mapped_uid);
388 cred->cr_uid = pw->pw_uid;
389 printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid);
398 typedef struct gss_union_ctx_id_t {
400 gss_ctx_id_t internal_ctx_id;
401 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
404 * return -1 only if we detect error during reading from upcall channel,
405 * all other cases return 0.
408 handle_nullreq(FILE *f) {
410 char in_tok_buf[TOKEN_BUF_SIZE];
411 char in_handle_buf[15];
412 char out_handle_buf[15];
413 gss_buffer_desc in_tok = {.value = in_tok_buf},
414 out_tok = {.value = NULL},
415 in_handle = {.value = in_handle_buf},
416 out_handle = {.value = out_handle_buf},
417 ctx_token = {.value = NULL},
418 ignore_out_tok = {.value = NULL},
419 /* XXX isn't there a define for this?: */
420 null_token = {.value = NULL};
424 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
425 gss_name_t client_name;
426 gss_OID mech = GSS_C_NO_OID;
427 gss_cred_id_t svc_cred;
428 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
429 u_int32_t ignore_min_stat;
430 struct svc_cred cred;
431 static char *lbuf = NULL;
432 static int lbuflen = 0;
435 printerr(2, "handling null request\n");
437 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
438 printerr(0, "WARNING: handle_nullreq: "
439 "failed reading request\n");
445 qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
446 qword_get(&cp, (char *) &nid, sizeof(nid));
447 qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
448 printerr(1, "handling req: svc %u, nid %016llx, idx %llx\n",
449 lustre_svc, nid, handle_seq);
451 in_handle.length = (size_t) qword_get(&cp, in_handle.value,
452 sizeof(in_handle_buf));
453 printerr(3, "in_handle: \n");
454 print_hexl(3, in_handle.value, in_handle.length);
456 in_tok.length = (size_t) qword_get(&cp, in_tok.value,
458 printerr(3, "in_tok: \n");
459 print_hexl(3, in_tok.value, in_tok.length);
461 if (in_tok.length < 0) {
462 printerr(0, "WARNING: handle_nullreq: "
463 "failed parsing request\n");
467 if (in_handle.length != 0) { /* CONTINUE_INIT case */
468 if (in_handle.length != sizeof(ctx)) {
469 printerr(0, "WARNING: handle_nullreq: "
470 "input handle has unexpected length %d\n",
474 /* in_handle is the context id stored in the out_handle
475 * for the GSS_S_CONTINUE_NEEDED case below. */
476 memcpy(&ctx, in_handle.value, in_handle.length);
479 svc_cred = gssd_select_svc_cred(lustre_svc);
481 printerr(0, "no service credential for svc %u\n", lustre_svc);
485 maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
486 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
487 &mech, &out_tok, &ret_flags, NULL, NULL);
489 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
490 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
492 /* Save the context handle for future calls */
493 out_handle.length = sizeof(ctx);
494 memcpy(out_handle.value, &ctx, sizeof(ctx));
495 goto continue_needed;
497 else if (maj_stat != GSS_S_COMPLETE) {
498 printerr(0, "WARNING: gss_accept_sec_context failed\n");
499 pgsserr("handle_nullreq: gss_accept_sec_context",
500 maj_stat, min_stat, mech);
504 if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
505 /* get_ids() prints error msg */
506 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
507 gss_release_name(&ignore_min_stat, &client_name);
510 gss_release_name(&ignore_min_stat, &client_name);
512 /* Context complete. Pass handle_seq in out_handle to use
513 * for context lookup in the kernel. */
514 out_handle.length = sizeof(handle_seq);
515 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
517 /* kernel needs ctx to calculate verifier on null response, so
518 * must give it context before doing null call: */
519 if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
520 printerr(0, "WARNING: handle_nullreq: "
521 "serialize_context_for_kernel failed\n");
522 maj_stat = GSS_S_FAILURE;
525 /* We no longer need the gss context */
526 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
528 do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
530 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
531 &out_handle, &out_tok);
533 if (ctx_token.value != NULL)
534 free(ctx_token.value);
535 if (out_tok.value != NULL)
536 gss_release_buffer(&ignore_min_stat, &out_tok);
540 if (ctx != GSS_C_NO_CONTEXT)
541 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
542 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
543 &null_token, &null_token);