]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/svcgssd_proc.c
fd1076efc6fc0b894f95d01b1aa01441d4269e24
[nfs-utils.git] / utils / gssd / 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 #define TOKEN_BUF_SIZE          8192
60
61 struct svc_cred {
62         uid_t   cr_uid;
63         gid_t   cr_gid;
64         int     cr_ngroups;
65         gid_t   cr_groups[NGROUPS];
66 };
67
68 static int
69 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
70                 gss_OID mech, gss_buffer_desc *context_token)
71 {
72         FILE *f;
73         int i;
74         char *fname = NULL;
75
76         printerr(1, "doing downcall\n");
77         if ((fname = mech2file(mech)) == NULL)
78                 goto out_err;
79         f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
80         if (f == NULL) {
81                 printerr(0, "WARNING: unable to open downcall channel "
82                              "%s: %s\n",
83                              SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
84                 goto out_err;
85         }
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);
96         qword_eol(f);
97         fclose(f);
98         return 0;
99 out_err:
100         printerr(0, "WARNING: downcall failed\n");
101         return -1;
102 }
103
104 struct gss_verifier {
105         u_int32_t       flav;
106         gss_buffer_desc body;
107 };
108
109 #define RPCSEC_GSS_SEQ_WIN      5
110
111 static int
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)
115 {
116         char buf[2 * TOKEN_BUF_SIZE];
117         char *bp = buf;
118         int blen = sizeof(buf);
119         /* XXXARG: */
120         int g;
121
122         printerr(1, "sending null reply\n");
123
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);
132         if (blen <= 0) {
133                 printerr(0, "WARNING: send_respsonse: message too long\n");
134                 return -1;
135         }
136         g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
137         if (g == -1) {
138                 printerr(0, "WARNING: open %s failed: %s\n",
139                                 SVCGSSD_INIT_CHANNEL, strerror(errno));
140                 return -1;
141         }
142         *bp = '\0';
143         printerr(1, "writing message: %s", buf);
144         if (write(g, buf, bp - buf) == -1) {
145                 printerr(0, "WARNING: failed to write message\n");
146                 close(g);
147                 return -1;
148         }
149         close(g);
150         return 0;
151 }
152
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
161
162 static void
163 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
164 {
165         int ret;
166         static gid_t *groups = NULL;
167
168         cred->cr_ngroups = NGROUPS;
169         ret = nfs4_gss_princ_to_grouplist(secname, name,
170                         cred->cr_groups, &cred->cr_ngroups);
171         if (ret < 0) {
172                 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
173                 ret = nfs4_gss_princ_to_grouplist(secname, name,
174                                 groups, &cred->cr_ngroups);
175                 if (ret < 0)
176                         cred->cr_ngroups = 0;
177                 else {
178                         if (cred->cr_ngroups > NGROUPS)
179                                 cred->cr_ngroups = NGROUPS;
180                         memcpy(cred->cr_groups, groups,
181                                         cred->cr_ngroups*sizeof(gid_t));
182                 }
183         }
184 }
185
186 static int
187 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
188 {
189         u_int32_t       maj_stat, min_stat;
190         gss_buffer_desc name;
191         char            *sname;
192         int             res = -1;
193         uid_t           uid, gid;
194         gss_OID         name_type = GSS_C_NO_OID;
195         char            *secname;
196         gid_t           *groups;
197
198         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
199         if (maj_stat != GSS_S_COMPLETE) {
200                 pgsserr("get_ids: gss_display_name",
201                         maj_stat, min_stat, mech);
202                 goto out;
203         }
204         if (!(sname = calloc(name.length + 1, 1))) {
205                 printerr(0, "WARNING: get_ids: error allocating %d bytes "
206                         "for sname\n", name.length + 1);
207                 goto out;
208         }
209         memcpy(sname, name.value, name.length);
210         printerr(1, "sname = %s\n", sname);
211
212         res = -EINVAL;
213         if ((secname = mech2file(mech)) == NULL) {
214                 printerr(0, "WARNING: get_ids: error mapping mech to "
215                         "file for name '%s'\n", sname);
216                 goto out_free;
217         }
218         nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
219         res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
220         if (res < 0) {
221                 printerr(0, "WARNING: get_ids: unable to map "
222                         "name '%s' to a uid\n", sname);
223                 goto out_free;
224         }
225         cred->cr_uid = uid;
226         cred->cr_gid = gid;
227         add_supplementary_groups(secname, sname, cred);
228         res = 0;
229 out_free:
230         free(sname);
231 out:
232         return res;
233 }
234
235 void
236 print_hexl(int pri, unsigned char *cp, int length)
237 {
238         int i, j, jm;
239         unsigned char c;
240
241         printerr(pri, "length %d\n",length);
242         printerr(pri, "\n");
243
244         for (i = 0; i < length; i += 0x10) {
245                 printerr(pri, "  %04x: ", (u_int)i);
246                 jm = length - i;
247                 jm = jm > 16 ? 16 : jm;
248
249                 for (j = 0; j < jm; j++) {
250                         if ((j % 2) == 1)
251                                 printerr(pri,"%02x ", (u_int)cp[i+j]);
252                         else
253                                 printerr(pri,"%02x", (u_int)cp[i+j]);
254                 }
255                 for (; j < 16; j++) {
256                         if ((j % 2) == 1)
257                                 printerr(pri,"   ");
258                         else
259                                 printerr(pri,"  ");
260                 }
261                 printerr(pri," ");
262
263                 for (j = 0; j < jm; j++) {
264                         c = cp[i+j];
265                         c = isprint(c) ? c : '.';
266                         printerr(pri,"%c", c);
267                 }
268                 printerr(pri,"\n");
269         }
270 }
271
272 void
273 handle_nullreq(FILE *f) {
274         /* XXX initialize to a random integer to reduce chances of unnecessary
275          * invalidation of existing ctx's on restarting svcgssd. */
276         static u_int32_t        handle_seq = 0;
277         char                    in_tok_buf[TOKEN_BUF_SIZE];
278         char                    in_handle_buf[15];
279         char                    out_handle_buf[15];
280         gss_buffer_desc         in_tok = {.value = in_tok_buf},
281                                 out_tok = {.value = NULL},
282                                 in_handle = {.value = in_handle_buf},
283                                 out_handle = {.value = out_handle_buf},
284                                 ctx_token = {.value = NULL},
285         /* XXX isn't there a define for this?: */
286                                 null_token = {.value = NULL};
287         u_int32_t               ret_flags;
288         gss_ctx_id_t            ctx = GSS_C_NO_CONTEXT;
289         gss_name_t              client_name;
290         gss_OID                 mech = GSS_C_NO_OID;
291         u_int32_t               maj_stat = GSS_S_FAILURE, min_stat = 0;
292         struct svc_cred         cred;
293         static char             *lbuf = NULL;
294         static int              lbuflen = 0;
295         static char             *cp;
296
297         printerr(1, "handling null request\n");
298
299         if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
300                 printerr(0, "WARNING: handle_nullreq: "
301                             "failed reading request\n");
302                 return;
303         }
304
305         cp = lbuf;
306
307         in_handle.length = (size_t) qword_get(&cp, in_handle.value,
308                                               sizeof(in_handle_buf));
309         printerr(2, "in_handle: \n");
310         print_hexl(2, in_handle.value, in_handle.length);
311
312         in_tok.length = (size_t) qword_get(&cp, in_tok.value,
313                                            sizeof(in_tok_buf));
314         printerr(2, "in_tok: \n");
315         print_hexl(2, in_tok.value, in_tok.length);
316
317         if (in_tok.length < 0) {
318                 printerr(0, "WARNING: handle_nullreq: "
319                             "failed parsing request\n");
320                 goto out_err;
321         }
322
323         if (in_handle.length != 0) { /* CONTINUE_INIT case */
324                 if (in_handle.length != sizeof(ctx)) {
325                         printerr(0, "WARNING: handle_nullreq: "
326                                     "input handle has unexpected length %d\n",
327                                     in_handle.length);
328                         goto out_err;
329                 }
330                 /* in_handle is the context id stored in the out_handle
331                  * for the GSS_S_CONTINUE_NEEDED case below.  */
332                 memcpy(&ctx, in_handle.value, in_handle.length);
333         }
334
335         maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
336                         &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
337                         &mech, &out_tok, &ret_flags, NULL, NULL);
338
339         if (maj_stat == GSS_S_CONTINUE_NEEDED) {
340                 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
341
342                 /* Save the context handle for future calls */
343                 out_handle.length = sizeof(ctx);
344                 memcpy(out_handle.value, &ctx, sizeof(ctx));
345                 goto continue_needed;
346         }
347         else if (maj_stat != GSS_S_COMPLETE) {
348                 printerr(0, "WARNING: gss_accept_sec_context failed\n");
349                 pgsserr("handle_nullreq: gss_accept_sec_context",
350                         maj_stat, min_stat, mech);
351                 goto out_err;
352         }
353         if (get_ids(client_name, mech, &cred)) {
354                 /* get_ids() prints error msg */
355                 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
356                 goto out_err;
357         }
358
359
360         /* Context complete. Pass handle_seq in out_handle to use
361          * for context lookup in the kernel. */
362         handle_seq++;
363         out_handle.length = sizeof(handle_seq);
364         memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
365
366         /* kernel needs ctx to calculate verifier on null response, so
367          * must give it context before doing null call: */
368         if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
369                 printerr(0, "WARNING: handle_nullreq: "
370                             "serialize_context_for_kernel failed\n");
371                 maj_stat = GSS_S_FAILURE;
372                 goto out_err;
373         }
374         do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
375 continue_needed:
376         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
377                         &out_handle, &out_tok);
378 out:
379         if (ctx_token.value != NULL)
380                 free(ctx_token.value);
381         printerr(1, "finished handling null request\n");
382         return;
383
384 out_err:
385         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
386                         &null_token, &null_token);
387         goto out;
388 }