xref: /illumos-gate/usr/src/lib/gss_mechs/mech_dh/backend/mech/token.c (revision 1da57d551424de5a9d469760be7c4b4d4f10a755)
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  *	token.c
24  *
25  *	Copyright (c) 1997, by Sun Microsystems, Inc.
26  *	All rights reserved.
27  *
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "dh_gssapi.h"
35 #include "crypto.h"
36 
37 extern int
38 get_der_length(unsigned char **, unsigned int, unsigned int *);
39 
40 extern unsigned int
41 der_length_size(unsigned int);
42 
43 extern int
44 put_der_length(unsigned int, unsigned char **, unsigned int);
45 
46 #define	MSO_BIT (8*(sizeof (int) - 1))	/* Most significant octet bit */
47 
48 static OM_uint32
49 __xdr_encode_token(XDR *, gss_buffer_t, dh_token_t, dh_key_set_t);
50 
51 static OM_uint32
52 __xdr_decode_token(XDR *, gss_buffer_t,
53 		dh_token_t, dh_key_set_t, dh_signature_t);
54 
55 /*
56  * get_qop: For a Diffie-Hellman token_t, return the associate QOP
57  */
58 static dh_qop_t
get_qop(dh_token_t t)59 get_qop(dh_token_t t)
60 {
61 	dh_token_body_t body = &t->ver.dh_version_u.body;
62 	switch (body->type) {
63 	case DH_INIT_CNTX:
64 	case DH_ACCEPT_CNTX:
65 		return (DH_MECH_QOP);
66 	case DH_MIC:
67 		return (body->dh_token_body_desc_u.sign.qop);
68 	case DH_WRAP:
69 		return (body->dh_token_body_desc_u.seal.mic.qop);
70 	default:
71 		/* Should never get here */
72 		return (DH_MECH_QOP);
73 	}
74 }
75 
76 /*
77  * __make_ap_token: This routine generates a Diffie-Hellman serialized
78  * token which has an ASN.1 application 0 header prepended. The unserialized
79  * token supplied should be of type DH_INIT_CNTX.
80  *
81  * The ASN.1 applicationtion prefix is encoded as follows:
82  *
83  *	+------+
84  *	| 0x60 |	1	TAG for APPLICATION 0
85  *	+------+
86  *	|      |
87  *	~      ~     app_size	DER encoded length of oid_size + token_size
88  *	|      |
89  *      +------+
90  *	| 0x06 |	1	TAG for OID
91  *	+------+
92  *	|      |  der_length_size
93  *	~      ~  (mech->length) DER encoded length of mech->length
94  *	|      |
95  *	+------+
96  *	|      |
97  *	~      ~  mech->length	OID elements (mech->elements)
98  *	|      |
99  *	+------+
100  *	| 0x00 |       0-3	XDR padding
101  *	+------+
102  *	|      |
103  *	~      ~		Serialized DH token
104  *	|      |
105  *	+------+
106  *	| 0x00 |       0-3	Left over XDR padding
107  *	+------+
108  *
109  * We will define the token_size to be the sizeof the serialize token plus
110  * 3 the maximum XDR paddinging that will be needed. Thus the XDR padding
111  * plus the left over XDR padding will alway equal 3.
112  */
113 OM_uint32
__make_ap_token(gss_buffer_t result,gss_OID mech,dh_token_t token,dh_key_set_t keys)114 __make_ap_token(gss_buffer_t result, /* The serialized token */
115 		gss_OID mech, /* The mechanism this is for */
116 		dh_token_t token, /* The unserialized input token */
117 		dh_key_set_t keys /* The session keys to sign the token */)
118 {
119 	unsigned int size, hsize, token_size, app_size, oid_size, start;
120 	XDR xdrs;
121 	unsigned char *sv, *buf, *xdrmem;
122 	OM_uint32 stat;
123 
124 	/* Allocate the signature for the input token */
125 	if ((stat = __alloc_sig(get_qop(token),
126 				&token->verifier))
127 	    != DH_SUCCESS)
128 		return (stat);
129 
130 	/*
131 	 * We will first determine the size of the output token in
132 	 * a bottom up fashion.
133 	 */
134 
135 	/* Fetch the size of a serialized DH token */
136 	token_size = xdr_sizeof((xdrproc_t)xdr_dh_token_desc, (void *)token);
137 
138 	/*
139 	 * The token itself needs to be pasted on to the ASN.1
140 	 * application header on BYTES_PER_XDR_UNIT boundry. So we may
141 	 *  need upto BYTES_PER_XDR_UNIT - 1 extra bytes.
142 	 */
143 	token_size += BYTES_PER_XDR_UNIT -1;
144 
145 
146 	oid_size = mech->length;
147 	oid_size += der_length_size(mech->length);
148 	oid_size += 1;   /* tag x06 for Oid */
149 	/* bytes to store the length */
150 	app_size = der_length_size(oid_size + token_size);
151 
152 	hsize = app_size + oid_size;
153 	hsize += 1;  /* tag 0x60  for application 0 */
154 	size = hsize + token_size;
155 
156 	/* Allocate a buffer to serialize into */
157 	buf = New(unsigned char, size);
158 	if (buf == NULL) {
159 		__free_signature(&token->verifier);
160 		return (DH_NOMEM_FAILURE);
161 	}
162 
163 	result->value = sv = buf;
164 	result->length = size;
165 
166 	/* ASN.1 application 0 header */
167 
168 	/* Encode the tag */
169 	*buf++ = 0x60;
170 	/* Encode the app length */
171 	put_der_length(oid_size + token_size, &buf, app_size);
172 
173 	/* Encode the OID tag */
174 	*buf++ = 0x06;
175 	/* Encode the OID length */
176 	put_der_length(mech->length, &buf, oid_size);
177 	/* Encode the OID elemeents */
178 	memcpy(buf, mech->elements, mech->length);
179 
180 	/* Encode the Diffie-Hellmam token */
181 	/*
182 	 * Token has to be on BYTES_PER_XDR_UNIT boundry. (RNDUP is
183 	 * from xdr.h)
184 	 */
185 	start = RNDUP(hsize);
186 	/* Buffer for xdrmem_create to use */
187 	xdrmem = &sv[start];
188 
189 	xdrmem_create(&xdrs, (caddr_t)xdrmem, token_size, XDR_ENCODE);
190 	/* Paste the DH token on */
191 	if ((stat = __xdr_encode_token(&xdrs, NULL, token, keys))
192 	    != DH_SUCCESS) {
193 		__free_signature(&token->verifier);
194 		__dh_release_buffer(result);
195 	}
196 
197 	/* We're done with the signature, the token has been serialized */
198 	__free_signature(&token->verifier);
199 
200 	return (stat);
201 }
202 
203 /*
204  * __make_token: Given an unserialized DH token, serialize it puting the
205  * serialized output in result. If this token has a type of DH_MIC, then
206  * the optional message, msg, should be supplied. The mic caluclated will be
207  * over the message as well as the serialized token.
208  */
209 OM_uint32
__make_token(gss_buffer_t result,gss_buffer_t msg,dh_token_t token,dh_key_set_t keys)210 __make_token(gss_buffer_t result, /* Serialized token goes here */
211 	    gss_buffer_t msg,	/* Optional message for DH_MIC tokens */
212 	    dh_token_t token,	/* The token to encode */
213 	    dh_key_set_t keys	/* The keys to encrypt the check sum with */)
214 {
215 	unsigned int token_size;
216 	XDR xdrs;
217 	unsigned char *buf;
218 	OM_uint32 stat;
219 
220 	/* Allocate a signature for this token */
221 	if ((stat = __alloc_sig(get_qop(token),
222 				&token->verifier))
223 	    != DH_SUCCESS)
224 		return (stat);
225 
226 	/* Get the output token size to know how much to allocate */
227 	token_size = xdr_sizeof((xdrproc_t)xdr_dh_token_desc, (void *)token);
228 
229 	/* Allocate the buffer to hold the serialized token */
230 	buf = New(unsigned char, token_size);
231 	if (buf == NULL) {
232 		__free_signature(&token->verifier);
233 		return (DH_NOMEM_FAILURE);
234 	}
235 
236 	/* Set the result */
237 	result->length = token_size;
238 	result->value = (void *)buf;
239 
240 	/* Create the xdr stream using the allocated buffer */
241 	xdrmem_create(&xdrs, (char *)buf, token_size, XDR_ENCODE);
242 
243 	/* Encode the token */
244 	if ((stat = __xdr_encode_token(&xdrs, msg, token, keys))
245 	    != DH_SUCCESS) {
246 		__free_signature(&token->verifier);
247 		__dh_release_buffer(result);
248 	}
249 
250 	/* Release the signature */
251 	__free_signature(&token->verifier);
252 	return (stat);
253 }
254 
255 /*
256  * __get_ap_token: This routine deserializes a Diffie-Hellman serialized
257  * token which has an ASN.1 application 0 header prepended. The resulting
258  * unserialized token supplied should be of type DH_INIT_CNTX..
259  *
260  * The ASN.1 applicationtion prefix  and token is encoded as follows:
261  *
262  *	+------+
263  *	| 0x60 |	1	TAG for APPLICATION 0
264  *	+------+
265  *	|      |
266  *	~      ~     app_size	DER encoded length of oid_size + token_size
267  *	|      |
268  *      +------+
269  *	| 0x06 |	1	TAG for OID
270  *	+------+
271  *	|      |  der_length_size
272  *	~      ~  (mech->length) DER encoded length of mech->length
273  *	|      |
274  *	+------+
275  *	|      |
276  *	~      ~  mech->length	OID elements (mech->elements)
277  *	|      |
278  *	+------+
279  *	| 0x00 |       0-3	XDR padding
280  *	+------+
281  *	|      |
282  *	~      ~		Serialized DH token
283  *	|      |
284  *	+------+
285  *	| 0x00 |       0-3	Left over XDR padding
286  *	+------+
287  *
288  * We will define the token_size to be the sizeof the serialize token plus
289  * 3 the maximum XDR paddinging that will be needed. Thus the XDR padding
290  * plus the left over XDR padding will alway equal 3.
291  */
292 OM_uint32
__get_ap_token(gss_buffer_t input,gss_OID mech,dh_token_t token,dh_signature_t sig)293 __get_ap_token(gss_buffer_t input, /* The token to deserialize */
294 	    gss_OID mech, /* This context's OID */
295 	    dh_token_t token, /* The resulting token */
296 	    dh_signature_t sig /* The signature found over the input token */)
297 {
298 	unsigned char *buf, *p;
299 	unsigned int oid_len, token_len, bytes, hsize;
300 	int len;
301 	OM_uint32 stat;
302 	XDR xdrs;
303 
304 	/* Set p and buf to point to the beginning of the token */
305 	p = buf = (unsigned char *)input->value;
306 
307 	/* Check that this is an ASN.1 APPLICATION 0 token */
308 	if (*p++ != 0x60)
309 		return (DH_DECODE_FAILURE);
310 
311 	/* Determine the length for the DER encoding of the packet length */
312 	if ((len = get_der_length(&p, input->length - 1, &bytes)) < 0)
313 		return (DH_DECODE_FAILURE);
314 
315 	/*
316 	 * See if the number of bytes specified by the
317 	 * encoded length is all there
318 	 */
319 	if (input->length - 1 - bytes != len)
320 		return (DH_DECODE_FAILURE);
321 
322 	/*
323 	 * Running total of the APPLICATION 0 prefix so far. One for the
324 	 * tag (0x60) and the bytes necessary to encode the length of the
325 	 * packet.
326 	 */
327 	hsize = 1 + bytes;
328 
329 	/* Check that we're now looking at an OID */
330 	if (*p++ != 0x06)
331 		return (DH_DECODE_FAILURE);
332 
333 	/* Get OID length and the number of bytes that to encode it */
334 	oid_len = get_der_length(&p, len - 1, &bytes);
335 
336 	/*
337 	 * Now add the byte for the OID tag, plus the bytes for the oid
338 	 * length, plus the oid length its self. That is, add the size
339 	 * of the encoding of the OID to the running total of the
340 	 * APPLICATION 0 header. The result is the total size of the header.
341 	 */
342 	hsize += 1 + bytes + oid_len;
343 
344 	/*
345 	 * The DH token length is the application length minus the length
346 	 * of the OID encoding.
347 	 */
348 	token_len = len - 1 - bytes - oid_len;
349 
350 	/* Sanity check the token length */
351 	if (input->length - hsize != token_len)
352 		return (DH_DECODE_FAILURE);
353 
354 	/* Check that this token is for this OID */
355 	if (mech->length != oid_len)
356 		return (DH_DECODE_FAILURE);
357 	if (memcmp(mech->elements, p, oid_len) != 0)
358 		return (DH_DECODE_FAILURE);
359 
360 	/* Round up the header size to XDR boundry */
361 	hsize = RNDUP(hsize);
362 
363 	/* Get the start of XDR encoded token */
364 	p = &buf[hsize];
365 
366 	/* Create and XDR stream to decode from */
367 	xdrmem_create(&xdrs, (caddr_t)p, token_len, XDR_DECODE);
368 
369 	/*
370 	 * Clear the deserialized token (we'll have the xdr routines
371 	 * do the the allocations).
372 	 */
373 	memset(token, 0, sizeof (dh_token_desc));
374 
375 	/* Zero out the signature */
376 	memset(sig, 0, sizeof (*sig));
377 
378 	/*
379 	 * Decode the DH_INIT_CNTX token. Note that at this point we have no
380 	 * session keys established, so that keys is null. The unencrypted
381 	 * signature will be made available to the caller in sig. The
382 	 * caller can then attempt to decrypt the session keys in token
383 	 * and encrypt the returned sig  with those keys to check the
384 	 * integrity of the token.
385 	 */
386 	if ((stat = __xdr_decode_token(&xdrs, NULL, token, NULL, sig))
387 	    != DH_SUCCESS) {
388 		xdr_free(xdr_dh_token_desc, (char *)token);
389 		return (stat);
390 	}
391 
392 	return (stat);
393 }
394 
395 /*
396  * __get_token: Deserialize a supplied Diffie-Hellman token. Note the
397  * session keys should always be supplied to this routine. The message
398  * should only be supplied if the token is of DH_MIC type.
399  */
400 OM_uint32
__get_token(gss_buffer_t input,gss_buffer_t msg,dh_token_t token,dh_key_set_t keys)401 __get_token(gss_buffer_t input, /* The token to deserialize */
402 	    gss_buffer_t msg, /* Optional message to generate verifier over */
403 	    dh_token_t token,    /* The decode token */
404 	    dh_key_set_t keys    /* The session keys */)
405 {
406 	XDR xdrs;
407 	dh_signature sig;
408 	OM_uint32 stat;
409 
410 	/* Create a an XDR stream out of the input token */
411 	xdrmem_create(&xdrs, (caddr_t)input->value, input->length, XDR_DECODE);
412 
413 	/* Clear the token_desc and signature. */
414 	memset(token, 0, sizeof (dh_token_desc));
415 	memset(&sig, 0, sizeof (sig));
416 
417 	/* Decode the token */
418 	if ((stat = __xdr_decode_token(&xdrs, msg, token, keys, &sig))
419 	    != DH_SUCCESS)
420 		/* If we fail release the deserialized token */
421 		xdr_free(xdr_dh_token_desc, (char *)token);
422 
423 	/* We always free the signature */
424 	__free_signature(&sig);
425 
426 	return (stat);
427 }
428 
429 /*
430  * Warning these routines assumes that xdrs was created with xdrmem_create!
431  */
432 
433 /*
434  * __xdr_encode_token: Given an allocated xdrs stream serialize the supplied
435  * token_desc pointed to by objp, using keys to encrypt the signature. If
436  * msg is non null then calculate the signature over msg as well as the
437  * serialized token. Note this protocol is designed with the signature as
438  * the last part of any token. In this way the signature that is calculated is
439  * always done over the entire token. All fields in any token are thus
440  * protected from tampering
441  */
442 static OM_uint32
__xdr_encode_token(register XDR * xdrs,gss_buffer_t msg,dh_token_desc * objp,dh_key_set_t keys)443 __xdr_encode_token(register XDR *xdrs, gss_buffer_t msg,
444 		dh_token_desc *objp, dh_key_set_t keys)
445 {
446 	OM_uint32 stat;
447 
448 	/* Check that xdrs is valid */
449 	if (xdrs == 0 || xdrs->x_op != XDR_ENCODE)
450 		return (DH_BADARG_FAILURE);
451 
452 	/* Encode the protocol versioned body */
453 	if (!xdr_dh_version(xdrs, &objp->ver))
454 		return (DH_ENCODE_FAILURE);
455 
456 	/* Calculate the signature */
457 	stat = __mk_sig(get_qop(objp), xdrs->x_base,
458 			xdr_getpos(xdrs), msg, keys,
459 			&objp->verifier);
460 
461 	if (stat != DH_SUCCESS)
462 		return (stat);
463 
464 	/* Encode the signature */
465 	if (!xdr_dh_signature(xdrs, &objp->verifier))
466 		return (DH_ENCODE_FAILURE);
467 
468 	return (DH_SUCCESS);
469 }
470 
471 /*
472  * __xdr_decode_token: Decode a token from an XDR stream into a token_desc
473  * pointed to by objp. We will calculate a signature over the serialized
474  * token and an optional message. The calculated signature will be
475  * returned to the caller in sig. If the supplied keys are available this
476  * routine will compare that the verifier in the deserialized token is
477  * the same as the calculated signature over the input stream. This is
478  * the usual case. However if the supplied serialized token is DH_INIT_CNTX,
479  * the keys have not yet been established. So we just give the caller back
480  * our raw signature (Non encrypted) and the deserialized token. Higher in
481  * the food chain (currently __dh_gss_accept_sec_context), we will attempt
482  * to decrypt the session keys and call __verify_sig with the decrypted
483  * session keys the signature returned from this routine and the deserialized
484  * token.
485  *
486  * Note it is assumed that sig does point to a valid uninitialized signature.
487  */
488 
489 static OM_uint32
__xdr_decode_token(register XDR * xdrs,gss_buffer_t msg,dh_token_desc * objp,dh_key_set_t keys,dh_signature_t sig)490 __xdr_decode_token(register XDR *xdrs, gss_buffer_t msg,
491 		dh_token_desc *objp, dh_key_set_t keys, dh_signature_t sig)
492 {
493 	OM_uint32 stat;
494 
495 	/* Check that we are decoding */
496 	if (xdrs == 0 || xdrs->x_op != XDR_DECODE)
497 		return (DH_BADARG_FAILURE);
498 
499 	/* Decode the protocol versioned body */
500 	if (!xdr_dh_version(xdrs, &objp->ver))
501 		return (DH_DECODE_FAILURE);
502 
503 	/* Allocate the signature for this tokens QOP */
504 	if ((stat = __alloc_sig(get_qop(objp), sig)) != DH_SUCCESS)
505 		return (stat);
506 
507 	/*
508 	 * Call __mk_sig in crypto.c to calculate the signature based on
509 	 * the decoded QOP. __mk_sig will encrypt the signature with the
510 	 * supplied keys if they are available. If keys is null the signature
511 	 * will be just the unencrypted check sum.
512 	 */
513 	stat = __mk_sig(get_qop(objp), xdrs->x_base,
514 			xdr_getpos(xdrs), msg, keys, sig);
515 	if (stat != DH_SUCCESS)
516 		return (stat);
517 
518 	/* Now decode the supplied signature */
519 	if (!xdr_dh_signature(xdrs, &objp->verifier))
520 		return (stat);
521 
522 	/*
523 	 * If we have keys then we can check that the signatures
524 	 * are the same
525 	 */
526 	if (keys && !__cmpsig(sig, &objp->verifier))
527 		return (DH_VERIFIER_MISMATCH);
528 
529 	return (DH_SUCCESS);
530 }
531