Whamcloud - gitweb
LU-867 gss: adapt to 2.6.32 kernel changes cache_detail
[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.sptlrpc.context/channel"
57 #define SVCGSSD_INIT_CHANNEL    "/proc/net/rpc/auth.sptlrpc.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         uint32_t cr_usr_oss;
66         uid_t    cr_uid;
67         uid_t    cr_mapped_uid;
68         uid_t    cr_gid;
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         char *fname = NULL;
77         int err;
78
79         printerr(2, "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_remote);
93         qword_printint(f, cred->cr_usr_root);
94         qword_printint(f, cred->cr_usr_mds);
95         qword_printint(f, cred->cr_usr_oss);
96         qword_printint(f, cred->cr_mapped_uid);
97         qword_printint(f, cred->cr_uid);
98         qword_printint(f, cred->cr_gid);
99         qword_print(f, fname);
100         qword_printhex(f, context_token->value, context_token->length);
101         err = qword_eol(f);
102         fclose(f);
103         return err;
104 out_err:
105         printerr(0, "WARNING: downcall failed\n");
106         return -1;
107 }
108
109 struct gss_verifier {
110         u_int32_t       flav;
111         gss_buffer_desc body;
112 };
113
114 #define RPCSEC_GSS_SEQ_WIN      5
115
116 static int
117 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
118               u_int32_t maj_stat, u_int32_t min_stat,
119               gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
120 {
121         char buf[2 * TOKEN_BUF_SIZE];
122         char *bp = buf;
123         int blen = sizeof(buf);
124         /* XXXARG: */
125         int g;
126
127         printerr(2, "sending null reply\n");
128
129         qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
130         qword_addhex(&bp, &blen, in_token->value, in_token->length);
131         qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
132         qword_adduint(&bp, &blen, maj_stat);
133         qword_adduint(&bp, &blen, min_stat);
134         qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
135         qword_addhex(&bp, &blen, out_token->value, out_token->length);
136         qword_addeol(&bp, &blen);
137         if (blen <= 0) {
138                 printerr(0, "WARNING: send_respsonse: message too long\n");
139                 return -1;
140         }
141         g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
142         if (g == -1) {
143                 printerr(0, "WARNING: open %s failed: %s\n",
144                                 SVCGSSD_INIT_CHANNEL, strerror(errno));
145                 return -1;
146         }
147         *bp = '\0';
148         printerr(3, "writing message: %s", buf);
149         if (write(g, buf, bp - buf) == -1) {
150                 printerr(0, "WARNING: failed to write message\n");
151                 close(g);
152                 return -1;
153         }
154         close(g);
155         return 0;
156 }
157
158 #define rpc_auth_ok                     0
159 #define rpc_autherr_badcred             1
160 #define rpc_autherr_rejectedcred        2
161 #define rpc_autherr_badverf             3
162 #define rpc_autherr_rejectedverf        4
163 #define rpc_autherr_tooweak             5
164 #define rpcsec_gsserr_credproblem       13
165 #define rpcsec_gsserr_ctxproblem        14
166
167 #if 0
168 static void
169 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
170 {
171         int ret;
172         static gid_t *groups = NULL;
173
174         cred->cr_ngroups = NGROUPS;
175         ret = nfs4_gss_princ_to_grouplist(secname, name,
176                         cred->cr_groups, &cred->cr_ngroups);
177         if (ret < 0) {
178                 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
179                 ret = nfs4_gss_princ_to_grouplist(secname, name,
180                                 groups, &cred->cr_ngroups);
181                 if (ret < 0)
182                         cred->cr_ngroups = 0;
183                 else {
184                         if (cred->cr_ngroups > NGROUPS)
185                                 cred->cr_ngroups = NGROUPS;
186                         memcpy(cred->cr_groups, groups,
187                                         cred->cr_ngroups*sizeof(gid_t));
188                 }
189         }
190 }
191 #endif
192
193 #if 0
194 static int
195 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
196 {
197         u_int32_t       maj_stat, min_stat;
198         gss_buffer_desc name;
199         char            *sname;
200         int             res = -1;
201         uid_t           uid, gid;
202         gss_OID         name_type = GSS_C_NO_OID;
203         char            *secname;
204
205         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
206         if (maj_stat != GSS_S_COMPLETE) {
207                 pgsserr("get_ids: gss_display_name",
208                         maj_stat, min_stat, mech);
209                 goto out;
210         }
211         if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
212             !(sname = calloc(name.length + 1, 1))) {
213                 printerr(0, "WARNING: get_ids: error allocating %d bytes "
214                         "for sname\n", name.length + 1);
215                 gss_release_buffer(&min_stat, &name);
216                 goto out;
217         }
218         memcpy(sname, name.value, name.length);
219         printerr(1, "sname = %s\n", sname);
220         gss_release_buffer(&min_stat, &name);
221
222         res = -EINVAL;
223         if ((secname = mech2file(mech)) == NULL) {
224                 printerr(0, "WARNING: get_ids: error mapping mech to "
225                         "file for name '%s'\n", sname);
226                 goto out_free;
227         }
228         nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
229         res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
230         if (res < 0) {
231                 /*
232                  * -ENOENT means there was no mapping, any other error
233                  * value means there was an error trying to do the
234                  * mapping.
235                  * If there was no mapping, we send down the value -1
236                  * to indicate that the anonuid/anongid for the export
237                  * should be used.
238                  */
239                 if (res == -ENOENT) {
240                         cred->cr_uid = -1;
241                         cred->cr_gid = -1;
242                         cred->cr_ngroups = 0;
243                         res = 0;
244                         goto out_free;
245                 }
246                 printerr(0, "WARNING: get_ids: failed to map name '%s' "
247                         "to uid/gid: %s\n", sname, strerror(-res));
248                 goto out_free;
249         }
250         cred->cr_uid = uid;
251         cred->cr_gid = gid;
252         add_supplementary_groups(secname, sname, cred);
253         res = 0;
254 out_free:
255         free(sname);
256 out:
257         return res;
258 }
259 #endif
260
261 #if 0
262 void
263 print_hexl(int pri, unsigned char *cp, int length)
264 {
265         int i, j, jm;
266         unsigned char c;
267
268         printerr(pri, "length %d\n",length);
269         printerr(pri, "\n");
270
271         for (i = 0; i < length; i += 0x10) {
272                 printerr(pri, "  %04x: ", (u_int)i);
273                 jm = length - i;
274                 jm = jm > 16 ? 16 : jm;
275
276                 for (j = 0; j < jm; j++) {
277                         if ((j % 2) == 1)
278                                 printerr(pri,"%02x ", (u_int)cp[i+j]);
279                         else
280                                 printerr(pri,"%02x", (u_int)cp[i+j]);
281                 }
282                 for (; j < 16; j++) {
283                         if ((j % 2) == 1)
284                                 printerr(pri,"   ");
285                         else
286                                 printerr(pri,"  ");
287                 }
288                 printerr(pri," ");
289
290                 for (j = 0; j < jm; j++) {
291                         c = cp[i+j];
292                         c = isprint(c) ? c : '.';
293                         printerr(pri,"%c", c);
294                 }
295                 printerr(pri,"\n");
296         }
297 }
298 #endif
299
300 static int
301 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
302         lnet_nid_t nid, uint32_t lustre_svc)
303 {
304         u_int32_t       maj_stat, min_stat;
305         gss_buffer_desc name;
306         char            *sname, *host, *realm;
307         const int       namebuf_size = 512;
308         char            namebuf[namebuf_size];
309         int             res = -1;
310         gss_OID         name_type = GSS_C_NO_OID;
311         struct passwd   *pw;
312
313         cred->cr_remote = 0;
314         cred->cr_usr_root = cred->cr_usr_mds = cred->cr_usr_oss = 0;
315         cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
316
317         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
318         if (maj_stat != GSS_S_COMPLETE) {
319                 pgsserr("get_ids: gss_display_name",
320                         maj_stat, min_stat, mech);
321                 return -1;
322         }
323         if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
324             !(sname = calloc(name.length + 1, 1))) {
325                 printerr(0, "WARNING: get_ids: error allocating %d bytes "
326                         "for sname\n", name.length + 1);
327                 gss_release_buffer(&min_stat, &name);
328                 return -1;
329         }
330         memcpy(sname, name.value, name.length);
331         gss_release_buffer(&min_stat, &name);
332
333         if (lustre_svc == LUSTRE_GSS_SVC_MDS)
334                 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
335         else
336                 cred->cr_mapped_uid = -1;
337
338         realm = strchr(sname, '@');
339         if (realm) {
340                 *realm++ = '\0';
341         } else {
342                 printerr(0, "ERROR: %s has no realm name\n", sname);
343                 goto out_free;
344         }
345
346         host = strchr(sname, '/');
347         if (host)
348                 *host++ = '\0';
349
350         if (strcmp(sname, GSSD_SERVICE_MGS) == 0) {
351                 printerr(0, "forbid %s as a user name\n", sname);
352                 goto out_free;
353         }
354
355         /* 1. check host part */
356         if (host) {
357                 if (lnet_nid2hostname(nid, namebuf, namebuf_size)) {
358                         printerr(0, "ERROR: failed to resolve hostname for "
359                                  "%s/%s@%s from %016llx\n",
360                                  sname, host, realm, nid);
361                         goto out_free;
362                 }
363
364                 if (strcasecmp(host, namebuf)) {
365                         printerr(0, "ERROR: %s/%s@%s claimed hostname doesn't "
366                                  "match %s, nid %016llx\n", sname, host, realm,
367                                  namebuf, nid);
368                         goto out_free;
369                 }
370         } else {
371                 if (!strcmp(sname, GSSD_SERVICE_MDS) ||
372                     !strcmp(sname, GSSD_SERVICE_OSS)) {
373                         printerr(0, "ERROR: %s@%s from %016llx doesn't "
374                                  "bind with hostname\n", sname, realm, nid);
375                         goto out_free;
376                 }
377         }
378
379         /* 2. check realm and user */
380         switch (lustre_svc) {
381         case LUSTRE_GSS_SVC_MDS:
382                 if (strcasecmp(mds_local_realm, realm)) {
383                         cred->cr_remote = 1;
384
385                         /* only allow mapped user from remote realm */
386                         if (cred->cr_mapped_uid == -1) {
387                                 printerr(0, "ERROR: %s%s%s@%s from %016llx "
388                                          "is remote but without mapping\n",
389                                          sname, host ? "/" : "",
390                                          host ? host : "", realm, nid);
391                                 break;
392                         }
393                 } else {
394                         if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
395                                 cred->cr_uid = 0;
396                                 cred->cr_usr_root = 1;
397                         } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
398                                 cred->cr_uid = 0;
399                                 cred->cr_usr_mds = 1;
400                         } else if (!strcmp(sname, GSSD_SERVICE_OSS)) {
401                                 printerr(0, "ERROR: MDS doesn't accept "
402                                          "user "GSSD_SERVICE_OSS"\n");
403                                 break;
404                         } else {
405                                 pw = getpwnam(sname);
406                                 if (pw != NULL) {
407                                         cred->cr_uid = pw->pw_uid;
408                                         printerr(2, "%s resolve to uid %u\n",
409                                                  sname, cred->cr_uid);
410                                 } else if (cred->cr_mapped_uid != -1) {
411                                         printerr(2, "user %s from %016llx is "
412                                                  "mapped to %u\n", sname, nid,
413                                                  cred->cr_mapped_uid);
414                                 } else {
415                                         printerr(0, "ERROR: invalid user, "
416                                                  "%s/%s@%s from %016llx\n",
417                                                  sname, host, realm, nid);
418                                         break;
419                                 }
420                         }
421                 }
422
423                 res = 0;
424                 break;
425         case LUSTRE_GSS_SVC_MGS:
426                 if (!strcmp(sname, GSSD_SERVICE_OSS)) {
427                         cred->cr_uid = 0;
428                         cred->cr_usr_oss = 1;
429                 }
430                 /* fall through */
431         case LUSTRE_GSS_SVC_OSS:
432                 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
433                         cred->cr_uid = 0;
434                         cred->cr_usr_root = 1;
435                 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
436                         cred->cr_uid = 0;
437                         cred->cr_usr_mds = 1;
438                 } else {
439                         printerr(0, "ERROR: svc %d doesn't accept user %s"
440                                  "from %016llx\n", lustre_svc, sname, nid);
441                         break;
442                 }
443                 res = 0;
444                 break;
445         default:
446                 assert(0);
447         }
448
449 out_free:
450         if (!res)
451                 printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n",
452                          lustre_svc_name[lustre_svc], sname,
453                          host ? "/" : "", host ? host : "", realm, nid);
454         free(sname);
455         return res;
456 }
457
458 typedef struct gss_union_ctx_id_t {
459         gss_OID         mech_type;
460         gss_ctx_id_t    internal_ctx_id;
461 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
462
463 /*
464  * return -1 only if we detect error during reading from upcall channel,
465  * all other cases return 0.
466  */
467 int
468 handle_nullreq(FILE *f) {
469         uint64_t                handle_seq;
470         char                    in_tok_buf[TOKEN_BUF_SIZE];
471         char                    in_handle_buf[15];
472         char                    out_handle_buf[15];
473         gss_buffer_desc         in_tok = {.value = in_tok_buf},
474                                 out_tok = {.value = NULL},
475                                 in_handle = {.value = in_handle_buf},
476                                 out_handle = {.value = out_handle_buf},
477                                 ctx_token = {.value = NULL},
478                                 ignore_out_tok = {.value = NULL},
479         /* XXX isn't there a define for this?: */
480                                 null_token = {.value = NULL};
481         uint32_t                lustre_svc;
482         lnet_nid_t              nid;
483         u_int32_t               ret_flags;
484         gss_ctx_id_t            ctx = GSS_C_NO_CONTEXT;
485         gss_name_t              client_name;
486         gss_OID                 mech = GSS_C_NO_OID;
487         gss_cred_id_t           svc_cred;
488         u_int32_t               maj_stat = GSS_S_FAILURE, min_stat = 0;
489         u_int32_t               ignore_min_stat;
490         struct svc_cred         cred;
491         static char             *lbuf = NULL;
492         static int              lbuflen = 0;
493         static char             *cp;
494
495         printerr(2, "handling null request\n");
496
497         if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
498                 printerr(0, "WARNING: handle_nullreq: "
499                             "failed reading request\n");
500                 return -1;
501         }
502
503         cp = lbuf;
504
505         qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
506         qword_get(&cp, (char *) &nid, sizeof(nid));
507         qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
508         printerr(2, "handling req: svc %u, nid %016llx, idx %llx\n",
509                  lustre_svc, nid, handle_seq);
510
511         in_handle.length = (size_t) qword_get(&cp, in_handle.value,
512                                               sizeof(in_handle_buf));
513         printerr(3, "in_handle: \n");
514         print_hexl(3, in_handle.value, in_handle.length);
515
516         in_tok.length = (size_t) qword_get(&cp, in_tok.value,
517                                            sizeof(in_tok_buf));
518         printerr(3, "in_tok: \n");
519         print_hexl(3, in_tok.value, in_tok.length);
520
521         if (in_tok.length < 0) {
522                 printerr(0, "WARNING: handle_nullreq: "
523                             "failed parsing request\n");
524                 goto out_err;
525         }
526
527         if (in_handle.length != 0) { /* CONTINUE_INIT case */
528                 if (in_handle.length != sizeof(ctx)) {
529                         printerr(0, "WARNING: handle_nullreq: "
530                                     "input handle has unexpected length %d\n",
531                                     in_handle.length);
532                         goto out_err;
533                 }
534                 /* in_handle is the context id stored in the out_handle
535                  * for the GSS_S_CONTINUE_NEEDED case below.  */
536                 memcpy(&ctx, in_handle.value, in_handle.length);
537         }
538
539         svc_cred = gssd_select_svc_cred(lustre_svc);
540         if (!svc_cred) {
541                 printerr(0, "no service credential for svc %u\n", lustre_svc);
542                 goto out_err;
543         }
544
545         maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
546                         &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
547                         &mech, &out_tok, &ret_flags, NULL, NULL);
548
549         if (maj_stat == GSS_S_CONTINUE_NEEDED) {
550                 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
551
552                 /* Save the context handle for future calls */
553                 out_handle.length = sizeof(ctx);
554                 memcpy(out_handle.value, &ctx, sizeof(ctx));
555                 goto continue_needed;
556         }
557         else if (maj_stat != GSS_S_COMPLETE) {
558                 printerr(0, "WARNING: gss_accept_sec_context failed\n");
559                 pgsserr("handle_nullreq: gss_accept_sec_context",
560                         maj_stat, min_stat, mech);
561                 goto out_err;
562         }
563
564         if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
565                 /* get_ids() prints error msg */
566                 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
567                 gss_release_name(&ignore_min_stat, &client_name);
568                 goto out_err;
569         }
570         gss_release_name(&ignore_min_stat, &client_name);
571
572         /* Context complete. Pass handle_seq in out_handle to use
573          * for context lookup in the kernel. */
574         out_handle.length = sizeof(handle_seq);
575         memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
576
577         /* kernel needs ctx to calculate verifier on null response, so
578          * must give it context before doing null call: */
579         if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
580                 printerr(0, "WARNING: handle_nullreq: "
581                             "serialize_context_for_kernel failed\n");
582                 maj_stat = GSS_S_FAILURE;
583                 goto out_err;
584         }
585         /* We no longer need the gss context */
586         gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
587
588         do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
589 continue_needed:
590         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
591                         &out_handle, &out_tok);
592 out:
593         if (ctx_token.value != NULL)
594                 free(ctx_token.value);
595         if (out_tok.value != NULL)
596                 gss_release_buffer(&ignore_min_stat, &out_tok);
597         return 0;
598
599 out_err:
600         if (ctx != GSS_C_NO_CONTEXT)
601                 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
602         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
603                         &null_token, &null_token);
604         goto out;
605 }