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