svcgssd updates
[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
48 #include "svcgssd.h"
49 #include "gss_util.h"
50 #include "err_util.h"
51 #include "context.h"
52 #include "cacheio.h"
53
54 /* XXX: ? */
55 #ifndef NGROUPS
56 #define NGROUPS 32
57 #endif
58
59 extern char * mech2file(gss_OID mech);
60 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
61 #define SVCGSSD_INIT_CHANNEL    "/proc/net/rpc/auth.rpcsec.init/channel"
62
63 struct svc_cred {
64         uid_t   cr_uid;
65         gid_t   cr_gid;
66         int     cr_ngroups;
67         gid_t   cr_groups[NGROUPS];
68 };
69
70 static int
71 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
72                 gss_OID mech, gss_buffer_desc *context_token)
73 {
74         FILE *f;
75         int i;
76         char *fname = NULL;
77
78         printerr(1, "doing downcall\n");
79         if ((fname = mech2file(mech)) == NULL)
80                 goto out_err;
81         f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
82         if (f == NULL) {
83                 printerr(0, "WARNING: unable to open downcall channel "
84                              "%s: %s\n",
85                              SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
86                 goto out_err;
87         }
88         qword_printhex(f, out_handle->value, out_handle->length);
89         /* XXX are types OK for the rest of this? */
90         qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
91         qword_printint(f, cred->cr_uid);
92         qword_printint(f, cred->cr_gid);
93         qword_printint(f, cred->cr_ngroups);
94         for (i=0; i < cred->cr_ngroups; i++)
95                 qword_printint(f, cred->cr_groups[i]);
96         qword_print(f, fname);
97         qword_printhex(f, context_token->value, context_token->length);
98         qword_eol(f);
99         fclose(f);
100         return 0;
101 out_err:
102         printerr(0, "WARNING: downcall failed\n");
103         return -1;
104 }
105
106 struct gss_verifier {
107         u_int32_t       flav;
108         gss_buffer_desc body;
109 };
110
111 #define RPCSEC_GSS_SEQ_WIN      5
112
113 static int
114 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
115               u_int32_t maj_stat, u_int32_t min_stat,
116               gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
117 {
118         char buf[2 * 4096];
119         char *bp = buf;
120         int blen = sizeof(buf);
121         /* XXXARG: */
122         int g;
123
124         printerr(1, "sending null reply\n");
125
126         qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
127         qword_addhex(&bp, &blen, in_token->value, in_token->length);
128         qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
129         qword_addint(&bp, &blen, maj_stat);
130         qword_addint(&bp, &blen, min_stat);
131         qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
132         qword_addhex(&bp, &blen, out_token->value, out_token->length);
133         qword_addeol(&bp, &blen);
134         if (blen <= 0) {
135                 printerr(0, "WARNING: send_respsonse: message too long\n");
136                 return -1;
137         }
138         g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
139         if (g == -1) {
140                 printerr(0, "WARNING: open %s failed: %s\n",
141                                 SVCGSSD_INIT_CHANNEL, strerror(errno));
142                 return -1;
143         }
144         *bp = '\0';
145         printerr(1, "writing message: %s", buf);
146         if (write(g, buf, bp - buf) == -1) {
147                 printerr(0, "WARNING: failed to write message\n");
148                 close(g);
149                 return -1;
150         }
151         close(g);
152         return 0;
153 }
154
155 #define rpc_auth_ok                     0
156 #define rpc_autherr_badcred             1
157 #define rpc_autherr_rejectedcred        2
158 #define rpc_autherr_badverf             3
159 #define rpc_autherr_rejectedverf        4
160 #define rpc_autherr_tooweak             5
161 #define rpcsec_gsserr_credproblem       13
162 #define rpcsec_gsserr_ctxproblem        14
163
164 static int
165 get_ids(gss_name_t client_name, gss_OID *mech, struct svc_cred *cred)
166 {
167         u_int32_t       maj_stat, min_stat;
168         gss_buffer_desc name;
169         char            *sname;
170         int             res = -1;
171         struct passwd   *pw = NULL;
172         gss_OID         name_type;
173         char            *c;
174
175         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
176         if (maj_stat != GSS_S_COMPLETE)
177                 goto out;
178         if (!(sname = calloc(name.length + 1, 1)))
179                 goto out;
180         memcpy(sname, name.value, name.length);
181         printerr(1, "sname = %s\n", sname);
182         /* XXX: should use same mapping as idmapd?  Or something; for now
183          * I'm just chopping off the domain. */
184         /* XXX: note that idmapd also does this!  It doesn't check the domain
185          * name. */
186         if ((c = strchr(sname, '@')) != NULL)
187                 *c = '\0';
188         /* XXX? mapping unknown users (including machine creds) to nobody: */
189         if ( !(pw = getpwnam(sname)) && !(pw = getpwnam("nobody")) )
190                 goto out_free;
191         cred->cr_uid = pw->pw_uid;
192         cred->cr_gid = pw->pw_gid;
193         /* XXX Read password file?  Use initgroups? I dunno...*/
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 }