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