1/*
2  SPDX-License-Identifier: BSD-3-Clause
3
4  rpcsec_gss_prot.c
5
6  Copyright (c) 2000 The Regents of the University of Michigan.
7  All rights reserved.
8
9  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
10  All rights reserved, all wrongs reversed.
11
12  Redistribution and use in source and binary forms, with or without
13  modification, are permitted provided that the following conditions
14  are met:
15
16  1. Redistributions of source code must retain the above copyright
17     notice, this list of conditions and the following disclaimer.
18  2. Redistributions in binary form must reproduce the above copyright
19     notice, this list of conditions and the following disclaimer in the
20     documentation and/or other materials provided with the distribution.
21  3. Neither the name of the University nor the names of its
22     contributors may be used to endorse or promote products derived
23     from this software without specific prior written permission.
24
25  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
26  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
32  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
37  $Id: authgss_prot.c,v 1.18 2000/09/01 04:14:03 dugsong Exp $
38*/
39/* $FreeBSD$ */
40
41#include <stdio.h>
42#include <stdlib.h>
43#include <stdarg.h>
44#include <string.h>
45#include <rpc/rpc.h>
46#include <rpc/rpcsec_gss.h>
47#include "rpcsec_gss_int.h"
48
49#define MAX_GSS_SIZE	10240	/* XXX */
50
51bool_t
52xdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *p)
53{
54	char *val;
55	u_int len;
56	bool_t ret;
57
58	val = p->value;
59	len = p->length;
60	ret = xdr_bytes(xdrs, &val, &len, MAX_GSS_SIZE);
61	p->value = val;
62	p->length = len;
63
64	return (ret);
65}
66
67bool_t
68xdr_rpc_gss_cred(XDR *xdrs, struct rpc_gss_cred *p)
69{
70	enum_t proc, svc;
71	bool_t ret;
72
73	proc = p->gc_proc;
74	svc = p->gc_svc;
75	ret = (xdr_u_int(xdrs, &p->gc_version) &&
76	    xdr_enum(xdrs, &proc) &&
77	    xdr_u_int(xdrs, &p->gc_seq) &&
78	    xdr_enum(xdrs, &svc) &&
79	    xdr_gss_buffer_desc(xdrs, &p->gc_handle));
80	p->gc_proc = proc;
81	p->gc_svc = svc;
82
83	return (ret);
84}
85
86bool_t
87xdr_rpc_gss_init_res(XDR *xdrs, struct rpc_gss_init_res *p)
88{
89
90	return (xdr_gss_buffer_desc(xdrs, &p->gr_handle) &&
91	    xdr_u_int(xdrs, &p->gr_major) &&
92	    xdr_u_int(xdrs, &p->gr_minor) &&
93	    xdr_u_int(xdrs, &p->gr_win) &&
94	    xdr_gss_buffer_desc(xdrs, &p->gr_token));
95}
96
97bool_t
98xdr_rpc_gss_wrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr,
99		      gss_ctx_id_t ctx, gss_qop_t qop,
100		      rpc_gss_service_t svc, u_int seq)
101{
102	gss_buffer_desc	databuf, wrapbuf;
103	OM_uint32	maj_stat, min_stat;
104	int		start, end, conf_state;
105	u_int		len;
106	bool_t		xdr_stat;
107
108	/* Skip databody length. */
109	start = XDR_GETPOS(xdrs);
110	XDR_SETPOS(xdrs, start + 4);
111
112	/* Marshal rpc_gss_data_t (sequence number + arguments). */
113	if (!xdr_u_int(xdrs, &seq) || !xdr_func(xdrs, xdr_ptr))
114		return (FALSE);
115	end = XDR_GETPOS(xdrs);
116
117	/* Set databuf to marshalled rpc_gss_data_t. */
118	databuf.length = end - start - 4;
119	XDR_SETPOS(xdrs, start + 4);
120	databuf.value = XDR_INLINE(xdrs, databuf.length);
121
122	xdr_stat = FALSE;
123
124	if (svc == rpc_gss_svc_integrity) {
125		/* Marshal databody_integ length. */
126		XDR_SETPOS(xdrs, start);
127		len = databuf.length;
128		if (!xdr_u_int(xdrs, &len))
129			return (FALSE);
130
131		/* Checksum rpc_gss_data_t. */
132		maj_stat = gss_get_mic(&min_stat, ctx, qop,
133				       &databuf, &wrapbuf);
134		if (maj_stat != GSS_S_COMPLETE) {
135			log_debug("gss_get_mic failed");
136			return (FALSE);
137		}
138		/* Marshal checksum. */
139		XDR_SETPOS(xdrs, end);
140		xdr_stat = xdr_gss_buffer_desc(xdrs, &wrapbuf);
141		gss_release_buffer(&min_stat, &wrapbuf);
142	}
143	else if (svc == rpc_gss_svc_privacy) {
144		/* Encrypt rpc_gss_data_t. */
145		maj_stat = gss_wrap(&min_stat, ctx, TRUE, qop, &databuf,
146				    &conf_state, &wrapbuf);
147		if (maj_stat != GSS_S_COMPLETE) {
148			log_status("gss_wrap", NULL, maj_stat, min_stat);
149			return (FALSE);
150		}
151		/* Marshal databody_priv. */
152		XDR_SETPOS(xdrs, start);
153		xdr_stat = xdr_gss_buffer_desc(xdrs, &wrapbuf);
154		gss_release_buffer(&min_stat, &wrapbuf);
155	}
156	return (xdr_stat);
157}
158
159bool_t
160xdr_rpc_gss_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr,
161			gss_ctx_id_t ctx, gss_qop_t qop,
162			rpc_gss_service_t svc, u_int seq)
163{
164	XDR		tmpxdrs;
165	gss_buffer_desc	databuf, wrapbuf;
166	OM_uint32	maj_stat, min_stat;
167	u_int		seq_num, conf_state, qop_state;
168	bool_t		xdr_stat;
169
170	if (xdr_func == (xdrproc_t) xdr_void || xdr_ptr == NULL)
171		return (TRUE);
172
173	memset(&databuf, 0, sizeof(databuf));
174	memset(&wrapbuf, 0, sizeof(wrapbuf));
175
176	if (svc == rpc_gss_svc_integrity) {
177		/* Decode databody_integ. */
178		if (!xdr_gss_buffer_desc(xdrs, &databuf)) {
179			log_debug("xdr decode databody_integ failed");
180			return (FALSE);
181		}
182		/* Decode checksum. */
183		if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) {
184			mem_free(databuf.value, databuf.length);
185			log_debug("xdr decode checksum failed");
186			return (FALSE);
187		}
188		/* Verify checksum and QOP. */
189		maj_stat = gss_verify_mic(&min_stat, ctx, &databuf,
190					  &wrapbuf, &qop_state);
191		mem_free(wrapbuf.value, wrapbuf.length);
192
193		if (maj_stat != GSS_S_COMPLETE || qop_state != qop) {
194			mem_free(databuf.value, databuf.length);
195			log_status("gss_verify_mic", NULL, maj_stat, min_stat);
196			return (FALSE);
197		}
198	} else if (svc == rpc_gss_svc_privacy) {
199		/* Decode databody_priv. */
200		if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) {
201			log_debug("xdr decode databody_priv failed");
202			return (FALSE);
203		}
204		/* Decrypt databody. */
205		maj_stat = gss_unwrap(&min_stat, ctx, &wrapbuf, &databuf,
206				      &conf_state, &qop_state);
207
208		mem_free(wrapbuf.value, wrapbuf.length);
209
210		/* Verify encryption and QOP. */
211		if (maj_stat != GSS_S_COMPLETE || qop_state != qop ||
212			conf_state != TRUE) {
213			gss_release_buffer(&min_stat, &databuf);
214			log_status("gss_unwrap", NULL, maj_stat, min_stat);
215			return (FALSE);
216		}
217	}
218	/* Decode rpc_gss_data_t (sequence number + arguments). */
219	xdrmem_create(&tmpxdrs, databuf.value, databuf.length, XDR_DECODE);
220	xdr_stat = (xdr_u_int(&tmpxdrs, &seq_num) &&
221	    xdr_func(&tmpxdrs, xdr_ptr));
222	XDR_DESTROY(&tmpxdrs);
223
224	/*
225	 * Integrity service allocates databuf via XDR so free it the
226	 * same way.
227	 */
228	if (svc == rpc_gss_svc_integrity) {
229		xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &databuf);
230	} else {
231		gss_release_buffer(&min_stat, &databuf);
232	}
233
234	/* Verify sequence number. */
235	if (xdr_stat == TRUE && seq_num != seq) {
236		log_debug("wrong sequence number in databody");
237		return (FALSE);
238	}
239	return (xdr_stat);
240}
241
242#ifdef DEBUG
243#include <ctype.h>
244
245void
246log_debug(const char *fmt, ...)
247{
248	va_list ap;
249
250	va_start(ap, fmt);
251	fprintf(stderr, "rpcsec_gss: ");
252	vfprintf(stderr, fmt, ap);
253	fprintf(stderr, "\n");
254	va_end(ap);
255}
256
257void
258log_status(const char *m, gss_OID mech, OM_uint32 maj_stat, OM_uint32 min_stat)
259{
260	OM_uint32 min;
261	gss_buffer_desc msg;
262	int msg_ctx = 0;
263
264	fprintf(stderr, "rpcsec_gss: %s: ", m);
265
266	gss_display_status(&min, maj_stat, GSS_C_GSS_CODE, GSS_C_NULL_OID,
267			   &msg_ctx, &msg);
268	fprintf(stderr, "%s - ", (char *)msg.value);
269	gss_release_buffer(&min, &msg);
270
271	gss_display_status(&min, min_stat, GSS_C_MECH_CODE, mech,
272			   &msg_ctx, &msg);
273	fprintf(stderr, "%s\n", (char *)msg.value);
274	gss_release_buffer(&min, &msg);
275}
276
277#else
278
279void
280log_debug(__unused const char *fmt, ...)
281{
282}
283
284void
285log_status(__unused const char *m, __unused gss_OID mech,
286    __unused OM_uint32 maj_stat, __unused OM_uint32 min_stat)
287{
288}
289
290#endif
291
292
293