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