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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * RC4 provider for the Kernel Cryptographic Framework (KCF)
  28  */
  29 
  30 #include <sys/types.h>
  31 #include <sys/systm.h>
  32 #include <sys/modctl.h>
  33 #include <sys/cmn_err.h>
  34 #include <sys/ddi.h>
  35 #include <sys/crypto/common.h>
  36 #include <sys/crypto/spi.h>
  37 #include <sys/sysmacros.h>
  38 #include <sys/strsun.h>
  39 #include <arcfour.h>
  40 
  41 extern struct mod_ops mod_cryptoops;
  42 
  43 /*
  44  * Module linkage information for the kernel.
  45  */
  46 static struct modlcrypto modlcrypto = {
  47         &mod_cryptoops,
  48         "RC4 Kernel SW Provider"
  49 };
  50 
  51 static struct modlinkage modlinkage = {
  52         MODREV_1,
  53         (void *)&modlcrypto,
  54         NULL
  55 };
  56 
  57 /*
  58  * CSPI information (entry points, provider info, etc.)
  59  */
  60 
  61 #define RC4_MECH_INFO_TYPE      0
  62 /*
  63  * Mechanism info structure passed to KCF during registration.
  64  */
  65 static crypto_mech_info_t rc4_mech_info_tab[] = {
  66         {SUN_CKM_RC4, RC4_MECH_INFO_TYPE,
  67             CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
  68             CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC,
  69             ARCFOUR_MIN_KEY_BITS, ARCFOUR_MAX_KEY_BITS,
  70             CRYPTO_KEYSIZE_UNIT_IN_BITS | CRYPTO_CAN_SHARE_OPSTATE}
  71 };
  72 
  73 static void rc4_provider_status(crypto_provider_handle_t, uint_t *);
  74 
  75 static crypto_control_ops_t rc4_control_ops = {
  76         rc4_provider_status
  77 };
  78 
  79 static int rc4_common_init(crypto_ctx_t *, crypto_mechanism_t *,
  80     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
  81 
  82 static int rc4_crypt_update(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
  83     crypto_req_handle_t);
  84 
  85 static int rc4_crypt_final(crypto_ctx_t *, crypto_data_t *,
  86     crypto_req_handle_t);
  87 
  88 static int rc4_crypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
  89     crypto_req_handle_t);
  90 
  91 static int rc4_crypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
  92     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
  93     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
  94 
  95 
  96 static crypto_cipher_ops_t rc4_cipher_ops = {
  97         rc4_common_init,
  98         rc4_crypt,
  99         rc4_crypt_update,
 100         rc4_crypt_final,
 101         rc4_crypt_atomic,
 102         rc4_common_init,
 103         rc4_crypt,
 104         rc4_crypt_update,
 105         rc4_crypt_final,
 106         rc4_crypt_atomic
 107 };
 108 
 109 static int rc4_free_context(crypto_ctx_t *);
 110 
 111 static crypto_ctx_ops_t rc4_ctx_ops = {
 112         NULL,
 113         rc4_free_context
 114 };
 115 
 116 static crypto_ops_t rc4_crypto_ops = {
 117         &rc4_control_ops,
 118         NULL,
 119         &rc4_cipher_ops,
 120         NULL,
 121         NULL,
 122         NULL,
 123         NULL,
 124         NULL,
 125         NULL,
 126         NULL,
 127         NULL,
 128         NULL,
 129         NULL,
 130         &rc4_ctx_ops
 131 };
 132 
 133 static crypto_provider_info_t rc4_prov_info = {
 134         CRYPTO_SPI_VERSION_1,
 135         "RC4 Software Provider",
 136         CRYPTO_SW_PROVIDER,
 137         {&modlinkage},
 138         NULL,
 139         &rc4_crypto_ops,
 140         sizeof (rc4_mech_info_tab)/sizeof (crypto_mech_info_t),
 141         rc4_mech_info_tab
 142 };
 143 
 144 static crypto_kcf_provider_handle_t rc4_prov_handle = NULL;
 145 
 146 static mblk_t *advance_position(mblk_t *, off_t, uchar_t **);
 147 static int crypto_arcfour_crypt(ARCFour_key *, uchar_t *, crypto_data_t *,
 148     int);
 149 
 150 int
 151 _init(void)
 152 {
 153         int ret;
 154 
 155         if ((ret = mod_install(&modlinkage)) != 0)
 156                 return (ret);
 157 
 158         /* Register with KCF.  If the registration fails, remove the module. */
 159         if (crypto_register_provider(&rc4_prov_info, &rc4_prov_handle)) {
 160                 (void) mod_remove(&modlinkage);
 161                 return (EACCES);
 162         }
 163 
 164         return (0);
 165 }
 166 
 167 int
 168 _fini(void)
 169 {
 170         /* Unregister from KCF if module is registered */
 171         if (rc4_prov_handle != NULL) {
 172                 if (crypto_unregister_provider(rc4_prov_handle))
 173                         return (EBUSY);
 174 
 175                 rc4_prov_handle = NULL;
 176         }
 177 
 178         return (mod_remove(&modlinkage));
 179 }
 180 
 181 int
 182 _info(struct modinfo *modinfop)
 183 {
 184         return (mod_info(&modlinkage, modinfop));
 185 }
 186 
 187 
 188 /*
 189  * KCF software provider control entry points.
 190  */
 191 /* ARGSUSED */
 192 static void
 193 rc4_provider_status(crypto_provider_handle_t provider, uint_t *status)
 194 {
 195         *status = CRYPTO_PROVIDER_READY;
 196 }
 197 
 198 /* ARGSUSED */
 199 static int
 200 rc4_common_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
 201     crypto_key_t *key, crypto_spi_ctx_template_t template,
 202     crypto_req_handle_t req)
 203 {
 204 
 205 /* EXPORT DELETE START */
 206 
 207         ARCFour_key *keystream;
 208 
 209         if ((mechanism)->cm_type != RC4_MECH_INFO_TYPE)
 210                 return (CRYPTO_MECHANISM_INVALID);
 211 
 212         if (key->ck_format != CRYPTO_KEY_RAW)
 213                 return (CRYPTO_KEY_TYPE_INCONSISTENT);
 214 
 215         if (key->ck_length < ARCFOUR_MIN_KEY_BITS ||
 216             key->ck_length > ARCFOUR_MAX_KEY_BITS) {
 217                 return (CRYPTO_KEY_SIZE_RANGE);
 218         }
 219 
 220         /*
 221          * Allocate an RC4 key stream.
 222          */
 223         if ((keystream = kmem_alloc(sizeof (ARCFour_key),
 224             crypto_kmflag(req))) == NULL)
 225                 return (CRYPTO_HOST_MEMORY);
 226 
 227         arcfour_key_init(keystream, key->ck_data,
 228             CRYPTO_BITS2BYTES(key->ck_length));
 229 
 230         ctx->cc_provider_private = keystream;
 231 
 232 /* EXPORT DELETE END */
 233 
 234         return (CRYPTO_SUCCESS);
 235 }
 236 
 237 static int
 238 rc4_crypt(crypto_ctx_t *ctx, crypto_data_t *input, crypto_data_t *output,
 239     crypto_req_handle_t req)
 240 {
 241         int ret;
 242 
 243         ret = rc4_crypt_update(ctx, input, output, req);
 244 
 245         if (ret != CRYPTO_BUFFER_TOO_SMALL)
 246                 (void) rc4_free_context(ctx);
 247 
 248         return (ret);
 249 }
 250 
 251 /* ARGSUSED */
 252 static int
 253 rc4_crypt_update(crypto_ctx_t *ctx, crypto_data_t *input, crypto_data_t *output,
 254     crypto_req_handle_t req)
 255 {
 256         int ret = CRYPTO_SUCCESS;
 257 
 258 /* EXPORT DELETE START */
 259 
 260         ARCFour_key *key;
 261         off_t saveoffset;
 262 
 263         ASSERT(ctx->cc_provider_private != NULL);
 264 
 265         if ((ctx->cc_flags & CRYPTO_USE_OPSTATE) && ctx->cc_opstate != NULL)
 266                 key = ctx->cc_opstate;
 267         else
 268                 key = ctx->cc_provider_private;
 269 
 270         /* Simple case: in-line encipherment */
 271 
 272         if (output == NULL) {
 273                 switch (input->cd_format) {
 274                 case CRYPTO_DATA_RAW: {
 275                         char *start, *end;
 276                         start = input->cd_raw.iov_base + input->cd_offset;
 277 
 278                         end =  input->cd_raw.iov_base + input->cd_raw.iov_len;
 279 
 280                         if (start + input->cd_length > end)
 281                                 return (CRYPTO_DATA_INVALID);
 282 
 283                         arcfour_crypt(key, (uchar_t *)start, (uchar_t *)start,
 284                             input->cd_length);
 285                         break;
 286                 }
 287                 case CRYPTO_DATA_MBLK: {
 288                         uchar_t *start, *end;
 289                         size_t len, left;
 290                         mblk_t *mp = input->cd_mp, *mp1, *mp2;
 291 
 292                         ASSERT(mp != NULL);
 293 
 294                         mp1 = advance_position(mp, input->cd_offset, &start);
 295 
 296                         if (mp1 == NULL)
 297                                 return (CRYPTO_DATA_LEN_RANGE);
 298 
 299                         mp2 = advance_position(mp, input->cd_offset +
 300                             input->cd_length, &end);
 301 
 302                         if (mp2 == NULL)
 303                                 return (CRYPTO_DATA_LEN_RANGE);
 304 
 305                         left = input->cd_length;
 306                         while (mp1 != NULL) {
 307                                 if (_PTRDIFF(mp1->b_wptr, start) > left) {
 308                                         len = left;
 309                                         arcfour_crypt(key, start, start, len);
 310                                         mp1 = NULL;
 311                                 } else {
 312                                         len = _PTRDIFF(mp1->b_wptr, start);
 313                                         arcfour_crypt(key, start, start, len);
 314                                         mp1 = mp1->b_cont;
 315                                         start = mp1->b_rptr;
 316                                         left -= len;
 317                                 }
 318                         }
 319                         break;
 320                 }
 321                 case CRYPTO_DATA_UIO: {
 322                         uio_t *uiop = input->cd_uio;
 323                         off_t offset = input->cd_offset;
 324                         size_t length = input->cd_length;
 325                         uint_t vec_idx;
 326                         size_t cur_len;
 327 
 328                         /*
 329                          * Jump to the first iovec containing data to be
 330                          * processed.
 331                          */
 332                         for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
 333                             offset >= uiop->uio_iov[vec_idx].iov_len;
 334                             offset -= uiop->uio_iov[vec_idx++].iov_len)
 335                                 ;
 336                         if (vec_idx == uiop->uio_iovcnt) {
 337                                 return (CRYPTO_DATA_LEN_RANGE);
 338                         }
 339 
 340                         /*
 341                          * Now process the iovecs.
 342                          */
 343                         while (vec_idx < uiop->uio_iovcnt && length > 0) {
 344                                 uchar_t *start;
 345                                 iovec_t *iovp = &(uiop->uio_iov[vec_idx]);
 346 
 347                                 cur_len = MIN(iovp->iov_len - offset, length);
 348 
 349                                 start = (uchar_t *)(iovp->iov_base + offset);
 350                                 arcfour_crypt(key, start + offset,
 351                                     start + offset, cur_len);
 352 
 353                                 length -= cur_len;
 354                                 vec_idx++;
 355                                 offset = 0;
 356                         }
 357 
 358                         if (vec_idx == uiop->uio_iovcnt && length > 0) {
 359 
 360                                 return (CRYPTO_DATA_LEN_RANGE);
 361                         }
 362                         break;
 363                 }
 364                 }
 365                 return (CRYPTO_SUCCESS);
 366         }
 367 
 368         /*
 369          * We need to just return the length needed to store the output.
 370          * We should not destroy the context for the following case.
 371          */
 372 
 373         if (input->cd_length > output->cd_length) {
 374                 output->cd_length = input->cd_length;
 375                 return (CRYPTO_BUFFER_TOO_SMALL);
 376         }
 377 
 378         saveoffset = output->cd_offset;
 379 
 380         switch (input->cd_format) {
 381         case CRYPTO_DATA_RAW: {
 382                 char *start, *end;
 383                 start = input->cd_raw.iov_base + input->cd_offset;
 384 
 385                 end =  input->cd_raw.iov_base + input->cd_raw.iov_len;
 386 
 387                 if (start + input->cd_length > end)
 388                         return (CRYPTO_DATA_LEN_RANGE);
 389 
 390                 ret = crypto_arcfour_crypt(key, (uchar_t *)start, output,
 391                     input->cd_length);
 392 
 393                 if (ret != CRYPTO_SUCCESS)
 394                         return (ret);
 395                 break;
 396         }
 397         case CRYPTO_DATA_MBLK: {
 398                 uchar_t *start, *end;
 399                 size_t len, left;
 400                 mblk_t *mp = input->cd_mp, *mp1, *mp2;
 401 
 402                 ASSERT(mp != NULL);
 403 
 404                 mp1 = advance_position(mp, input->cd_offset, &start);
 405 
 406                 if (mp1 == NULL)
 407                         return (CRYPTO_DATA_LEN_RANGE);
 408 
 409                 mp2 = advance_position(mp, input->cd_offset + input->cd_length,
 410                     &end);
 411 
 412                 if (mp2 == NULL)
 413                         return (CRYPTO_DATA_LEN_RANGE);
 414 
 415                 left = input->cd_length;
 416                 while (mp1 != NULL) {
 417                         if (_PTRDIFF(mp1->b_wptr, start) > left) {
 418                                 len = left;
 419                                 ret = crypto_arcfour_crypt(key, start, output,
 420                                     len);
 421                                 if (ret != CRYPTO_SUCCESS)
 422                                         return (ret);
 423                                 mp1 = NULL;
 424                         } else {
 425                                 len = _PTRDIFF(mp1->b_wptr, start);
 426                                 ret = crypto_arcfour_crypt(key, start, output,
 427                                     len);
 428                                 if (ret != CRYPTO_SUCCESS)
 429                                         return (ret);
 430                                 mp1 = mp1->b_cont;
 431                                 start = mp1->b_rptr;
 432                                 left -= len;
 433                                 output->cd_offset += len;
 434                         }
 435                 }
 436                 break;
 437         }
 438         case CRYPTO_DATA_UIO: {
 439                 uio_t *uiop = input->cd_uio;
 440                 off_t offset = input->cd_offset;
 441                 size_t length = input->cd_length;
 442                 uint_t vec_idx;
 443                 size_t cur_len;
 444 
 445                 /*
 446                  * Jump to the first iovec containing data to be
 447                  * processed.
 448                  */
 449                 for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
 450                     offset >= uiop->uio_iov[vec_idx].iov_len;
 451                     offset -= uiop->uio_iov[vec_idx++].iov_len)
 452                         ;
 453                 if (vec_idx == uiop->uio_iovcnt) {
 454                         return (CRYPTO_DATA_LEN_RANGE);
 455                 }
 456 
 457                 /*
 458                  * Now process the iovecs.
 459                  */
 460                 while (vec_idx < uiop->uio_iovcnt && length > 0) {
 461                         uchar_t *start;
 462                         iovec_t *iovp = &(uiop->uio_iov[vec_idx]);
 463                         cur_len = MIN(iovp->iov_len - offset, length);
 464 
 465                         start = (uchar_t *)(iovp->iov_base + offset);
 466                         ret = crypto_arcfour_crypt(key, start + offset,
 467                             output, cur_len);
 468                         if (ret != CRYPTO_SUCCESS)
 469                                 return (ret);
 470 
 471                         length -= cur_len;
 472                         vec_idx++;
 473                         offset = 0;
 474                         output->cd_offset += cur_len;
 475                 }
 476 
 477                 if (vec_idx == uiop->uio_iovcnt && length > 0) {
 478 
 479                         return (CRYPTO_DATA_LEN_RANGE);
 480                 }
 481         }
 482         }
 483 
 484         output->cd_offset = saveoffset;
 485         output->cd_length = input->cd_length;
 486 
 487 /* EXPORT DELETE END */
 488 
 489         return (ret);
 490 }
 491 
 492 /* ARGSUSED */
 493 static int rc4_crypt_final(crypto_ctx_t *ctx, crypto_data_t *data,
 494     crypto_req_handle_t req)
 495 {
 496         /* No final part for streams ciphers. Just free the context */
 497         if (data != NULL)
 498                 data->cd_length = 0;
 499 
 500         return (rc4_free_context(ctx));
 501 }
 502 
 503 /* ARGSUSED */
 504 static int
 505 rc4_crypt_atomic(crypto_provider_handle_t handle, crypto_session_id_t session,
 506     crypto_mechanism_t *mechanism, crypto_key_t *key, crypto_data_t *input,
 507     crypto_data_t *output, crypto_spi_ctx_template_t template,
 508     crypto_req_handle_t req)
 509 {
 510         crypto_ctx_t ctx;
 511         int ret;
 512 
 513         bzero(&ctx, sizeof (crypto_ctx_t));
 514         ret = rc4_common_init(&ctx, mechanism, key, template, req);
 515 
 516         if (ret != CRYPTO_SUCCESS)
 517                 return (ret);
 518 
 519         ret = rc4_crypt_update(&ctx, input, output, req);
 520 
 521         (void) rc4_free_context(&ctx);
 522 
 523         return (ret);
 524 }
 525 
 526 /* ARGSUSED */
 527 static int
 528 rc4_free_context(crypto_ctx_t *ctx)
 529 {
 530 
 531 /* EXPORT DELETE START */
 532 
 533         ARCFour_key *keystream = ctx->cc_provider_private;
 534 
 535         if (keystream != NULL) {
 536                 bzero(keystream, sizeof (ARCFour_key));
 537                 kmem_free(keystream, sizeof (ARCFour_key));
 538                 ctx->cc_provider_private = NULL;
 539         }
 540 
 541 /* EXPORT DELETE END */
 542 
 543         return (CRYPTO_SUCCESS);
 544 }
 545 
 546 /* Encrypts a contiguous input 'in' into the 'out' crypto_data_t */
 547 
 548 static int
 549 crypto_arcfour_crypt(ARCFour_key *key, uchar_t *in, crypto_data_t *out,
 550     int length)
 551 {
 552         switch (out->cd_format) {
 553                 case CRYPTO_DATA_RAW: {
 554                         uchar_t *start, *end;
 555                         start = (uchar_t *)(out->cd_raw.iov_base +
 556                             out->cd_offset);
 557 
 558                         end = (uchar_t *)(out->cd_raw.iov_base +
 559                             out->cd_raw.iov_len);
 560 
 561                         if (start + out->cd_length > end)
 562                                 return (CRYPTO_DATA_LEN_RANGE);
 563 
 564                         arcfour_crypt(key, in, start, length);
 565 
 566                         return (CRYPTO_SUCCESS);
 567                 }
 568                 case CRYPTO_DATA_MBLK: {
 569                         uchar_t *start, *end;
 570                         size_t len, left;
 571                         mblk_t *mp = out->cd_mp, *mp1, *mp2;
 572 
 573                         ASSERT(mp != NULL);
 574 
 575                         mp1 = advance_position(mp, out->cd_offset, &start);
 576 
 577                         if (mp1 == NULL)
 578                                 return (CRYPTO_DATA_LEN_RANGE);
 579 
 580                         mp2 = advance_position(mp, out->cd_offset +
 581                             out->cd_length, &end);
 582 
 583                         if (mp2 == NULL)
 584                                 return (CRYPTO_DATA_LEN_RANGE);
 585 
 586                         left = length;
 587                         while (mp1 != NULL) {
 588                                 if (_PTRDIFF(mp1->b_wptr, start) > left) {
 589                                         len = left;
 590                                         arcfour_crypt(key, in, start, len);
 591                                         mp1 = NULL;
 592                                 } else {
 593                                         len = _PTRDIFF(mp1->b_wptr, start);
 594                                         arcfour_crypt(key, in, start, len);
 595                                         mp1 = mp1->b_cont;
 596                                         start = mp1->b_rptr;
 597                                         left -= len;
 598                                 }
 599                         }
 600                         break;
 601                 }
 602                 case CRYPTO_DATA_UIO: {
 603                         uio_t *uiop = out->cd_uio;
 604                         off_t offset = out->cd_offset;
 605                         size_t len = length;
 606                         uint_t vec_idx;
 607                         size_t cur_len;
 608 
 609                         /*
 610                          * Jump to the first iovec containing data to be
 611                          * processed.
 612                          */
 613                         for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
 614                             offset >= uiop->uio_iov[vec_idx].iov_len;
 615                             offset -= uiop->uio_iov[vec_idx++].iov_len)
 616                                 ;
 617                         if (vec_idx == uiop->uio_iovcnt) {
 618                                 return (CRYPTO_DATA_LEN_RANGE);
 619                         }
 620 
 621                         /*
 622                          * Now process the iovecs.
 623                          */
 624                         while (vec_idx < uiop->uio_iovcnt && len > 0) {
 625                                 uchar_t *start;
 626                                 iovec_t *iovp = &(uiop->uio_iov[vec_idx]);
 627                                 cur_len = MIN(iovp->iov_len - offset, len);
 628 
 629                                 start = (uchar_t *)(iovp->iov_base + offset);
 630                                 arcfour_crypt(key, start + offset,
 631                                     start + offset, cur_len);
 632 
 633                                 len -= cur_len;
 634                                 vec_idx++;
 635                                 offset = 0;
 636                         }
 637 
 638                         if (vec_idx == uiop->uio_iovcnt && len > 0) {
 639                                 return (CRYPTO_DATA_LEN_RANGE);
 640                         }
 641                         break;
 642                 }
 643                 default:
 644                         return (CRYPTO_DATA_INVALID);
 645         }
 646         return (CRYPTO_SUCCESS);
 647 }
 648 
 649 /*
 650  * Advances 'offset' bytes from the beginning of the first block in 'mp',
 651  * possibly jumping across b_cont boundary
 652  * '*cpp' is set to the position of the byte we want, and the block where
 653  * 'cpp' is returned.
 654  */
 655 static mblk_t *
 656 advance_position(mblk_t *mp, off_t offset, uchar_t **cpp)
 657 {
 658         mblk_t *mp1 = mp;
 659         size_t l;
 660         off_t o = offset;
 661 
 662         while (mp1 != NULL) {
 663                 l = MBLKL(mp1);
 664 
 665                 if (l <= o) {
 666                         o -= l;
 667                         mp1 = mp1->b_cont;
 668                 } else {
 669                         *cpp = (uchar_t *)(mp1->b_rptr + o);
 670                         break;
 671                 }
 672         }
 673         return (mp1);
 674 }