Whamcloud - gitweb
LU-6484 build: autoconf checks cleanup
[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   Copyright (c) 2014, Intel Corporation.
10
11   Redistribution and use in source and binary forms, with or without
12   modification, are permitted provided that the following conditions
13   are met:
14
15   1. Redistributions of source code must retain the above copyright
16      notice, this list of conditions and the following disclaimer.
17   2. Redistributions in binary form must reproduce the above copyright
18      notice, this list of conditions and the following disclaimer in the
19      documentation and/or other materials provided with the distribution.
20   3. Neither the name of the University nor the names of its
21      contributors may be used to endorse or promote products derived
22      from this software without specific prior written permission.
23
24   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
36 */
37
38 #include <sys/param.h>
39 #include <sys/stat.h>
40
41 #include <pwd.h>
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <ctype.h>
45 #include <string.h>
46 #include <fcntl.h>
47 #include <errno.h>
48 #ifdef HAVE_NETDB_H
49 # include <netdb.h>
50 #endif
51
52 #include "svcgssd.h"
53 #include "gss_util.h"
54 #include "err_util.h"
55 #include "context.h"
56 #include "cacheio.h"
57 #include "lsupport.h"
58
59 extern char * mech2file(gss_OID mech);
60 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.sptlrpc.context/channel"
61 #define SVCGSSD_INIT_CHANNEL    "/proc/net/rpc/auth.sptlrpc.init/channel"
62
63 #define TOKEN_BUF_SIZE          8192
64
65 struct svc_cred {
66         uint32_t cr_remote;
67         uint32_t cr_usr_root;
68         uint32_t cr_usr_mds;
69         uint32_t cr_usr_oss;
70         uid_t    cr_uid;
71         uid_t    cr_mapped_uid;
72         uid_t    cr_gid;
73 };
74
75 static int
76 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
77                 gss_OID mech, gss_buffer_desc *context_token)
78 {
79         FILE *f;
80         char *fname = NULL;
81         int err;
82
83         printerr(2, "doing downcall\n");
84         if ((fname = mech2file(mech)) == NULL)
85                 goto out_err;
86         f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
87         if (f == NULL) {
88                 printerr(0, "WARNING: unable to open downcall channel "
89                              "%s: %s\n",
90                              SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
91                 goto out_err;
92         }
93         qword_printhex(f, out_handle->value, out_handle->length);
94         /* XXX are types OK for the rest of this? */
95         qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
96         qword_printint(f, cred->cr_remote);
97         qword_printint(f, cred->cr_usr_root);
98         qword_printint(f, cred->cr_usr_mds);
99         qword_printint(f, cred->cr_usr_oss);
100         qword_printint(f, cred->cr_mapped_uid);
101         qword_printint(f, cred->cr_uid);
102         qword_printint(f, cred->cr_gid);
103         qword_print(f, fname);
104         qword_printhex(f, context_token->value, context_token->length);
105         err = qword_eol(f);
106         fclose(f);
107         return err;
108 out_err:
109         printerr(0, "WARNING: downcall failed\n");
110         return -1;
111 }
112
113 struct gss_verifier {
114         u_int32_t       flav;
115         gss_buffer_desc body;
116 };
117
118 #define RPCSEC_GSS_SEQ_WIN      5
119
120 static int
121 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
122               u_int32_t maj_stat, u_int32_t min_stat,
123               gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
124 {
125         char buf[2 * TOKEN_BUF_SIZE];
126         char *bp = buf;
127         int blen = sizeof(buf);
128         /* XXXARG: */
129         int g;
130
131         printerr(2, "sending null reply\n");
132
133         qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
134         qword_addhex(&bp, &blen, in_token->value, in_token->length);
135         qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
136         qword_adduint(&bp, &blen, maj_stat);
137         qword_adduint(&bp, &blen, min_stat);
138         qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
139         qword_addhex(&bp, &blen, out_token->value, out_token->length);
140         qword_addeol(&bp, &blen);
141         if (blen <= 0) {
142                 printerr(0, "WARNING: send_respsonse: message too long\n");
143                 return -1;
144         }
145         g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
146         if (g == -1) {
147                 printerr(0, "WARNING: open %s failed: %s\n",
148                                 SVCGSSD_INIT_CHANNEL, strerror(errno));
149                 return -1;
150         }
151         *bp = '\0';
152         printerr(3, "writing message: %s", buf);
153         if (write(g, buf, bp - buf) == -1) {
154                 printerr(0, "WARNING: failed to write message\n");
155                 close(g);
156                 return -1;
157         }
158         close(g);
159         return 0;
160 }
161
162 #define rpc_auth_ok                     0
163 #define rpc_autherr_badcred             1
164 #define rpc_autherr_rejectedcred        2
165 #define rpc_autherr_badverf             3
166 #define rpc_autherr_rejectedverf        4
167 #define rpc_autherr_tooweak             5
168 #define rpcsec_gsserr_credproblem       13
169 #define rpcsec_gsserr_ctxproblem        14
170
171 #if 0
172 static void
173 add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
174 {
175         int ret;
176         static gid_t *groups = NULL;
177
178         cred->cr_ngroups = NGROUPS;
179         ret = nfs4_gss_princ_to_grouplist(secname, name,
180                         cred->cr_groups, &cred->cr_ngroups);
181         if (ret < 0) {
182                 groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
183                 ret = nfs4_gss_princ_to_grouplist(secname, name,
184                                 groups, &cred->cr_ngroups);
185                 if (ret < 0)
186                         cred->cr_ngroups = 0;
187                 else {
188                         if (cred->cr_ngroups > NGROUPS)
189                                 cred->cr_ngroups = NGROUPS;
190                         memcpy(cred->cr_groups, groups,
191                                         cred->cr_ngroups*sizeof(gid_t));
192                 }
193         }
194 }
195 #endif
196
197 #if 0
198 static int
199 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
200 {
201         u_int32_t       maj_stat, min_stat;
202         gss_buffer_desc name;
203         char            *sname;
204         int             res = -1;
205         uid_t           uid, gid;
206         gss_OID         name_type = GSS_C_NO_OID;
207         char            *secname;
208
209         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
210         if (maj_stat != GSS_S_COMPLETE) {
211                 pgsserr("get_ids: gss_display_name",
212                         maj_stat, min_stat, mech);
213                 goto out;
214         }
215         if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
216             !(sname = calloc(name.length + 1, 1))) {
217                 printerr(0, "WARNING: get_ids: error allocating %d bytes "
218                         "for sname\n", name.length + 1);
219                 gss_release_buffer(&min_stat, &name);
220                 goto out;
221         }
222         memcpy(sname, name.value, name.length);
223         printerr(1, "sname = %s\n", sname);
224         gss_release_buffer(&min_stat, &name);
225
226         res = -EINVAL;
227         if ((secname = mech2file(mech)) == NULL) {
228                 printerr(0, "WARNING: get_ids: error mapping mech to "
229                         "file for name '%s'\n", sname);
230                 goto out_free;
231         }
232         nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
233         res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
234         if (res < 0) {
235                 /*
236                  * -ENOENT means there was no mapping, any other error
237                  * value means there was an error trying to do the
238                  * mapping.
239                  * If there was no mapping, we send down the value -1
240                  * to indicate that the anonuid/anongid for the export
241                  * should be used.
242                  */
243                 if (res == -ENOENT) {
244                         cred->cr_uid = -1;
245                         cred->cr_gid = -1;
246                         cred->cr_ngroups = 0;
247                         res = 0;
248                         goto out_free;
249                 }
250                 printerr(0, "WARNING: get_ids: failed to map name '%s' "
251                         "to uid/gid: %s\n", sname, strerror(-res));
252                 goto out_free;
253         }
254         cred->cr_uid = uid;
255         cred->cr_gid = gid;
256         add_supplementary_groups(secname, sname, cred);
257         res = 0;
258 out_free:
259         free(sname);
260 out:
261         return res;
262 }
263 #endif
264
265 #if 0
266 void
267 print_hexl(int pri, unsigned char *cp, int length)
268 {
269         int i, j, jm;
270         unsigned char c;
271
272         printerr(pri, "length %d\n",length);
273         printerr(pri, "\n");
274
275         for (i = 0; i < length; i += 0x10) {
276                 printerr(pri, "  %04x: ", (u_int)i);
277                 jm = length - i;
278                 jm = jm > 16 ? 16 : jm;
279
280                 for (j = 0; j < jm; j++) {
281                         if ((j % 2) == 1)
282                                 printerr(pri,"%02x ", (u_int)cp[i+j]);
283                         else
284                                 printerr(pri,"%02x", (u_int)cp[i+j]);
285                 }
286                 for (; j < 16; j++) {
287                         if ((j % 2) == 1)
288                                 printerr(pri,"   ");
289                         else
290                                 printerr(pri,"  ");
291                 }
292                 printerr(pri," ");
293
294                 for (j = 0; j < jm; j++) {
295                         c = cp[i+j];
296                         c = isprint(c) ? c : '.';
297                         printerr(pri,"%c", c);
298                 }
299                 printerr(pri,"\n");
300         }
301 }
302 #endif
303
304 static int
305 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
306         lnet_nid_t nid, uint32_t lustre_svc)
307 {
308         u_int32_t       maj_stat, min_stat;
309         gss_buffer_desc name;
310         char            *sname, *host, *realm;
311         const int       namebuf_size = 512;
312         char            namebuf[namebuf_size];
313         int             res = -1;
314         gss_OID         name_type = GSS_C_NO_OID;
315         struct passwd   *pw;
316
317         cred->cr_remote = 0;
318         cred->cr_usr_root = cred->cr_usr_mds = cred->cr_usr_oss = 0;
319         cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
320
321         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
322         if (maj_stat != GSS_S_COMPLETE) {
323                 pgsserr("get_ids: gss_display_name",
324                         maj_stat, min_stat, mech);
325                 return -1;
326         }
327         if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
328             !(sname = calloc(name.length + 1, 1))) {
329                 printerr(0, "WARNING: get_ids: error allocating %d bytes "
330                         "for sname\n", name.length + 1);
331                 gss_release_buffer(&min_stat, &name);
332                 return -1;
333         }
334         memcpy(sname, name.value, name.length);
335         sname[name.length] = '\0';
336         gss_release_buffer(&min_stat, &name);
337
338         if (lustre_svc == LUSTRE_GSS_SVC_MDS)
339                 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
340         else
341                 cred->cr_mapped_uid = -1;
342
343         realm = strchr(sname, '@');
344         if (realm) {
345                 *realm++ = '\0';
346         } else {
347                 printerr(0, "ERROR: %s has no realm name\n", sname);
348                 goto out_free;
349         }
350
351         host = strchr(sname, '/');
352         if (host)
353                 *host++ = '\0';
354
355         if (strcmp(sname, GSSD_SERVICE_MGS) == 0) {
356                 printerr(0, "forbid %s as a user name\n", sname);
357                 goto out_free;
358         }
359
360         /* 1. check host part */
361         if (host) {
362                 if (lnet_nid2hostname(nid, namebuf, namebuf_size)) {
363                         printerr(0, "ERROR: failed to resolve hostname for "
364                                  "%s/%s@%s from %016llx\n",
365                                  sname, host, realm, nid);
366                         goto out_free;
367                 }
368
369                 if (strcasecmp(host, namebuf)) {
370                         printerr(0, "ERROR: %s/%s@%s claimed hostname doesn't "
371                                  "match %s, nid %016llx\n", sname, host, realm,
372                                  namebuf, nid);
373                         goto out_free;
374                 }
375         } else {
376                 if (!strcmp(sname, GSSD_SERVICE_MDS) ||
377                     !strcmp(sname, GSSD_SERVICE_OSS)) {
378                         printerr(0, "ERROR: %s@%s from %016llx doesn't "
379                                  "bind with hostname\n", sname, realm, nid);
380                         goto out_free;
381                 }
382         }
383
384         /* 2. check realm and user */
385         switch (lustre_svc) {
386         case LUSTRE_GSS_SVC_MDS:
387                 if (strcasecmp(mds_local_realm, realm)) {
388                         cred->cr_remote = 1;
389
390                         /* only allow mapped user from remote realm */
391                         if (cred->cr_mapped_uid == -1) {
392                                 printerr(0, "ERROR: %s%s%s@%s from %016llx "
393                                          "is remote but without mapping\n",
394                                          sname, host ? "/" : "",
395                                          host ? host : "", realm, nid);
396                                 break;
397                         }
398                 } else {
399                         if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
400                                 cred->cr_uid = 0;
401                                 cred->cr_usr_root = 1;
402                         } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
403                                 cred->cr_uid = 0;
404                                 cred->cr_usr_mds = 1;
405                         } else if (!strcmp(sname, GSSD_SERVICE_OSS)) {
406                                 cred->cr_uid = 0;
407                                 cred->cr_usr_oss = 1;
408                         } else {
409                                 pw = getpwnam(sname);
410                                 if (pw != NULL) {
411                                         cred->cr_uid = pw->pw_uid;
412                                         printerr(2, "%s resolve to uid %u\n",
413                                                  sname, cred->cr_uid);
414                                 } else if (cred->cr_mapped_uid != -1) {
415                                         printerr(2, "user %s from %016llx is "
416                                                  "mapped to %u\n", sname, nid,
417                                                  cred->cr_mapped_uid);
418                                 } else {
419                                         printerr(0, "ERROR: invalid user, "
420                                                  "%s/%s@%s from %016llx\n",
421                                                  sname, host, realm, nid);
422                                         break;
423                                 }
424                         }
425                 }
426
427                 res = 0;
428                 break;
429         case LUSTRE_GSS_SVC_MGS:
430                 if (!strcmp(sname, GSSD_SERVICE_OSS)) {
431                         cred->cr_uid = 0;
432                         cred->cr_usr_oss = 1;
433                 }
434                 /* fall through */
435         case LUSTRE_GSS_SVC_OSS:
436                 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
437                         cred->cr_uid = 0;
438                         cred->cr_usr_root = 1;
439                 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
440                         cred->cr_uid = 0;
441                         cred->cr_usr_mds = 1;
442                 }
443                 if (cred->cr_uid == -1) {
444                         printerr(0, "ERROR: svc %d doesn't accept user %s "
445                                  "from %016llx\n", lustre_svc, sname, nid);
446                         break;
447                 }
448                 res = 0;
449                 break;
450         default:
451                 assert(0);
452         }
453
454 out_free:
455         if (!res)
456                 printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n",
457                          lustre_svc_name[lustre_svc], sname,
458                          host ? "/" : "", host ? host : "", realm, nid);
459         free(sname);
460         return res;
461 }
462
463 typedef struct gss_union_ctx_id_t {
464         gss_OID         mech_type;
465         gss_ctx_id_t    internal_ctx_id;
466 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
467
468 /*
469  * return -1 only if we detect error during reading from upcall channel,
470  * all other cases return 0.
471  */
472 int
473 handle_nullreq(FILE *f) {
474         uint64_t                handle_seq;
475         char                    in_tok_buf[TOKEN_BUF_SIZE];
476         char                    in_handle_buf[15];
477         char                    out_handle_buf[15];
478         gss_buffer_desc         in_tok = {.value = in_tok_buf},
479                                 out_tok = {.value = NULL},
480                                 in_handle = {.value = in_handle_buf},
481                                 out_handle = {.value = out_handle_buf},
482                                 ctx_token = {.value = NULL},
483                                 ignore_out_tok = {.value = NULL},
484         /* XXX isn't there a define for this?: */
485                                 null_token = {.value = NULL};
486         uint32_t                lustre_svc;
487         lnet_nid_t              nid;
488         u_int32_t               ret_flags;
489         gss_ctx_id_t            ctx = GSS_C_NO_CONTEXT;
490         gss_name_t              client_name;
491         gss_OID                 mech = GSS_C_NO_OID;
492         gss_cred_id_t           svc_cred;
493         u_int32_t               maj_stat = GSS_S_FAILURE, min_stat = 0;
494         u_int32_t               ignore_min_stat;
495         int                     get_len;
496         struct svc_cred         cred;
497         static char             *lbuf = NULL;
498         static int              lbuflen = 0;
499         static char             *cp;
500
501         printerr(2, "handling null request\n");
502
503         if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
504                 printerr(0, "WARNING: handle_nullreq: "
505                             "failed reading request\n");
506                 return -1;
507         }
508
509         cp = lbuf;
510
511         qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
512         qword_get(&cp, (char *) &nid, sizeof(nid));
513         qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
514         printerr(2, "handling req: svc %u, nid %016llx, idx %llx\n",
515                  lustre_svc, nid, handle_seq);
516
517         get_len = qword_get(&cp, in_handle.value, sizeof(in_handle_buf));
518         if (get_len < 0) {
519                 printerr(0, "WARNING: handle_nullreq: "
520                             "failed parsing request\n");
521                 goto out_err;
522         }
523         in_handle.length = (size_t)get_len;
524
525         printerr(3, "in_handle:\n");
526         print_hexl(3, in_handle.value, in_handle.length);
527
528         get_len = qword_get(&cp, in_tok.value, sizeof(in_tok_buf));
529         if (get_len < 0) {
530                 printerr(0, "WARNING: handle_nullreq: "
531                             "failed parsing request\n");
532                 goto out_err;
533         }
534         in_tok.length = (size_t)get_len;
535
536         printerr(3, "in_tok:\n");
537         print_hexl(3, in_tok.value, in_tok.length);
538
539         if (in_handle.length != 0) { /* CONTINUE_INIT case */
540                 if (in_handle.length != sizeof(ctx)) {
541                         printerr(0, "WARNING: handle_nullreq: "
542                                     "input handle has unexpected length %d\n",
543                                     in_handle.length);
544                         goto out_err;
545                 }
546                 /* in_handle is the context id stored in the out_handle
547                  * for the GSS_S_CONTINUE_NEEDED case below.  */
548                 memcpy(&ctx, in_handle.value, in_handle.length);
549         }
550
551         svc_cred = gssd_select_svc_cred(lustre_svc);
552         if (!svc_cred) {
553                 printerr(0, "no service credential for svc %u\n", lustre_svc);
554                 goto out_err;
555         }
556
557         maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
558                         &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
559                         &mech, &out_tok, &ret_flags, NULL, NULL);
560
561         if (maj_stat == GSS_S_CONTINUE_NEEDED) {
562                 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
563
564                 /* Save the context handle for future calls */
565                 out_handle.length = sizeof(ctx);
566                 memcpy(out_handle.value, &ctx, sizeof(ctx));
567                 goto continue_needed;
568         }
569         else if (maj_stat != GSS_S_COMPLETE) {
570                 printerr(0, "WARNING: gss_accept_sec_context failed\n");
571                 pgsserr("handle_nullreq: gss_accept_sec_context",
572                         maj_stat, min_stat, mech);
573                 goto out_err;
574         }
575
576         if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
577                 /* get_ids() prints error msg */
578                 maj_stat = GSS_S_BAD_NAME; /* XXX ? */
579                 gss_release_name(&ignore_min_stat, &client_name);
580                 goto out_err;
581         }
582         gss_release_name(&ignore_min_stat, &client_name);
583
584         /* Context complete. Pass handle_seq in out_handle to use
585          * for context lookup in the kernel. */
586         out_handle.length = sizeof(handle_seq);
587         memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
588
589         /* kernel needs ctx to calculate verifier on null response, so
590          * must give it context before doing null call: */
591         if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
592                 printerr(0, "WARNING: handle_nullreq: "
593                             "serialize_context_for_kernel failed\n");
594                 maj_stat = GSS_S_FAILURE;
595                 goto out_err;
596         }
597         /* We no longer need the gss context */
598         gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
599
600         do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
601 continue_needed:
602         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
603                         &out_handle, &out_tok);
604 out:
605         if (ctx_token.value != NULL)
606                 free(ctx_token.value);
607         if (out_tok.value != NULL)
608                 gss_release_buffer(&ignore_min_stat, &out_tok);
609         return 0;
610
611 out_err:
612         if (ctx != GSS_C_NO_CONTEXT)
613                 gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
614         send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
615                         &null_token, &null_token);
616         goto out;
617 }