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>
56 extern char * mech2file(gss_OID mech);
57 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
58 #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.rpcsec.init/channel"
60 #define TOKEN_BUF_SIZE 8192
66 gid_t cr_groups[NGROUPS];
70 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
71 gss_OID mech, gss_buffer_desc *context_token,
79 printerr(1, "doing downcall\n");
80 if ((fname = mech2file(mech)) == NULL)
82 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
84 printerr(0, "WARNING: unable to open downcall channel "
86 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
89 qword_printhex(f, out_handle->value, out_handle->length);
90 /* XXX are types OK for the rest of this? */
91 /* For context cache, use the actual context endtime */
92 qword_printint(f, endtime);
93 qword_printint(f, cred->cr_uid);
94 qword_printint(f, cred->cr_gid);
95 qword_printint(f, cred->cr_ngroups);
96 printerr(2, "mech: %s, hndl len: %d, ctx len %d, timeout: %d (%d from now), "
97 "uid: %d, gid: %d, num aux grps: %d:\n",
98 fname, out_handle->length, context_token->length,
99 endtime, endtime - time(0),
100 cred->cr_uid, cred->cr_gid, cred->cr_ngroups);
101 for (i=0; i < cred->cr_ngroups; i++) {
102 qword_printint(f, cred->cr_groups[i]);
103 printerr(2, " (%4d) %d\n", i+1, cred->cr_groups[i]);
105 qword_print(f, fname);
106 qword_printhex(f, context_token->value, context_token->length);
111 printerr(1, "WARNING: downcall failed\n");
115 struct gss_verifier {
117 gss_buffer_desc body;
120 #define RPCSEC_GSS_SEQ_WIN 5
123 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
124 u_int32_t maj_stat, u_int32_t min_stat,
125 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
127 char buf[2 * TOKEN_BUF_SIZE];
129 int blen = sizeof(buf);
133 printerr(1, "sending null reply\n");
135 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
136 qword_addhex(&bp, &blen, in_token->value, in_token->length);
137 /* For init cache, only needed for a short time */
138 qword_addint(&bp, &blen, time(0) + 60);
139 qword_adduint(&bp, &blen, maj_stat);
140 qword_adduint(&bp, &blen, min_stat);
141 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
142 qword_addhex(&bp, &blen, out_token->value, out_token->length);
143 qword_addeol(&bp, &blen);
145 printerr(0, "WARNING: send_respsonse: message too long\n");
148 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
150 printerr(0, "WARNING: open %s failed: %s\n",
151 SVCGSSD_INIT_CHANNEL, strerror(errno));
155 printerr(3, "writing message: %s", buf);
156 if (write(g, buf, bp - buf) == -1) {
157 printerr(0, "WARNING: failed to write message\n");
165 #define rpc_auth_ok 0
166 #define rpc_autherr_badcred 1
167 #define rpc_autherr_rejectedcred 2
168 #define rpc_autherr_badverf 3
169 #define rpc_autherr_rejectedverf 4
170 #define rpc_autherr_tooweak 5
171 #define rpcsec_gsserr_credproblem 13
172 #define rpcsec_gsserr_ctxproblem 14
175 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
178 static gid_t *groups = NULL;
180 cred->cr_ngroups = NGROUPS;
181 ret = nfs4_gss_princ_to_grouplist(secname, name,
182 cred->cr_groups, &cred->cr_ngroups);
184 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
185 ret = nfs4_gss_princ_to_grouplist(secname, name,
186 groups, &cred->cr_ngroups);
188 cred->cr_ngroups = 0;
190 if (cred->cr_ngroups > NGROUPS)
191 cred->cr_ngroups = NGROUPS;
192 memcpy(cred->cr_groups, groups,
193 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(1, "WARNING: get_ids: failed to map name '%s' "
251 "to uid/gid: %s\n", sname, strerror(-res));
256 add_supplementary_groups(secname, sname, cred);
266 print_hexl(const char *description, unsigned char *cp, int length)
271 printf("%s (length %d)\n", description, length);
273 for (i = 0; i < length; i += 0x10) {
274 printf(" %04x: ", (u_int)i);
276 jm = jm > 16 ? 16 : jm;
278 for (j = 0; j < jm; j++) {
280 printf("%02x ", (u_int)cp[i+j]);
282 printf("%02x", (u_int)cp[i+j]);
284 for (; j < 16; j++) {
292 for (j = 0; j < jm; j++) {
294 c = isprint(c) ? c : '.';
303 handle_nullreq(FILE *f) {
304 /* XXX initialize to a random integer to reduce chances of unnecessary
305 * invalidation of existing ctx's on restarting svcgssd. */
306 static u_int32_t handle_seq = 0;
307 char in_tok_buf[TOKEN_BUF_SIZE];
308 char in_handle_buf[15];
309 char out_handle_buf[15];
310 gss_buffer_desc in_tok = {.value = in_tok_buf},
311 out_tok = {.value = NULL},
312 in_handle = {.value = in_handle_buf},
313 out_handle = {.value = out_handle_buf},
314 ctx_token = {.value = NULL},
315 ignore_out_tok = {.value = NULL},
316 /* XXX isn't there a define for this?: */
317 null_token = {.value = NULL};
319 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
320 gss_name_t client_name;
321 gss_OID mech = GSS_C_NO_OID;
322 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
323 u_int32_t ignore_min_stat;
324 struct svc_cred cred;
325 static char *lbuf = NULL;
326 static int lbuflen = 0;
330 printerr(1, "handling null request\n");
332 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
333 printerr(0, "WARNING: handle_nullreq: "
334 "failed reading request\n");
340 in_handle.length = (size_t) qword_get(&cp, in_handle.value,
341 sizeof(in_handle_buf));
343 print_hexl("in_handle", in_handle.value, in_handle.length);
346 in_tok.length = (size_t) qword_get(&cp, in_tok.value,
349 print_hexl("in_tok", in_tok.value, in_tok.length);
352 if (in_tok.length < 0) {
353 printerr(0, "WARNING: handle_nullreq: "
354 "failed parsing request\n");
358 if (in_handle.length != 0) { /* CONTINUE_INIT case */
359 if (in_handle.length != sizeof(ctx)) {
360 printerr(0, "WARNING: handle_nullreq: "
361 "input handle has unexpected length %d\n",
365 /* in_handle is the context id stored in the out_handle
366 * for the GSS_S_CONTINUE_NEEDED case below. */
367 memcpy(&ctx, in_handle.value, in_handle.length);
370 maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
371 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
372 &mech, &out_tok, &ret_flags, NULL, NULL);
374 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
375 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
377 /* Save the context handle for future calls */
378 out_handle.length = sizeof(ctx);
379 memcpy(out_handle.value, &ctx, sizeof(ctx));
380 goto continue_needed;
382 else if (maj_stat != GSS_S_COMPLETE) {
383 printerr(1, "WARNING: gss_accept_sec_context failed\n");
384 pgsserr("handle_nullreq: gss_accept_sec_context",
385 maj_stat, min_stat, mech);
388 if (get_ids(client_name, mech, &cred)) {
389 /* get_ids() prints error msg */
390 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
391 gss_release_name(&ignore_min_stat, &client_name);
394 gss_release_name(&ignore_min_stat, &client_name);
397 /* Context complete. Pass handle_seq in out_handle to use
398 * for context lookup in the kernel. */
400 out_handle.length = sizeof(handle_seq);
401 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
403 /* kernel needs ctx to calculate verifier on null response, so
404 * must give it context before doing null call: */
405 if (serialize_context_for_kernel(ctx, &ctx_token, mech, &ctx_endtime)) {
406 printerr(0, "WARNING: handle_nullreq: "
407 "serialize_context_for_kernel failed\n");
408 maj_stat = GSS_S_FAILURE;
411 /* We no longer need the gss context */
412 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
414 do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime);
416 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
417 &out_handle, &out_tok);
419 if (ctx_token.value != NULL)
420 free(ctx_token.value);
421 if (out_tok.value != NULL)
422 gss_release_buffer(&ignore_min_stat, &out_tok);
423 printerr(1, "finished handling null request\n");
427 if (ctx != GSS_C_NO_CONTEXT)
428 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
429 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
430 &null_token, &null_token);