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