]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/svcgssd/svcgssd_proc.c
Disable NEED_SVCGSSD by default.
[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 /* XXX: ? */
56 #ifndef NGROUPS
57 #define NGROUPS 32
58 #endif
59
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"
63
64 struct svc_cred {
65         uid_t   cr_uid;
66         gid_t   cr_gid;
67         int     cr_ngroups;
68         gid_t   cr_groups[NGROUPS];
69 };
70
71 static int
72 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
73                 gss_OID mech, gss_buffer_desc *context_token)
74 {
75         FILE *f;
76         int i;
77         char *fname = NULL;
78
79         printerr(1, "doing downcall\n");
80         if ((fname = mech2file(mech)) == NULL)
81                 goto out_err;
82         f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
83         if (f == NULL) {
84                 printerr(0, "WARNING: unable to open downcall channel "
85                              "%s: %s\n",
86                              SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
87                 goto out_err;
88         }
89         qword_printhex(f, out_handle->value, out_handle->length);
90         /* XXX are types OK for the rest of this? */
91         qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
92         qword_printint(f, cred->cr_uid);
93         qword_printint(f, cred->cr_gid);
94         qword_printint(f, cred->cr_ngroups);
95         for (i=0; i < cred->cr_ngroups; i++)
96                 qword_printint(f, cred->cr_groups[i]);
97         qword_print(f, fname);
98         qword_printhex(f, context_token->value, context_token->length);
99         qword_eol(f);
100         fclose(f);
101         return 0;
102 out_err:
103         printerr(0, "WARNING: downcall failed\n");
104         return -1;
105 }
106
107 struct gss_verifier {
108         u_int32_t       flav;
109         gss_buffer_desc body;
110 };
111
112 #define RPCSEC_GSS_SEQ_WIN      5
113
114 static int
115 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
116               u_int32_t maj_stat, u_int32_t min_stat,
117               gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
118 {
119         char buf[2 * 4096];
120         char *bp = buf;
121         int blen = sizeof(buf);
122         /* XXXARG: */
123         int g;
124
125         printerr(1, "sending null reply\n");
126
127         qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
128         qword_addhex(&bp, &blen, in_token->value, in_token->length);
129         qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
130         qword_addint(&bp, &blen, maj_stat);
131         qword_addint(&bp, &blen, min_stat);
132         qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
133         qword_addhex(&bp, &blen, out_token->value, out_token->length);
134         qword_addeol(&bp, &blen);
135         if (blen <= 0) {
136                 printerr(0, "WARNING: send_respsonse: message too long\n");
137                 return -1;
138         }
139         g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
140         if (g == -1) {
141                 printerr(0, "WARNING: open %s failed: %s\n",
142                                 SVCGSSD_INIT_CHANNEL, strerror(errno));
143                 return -1;
144         }
145         *bp = '\0';
146         printerr(1, "writing message: %s", buf);
147         if (write(g, buf, bp - buf) == -1) {
148                 printerr(0, "WARNING: failed to write message\n");
149                 close(g);
150                 return -1;
151         }
152         close(g);
153         return 0;
154 }
155
156 #define rpc_auth_ok                     0
157 #define rpc_autherr_badcred             1
158 #define rpc_autherr_rejectedcred        2
159 #define rpc_autherr_badverf             3
160 #define rpc_autherr_rejectedverf        4
161 #define rpc_autherr_tooweak             5
162 #define rpcsec_gsserr_credproblem       13
163 #define rpcsec_gsserr_ctxproblem        14
164
165 static int
166 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
167 {
168         u_int32_t       maj_stat, min_stat;
169         gss_buffer_desc name;
170         char            *sname;
171         int             res = -1;
172         uid_t           uid, gid;
173         gss_OID         name_type;
174         char            *secname;
175
176         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
177         if (maj_stat != GSS_S_COMPLETE)
178                 goto out;
179         if (!(sname = calloc(name.length + 1, 1)))
180                 goto out;
181         memcpy(sname, name.value, name.length);
182         printerr(1, "sname = %s\n", sname);
183
184         res = -EINVAL;
185         if ((secname = mech2file(mech)) == NULL)
186                 goto out_free;
187         nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
188         res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
189         if (res < 0)
190                 goto out_free;
191         cred->cr_uid = uid;
192         cred->cr_gid = gid;
193         /*XXX: want add_supplementary_groups(secname, sname, cred)? */
194         cred->cr_ngroups = 0;
195         res = 0;
196 out_free:
197         free(sname);
198 out:
199         if (res)
200                 printerr(0, "WARNING: get_uid failed\n");
201         return res;
202 }
203
204 void
205 print_hexl(int pri, unsigned char *cp, int length)
206 {
207         int i, j, jm;
208         unsigned char c;
209
210         printerr(pri, "length %d\n",length);
211         printerr(pri, "\n");
212
213         for (i = 0; i < length; i += 0x10) {
214                 printerr(pri, "  %04x: ", (u_int)i);
215                 jm = length - i;
216                 jm = jm > 16 ? 16 : jm;
217
218                 for (j = 0; j < jm; j++) {
219                         if ((j % 2) == 1)
220                                 printerr(pri,"%02x ", (u_int)cp[i+j]);
221                         else
222                                 printerr(pri,"%02x", (u_int)cp[i+j]);
223                 }
224                 for (; j < 16; j++) {
225                         if ((j % 2) == 1)
226                                 printerr(pri,"   ");
227                         else
228                                 printerr(pri,"  ");
229                 }
230                 printerr(pri," ");
231
232                 for (j = 0; j < jm; j++) {
233                         c = cp[i+j];
234                         c = isprint(c) ? c : '.';
235                         printerr(pri,"%c", c);
236                 }
237                 printerr(pri,"\n");
238         }
239 }
240
241 void
242 handle_nullreq(FILE *f) {
243         /* XXX initialize to a random integer to reduce chances of unnecessary
244          * invalidation of existing ctx's on restarting svcgssd. */
245         static u_int32_t        handle_seq = 0;
246         char                    in_tok_buf[1023];
247         char                    in_handle_buf[15];
248         char                    out_handle_buf[15];
249         gss_buffer_desc         in_tok = {.value = in_tok_buf},
250                                 out_tok = {.value = NULL},
251                                 in_handle = {.value = in_handle_buf},
252                                 out_handle = {.value = out_handle_buf},
253                                 ctx_token = {.value = NULL},
254         /* XXX isn't there a define for this?: */
255                                 null_token = {.value = NULL};
256         u_int32_t               ret_flags;
257         gss_ctx_id_t            ctx = GSS_C_NO_CONTEXT;
258         gss_name_t              client_name;
259         gss_OID                 mech;
260         u_int32_t               maj_stat = GSS_S_FAILURE, min_stat = 0;
261         struct svc_cred         cred;
262         static char             *lbuf = NULL;
263         static int              lbuflen = 0;
264         static char             *cp;
265
266         printerr(1, "handling null request\n");
267
268         if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
269                 printerr(0, "WARNING: handle_nullreq: "
270                             "failed reading request\n");
271                 return;
272         }
273
274         cp = lbuf;
275
276         in_handle.length
277                 = qword_get(&cp, in_handle.value, sizeof(in_handle_buf));
278         printerr(2, "in_handle: \n");
279         print_hexl(2, in_handle.value, in_handle.length);
280         handle_seq++;
281         out_handle.length = sizeof(handle_seq);
282         memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
283
284         in_tok.length = qword_get(&cp, in_tok.value, sizeof(in_tok_buf));
285         printerr(2, "in_tok: \n");
286         print_hexl(2, in_tok.value, in_tok.length);
287
288         if (in_tok.length < 0) {
289                 printerr(0, "WARNING: handle_nullreq: "
290                             "failed parsing request\n");
291                 goto out_err;
292         }
293
294         if (in_handle.length != 0) { /* CONTINUE_INIT case */
295                 printerr(0, "WARNING: handle_nullreq: "
296                             "CONTINUE_INIT unsupported\n");
297                 send_response(f, &in_handle, &in_tok, -1, -1, &null_token,
298                                 &null_token);
299                 goto out_err;
300         }
301
302         maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
303                         &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
304                         &mech, &out_tok, &ret_flags, NULL, NULL);
305         if (maj_stat != GSS_S_COMPLETE) {
306                 printerr(0, "WARNING: gss_accept_sec_context failed\n");
307                 pgsserr("handle_nullreq: gss_accept_sec_context",
308                         maj_stat, min_stat, mech);
309                 send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
310                                 &null_token, &null_token);
311                 goto out_err;
312         }
313         if (get_ids(client_name, mech, &cred)) {
314                 printerr(0, "WARNING: handle_nullreq: get_uid failed\n");
315                 send_response(f, &in_handle, &in_tok, GSS_S_BAD_NAME /* XXX? */,
316                                 0, &null_token, &null_token);
317                 goto out_err;
318         }
319
320         /* kernel needs ctx to calculate verifier on null response, so
321          * must give it context before doing null call: */
322         if (serialize_context_for_kernel(ctx, &ctx_token)) {
323                 printerr(0, "WARNING: handle_nullreq: "
324                             "serialize_context_for_kernel failed\n");
325                 send_response(f, &in_handle, &in_tok, -1, /* XXX? */
326                                 0, &null_token, &null_token);
327                 goto out_err;
328         }
329         do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
330         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
331                         &out_handle, &out_tok);
332         goto out;
333 out_err:
334 out:
335         if (ctx_token.value != NULL)
336                 free(ctx_token.value);
337         printerr(1, "finished handling null request\n");
338         return;
339 }