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
49bool_t
50xdr_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
65bool_t
66xdr_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
84bool_t
85xdr_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
95bool_t
96xdr_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
157bool_t
158xdr_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
243void
244log_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
255void
256log_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
277void
278log_debug(__unused const char *fmt, ...)
279{
280}
281
282void
283log_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