]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/svcgssd/svcgssd_proc.c
Add option to set rpcsec_gss debugging level (if available)
[nfs-utils.git] / utils / svcgssd / svcgssd_proc.c
1 /*
2   svc_in_gssd_proc.c
3
4   Copyright (c) 2000 The Regents of the University of Michigan.
5   All rights reserved.
6
7   Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
8
9   Redistribution and use in source and binary forms, with or without
10   modification, are permitted provided that the following conditions
11   are met:
12
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.
21
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.
33
34 */
35
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 #include <rpc/rpc.h>
39
40 #include <pwd.h>
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <ctype.h>
44 #include <string.h>
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <nfsidmap.h>
48
49 #include "svcgssd.h"
50 #include "gss_util.h"
51 #include "err_util.h"
52 #include "context.h"
53 #include "cacheio.h"
54
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"
58
59 struct svc_cred {
60         uid_t   cr_uid;
61         gid_t   cr_gid;
62         int     cr_ngroups;
63         gid_t   cr_groups[NGROUPS];
64 };
65
66 static int
67 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
68                 gss_OID mech, gss_buffer_desc *context_token)
69 {
70         FILE *f;
71         int i;
72         char *fname = NULL;
73
74         printerr(1, "doing downcall\n");
75         if ((fname = mech2file(mech)) == NULL)
76                 goto out_err;
77         f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
78         if (f == NULL) {
79                 printerr(0, "WARNING: unable to open downcall channel "
80                              "%s: %s\n",
81                              SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
82                 goto out_err;
83         }
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);
94         qword_eol(f);
95         fclose(f);
96         return 0;
97 out_err:
98         printerr(0, "WARNING: downcall failed\n");
99         return -1;
100 }
101
102 struct gss_verifier {
103         u_int32_t       flav;
104         gss_buffer_desc body;
105 };
106
107 #define RPCSEC_GSS_SEQ_WIN      5
108
109 static int
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)
113 {
114         char buf[2 * 4096];
115         char *bp = buf;
116         int blen = sizeof(buf);
117         /* XXXARG: */
118         int g;
119
120         printerr(1, "sending null reply\n");
121
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);
130         if (blen <= 0) {
131                 printerr(0, "WARNING: send_respsonse: message too long\n");
132                 return -1;
133         }
134         g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
135         if (g == -1) {
136                 printerr(0, "WARNING: open %s failed: %s\n",
137                                 SVCGSSD_INIT_CHANNEL, strerror(errno));
138                 return -1;
139         }
140         *bp = '\0';
141         printerr(1, "writing message: %s", buf);
142         if (write(g, buf, bp - buf) == -1) {
143                 printerr(0, "WARNING: failed to write message\n");
144                 close(g);
145                 return -1;
146         }
147         close(g);
148         return 0;
149 }
150
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
159
160 static void
161 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
162 {
163         int ret;
164         static gid_t *groups = NULL;
165
166         cred->cr_ngroups = NGROUPS;
167         ret = nfs4_gss_princ_to_grouplist(secname, name,
168                         cred->cr_groups, &cred->cr_ngroups);
169         if (ret < 0) {
170                 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
171                 ret = nfs4_gss_princ_to_grouplist(secname, name,
172                                 groups, &cred->cr_ngroups);
173                 if (ret < 0)
174                         cred->cr_ngroups = 0;
175                 else {
176                         if (cred->cr_ngroups > NGROUPS)
177                                 cred->cr_ngroups = NGROUPS;
178                         memcpy(cred->cr_groups, groups,
179                                         cred->cr_ngroups*sizeof(gid_t));
180                 }
181         }
182 }
183
184 static int
185 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
186 {
187         u_int32_t       maj_stat, min_stat;
188         gss_buffer_desc name;
189         char            *sname;
190         int             res = -1;
191         uid_t           uid, gid;
192         gss_OID         name_type;
193         char            *secname;
194         gid_t           *groups;
195
196         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
197         if (maj_stat != GSS_S_COMPLETE)
198                 goto out;
199         if (!(sname = calloc(name.length + 1, 1)))
200                 goto out;
201         memcpy(sname, name.value, name.length);
202         printerr(1, "sname = %s\n", sname);
203
204         res = -EINVAL;
205         if ((secname = mech2file(mech)) == NULL)
206                 goto out_free;
207         nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
208         res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
209         if (res < 0)
210                 goto out_free;
211         cred->cr_uid = uid;
212         cred->cr_gid = gid;
213         add_supplementary_groups(secname, sname, cred);
214         res = 0;
215 out_free:
216         free(sname);
217 out:
218         if (res)
219                 printerr(0, "WARNING: get_uid failed\n");
220         return res;
221 }
222
223 void
224 print_hexl(int pri, unsigned char *cp, int length)
225 {
226         int i, j, jm;
227         unsigned char c;
228
229         printerr(pri, "length %d\n",length);
230         printerr(pri, "\n");
231
232         for (i = 0; i < length; i += 0x10) {
233                 printerr(pri, "  %04x: ", (u_int)i);
234                 jm = length - i;
235                 jm = jm > 16 ? 16 : jm;
236
237                 for (j = 0; j < jm; j++) {
238                         if ((j % 2) == 1)
239                                 printerr(pri,"%02x ", (u_int)cp[i+j]);
240                         else
241                                 printerr(pri,"%02x", (u_int)cp[i+j]);
242                 }
243                 for (; j < 16; j++) {
244                         if ((j % 2) == 1)
245                                 printerr(pri,"   ");
246                         else
247                                 printerr(pri,"  ");
248                 }
249                 printerr(pri," ");
250
251                 for (j = 0; j < jm; j++) {
252                         c = cp[i+j];
253                         c = isprint(c) ? c : '.';
254                         printerr(pri,"%c", c);
255                 }
256                 printerr(pri,"\n");
257         }
258 }
259
260 void
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};
275         u_int32_t               ret_flags;
276         gss_ctx_id_t            ctx = GSS_C_NO_CONTEXT;
277         gss_name_t              client_name;
278         gss_OID                 mech;
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;
283         static char             *cp;
284
285         printerr(1, "handling null request\n");
286
287         if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
288                 printerr(0, "WARNING: handle_nullreq: "
289                             "failed reading request\n");
290                 return;
291         }
292
293         cp = lbuf;
294
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);
299         handle_seq++;
300         out_handle.length = sizeof(handle_seq);
301         memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
302
303         in_tok.length = (size_t) qword_get(&cp, in_tok.value,
304                                            sizeof(in_tok_buf));
305         printerr(2, "in_tok: \n");
306         print_hexl(2, in_tok.value, in_tok.length);
307
308         if (in_tok.length < 0) {
309                 printerr(0, "WARNING: handle_nullreq: "
310                             "failed parsing request\n");
311                 goto out_err;
312         }
313
314         if (in_handle.length != 0) { /* CONTINUE_INIT case */
315                 printerr(0, "WARNING: handle_nullreq: "
316                             "CONTINUE_INIT unsupported\n");
317                 goto out_err;
318         }
319
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);
327                 goto out_err;
328         }
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 ? */
332                 goto out_err;
333         }
334
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;
341                 goto out_err;
342         }
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);
346         goto out;
347 out_err:
348         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
349                         &null_token, &null_token);
350 out:
351         if (ctx_token.value != NULL)
352                 free(ctx_token.value);
353         printerr(1, "finished handling null request\n");
354         return;
355 }