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 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright (c) 2016 by Delphix. All rights reserved.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/note.h>
31 #include "dh_gssapi.h"
32 
33 /*
34  * This module contains the implementation of the gssapi context support
35  * routines for the Diffie-Hellman mechanism.
36  *
37  * The GSS routines that are supported by this module are:
38  *	gss_context_time
39  *	gss_delete_sec_context
40  *	gss_inquire_context
41  *	gss_wrap_size_limit
42  *
43  * The following routines are not supported for the Diffie-Hellman
44  * Mechanism at this time.
45  *	gss_export_sec_context
46  *	gss_import_sec_context
47  *
48  * The following routine is not supported since it is obsolete in version 2
49  * of the GSS-API.
50  *	gss_process_context_token.
51  *
52  * Note that support for gss_init_sec_context and gss_accept_sec_context is
53  * found in context_establish.c
54  */
55 
56 OM_uint32
__dh_gss_context_time(void * ctx,OM_uint32 * minor,gss_ctx_id_t context,OM_uint32 * time_remaining)57 __dh_gss_context_time(void *ctx, /* Mechanism context (not used) */
58 		    OM_uint32 * minor, /* GSS minor status */
59 		    gss_ctx_id_t context, /* GSS context handle */
60 		    OM_uint32* time_remaining /* Time remaining */)
61 
62 {
63 _NOTE(ARGUNUSED(ctx))
64 	/* Context is a dh context */
65 	dh_gss_context_t cntx = (dh_gss_context_t)context;
66 	time_t now = time(0);
67 
68 	if (minor == 0)
69 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
70 
71 	if (time_remaining == 0)
72 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
73 
74 	/* Validate context */
75 	if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
76 		return (GSS_S_NO_CONTEXT);
77 
78 	/* See if it is always valid */
79 	if (cntx->expire == (time_t)GSS_C_INDEFINITE) {
80 		*time_remaining = GSS_C_INDEFINITE;
81 		return (GSS_S_COMPLETE);
82 	}
83 
84 	/* Calculate the remainning time */
85 	*time_remaining = (now < cntx->expire) ? cntx->expire - now : 0;
86 
87 	/* Return expired if there is no time left */
88 	return (*time_remaining ? GSS_S_COMPLETE : GSS_S_CONTEXT_EXPIRED);
89 }
90 
91 /*
92  * Delete a Diffie-Hellman context that is pointed to by context.
93  * On a successfull return *context will be NULL.
94  */
95 
96 OM_uint32
__dh_gss_delete_sec_context(void * ctx,OM_uint32 * minor,gss_ctx_id_t * context,gss_buffer_t token)97 __dh_gss_delete_sec_context(void *ctx, /* Mechanism context */
98 			    OM_uint32 *minor, /* Mechanism status */
99 			    gss_ctx_id_t *context, /* GSS context */
100 			    gss_buffer_t token /* GSS token */)
101 {
102 _NOTE(ARGUNUSED(ctx))
103 
104 	dh_gss_context_t cntx;
105 
106 	if (context == 0)
107 		return (GSS_S_CALL_INACCESSIBLE_READ |
108 			GSS_S_CALL_INACCESSIBLE_WRITE);
109 
110 	/* context is a Diffie-Hellman context */
111 	cntx = (dh_gss_context_t)*context;
112 
113 	if (minor == 0)
114 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
115 
116 	/*
117 	 * If token then set the length to zero value to zero to indicate
118 	 * We indicat a null token since we don't need to send a token to
119 	 * the other side.
120 	 */
121 
122 	if (token) {
123 		token->length = 0;
124 		token->value = NULL;
125 	}
126 
127 	/* Deleting a null context is OK */
128 	if (cntx == NULL)
129 		return (GSS_S_COMPLETE);
130 
131 	/* Validate the context */
132 	if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
133 		return (GSS_S_NO_CONTEXT);
134 
135 	/* Zero out the session keys! */
136 	memset(cntx->keys, 0, cntx->no_keys * sizeof (des_block));
137 
138 	/* Unregister the context */
139 	*minor = __dh_remove_context(cntx);
140 
141 	/* Free storage */
142 	__dh_destroy_seq_hist(cntx);
143 	free(cntx->remote);
144 	free(cntx->local);
145 	Free(cntx->keys);
146 	Free(cntx);
147 
148 	/* Set context to NULL */
149 	*context = NULL;
150 
151 	return (GSS_S_COMPLETE);
152 }
153 
154 
155 /*
156  * Diffie-Hellman mechanism currently does not support exporting and importing
157  * gss contexts.
158  */
159 
160 OM_uint32
161 /*ARGSUSED*/
__dh_gss_export_sec_context(void * ctx,OM_uint32 * minor,gss_ctx_id_t * context,gss_buffer_t token)162 __dh_gss_export_sec_context(void *ctx, OM_uint32 *minor,
163 			    gss_ctx_id_t *context, gss_buffer_t token)
164 {
165 	return (GSS_S_UNAVAILABLE);
166 }
167 
168 OM_uint32
169 /*ARGSUSED*/
__dh_gss_import_sec_context(void * ctx,OM_uint32 * minor,gss_buffer_t token,gss_ctx_id_t * context)170 __dh_gss_import_sec_context(void * ctx, OM_uint32 *minor,
171 			    gss_buffer_t token, gss_ctx_id_t *context)
172 {
173 	return (GSS_S_UNAVAILABLE);
174 }
175 
176 /*
177  * Get the state of a Diffie-Hellman context
178  */
179 
180 OM_uint32
__dh_gss_inquire_context(void * ctx,OM_uint32 * minor,gss_ctx_id_t context,gss_name_t * initiator,gss_name_t * acceptor,OM_uint32 * time_rec,gss_OID * mech,OM_uint32 * flags_rec,int * local,int * open)181 __dh_gss_inquire_context(void *ctx, /* Mechanism context */
182 			OM_uint32 *minor, /* Mechanism status */
183 			gss_ctx_id_t context, /* GSS context */
184 			gss_name_t *initiator, /* Name of initiator */
185 			gss_name_t *acceptor, /* Name of acceptor */
186 			OM_uint32 *time_rec, /* Amount of time left */
187 			gss_OID *mech, /* return OID of mechanism */
188 			OM_uint32 *flags_rec, /* flags set on context */
189 			int *local, /* True if we're the initiator */
190 			int *open /* True if the context is established */)
191 {
192 	dh_gss_context_t cntx;
193 	OM_uint32 stat = GSS_S_COMPLETE;
194 	OM_uint32 t;
195 
196 	/* context is a Diffie-Hellman */
197 	cntx = (dh_gss_context_t)context;
198 
199 	/* Validate the context */
200 	if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
201 		return (GSS_S_NO_CONTEXT);
202 
203 	/* If the caller wants the mechanism OID set *mech to if we can */
204 	if (mech) {
205 		if (ctx == 0) {
206 			*mech = GSS_C_NO_OID;
207 			return (GSS_S_CALL_INACCESSIBLE_READ);
208 		}
209 		else
210 			*mech = ((dh_context_t)ctx)->mech;
211 	}
212 
213 	/* set t to be the time left on the context */
214 	if (cntx->expire == GSS_C_INDEFINITE)
215 		t = GSS_C_INDEFINITE;
216 	else {
217 		time_t now = time(0);
218 		t = now > cntx->expire ? 0 : (OM_uint32)(cntx->expire - now);
219 	}
220 
221 	/* If the caller wants the initiator set *initiator to it. */
222 	if (initiator) {
223 		dh_principal p = cntx->initiate ? cntx->local : cntx->remote;
224 		*initiator = (gss_name_t)strdup(p);
225 	}
226 
227 	/* If the callers wants the acceptor set *acceptor to it. */
228 	if (acceptor) {
229 		dh_principal p = cntx->initiate ? cntx->remote : cntx->local;
230 		*acceptor = (gss_name_t)strdup(p);
231 	}
232 
233 	/* If the caller wants the time remaining set *time_rec to t */
234 	if (time_rec)
235 		*time_rec = t;
236 
237 
238 	/* Return the flags in flags_rec if set */
239 	if (flags_rec)
240 		*flags_rec = cntx->flags;
241 
242 	/* ditto for local */
243 	if (local)
244 		*local = cntx->initiate;
245 
246 	/* ditto for open */
247 	if (open)
248 		*open = (cntx->state == ESTABLISHED);
249 
250 
251 	/* return GSS_S_CONTEXT_EXPIRED if no time is left on the context */
252 	return ((t == 0 ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE) | stat);
253 }
254 
255 /*
256  * __dh_gss_process_context_token.
257  * This routine is not implemented. It is depricated in version 2.
258  */
259 
260 OM_uint32
261 /*ARGSUSED*/
__dh_gss_process_context_token(void * ctx,OM_uint32 * minor,gss_ctx_id_t context,gss_buffer_t token)262 __dh_gss_process_context_token(void *ctx, OM_uint32 *minor,
263     gss_ctx_id_t context, gss_buffer_t token)
264 {
265 	return (GSS_S_UNAVAILABLE);
266 }
267 
268 /*
269  * This implements the gss_wrap_size_limit entry point for Diffie-Hellman
270  * mechanism. See RFC 2078 for details. The idea here is for a context,
271  * qop, whether confidentiality is specified, and an output size, return
272  * the maximum input size that will fit in the given output size. Typically
273  * the output size would be the MTU of the higher level protocol using the
274  * GSS-API.
275  */
276 
277 OM_uint32
__dh_gss_wrap_size_limit(void * ctx,OM_uint32 * minor,gss_ctx_id_t context,int conf_req,gss_qop_t qop_req,OM_uint32 output_size,OM_uint32 * input_size)278 __dh_gss_wrap_size_limit(void *ctx, /* Mechanism context (not used) */
279 			OM_uint32 *minor, /* Mechanism status */
280 			gss_ctx_id_t context, /* GSS context handle */
281 			int conf_req, /* True if confidentiality is wanted */
282 			gss_qop_t qop_req, /* Requested QOP */
283 			OM_uint32 output_size, /* The maximum ouput size */
284 			OM_uint32 *input_size /* Input size returned */)
285 {
286 _NOTE(ARGUNUSED(ctx))
287 	OM_uint32 major, stat = GSS_S_COMPLETE;
288 	unsigned int msgsize, sigsize, pad = 1, size;
289 	dh_token_desc token;
290 	dh_wrap_t wrap = &token.ver.dh_version_u.body.dh_token_body_desc_u.seal;
291 	OM_uint32 left;
292 
293 	if (input_size == 0)
294 		stat = GSS_S_CALL_INACCESSIBLE_WRITE;
295 
296 	/* We check for valid unexpired context by calling gss_context_time. */
297 	if ((major = stat | __dh_gss_context_time(ctx, minor, context, &left))
298 	    != GSS_S_COMPLETE)
299 		return (major | stat);
300 
301 	/* Find the signature size for this qop. */
302 	if ((*minor = __get_sig_size(qop_req, &sigsize)) != DH_SUCCESS)
303 		return (GSS_S_BAD_QOP | stat);
304 
305 	/* Just return if we can't give the caller what it asked for. */
306 	if (stat)
307 		return (stat);
308 
309 	/*
310 	 * If we requested confidentiality, get the cipher pad for the
311 	 * requested qop. Since we can't support privacy the cipher pad
312 	 * is always 1.
313 	 */
314 	if (conf_req)
315 		pad = 1;
316 
317 	/*
318 	 * Set up an empty wrap token to calculate header and signature
319 	 * overhead.
320 	 */
321 
322 	token.ver.verno = DH_PROTO_VERSION;
323 	token.ver.dh_version_u.body.type = DH_WRAP;
324 	wrap->mic.qop = qop_req;
325 	wrap->mic.seqnum = 0;
326 	wrap->mic.client_flag = 0;
327 	wrap->body.body_len = 0;
328 	wrap->body.body_val = 0;
329 	token.verifier.dh_signature_len = sigsize;
330 	token.verifier.dh_signature_val = 0;
331 
332 	/* This is the size of an empy wrap token */
333 	size =  xdr_sizeof((xdrproc_t)xdr_dh_token_desc, (void *)&token);
334 
335 	/* This is the amount of space left to put our message. */
336 	msgsize = (output_size > size) ? output_size - size : 0;
337 
338 	/* XDR needs to pad to a four byte boundry */
339 	msgsize = (msgsize / 4) * 4;
340 
341 	/* We need to pad to pad bytes for encryption (=1 if conf_req = 0) */
342 	msgsize = (msgsize / pad) * pad;
343 
344 	/*
345 	 * The serialization of the inner message includes
346 	 * the original length.
347 	 */
348 
349 	msgsize = (msgsize > sizeof (uint_t)) ? msgsize - sizeof (uint_t) : 0;
350 
351 	/*
352 	 * We now have the space for the inner wrap message, which is also
353 	 * XDR encoded and is padded to a four byte boundry.
354 	 */
355 
356 	msgsize = (msgsize / 4) * 4;
357 
358 	*input_size = msgsize;
359 
360 	return (GSS_S_COMPLETE);
361 }
362