Whamcloud - gitweb
5ae4213c761658662fd63c929856a08c55e12c37
[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 <stdbool.h>
54 #include <lnet/nidstr.h>
55
56 #include "svcgssd.h"
57 #include "gss_util.h"
58 #include "err_util.h"
59 #include "context.h"
60 #include "cacheio.h"
61 #include "lsupport.h"
62 #include "gss_oids.h"
63 #include "sk_utils.h"
64 #include <lustre/lustre_idl.h>
65
66 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.sptlrpc.context/channel"
67 #define SVCGSSD_INIT_CHANNEL    "/proc/net/rpc/auth.sptlrpc.init/channel"
68
69 #define TOKEN_BUF_SIZE          8192
70
71 struct svc_cred {
72         uint32_t cr_remote;
73         uint32_t cr_usr_root;
74         uint32_t cr_usr_mds;
75         uint32_t cr_usr_oss;
76         uid_t    cr_uid;
77         uid_t    cr_mapped_uid;
78         uid_t    cr_gid;
79 };
80
81 struct svc_nego_data {
82         /* kernel data*/
83         uint32_t        lustre_svc;
84         lnet_nid_t      nid;
85         uint64_t        handle_seq;
86         char            nm_name[LUSTRE_NODEMAP_NAME_LENGTH + 1];
87         gss_buffer_desc in_tok;
88         gss_buffer_desc out_tok;
89         gss_buffer_desc in_handle;
90         gss_buffer_desc out_handle;
91         uint32_t        maj_stat;
92         uint32_t        min_stat;
93
94         /* userspace data */
95         gss_OID                 mech;
96         gss_ctx_id_t            ctx;
97         gss_buffer_desc         ctx_token;
98 };
99
100 static int
101 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
102                 gss_OID mechoid, gss_buffer_desc *context_token)
103 {
104         FILE *f;
105         const char *mechname;
106         int err;
107
108         printerr(2, "doing downcall\n");
109         mechname = gss_OID_mech_name(mechoid);
110         if (mechname == NULL)
111                 goto out_err;
112         f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
113         if (f == NULL) {
114                 printerr(0, "WARNING: unable to open downcall channel "
115                              "%s: %s\n",
116                              SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
117                 goto out_err;
118         }
119         qword_printhex(f, out_handle->value, out_handle->length);
120         /* XXX are types OK for the rest of this? */
121         qword_printint(f, 3600); /* an hour should be sufficient */
122         qword_printint(f, cred->cr_remote);
123         qword_printint(f, cred->cr_usr_root);
124         qword_printint(f, cred->cr_usr_mds);
125         qword_printint(f, cred->cr_usr_oss);
126         qword_printint(f, cred->cr_mapped_uid);
127         qword_printint(f, cred->cr_uid);
128         qword_printint(f, cred->cr_gid);
129         qword_print(f, mechname);
130         qword_printhex(f, context_token->value, context_token->length);
131         err = qword_eol(f);
132         fclose(f);
133         return err;
134 out_err:
135         printerr(0, "WARNING: downcall failed\n");
136         return -1;
137 }
138
139 struct gss_verifier {
140         u_int32_t       flav;
141         gss_buffer_desc body;
142 };
143
144 #define RPCSEC_GSS_SEQ_WIN      5
145
146 static int
147 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
148               u_int32_t maj_stat, u_int32_t min_stat,
149               gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
150 {
151         char buf[2 * TOKEN_BUF_SIZE];
152         char *bp = buf;
153         int blen = sizeof(buf);
154         /* XXXARG: */
155         int g;
156
157         printerr(2, "sending reply\n");
158         qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
159         qword_addhex(&bp, &blen, in_token->value, in_token->length);
160         qword_addint(&bp, &blen, 3600); /* an hour should be sufficient */
161         qword_adduint(&bp, &blen, maj_stat);
162         qword_adduint(&bp, &blen, min_stat);
163         qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
164         qword_addhex(&bp, &blen, out_token->value, out_token->length);
165         qword_addeol(&bp, &blen);
166         if (blen <= 0) {
167                 printerr(0, "WARNING: send_response: message too long\n");
168                 return -1;
169         }
170         g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
171         if (g == -1) {
172                 printerr(0, "WARNING: open %s failed: %s\n",
173                                 SVCGSSD_INIT_CHANNEL, strerror(errno));
174                 return -1;
175         }
176         *bp = '\0';
177         printerr(3, "writing message: %s", buf);
178         if (write(g, buf, bp - buf) == -1) {
179                 printerr(0, "WARNING: failed to write message\n");
180                 close(g);
181                 return -1;
182         }
183         close(g);
184         return 0;
185 }
186
187 #define rpc_auth_ok                     0
188 #define rpc_autherr_badcred             1
189 #define rpc_autherr_rejectedcred        2
190 #define rpc_autherr_badverf             3
191 #define rpc_autherr_rejectedverf        4
192 #define rpc_autherr_tooweak             5
193 #define rpcsec_gsserr_credproblem       13
194 #define rpcsec_gsserr_ctxproblem        14
195
196 static int
197 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
198         lnet_nid_t nid, uint32_t lustre_svc)
199 {
200         u_int32_t       maj_stat, min_stat;
201         gss_buffer_desc name;
202         char            *sname, *host, *realm;
203         const int       namebuf_size = 512;
204         char            namebuf[namebuf_size];
205         int             res = -1;
206         gss_OID         name_type = GSS_C_NO_OID;
207         struct passwd   *pw;
208
209         cred->cr_remote = 0;
210         cred->cr_usr_root = cred->cr_usr_mds = cred->cr_usr_oss = 0;
211         cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
212
213         maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
214         if (maj_stat != GSS_S_COMPLETE) {
215                 pgsserr("get_ids: gss_display_name",
216                         maj_stat, min_stat, mech);
217                 return -1;
218         }
219         if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
220             !(sname = calloc(name.length + 1, 1))) {
221                 printerr(0, "WARNING: get_ids: error allocating %zu bytes "
222                         "for sname\n", name.length + 1);
223                 gss_release_buffer(&min_stat, &name);
224                 return -1;
225         }
226         memcpy(sname, name.value, name.length);
227         sname[name.length] = '\0';
228         gss_release_buffer(&min_stat, &name);
229
230         if (lustre_svc == LUSTRE_GSS_SVC_MDS)
231                 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
232         else
233                 cred->cr_mapped_uid = -1;
234
235         realm = strchr(sname, '@');
236         if (realm) {
237                 *realm++ = '\0';
238         } else {
239                 printerr(0, "ERROR: %s has no realm name\n", sname);
240                 goto out_free;
241         }
242
243         host = strchr(sname, '/');
244         if (host)
245                 *host++ = '\0';
246
247         if (strcmp(sname, GSSD_SERVICE_MGS) == 0) {
248                 printerr(0, "forbid %s as a user name\n", sname);
249                 goto out_free;
250         }
251
252         /* 1. check host part */
253         if (host) {
254                 if (lnet_nid2hostname(nid, namebuf, namebuf_size)) {
255                         printerr(0, "ERROR: failed to resolve hostname for "
256                                  "%s/%s@%s from %016llx\n",
257                                  sname, host, realm, nid);
258                         goto out_free;
259                 }
260
261                 if (strcasecmp(host, namebuf)) {
262                         printerr(0, "ERROR: %s/%s@%s claimed hostname doesn't "
263                                  "match %s, nid %016llx\n", sname, host, realm,
264                                  namebuf, nid);
265                         goto out_free;
266                 }
267         } else {
268                 if (!strcmp(sname, GSSD_SERVICE_MDS) ||
269                     !strcmp(sname, GSSD_SERVICE_OSS)) {
270                         printerr(0, "ERROR: %s@%s from %016llx doesn't "
271                                  "bind with hostname\n", sname, realm, nid);
272                         goto out_free;
273                 }
274         }
275
276         /* 2. check realm and user */
277         switch (lustre_svc) {
278         case LUSTRE_GSS_SVC_MDS:
279                 if (strcasecmp(mds_local_realm, realm)) {
280                         cred->cr_remote = 1;
281
282                         /* only allow mapped user from remote realm */
283                         if (cred->cr_mapped_uid == -1) {
284                                 printerr(0, "ERROR: %s%s%s@%s from %016llx "
285                                          "is remote but without mapping\n",
286                                          sname, host ? "/" : "",
287                                          host ? host : "", realm, nid);
288                                 break;
289                         }
290                 } else {
291                         if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
292                                 cred->cr_uid = 0;
293                                 cred->cr_usr_root = 1;
294                         } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
295                                 cred->cr_uid = 0;
296                                 cred->cr_usr_mds = 1;
297                         } else if (!strcmp(sname, GSSD_SERVICE_OSS)) {
298                                 cred->cr_uid = 0;
299                                 cred->cr_usr_oss = 1;
300                         } else {
301                                 pw = getpwnam(sname);
302                                 if (pw != NULL) {
303                                         cred->cr_uid = pw->pw_uid;
304                                         printerr(2, "%s resolve to uid %u\n",
305                                                  sname, cred->cr_uid);
306                                 } else if (cred->cr_mapped_uid != -1) {
307                                         printerr(2, "user %s from %016llx is "
308                                                  "mapped to %u\n", sname, nid,
309                                                  cred->cr_mapped_uid);
310                                 } else {
311                                         printerr(0, "ERROR: invalid user, "
312                                                  "%s/%s@%s from %016llx\n",
313                                                  sname, host, realm, nid);
314                                         break;
315                                 }
316                         }
317                 }
318
319                 res = 0;
320                 break;
321         case LUSTRE_GSS_SVC_MGS:
322                 if (!strcmp(sname, GSSD_SERVICE_OSS)) {
323                         cred->cr_uid = 0;
324                         cred->cr_usr_oss = 1;
325                 }
326                 /* fall through */
327         case LUSTRE_GSS_SVC_OSS:
328                 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
329                         cred->cr_uid = 0;
330                         cred->cr_usr_root = 1;
331                 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
332                         cred->cr_uid = 0;
333                         cred->cr_usr_mds = 1;
334                 }
335                 if (cred->cr_uid == -1) {
336                         printerr(0, "ERROR: svc %d doesn't accept user %s "
337                                  "from %016llx\n", lustre_svc, sname, nid);
338                         break;
339                 }
340                 res = 0;
341                 break;
342         default:
343                 assert(0);
344         }
345
346 out_free:
347         if (!res)
348                 printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n",
349                          lustre_svc_name[lustre_svc], sname,
350                          host ? "/" : "", host ? host : "", realm, nid);
351         free(sname);
352         return res;
353 }
354
355 typedef struct gss_union_ctx_id_t {
356         gss_OID         mech_type;
357         gss_ctx_id_t    internal_ctx_id;
358 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
359
360 int handle_sk(struct svc_nego_data *snd)
361 {
362 #ifdef HAVE_OPENSSL_SSK
363         struct sk_cred *skc = NULL;
364         struct svc_cred cred;
365         gss_buffer_desc bufs[SK_INIT_BUFFERS];
366         gss_buffer_desc remote_pub_key = GSS_C_EMPTY_BUFFER;
367         char *target;
368         uint32_t rc = GSS_S_DEFECTIVE_TOKEN;
369         uint32_t version;
370         uint32_t flags;
371         int i;
372
373         printerr(3, "Handling sk request\n");
374         memset(bufs, 0, sizeof(gss_buffer_desc) * SK_INIT_BUFFERS);
375
376         /* See lgss_sk_using_cred() for client side token formation.
377          * Decoding initiator buffers */
378         i = sk_decode_netstring(bufs, SK_INIT_BUFFERS, &snd->in_tok);
379         if (i < SK_INIT_BUFFERS) {
380                 printerr(0, "Invalid netstring token received from peer\n");
381                 goto cleanup_buffers;
382         }
383
384         /* Allowing for a larger length first buffer in the future */
385         if (bufs[SK_INIT_VERSION].length < sizeof(version)) {
386                 printerr(0, "Invalid version received (wrong size)\n");
387                 goto cleanup_buffers;
388         }
389         memcpy(&version, bufs[SK_INIT_VERSION].value, sizeof(version));
390         version = be32toh(version);
391         if (version != SK_MSG_VERSION) {
392                 printerr(0, "Invalid version received: %d\n", version);
393                 goto cleanup_buffers;
394         }
395
396         rc = GSS_S_FAILURE;
397
398         /* target must be a null terminated string */
399         i = bufs[SK_INIT_TARGET].length - 1;
400         target = bufs[SK_INIT_TARGET].value;
401         if (i >= 0 && target[i] != '\0') {
402                 printerr(0, "Invalid target from netstring\n");
403                 goto cleanup_buffers;
404         }
405
406         if (bufs[SK_INIT_FLAGS].length != sizeof(flags)) {
407                 printerr(0, "Invalid flags from netstring\n");
408                 goto cleanup_buffers;
409         }
410         memcpy(&flags, bufs[SK_INIT_FLAGS].value, sizeof(flags));
411
412         skc = sk_create_cred(target, snd->nm_name, be32toh(flags));
413         if (!skc) {
414                 printerr(0, "Failed to create sk credentials\n");
415                 goto cleanup_buffers;
416         }
417
418         /* Verify that the peer has used a prime size greater or equal to
419          * the size specified in the key file which may contain only zero
420          * fill but the size specifies the mimimum supported size on
421          * servers */
422         if (skc->sc_flags & LGSS_SVC_PRIV &&
423             bufs[SK_INIT_P].length < skc->sc_p.length) {
424                 printerr(0, "Peer DHKE prime does not meet the size required "
425                          "by keyfile: %zd bits\n", skc->sc_p.length * 8);
426                 goto cleanup_buffers;
427         }
428
429         /* Throw out the p from the server and use the wire data */
430         free(skc->sc_p.value);
431         skc->sc_p.value = NULL;
432         skc->sc_p.length = 0;
433
434         /* Take control of all the allocated buffers from decoding */
435         if (bufs[SK_INIT_RANDOM].length !=
436             sizeof(skc->sc_kctx.skc_peer_random)) {
437                 printerr(0, "Invalid size for client random\n");
438                 goto cleanup_buffers;
439         }
440
441         memcpy(&skc->sc_kctx.skc_peer_random, bufs[SK_INIT_RANDOM].value,
442                sizeof(skc->sc_kctx.skc_peer_random));
443         skc->sc_p = bufs[SK_INIT_P];
444         remote_pub_key = bufs[SK_INIT_PUB_KEY];
445         skc->sc_nodemap_hash = bufs[SK_INIT_NODEMAP];
446         skc->sc_hmac = bufs[SK_INIT_HMAC];
447
448         /* Verify HMAC from peer.  Ideally this would happen before anything
449          * else but we don't have enough information to lookup key without the
450          * token (fsname and cluster_hash) so it's done after. */
451         rc = sk_verify_hmac(skc, bufs, SK_INIT_BUFFERS - 1, EVP_sha256(),
452                             &skc->sc_hmac);
453         if (rc != GSS_S_COMPLETE) {
454                 printerr(0, "HMAC verification error: 0x%x from peer %s\n",
455                          rc, libcfs_nid2str((lnet_nid_t)snd->nid));
456                 goto cleanup_partial;
457         }
458
459         /* Check that the cluster hash matches the hash of nodemap name */
460         rc = sk_verify_hash(snd->nm_name, EVP_sha256(), &skc->sc_nodemap_hash);
461         if (rc != GSS_S_COMPLETE) {
462                 printerr(0, "Cluster hash failed validation: 0x%x\n", rc);
463                 goto cleanup_partial;
464         }
465
466         rc = sk_gen_params(skc);
467         if (rc != GSS_S_COMPLETE) {
468                 printerr(0, "Failed to generate DH params for responder\n");
469                 goto cleanup_partial;
470         }
471         if (sk_compute_dh_key(skc, &remote_pub_key)) {
472                 printerr(0, "Failed to compute session key from DH params\n");
473                 goto cleanup_partial;
474         }
475
476         /* Cleanup init buffers we have copied or don't need anymore */
477         free(bufs[SK_INIT_VERSION].value);
478         free(bufs[SK_INIT_RANDOM].value);
479         free(bufs[SK_INIT_TARGET].value);
480         free(bufs[SK_INIT_FLAGS].value);
481
482         /* Server reply contains the servers public key, random,  and HMAC */
483         version = htobe32(SK_MSG_VERSION);
484         bufs[SK_RESP_VERSION].value = &version;
485         bufs[SK_RESP_VERSION].length = sizeof(version);
486         bufs[SK_RESP_RANDOM].value = &skc->sc_kctx.skc_host_random;
487         bufs[SK_RESP_RANDOM].length = sizeof(skc->sc_kctx.skc_host_random);
488         bufs[SK_RESP_PUB_KEY] = skc->sc_pub_key;
489         if (sk_sign_bufs(&skc->sc_kctx.skc_shared_key, bufs,
490                          SK_RESP_BUFFERS - 1, EVP_sha256(),
491                          &skc->sc_hmac)) {
492                 printerr(0, "Failed to sign parameters\n");
493                 goto out_err;
494         }
495         bufs[SK_RESP_HMAC] = skc->sc_hmac;
496         if (sk_encode_netstring(bufs, SK_RESP_BUFFERS, &snd->out_tok)) {
497                 printerr(0, "Failed to encode netstring for token\n");
498                 goto out_err;
499         }
500         printerr(2, "Created netstring of %zd bytes\n", snd->out_tok.length);
501
502         if (sk_session_kdf(skc, snd->nid, &snd->in_tok, &snd->out_tok)) {
503                 printerr(0, "Failed to calulate derviced session key\n");
504                 goto out_err;
505         }
506         if (sk_compute_keys(skc)) {
507                 printerr(0, "Failed to compute HMAC and encryption keys\n");
508                 goto out_err;
509         }
510         if (sk_serialize_kctx(skc, &snd->ctx_token)) {
511                 printerr(0, "Failed to serialize context for kernel\n");
512                 goto out_err;
513         }
514
515         snd->out_handle.length = sizeof(snd->handle_seq);
516         memcpy(snd->out_handle.value, &snd->handle_seq,
517                sizeof(snd->handle_seq));
518         snd->maj_stat = GSS_S_COMPLETE;
519
520         /* fix credentials */
521         memset(&cred, 0, sizeof(cred));
522         cred.cr_mapped_uid = -1;
523
524         if (skc->sc_flags & LGSS_ROOT_CRED_ROOT)
525                 cred.cr_usr_root = 1;
526         if (skc->sc_flags & LGSS_ROOT_CRED_MDT)
527                 cred.cr_usr_mds = 1;
528         if (skc->sc_flags & LGSS_ROOT_CRED_OST)
529                 cred.cr_usr_oss = 1;
530
531         do_svc_downcall(&snd->out_handle, &cred, snd->mech, &snd->ctx_token);
532
533         /* cleanup ctx_token, out_tok is cleaned up in handle_channel_req */
534         free(remote_pub_key.value);
535         free(snd->ctx_token.value);
536         snd->ctx_token.length = 0;
537
538         printerr(3, "sk returning success\n");
539         return 0;
540
541 cleanup_buffers:
542         for (i = 0; i < SK_INIT_BUFFERS; i++)
543                 free(bufs[i].value);
544         sk_free_cred(skc);
545         snd->maj_stat = rc;
546         return -1;
547
548 cleanup_partial:
549         free(bufs[SK_INIT_VERSION].value);
550         free(bufs[SK_INIT_RANDOM].value);
551         free(bufs[SK_INIT_TARGET].value);
552         free(bufs[SK_INIT_FLAGS].value);
553         free(remote_pub_key.value);
554         sk_free_cred(skc);
555         snd->maj_stat = rc;
556         return -1;
557
558 out_err:
559         snd->maj_stat = rc;
560         if (snd->ctx_token.value) {
561                 free(snd->ctx_token.value);
562                 snd->ctx_token.value = 0;
563                 snd->ctx_token.length = 0;
564         }
565         free(remote_pub_key.value);
566         sk_free_cred(skc);
567         printerr(3, "sk returning failure\n");
568 #else /* !HAVE_OPENSSL_SSK */
569         printerr(0, "ERROR: shared key subflavour is not enabled\n");
570 #endif /* HAVE_OPENSSL_SSK */
571         return -1;
572 }
573
574 int handle_null(struct svc_nego_data *snd)
575 {
576         struct svc_cred cred;
577         uint64_t tmp;
578         uint32_t flags;
579
580         /* null just uses the same token as the return token and for
581          * for sending to the kernel.  It is a single uint64_t. */
582         if (snd->in_tok.length != sizeof(uint64_t)) {
583                 snd->maj_stat = GSS_S_DEFECTIVE_TOKEN;
584                 printerr(0, "Invalid token size (%zd) received\n",
585                          snd->in_tok.length);
586                 return -1;
587         }
588         snd->out_tok.length = snd->in_tok.length;
589         snd->out_tok.value = malloc(snd->out_tok.length);
590         if (!snd->out_tok.value) {
591                 snd->maj_stat = GSS_S_FAILURE;
592                 printerr(0, "Failed to allocate out_tok\n");
593                 return -1;
594         }
595
596         snd->ctx_token.length = snd->in_tok.length;
597         snd->ctx_token.value = malloc(snd->ctx_token.length);
598         if (!snd->ctx_token.value) {
599                 snd->maj_stat = GSS_S_FAILURE;
600                 printerr(0, "Failed to allocate ctx_token\n");
601                 return -1;
602         }
603
604         snd->out_handle.length = sizeof(snd->handle_seq);
605         memcpy(snd->out_handle.value, &snd->handle_seq,
606                sizeof(snd->handle_seq));
607         snd->maj_stat = GSS_S_COMPLETE;
608
609         memcpy(&tmp, snd->in_tok.value, sizeof(tmp));
610         tmp = be64toh(tmp);
611         flags = (uint32_t)(tmp & 0x00000000ffffffff);
612         memset(&cred, 0, sizeof(cred));
613         cred.cr_mapped_uid = -1;
614
615         if (flags & LGSS_ROOT_CRED_ROOT)
616                 cred.cr_usr_root = 1;
617         if (flags & LGSS_ROOT_CRED_MDT)
618                 cred.cr_usr_mds = 1;
619         if (flags & LGSS_ROOT_CRED_OST)
620                 cred.cr_usr_oss = 1;
621
622         do_svc_downcall(&snd->out_handle, &cred, snd->mech, &snd->ctx_token);
623
624         /* cleanup ctx_token, out_tok is cleaned up in handle_channel_req */
625         free(snd->ctx_token.value);
626         snd->ctx_token.length = 0;
627
628         return 0;
629 }
630
631 static int handle_krb(struct svc_nego_data *snd)
632 {
633         u_int32_t               ret_flags;
634         gss_name_t              client_name;
635         gss_buffer_desc         ignore_out_tok = {.value = NULL};
636         gss_OID                 mech = GSS_C_NO_OID;
637         gss_cred_id_t           svc_cred;
638         u_int32_t               ignore_min_stat;
639         struct svc_cred         cred;
640
641         svc_cred = gssd_select_svc_cred(snd->lustre_svc);
642         if (!svc_cred) {
643                 printerr(0, "no service credential for svc %u\n",
644                          snd->lustre_svc);
645                 goto out_err;
646         }
647
648         snd->maj_stat = gss_accept_sec_context(&snd->min_stat, &snd->ctx,
649                                                svc_cred, &snd->in_tok,
650                                                GSS_C_NO_CHANNEL_BINDINGS,
651                                                &client_name, &mech,
652                                                &snd->out_tok, &ret_flags, NULL,
653                                                NULL);
654
655         if (snd->maj_stat == GSS_S_CONTINUE_NEEDED) {
656                 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
657
658                 /* Save the context handle for future calls */
659                 snd->out_handle.length = sizeof(snd->ctx);
660                 memcpy(snd->out_handle.value, &snd->ctx, sizeof(snd->ctx));
661                 return 0;
662         } else if (snd->maj_stat != GSS_S_COMPLETE) {
663                 printerr(0, "WARNING: gss_accept_sec_context failed\n");
664                 pgsserr("handle_krb: gss_accept_sec_context",
665                         snd->maj_stat, snd->min_stat, mech);
666                 goto out_err;
667         }
668
669         if (get_ids(client_name, mech, &cred, snd->nid, snd->lustre_svc)) {
670                 /* get_ids() prints error msg */
671                 snd->maj_stat = GSS_S_BAD_NAME; /* XXX ? */
672                 gss_release_name(&ignore_min_stat, &client_name);
673                 goto out_err;
674         }
675         gss_release_name(&ignore_min_stat, &client_name);
676
677         /* Context complete. Pass handle_seq in out_handle to use
678          * for context lookup in the kernel. */
679         snd->out_handle.length = sizeof(snd->handle_seq);
680         memcpy(snd->out_handle.value, &snd->handle_seq,
681                sizeof(snd->handle_seq));
682
683         /* kernel needs ctx to calculate verifier on null response, so
684          * must give it context before doing null call: */
685         if (serialize_context_for_kernel(snd->ctx, &snd->ctx_token, mech)) {
686                 printerr(0, "WARNING: handle_krb: "
687                          "serialize_context_for_kernel failed\n");
688                 snd->maj_stat = GSS_S_FAILURE;
689                 goto out_err;
690         }
691         /* We no longer need the gss context */
692         gss_delete_sec_context(&ignore_min_stat, &snd->ctx, &ignore_out_tok);
693         do_svc_downcall(&snd->out_handle, &cred, mech, &snd->ctx_token);
694
695         return 0;
696
697 out_err:
698         if (snd->ctx != GSS_C_NO_CONTEXT)
699                 gss_delete_sec_context(&ignore_min_stat, &snd->ctx,
700                                        &ignore_out_tok);
701
702         return 1;
703 }
704
705 /*
706  * return -1 only if we detect error during reading from upcall channel,
707  * all other cases return 0.
708  */
709 int handle_channel_request(FILE *f)
710 {
711         char                    in_tok_buf[TOKEN_BUF_SIZE];
712         char                    in_handle_buf[15];
713         char                    out_handle_buf[15];
714         gss_buffer_desc         ctx_token      = {.value = NULL},
715                                 null_token     = {.value = NULL};
716         uint32_t                lustre_mech;
717         static char             *lbuf;
718         static int              lbuflen;
719         static char             *cp;
720         int                     get_len;
721         int                     rc = 1;
722         u_int32_t               ignore_min_stat;
723         struct svc_nego_data    snd = {
724                 .in_tok.value           = in_tok_buf,
725                 .in_handle.value        = in_handle_buf,
726                 .out_handle.value       = out_handle_buf,
727                 .maj_stat               = GSS_S_FAILURE,
728                 .ctx                    = GSS_C_NO_CONTEXT,
729         };
730
731         printerr(2, "handling request\n");
732         if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
733                 printerr(0, "WARNING: failed reading request\n");
734                 return -1;
735         }
736
737         cp = lbuf;
738
739         /* see rsi_request() for the format of data being input here */
740         qword_get(&cp, (char *)&snd.lustre_svc, sizeof(snd.lustre_svc));
741
742         /* lustre_svc is the svc and gss subflavor */
743         lustre_mech = (snd.lustre_svc & LUSTRE_GSS_MECH_MASK) >>
744                       LUSTRE_GSS_MECH_SHIFT;
745         snd.lustre_svc = snd.lustre_svc & LUSTRE_GSS_SVC_MASK;
746         switch (lustre_mech) {
747         case LGSS_MECH_KRB5:
748                 if (!krb_enabled) {
749                         printerr(1, "WARNING: Request for kerberos but service "
750                                  "support not enabled\n");
751                         goto ignore;
752                 }
753                 snd.mech = &krb5oid;
754                 break;
755         case LGSS_MECH_NULL:
756                 if (!null_enabled) {
757                         printerr(1, "WARNING: Request for gssnull but service "
758                                  "support not enabled\n");
759                         goto ignore;
760                 }
761                 snd.mech = &nulloid;
762                 break;
763         case LGSS_MECH_SK:
764 #ifdef HAVE_OPENSSL_SSK
765                 if (!sk_enabled) {
766                         printerr(1, "WARNING: Request for sk but service "
767                                  "support not enabled\n");
768                         goto ignore;
769                 }
770                 snd.mech = &skoid;
771 #else
772                 printerr(1, "ERROR: Request for sk but service "
773                          "support not enabled\n");
774 #endif
775                 break;
776         default:
777                 printerr(0, "WARNING: invalid mechanism recevied: %d\n",
778                          lustre_mech);
779                 goto out_err;
780                 break;
781         }
782
783         qword_get(&cp, (char *)&snd.nid, sizeof(snd.nid));
784         qword_get(&cp, (char *)&snd.handle_seq, sizeof(snd.handle_seq));
785         qword_get(&cp, snd.nm_name, sizeof(snd.nm_name));
786         printerr(2, "handling req: svc %u, nid %016llx, idx %"PRIx64" nodemap "
787                  "%s\n", snd.lustre_svc, snd.nid, snd.handle_seq, snd.nm_name);
788
789         get_len = qword_get(&cp, snd.in_handle.value, sizeof(in_handle_buf));
790         if (get_len < 0) {
791                 printerr(0, "WARNING: failed parsing request\n");
792                 goto out_err;
793         }
794         snd.in_handle.length = (size_t)get_len;
795
796         printerr(3, "in_handle:\n");
797         print_hexl(3, snd.in_handle.value, snd.in_handle.length);
798
799         get_len = qword_get(&cp, snd.in_tok.value, sizeof(in_tok_buf));
800         if (get_len < 0) {
801                 printerr(0, "WARNING: failed parsing request\n");
802                 goto out_err;
803         }
804         snd.in_tok.length = (size_t)get_len;
805
806         printerr(3, "in_tok:\n");
807         print_hexl(3, snd.in_tok.value, snd.in_tok.length);
808
809         if (snd.in_handle.length != 0) { /* CONTINUE_INIT case */
810                 if (snd.in_handle.length != sizeof(snd.ctx)) {
811                         printerr(0, "WARNING: input handle has unexpected "
812                                  "length %zu\n", snd.in_handle.length);
813                         goto out_err;
814                 }
815                 /* in_handle is the context id stored in the out_handle
816                  * for the GSS_S_CONTINUE_NEEDED case below.  */
817                 memcpy(&snd.ctx, snd.in_handle.value, snd.in_handle.length);
818         }
819
820         if (lustre_mech == LGSS_MECH_KRB5)
821                 rc = handle_krb(&snd);
822         else if (lustre_mech == LGSS_MECH_SK)
823                 rc = handle_sk(&snd);
824         else if (lustre_mech == LGSS_MECH_NULL)
825                 rc = handle_null(&snd);
826         else
827                 printerr(0, "WARNING: Received or request for"
828                          "subflavor that is not enabled: %d\n", lustre_mech);
829
830 out_err:
831         /* Failures send a null token */
832         if (rc == 0)
833                 send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat,
834                               snd.min_stat, &snd.out_handle, &snd.out_tok);
835         else
836                 send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat,
837                               snd.min_stat, &null_token, &null_token);
838
839         /* cleanup buffers */
840         if (snd.ctx_token.value != NULL)
841                 free(ctx_token.value);
842         if (snd.out_tok.value != NULL)
843                 gss_release_buffer(&ignore_min_stat, &snd.out_tok);
844
845         /* For junk wire data just ignore */
846 ignore:
847         return 0;
848 }