xref: /illumos-gate/usr/src/uts/common/rpc/sec_gss/rpcsec_gss_misc.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1996,1997,1999,2002-2003 Sun Microsystems, Inc.
24  * All rights reserved.  Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
31  *
32  * $Header:
33  * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi_misc.c,v 1.10
34  * 1994/10/27 12:39:23 jik Exp $
35  */
36 
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/stream.h>
40 #include <sys/strsubr.h>
41 #include <sys/cmn_err.h>
42 #include <gssapi/gssapi.h>
43 #include <rpc/rpc.h>
44 #include <rpc/rpcsec_defs.h>
45 
46 /*
47  * The initial allocation size for dynamic allocation.
48  */
49 #define	CKU_INITSIZE    2048
50 
51 /*
52  * The size of additional allocations, if required.  It is larger to
53  * reduce the number of actual allocations.
54  */
55 #define	CKU_ALLOCSIZE   8192
56 
57 
58 /*
59  * Miscellaneous XDR routines.
60  */
61 bool_t
62 __xdr_gss_buf(xdrs, buf)
63 	XDR		*xdrs;
64 	gss_buffer_t	buf;
65 {
66 	uint_t cast_len, bound_len;
67 
68 	/*
69 	 * We go through this contortion because size_t is a now a ulong,
70 	 * GSS-API uses ulongs.
71 	 */
72 
73 	if (xdrs->x_op != XDR_DECODE) {
74 		bound_len = cast_len = (uint_t)buf->length;
75 	} else {
76 		bound_len = (uint_t)-1;
77 	}
78 
79 	if (xdr_bytes(xdrs, (char **)&buf->value, &cast_len,
80 	    bound_len) == TRUE) {
81 		if (xdrs->x_op == XDR_DECODE)
82 			buf->length = cast_len;
83 
84 		return (TRUE);
85 	}
86 
87 	return (FALSE);
88 }
89 
90 bool_t
91 __xdr_rpc_gss_creds(xdrs, creds)
92 	XDR			*xdrs;
93 	rpc_gss_creds		*creds;
94 {
95 	if (!xdr_u_int(xdrs, (uint_t *)&creds->version) ||
96 				!xdr_u_int(xdrs, (uint_t *)&creds->gss_proc) ||
97 				!xdr_u_int(xdrs, (uint_t *)&creds->seq_num) ||
98 				!xdr_u_int(xdrs, (uint_t *)&creds->service) ||
99 				!__xdr_gss_buf(xdrs, &creds->ctx_handle))
100 		return (FALSE);
101 	return (TRUE);
102 }
103 
104 bool_t
105 __xdr_rpc_gss_init_arg(xdrs, init_arg)
106 	XDR			*xdrs;
107 	rpc_gss_init_arg	*init_arg;
108 {
109 	if (!__xdr_gss_buf(xdrs, init_arg))
110 		return (FALSE);
111 	return (TRUE);
112 }
113 
114 bool_t
115 __xdr_rpc_gss_init_res(xdrs, init_res)
116 	XDR			*xdrs;
117 	rpc_gss_init_res	*init_res;
118 {
119 	if (!__xdr_gss_buf(xdrs, &init_res->ctx_handle) ||
120 			!xdr_u_int(xdrs, (uint_t *)&init_res->gss_major) ||
121 			!xdr_u_int(xdrs, (uint_t *)&init_res->gss_minor) ||
122 			!xdr_u_int(xdrs, (uint_t *)&init_res->seq_window) ||
123 			!__xdr_gss_buf(xdrs, &init_res->token))
124 		return (FALSE);
125 	return (TRUE);
126 }
127 
128 /*
129  * Generic routine to wrap data used by client and server sides.
130  */
131 bool_t
132 __rpc_gss_wrap_data(service, qop, context, seq_num, out_xdrs,
133 			xdr_func, xdr_ptr)
134 	OM_uint32		qop;
135 	rpc_gss_service_t	service;
136 	gss_ctx_id_t		context;
137 	uint_t			seq_num;
138 	XDR			*out_xdrs;
139 	bool_t			(*xdr_func)();
140 	caddr_t			xdr_ptr;
141 {
142 	OM_uint32		major, minor;
143 	gss_buffer_desc		in_buf, out_buf;
144 	XDR			temp_xdrs;
145 	char			*mp;
146 /* EXPORT DELETE START */
147 	bool_t			conf_state;
148 /* EXPORT DELETE END */
149 	bool_t			ret = FALSE;
150 	int			size;
151 
152 	/*
153 	 * Create a temporary XDR/buffer to hold the data to be wrapped.
154 	 * We need an extra bit for the sequence number serialized first.
155 	 */
156 	size = xdr_sizeof(xdr_func, xdr_ptr) + BYTES_PER_XDR_UNIT;
157 	mp = kmem_alloc(size, KM_SLEEP);
158 	out_buf.length = 0;
159 
160 	xdrmem_create(&temp_xdrs, mp, size, XDR_ENCODE);
161 
162 	/*
163 	 * serialize the sequence number into tmp memory
164 	 */
165 	if (!xdr_u_int(&temp_xdrs, &seq_num))
166 		goto fail;
167 
168 	/*
169 	 * serialize the arguments into tmp memory
170 	 */
171 	if (!(*xdr_func)(&temp_xdrs, xdr_ptr))
172 		goto fail;
173 
174 	/*
175 	 * Data to be wrapped goes in in_buf.  If privacy is used,
176 	 * out_buf will have wrapped data (in_buf will no longer be
177 	 * needed).  If integrity is used, out_buf will have checksum
178 	 * which will follow the data in in_buf.
179 	 */
180 	in_buf.length = xdr_getpos(&temp_xdrs);
181 	in_buf.value = (char *)temp_xdrs.x_base;
182 
183 	switch (service) {
184 	case rpc_gss_svc_privacy:
185 
186 /* EXPORT DELETE START */
187 		if ((major = kgss_seal(&minor, context, TRUE, qop, &in_buf,
188 				&conf_state, &out_buf)) != GSS_S_COMPLETE) {
189 			RPCGSS_LOG1(1, "rpc_gss_wrap: kgss_seal failed."
190 				"major = %x, minor = %x", major, minor);
191 			goto fail;
192 		}
193 		in_buf.length = 0;	/* in_buf not needed */
194 		if (!conf_state)
195 /* EXPORT DELETE END */
196 			goto fail;
197 /* EXPORT DELETE START */
198 		break;
199 /* EXPORT DELETE END */
200 	case rpc_gss_svc_integrity:
201 		if ((major = kgss_sign(&minor, context, qop, &in_buf,
202 				&out_buf)) != GSS_S_COMPLETE) {
203 			RPCGSS_LOG1(1, "rpc_gss_wrap: kgss_sign failed."
204 				"major = %x, minor = %x", major, minor);
205 			goto fail;
206 		}
207 		break;
208 	default:
209 		goto fail;
210 	}
211 
212 	/*
213 	 * write out in_buf and out_buf as needed
214 	 */
215 	if (in_buf.length != 0) {
216 		if (!__xdr_gss_buf(out_xdrs, &in_buf))
217 			goto fail;
218 	}
219 
220 	if (!__xdr_gss_buf(out_xdrs, &out_buf))
221 		goto fail;
222 	ret = TRUE;
223 fail:
224 	kmem_free(mp, size);
225 	if (out_buf.length != 0)
226 		(void) gss_release_buffer(&minor, &out_buf);
227 	return (ret);
228 }
229 
230 /*
231  * Generic routine to unwrap data used by client and server sides.
232  */
233 bool_t
234 __rpc_gss_unwrap_data(service, context, seq_num, qop_check, in_xdrs,
235 			xdr_func, xdr_ptr)
236 	rpc_gss_service_t	service;
237 	gss_ctx_id_t		context;
238 	uint_t			seq_num;
239 	OM_uint32		qop_check;
240 	XDR			*in_xdrs;
241 	bool_t			(*xdr_func)();
242 	caddr_t			xdr_ptr;
243 {
244 	gss_buffer_desc		in_buf, out_buf;
245 	XDR			temp_xdrs;
246 	uint_t			seq_num2;
247 	bool_t			conf = FALSE;
248 	OM_uint32		major = GSS_S_COMPLETE, minor = 0;
249 	int			qop = 0;
250 
251 	in_buf.value = NULL;
252 	out_buf.value = NULL;
253 
254 	/*
255 	 * Pull out wrapped data.  For privacy service, this is the
256 	 * encrypted data.  For integrity service, this is the data
257 	 * followed by a checksum.
258 	 */
259 	if (!__xdr_gss_buf(in_xdrs, &in_buf)) {
260 		return (FALSE);
261 	}
262 
263 	if (service == rpc_gss_svc_privacy) {
264 		major = GSS_S_FAILURE;
265 /* EXPORT DELETE START */
266 		major = kgss_unseal(&minor, context, &in_buf, &out_buf, &conf,
267 					&qop);
268 /* EXPORT DELETE END */
269 		kmem_free(in_buf.value, in_buf.length);
270 		if (major != GSS_S_COMPLETE) {
271 			RPCGSS_LOG1(1, "rpc_gss_unwrap: kgss_unseal failed."
272 				"major = %x, minor = %x", major, minor);
273 			return (FALSE);
274 		}
275 		/*
276 		 * Keep the returned token (unencrypted data) in in_buf.
277 		 */
278 		in_buf.length = out_buf.length;
279 		in_buf.value = out_buf.value;
280 
281 		/*
282 		 * If privacy was not used, or if QOP is not what we are
283 		 * expecting, fail.
284 		 */
285 		if (!conf || qop != qop_check)
286 			goto fail;
287 
288 	} else if (service == rpc_gss_svc_integrity) {
289 		if (!__xdr_gss_buf(in_xdrs, &out_buf)) {
290 			return (FALSE);
291 		}
292 		major = kgss_verify(&minor, context, &in_buf, &out_buf,
293 				&qop);
294 		kmem_free(out_buf.value, out_buf.length);
295 		if (major != GSS_S_COMPLETE) {
296 			kmem_free(in_buf.value, in_buf.length);
297 			RPCGSS_LOG1(1, "rpc_gss_unwrap: kgss_verify failed."
298 				"major = %x, minor = %x", major, minor);
299 			return (FALSE);
300 		}
301 
302 		/*
303 		 * If QOP is not what we are expecting, fail.
304 		 */
305 		if (qop != qop_check)
306 			goto fail;
307 	}
308 
309 	xdrmem_create(&temp_xdrs, in_buf.value, in_buf.length, XDR_DECODE);
310 
311 	/*
312 	 * The data consists of the sequence number followed by the
313 	 * arguments.  Make sure sequence number is what we are
314 	 * expecting (i.e., the value in the header).
315 	 */
316 	if (!xdr_u_int(&temp_xdrs, &seq_num2))
317 		goto fail;
318 	if (seq_num2 != seq_num)
319 		goto fail;
320 
321 	/*
322 	 * Deserialize the arguments into xdr_ptr, and release in_buf.
323 	 */
324 	if (!(*xdr_func)(&temp_xdrs, xdr_ptr)) {
325 		goto fail;
326 	}
327 
328 	if (service == rpc_gss_svc_privacy)
329 		(void) gss_release_buffer(&minor, &in_buf);
330 	else
331 		kmem_free(in_buf.value, in_buf.length);
332 	XDR_DESTROY(&temp_xdrs);
333 	return (TRUE);
334 fail:
335 	XDR_DESTROY(&temp_xdrs);
336 	if (service == rpc_gss_svc_privacy)
337 		(void) gss_release_buffer(&minor, &in_buf);
338 	else
339 		kmem_free(in_buf.value, in_buf.length);
340 	return (FALSE);
341 }
342