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 /* $FreeBSD$ */
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdarg.h>
42 #include <string.h>
43 #include <rpc/rpc.h>
44 #include <rpc/rpcsec_gss.h>
45 #include "rpcsec_gss_int.h"
46 
47 #define MAX_GSS_SIZE	10240	/* XXX */
48 
49 bool_t
50 xdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *p)
51 {
52 	char *val;
53 	u_int len;
54 	bool_t ret;
55 
56 	val = p->value;
57 	len = p->length;
58 	ret = xdr_bytes(xdrs, &val, &len, MAX_GSS_SIZE);
59 	p->value = val;
60 	p->length = len;
61 
62 	return (ret);
63 }
64 
65 bool_t
66 xdr_rpc_gss_cred(XDR *xdrs, struct rpc_gss_cred *p)
67 {
68 	enum_t proc, svc;
69 	bool_t ret;
70 
71 	proc = p->gc_proc;
72 	svc = p->gc_svc;
73 	ret = (xdr_u_int(xdrs, &p->gc_version) &&
74 	    xdr_enum(xdrs, &proc) &&
75 	    xdr_u_int(xdrs, &p->gc_seq) &&
76 	    xdr_enum(xdrs, &svc) &&
77 	    xdr_gss_buffer_desc(xdrs, &p->gc_handle));
78 	p->gc_proc = proc;
79 	p->gc_svc = svc;
80 
81 	return (ret);
82 }
83 
84 bool_t
85 xdr_rpc_gss_init_res(XDR *xdrs, struct rpc_gss_init_res *p)
86 {
87 
88 	return (xdr_gss_buffer_desc(xdrs, &p->gr_handle) &&
89 	    xdr_u_int(xdrs, &p->gr_major) &&
90 	    xdr_u_int(xdrs, &p->gr_minor) &&
91 	    xdr_u_int(xdrs, &p->gr_win) &&
92 	    xdr_gss_buffer_desc(xdrs, &p->gr_token));
93 }
94 
95 bool_t
96 xdr_rpc_gss_wrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr,
97 		      gss_ctx_id_t ctx, gss_qop_t qop,
98 		      rpc_gss_service_t svc, u_int seq)
99 {
100 	gss_buffer_desc	databuf, wrapbuf;
101 	OM_uint32	maj_stat, min_stat;
102 	int		start, end, conf_state;
103 	u_int		len;
104 	bool_t		xdr_stat;
105 
106 	/* Skip databody length. */
107 	start = XDR_GETPOS(xdrs);
108 	XDR_SETPOS(xdrs, start + 4);
109 
110 	/* Marshal rpc_gss_data_t (sequence number + arguments). */
111 	if (!xdr_u_int(xdrs, &seq) || !xdr_func(xdrs, xdr_ptr))
112 		return (FALSE);
113 	end = XDR_GETPOS(xdrs);
114 
115 	/* Set databuf to marshalled rpc_gss_data_t. */
116 	databuf.length = end - start - 4;
117 	XDR_SETPOS(xdrs, start + 4);
118 	databuf.value = XDR_INLINE(xdrs, databuf.length);
119 
120 	xdr_stat = FALSE;
121 
122 	if (svc == rpc_gss_svc_integrity) {
123 		/* Marshal databody_integ length. */
124 		XDR_SETPOS(xdrs, start);
125 		len = databuf.length;
126 		if (!xdr_u_int(xdrs, &len))
127 			return (FALSE);
128 
129 		/* Checksum rpc_gss_data_t. */
130 		maj_stat = gss_get_mic(&min_stat, ctx, qop,
131 				       &databuf, &wrapbuf);
132 		if (maj_stat != GSS_S_COMPLETE) {
133 			log_debug("gss_get_mic failed");
134 			return (FALSE);
135 		}
136 		/* Marshal checksum. */
137 		XDR_SETPOS(xdrs, end);
138 		xdr_stat = xdr_gss_buffer_desc(xdrs, &wrapbuf);
139 		gss_release_buffer(&min_stat, &wrapbuf);
140 	}
141 	else if (svc == rpc_gss_svc_privacy) {
142 		/* Encrypt rpc_gss_data_t. */
143 		maj_stat = gss_wrap(&min_stat, ctx, TRUE, qop, &databuf,
144 				    &conf_state, &wrapbuf);
145 		if (maj_stat != GSS_S_COMPLETE) {
146 			log_status("gss_wrap", NULL, maj_stat, min_stat);
147 			return (FALSE);
148 		}
149 		/* Marshal databody_priv. */
150 		XDR_SETPOS(xdrs, start);
151 		xdr_stat = xdr_gss_buffer_desc(xdrs, &wrapbuf);
152 		gss_release_buffer(&min_stat, &wrapbuf);
153 	}
154 	return (xdr_stat);
155 }
156 
157 bool_t
158 xdr_rpc_gss_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr,
159 			gss_ctx_id_t ctx, gss_qop_t qop,
160 			rpc_gss_service_t svc, u_int seq)
161 {
162 	XDR		tmpxdrs;
163 	gss_buffer_desc	databuf, wrapbuf;
164 	OM_uint32	maj_stat, min_stat;
165 	u_int		seq_num, conf_state, qop_state;
166 	bool_t		xdr_stat;
167 
168 	if (xdr_func == (xdrproc_t) xdr_void || xdr_ptr == NULL)
169 		return (TRUE);
170 
171 	memset(&databuf, 0, sizeof(databuf));
172 	memset(&wrapbuf, 0, sizeof(wrapbuf));
173 
174 	if (svc == rpc_gss_svc_integrity) {
175 		/* Decode databody_integ. */
176 		if (!xdr_gss_buffer_desc(xdrs, &databuf)) {
177 			log_debug("xdr decode databody_integ failed");
178 			return (FALSE);
179 		}
180 		/* Decode checksum. */
181 		if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) {
182 			mem_free(databuf.value, databuf.length);
183 			log_debug("xdr decode checksum failed");
184 			return (FALSE);
185 		}
186 		/* Verify checksum and QOP. */
187 		maj_stat = gss_verify_mic(&min_stat, ctx, &databuf,
188 					  &wrapbuf, &qop_state);
189 		mem_free(wrapbuf.value, wrapbuf.length);
190 
191 		if (maj_stat != GSS_S_COMPLETE || qop_state != qop) {
192 			mem_free(databuf.value, databuf.length);
193 			log_status("gss_verify_mic", NULL, maj_stat, min_stat);
194 			return (FALSE);
195 		}
196 	} else if (svc == rpc_gss_svc_privacy) {
197 		/* Decode databody_priv. */
198 		if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) {
199 			log_debug("xdr decode databody_priv failed");
200 			return (FALSE);
201 		}
202 		/* Decrypt databody. */
203 		maj_stat = gss_unwrap(&min_stat, ctx, &wrapbuf, &databuf,
204 				      &conf_state, &qop_state);
205 
206 		mem_free(wrapbuf.value, wrapbuf.length);
207 
208 		/* Verify encryption and QOP. */
209 		if (maj_stat != GSS_S_COMPLETE || qop_state != qop ||
210 			conf_state != TRUE) {
211 			gss_release_buffer(&min_stat, &databuf);
212 			log_status("gss_unwrap", NULL, maj_stat, min_stat);
213 			return (FALSE);
214 		}
215 	}
216 	/* Decode rpc_gss_data_t (sequence number + arguments). */
217 	xdrmem_create(&tmpxdrs, databuf.value, databuf.length, XDR_DECODE);
218 	xdr_stat = (xdr_u_int(&tmpxdrs, &seq_num) &&
219 	    xdr_func(&tmpxdrs, xdr_ptr));
220 	XDR_DESTROY(&tmpxdrs);
221 
222 	/*
223 	 * Integrity service allocates databuf via XDR so free it the
224 	 * same way.
225 	 */
226 	if (svc == rpc_gss_svc_integrity) {
227 		xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &databuf);
228 	} else {
229 		gss_release_buffer(&min_stat, &databuf);
230 	}
231 
232 	/* Verify sequence number. */
233 	if (xdr_stat == TRUE && seq_num != seq) {
234 		log_debug("wrong sequence number in databody");
235 		return (FALSE);
236 	}
237 	return (xdr_stat);
238 }
239 
240 #ifdef DEBUG
241 #include <ctype.h>
242 
243 void
244 log_debug(const char *fmt, ...)
245 {
246 	va_list ap;
247 
248 	va_start(ap, fmt);
249 	fprintf(stderr, "rpcsec_gss: ");
250 	vfprintf(stderr, fmt, ap);
251 	fprintf(stderr, "\n");
252 	va_end(ap);
253 }
254 
255 void
256 log_status(const char *m, gss_OID mech, OM_uint32 maj_stat, OM_uint32 min_stat)
257 {
258 	OM_uint32 min;
259 	gss_buffer_desc msg;
260 	int msg_ctx = 0;
261 
262 	fprintf(stderr, "rpcsec_gss: %s: ", m);
263 
264 	gss_display_status(&min, maj_stat, GSS_C_GSS_CODE, GSS_C_NULL_OID,
265 			   &msg_ctx, &msg);
266 	fprintf(stderr, "%s - ", (char *)msg.value);
267 	gss_release_buffer(&min, &msg);
268 
269 	gss_display_status(&min, min_stat, GSS_C_MECH_CODE, mech,
270 			   &msg_ctx, &msg);
271 	fprintf(stderr, "%s\n", (char *)msg.value);
272 	gss_release_buffer(&min, &msg);
273 }
274 
275 #else
276 
277 void
278 log_debug(__unused const char *fmt, ...)
279 {
280 }
281 
282 void
283 log_status(__unused const char *m, __unused gss_OID mech,
284     __unused OM_uint32 maj_stat, __unused OM_uint32 min_stat)
285 {
286 }
287 
288 #endif
289 
290 
291