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  *	MICwrap.c
24  *
25  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  *
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <sys/note.h>
33 #include "dh_gssapi.h"
34 #include "crypto.h"
35 
36 /*
37  * This module implements the GSS-API entry points gss_sign,
38  * gss_verify, gss_seal, and gss_unseal.
39  */
40 
41 /*
42  * __dh_gss_sign: Sign (Caluculate a check sum as specified by the qop
43  * and encrypt it with a cipher also determined by the qop using the context
44  * session keys). the message with the given qop and return
45  * a Diffie-Hellman DH_MIC token pointed to by token.
46  */
47 
48 OM_uint32
__dh_gss_sign(void * ctx,OM_uint32 * minor,gss_ctx_id_t context,int qop_req,gss_buffer_t message,gss_buffer_t token)49 __dh_gss_sign(void *ctx, /* Per mechanism context (not used) */
50 	    OM_uint32 *minor, /* Mechanism status */
51 	    gss_ctx_id_t context, /* GSS context */
52 	    int qop_req, /* Requested qop */
53 	    gss_buffer_t message, /* Input message */
54 	    gss_buffer_t token /* output token */)
55 {
56 _NOTE(ARGUNUSED(ctx))
57 	/* context is a Diffie-Hellman context */
58 	dh_gss_context_t cntx = (dh_gss_context_t)context;
59 	dh_token_desc tok;
60 	/* grap a pointer to the mic part of the token */
61 	dh_mic_t mic = &tok.ver.dh_version_u.body.dh_token_body_desc_u.sign;
62 	dh_key_set keys;
63 
64 	/*
65 	 * Make sure we can return the mechanism status an the token
66 	 * containning the MIC
67 	 */
68 	if (minor == 0 || token == GSS_C_NO_BUFFER)
69 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
70 
71 	/* Make sure the context is valid */
72 	if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
73 		return (GSS_S_NO_CONTEXT);
74 
75 	/* that it is established, */
76 	if (cntx->state != ESTABLISHED)
77 		return (GSS_S_NO_CONTEXT);
78 
79 	/* and that it has not expired */
80 	if (cntx->expire != GSS_C_INDEFINITE && cntx->expire < time(0))
81 		return (GSS_S_CONTEXT_EXPIRED);
82 
83 	/* Package the context session keys in a key_set for __make_token */
84 	keys.dh_key_set_len = cntx->no_keys;
85 	keys.dh_key_set_val = cntx->keys;
86 
87 	/* Set the token version number and type */
88 	tok.ver.verno = cntx->proto_version;
89 	tok.ver.dh_version_u.body.type = DH_MIC;
90 
91 	/* Set the token qop, seq_number and client flag */
92 	mic->qop = qop_req;
93 
94 	mic->seqnum = __dh_next_seqno(cntx);
95 
96 	mic->client_flag = cntx->initiate;
97 
98 	/*
99 	 * Build the the output token from the message the diffie-hellman
100 	 * non serialized tok and the context keys.
101 	 */
102 	if ((*minor = __make_token(token, message, &tok, &keys))
103 	    != DH_SUCCESS) {
104 		return (GSS_S_FAILURE);
105 	}
106 
107 	return (GSS_S_COMPLETE);
108 }
109 
110 
111 /*
112  * __dh_gss_verify: calculate the signature of the message and compare
113  * it to the signature represented by the DH_MIC token supplied. If the
114  * major return value is GSS_S_COMPLETE, then *qop will be the qop that
115  * was used in token.
116  */
117 
118 OM_uint32
__dh_gss_verify(void * ctx,OM_uint32 * minor,gss_ctx_id_t context,gss_buffer_t message,gss_buffer_t token,int * qop)119 __dh_gss_verify(void *ctx, /* Per mechanism context (not used) */
120 		OM_uint32 *minor, /* Mechanism status */
121 		gss_ctx_id_t context, /* GSS context */
122 		gss_buffer_t message, /* The message */
123 		gss_buffer_t token, /* The DH_MIC message token */
124 		int *qop /* qop used */)
125 {
126 _NOTE(ARGUNUSED(ctx))
127 	/* context is a Diffie-Hellman context */
128 	dh_gss_context_t cntx = (dh_gss_context_t)context;
129 	dh_token_desc tok;
130 	/* Grab the mic of the token */
131 	dh_mic_t mic = &tok.ver.dh_version_u.body.dh_token_body_desc_u.sign;
132 	dh_key_set keys;
133 	OM_uint32 stat;
134 
135 	if (minor == 0)
136 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
137 
138 	/* Validate the context */
139 	if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
140 		return (GSS_S_NO_CONTEXT);
141 
142 	/* Check that the context is established */
143 	if (cntx->state != ESTABLISHED)
144 		return (GSS_S_NO_CONTEXT);
145 
146 	/* and that it has not expired */
147 	if (cntx->expire != GSS_C_INDEFINITE && cntx->expire < time(0))
148 		return (GSS_S_CONTEXT_EXPIRED);
149 
150 	/* Package up the context session keys in to a key set */
151 	keys.dh_key_set_len = cntx->no_keys;
152 	keys.dh_key_set_val = cntx->keys;
153 
154 	/* Deserialize token into tok using messaget and keys */
155 	if ((*minor = __get_token(token, message,
156 				&tok, &keys)) != DH_SUCCESS) {
157 		switch (*minor) {
158 		case DH_DECODE_FAILURE:
159 			return (GSS_S_DEFECTIVE_TOKEN);
160 		case DH_VERIFIER_MISMATCH:
161 			return (GSS_S_BAD_SIG);
162 		default:
163 			return (GSS_S_FAILURE);
164 		}
165 	}
166 
167 	/* Check that the tok version is supported */
168 	if (tok.ver.verno != cntx->proto_version ||
169 	    tok.ver.dh_version_u.body.type != DH_MIC) {
170 		xdr_free(xdr_dh_token_desc, (char *)&tok);
171 		return (GSS_S_DEFECTIVE_TOKEN);
172 	}
173 
174 	/* Set the return qop */
175 	if (qop != NULL)
176 		*qop = mic->qop;
177 
178 	/* Sequence & Replay detection here */
179 	stat = __dh_seq_detection(cntx, mic->seqnum);
180 
181 	/* free the deserialize token tok */
182 	xdr_free(xdr_dh_token_desc, (char *)&tok);
183 
184 	/*
185 	 * If client flag is the same as the initiator flag, we're talking
186 	 * to our selves or we're being spoofed. We return
187 	 * GSS_S_DUPLICATE_TOKEN since its the best return code in the
188 	 * supplementry group.
189 	 */
190 
191 	if (mic->client_flag == cntx->initiate)
192 		stat |= GSS_S_DUPLICATE_TOKEN;
193 
194 	return (stat);
195 }
196 
197 
198 /*
199  * __dh_gss_seal: Seal a message, i.e, it wraps or embeds a supplied message
200  * in a DH_WRAP token to be delivered to the other side. A message check
201  * over the whole message is include and is selected base on the supplied
202  * qop. If the qop supports privacy and confidentiality was requested, then
203  * the embedded message will be encrypted. A return flag will be set if
204  * the message was encrypted.
205  *
206  * NOTE: IN THE CURRENT PRODUCT NO QOP CAN SUPPORT PRIVACY. THE *conf_state
207  * FLAG WILL ALWAYS BE ZERO.
208  */
209 
210 OM_uint32
__dh_gss_seal(void * ctx,OM_uint32 * minor,gss_ctx_id_t context,int conf_req,int qop_req,gss_buffer_t input,int * conf_state,gss_buffer_t output)211 __dh_gss_seal(void * ctx, /* Per mechanism context */
212 	    OM_uint32 *minor, /* Mechanism status */
213 	    gss_ctx_id_t context, /* GSS context */
214 	    int conf_req, /* True to request privacy */
215 	    int qop_req, /* Use the requested qop */
216 	    gss_buffer_t input, /* Input message to wrap */
217 	    int *conf_state, /* True if message was encrypted */
218 	    gss_buffer_t output /* Contains the ouputed DH_WRAP token*/)
219 {
220 _NOTE(ARGUNUSED(ctx))
221 	/* context is a Diffie-Hellman context */
222 	dh_gss_context_t cntx = (dh_gss_context_t)context;
223 	dh_token_desc tok;
224 	/* Get a pointer to the wrap protion of the token */
225 	dh_wrap_t wrap = &tok.ver.dh_version_u.body.dh_token_body_desc_u.seal;
226 	dh_key_set keys;
227 	gss_buffer_desc body;
228 
229 	if (minor == 0)
230 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
231 
232 	/* See if the context is valid */
233 	if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
234 		return (GSS_S_NO_CONTEXT);
235 
236 	/* that it is established, */
237 	if (cntx->state != ESTABLISHED)
238 		return (GSS_S_NO_CONTEXT);
239 
240 	/* and that it has not expired */
241 	if (cntx->expire != GSS_C_INDEFINITE && cntx->expire < time(0))
242 		return (GSS_S_CONTEXT_EXPIRED);
243 
244 	/* Package the session keys in a key_set */
245 	keys.dh_key_set_len = cntx->no_keys;
246 	keys.dh_key_set_val = cntx->keys;
247 
248 	/* Set the version and token type */
249 	tok.ver.verno = cntx->proto_version;
250 	tok.ver.dh_version_u.body.type = DH_WRAP;
251 
252 	/* Set the qop, initiate flag, and sequence number */
253 	wrap->mic.qop = qop_req;
254 	wrap->mic.client_flag = cntx->initiate;
255 	wrap->mic.seqnum = __dh_next_seqno(cntx);
256 
257 	/*
258 	 * Wrap the supplied message and encrypted if it is requested
259 	 * and allowed. The qop will have to have an associated cipher
260 	 * routine. NOTE: BECAUSE OF EXPORT CONTROLS, THE MECHANISM
261 	 * CURRENTLY WILL NOT DO ENCRYPTION AND conf_stat WILL ALWAY BE SET
262 	 * TO FALSE.
263 	 */
264 	if ((*minor = __QOPSeal(wrap->mic.qop, input, conf_req,
265 				&keys, &body, conf_state)) != DH_SUCCESS) {
266 		__free_signature(&tok.verifier);
267 		return (GSS_S_FAILURE);
268 	}
269 
270 	/* The body now contains the wrapped orignal message */
271 	wrap->body.body_len = body.length;
272 	wrap->body.body_val = (char *)body.value;
273 
274 	/*
275 	 * Tell the other side if encrypted.
276 	 * SEE NOTE ABOVE. THIS WILL ALWAYS BE FALSE.
277 	 */
278 	if (conf_state)
279 		wrap->conf_flag = *conf_state;
280 	else
281 		wrap->conf_flag = FALSE;
282 
283 	/* Serialize the token tok into output using the session keys */
284 	if ((*minor = __make_token(output, NULL, &tok, &keys)) != DH_SUCCESS) {
285 		__dh_release_buffer(&body);
286 		return (GSS_S_FAILURE);
287 	}
288 	/* We're done with the wrapped body */
289 	__dh_release_buffer(&body);
290 
291 	return (GSS_S_COMPLETE);
292 }
293 
294 /*
295  * __dh_gss_unseal: Unwrap a supplied DH_WRAP token extracting the orginal
296  * message, qop_used, and whether privacy was used.
297  *
298  * NOTE: BECAUSE OF EXPORT CONTROLS, NO QOP IN THE MECHANISM SUPPORTS
299  * PRIVACY. *conf_state WILL ALWAY BE FALSE.
300  */
301 
302 OM_uint32
__dh_gss_unseal(void * ctx,OM_uint32 * minor,gss_ctx_id_t context,gss_buffer_t input,gss_buffer_t output,int * conf_state,int * qop_used)303 __dh_gss_unseal(void *ctx, /* Per mechanism context (not used) */
304 		OM_uint32 *minor, /* Mechanism status */
305 		gss_ctx_id_t context, /* GSS context handle */
306 		gss_buffer_t input, /* Wrapped Diffie-Hellman token */
307 		gss_buffer_t output, /* The unwrapped message */
308 		int *conf_state, /* True if the message was encrypted */
309 		int *qop_used /* QOP used in token */)
310 {
311 _NOTE(ARGUNUSED(ctx))
312 	/* context is a Diffie-Hellman context */
313 	dh_gss_context_t cntx = (dh_gss_context_t)context;
314 	dh_token_desc tok;
315 	/* Grap the wrap portion of the above token */
316 	dh_wrap_t wrap = &tok.ver.dh_version_u.body.dh_token_body_desc_u.seal;
317 	dh_key_set keys;
318 	gss_buffer_desc message;
319 	OM_uint32 stat;
320 
321 	if (minor == 0 || conf_state == 0 || output == GSS_C_NO_BUFFER)
322 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
323 
324 	/* Validate context, */
325 	if ((*minor = __dh_validate_context(cntx)) != DH_SUCCESS)
326 		return (GSS_S_NO_CONTEXT);
327 
328 	/* check if it is established, */
329 	if (cntx->state != ESTABLISHED)
330 		return (GSS_S_NO_CONTEXT);
331 
332 	/* and that it has not expired */
333 	if (cntx->expire != GSS_C_INDEFINITE && cntx->expire < time(0))
334 		return (GSS_S_CONTEXT_EXPIRED);
335 
336 	/* Package up the session keys in to a key_set */
337 	keys.dh_key_set_len = cntx->no_keys;
338 	keys.dh_key_set_val = cntx->keys;
339 
340 	/* Deserialize the input in to  tok using keys */
341 	if ((*minor = __get_token(input, NULL, &tok, &keys)) != DH_SUCCESS) {
342 		switch (*minor) {
343 		case DH_DECODE_FAILURE:
344 		case DH_UNKNOWN_QOP:
345 			return (GSS_S_DEFECTIVE_TOKEN);
346 		case DH_VERIFIER_MISMATCH:
347 			return (GSS_S_BAD_SIG);
348 		default:
349 			return (GSS_S_FAILURE);
350 		}
351 	}
352 
353 	/* Set the qop_used and confidentiality state */
354 	if (qop_used != NULL)
355 		*qop_used = wrap->mic.qop;
356 	*conf_state = wrap->conf_flag;
357 
358 	/* See if this is a version that we can support */
359 	if (tok.ver.verno != cntx->proto_version ||
360 	    tok.ver.dh_version_u.body.type != DH_WRAP) {
361 		xdr_free(xdr_dh_token_desc, (char *)&tok);
362 		return (GSS_S_DEFECTIVE_TOKEN);
363 	}
364 
365 	/* Put the unwrapped body in to a gss_buffer */
366 	message.length = wrap->body.body_len;
367 	message.value = wrap->body.body_val;
368 
369 	/*
370 	 * Unwrap the message putting the result in output. We use the
371 	 * qop from the token, the session keys, and set *conf_state if
372 	 * encryption was used.
373 	 *
374 	 * NOTE: THIS MECHANISM DOES NOT SUPPORT ENCRYPTION. *conf_state
375 	 * WILL ALWAY BE FALSE.
376 	 */
377 	if ((*minor = __QOPUnSeal(wrap->mic.qop, &message,
378 				*conf_state, &keys, output))
379 	    != DH_SUCCESS) {
380 		xdr_free(xdr_dh_token_desc, (char *)&tok);
381 		return (*minor == DH_UNKNOWN_QOP ?
382 				GSS_S_DEFECTIVE_TOKEN : GSS_S_FAILURE);
383 	}
384 
385 	/* Sequence & Replay detection here */
386 	stat = __dh_seq_detection(cntx, wrap->mic.seqnum);
387 
388 	/*
389 	 * If client flag is the same as the initiator flag, we're talking
390 	 * to our selves or we're being spoofed. We return
391 	 * GSS_S_DUPLICATE_TOKEN since its the best return code in the
392 	 * supplementry group.
393 	 */
394 
395 	if (wrap->mic.client_flag == cntx->initiate)
396 		stat |= GSS_S_DUPLICATE_TOKEN;
397 
398 	/* Were done with the deserialize token, tok */
399 	xdr_free(xdr_dh_token_desc, (char *)&tok);
400 
401 	return (stat);
402 }
403