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