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