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