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  *      crypto.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 <sys/note.h>
  33 #include "dh_gssapi.h"
  34 #include "crypto.h"
  35 
  36 /* Release the storage for a signature */
  37 void
  38 __free_signature(dh_signature_t sig)
  39 {
  40         Free(sig->dh_signature_val);
  41         sig->dh_signature_val = NULL;
  42         sig->dh_signature_len = 0;
  43 }
  44 
  45 /* Release the storage for a gss_buffer */
  46 void
  47 __dh_release_buffer(gss_buffer_t b)
  48 {
  49         Free(b->value);
  50         b->length = 0;
  51         b->value = NULL;
  52 }
  53 
  54 typedef struct cipher_entry {
  55         cipher_proc cipher;     /* Routine to en/decrypt with */
  56         unsigned int pad;       /* Padding need for the routine */
  57 } cipher_entry, *cipher_t;
  58 
  59 typedef struct verifer_entry {
  60         verifier_proc msg;      /* Routine to calculate the check sum */
  61         unsigned int size;      /* Size of check sum */
  62         cipher_t signer;        /* Cipher entry to sign the check sum */
  63 } verifier_entry, *verifier_t;
  64 
  65 typedef struct QOP_entry {
  66         int export_level;       /* Not currentlyt used */
  67         verifier_t verifier;    /* Verifier entry to use for integrity */
  68 } QOP_entry;
  69 
  70 /*
  71  * Return the length produced by using cipher entry c given the supplied len
  72  */
  73 static unsigned int
  74 cipher_pad(cipher_t c, unsigned int len)
  75 {
  76         unsigned int pad;
  77 
  78         pad = c ? c->pad : 1;
  79 
  80         return (((len + pad - 1)/pad)*pad);
  81 }
  82 
  83 
  84 /* EXPORT DELETE START */
  85 
  86 /*
  87  * Des [en/de]crypt buffer, buf of length, len for each key provided using
  88  * an CBC initialization vector ivec.
  89  * If the mode is encrypt we will use the following pattern if the number
  90  * of keys is odd
  91  * encrypt(buf, k[0]), decrypt(buf, k[1]), encrypt(buf, k[2])
  92  *      decrypt(buf, k[4]) ... encrypt(buf, k[keynum - 1])
  93  * If we have an even number of keys and additional encryption will be
  94  * done with the first key, i.e., ecrypt(buf, k[0]);
  95  * In each [en/de]cription above we will used the passed in CBC initialization
  96  * vector. The new initialization vector will be the vector return from the
  97  * last encryption.
  98  *
  99  * In the decryption case we reverse the proccess. Note in this case
 100  * the return ivec will be from the first decryption.
 101  */
 102 
 103 static int
 104 __desN_crypt(des_block keys[], int keynum, char *buf, unsigned int len,
 105     unsigned int mode, char *ivec)
 106 {
 107         /* Get the direction of ciphering */
 108         unsigned int m = mode & (DES_ENCRYPT | DES_DECRYPT);
 109         /* Get the remaining flags from mode */
 110         unsigned int flags = mode & ~(DES_ENCRYPT | DES_DECRYPT);
 111         des_block svec, dvec;
 112         int i, j, stat;
 113 
 114         /* Do we have at least one key */
 115         if (keynum < 1)
 116                 return (DESERR_BADPARAM);
 117 
 118         /* Save the passed in ivec */
 119         memcpy(svec.c, ivec, sizeof (des_block));
 120 
 121         /* For  each key do the appropriate cipher */
 122         for (i = 0; i < keynum; i++) {
 123                 j = (mode & DES_DECRYPT) ? keynum - 1 - i : i;
 124                 stat = cbc_crypt(keys[j].c, buf, len, m | flags, ivec);
 125                 if (mode & DES_DECRYPT && i == 0)
 126                         memcpy(dvec.c, ivec, sizeof (des_block));
 127 
 128                 if (DES_FAILED(stat))
 129                         return (stat);
 130 
 131                 m = (m == DES_ENCRYPT ? DES_DECRYPT : DES_ENCRYPT);
 132 
 133                 if ((mode & DES_DECRYPT) || i != keynum - 1 || i%2)
 134                         memcpy(ivec, svec.c, sizeof (des_block));
 135         }
 136 
 137         /*
 138          * If we have an even number of keys then do an extra round of
 139          * [en/de]cryption with the first key.
 140          */
 141         if (keynum % 2 == 0)
 142                 stat = cbc_crypt(keys[0].c, buf, len, mode, ivec);
 143 
 144         /* If were decrypting ivec is set from first decryption */
 145         if (mode & DES_DECRYPT)
 146                 memcpy(ivec, dvec.c, sizeof (des_block));
 147 
 148         return (stat);
 149 }
 150 
 151 /* EXPORT DELETE END */
 152 
 153 
 154 /*
 155  * DesN crypt packaged for use as a cipher entry
 156  */
 157 static OM_uint32
 158 __dh_desN_crypt(gss_buffer_t buf, dh_key_set_t keys, cipher_mode_t cipher_mode)
 159 {
 160         int stat = DESERR_BADPARAM;
 161 /* EXPORT DELETE START */
 162         int encrypt_flag = (cipher_mode == ENCIPHER);
 163         unsigned mode = (encrypt_flag ? DES_ENCRYPT : DES_DECRYPT) | DES_HW;
 164         des_block ivec;
 165 
 166         if (keys->dh_key_set_len < 1)
 167                 return (DH_BADARG_FAILURE);
 168 
 169         /*
 170          * We all ways start of with ivec set to zeros. There is no
 171          * good way to maintain ivecs since packets could be out of sequence
 172          * duplicated or worst of all lost. Under these conditions the
 173          * higher level protocol would have to some how resync the ivecs
 174          * on both sides and start again. Theres no mechanism for this in
 175          * GSS.
 176          */
 177         memset(&ivec, 0, sizeof (ivec));
 178 
 179         /* Do the encryption/decryption */
 180         stat = __desN_crypt(keys->dh_key_set_val, keys->dh_key_set_len,
 181                             (char *)buf->value, buf->length, mode, ivec.c);
 182 /* EXPORT DELETE END */
 183 
 184         if (DES_FAILED(stat))
 185                 return (DH_CIPHER_FAILURE);
 186 
 187         return (DH_SUCCESS);
 188 }
 189 
 190 /*
 191  * Package up plain des cbc crypt for use as a cipher entry.
 192  */
 193 static OM_uint32
 194 __dh_des_crypt(gss_buffer_t buf, dh_key_set_t keys, cipher_mode_t cipher_mode)
 195 {
 196         int stat = DESERR_BADPARAM;
 197 /* EXPORT DELETE START */
 198         int encrypt_flag = (cipher_mode == ENCIPHER);
 199         unsigned mode = (encrypt_flag ? DES_ENCRYPT : DES_DECRYPT) | DES_HW;
 200         des_block ivec;
 201 
 202         if (keys->dh_key_set_len < 1)
 203                 return (DH_BADARG_FAILURE);
 204 
 205         /*  Set the ivec to zeros and then cbc crypt the result */
 206         memset(&ivec, 0, sizeof (ivec));
 207         stat = cbc_crypt(keys->dh_key_set_val[0].c, (char *)buf->value,
 208                         buf->length, mode, ivec.c);
 209 /* EXPORT DELETE END */
 210 
 211         if (DES_FAILED(stat))
 212                 return (DH_CIPHER_FAILURE);
 213 
 214         return (DH_SUCCESS);
 215 }
 216 
 217 /*
 218  * MD5_verifier: This is a verifier routine suitable for use in a
 219  * verifier entry. It calculates the MD5 check sum over an optional
 220  * msg and a token. It signs it using the supplied cipher_proc and stores
 221  * the result in signature.
 222  *
 223  * Note signature should already be allocated and be large enough to
 224  * hold the signature after its been encrypted. If keys is null, then
 225  * we will just return the unencrypted check sum.
 226  */
 227 static OM_uint32
 228 MD5_verifier(gss_buffer_t tok, /* The buffer to sign */
 229             gss_buffer_t msg, /* Optional buffer to include */
 230             cipher_proc signer, /* Routine to encrypt the integrity check */
 231             dh_key_set_t keys, /* Optiona keys to be used with the above */
 232             dh_signature_t signature /* The resulting MIC */)
 233 {
 234         MD5_CTX md5_ctx;        /* MD5 context */
 235         gss_buffer_desc buf;    /* GSS buffer to hold keys for cipher routine */
 236 
 237         /* Initialize the MD5 context */
 238         MD5Init(&md5_ctx);
 239         /* If we have a message to digest, digest it */
 240         if (msg)
 241             MD5Update(&md5_ctx, (unsigned char *)msg->value, msg->length);
 242         /* Digest the supplied token */
 243         MD5Update(&md5_ctx, (unsigned char *)tok->value, tok->length);
 244         /* Finalize the sum. The MD5 context contains the digets */
 245         MD5Final(&md5_ctx);
 246 
 247         /* Copy the digest to the signature */
 248         memcpy(signature->dh_signature_val, (void *)md5_ctx.digest, 16);
 249 
 250         buf.length = signature->dh_signature_len;
 251         buf.value = signature->dh_signature_val;
 252 
 253         /* If we have keys encrypt it */
 254         if (keys != NULL)
 255                 return (signer(&buf, keys, ENCIPHER));
 256 
 257         return (DH_SUCCESS);
 258 }
 259 
 260 /* Cipher table */
 261 static
 262 cipher_entry cipher_tab[] = {
 263         { NULL, 1},
 264         { __dh_desN_crypt, 8},
 265         { __dh_des_crypt, 8}
 266 };
 267 
 268 
 269 #define __NO_CRYPT      &cipher_tab[0]
 270 #define __DES_N_CRYPT   &cipher_tab[1]
 271 #define __DES_CRYPT     &cipher_tab[2]
 272 
 273 /* Verifier table */
 274 static
 275 verifier_entry verifier_tab[] = {
 276         { MD5_verifier, 16, __DES_N_CRYPT },
 277         { MD5_verifier, 16, __DES_CRYPT }
 278 };
 279 
 280 /* QOP table */
 281 static
 282 QOP_entry QOP_table[] = {
 283         { 0, &verifier_tab[0] },
 284         { 0, &verifier_tab[1] }
 285 };
 286 
 287 #define QOP_ENTRIES (sizeof (QOP_table) / sizeof (QOP_entry))
 288 
 289 /*
 290  * __dh_is_valid_QOP: Return true if qop is valid entry into the QOP
 291  * table, else return false.
 292  */
 293 bool_t
 294 __dh_is_valid_QOP(dh_qop_t qop)
 295 {
 296         bool_t is_valid = FALSE;
 297 
 298         is_valid = qop < QOP_ENTRIES;
 299 
 300         return (is_valid);
 301 }
 302 
 303 /*
 304  * __alloc_sig: Allocate a signature for a given QOP. This takes into
 305  * account the size of the signature after padding for the encryption
 306  * routine.
 307  */
 308 OM_uint32
 309 __alloc_sig(dh_qop_t qop, dh_signature_t sig)
 310 {
 311         OM_uint32 stat = DH_VERIFIER_FAILURE;
 312         verifier_entry *v;
 313 
 314         /* Check that the QOP is valid */
 315         if (!__dh_is_valid_QOP(qop))
 316                 return (DH_UNKNOWN_QOP);
 317 
 318         /* Get the verifier entry from the QOP entry */
 319         v = QOP_table[qop].verifier;
 320 
 321         /* Calulate the length needed for the signature */
 322         sig->dh_signature_len = cipher_pad(v->signer, v->size);
 323 
 324         /* Allocate the signature */
 325         sig->dh_signature_val = (void*)New(char, sig->dh_signature_len);
 326         if (sig->dh_signature_val == NULL) {
 327                 sig->dh_signature_len = 0;
 328                 return (DH_NOMEM_FAILURE);
 329         }
 330 
 331         stat = DH_SUCCESS;
 332 
 333         return (stat);
 334 }
 335 
 336 /*
 337  * __get_sig_size: Return the total size needed for a signature given a QOP.
 338  */
 339 OM_uint32
 340 __get_sig_size(dh_qop_t qop, unsigned int *size)
 341 {
 342         /* Check for valid QOP */
 343         if (__dh_is_valid_QOP(qop)) {
 344                 /* Get the verifier entry */
 345                 verifier_t v = QOP_table[qop].verifier;
 346 
 347                 /* Return the size include the padding needed for encryption */
 348                 *size = v ? cipher_pad(v->signer, v->size) : 0;
 349 
 350                 return (DH_SUCCESS);
 351         }
 352         *size = 0;
 353 
 354         return (DH_UNKNOWN_QOP);
 355 }
 356 
 357 /*
 358  * __mk_sig: Generate a signature using a given qop over a token of a
 359  * given length and an optional message. We use the supplied keys to
 360  * encrypt the check sum if they are available. The output is place
 361  * in a preallocate signature, that was allocated using __alloc_sig.
 362  */
 363 OM_uint32
 364 __mk_sig(dh_qop_t qop, /* The QOP to use */
 365         char *tok, /* The token to sign */
 366         long len, /* The tokens length */
 367         gss_buffer_t mesg,      /* An optional message to be included */
 368         dh_key_set_t keys, /* The optional encryption keys */
 369         dh_signature_t sig /* The resulting MIC */)
 370 {
 371         OM_uint32 stat = DH_VERIFIER_FAILURE;
 372 
 373 
 374         verifier_entry *v;      /* Verifier entry */
 375         gss_buffer_desc buf;    /* Buffer to package tok */
 376 
 377         /* Make sure the QOP is valid */
 378         if (!__dh_is_valid_QOP(qop))
 379                 return (DH_UNKNOWN_QOP);
 380 
 381         /* Grab the verifier entry for the qop */
 382         v = QOP_table[qop].verifier;
 383 
 384         /* Package the token for use in a verifier_proc */
 385         buf.length = len;
 386         buf.value = tok;
 387 
 388         /*
 389          * Calculate the signature using the supplied keys. If keys
 390          * is null, the the v->signer->cipher routine will not be called
 391          * and sig will not be encrypted.
 392          */
 393         stat = (*v->msg)(&buf, mesg, v->signer->cipher, keys, sig);
 394 
 395         return (stat);
 396 }
 397 
 398 /*
 399  * __verify_sig: Verify that the supplied signature, sig, is the same
 400  * as the token verifier
 401  */
 402 OM_uint32
 403 __verify_sig(dh_token_t token, /* The token to be verified */
 404             dh_qop_t qop, /* The QOP to use */
 405             dh_key_set_t keys, /* The context session keys */
 406             dh_signature_t sig /* The signature from the serialized token */)
 407 {
 408         OM_uint32 stat = DH_VERIFIER_FAILURE;
 409 
 410         cipher_proc cipher;     /* cipher routine to use */
 411         gss_buffer_desc buf;    /* Packaging for sig */
 412 
 413         /* Check the QOP */
 414         if (!__dh_is_valid_QOP(qop))
 415                 return (DH_UNKNOWN_QOP);
 416 
 417         /* Package up the supplied signature */
 418         buf.length = sig->dh_signature_len;
 419         buf.value = sig->dh_signature_val;
 420 
 421         /* Get the cipher proc to use from the verifier entry for qop */
 422         cipher = QOP_table[qop].verifier->signer->cipher;
 423 
 424         /* Encrypt the check sum using the supplied set of keys */
 425         if ((stat = (*cipher)(&buf, keys, ENCIPHER)) != DH_SUCCESS)
 426                 return (stat);
 427 
 428         /* Compare the signatures */
 429         if (__cmpsig(sig, &token->verifier))
 430                 return (DH_SUCCESS);
 431 
 432         stat = DH_VERIFIER_MISMATCH;
 433 
 434         return (stat);
 435 }
 436 
 437 /*
 438  * __cmpsig: Return true if two signatures are the same, else false.
 439  */
 440 bool_t
 441 __cmpsig(dh_signature_t s1, dh_signature_t s2)
 442 {
 443         return (s1->dh_signature_len == s2->dh_signature_len &&
 444             memcmp(s1->dh_signature_val,
 445                 s2->dh_signature_val, s1->dh_signature_len) == 0);
 446 }
 447 
 448 /*
 449  * wrap_msg_body: Wrap the message pointed to be in into a
 450  * message pointed to by out that has ben padded out by pad bytes.
 451  *
 452  * The output message looks like:
 453  * out->length = total length of out->value including any padding
 454  * out->value points to memory as follows:
 455  * +------------+-------------------------+---------|
 456  * | in->length | in->value               | XDR PAD |
 457  * +------------+-------------------------+---------|
 458  *    4 bytes      in->length bytes         0 - 3
 459  */
 460 static OM_uint32
 461 wrap_msg_body(gss_buffer_t in, gss_buffer_t out)
 462 {
 463         XDR xdrs;                       /* xdrs to wrap with */
 464         unsigned int len, out_len;      /* length  */
 465         size_t size;
 466 
 467         out->length = 0;
 468         out->value = 0;
 469 
 470         /* Make sure the address of len points to a 32 bit word */
 471         len = (unsigned int)in->length;
 472         if (len != in->length)
 473                 return (DH_ENCODE_FAILURE);
 474 
 475         size = ((in->length + sizeof (OM_uint32) + 3)/4) * 4;
 476         out_len = size;
 477         if (out_len != size)
 478                 return (DH_ENCODE_FAILURE);
 479 
 480         /* Allocate the output buffer and set the length */
 481         if ((out->value = (void *)New(char, len)) == NULL)
 482                 return (DH_NOMEM_FAILURE);
 483         out->length = out_len;
 484 
 485 
 486         /* Create xdr stream to wrap into */
 487         xdrmem_create(&xdrs, out->value, out->length, XDR_ENCODE);
 488 
 489         /* Wrap the bytes in value */
 490         if (!xdr_bytes(&xdrs, (char **)&in->value, &len, len)) {
 491                 __dh_release_buffer(out);
 492                 return (DH_ENCODE_FAILURE);
 493         }
 494 
 495         return (DH_SUCCESS);
 496 }
 497 
 498 /*
 499  * __QOPSeal: Wrap the input message placing the output in output given
 500  * a valid QOP. If confidentialiy is requested it is ignored. We can't
 501  * support privacy. The return flag will always be zero.
 502  */
 503 OM_uint32
 504 __QOPSeal(dh_qop_t qop, /* The QOP to use */
 505         gss_buffer_t input, /* The buffer to wrap */
 506         int conf_req, /* Do we want privacy ? */
 507         dh_key_set_t keys, /* The session keys */
 508         gss_buffer_t output, /* The wraped message */
 509         int *conf_ret /* Did we encrypt it? */)
 510 {
 511 _NOTE(ARGUNUSED(conf_req,keys))
 512         OM_uint32 stat = DH_CIPHER_FAILURE;
 513 
 514         *conf_ret = FALSE;      /* No encryption allowed */
 515 
 516         /* Check for valid QOP */
 517         if (!__dh_is_valid_QOP(qop))
 518                 return (DH_UNKNOWN_QOP);
 519 
 520         /* Wrap the message */
 521         if ((stat = wrap_msg_body(input, output))
 522             != DH_SUCCESS)
 523                 return (stat);
 524 
 525         return (stat);
 526 }
 527 
 528 /*
 529  * unwrap_msg_body: Unwrap the message, that was wrapped from above
 530  */
 531 static OM_uint32
 532 unwrap_msg_body(gss_buffer_t in, gss_buffer_t out)
 533 {
 534         XDR xdrs;
 535         unsigned int len;       /* sizeof (len) == 32bits */
 536 
 537         /* Create an xdr stream to on wrap in */
 538         xdrmem_create(&xdrs, in->value, in->length, XDR_DECODE);
 539 
 540         /* Unwrap the input into out->value */
 541         if (!xdr_bytes(&xdrs, (char **)&out->value, &len, in->length))
 542                 return (DH_DECODE_FAILURE);
 543 
 544         /* set the length */
 545         out->length = len;
 546 
 547         return (DH_SUCCESS);
 548 }
 549 
 550 /*
 551  * __QOPUnSeal: Unwrap the input message into output using the supplied QOP.
 552  * Note it is the callers responsibility to release the allocated output
 553  * buffer. If conf_req is true we return DH_CIPHER_FAILURE since we don't
 554  * support privacy.
 555  */
 556 OM_uint32
 557 __QOPUnSeal(dh_qop_t qop, /* The QOP to use */
 558             gss_buffer_t input, /* The message to unwrap */
 559             int conf_req, /* Is the message encrypted */
 560             dh_key_set_t keys, /* The session keys to decrypt if conf_req */
 561             gss_buffer_t output /* The unwraped message */)
 562 {
 563 _NOTE(ARGUNUSED(keys))
 564         OM_uint32 stat = DH_CIPHER_FAILURE;
 565 
 566         /* Check that the qop is valid */
 567         if (!__dh_is_valid_QOP(qop))
 568                 return (DH_UNKNOWN_QOP);
 569 
 570         /* Set output to sane values */
 571         output->length = 0;
 572         output->value = NULL;
 573 
 574         /* Fail if this is privacy */
 575         if (conf_req)
 576                 return (DH_CIPHER_FAILURE);
 577 
 578         /* Unwrap the input into the output, return the status */
 579         stat = unwrap_msg_body(input, output);
 580 
 581         return (stat);
 582 }