Correct PPTP server firewall rules chain.
[tomato/davidwu.git] / release / src / router / nfs-utils / utils / gssd / svcgssd_proc.c
blobb390beae2fe90ef17908e816b976d34442d540f3
1 /*
2 svc_in_gssd_proc.c
4 Copyright (c) 2000 The Regents of the University of Michigan.
5 All rights reserved.
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
11 are met:
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 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif /* HAVE_CONFIG_H */
40 #include <sys/param.h>
41 #include <sys/stat.h>
42 #include <rpc/rpc.h>
44 #include <pwd.h>
45 #include <stdio.h>
46 #include <unistd.h>
47 #include <ctype.h>
48 #include <string.h>
49 #include <fcntl.h>
50 #include <errno.h>
51 #include <nfsidmap.h>
52 #include <nfslib.h>
53 #include <time.h>
55 #include "svcgssd.h"
56 #include "gss_util.h"
57 #include "err_util.h"
58 #include "context.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
66 struct svc_cred {
67 uid_t cr_uid;
68 gid_t cr_gid;
69 int cr_ngroups;
70 gid_t cr_groups[NGROUPS];
73 static int
74 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
75 gss_OID mech, gss_buffer_desc *context_token,
76 int32_t endtime)
78 FILE *f;
79 int i;
80 char *fname = NULL;
81 int err;
83 printerr(1, "doing downcall\n");
84 if ((fname = mech2file(mech)) == NULL)
85 goto out_err;
86 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
87 if (f == NULL) {
88 printerr(0, "WARNING: unable to open downcall channel "
89 "%s: %s\n",
90 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
91 goto out_err;
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);
111 err = qword_eol(f);
112 fclose(f);
113 return err;
114 out_err:
115 printerr(1, "WARNING: downcall failed\n");
116 return -1;
119 struct gss_verifier {
120 u_int32_t flav;
121 gss_buffer_desc body;
124 #define RPCSEC_GSS_SEQ_WIN 5
126 static int
127 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
128 u_int32_t maj_stat, u_int32_t min_stat,
129 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
131 char buf[2 * TOKEN_BUF_SIZE];
132 char *bp = buf;
133 int blen = sizeof(buf);
134 /* XXXARG: */
135 int g;
137 printerr(1, "sending null reply\n");
139 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
140 qword_addhex(&bp, &blen, in_token->value, in_token->length);
141 /* For init cache, only needed for a short time */
142 qword_addint(&bp, &blen, time(0) + 60);
143 qword_adduint(&bp, &blen, maj_stat);
144 qword_adduint(&bp, &blen, min_stat);
145 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
146 qword_addhex(&bp, &blen, out_token->value, out_token->length);
147 qword_addeol(&bp, &blen);
148 if (blen <= 0) {
149 printerr(0, "WARNING: send_respsonse: message too long\n");
150 return -1;
152 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
153 if (g == -1) {
154 printerr(0, "WARNING: open %s failed: %s\n",
155 SVCGSSD_INIT_CHANNEL, strerror(errno));
156 return -1;
158 *bp = '\0';
159 printerr(3, "writing message: %s", buf);
160 if (write(g, buf, bp - buf) == -1) {
161 printerr(0, "WARNING: failed to write message\n");
162 close(g);
163 return -1;
165 close(g);
166 return 0;
169 #define rpc_auth_ok 0
170 #define rpc_autherr_badcred 1
171 #define rpc_autherr_rejectedcred 2
172 #define rpc_autherr_badverf 3
173 #define rpc_autherr_rejectedverf 4
174 #define rpc_autherr_tooweak 5
175 #define rpcsec_gsserr_credproblem 13
176 #define rpcsec_gsserr_ctxproblem 14
178 static void
179 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
181 int ret;
182 static gid_t *groups = NULL;
184 cred->cr_ngroups = NGROUPS;
185 ret = nfs4_gss_princ_to_grouplist(secname, name,
186 cred->cr_groups, &cred->cr_ngroups);
187 if (ret < 0) {
188 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
189 ret = nfs4_gss_princ_to_grouplist(secname, name,
190 groups, &cred->cr_ngroups);
191 if (ret < 0)
192 cred->cr_ngroups = 0;
193 else {
194 if (cred->cr_ngroups > NGROUPS)
195 cred->cr_ngroups = NGROUPS;
196 memcpy(cred->cr_groups, groups,
197 cred->cr_ngroups*sizeof(gid_t));
202 static int
203 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
205 u_int32_t maj_stat, min_stat;
206 gss_buffer_desc name;
207 char *sname;
208 int res = -1;
209 uid_t uid, gid;
210 gss_OID name_type = GSS_C_NO_OID;
211 char *secname;
213 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
214 if (maj_stat != GSS_S_COMPLETE) {
215 pgsserr("get_ids: gss_display_name",
216 maj_stat, min_stat, mech);
217 goto out;
219 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
220 !(sname = calloc(name.length + 1, 1))) {
221 printerr(0, "WARNING: get_ids: error allocating %d bytes "
222 "for sname\n", name.length + 1);
223 gss_release_buffer(&min_stat, &name);
224 goto out;
226 memcpy(sname, name.value, name.length);
227 printerr(1, "sname = %s\n", sname);
228 gss_release_buffer(&min_stat, &name);
230 res = -EINVAL;
231 if ((secname = mech2file(mech)) == NULL) {
232 printerr(0, "WARNING: get_ids: error mapping mech to "
233 "file for name '%s'\n", sname);
234 goto out_free;
236 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
237 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
238 if (res < 0) {
240 * -ENOENT means there was no mapping, any other error
241 * value means there was an error trying to do the
242 * mapping.
243 * If there was no mapping, we send down the value -1
244 * to indicate that the anonuid/anongid for the export
245 * should be used.
247 if (res == -ENOENT) {
248 cred->cr_uid = -1;
249 cred->cr_gid = -1;
250 cred->cr_ngroups = 0;
251 res = 0;
252 goto out_free;
254 printerr(1, "WARNING: get_ids: failed to map name '%s' "
255 "to uid/gid: %s\n", sname, strerror(-res));
256 goto out_free;
258 cred->cr_uid = uid;
259 cred->cr_gid = gid;
260 add_supplementary_groups(secname, sname, cred);
261 res = 0;
262 out_free:
263 free(sname);
264 out:
265 return res;
268 #ifdef DEBUG
269 void
270 print_hexl(const char *description, unsigned char *cp, int length)
272 int i, j, jm;
273 unsigned char c;
275 printf("%s (length %d)\n", description, length);
277 for (i = 0; i < length; i += 0x10) {
278 printf(" %04x: ", (u_int)i);
279 jm = length - i;
280 jm = jm > 16 ? 16 : jm;
282 for (j = 0; j < jm; j++) {
283 if ((j % 2) == 1)
284 printf("%02x ", (u_int)cp[i+j]);
285 else
286 printf("%02x", (u_int)cp[i+j]);
288 for (; j < 16; j++) {
289 if ((j % 2) == 1)
290 printf(" ");
291 else
292 printf(" ");
294 printf(" ");
296 for (j = 0; j < jm; j++) {
297 c = cp[i+j];
298 c = isprint(c) ? c : '.';
299 printf("%c", c);
301 printf("\n");
304 #endif
306 void
307 handle_nullreq(FILE *f) {
308 /* XXX initialize to a random integer to reduce chances of unnecessary
309 * invalidation of existing ctx's on restarting svcgssd. */
310 static u_int32_t handle_seq = 0;
311 char in_tok_buf[TOKEN_BUF_SIZE];
312 char in_handle_buf[15];
313 char out_handle_buf[15];
314 gss_buffer_desc in_tok = {.value = in_tok_buf},
315 out_tok = {.value = NULL},
316 in_handle = {.value = in_handle_buf},
317 out_handle = {.value = out_handle_buf},
318 ctx_token = {.value = NULL},
319 ignore_out_tok = {.value = NULL},
320 /* XXX isn't there a define for this?: */
321 null_token = {.value = NULL};
322 u_int32_t ret_flags;
323 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
324 gss_name_t client_name;
325 gss_OID mech = GSS_C_NO_OID;
326 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
327 u_int32_t ignore_min_stat;
328 struct svc_cred cred;
329 static char *lbuf = NULL;
330 static int lbuflen = 0;
331 static char *cp;
332 int32_t ctx_endtime;
334 printerr(1, "handling null request\n");
336 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
337 printerr(0, "WARNING: handle_nullreq: "
338 "failed reading request\n");
339 return;
342 cp = lbuf;
344 in_handle.length = (size_t) qword_get(&cp, in_handle.value,
345 sizeof(in_handle_buf));
346 #ifdef DEBUG
347 print_hexl("in_handle", in_handle.value, in_handle.length);
348 #endif
350 in_tok.length = (size_t) qword_get(&cp, in_tok.value,
351 sizeof(in_tok_buf));
352 #ifdef DEBUG
353 print_hexl("in_tok", in_tok.value, in_tok.length);
354 #endif
356 if (in_tok.length < 0) {
357 printerr(0, "WARNING: handle_nullreq: "
358 "failed parsing request\n");
359 goto out_err;
362 if (in_handle.length != 0) { /* CONTINUE_INIT case */
363 if (in_handle.length != sizeof(ctx)) {
364 printerr(0, "WARNING: handle_nullreq: "
365 "input handle has unexpected length %d\n",
366 in_handle.length);
367 goto out_err;
369 /* in_handle is the context id stored in the out_handle
370 * for the GSS_S_CONTINUE_NEEDED case below. */
371 memcpy(&ctx, in_handle.value, in_handle.length);
374 maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
375 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
376 &mech, &out_tok, &ret_flags, NULL, NULL);
378 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
379 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
381 /* Save the context handle for future calls */
382 out_handle.length = sizeof(ctx);
383 memcpy(out_handle.value, &ctx, sizeof(ctx));
384 goto continue_needed;
386 else if (maj_stat != GSS_S_COMPLETE) {
387 printerr(1, "WARNING: gss_accept_sec_context failed\n");
388 pgsserr("handle_nullreq: gss_accept_sec_context",
389 maj_stat, min_stat, mech);
390 goto out_err;
392 if (get_ids(client_name, mech, &cred)) {
393 /* get_ids() prints error msg */
394 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
395 gss_release_name(&ignore_min_stat, &client_name);
396 goto out_err;
398 gss_release_name(&ignore_min_stat, &client_name);
401 /* Context complete. Pass handle_seq in out_handle to use
402 * for context lookup in the kernel. */
403 handle_seq++;
404 out_handle.length = sizeof(handle_seq);
405 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
407 /* kernel needs ctx to calculate verifier on null response, so
408 * must give it context before doing null call: */
409 if (serialize_context_for_kernel(ctx, &ctx_token, mech, &ctx_endtime)) {
410 printerr(0, "WARNING: handle_nullreq: "
411 "serialize_context_for_kernel failed\n");
412 maj_stat = GSS_S_FAILURE;
413 goto out_err;
415 /* We no longer need the gss context */
416 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
418 do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime);
419 continue_needed:
420 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
421 &out_handle, &out_tok);
422 out:
423 if (ctx_token.value != NULL)
424 free(ctx_token.value);
425 if (out_tok.value != NULL)
426 gss_release_buffer(&ignore_min_stat, &out_tok);
427 printerr(1, "finished handling null request\n");
428 return;
430 out_err:
431 if (ctx != GSS_C_NO_CONTEXT)
432 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
433 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
434 &null_token, &null_token);
435 goto out;