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