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