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