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