Whamcloud - gitweb
- make HEAD from b_post_cmd3
[fs/lustre-release.git] / lustre / utils / gss / 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
39 #include <pwd.h>
40 #include <stdio.h>
41 #include <unistd.h>
42 #include <ctype.h>
43 #include <string.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #include <netdb.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 #include "lsupport.h"
54
55 extern char * mech2file(gss_OID mech);
56 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.context/channel"
57 #define SVCGSSD_INIT_CHANNEL    "/proc/net/rpc/auth.ptlrpcs.init/channel"
58
59 #define TOKEN_BUF_SIZE          8192
60
61 struct svc_cred {
62         uint32_t cr_remote;
63         uint32_t cr_usr_root;
64         uint32_t cr_usr_mds;
65         uid_t    cr_uid;
66         uid_t    cr_mapped_uid;
67         uid_t    cr_gid;
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         char *fname = NULL;
76
77         printerr(2, "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_remote);
91         qword_printint(f, cred->cr_usr_root);
92         qword_printint(f, cred->cr_usr_mds);
93         qword_printint(f, cred->cr_mapped_uid);
94         qword_printint(f, cred->cr_uid);
95         qword_printint(f, cred->cr_gid);
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 * TOKEN_BUF_SIZE];
119         char *bp = buf;
120         int blen = sizeof(buf);
121         /* XXXARG: */
122         int g;
123
124         printerr(2, "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(3, "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 #if 0
165 static void
166 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
167 {
168         int ret;
169         static gid_t *groups = NULL;
170
171         cred->cr_ngroups = NGROUPS;
172         ret = nfs4_gss_princ_to_grouplist(secname, name,
173                         cred->cr_groups, &cred->cr_ngroups);
174         if (ret < 0) {
175                 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
176                 ret = nfs4_gss_princ_to_grouplist(secname, name,
177                                 groups, &cred->cr_ngroups);
178                 if (ret < 0)
179                         cred->cr_ngroups = 0;
180                 else {
181                         if (cred->cr_ngroups > NGROUPS)
182                                 cred->cr_ngroups = NGROUPS;
183                         memcpy(cred->cr_groups, groups,
184                                         cred->cr_ngroups*sizeof(gid_t));
185                 }
186         }
187 }
188 #endif
189
190 #if 0
191 static int
192 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
193 {
194         u_int32_t       maj_stat, min_stat;
195         gss_buffer_desc name;
196         char            *sname;
197         int             res = -1;
198         uid_t           uid, gid;
199         gss_OID         name_type = GSS_C_NO_OID;
200         char            *secname;
201
202         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
203         if (maj_stat != GSS_S_COMPLETE) {
204                 pgsserr("get_ids: gss_display_name",
205                         maj_stat, min_stat, mech);
206                 goto out;
207         }
208         if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
209             !(sname = calloc(name.length + 1, 1))) {
210                 printerr(0, "WARNING: get_ids: error allocating %d bytes "
211                         "for sname\n", name.length + 1);
212                 gss_release_buffer(&min_stat, &name);
213                 goto out;
214         }
215         memcpy(sname, name.value, name.length);
216         printerr(1, "sname = %s\n", sname);
217         gss_release_buffer(&min_stat, &name);
218
219         res = -EINVAL;
220         if ((secname = mech2file(mech)) == NULL) {
221                 printerr(0, "WARNING: get_ids: error mapping mech to "
222                         "file for name '%s'\n", sname);
223                 goto out_free;
224         }
225         nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
226         res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
227         if (res < 0) {
228                 /*
229                  * -ENOENT means there was no mapping, any other error
230                  * value means there was an error trying to do the
231                  * mapping.
232                  * If there was no mapping, we send down the value -1
233                  * to indicate that the anonuid/anongid for the export
234                  * should be used.
235                  */
236                 if (res == -ENOENT) {
237                         cred->cr_uid = -1;
238                         cred->cr_gid = -1;
239                         cred->cr_ngroups = 0;
240                         res = 0;
241                         goto out_free;
242                 }
243                 printerr(0, "WARNING: get_ids: failed to map name '%s' "
244                         "to uid/gid: %s\n", sname, strerror(-res));
245                 goto out_free;
246         }
247         cred->cr_uid = uid;
248         cred->cr_gid = gid;
249         add_supplementary_groups(secname, sname, cred);
250         res = 0;
251 out_free:
252         free(sname);
253 out:
254         return res;
255 }
256 #endif
257
258 #if 0
259 void
260 print_hexl(int pri, unsigned char *cp, int length)
261 {
262         int i, j, jm;
263         unsigned char c;
264
265         printerr(pri, "length %d\n",length);
266         printerr(pri, "\n");
267
268         for (i = 0; i < length; i += 0x10) {
269                 printerr(pri, "  %04x: ", (u_int)i);
270                 jm = length - i;
271                 jm = jm > 16 ? 16 : jm;
272
273                 for (j = 0; j < jm; j++) {
274                         if ((j % 2) == 1)
275                                 printerr(pri,"%02x ", (u_int)cp[i+j]);
276                         else
277                                 printerr(pri,"%02x", (u_int)cp[i+j]);
278                 }
279                 for (; j < 16; j++) {
280                         if ((j % 2) == 1)
281                                 printerr(pri,"   ");
282                         else
283                                 printerr(pri,"  ");
284                 }
285                 printerr(pri," ");
286
287                 for (j = 0; j < jm; j++) {
288                         c = cp[i+j];
289                         c = isprint(c) ? c : '.';
290                         printerr(pri,"%c", c);
291                 }
292                 printerr(pri,"\n");
293         }
294 }
295 #endif
296
297 static int
298 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
299         lnet_nid_t nid, uint32_t lustre_svc)
300 {
301         u_int32_t       maj_stat, min_stat;
302         gss_buffer_desc name;
303         char            *sname, *realm, *slash;
304         int             res = -1;
305         gss_OID         name_type = GSS_C_NO_OID;
306         struct passwd   *pw;
307
308         cred->cr_remote = cred->cr_usr_root = cred->cr_usr_mds = 0;
309         cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
310
311         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
312         if (maj_stat != GSS_S_COMPLETE) {
313                 pgsserr("get_ids: gss_display_name",
314                         maj_stat, min_stat, mech);
315                 return -1;
316         }
317         if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
318             !(sname = calloc(name.length + 1, 1))) {
319                 printerr(0, "WARNING: get_ids: error allocating %d bytes "
320                         "for sname\n", name.length + 1);
321                 gss_release_buffer(&min_stat, &name);
322                 return -1;
323         }
324         memcpy(sname, name.value, name.length);
325         printerr(1, "authenticated %s from %016llx\n", sname, nid);
326         gss_release_buffer(&min_stat, &name);
327
328         if (lustre_svc == LUSTRE_GSS_SVC_MDS)
329                 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
330         else
331                 cred->cr_mapped_uid = -1;
332
333         realm = strchr(sname, '@');
334         if (!realm) {
335                 printerr(0, "WARNNING: principal %s contains no realm name\n",
336                          sname);
337                 cred->cr_remote = (mds_local_realm != NULL);
338         } else {
339                 *realm++ = '\0';
340                 if (!mds_local_realm)
341                         cred->cr_remote = 1;
342                 else
343                         cred->cr_remote =
344                                 (strcasecmp(mds_local_realm, realm) != 0);
345         }
346
347         if (cred->cr_remote) {
348                 if (cred->cr_mapped_uid != -1)
349                         res = 0;
350                 else if (lustre_svc == LUSTRE_GSS_SVC_OSS &&
351                          strcmp(sname, "lustre_root") == 0)
352                         res = 0;
353                 else
354                         printerr(0, "principal %s is remote without mapping\n",
355                                  sname);
356                 goto out_free;
357         }
358
359         slash = strchr(sname, '/');
360         if (slash)
361                 *slash = '\0';
362
363         if (!(pw = getpwnam(sname))) {
364                 /* If client use machine credential, we map it to root, which
365                  * will subject to further mapping by root-squash in kernel.
366                  *
367                  * MDS service keytab is treated as special user, also mapped
368                  * to root. OSS service keytab can't be used as a user.
369                  */
370                 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
371                         printerr(2, "lustre_root principal, resolve to uid 0\n");
372                         cred->cr_uid = 0;
373                         cred->cr_usr_root = 1;
374                 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
375                         printerr(2, "mds service principal, resolve to uid 0\n");
376                         cred->cr_uid = 0;
377                         cred->cr_usr_mds = 1;
378                 } else {
379                         cred->cr_uid = -1;
380                         if (cred->cr_mapped_uid == -1) {
381                                 printerr(0, "invalid user %s\n", sname);
382                                 goto out_free;
383                         }
384                         printerr(2, "user %s mapped to %u\n",
385                                  sname, cred->cr_mapped_uid);
386                 }
387         } else {
388                 cred->cr_uid = pw->pw_uid;
389                 printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid);
390         }
391
392         res = 0;
393 out_free:
394         free(sname);
395         return res;
396 }
397
398 typedef struct gss_union_ctx_id_t {
399         gss_OID         mech_type;
400         gss_ctx_id_t    internal_ctx_id;
401 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
402
403 /*
404  * return -1 only if we detect error during reading from upcall channel,
405  * all other cases return 0.
406  */
407 int
408 handle_nullreq(FILE *f) {
409         uint64_t                handle_seq;
410         char                    in_tok_buf[TOKEN_BUF_SIZE];
411         char                    in_handle_buf[15];
412         char                    out_handle_buf[15];
413         gss_buffer_desc         in_tok = {.value = in_tok_buf},
414                                 out_tok = {.value = NULL},
415                                 in_handle = {.value = in_handle_buf},
416                                 out_handle = {.value = out_handle_buf},
417                                 ctx_token = {.value = NULL},
418                                 ignore_out_tok = {.value = NULL},
419         /* XXX isn't there a define for this?: */
420                                 null_token = {.value = NULL};
421         uint32_t                lustre_svc;
422         lnet_nid_t              nid;
423         u_int32_t               ret_flags;
424         gss_ctx_id_t            ctx = GSS_C_NO_CONTEXT;
425         gss_name_t              client_name;
426         gss_OID                 mech = GSS_C_NO_OID;
427         gss_cred_id_t           svc_cred;
428         u_int32_t               maj_stat = GSS_S_FAILURE, min_stat = 0;
429         u_int32_t               ignore_min_stat;
430         struct svc_cred         cred;
431         static char             *lbuf = NULL;
432         static int              lbuflen = 0;
433         static char             *cp;
434
435         printerr(2, "handling null request\n");
436
437         if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
438                 printerr(0, "WARNING: handle_nullreq: "
439                             "failed reading request\n");
440                 return -1;
441         }
442
443         cp = lbuf;
444
445         qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
446         qword_get(&cp, (char *) &nid, sizeof(nid));
447         qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
448         printerr(1, "handling req: svc %u, nid %016llx, idx %llx\n",
449                  lustre_svc, nid, handle_seq);
450
451         in_handle.length = (size_t) qword_get(&cp, in_handle.value,
452                                               sizeof(in_handle_buf));
453         printerr(3, "in_handle: \n");
454         print_hexl(3, in_handle.value, in_handle.length);
455
456         in_tok.length = (size_t) qword_get(&cp, in_tok.value,
457                                            sizeof(in_tok_buf));
458         printerr(3, "in_tok: \n");
459         print_hexl(3, in_tok.value, in_tok.length);
460
461         if (in_tok.length < 0) {
462                 printerr(0, "WARNING: handle_nullreq: "
463                             "failed parsing request\n");
464                 goto out_err;
465         }
466
467         if (in_handle.length != 0) { /* CONTINUE_INIT case */
468                 if (in_handle.length != sizeof(ctx)) {
469                         printerr(0, "WARNING: handle_nullreq: "
470                                     "input handle has unexpected length %d\n",
471                                     in_handle.length);
472                         goto out_err;
473                 }
474                 /* in_handle is the context id stored in the out_handle
475                  * for the GSS_S_CONTINUE_NEEDED case below.  */
476                 memcpy(&ctx, in_handle.value, in_handle.length);
477         }
478
479         svc_cred = gssd_select_svc_cred(lustre_svc);
480         if (!svc_cred) {
481                 printerr(0, "no service credential for svc %u\n", lustre_svc);
482                 goto out_err;
483         }
484
485         maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
486                         &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
487                         &mech, &out_tok, &ret_flags, NULL, NULL);
488
489         if (maj_stat == GSS_S_CONTINUE_NEEDED) {
490                 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
491
492                 /* Save the context handle for future calls */
493                 out_handle.length = sizeof(ctx);
494                 memcpy(out_handle.value, &ctx, sizeof(ctx));
495                 goto continue_needed;
496         }
497         else if (maj_stat != GSS_S_COMPLETE) {
498                 printerr(0, "WARNING: gss_accept_sec_context failed\n");
499                 pgsserr("handle_nullreq: gss_accept_sec_context",
500                         maj_stat, min_stat, mech);
501                 goto out_err;
502         }
503
504         if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
505                 /* get_ids() prints error msg */
506                 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
507                 gss_release_name(&ignore_min_stat, &client_name);
508                 goto out_err;
509         }
510         gss_release_name(&ignore_min_stat, &client_name);
511
512         /* Context complete. Pass handle_seq in out_handle to use
513          * for context lookup in the kernel. */
514         out_handle.length = sizeof(handle_seq);
515         memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
516
517         /* kernel needs ctx to calculate verifier on null response, so
518          * must give it context before doing null call: */
519         if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
520                 printerr(0, "WARNING: handle_nullreq: "
521                             "serialize_context_for_kernel failed\n");
522                 maj_stat = GSS_S_FAILURE;
523                 goto out_err;
524         }
525         /* We no longer need the gss context */
526         gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
527
528         do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
529 continue_needed:
530         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
531                         &out_handle, &out_tok);
532 out:
533         if (ctx_token.value != NULL)
534                 free(ctx_token.value);
535         if (out_tok.value != NULL)
536                 gss_release_buffer(&ignore_min_stat, &out_tok);
537         return 0;
538
539 out_err:
540         if (ctx != GSS_C_NO_CONTEXT)
541                 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
542         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
543                         &null_token, &null_token);
544         goto out;
545 }