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.rpcsec.context/channel"
57 #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.rpcsec.init/channel"
59 #define TOKEN_BUF_SIZE 8192
65 gid_t cr_groups[NGROUPS];
69 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
70 gss_OID mech, gss_buffer_desc *context_token)
76 printerr(1, "doing downcall\n");
77 if ((fname = mech2file(mech)) == NULL)
79 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
81 printerr(0, "WARNING: unable to open downcall channel "
83 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
86 qword_printhex(f, out_handle->value, out_handle->length);
87 /* XXX are types OK for the rest of this? */
88 qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
89 qword_printint(f, cred->cr_uid);
90 qword_printint(f, cred->cr_gid);
91 qword_printint(f, cred->cr_ngroups);
92 for (i=0; i < cred->cr_ngroups; i++)
93 qword_printint(f, cred->cr_groups[i]);
94 qword_print(f, fname);
95 qword_printhex(f, context_token->value, context_token->length);
100 printerr(0, "WARNING: downcall failed\n");
104 struct gss_verifier {
106 gss_buffer_desc body;
109 #define RPCSEC_GSS_SEQ_WIN 5
112 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
113 u_int32_t maj_stat, u_int32_t min_stat,
114 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
116 char buf[2 * TOKEN_BUF_SIZE];
118 int blen = sizeof(buf);
122 printerr(1, "sending null reply\n");
124 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
125 qword_addhex(&bp, &blen, in_token->value, in_token->length);
126 qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
127 qword_addint(&bp, &blen, maj_stat);
128 qword_addint(&bp, &blen, min_stat);
129 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
130 qword_addhex(&bp, &blen, out_token->value, out_token->length);
131 qword_addeol(&bp, &blen);
133 printerr(0, "WARNING: send_respsonse: message too long\n");
136 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
138 printerr(0, "WARNING: open %s failed: %s\n",
139 SVCGSSD_INIT_CHANNEL, strerror(errno));
143 printerr(3, "writing message: %s", buf);
144 if (write(g, buf, bp - buf) == -1) {
145 printerr(0, "WARNING: failed to write message\n");
153 #define rpc_auth_ok 0
154 #define rpc_autherr_badcred 1
155 #define rpc_autherr_rejectedcred 2
156 #define rpc_autherr_badverf 3
157 #define rpc_autherr_rejectedverf 4
158 #define rpc_autherr_tooweak 5
159 #define rpcsec_gsserr_credproblem 13
160 #define rpcsec_gsserr_ctxproblem 14
163 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
166 static gid_t *groups = NULL;
168 cred->cr_ngroups = NGROUPS;
169 ret = nfs4_gss_princ_to_grouplist(secname, name,
170 cred->cr_groups, &cred->cr_ngroups);
172 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
173 ret = nfs4_gss_princ_to_grouplist(secname, name,
174 groups, &cred->cr_ngroups);
176 cred->cr_ngroups = 0;
178 if (cred->cr_ngroups > NGROUPS)
179 cred->cr_ngroups = NGROUPS;
180 memcpy(cred->cr_groups, groups,
181 cred->cr_ngroups*sizeof(gid_t));
187 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
189 u_int32_t maj_stat, min_stat;
190 gss_buffer_desc name;
194 gss_OID name_type = GSS_C_NO_OID;
197 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
198 if (maj_stat != GSS_S_COMPLETE) {
199 pgsserr("get_ids: gss_display_name",
200 maj_stat, min_stat, mech);
203 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
204 !(sname = calloc(name.length + 1, 1))) {
205 printerr(0, "WARNING: get_ids: error allocating %d bytes "
206 "for sname\n", name.length + 1);
207 gss_release_buffer(&min_stat, &name);
210 memcpy(sname, name.value, name.length);
211 printerr(1, "sname = %s\n", sname);
212 gss_release_buffer(&min_stat, &name);
215 if ((secname = mech2file(mech)) == NULL) {
216 printerr(0, "WARNING: get_ids: error mapping mech to "
217 "file for name '%s'\n", sname);
220 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
221 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
224 * -ENOENT means there was no mapping, any other error
225 * value means there was an error trying to do the
228 if (res == -ENOENT) {
229 cred->cr_uid = -2; /* XXX */
230 cred->cr_gid = -2; /* XXX */
231 cred->cr_groups[0] = -2;/* XXX */
232 cred->cr_ngroups = 1;
236 printerr(0, "WARNING: get_ids: failed to map name '%s' "
237 "to uid/gid: %s\n", sname, strerror(-res));
242 add_supplementary_groups(secname, sname, cred);
251 print_hexl(int pri, unsigned char *cp, int length)
256 printerr(pri, "length %d\n",length);
259 for (i = 0; i < length; i += 0x10) {
260 printerr(pri, " %04x: ", (u_int)i);
262 jm = jm > 16 ? 16 : jm;
264 for (j = 0; j < jm; j++) {
266 printerr(pri,"%02x ", (u_int)cp[i+j]);
268 printerr(pri,"%02x", (u_int)cp[i+j]);
270 for (; j < 16; j++) {
278 for (j = 0; j < jm; j++) {
280 c = isprint(c) ? c : '.';
281 printerr(pri,"%c", c);
288 handle_nullreq(FILE *f) {
289 /* XXX initialize to a random integer to reduce chances of unnecessary
290 * invalidation of existing ctx's on restarting svcgssd. */
291 static u_int32_t handle_seq = 0;
292 char in_tok_buf[TOKEN_BUF_SIZE];
293 char in_handle_buf[15];
294 char out_handle_buf[15];
295 gss_buffer_desc in_tok = {.value = in_tok_buf},
296 out_tok = {.value = NULL},
297 in_handle = {.value = in_handle_buf},
298 out_handle = {.value = out_handle_buf},
299 ctx_token = {.value = NULL},
300 ignore_out_tok = {.value = NULL},
301 /* XXX isn't there a define for this?: */
302 null_token = {.value = NULL};
304 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
305 gss_name_t client_name;
306 gss_OID mech = GSS_C_NO_OID;
307 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
308 u_int32_t ignore_min_stat;
309 struct svc_cred cred;
310 static char *lbuf = NULL;
311 static int lbuflen = 0;
314 printerr(1, "handling null request\n");
316 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
317 printerr(0, "WARNING: handle_nullreq: "
318 "failed reading request\n");
324 in_handle.length = (size_t) qword_get(&cp, in_handle.value,
325 sizeof(in_handle_buf));
326 printerr(2, "in_handle: \n");
327 print_hexl(2, in_handle.value, in_handle.length);
329 in_tok.length = (size_t) qword_get(&cp, in_tok.value,
331 printerr(2, "in_tok: \n");
332 print_hexl(2, in_tok.value, in_tok.length);
334 if (in_tok.length < 0) {
335 printerr(0, "WARNING: handle_nullreq: "
336 "failed parsing request\n");
340 if (in_handle.length != 0) { /* CONTINUE_INIT case */
341 if (in_handle.length != sizeof(ctx)) {
342 printerr(0, "WARNING: handle_nullreq: "
343 "input handle has unexpected length %d\n",
347 /* in_handle is the context id stored in the out_handle
348 * for the GSS_S_CONTINUE_NEEDED case below. */
349 memcpy(&ctx, in_handle.value, in_handle.length);
352 maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
353 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
354 &mech, &out_tok, &ret_flags, NULL, NULL);
356 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
357 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
359 /* Save the context handle for future calls */
360 out_handle.length = sizeof(ctx);
361 memcpy(out_handle.value, &ctx, sizeof(ctx));
362 goto continue_needed;
364 else if (maj_stat != GSS_S_COMPLETE) {
365 printerr(0, "WARNING: gss_accept_sec_context failed\n");
366 pgsserr("handle_nullreq: gss_accept_sec_context",
367 maj_stat, min_stat, mech);
370 if (get_ids(client_name, mech, &cred)) {
371 /* get_ids() prints error msg */
372 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
373 gss_release_name(&ignore_min_stat, &client_name);
376 gss_release_name(&ignore_min_stat, &client_name);
379 /* Context complete. Pass handle_seq in out_handle to use
380 * for context lookup in the kernel. */
382 out_handle.length = sizeof(handle_seq);
383 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
385 /* kernel needs ctx to calculate verifier on null response, so
386 * must give it context before doing null call: */
387 if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
388 printerr(0, "WARNING: handle_nullreq: "
389 "serialize_context_for_kernel failed\n");
390 maj_stat = GSS_S_FAILURE;
393 /* We no longer need the gss context */
394 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
396 do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
398 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
399 &out_handle, &out_tok);
401 if (ctx_token.value != NULL)
402 free(ctx_token.value);
403 if (out_tok.value != NULL)
404 gss_release_buffer(&ignore_min_stat, &out_tok);
405 printerr(1, "finished handling null request\n");
409 if (ctx != GSS_C_NO_CONTEXT)
410 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
411 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
412 &null_token, &null_token);