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)
77 printerr(1, "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_uid);
91 qword_printint(f, cred->cr_gid);
92 qword_printint(f, cred->cr_ngroups);
93 printerr(2, "mech: %s, hndl len: %d, ctx len %d, timeout: %d, "
94 "uid: %d, gid: %d, num aux grps: %d:\n",
95 fname, out_handle->length, context_token->length, 0x7fffffff,
96 cred->cr_uid, cred->cr_gid, cred->cr_ngroups);
97 for (i=0; i < cred->cr_ngroups; i++) {
98 qword_printint(f, cred->cr_groups[i]);
99 printerr(2, " (%4d) %d\n", i+1, cred->cr_groups[i]);
101 qword_print(f, fname);
102 qword_printhex(f, context_token->value, context_token->length);
107 printerr(0, "WARNING: downcall failed\n");
111 struct gss_verifier {
113 gss_buffer_desc body;
116 #define RPCSEC_GSS_SEQ_WIN 5
119 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
120 u_int32_t maj_stat, u_int32_t min_stat,
121 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
123 char buf[2 * TOKEN_BUF_SIZE];
125 int blen = sizeof(buf);
129 printerr(1, "sending null reply\n");
131 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
132 qword_addhex(&bp, &blen, in_token->value, in_token->length);
133 qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
134 qword_adduint(&bp, &blen, maj_stat);
135 qword_adduint(&bp, &blen, min_stat);
136 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
137 qword_addhex(&bp, &blen, out_token->value, out_token->length);
138 qword_addeol(&bp, &blen);
140 printerr(0, "WARNING: send_respsonse: message too long\n");
143 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
145 printerr(0, "WARNING: open %s failed: %s\n",
146 SVCGSSD_INIT_CHANNEL, strerror(errno));
150 printerr(3, "writing message: %s", buf);
151 if (write(g, buf, bp - buf) == -1) {
152 printerr(0, "WARNING: failed to write message\n");
160 #define rpc_auth_ok 0
161 #define rpc_autherr_badcred 1
162 #define rpc_autherr_rejectedcred 2
163 #define rpc_autherr_badverf 3
164 #define rpc_autherr_rejectedverf 4
165 #define rpc_autherr_tooweak 5
166 #define rpcsec_gsserr_credproblem 13
167 #define rpcsec_gsserr_ctxproblem 14
170 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
173 static gid_t *groups = NULL;
175 cred->cr_ngroups = NGROUPS;
176 ret = nfs4_gss_princ_to_grouplist(secname, name,
177 cred->cr_groups, &cred->cr_ngroups);
179 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
180 ret = nfs4_gss_princ_to_grouplist(secname, name,
181 groups, &cred->cr_ngroups);
183 cred->cr_ngroups = 0;
185 if (cred->cr_ngroups > NGROUPS)
186 cred->cr_ngroups = NGROUPS;
187 memcpy(cred->cr_groups, groups,
188 cred->cr_ngroups*sizeof(gid_t));
194 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
196 u_int32_t maj_stat, min_stat;
197 gss_buffer_desc name;
201 gss_OID name_type = GSS_C_NO_OID;
204 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
205 if (maj_stat != GSS_S_COMPLETE) {
206 pgsserr("get_ids: gss_display_name",
207 maj_stat, min_stat, mech);
210 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
211 !(sname = calloc(name.length + 1, 1))) {
212 printerr(0, "WARNING: get_ids: error allocating %d bytes "
213 "for sname\n", name.length + 1);
214 gss_release_buffer(&min_stat, &name);
217 memcpy(sname, name.value, name.length);
218 printerr(1, "sname = %s\n", sname);
219 gss_release_buffer(&min_stat, &name);
222 if ((secname = mech2file(mech)) == NULL) {
223 printerr(0, "WARNING: get_ids: error mapping mech to "
224 "file for name '%s'\n", sname);
227 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
228 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
231 * -ENOENT means there was no mapping, any other error
232 * value means there was an error trying to do the
234 * If there was no mapping, we send down the value -1
235 * to indicate that the anonuid/anongid for the export
238 if (res == -ENOENT) {
241 cred->cr_ngroups = 0;
245 printerr(0, "WARNING: get_ids: failed to map name '%s' "
246 "to uid/gid: %s\n", sname, strerror(-res));
251 add_supplementary_groups(secname, sname, cred);
261 print_hexl(const char *description, unsigned char *cp, int length)
266 printf("%s (length %d)\n", description, length);
268 for (i = 0; i < length; i += 0x10) {
269 printf(" %04x: ", (u_int)i);
271 jm = jm > 16 ? 16 : jm;
273 for (j = 0; j < jm; j++) {
275 printf("%02x ", (u_int)cp[i+j]);
277 printf("%02x", (u_int)cp[i+j]);
279 for (; j < 16; j++) {
287 for (j = 0; j < jm; j++) {
289 c = isprint(c) ? c : '.';
298 handle_nullreq(FILE *f) {
299 /* XXX initialize to a random integer to reduce chances of unnecessary
300 * invalidation of existing ctx's on restarting svcgssd. */
301 static u_int32_t handle_seq = 0;
302 char in_tok_buf[TOKEN_BUF_SIZE];
303 char in_handle_buf[15];
304 char out_handle_buf[15];
305 gss_buffer_desc in_tok = {.value = in_tok_buf},
306 out_tok = {.value = NULL},
307 in_handle = {.value = in_handle_buf},
308 out_handle = {.value = out_handle_buf},
309 ctx_token = {.value = NULL},
310 ignore_out_tok = {.value = NULL},
311 /* XXX isn't there a define for this?: */
312 null_token = {.value = NULL};
314 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
315 gss_name_t client_name;
316 gss_OID mech = GSS_C_NO_OID;
317 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
318 u_int32_t ignore_min_stat;
319 struct svc_cred cred;
320 static char *lbuf = NULL;
321 static int lbuflen = 0;
324 printerr(1, "handling null request\n");
326 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
327 printerr(0, "WARNING: handle_nullreq: "
328 "failed reading request\n");
334 in_handle.length = (size_t) qword_get(&cp, in_handle.value,
335 sizeof(in_handle_buf));
337 print_hexl("in_handle", in_handle.value, in_handle.length);
340 in_tok.length = (size_t) qword_get(&cp, in_tok.value,
343 print_hexl("in_tok", in_tok.value, in_tok.length);
346 if (in_tok.length < 0) {
347 printerr(0, "WARNING: handle_nullreq: "
348 "failed parsing request\n");
352 if (in_handle.length != 0) { /* CONTINUE_INIT case */
353 if (in_handle.length != sizeof(ctx)) {
354 printerr(0, "WARNING: handle_nullreq: "
355 "input handle has unexpected length %d\n",
359 /* in_handle is the context id stored in the out_handle
360 * for the GSS_S_CONTINUE_NEEDED case below. */
361 memcpy(&ctx, in_handle.value, in_handle.length);
364 maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
365 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
366 &mech, &out_tok, &ret_flags, NULL, NULL);
368 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
369 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
371 /* Save the context handle for future calls */
372 out_handle.length = sizeof(ctx);
373 memcpy(out_handle.value, &ctx, sizeof(ctx));
374 goto continue_needed;
376 else if (maj_stat != GSS_S_COMPLETE) {
377 printerr(0, "WARNING: gss_accept_sec_context failed\n");
378 pgsserr("handle_nullreq: gss_accept_sec_context",
379 maj_stat, min_stat, mech);
382 if (get_ids(client_name, mech, &cred)) {
383 /* get_ids() prints error msg */
384 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
385 gss_release_name(&ignore_min_stat, &client_name);
388 gss_release_name(&ignore_min_stat, &client_name);
391 /* Context complete. Pass handle_seq in out_handle to use
392 * for context lookup in the kernel. */
394 out_handle.length = sizeof(handle_seq);
395 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
397 /* kernel needs ctx to calculate verifier on null response, so
398 * must give it context before doing null call: */
399 if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
400 printerr(0, "WARNING: handle_nullreq: "
401 "serialize_context_for_kernel failed\n");
402 maj_stat = GSS_S_FAILURE;
405 /* We no longer need the gss context */
406 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
408 do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
410 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
411 &out_handle, &out_tok);
413 if (ctx_token.value != NULL)
414 free(ctx_token.value);
415 if (out_tok.value != NULL)
416 gss_release_buffer(&ignore_min_stat, &out_tok);
417 printerr(1, "finished handling null request\n");
421 if (ctx != GSS_C_NO_CONTEXT)
422 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
423 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
424 &null_token, &null_token);