1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * GSS Proxy upcall module
4 *
5 *  Copyright (C) 2012 Simo Sorce <simo@redhat.com>
6 */
7
8#include <linux/sunrpc/svcauth.h>
9#include "gss_rpc_xdr.h"
10
11static int gssx_enc_bool(struct xdr_stream *xdr, int v)
12{
13	__be32 *p;
14
15	p = xdr_reserve_space(xdr, 4);
16	if (unlikely(p == NULL))
17		return -ENOSPC;
18	*p = v ? xdr_one : xdr_zero;
19	return 0;
20}
21
22static int gssx_dec_bool(struct xdr_stream *xdr, u32 *v)
23{
24	__be32 *p;
25
26	p = xdr_inline_decode(xdr, 4);
27	if (unlikely(p == NULL))
28		return -ENOSPC;
29	*v = be32_to_cpu(*p);
30	return 0;
31}
32
33static int gssx_enc_buffer(struct xdr_stream *xdr,
34			   const gssx_buffer *buf)
35{
36	__be32 *p;
37
38	p = xdr_reserve_space(xdr, sizeof(u32) + buf->len);
39	if (!p)
40		return -ENOSPC;
41	xdr_encode_opaque(p, buf->data, buf->len);
42	return 0;
43}
44
45static int gssx_enc_in_token(struct xdr_stream *xdr,
46			     const struct gssp_in_token *in)
47{
48	__be32 *p;
49
50	p = xdr_reserve_space(xdr, 4);
51	if (!p)
52		return -ENOSPC;
53	*p = cpu_to_be32(in->page_len);
54
55	/* all we need to do is to write pages */
56	xdr_write_pages(xdr, in->pages, in->page_base, in->page_len);
57
58	return 0;
59}
60
61
62static int gssx_dec_buffer(struct xdr_stream *xdr,
63			   gssx_buffer *buf)
64{
65	u32 length;
66	__be32 *p;
67
68	p = xdr_inline_decode(xdr, 4);
69	if (unlikely(p == NULL))
70		return -ENOSPC;
71
72	length = be32_to_cpup(p);
73	p = xdr_inline_decode(xdr, length);
74	if (unlikely(p == NULL))
75		return -ENOSPC;
76
77	if (buf->len == 0) {
78		/* we intentionally are not interested in this buffer */
79		return 0;
80	}
81	if (length > buf->len)
82		return -ENOSPC;
83
84	if (!buf->data) {
85		buf->data = kmemdup(p, length, GFP_KERNEL);
86		if (!buf->data)
87			return -ENOMEM;
88	} else {
89		memcpy(buf->data, p, length);
90	}
91	buf->len = length;
92	return 0;
93}
94
95static int gssx_enc_option(struct xdr_stream *xdr,
96			   struct gssx_option *opt)
97{
98	int err;
99
100	err = gssx_enc_buffer(xdr, &opt->option);
101	if (err)
102		return err;
103	err = gssx_enc_buffer(xdr, &opt->value);
104	return err;
105}
106
107static int gssx_dec_option(struct xdr_stream *xdr,
108			   struct gssx_option *opt)
109{
110	int err;
111
112	err = gssx_dec_buffer(xdr, &opt->option);
113	if (err)
114		return err;
115	err = gssx_dec_buffer(xdr, &opt->value);
116	return err;
117}
118
119static int dummy_enc_opt_array(struct xdr_stream *xdr,
120				const struct gssx_option_array *oa)
121{
122	__be32 *p;
123
124	if (oa->count != 0)
125		return -EINVAL;
126
127	p = xdr_reserve_space(xdr, 4);
128	if (!p)
129		return -ENOSPC;
130	*p = 0;
131
132	return 0;
133}
134
135static int dummy_dec_opt_array(struct xdr_stream *xdr,
136				struct gssx_option_array *oa)
137{
138	struct gssx_option dummy;
139	u32 count, i;
140	__be32 *p;
141
142	p = xdr_inline_decode(xdr, 4);
143	if (unlikely(p == NULL))
144		return -ENOSPC;
145	count = be32_to_cpup(p++);
146	memset(&dummy, 0, sizeof(dummy));
147	for (i = 0; i < count; i++) {
148		gssx_dec_option(xdr, &dummy);
149	}
150
151	oa->count = 0;
152	oa->data = NULL;
153	return 0;
154}
155
156static int get_host_u32(struct xdr_stream *xdr, u32 *res)
157{
158	__be32 *p;
159
160	p = xdr_inline_decode(xdr, 4);
161	if (!p)
162		return -EINVAL;
163	/* Contents of linux creds are all host-endian: */
164	memcpy(res, p, sizeof(u32));
165	return 0;
166}
167
168static int gssx_dec_linux_creds(struct xdr_stream *xdr,
169				struct svc_cred *creds)
170{
171	u32 length;
172	__be32 *p;
173	u32 tmp;
174	u32 N;
175	int i, err;
176
177	p = xdr_inline_decode(xdr, 4);
178	if (unlikely(p == NULL))
179		return -ENOSPC;
180
181	length = be32_to_cpup(p);
182
183	if (length > (3 + NGROUPS_MAX) * sizeof(u32))
184		return -ENOSPC;
185
186	/* uid */
187	err = get_host_u32(xdr, &tmp);
188	if (err)
189		return err;
190	creds->cr_uid = make_kuid(&init_user_ns, tmp);
191
192	/* gid */
193	err = get_host_u32(xdr, &tmp);
194	if (err)
195		return err;
196	creds->cr_gid = make_kgid(&init_user_ns, tmp);
197
198	/* number of additional gid's */
199	err = get_host_u32(xdr, &tmp);
200	if (err)
201		return err;
202	N = tmp;
203	if ((3 + N) * sizeof(u32) != length)
204		return -EINVAL;
205	creds->cr_group_info = groups_alloc(N);
206	if (creds->cr_group_info == NULL)
207		return -ENOMEM;
208
209	/* gid's */
210	for (i = 0; i < N; i++) {
211		kgid_t kgid;
212		err = get_host_u32(xdr, &tmp);
213		if (err)
214			goto out_free_groups;
215		err = -EINVAL;
216		kgid = make_kgid(&init_user_ns, tmp);
217		if (!gid_valid(kgid))
218			goto out_free_groups;
219		creds->cr_group_info->gid[i] = kgid;
220	}
221	groups_sort(creds->cr_group_info);
222
223	return 0;
224out_free_groups:
225	groups_free(creds->cr_group_info);
226	return err;
227}
228
229static int gssx_dec_option_array(struct xdr_stream *xdr,
230				 struct gssx_option_array *oa)
231{
232	struct svc_cred *creds;
233	u32 count, i;
234	__be32 *p;
235	int err;
236
237	p = xdr_inline_decode(xdr, 4);
238	if (unlikely(p == NULL))
239		return -ENOSPC;
240	count = be32_to_cpup(p++);
241	if (!count)
242		return 0;
243
244	/* we recognize only 1 currently: CREDS_VALUE */
245	oa->count = 1;
246
247	oa->data = kmalloc(sizeof(struct gssx_option), GFP_KERNEL);
248	if (!oa->data)
249		return -ENOMEM;
250
251	creds = kzalloc(sizeof(struct svc_cred), GFP_KERNEL);
252	if (!creds) {
253		err = -ENOMEM;
254		goto free_oa;
255	}
256
257	oa->data[0].option.data = CREDS_VALUE;
258	oa->data[0].option.len = sizeof(CREDS_VALUE);
259	oa->data[0].value.data = (void *)creds;
260	oa->data[0].value.len = 0;
261
262	for (i = 0; i < count; i++) {
263		gssx_buffer dummy = { 0, NULL };
264		u32 length;
265
266		/* option buffer */
267		p = xdr_inline_decode(xdr, 4);
268		if (unlikely(p == NULL)) {
269			err = -ENOSPC;
270			goto free_creds;
271		}
272
273		length = be32_to_cpup(p);
274		p = xdr_inline_decode(xdr, length);
275		if (unlikely(p == NULL)) {
276			err = -ENOSPC;
277			goto free_creds;
278		}
279
280		if (length == sizeof(CREDS_VALUE) &&
281		    memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) {
282			/* We have creds here. parse them */
283			err = gssx_dec_linux_creds(xdr, creds);
284			if (err)
285				goto free_creds;
286			oa->data[0].value.len = 1; /* presence */
287		} else {
288			/* consume uninteresting buffer */
289			err = gssx_dec_buffer(xdr, &dummy);
290			if (err)
291				goto free_creds;
292		}
293	}
294	return 0;
295
296free_creds:
297	kfree(creds);
298free_oa:
299	kfree(oa->data);
300	oa->data = NULL;
301	return err;
302}
303
304static int gssx_dec_status(struct xdr_stream *xdr,
305			   struct gssx_status *status)
306{
307	__be32 *p;
308	int err;
309
310	/* status->major_status */
311	p = xdr_inline_decode(xdr, 8);
312	if (unlikely(p == NULL))
313		return -ENOSPC;
314	p = xdr_decode_hyper(p, &status->major_status);
315
316	/* status->mech */
317	err = gssx_dec_buffer(xdr, &status->mech);
318	if (err)
319		return err;
320
321	/* status->minor_status */
322	p = xdr_inline_decode(xdr, 8);
323	if (unlikely(p == NULL))
324		return -ENOSPC;
325	p = xdr_decode_hyper(p, &status->minor_status);
326
327	/* status->major_status_string */
328	err = gssx_dec_buffer(xdr, &status->major_status_string);
329	if (err)
330		return err;
331
332	/* status->minor_status_string */
333	err = gssx_dec_buffer(xdr, &status->minor_status_string);
334	if (err)
335		return err;
336
337	/* status->server_ctx */
338	err = gssx_dec_buffer(xdr, &status->server_ctx);
339	if (err)
340		return err;
341
342	/* we assume we have no options for now, so simply consume them */
343	/* status->options */
344	err = dummy_dec_opt_array(xdr, &status->options);
345
346	return err;
347}
348
349static int gssx_enc_call_ctx(struct xdr_stream *xdr,
350			     const struct gssx_call_ctx *ctx)
351{
352	struct gssx_option opt;
353	__be32 *p;
354	int err;
355
356	/* ctx->locale */
357	err = gssx_enc_buffer(xdr, &ctx->locale);
358	if (err)
359		return err;
360
361	/* ctx->server_ctx */
362	err = gssx_enc_buffer(xdr, &ctx->server_ctx);
363	if (err)
364		return err;
365
366	/* we always want to ask for lucid contexts */
367	/* ctx->options */
368	p = xdr_reserve_space(xdr, 4);
369	*p = cpu_to_be32(2);
370
371	/* we want a lucid_v1 context */
372	opt.option.data = LUCID_OPTION;
373	opt.option.len = sizeof(LUCID_OPTION);
374	opt.value.data = LUCID_VALUE;
375	opt.value.len = sizeof(LUCID_VALUE);
376	err = gssx_enc_option(xdr, &opt);
377
378	/* ..and user creds */
379	opt.option.data = CREDS_OPTION;
380	opt.option.len = sizeof(CREDS_OPTION);
381	opt.value.data = CREDS_VALUE;
382	opt.value.len = sizeof(CREDS_VALUE);
383	err = gssx_enc_option(xdr, &opt);
384
385	return err;
386}
387
388static int gssx_dec_name_attr(struct xdr_stream *xdr,
389			     struct gssx_name_attr *attr)
390{
391	int err;
392
393	/* attr->attr */
394	err = gssx_dec_buffer(xdr, &attr->attr);
395	if (err)
396		return err;
397
398	/* attr->value */
399	err = gssx_dec_buffer(xdr, &attr->value);
400	if (err)
401		return err;
402
403	/* attr->extensions */
404	err = dummy_dec_opt_array(xdr, &attr->extensions);
405
406	return err;
407}
408
409static int dummy_enc_nameattr_array(struct xdr_stream *xdr,
410				    struct gssx_name_attr_array *naa)
411{
412	__be32 *p;
413
414	if (naa->count != 0)
415		return -EINVAL;
416
417	p = xdr_reserve_space(xdr, 4);
418	if (!p)
419		return -ENOSPC;
420	*p = 0;
421
422	return 0;
423}
424
425static int dummy_dec_nameattr_array(struct xdr_stream *xdr,
426				    struct gssx_name_attr_array *naa)
427{
428	struct gssx_name_attr dummy = { .attr = {.len = 0} };
429	u32 count, i;
430	__be32 *p;
431
432	p = xdr_inline_decode(xdr, 4);
433	if (unlikely(p == NULL))
434		return -ENOSPC;
435	count = be32_to_cpup(p++);
436	for (i = 0; i < count; i++) {
437		gssx_dec_name_attr(xdr, &dummy);
438	}
439
440	naa->count = 0;
441	naa->data = NULL;
442	return 0;
443}
444
445static struct xdr_netobj zero_netobj = {};
446
447static struct gssx_name_attr_array zero_name_attr_array = {};
448
449static struct gssx_option_array zero_option_array = {};
450
451static int gssx_enc_name(struct xdr_stream *xdr,
452			 struct gssx_name *name)
453{
454	int err;
455
456	/* name->display_name */
457	err = gssx_enc_buffer(xdr, &name->display_name);
458	if (err)
459		return err;
460
461	/* name->name_type */
462	err = gssx_enc_buffer(xdr, &zero_netobj);
463	if (err)
464		return err;
465
466	/* name->exported_name */
467	err = gssx_enc_buffer(xdr, &zero_netobj);
468	if (err)
469		return err;
470
471	/* name->exported_composite_name */
472	err = gssx_enc_buffer(xdr, &zero_netobj);
473	if (err)
474		return err;
475
476	/* leave name_attributes empty for now, will add once we have any
477	 * to pass up at all */
478	/* name->name_attributes */
479	err = dummy_enc_nameattr_array(xdr, &zero_name_attr_array);
480	if (err)
481		return err;
482
483	/* leave options empty for now, will add once we have any options
484	 * to pass up at all */
485	/* name->extensions */
486	err = dummy_enc_opt_array(xdr, &zero_option_array);
487
488	return err;
489}
490
491
492static int gssx_dec_name(struct xdr_stream *xdr,
493			 struct gssx_name *name)
494{
495	struct xdr_netobj dummy_netobj = { .len = 0 };
496	struct gssx_name_attr_array dummy_name_attr_array = { .count = 0 };
497	struct gssx_option_array dummy_option_array = { .count = 0 };
498	int err;
499
500	/* name->display_name */
501	err = gssx_dec_buffer(xdr, &name->display_name);
502	if (err)
503		return err;
504
505	/* name->name_type */
506	err = gssx_dec_buffer(xdr, &dummy_netobj);
507	if (err)
508		return err;
509
510	/* name->exported_name */
511	err = gssx_dec_buffer(xdr, &dummy_netobj);
512	if (err)
513		return err;
514
515	/* name->exported_composite_name */
516	err = gssx_dec_buffer(xdr, &dummy_netobj);
517	if (err)
518		return err;
519
520	/* we assume we have no attributes for now, so simply consume them */
521	/* name->name_attributes */
522	err = dummy_dec_nameattr_array(xdr, &dummy_name_attr_array);
523	if (err)
524		return err;
525
526	/* we assume we have no options for now, so simply consume them */
527	/* name->extensions */
528	err = dummy_dec_opt_array(xdr, &dummy_option_array);
529
530	return err;
531}
532
533static int dummy_enc_credel_array(struct xdr_stream *xdr,
534				  struct gssx_cred_element_array *cea)
535{
536	__be32 *p;
537
538	if (cea->count != 0)
539		return -EINVAL;
540
541	p = xdr_reserve_space(xdr, 4);
542	if (!p)
543		return -ENOSPC;
544	*p = 0;
545
546	return 0;
547}
548
549static int gssx_enc_cred(struct xdr_stream *xdr,
550			 struct gssx_cred *cred)
551{
552	int err;
553
554	/* cred->desired_name */
555	err = gssx_enc_name(xdr, &cred->desired_name);
556	if (err)
557		return err;
558
559	/* cred->elements */
560	err = dummy_enc_credel_array(xdr, &cred->elements);
561	if (err)
562		return err;
563
564	/* cred->cred_handle_reference */
565	err = gssx_enc_buffer(xdr, &cred->cred_handle_reference);
566	if (err)
567		return err;
568
569	/* cred->needs_release */
570	err = gssx_enc_bool(xdr, cred->needs_release);
571
572	return err;
573}
574
575static int gssx_enc_ctx(struct xdr_stream *xdr,
576			struct gssx_ctx *ctx)
577{
578	__be32 *p;
579	int err;
580
581	/* ctx->exported_context_token */
582	err = gssx_enc_buffer(xdr, &ctx->exported_context_token);
583	if (err)
584		return err;
585
586	/* ctx->state */
587	err = gssx_enc_buffer(xdr, &ctx->state);
588	if (err)
589		return err;
590
591	/* ctx->need_release */
592	err = gssx_enc_bool(xdr, ctx->need_release);
593	if (err)
594		return err;
595
596	/* ctx->mech */
597	err = gssx_enc_buffer(xdr, &ctx->mech);
598	if (err)
599		return err;
600
601	/* ctx->src_name */
602	err = gssx_enc_name(xdr, &ctx->src_name);
603	if (err)
604		return err;
605
606	/* ctx->targ_name */
607	err = gssx_enc_name(xdr, &ctx->targ_name);
608	if (err)
609		return err;
610
611	/* ctx->lifetime */
612	p = xdr_reserve_space(xdr, 8+8);
613	if (!p)
614		return -ENOSPC;
615	p = xdr_encode_hyper(p, ctx->lifetime);
616
617	/* ctx->ctx_flags */
618	p = xdr_encode_hyper(p, ctx->ctx_flags);
619
620	/* ctx->locally_initiated */
621	err = gssx_enc_bool(xdr, ctx->locally_initiated);
622	if (err)
623		return err;
624
625	/* ctx->open */
626	err = gssx_enc_bool(xdr, ctx->open);
627	if (err)
628		return err;
629
630	/* leave options empty for now, will add once we have any options
631	 * to pass up at all */
632	/* ctx->options */
633	err = dummy_enc_opt_array(xdr, &ctx->options);
634
635	return err;
636}
637
638static int gssx_dec_ctx(struct xdr_stream *xdr,
639			struct gssx_ctx *ctx)
640{
641	__be32 *p;
642	int err;
643
644	/* ctx->exported_context_token */
645	err = gssx_dec_buffer(xdr, &ctx->exported_context_token);
646	if (err)
647		return err;
648
649	/* ctx->state */
650	err = gssx_dec_buffer(xdr, &ctx->state);
651	if (err)
652		return err;
653
654	/* ctx->need_release */
655	err = gssx_dec_bool(xdr, &ctx->need_release);
656	if (err)
657		return err;
658
659	/* ctx->mech */
660	err = gssx_dec_buffer(xdr, &ctx->mech);
661	if (err)
662		return err;
663
664	/* ctx->src_name */
665	err = gssx_dec_name(xdr, &ctx->src_name);
666	if (err)
667		return err;
668
669	/* ctx->targ_name */
670	err = gssx_dec_name(xdr, &ctx->targ_name);
671	if (err)
672		return err;
673
674	/* ctx->lifetime */
675	p = xdr_inline_decode(xdr, 8+8);
676	if (unlikely(p == NULL))
677		return -ENOSPC;
678	p = xdr_decode_hyper(p, &ctx->lifetime);
679
680	/* ctx->ctx_flags */
681	p = xdr_decode_hyper(p, &ctx->ctx_flags);
682
683	/* ctx->locally_initiated */
684	err = gssx_dec_bool(xdr, &ctx->locally_initiated);
685	if (err)
686		return err;
687
688	/* ctx->open */
689	err = gssx_dec_bool(xdr, &ctx->open);
690	if (err)
691		return err;
692
693	/* we assume we have no options for now, so simply consume them */
694	/* ctx->options */
695	err = dummy_dec_opt_array(xdr, &ctx->options);
696
697	return err;
698}
699
700static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb)
701{
702	__be32 *p;
703	int err;
704
705	/* cb->initiator_addrtype */
706	p = xdr_reserve_space(xdr, 8);
707	if (!p)
708		return -ENOSPC;
709	p = xdr_encode_hyper(p, cb->initiator_addrtype);
710
711	/* cb->initiator_address */
712	err = gssx_enc_buffer(xdr, &cb->initiator_address);
713	if (err)
714		return err;
715
716	/* cb->acceptor_addrtype */
717	p = xdr_reserve_space(xdr, 8);
718	if (!p)
719		return -ENOSPC;
720	p = xdr_encode_hyper(p, cb->acceptor_addrtype);
721
722	/* cb->acceptor_address */
723	err = gssx_enc_buffer(xdr, &cb->acceptor_address);
724	if (err)
725		return err;
726
727	/* cb->application_data */
728	err = gssx_enc_buffer(xdr, &cb->application_data);
729
730	return err;
731}
732
733void gssx_enc_accept_sec_context(struct rpc_rqst *req,
734				 struct xdr_stream *xdr,
735				 const void *data)
736{
737	const struct gssx_arg_accept_sec_context *arg = data;
738	int err;
739
740	err = gssx_enc_call_ctx(xdr, &arg->call_ctx);
741	if (err)
742		goto done;
743
744	/* arg->context_handle */
745	if (arg->context_handle)
746		err = gssx_enc_ctx(xdr, arg->context_handle);
747	else
748		err = gssx_enc_bool(xdr, 0);
749	if (err)
750		goto done;
751
752	/* arg->cred_handle */
753	if (arg->cred_handle)
754		err = gssx_enc_cred(xdr, arg->cred_handle);
755	else
756		err = gssx_enc_bool(xdr, 0);
757	if (err)
758		goto done;
759
760	/* arg->input_token */
761	err = gssx_enc_in_token(xdr, &arg->input_token);
762	if (err)
763		goto done;
764
765	/* arg->input_cb */
766	if (arg->input_cb)
767		err = gssx_enc_cb(xdr, arg->input_cb);
768	else
769		err = gssx_enc_bool(xdr, 0);
770	if (err)
771		goto done;
772
773	err = gssx_enc_bool(xdr, arg->ret_deleg_cred);
774	if (err)
775		goto done;
776
777	/* leave options empty for now, will add once we have any options
778	 * to pass up at all */
779	/* arg->options */
780	err = dummy_enc_opt_array(xdr, &arg->options);
781
782	xdr_inline_pages(&req->rq_rcv_buf,
783		PAGE_SIZE/2 /* pretty arbitrary */,
784		arg->pages, 0 /* page base */, arg->npages * PAGE_SIZE);
785done:
786	if (err)
787		dprintk("RPC:       gssx_enc_accept_sec_context: %d\n", err);
788}
789
790int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
791				struct xdr_stream *xdr,
792				void *data)
793{
794	struct gssx_res_accept_sec_context *res = data;
795	u32 value_follows;
796	int err;
797	struct page *scratch;
798
799	scratch = alloc_page(GFP_KERNEL);
800	if (!scratch)
801		return -ENOMEM;
802	xdr_set_scratch_page(xdr, scratch);
803
804	/* res->status */
805	err = gssx_dec_status(xdr, &res->status);
806	if (err)
807		goto out_free;
808
809	/* res->context_handle */
810	err = gssx_dec_bool(xdr, &value_follows);
811	if (err)
812		goto out_free;
813	if (value_follows) {
814		err = gssx_dec_ctx(xdr, res->context_handle);
815		if (err)
816			goto out_free;
817	} else {
818		res->context_handle = NULL;
819	}
820
821	/* res->output_token */
822	err = gssx_dec_bool(xdr, &value_follows);
823	if (err)
824		goto out_free;
825	if (value_follows) {
826		err = gssx_dec_buffer(xdr, res->output_token);
827		if (err)
828			goto out_free;
829	} else {
830		res->output_token = NULL;
831	}
832
833	/* res->delegated_cred_handle */
834	err = gssx_dec_bool(xdr, &value_follows);
835	if (err)
836		goto out_free;
837	if (value_follows) {
838		/* we do not support upcall servers sending this data. */
839		err = -EINVAL;
840		goto out_free;
841	}
842
843	/* res->options */
844	err = gssx_dec_option_array(xdr, &res->options);
845
846out_free:
847	__free_page(scratch);
848	return err;
849}
850