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.
38 #endif /* HAVE_CONFIG_H */
40 #include <sys/param.h>
60 extern char * mech2file(gss_OID mech);
61 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
62 #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.rpcsec.init/channel"
64 #define TOKEN_BUF_SIZE 8192
70 gid_t cr_groups[NGROUPS];
74 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
75 gss_OID mech, gss_buffer_desc *context_token,
83 printerr(1, "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 /* For context cache, use the actual context endtime */
96 qword_printint(f, endtime);
97 qword_printint(f, cred->cr_uid);
98 qword_printint(f, cred->cr_gid);
99 qword_printint(f, cred->cr_ngroups);
100 printerr(2, "mech: %s, hndl len: %d, ctx len %d, timeout: %d (%d from now), "
101 "uid: %d, gid: %d, num aux grps: %d:\n",
102 fname, out_handle->length, context_token->length,
103 endtime, endtime - time(0),
104 cred->cr_uid, cred->cr_gid, cred->cr_ngroups);
105 for (i=0; i < cred->cr_ngroups; i++) {
106 qword_printint(f, cred->cr_groups[i]);
107 printerr(2, " (%4d) %d\n", i+1, cred->cr_groups[i]);
109 qword_print(f, fname);
110 qword_printhex(f, context_token->value, context_token->length);
113 printerr(1, "WARNING: error writing to downcall channel "
114 "%s: %s\n", SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
119 printerr(1, "WARNING: downcall failed\n");
123 struct gss_verifier {
125 gss_buffer_desc body;
128 #define RPCSEC_GSS_SEQ_WIN 5
131 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
132 u_int32_t maj_stat, u_int32_t min_stat,
133 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
135 char buf[2 * TOKEN_BUF_SIZE];
137 int blen = sizeof(buf);
141 printerr(1, "sending null reply\n");
143 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
144 qword_addhex(&bp, &blen, in_token->value, in_token->length);
145 /* For init cache, only needed for a short time */
146 qword_addint(&bp, &blen, time(0) + 60);
147 qword_adduint(&bp, &blen, maj_stat);
148 qword_adduint(&bp, &blen, min_stat);
149 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
150 qword_addhex(&bp, &blen, out_token->value, out_token->length);
151 qword_addeol(&bp, &blen);
153 printerr(0, "WARNING: send_respsonse: message too long\n");
156 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
158 printerr(0, "WARNING: open %s failed: %s\n",
159 SVCGSSD_INIT_CHANNEL, strerror(errno));
163 printerr(3, "writing message: %s", buf);
164 if (write(g, buf, bp - buf) == -1) {
165 printerr(0, "WARNING: failed to write message\n");
173 #define rpc_auth_ok 0
174 #define rpc_autherr_badcred 1
175 #define rpc_autherr_rejectedcred 2
176 #define rpc_autherr_badverf 3
177 #define rpc_autherr_rejectedverf 4
178 #define rpc_autherr_tooweak 5
179 #define rpcsec_gsserr_credproblem 13
180 #define rpcsec_gsserr_ctxproblem 14
183 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
186 static gid_t *groups = NULL;
188 cred->cr_ngroups = NGROUPS;
189 ret = nfs4_gss_princ_to_grouplist(secname, name,
190 cred->cr_groups, &cred->cr_ngroups);
192 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
193 ret = nfs4_gss_princ_to_grouplist(secname, name,
194 groups, &cred->cr_ngroups);
196 cred->cr_ngroups = 0;
198 if (cred->cr_ngroups > NGROUPS)
199 cred->cr_ngroups = NGROUPS;
200 memcpy(cred->cr_groups, groups,
201 cred->cr_ngroups*sizeof(gid_t));
207 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
209 u_int32_t maj_stat, min_stat;
210 gss_buffer_desc name;
214 gss_OID name_type = GSS_C_NO_OID;
217 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
218 if (maj_stat != GSS_S_COMPLETE) {
219 pgsserr("get_ids: gss_display_name",
220 maj_stat, min_stat, mech);
223 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
224 !(sname = calloc(name.length + 1, 1))) {
225 printerr(0, "WARNING: get_ids: error allocating %d bytes "
226 "for sname\n", name.length + 1);
227 gss_release_buffer(&min_stat, &name);
230 memcpy(sname, name.value, name.length);
231 printerr(1, "sname = %s\n", sname);
232 gss_release_buffer(&min_stat, &name);
235 if ((secname = mech2file(mech)) == NULL) {
236 printerr(0, "WARNING: get_ids: error mapping mech to "
237 "file for name '%s'\n", sname);
240 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
241 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
244 * -ENOENT means there was no mapping, any other error
245 * value means there was an error trying to do the
247 * If there was no mapping, we send down the value -1
248 * to indicate that the anonuid/anongid for the export
251 if (res == -ENOENT) {
254 cred->cr_ngroups = 0;
258 printerr(1, "WARNING: get_ids: failed to map name '%s' "
259 "to uid/gid: %s\n", sname, strerror(-res));
264 add_supplementary_groups(secname, sname, cred);
274 print_hexl(const char *description, unsigned char *cp, int length)
279 printf("%s (length %d)\n", description, length);
281 for (i = 0; i < length; i += 0x10) {
282 printf(" %04x: ", (u_int)i);
284 jm = jm > 16 ? 16 : jm;
286 for (j = 0; j < jm; j++) {
288 printf("%02x ", (u_int)cp[i+j]);
290 printf("%02x", (u_int)cp[i+j]);
292 for (; j < 16; j++) {
300 for (j = 0; j < jm; j++) {
302 c = isprint(c) ? c : '.';
311 handle_nullreq(FILE *f) {
312 /* XXX initialize to a random integer to reduce chances of unnecessary
313 * invalidation of existing ctx's on restarting svcgssd. */
314 static u_int32_t handle_seq = 0;
315 char in_tok_buf[TOKEN_BUF_SIZE];
316 char in_handle_buf[15];
317 char out_handle_buf[15];
318 gss_buffer_desc in_tok = {.value = in_tok_buf},
319 out_tok = {.value = NULL},
320 in_handle = {.value = in_handle_buf},
321 out_handle = {.value = out_handle_buf},
322 ctx_token = {.value = NULL},
323 ignore_out_tok = {.value = NULL},
324 /* XXX isn't there a define for this?: */
325 null_token = {.value = NULL};
327 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
328 gss_name_t client_name;
329 gss_OID mech = GSS_C_NO_OID;
330 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
331 u_int32_t ignore_min_stat;
332 struct svc_cred cred;
333 static char *lbuf = NULL;
334 static int lbuflen = 0;
338 printerr(1, "handling null request\n");
340 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
341 printerr(0, "WARNING: handle_nullreq: "
342 "failed reading request\n");
348 in_handle.length = (size_t) qword_get(&cp, in_handle.value,
349 sizeof(in_handle_buf));
351 print_hexl("in_handle", in_handle.value, in_handle.length);
354 in_tok.length = (size_t) qword_get(&cp, in_tok.value,
357 print_hexl("in_tok", in_tok.value, in_tok.length);
360 if (in_tok.length < 0) {
361 printerr(0, "WARNING: handle_nullreq: "
362 "failed parsing request\n");
366 if (in_handle.length != 0) { /* CONTINUE_INIT case */
367 if (in_handle.length != sizeof(ctx)) {
368 printerr(0, "WARNING: handle_nullreq: "
369 "input handle has unexpected length %d\n",
373 /* in_handle is the context id stored in the out_handle
374 * for the GSS_S_CONTINUE_NEEDED case below. */
375 memcpy(&ctx, in_handle.value, in_handle.length);
378 maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
379 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
380 &mech, &out_tok, &ret_flags, NULL, NULL);
382 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
383 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
385 /* Save the context handle for future calls */
386 out_handle.length = sizeof(ctx);
387 memcpy(out_handle.value, &ctx, sizeof(ctx));
388 goto continue_needed;
390 else if (maj_stat != GSS_S_COMPLETE) {
391 printerr(1, "WARNING: gss_accept_sec_context failed\n");
392 pgsserr("handle_nullreq: gss_accept_sec_context",
393 maj_stat, min_stat, mech);
396 if (get_ids(client_name, mech, &cred)) {
397 /* get_ids() prints error msg */
398 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
399 gss_release_name(&ignore_min_stat, &client_name);
402 gss_release_name(&ignore_min_stat, &client_name);
405 /* Context complete. Pass handle_seq in out_handle to use
406 * for context lookup in the kernel. */
408 out_handle.length = sizeof(handle_seq);
409 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
411 /* kernel needs ctx to calculate verifier on null response, so
412 * must give it context before doing null call: */
413 if (serialize_context_for_kernel(ctx, &ctx_token, mech, &ctx_endtime)) {
414 printerr(0, "WARNING: handle_nullreq: "
415 "serialize_context_for_kernel failed\n");
416 maj_stat = GSS_S_FAILURE;
419 /* We no longer need the gss context */
420 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
422 do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime);
424 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
425 &out_handle, &out_tok);
427 if (ctx_token.value != NULL)
428 free(ctx_token.value);
429 if (out_tok.value != NULL)
430 gss_release_buffer(&ignore_min_stat, &out_tok);
431 printerr(1, "finished handling null request\n");
435 if (ctx != GSS_C_NO_CONTEXT)
436 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
437 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
438 &null_token, &null_token);