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