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 */
56 bool_t
57 xdr_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 
73 bool_t
xdr_rpc_gss_cred(XDR * xdrs,struct rpc_gss_cred * p)74 xdr_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 
92 bool_t
xdr_rpc_gss_init_res(XDR * xdrs,struct rpc_gss_init_res * p)93 xdr_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 
103 static void
put_uint32(struct mbuf ** mp,uint32_t v)104 put_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 
115 bool_t
xdr_rpc_gss_wrap_data(struct mbuf ** argsp,gss_ctx_id_t ctx,gss_qop_t qop,rpc_gss_service_t svc,u_int seq)116 xdr_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 
185 static uint32_t
get_uint32(struct mbuf ** mp)186 get_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 
204 static void
m_trim(struct mbuf * m,int len)205 m_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 
222 bool_t
xdr_rpc_gss_unwrap_data(struct mbuf ** resultsp,gss_ctx_id_t ctx,gss_qop_t qop,rpc_gss_service_t svc,u_int seq)223 xdr_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 
326 void
rpc_gss_log_debug(const char * fmt,...)327 rpc_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 
338 void
rpc_gss_log_status(const char * m,gss_OID mech,OM_uint32 maj_stat,OM_uint32 min_stat)339 rpc_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 
360 void
rpc_gss_log_debug(__unused const char * fmt,...)361 rpc_gss_log_debug(__unused const char *fmt, ...)
362 {
363 }
364 
365 void
rpc_gss_log_status(__unused const char * m,__unused gss_OID mech,__unused OM_uint32 maj_stat,__unused OM_uint32 min_stat)366 rpc_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