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