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 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 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 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 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 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 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 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