Linux Audio

Check our new training course

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