1/*
2  rpcsec_gss_prot.c
3
4  SPDX-License-Identifier: BSD-3-Clause
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
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kobj.h>
43#include <sys/lock.h>
44#include <sys/malloc.h>
45#include <sys/mbuf.h>
46#include <sys/mutex.h>
47
48#include <rpc/rpc.h>
49#include <rpc/rpcsec_gss.h>
50
51#include "rpcsec_gss_int.h"
52
53#define MAX_GSS_SIZE	10240	/* XXX */
54
55#if 0				/* use the one from kgssapi */
56bool_t
57xdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *p)
58{
59	char *val;
60	u_int len;
61	bool_t ret;
62
63	val = p->value;
64	len = p->length;
65	ret = xdr_bytes(xdrs, &val, &len, MAX_GSS_SIZE);
66	p->value = val;
67	p->length = len;
68
69	return (ret);
70}
71#endif
72
73bool_t
74xdr_rpc_gss_cred(XDR *xdrs, struct rpc_gss_cred *p)
75{
76	enum_t proc, svc;
77	bool_t ret;
78
79	proc = p->gc_proc;
80	svc = p->gc_svc;
81	ret = (xdr_u_int(xdrs, &p->gc_version) &&
82	    xdr_enum(xdrs, &proc) &&
83	    xdr_u_int(xdrs, &p->gc_seq) &&
84	    xdr_enum(xdrs, &svc) &&
85	    xdr_gss_buffer_desc(xdrs, &p->gc_handle));
86	p->gc_proc = proc;
87	p->gc_svc = svc;
88
89	return (ret);
90}
91
92bool_t
93xdr_rpc_gss_init_res(XDR *xdrs, struct rpc_gss_init_res *p)
94{
95
96	return (xdr_gss_buffer_desc(xdrs, &p->gr_handle) &&
97	    xdr_u_int(xdrs, &p->gr_major) &&
98	    xdr_u_int(xdrs, &p->gr_minor) &&
99	    xdr_u_int(xdrs, &p->gr_win) &&
100	    xdr_gss_buffer_desc(xdrs, &p->gr_token));
101}
102
103static void
104put_uint32(struct mbuf **mp, uint32_t v)
105{
106	struct mbuf *m = *mp;
107	uint32_t n;
108
109	M_PREPEND(m, sizeof(uint32_t), M_WAITOK);
110	n = htonl(v);
111	bcopy(&n, mtod(m, uint32_t *), sizeof(uint32_t));
112	*mp = m;
113}
114
115bool_t
116xdr_rpc_gss_wrap_data(struct mbuf **argsp,
117		      gss_ctx_id_t ctx, gss_qop_t qop,
118		      rpc_gss_service_t svc, u_int seq)
119{
120	struct mbuf	*args, *mic;
121	OM_uint32	maj_stat, min_stat;
122	int		conf_state;
123	u_int		len;
124	static char	zpad[4];
125
126	args = *argsp;
127
128	/*
129	 * Prepend the sequence number before calling gss_get_mic or gss_wrap.
130	 */
131	put_uint32(&args, seq);
132	len = m_length(args, NULL);
133
134	if (svc == rpc_gss_svc_integrity) {
135		/* Checksum rpc_gss_data_t. */
136		maj_stat = gss_get_mic_mbuf(&min_stat, ctx, qop, args, &mic);
137		if (maj_stat != GSS_S_COMPLETE) {
138			rpc_gss_log_debug("gss_get_mic failed");
139			m_freem(args);
140			return (FALSE);
141		}
142
143		/*
144		 * Marshal databody_integ. Note that since args is
145		 * already RPC encoded, there will be no padding.
146		 */
147		put_uint32(&args, len);
148
149		/*
150		 * Marshal checksum. This is likely to need padding.
151		 */
152		len = m_length(mic, NULL);
153		put_uint32(&mic, len);
154		if (len != RNDUP(len)) {
155			m_append(mic, RNDUP(len) - len, zpad);
156		}
157
158		/*
159		 * Concatenate databody_integ with checksum.
160		 */
161		m_cat(args, mic);
162	} else if (svc == rpc_gss_svc_privacy) {
163		/* Encrypt rpc_gss_data_t. */
164		maj_stat = gss_wrap_mbuf(&min_stat, ctx, TRUE, qop,
165		    &args, &conf_state);
166		if (maj_stat != GSS_S_COMPLETE) {
167			rpc_gss_log_status("gss_wrap", NULL,
168			    maj_stat, min_stat);
169			return (FALSE);
170		}
171
172		/*
173		 *  Marshal databody_priv and deal with RPC padding.
174		 */
175		len = m_length(args, NULL);
176		put_uint32(&args, len);
177		if (len != RNDUP(len)) {
178			m_append(args, RNDUP(len) - len, zpad);
179		}
180	}
181	*argsp = args;
182	return (TRUE);
183}
184
185static uint32_t
186get_uint32(struct mbuf **mp)
187{
188	struct mbuf *m = *mp;
189	uint32_t n;
190
191	if (m->m_len < sizeof(uint32_t)) {
192		m = m_pullup(m, sizeof(uint32_t));
193		if (!m) {
194			*mp = NULL;
195			return (0);
196		}
197	}
198	bcopy(mtod(m, uint32_t *), &n, sizeof(uint32_t));
199	m_adj(m, sizeof(uint32_t));
200	*mp = m;
201	return (ntohl(n));
202}
203
204static void
205m_trim(struct mbuf *m, int len)
206{
207	struct mbuf *n;
208	int off;
209
210	if (m == NULL)
211		return;
212	n = m_getptr(m, len, &off);
213	if (n) {
214		n->m_len = off;
215		if (n->m_next) {
216			m_freem(n->m_next);
217			n->m_next = NULL;
218		}
219	}
220}
221
222bool_t
223xdr_rpc_gss_unwrap_data(struct mbuf **resultsp,
224			gss_ctx_id_t ctx, gss_qop_t qop,
225			rpc_gss_service_t svc, u_int seq)
226{
227	struct mbuf	*results, *message, *mic;
228	uint32_t	len, cklen;
229	OM_uint32	maj_stat, min_stat;
230	u_int		seq_num, conf_state, qop_state;
231
232	results = *resultsp;
233	*resultsp = NULL;
234
235	message = NULL;
236	if (svc == rpc_gss_svc_integrity) {
237		/*
238		 * Extract the seq+message part. Remember that there
239		 * may be extra RPC padding in the checksum. The
240		 * message part is RPC encoded already so no
241		 * padding.
242		 */
243		len = get_uint32(&results);
244		message = results;
245		results = m_split(results, len, M_WAITOK);
246		if (!results) {
247			m_freem(message);
248			return (FALSE);
249		}
250
251		/*
252		 * Extract the MIC and make it contiguous.
253		 */
254		cklen = get_uint32(&results);
255		if (!results) {
256			m_freem(message);
257			return (FALSE);
258		}
259		KASSERT(cklen <= MHLEN, ("unexpected large GSS-API checksum"));
260		mic = results;
261		if (cklen > mic->m_len) {
262			mic = m_pullup(mic, cklen);
263			if (!mic) {
264				m_freem(message);
265				return (FALSE);
266			}
267		}
268		if (cklen != RNDUP(cklen))
269			m_trim(mic, cklen);
270
271		/* Verify checksum and QOP. */
272		maj_stat = gss_verify_mic_mbuf(&min_stat, ctx,
273		    message, mic, &qop_state);
274		m_freem(mic);
275
276		if (maj_stat != GSS_S_COMPLETE || qop_state != qop) {
277			m_freem(message);
278			rpc_gss_log_status("gss_verify_mic", NULL,
279			    maj_stat, min_stat);
280			return (FALSE);
281		}
282	} else if (svc == rpc_gss_svc_privacy) {
283		/* Decode databody_priv. */
284		len = get_uint32(&results);
285		if (!results)
286			return (FALSE);
287
288		/* Decrypt databody. */
289		message = results;
290		if (len != RNDUP(len))
291			m_trim(message, len);
292		maj_stat = gss_unwrap_mbuf(&min_stat, ctx, &message,
293		    &conf_state, &qop_state);
294
295		/* Verify encryption and QOP. */
296		if (maj_stat != GSS_S_COMPLETE) {
297			rpc_gss_log_status("gss_unwrap", NULL,
298			    maj_stat, min_stat);
299			return (FALSE);
300		}
301		if (qop_state != qop || conf_state != TRUE) {
302			m_freem(results);
303			return (FALSE);
304		}
305	}
306
307	/* Decode rpc_gss_data_t (sequence number + arguments). */
308	seq_num = get_uint32(&message);
309	if (!message)
310		return (FALSE);
311
312	/* Verify sequence number. */
313	if (seq_num != seq) {
314		rpc_gss_log_debug("wrong sequence number in databody");
315		m_freem(message);
316		return (FALSE);
317	}
318
319	*resultsp = message;
320	return (TRUE);
321}
322
323#ifdef DEBUG
324#include <machine/stdarg.h>
325
326void
327rpc_gss_log_debug(const char *fmt, ...)
328{
329	va_list ap;
330
331	va_start(ap, fmt);
332	printf("rpcsec_gss: ");
333	vprintf(fmt, ap);
334	printf("\n");
335	va_end(ap);
336}
337
338void
339rpc_gss_log_status(const char *m, gss_OID mech, OM_uint32 maj_stat, OM_uint32 min_stat)
340{
341	OM_uint32 min;
342	gss_buffer_desc msg;
343	int msg_ctx = 0;
344
345	printf("rpcsec_gss: %s: ", m);
346
347	gss_display_status(&min, maj_stat, GSS_C_GSS_CODE, GSS_C_NULL_OID,
348			   &msg_ctx, &msg);
349	printf("%s - ", (char *)msg.value);
350	gss_release_buffer(&min, &msg);
351
352	gss_display_status(&min, min_stat, GSS_C_MECH_CODE, mech,
353			   &msg_ctx, &msg);
354	printf("%s\n", (char *)msg.value);
355	gss_release_buffer(&min, &msg);
356}
357
358#else
359
360void
361rpc_gss_log_debug(__unused const char *fmt, ...)
362{
363}
364
365void
366rpc_gss_log_status(__unused const char *m, __unused gss_OID mech,
367    __unused OM_uint32 maj_stat, __unused OM_uint32 min_stat)
368{
369}
370
371#endif
372
373
374