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"
63 gid_t cr_groups[NGROUPS];
67 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
68 gss_OID mech, gss_buffer_desc *context_token)
74 printerr(1, "doing downcall\n");
75 if ((fname = mech2file(mech)) == NULL)
77 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
79 printerr(0, "WARNING: unable to open downcall channel "
81 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
84 qword_printhex(f, out_handle->value, out_handle->length);
85 /* XXX are types OK for the rest of this? */
86 qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
87 qword_printint(f, cred->cr_uid);
88 qword_printint(f, cred->cr_gid);
89 qword_printint(f, cred->cr_ngroups);
90 for (i=0; i < cred->cr_ngroups; i++)
91 qword_printint(f, cred->cr_groups[i]);
92 qword_print(f, fname);
93 qword_printhex(f, context_token->value, context_token->length);
98 printerr(0, "WARNING: downcall failed\n");
102 struct gss_verifier {
104 gss_buffer_desc body;
107 #define RPCSEC_GSS_SEQ_WIN 5
110 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
111 u_int32_t maj_stat, u_int32_t min_stat,
112 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
116 int blen = sizeof(buf);
120 printerr(1, "sending null reply\n");
122 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
123 qword_addhex(&bp, &blen, in_token->value, in_token->length);
124 qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
125 qword_addint(&bp, &blen, maj_stat);
126 qword_addint(&bp, &blen, min_stat);
127 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
128 qword_addhex(&bp, &blen, out_token->value, out_token->length);
129 qword_addeol(&bp, &blen);
131 printerr(0, "WARNING: send_respsonse: message too long\n");
134 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
136 printerr(0, "WARNING: open %s failed: %s\n",
137 SVCGSSD_INIT_CHANNEL, strerror(errno));
141 printerr(1, "writing message: %s", buf);
142 if (write(g, buf, bp - buf) == -1) {
143 printerr(0, "WARNING: failed to write message\n");
151 #define rpc_auth_ok 0
152 #define rpc_autherr_badcred 1
153 #define rpc_autherr_rejectedcred 2
154 #define rpc_autherr_badverf 3
155 #define rpc_autherr_rejectedverf 4
156 #define rpc_autherr_tooweak 5
157 #define rpcsec_gsserr_credproblem 13
158 #define rpcsec_gsserr_ctxproblem 14
161 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
164 static gid_t *groups = NULL;
166 cred->cr_ngroups = NGROUPS;
167 ret = nfs4_gss_princ_to_grouplist(secname, name,
168 cred->cr_groups, &cred->cr_ngroups);
170 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
171 ret = nfs4_gss_princ_to_grouplist(secname, name,
172 groups, &cred->cr_ngroups);
174 cred->cr_ngroups = 0;
176 if (cred->cr_ngroups > NGROUPS)
177 cred->cr_ngroups = NGROUPS;
178 memcpy(cred->cr_groups, groups,
179 cred->cr_ngroups*sizeof(gid_t));
185 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
187 u_int32_t maj_stat, min_stat;
188 gss_buffer_desc name;
196 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
197 if (maj_stat != GSS_S_COMPLETE)
199 if (!(sname = calloc(name.length + 1, 1)))
201 memcpy(sname, name.value, name.length);
202 printerr(1, "sname = %s\n", sname);
205 if ((secname = mech2file(mech)) == NULL)
207 nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
208 res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
213 add_supplementary_groups(secname, sname, cred);
219 printerr(0, "WARNING: get_uid failed\n");
224 print_hexl(int pri, unsigned char *cp, int length)
229 printerr(pri, "length %d\n",length);
232 for (i = 0; i < length; i += 0x10) {
233 printerr(pri, " %04x: ", (u_int)i);
235 jm = jm > 16 ? 16 : jm;
237 for (j = 0; j < jm; j++) {
239 printerr(pri,"%02x ", (u_int)cp[i+j]);
241 printerr(pri,"%02x", (u_int)cp[i+j]);
243 for (; j < 16; j++) {
251 for (j = 0; j < jm; j++) {
253 c = isprint(c) ? c : '.';
254 printerr(pri,"%c", c);
261 handle_nullreq(FILE *f) {
262 /* XXX initialize to a random integer to reduce chances of unnecessary
263 * invalidation of existing ctx's on restarting svcgssd. */
264 static u_int32_t handle_seq = 0;
265 char in_tok_buf[8192];
266 char in_handle_buf[15];
267 char out_handle_buf[15];
268 gss_buffer_desc in_tok = {.value = in_tok_buf},
269 out_tok = {.value = NULL},
270 in_handle = {.value = in_handle_buf},
271 out_handle = {.value = out_handle_buf},
272 ctx_token = {.value = NULL},
273 /* XXX isn't there a define for this?: */
274 null_token = {.value = NULL};
276 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
277 gss_name_t client_name;
279 u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
280 struct svc_cred cred;
281 static char *lbuf = NULL;
282 static int lbuflen = 0;
285 printerr(1, "handling null request\n");
287 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
288 printerr(0, "WARNING: handle_nullreq: "
289 "failed reading request\n");
295 in_handle.length = (size_t) qword_get(&cp, in_handle.value,
296 sizeof(in_handle_buf));
297 printerr(2, "in_handle: \n");
298 print_hexl(2, in_handle.value, in_handle.length);
300 out_handle.length = sizeof(handle_seq);
301 memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
303 in_tok.length = (size_t) qword_get(&cp, in_tok.value,
305 printerr(2, "in_tok: \n");
306 print_hexl(2, in_tok.value, in_tok.length);
308 if (in_tok.length < 0) {
309 printerr(0, "WARNING: handle_nullreq: "
310 "failed parsing request\n");
314 if (in_handle.length != 0) { /* CONTINUE_INIT case */
315 printerr(0, "WARNING: handle_nullreq: "
316 "CONTINUE_INIT unsupported\n");
320 maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
321 &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
322 &mech, &out_tok, &ret_flags, NULL, NULL);
323 if (maj_stat != GSS_S_COMPLETE) {
324 printerr(0, "WARNING: gss_accept_sec_context failed\n");
325 pgsserr("handle_nullreq: gss_accept_sec_context",
326 maj_stat, min_stat, mech);
329 if (get_ids(client_name, mech, &cred)) {
330 printerr(0, "WARNING: handle_nullreq: get_uid failed\n");
331 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
335 /* kernel needs ctx to calculate verifier on null response, so
336 * must give it context before doing null call: */
337 if (serialize_context_for_kernel(ctx, &ctx_token)) {
338 printerr(0, "WARNING: handle_nullreq: "
339 "serialize_context_for_kernel failed\n");
340 maj_stat = GSS_S_FAILURE;
343 do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
344 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
345 &out_handle, &out_tok);
348 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
349 &null_token, &null_token);
351 if (ctx_token.value != NULL)
352 free(ctx_token.value);
353 printerr(1, "finished handling null request\n");