1184588Sdfr/*
2184588Sdfr  rpcsec_gss_prot.c
3184588Sdfr
4184588Sdfr  Copyright (c) 2000 The Regents of the University of Michigan.
5184588Sdfr  All rights reserved.
6184588Sdfr
7184588Sdfr  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
8184588Sdfr  All rights reserved, all wrongs reversed.
9184588Sdfr
10184588Sdfr  Redistribution and use in source and binary forms, with or without
11184588Sdfr  modification, are permitted provided that the following conditions
12184588Sdfr  are met:
13184588Sdfr
14184588Sdfr  1. Redistributions of source code must retain the above copyright
15184588Sdfr     notice, this list of conditions and the following disclaimer.
16184588Sdfr  2. Redistributions in binary form must reproduce the above copyright
17184588Sdfr     notice, this list of conditions and the following disclaimer in the
18184588Sdfr     documentation and/or other materials provided with the distribution.
19184588Sdfr  3. Neither the name of the University nor the names of its
20184588Sdfr     contributors may be used to endorse or promote products derived
21184588Sdfr     from this software without specific prior written permission.
22184588Sdfr
23184588Sdfr  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
24184588Sdfr  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25184588Sdfr  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26184588Sdfr  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27184588Sdfr  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28184588Sdfr  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29184588Sdfr  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30184588Sdfr  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31184588Sdfr  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32184588Sdfr  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33184588Sdfr  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34184588Sdfr
35184588Sdfr  $Id: authgss_prot.c,v 1.18 2000/09/01 04:14:03 dugsong Exp $
36184588Sdfr*/
37184588Sdfr
38184588Sdfr#include <sys/cdefs.h>
39184588Sdfr__FBSDID("$FreeBSD$");
40184588Sdfr
41184588Sdfr#include <sys/param.h>
42184588Sdfr#include <sys/systm.h>
43184588Sdfr#include <sys/kobj.h>
44184588Sdfr#include <sys/lock.h>
45184588Sdfr#include <sys/malloc.h>
46184588Sdfr#include <sys/mbuf.h>
47184588Sdfr#include <sys/mutex.h>
48184588Sdfr
49184588Sdfr#include <rpc/rpc.h>
50184588Sdfr#include <rpc/rpcsec_gss.h>
51184588Sdfr
52184588Sdfr#include "rpcsec_gss_int.h"
53184588Sdfr
54184588Sdfr#define MAX_GSS_SIZE	10240	/* XXX */
55184588Sdfr
56184588Sdfr#if 0				/* use the one from kgssapi */
57184588Sdfrbool_t
58184588Sdfrxdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *p)
59184588Sdfr{
60184588Sdfr	char *val;
61184588Sdfr	u_int len;
62184588Sdfr	bool_t ret;
63184588Sdfr
64184588Sdfr	val = p->value;
65184588Sdfr	len = p->length;
66184588Sdfr	ret = xdr_bytes(xdrs, &val, &len, MAX_GSS_SIZE);
67184588Sdfr	p->value = val;
68184588Sdfr	p->length = len;
69184588Sdfr
70184588Sdfr	return (ret);
71184588Sdfr}
72184588Sdfr#endif
73184588Sdfr
74184588Sdfrbool_t
75184588Sdfrxdr_rpc_gss_cred(XDR *xdrs, struct rpc_gss_cred *p)
76184588Sdfr{
77184588Sdfr	enum_t proc, svc;
78184588Sdfr	bool_t ret;
79184588Sdfr
80184588Sdfr	proc = p->gc_proc;
81184588Sdfr	svc = p->gc_svc;
82184588Sdfr	ret = (xdr_u_int(xdrs, &p->gc_version) &&
83184588Sdfr	    xdr_enum(xdrs, &proc) &&
84184588Sdfr	    xdr_u_int(xdrs, &p->gc_seq) &&
85184588Sdfr	    xdr_enum(xdrs, &svc) &&
86184588Sdfr	    xdr_gss_buffer_desc(xdrs, &p->gc_handle));
87184588Sdfr	p->gc_proc = proc;
88184588Sdfr	p->gc_svc = svc;
89184588Sdfr
90184588Sdfr	return (ret);
91184588Sdfr}
92184588Sdfr
93184588Sdfrbool_t
94184588Sdfrxdr_rpc_gss_init_res(XDR *xdrs, struct rpc_gss_init_res *p)
95184588Sdfr{
96184588Sdfr
97184588Sdfr	return (xdr_gss_buffer_desc(xdrs, &p->gr_handle) &&
98184588Sdfr	    xdr_u_int(xdrs, &p->gr_major) &&
99184588Sdfr	    xdr_u_int(xdrs, &p->gr_minor) &&
100184588Sdfr	    xdr_u_int(xdrs, &p->gr_win) &&
101184588Sdfr	    xdr_gss_buffer_desc(xdrs, &p->gr_token));
102184588Sdfr}
103184588Sdfr
104184588Sdfrstatic void
105184588Sdfrput_uint32(struct mbuf **mp, uint32_t v)
106184588Sdfr{
107184588Sdfr	struct mbuf *m = *mp;
108184588Sdfr	uint32_t n;
109184588Sdfr
110243882Sglebius	M_PREPEND(m, sizeof(uint32_t), M_WAITOK);
111184588Sdfr	n = htonl(v);
112184588Sdfr	bcopy(&n, mtod(m, uint32_t *), sizeof(uint32_t));
113184588Sdfr	*mp = m;
114184588Sdfr}
115184588Sdfr
116184588Sdfrbool_t
117184588Sdfrxdr_rpc_gss_wrap_data(struct mbuf **argsp,
118184588Sdfr		      gss_ctx_id_t ctx, gss_qop_t qop,
119184588Sdfr		      rpc_gss_service_t svc, u_int seq)
120184588Sdfr{
121184588Sdfr	struct mbuf	*args, *mic;
122184588Sdfr	OM_uint32	maj_stat, min_stat;
123184588Sdfr	int		conf_state;
124184588Sdfr	u_int		len;
125184588Sdfr	static char	zpad[4];
126184588Sdfr
127184588Sdfr	args = *argsp;
128184588Sdfr
129184588Sdfr	/*
130184588Sdfr	 * Prepend the sequence number before calling gss_get_mic or gss_wrap.
131184588Sdfr	 */
132184588Sdfr	put_uint32(&args, seq);
133184588Sdfr	len = m_length(args, NULL);
134184588Sdfr
135184588Sdfr	if (svc == rpc_gss_svc_integrity) {
136184588Sdfr		/* Checksum rpc_gss_data_t. */
137184588Sdfr		maj_stat = gss_get_mic_mbuf(&min_stat, ctx, qop, args, &mic);
138184588Sdfr		if (maj_stat != GSS_S_COMPLETE) {
139184588Sdfr			rpc_gss_log_debug("gss_get_mic failed");
140184588Sdfr			m_freem(args);
141184588Sdfr			return (FALSE);
142184588Sdfr		}
143184588Sdfr
144184588Sdfr		/*
145184588Sdfr		 * Marshal databody_integ. Note that since args is
146184588Sdfr		 * already RPC encoded, there will be no padding.
147184588Sdfr		 */
148184588Sdfr		put_uint32(&args, len);
149184588Sdfr
150184588Sdfr		/*
151184588Sdfr		 * Marshal checksum. This is likely to need padding.
152184588Sdfr		 */
153184588Sdfr		len = m_length(mic, NULL);
154184588Sdfr		put_uint32(&mic, len);
155184588Sdfr		if (len != RNDUP(len)) {
156184588Sdfr			m_append(mic, RNDUP(len) - len, zpad);
157184588Sdfr		}
158184588Sdfr
159184588Sdfr		/*
160184588Sdfr		 * Concatenate databody_integ with checksum.
161184588Sdfr		 */
162184588Sdfr		m_cat(args, mic);
163184588Sdfr	} else if (svc == rpc_gss_svc_privacy) {
164184588Sdfr		/* Encrypt rpc_gss_data_t. */
165184588Sdfr		maj_stat = gss_wrap_mbuf(&min_stat, ctx, TRUE, qop,
166184588Sdfr		    &args, &conf_state);
167184588Sdfr		if (maj_stat != GSS_S_COMPLETE) {
168184588Sdfr			rpc_gss_log_status("gss_wrap", NULL,
169184588Sdfr			    maj_stat, min_stat);
170184588Sdfr			return (FALSE);
171184588Sdfr		}
172184588Sdfr
173184588Sdfr		/*
174184588Sdfr		 *  Marshal databody_priv and deal with RPC padding.
175184588Sdfr		 */
176184588Sdfr		len = m_length(args, NULL);
177184588Sdfr		put_uint32(&args, len);
178184588Sdfr		if (len != RNDUP(len)) {
179184588Sdfr			m_append(args, RNDUP(len) - len, zpad);
180184588Sdfr		}
181184588Sdfr	}
182184588Sdfr	*argsp = args;
183184588Sdfr	return (TRUE);
184184588Sdfr}
185184588Sdfr
186184588Sdfrstatic uint32_t
187184588Sdfrget_uint32(struct mbuf **mp)
188184588Sdfr{
189184588Sdfr	struct mbuf *m = *mp;
190184588Sdfr	uint32_t n;
191184588Sdfr
192184588Sdfr	if (m->m_len < sizeof(uint32_t)) {
193184588Sdfr		m = m_pullup(m, sizeof(uint32_t));
194184588Sdfr		if (!m) {
195184588Sdfr			*mp = NULL;
196184588Sdfr			return (0);
197184588Sdfr		}
198184588Sdfr	}
199184588Sdfr	bcopy(mtod(m, uint32_t *), &n, sizeof(uint32_t));
200184588Sdfr	m_adj(m, sizeof(uint32_t));
201184588Sdfr	*mp = m;
202184588Sdfr	return (ntohl(n));
203184588Sdfr}
204184588Sdfr
205184588Sdfrstatic void
206184588Sdfrm_trim(struct mbuf *m, int len)
207184588Sdfr{
208184588Sdfr	struct mbuf *n;
209184588Sdfr	int off;
210184588Sdfr
211249096Sgnn	if (m == NULL)
212249096Sgnn		return;
213184588Sdfr	n = m_getptr(m, len, &off);
214184588Sdfr	if (n) {
215184588Sdfr		n->m_len = off;
216184588Sdfr		if (n->m_next) {
217184588Sdfr			m_freem(n->m_next);
218184588Sdfr			n->m_next = NULL;
219184588Sdfr		}
220184588Sdfr	}
221184588Sdfr}
222184588Sdfr
223184588Sdfrbool_t
224184588Sdfrxdr_rpc_gss_unwrap_data(struct mbuf **resultsp,
225184588Sdfr			gss_ctx_id_t ctx, gss_qop_t qop,
226184588Sdfr			rpc_gss_service_t svc, u_int seq)
227184588Sdfr{
228184588Sdfr	struct mbuf	*results, *message, *mic;
229184588Sdfr	uint32_t	len, cklen;
230184588Sdfr	OM_uint32	maj_stat, min_stat;
231184588Sdfr	u_int		seq_num, conf_state, qop_state;
232184588Sdfr
233184588Sdfr	results = *resultsp;
234184588Sdfr	*resultsp = NULL;
235184588Sdfr
236184588Sdfr	message = NULL;
237184588Sdfr	if (svc == rpc_gss_svc_integrity) {
238184588Sdfr		/*
239184588Sdfr		 * Extract the seq+message part. Remember that there
240184588Sdfr		 * may be extra RPC padding in the checksum. The
241184588Sdfr		 * message part is RPC encoded already so no
242184588Sdfr		 * padding.
243184588Sdfr		 */
244184588Sdfr		len = get_uint32(&results);
245184588Sdfr		message = results;
246243882Sglebius		results = m_split(results, len, M_WAITOK);
247184588Sdfr		if (!results) {
248184588Sdfr			m_freem(message);
249184588Sdfr			return (FALSE);
250184588Sdfr		}
251184588Sdfr
252184588Sdfr		/*
253184588Sdfr		 * Extract the MIC and make it contiguous.
254184588Sdfr		 */
255184588Sdfr		cklen = get_uint32(&results);
256249096Sgnn		if (!results) {
257249096Sgnn			m_freem(message);
258249096Sgnn			return (FALSE);
259249096Sgnn		}
260184588Sdfr		KASSERT(cklen <= MHLEN, ("unexpected large GSS-API checksum"));
261184588Sdfr		mic = results;
262249096Sgnn		if (cklen > mic->m_len) {
263184588Sdfr			mic = m_pullup(mic, cklen);
264249096Sgnn			if (!mic) {
265249096Sgnn				m_freem(message);
266249096Sgnn				return (FALSE);
267249096Sgnn			}
268249096Sgnn		}
269184588Sdfr		if (cklen != RNDUP(cklen))
270184588Sdfr			m_trim(mic, cklen);
271184588Sdfr
272184588Sdfr		/* Verify checksum and QOP. */
273184588Sdfr		maj_stat = gss_verify_mic_mbuf(&min_stat, ctx,
274184588Sdfr		    message, mic, &qop_state);
275184588Sdfr		m_freem(mic);
276184588Sdfr
277184588Sdfr		if (maj_stat != GSS_S_COMPLETE || qop_state != qop) {
278184588Sdfr			m_freem(message);
279184588Sdfr			rpc_gss_log_status("gss_verify_mic", NULL,
280184588Sdfr			    maj_stat, min_stat);
281184588Sdfr			return (FALSE);
282184588Sdfr		}
283184588Sdfr	} else if (svc == rpc_gss_svc_privacy) {
284184588Sdfr		/* Decode databody_priv. */
285184588Sdfr		len = get_uint32(&results);
286249096Sgnn		if (!results)
287249096Sgnn			return (FALSE);
288184588Sdfr
289184588Sdfr		/* Decrypt databody. */
290184588Sdfr		message = results;
291184588Sdfr		if (len != RNDUP(len))
292184588Sdfr			m_trim(message, len);
293184588Sdfr		maj_stat = gss_unwrap_mbuf(&min_stat, ctx, &message,
294184588Sdfr		    &conf_state, &qop_state);
295184588Sdfr
296184588Sdfr		/* Verify encryption and QOP. */
297184588Sdfr		if (maj_stat != GSS_S_COMPLETE) {
298184588Sdfr			rpc_gss_log_status("gss_unwrap", NULL,
299184588Sdfr			    maj_stat, min_stat);
300184588Sdfr			return (FALSE);
301184588Sdfr		}
302184588Sdfr		if (qop_state != qop || conf_state != TRUE) {
303184588Sdfr			m_freem(results);
304184588Sdfr			return (FALSE);
305184588Sdfr		}
306184588Sdfr	}
307184588Sdfr
308184588Sdfr	/* Decode rpc_gss_data_t (sequence number + arguments). */
309184588Sdfr	seq_num = get_uint32(&message);
310249096Sgnn	if (!message)
311249096Sgnn		return (FALSE);
312184588Sdfr
313184588Sdfr	/* Verify sequence number. */
314184588Sdfr	if (seq_num != seq) {
315184588Sdfr		rpc_gss_log_debug("wrong sequence number in databody");
316184588Sdfr		m_freem(message);
317184588Sdfr		return (FALSE);
318184588Sdfr	}
319184588Sdfr
320184588Sdfr	*resultsp = message;
321184588Sdfr	return (TRUE);
322184588Sdfr}
323184588Sdfr
324184588Sdfr#ifdef DEBUG
325201853Sbrooks#include <machine/stdarg.h>
326184588Sdfr
327184588Sdfrvoid
328184588Sdfrrpc_gss_log_debug(const char *fmt, ...)
329184588Sdfr{
330184588Sdfr	va_list ap;
331184588Sdfr
332184588Sdfr	va_start(ap, fmt);
333201853Sbrooks	printf("rpcsec_gss: ");
334201853Sbrooks	vprintf(fmt, ap);
335201853Sbrooks	printf("\n");
336184588Sdfr	va_end(ap);
337184588Sdfr}
338184588Sdfr
339184588Sdfrvoid
340184588Sdfrrpc_gss_log_status(const char *m, gss_OID mech, OM_uint32 maj_stat, OM_uint32 min_stat)
341184588Sdfr{
342184588Sdfr	OM_uint32 min;
343184588Sdfr	gss_buffer_desc msg;
344184588Sdfr	int msg_ctx = 0;
345184588Sdfr
346201853Sbrooks	printf("rpcsec_gss: %s: ", m);
347184588Sdfr
348184588Sdfr	gss_display_status(&min, maj_stat, GSS_C_GSS_CODE, GSS_C_NULL_OID,
349184588Sdfr			   &msg_ctx, &msg);
350184588Sdfr	printf("%s - ", (char *)msg.value);
351184588Sdfr	gss_release_buffer(&min, &msg);
352184588Sdfr
353184588Sdfr	gss_display_status(&min, min_stat, GSS_C_MECH_CODE, mech,
354184588Sdfr			   &msg_ctx, &msg);
355184588Sdfr	printf("%s\n", (char *)msg.value);
356184588Sdfr	gss_release_buffer(&min, &msg);
357184588Sdfr}
358184588Sdfr
359184588Sdfr#else
360184588Sdfr
361184588Sdfrvoid
362184588Sdfrrpc_gss_log_debug(__unused const char *fmt, ...)
363184588Sdfr{
364184588Sdfr}
365184588Sdfr
366184588Sdfrvoid
367184588Sdfrrpc_gss_log_status(__unused const char *m, __unused gss_OID mech,
368184588Sdfr    __unused OM_uint32 maj_stat, __unused OM_uint32 min_stat)
369184588Sdfr{
370184588Sdfr}
371184588Sdfr
372184588Sdfr#endif
373184588Sdfr
374184588Sdfr
375