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
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.
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]
25 * Copyright (c) 1997, by Sun Microsystems, Inc.
26 * All rights reserved.
30 #pragma ident "%Z%%M% %I% %E% SMI"
36 #include "dh_gssapi.h"
40 get_der_length(unsigned char **, unsigned int, unsigned int *);
43 der_length_size(unsigned int);
46 put_der_length(unsigned int, unsigned char **, unsigned int);
48 #define MSO_BIT (8*(sizeof (int) - 1)) /* Most significant octet bit */
51 __xdr_encode_token(XDR
*, gss_buffer_t
, dh_token_t
, dh_key_set_t
);
54 __xdr_decode_token(XDR
*, gss_buffer_t
,
55 dh_token_t
, dh_key_set_t
, dh_signature_t
);
58 * get_qop: For a Diffie-Hellman token_t, return the associate QOP
63 dh_token_body_t body
= &t
->ver
.dh_version_u
.body
;
69 return (body
->dh_token_body_desc_u
.sign
.qop
);
71 return (body
->dh_token_body_desc_u
.seal
.mic
.qop
);
73 /* Should never get here */
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.
83 * The ASN.1 applicationtion prefix is encoded as follows:
86 * | 0x60 | 1 TAG for APPLICATION 0
89 * ~ ~ app_size DER encoded length of oid_size + token_size
92 * | 0x06 | 1 TAG for OID
95 * ~ ~ (mech->length) DER encoded length of mech->length
99 * ~ ~ mech->length OID elements (mech->elements)
102 * | 0x00 | 0-3 XDR padding
105 * ~ ~ Serialized DH token
108 * | 0x00 | 0-3 Left over XDR padding
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.
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 */)
121 unsigned int size
, hsize
, token_size
, app_size
, oid_size
, start
;
123 unsigned char *sv
, *buf
, *xdrmem
;
126 /* Allocate the signature for the input token */
127 if ((stat
= __alloc_sig(get_qop(token
),
133 * We will first determine the size of the output token in
134 * a bottom up fashion.
137 /* Fetch the size of a serialized DH token */
138 token_size
= xdr_sizeof((xdrproc_t
)xdr_dh_token_desc
, (void *)token
);
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.
145 token_size
+= BYTES_PER_XDR_UNIT
-1;
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
);
154 hsize
= app_size
+ oid_size
;
155 hsize
+= 1; /* tag 0x60 for application 0 */
156 size
= hsize
+ token_size
;
158 /* Allocate a buffer to serialize into */
159 buf
= New(unsigned char, size
);
161 __free_signature(&token
->verifier
);
162 return (DH_NOMEM_FAILURE
);
165 result
->value
= sv
= buf
;
166 result
->length
= size
;
168 /* ASN.1 application 0 header */
172 /* Encode the app length */
173 put_der_length(oid_size
+ token_size
, &buf
, app_size
);
175 /* Encode the OID tag */
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
);
182 /* Encode the Diffie-Hellmam token */
184 * Token has to be on BYTES_PER_XDR_UNIT boundry. (RNDUP is
187 start
= RNDUP(hsize
);
188 /* Buffer for xdrmem_create to use */
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
))
195 __free_signature(&token
->verifier
);
196 __dh_release_buffer(result
);
199 /* We're done with the signature, the token has been serialized */
200 __free_signature(&token
->verifier
);
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.
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 */)
217 unsigned int token_size
;
222 /* Allocate a signature for this token */
223 if ((stat
= __alloc_sig(get_qop(token
),
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
);
231 /* Allocate the buffer to hold the serialized token */
232 buf
= New(unsigned char, token_size
);
234 __free_signature(&token
->verifier
);
235 return (DH_NOMEM_FAILURE
);
239 result
->length
= token_size
;
240 result
->value
= (void *)buf
;
242 /* Create the xdr stream using the allocated buffer */
243 xdrmem_create(&xdrs
, (char *)buf
, token_size
, XDR_ENCODE
);
245 /* Encode the token */
246 if ((stat
= __xdr_encode_token(&xdrs
, msg
, token
, keys
))
248 __free_signature(&token
->verifier
);
249 __dh_release_buffer(result
);
252 /* Release the signature */
253 __free_signature(&token
->verifier
);
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..
262 * The ASN.1 applicationtion prefix and token is encoded as follows:
265 * | 0x60 | 1 TAG for APPLICATION 0
268 * ~ ~ app_size DER encoded length of oid_size + token_size
271 * | 0x06 | 1 TAG for OID
273 * | | der_length_size
274 * ~ ~ (mech->length) DER encoded length of mech->length
278 * ~ ~ mech->length OID elements (mech->elements)
281 * | 0x00 | 0-3 XDR padding
284 * ~ ~ Serialized DH token
287 * | 0x00 | 0-3 Left over XDR padding
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.
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 */)
300 unsigned char *buf
, *p
;
301 unsigned int oid_len
, token_len
, bytes
, hsize
;
306 /* Set p and buf to point to the beginning of the token */
307 p
= buf
= (unsigned char *)input
->value
;
309 /* Check that this is an ASN.1 APPLICATION 0 token */
311 return (DH_DECODE_FAILURE
);
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
);
318 * See if the number of bytes specified by the
319 * encoded length is all there
321 if (input
->length
- 1 - bytes
!= len
)
322 return (DH_DECODE_FAILURE
);
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
331 /* Check that we're now looking at an OID */
333 return (DH_DECODE_FAILURE
);
335 /* Get OID length and the number of bytes that to encode it */
336 oid_len
= get_der_length(&p
, len
- 1, &bytes
);
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.
344 hsize
+= 1 + bytes
+ oid_len
;
347 * The DH token length is the application length minus the length
348 * of the OID encoding.
350 token_len
= len
- 1 - bytes
- oid_len
;
352 /* Sanity check the token length */
353 if (input
->length
- hsize
!= token_len
)
354 return (DH_DECODE_FAILURE
);
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
);
362 /* Round up the header size to XDR boundry */
363 hsize
= RNDUP(hsize
);
365 /* Get the start of XDR encoded token */
368 /* Create and XDR stream to decode from */
369 xdrmem_create(&xdrs
, (caddr_t
)p
, token_len
, XDR_DECODE
);
372 * Clear the deserialized token (we'll have the xdr routines
373 * do the the allocations).
375 memset(token
, 0, sizeof (dh_token_desc
));
377 /* Zero out the signature */
378 memset(sig
, 0, sizeof (*sig
));
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.
388 if ((stat
= __xdr_decode_token(&xdrs
, NULL
, token
, NULL
, sig
))
390 xdr_free(xdr_dh_token_desc
, (char *)token
);
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.
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 */)
412 /* Create a an XDR stream out of the input token */
413 xdrmem_create(&xdrs
, (caddr_t
)input
->value
, input
->length
, XDR_DECODE
);
415 /* Clear the token_desc and signature. */
416 memset(token
, 0, sizeof (dh_token_desc
));
417 memset(&sig
, 0, sizeof (sig
));
419 /* Decode the token */
420 if ((stat
= __xdr_decode_token(&xdrs
, msg
, token
, keys
, &sig
))
422 /* If we fail release the deserialized token */
423 xdr_free(xdr_dh_token_desc
, (char *)token
);
425 /* We always free the signature */
426 __free_signature(&sig
);
432 * Warning these routines assumes that xdrs was created with xdrmem_create!
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
445 __xdr_encode_token(register XDR
*xdrs
, gss_buffer_t msg
,
446 dh_token_desc
*objp
, dh_key_set_t keys
)
450 /* Check that xdrs is valid */
451 if (xdrs
== 0 || xdrs
->x_op
!= XDR_ENCODE
)
452 return (DH_BADARG_FAILURE
);
454 /* Encode the protocol versioned body */
455 if (!xdr_dh_version(xdrs
, &objp
->ver
))
456 return (DH_ENCODE_FAILURE
);
458 /* Calculate the signature */
459 stat
= __mk_sig(get_qop(objp
), xdrs
->x_base
,
460 xdr_getpos(xdrs
), msg
, keys
,
463 if (stat
!= DH_SUCCESS
)
466 /* Encode the signature */
467 if (!xdr_dh_signature(xdrs
, &objp
->verifier
))
468 return (DH_ENCODE_FAILURE
);
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
488 * Note it is assumed that sig does point to a valid uninitialized signature.
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
)
497 /* Check that we are decoding */
498 if (xdrs
== 0 || xdrs
->x_op
!= XDR_DECODE
)
499 return (DH_BADARG_FAILURE
);
501 /* Decode the protocol versioned body */
502 if (!xdr_dh_version(xdrs
, &objp
->ver
))
503 return (DH_DECODE_FAILURE
);
505 /* Allocate the signature for this tokens QOP */
506 if ((stat
= __alloc_sig(get_qop(objp
), sig
)) != DH_SUCCESS
)
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.
515 stat
= __mk_sig(get_qop(objp
), xdrs
->x_base
,
516 xdr_getpos(xdrs
), msg
, keys
, sig
);
517 if (stat
!= DH_SUCCESS
)
520 /* Now decode the supplied signature */
521 if (!xdr_dh_signature(xdrs
, &objp
->verifier
))
525 * If we have keys then we can check that the signatures
528 if (keys
&& !__cmpsig(sig
, &objp
->verifier
))
529 return (DH_VERIFIER_MISMATCH
);