xref: /freebsd/sys/rpc/rpcsec_gss/rpcsec_gss_prot.c (revision 298cf604ccf133b101c6fad42d1a078a1fac58ca)
1 /*
2   rpcsec_gss_prot.c
3 
4   Copyright (c) 2000 The Regents of the University of Michigan.
5   All rights reserved.
6 
7   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
8   All rights reserved, all wrongs reversed.
9 
10   Redistribution and use in source and binary forms, with or without
11   modification, are permitted provided that the following conditions
12   are met:
13 
14   1. Redistributions of source code must retain the above copyright
15      notice, this list of conditions and the following disclaimer.
16   2. Redistributions in binary form must reproduce the above copyright
17      notice, this list of conditions and the following disclaimer in the
18      documentation and/or other materials provided with the distribution.
19   3. Neither the name of the University nor the names of its
20      contributors may be used to endorse or promote products derived
21      from this software without specific prior written permission.
22 
23   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
24   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 
35   $Id: authgss_prot.c,v 1.18 2000/09/01 04:14:03 dugsong Exp $
36 */
37 
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
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 	n = m_getptr(m, len, &off);
212 	if (n) {
213 		n->m_len = off;
214 		if (n->m_next) {
215 			m_freem(n->m_next);
216 			n->m_next = NULL;
217 		}
218 	}
219 }
220 
221 bool_t
222 xdr_rpc_gss_unwrap_data(struct mbuf **resultsp,
223 			gss_ctx_id_t ctx, gss_qop_t qop,
224 			rpc_gss_service_t svc, u_int seq)
225 {
226 	struct mbuf	*results, *message, *mic;
227 	uint32_t	len, cklen;
228 	OM_uint32	maj_stat, min_stat;
229 	u_int		seq_num, conf_state, qop_state;
230 
231 	results = *resultsp;
232 	*resultsp = NULL;
233 
234 	message = NULL;
235 	if (svc == rpc_gss_svc_integrity) {
236 		/*
237 		 * Extract the seq+message part. Remember that there
238 		 * may be extra RPC padding in the checksum. The
239 		 * message part is RPC encoded already so no
240 		 * padding.
241 		 */
242 		len = get_uint32(&results);
243 		message = results;
244 		results = m_split(results, len, M_WAITOK);
245 		if (!results) {
246 			m_freem(message);
247 			return (FALSE);
248 		}
249 
250 		/*
251 		 * Extract the MIC and make it contiguous.
252 		 */
253 		cklen = get_uint32(&results);
254 		KASSERT(cklen <= MHLEN, ("unexpected large GSS-API checksum"));
255 		mic = results;
256 		if (cklen > mic->m_len)
257 			mic = m_pullup(mic, cklen);
258 		if (cklen != RNDUP(cklen))
259 			m_trim(mic, cklen);
260 
261 		/* Verify checksum and QOP. */
262 		maj_stat = gss_verify_mic_mbuf(&min_stat, ctx,
263 		    message, mic, &qop_state);
264 		m_freem(mic);
265 
266 		if (maj_stat != GSS_S_COMPLETE || qop_state != qop) {
267 			m_freem(message);
268 			rpc_gss_log_status("gss_verify_mic", NULL,
269 			    maj_stat, min_stat);
270 			return (FALSE);
271 		}
272 	} else if (svc == rpc_gss_svc_privacy) {
273 		/* Decode databody_priv. */
274 		len = get_uint32(&results);
275 
276 		/* Decrypt databody. */
277 		message = results;
278 		if (len != RNDUP(len))
279 			m_trim(message, len);
280 		maj_stat = gss_unwrap_mbuf(&min_stat, ctx, &message,
281 		    &conf_state, &qop_state);
282 
283 		/* Verify encryption and QOP. */
284 		if (maj_stat != GSS_S_COMPLETE) {
285 			rpc_gss_log_status("gss_unwrap", NULL,
286 			    maj_stat, min_stat);
287 			return (FALSE);
288 		}
289 		if (qop_state != qop || conf_state != TRUE) {
290 			m_freem(results);
291 			return (FALSE);
292 		}
293 	}
294 
295 	/* Decode rpc_gss_data_t (sequence number + arguments). */
296 	seq_num = get_uint32(&message);
297 
298 	/* Verify sequence number. */
299 	if (seq_num != seq) {
300 		rpc_gss_log_debug("wrong sequence number in databody");
301 		m_freem(message);
302 		return (FALSE);
303 	}
304 
305 	*resultsp = message;
306 	return (TRUE);
307 }
308 
309 #ifdef DEBUG
310 #include <machine/stdarg.h>
311 
312 void
313 rpc_gss_log_debug(const char *fmt, ...)
314 {
315 	va_list ap;
316 
317 	va_start(ap, fmt);
318 	printf("rpcsec_gss: ");
319 	vprintf(fmt, ap);
320 	printf("\n");
321 	va_end(ap);
322 }
323 
324 void
325 rpc_gss_log_status(const char *m, gss_OID mech, OM_uint32 maj_stat, OM_uint32 min_stat)
326 {
327 	OM_uint32 min;
328 	gss_buffer_desc msg;
329 	int msg_ctx = 0;
330 
331 	printf("rpcsec_gss: %s: ", m);
332 
333 	gss_display_status(&min, maj_stat, GSS_C_GSS_CODE, GSS_C_NULL_OID,
334 			   &msg_ctx, &msg);
335 	printf("%s - ", (char *)msg.value);
336 	gss_release_buffer(&min, &msg);
337 
338 	gss_display_status(&min, min_stat, GSS_C_MECH_CODE, mech,
339 			   &msg_ctx, &msg);
340 	printf("%s\n", (char *)msg.value);
341 	gss_release_buffer(&min, &msg);
342 }
343 
344 #else
345 
346 void
347 rpc_gss_log_debug(__unused const char *fmt, ...)
348 {
349 }
350 
351 void
352 rpc_gss_log_status(__unused const char *m, __unused gss_OID mech,
353     __unused OM_uint32 maj_stat, __unused OM_uint32 min_stat)
354 {
355 }
356 
357 #endif
358 
359 
360