17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
56a1073f8Skrishna * Common Development and Distribution License (the "License").
66a1073f8Skrishna * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
2295014fbbSDan OpenSolaris Anderson * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
237c478bd9Sstevel@tonic-gate * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate * RC4 provider for the Kernel Cryptographic Framework (KCF)
287c478bd9Sstevel@tonic-gate */
297c478bd9Sstevel@tonic-gate
307c478bd9Sstevel@tonic-gate #include <sys/types.h>
317c478bd9Sstevel@tonic-gate #include <sys/systm.h>
327c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
337c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
347c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
357c478bd9Sstevel@tonic-gate #include <sys/crypto/common.h>
367c478bd9Sstevel@tonic-gate #include <sys/crypto/spi.h>
377c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
387c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
397c478bd9Sstevel@tonic-gate #include <arcfour.h>
407c478bd9Sstevel@tonic-gate
417c478bd9Sstevel@tonic-gate extern struct mod_ops mod_cryptoops;
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate /*
447c478bd9Sstevel@tonic-gate * Module linkage information for the kernel.
457c478bd9Sstevel@tonic-gate */
467c478bd9Sstevel@tonic-gate static struct modlcrypto modlcrypto = {
477c478bd9Sstevel@tonic-gate &mod_cryptoops,
48d2b32306Smcpowers "RC4 Kernel SW Provider"
497c478bd9Sstevel@tonic-gate };
507c478bd9Sstevel@tonic-gate
517c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
527c478bd9Sstevel@tonic-gate MODREV_1,
537c478bd9Sstevel@tonic-gate (void *)&modlcrypto,
547c478bd9Sstevel@tonic-gate NULL
557c478bd9Sstevel@tonic-gate };
567c478bd9Sstevel@tonic-gate
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate * CSPI information (entry points, provider info, etc.)
597c478bd9Sstevel@tonic-gate */
607c478bd9Sstevel@tonic-gate
617c478bd9Sstevel@tonic-gate #define RC4_MECH_INFO_TYPE 0
627c478bd9Sstevel@tonic-gate /*
637c478bd9Sstevel@tonic-gate * Mechanism info structure passed to KCF during registration.
647c478bd9Sstevel@tonic-gate */
657c478bd9Sstevel@tonic-gate static crypto_mech_info_t rc4_mech_info_tab[] = {
667c478bd9Sstevel@tonic-gate {SUN_CKM_RC4, RC4_MECH_INFO_TYPE,
677c478bd9Sstevel@tonic-gate CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
687c478bd9Sstevel@tonic-gate CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC,
697c478bd9Sstevel@tonic-gate ARCFOUR_MIN_KEY_BITS, ARCFOUR_MAX_KEY_BITS,
706a1073f8Skrishna CRYPTO_KEYSIZE_UNIT_IN_BITS | CRYPTO_CAN_SHARE_OPSTATE}
717c478bd9Sstevel@tonic-gate };
727c478bd9Sstevel@tonic-gate
737c478bd9Sstevel@tonic-gate static void rc4_provider_status(crypto_provider_handle_t, uint_t *);
747c478bd9Sstevel@tonic-gate
757c478bd9Sstevel@tonic-gate static crypto_control_ops_t rc4_control_ops = {
767c478bd9Sstevel@tonic-gate rc4_provider_status
777c478bd9Sstevel@tonic-gate };
787c478bd9Sstevel@tonic-gate
797c478bd9Sstevel@tonic-gate static int rc4_common_init(crypto_ctx_t *, crypto_mechanism_t *,
807c478bd9Sstevel@tonic-gate crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
817c478bd9Sstevel@tonic-gate
827c478bd9Sstevel@tonic-gate static int rc4_crypt_update(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
837c478bd9Sstevel@tonic-gate crypto_req_handle_t);
847c478bd9Sstevel@tonic-gate
857c478bd9Sstevel@tonic-gate static int rc4_crypt_final(crypto_ctx_t *, crypto_data_t *,
867c478bd9Sstevel@tonic-gate crypto_req_handle_t);
877c478bd9Sstevel@tonic-gate
887c478bd9Sstevel@tonic-gate static int rc4_crypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
897c478bd9Sstevel@tonic-gate crypto_req_handle_t);
907c478bd9Sstevel@tonic-gate
917c478bd9Sstevel@tonic-gate static int rc4_crypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
927c478bd9Sstevel@tonic-gate crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
937c478bd9Sstevel@tonic-gate crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
947c478bd9Sstevel@tonic-gate
957c478bd9Sstevel@tonic-gate
967c478bd9Sstevel@tonic-gate static crypto_cipher_ops_t rc4_cipher_ops = {
977c478bd9Sstevel@tonic-gate rc4_common_init,
987c478bd9Sstevel@tonic-gate rc4_crypt,
997c478bd9Sstevel@tonic-gate rc4_crypt_update,
1007c478bd9Sstevel@tonic-gate rc4_crypt_final,
1017c478bd9Sstevel@tonic-gate rc4_crypt_atomic,
1027c478bd9Sstevel@tonic-gate rc4_common_init,
1037c478bd9Sstevel@tonic-gate rc4_crypt,
1047c478bd9Sstevel@tonic-gate rc4_crypt_update,
1057c478bd9Sstevel@tonic-gate rc4_crypt_final,
1067c478bd9Sstevel@tonic-gate rc4_crypt_atomic
1077c478bd9Sstevel@tonic-gate };
1087c478bd9Sstevel@tonic-gate
1097c478bd9Sstevel@tonic-gate static int rc4_free_context(crypto_ctx_t *);
1107c478bd9Sstevel@tonic-gate
1117c478bd9Sstevel@tonic-gate static crypto_ctx_ops_t rc4_ctx_ops = {
1127c478bd9Sstevel@tonic-gate NULL,
1137c478bd9Sstevel@tonic-gate rc4_free_context
1147c478bd9Sstevel@tonic-gate };
1157c478bd9Sstevel@tonic-gate
1167c478bd9Sstevel@tonic-gate static crypto_ops_t rc4_crypto_ops = {
1177c478bd9Sstevel@tonic-gate &rc4_control_ops,
1187c478bd9Sstevel@tonic-gate NULL,
1197c478bd9Sstevel@tonic-gate &rc4_cipher_ops,
1207c478bd9Sstevel@tonic-gate NULL,
1217c478bd9Sstevel@tonic-gate NULL,
1227c478bd9Sstevel@tonic-gate NULL,
1237c478bd9Sstevel@tonic-gate NULL,
1247c478bd9Sstevel@tonic-gate NULL,
1257c478bd9Sstevel@tonic-gate NULL,
1267c478bd9Sstevel@tonic-gate NULL,
1277c478bd9Sstevel@tonic-gate NULL,
1287c478bd9Sstevel@tonic-gate NULL,
1297c478bd9Sstevel@tonic-gate NULL,
1307c478bd9Sstevel@tonic-gate &rc4_ctx_ops
1317c478bd9Sstevel@tonic-gate };
1327c478bd9Sstevel@tonic-gate
1337c478bd9Sstevel@tonic-gate static crypto_provider_info_t rc4_prov_info = {
1347c478bd9Sstevel@tonic-gate CRYPTO_SPI_VERSION_1,
1357c478bd9Sstevel@tonic-gate "RC4 Software Provider",
1367c478bd9Sstevel@tonic-gate CRYPTO_SW_PROVIDER,
1377c478bd9Sstevel@tonic-gate {&modlinkage},
1387c478bd9Sstevel@tonic-gate NULL,
1397c478bd9Sstevel@tonic-gate &rc4_crypto_ops,
1407c478bd9Sstevel@tonic-gate sizeof (rc4_mech_info_tab)/sizeof (crypto_mech_info_t),
1417c478bd9Sstevel@tonic-gate rc4_mech_info_tab
1427c478bd9Sstevel@tonic-gate };
1437c478bd9Sstevel@tonic-gate
1447c478bd9Sstevel@tonic-gate static crypto_kcf_provider_handle_t rc4_prov_handle = NULL;
1457c478bd9Sstevel@tonic-gate
1467c478bd9Sstevel@tonic-gate static mblk_t *advance_position(mblk_t *, off_t, uchar_t **);
1477c478bd9Sstevel@tonic-gate static int crypto_arcfour_crypt(ARCFour_key *, uchar_t *, crypto_data_t *,
1487c478bd9Sstevel@tonic-gate int);
1497c478bd9Sstevel@tonic-gate
1507c478bd9Sstevel@tonic-gate int
_init(void)1517c478bd9Sstevel@tonic-gate _init(void)
1527c478bd9Sstevel@tonic-gate {
1537c478bd9Sstevel@tonic-gate int ret;
1547c478bd9Sstevel@tonic-gate
155*d3b2efc7SAnthony Scarpino if ((ret = mod_install(&modlinkage)) != 0)
156*d3b2efc7SAnthony Scarpino return (ret);
157*d3b2efc7SAnthony Scarpino
158*d3b2efc7SAnthony Scarpino /* Register with KCF. If the registration fails, remove the module. */
159*d3b2efc7SAnthony Scarpino if (crypto_register_provider(&rc4_prov_info, &rc4_prov_handle)) {
160*d3b2efc7SAnthony Scarpino (void) mod_remove(&modlinkage);
1617c478bd9Sstevel@tonic-gate return (EACCES);
1627c478bd9Sstevel@tonic-gate }
1637c478bd9Sstevel@tonic-gate
1647c478bd9Sstevel@tonic-gate return (0);
1657c478bd9Sstevel@tonic-gate }
1667c478bd9Sstevel@tonic-gate
1677c478bd9Sstevel@tonic-gate int
_fini(void)1687c478bd9Sstevel@tonic-gate _fini(void)
1697c478bd9Sstevel@tonic-gate {
170*d3b2efc7SAnthony Scarpino /* Unregister from KCF if module is registered */
1717c478bd9Sstevel@tonic-gate if (rc4_prov_handle != NULL) {
172*d3b2efc7SAnthony Scarpino if (crypto_unregister_provider(rc4_prov_handle))
1737c478bd9Sstevel@tonic-gate return (EBUSY);
174*d3b2efc7SAnthony Scarpino
1757c478bd9Sstevel@tonic-gate rc4_prov_handle = NULL;
1767c478bd9Sstevel@tonic-gate }
1777c478bd9Sstevel@tonic-gate
1787c478bd9Sstevel@tonic-gate return (mod_remove(&modlinkage));
1797c478bd9Sstevel@tonic-gate }
1807c478bd9Sstevel@tonic-gate
1817c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)1827c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1837c478bd9Sstevel@tonic-gate {
1847c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
1857c478bd9Sstevel@tonic-gate }
1867c478bd9Sstevel@tonic-gate
1877c478bd9Sstevel@tonic-gate
1887c478bd9Sstevel@tonic-gate /*
1897c478bd9Sstevel@tonic-gate * KCF software provider control entry points.
1907c478bd9Sstevel@tonic-gate */
1917c478bd9Sstevel@tonic-gate /* ARGSUSED */
1927c478bd9Sstevel@tonic-gate static void
rc4_provider_status(crypto_provider_handle_t provider,uint_t * status)1937c478bd9Sstevel@tonic-gate rc4_provider_status(crypto_provider_handle_t provider, uint_t *status)
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate *status = CRYPTO_PROVIDER_READY;
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate
1987c478bd9Sstevel@tonic-gate /* ARGSUSED */
1997c478bd9Sstevel@tonic-gate static int
rc4_common_init(crypto_ctx_t * ctx,crypto_mechanism_t * mechanism,crypto_key_t * key,crypto_spi_ctx_template_t template,crypto_req_handle_t req)2007c478bd9Sstevel@tonic-gate rc4_common_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
2017c478bd9Sstevel@tonic-gate crypto_key_t *key, crypto_spi_ctx_template_t template,
2027c478bd9Sstevel@tonic-gate crypto_req_handle_t req)
2037c478bd9Sstevel@tonic-gate {
2047c478bd9Sstevel@tonic-gate ARCFour_key *keystream;
2057c478bd9Sstevel@tonic-gate
2067c478bd9Sstevel@tonic-gate if ((mechanism)->cm_type != RC4_MECH_INFO_TYPE)
2077c478bd9Sstevel@tonic-gate return (CRYPTO_MECHANISM_INVALID);
2087c478bd9Sstevel@tonic-gate
2097c478bd9Sstevel@tonic-gate if (key->ck_format != CRYPTO_KEY_RAW)
2107c478bd9Sstevel@tonic-gate return (CRYPTO_KEY_TYPE_INCONSISTENT);
2117c478bd9Sstevel@tonic-gate
2127c478bd9Sstevel@tonic-gate if (key->ck_length < ARCFOUR_MIN_KEY_BITS ||
2137c478bd9Sstevel@tonic-gate key->ck_length > ARCFOUR_MAX_KEY_BITS) {
2147c478bd9Sstevel@tonic-gate return (CRYPTO_KEY_SIZE_RANGE);
2157c478bd9Sstevel@tonic-gate }
2167c478bd9Sstevel@tonic-gate
2177c478bd9Sstevel@tonic-gate /*
2187c478bd9Sstevel@tonic-gate * Allocate an RC4 key stream.
2197c478bd9Sstevel@tonic-gate */
2207c478bd9Sstevel@tonic-gate if ((keystream = kmem_alloc(sizeof (ARCFour_key),
2217c478bd9Sstevel@tonic-gate crypto_kmflag(req))) == NULL)
2227c478bd9Sstevel@tonic-gate return (CRYPTO_HOST_MEMORY);
2237c478bd9Sstevel@tonic-gate
22495014fbbSDan OpenSolaris Anderson arcfour_key_init(keystream, key->ck_data,
22595014fbbSDan OpenSolaris Anderson CRYPTO_BITS2BYTES(key->ck_length));
2267c478bd9Sstevel@tonic-gate
2277c478bd9Sstevel@tonic-gate ctx->cc_provider_private = keystream;
2287c478bd9Sstevel@tonic-gate
2297c478bd9Sstevel@tonic-gate return (CRYPTO_SUCCESS);
2307c478bd9Sstevel@tonic-gate }
2317c478bd9Sstevel@tonic-gate
2327c478bd9Sstevel@tonic-gate static int
rc4_crypt(crypto_ctx_t * ctx,crypto_data_t * input,crypto_data_t * output,crypto_req_handle_t req)2337c478bd9Sstevel@tonic-gate rc4_crypt(crypto_ctx_t *ctx, crypto_data_t *input, crypto_data_t *output,
2347c478bd9Sstevel@tonic-gate crypto_req_handle_t req)
2357c478bd9Sstevel@tonic-gate {
2367c478bd9Sstevel@tonic-gate int ret;
2377c478bd9Sstevel@tonic-gate
2387c478bd9Sstevel@tonic-gate ret = rc4_crypt_update(ctx, input, output, req);
2397c478bd9Sstevel@tonic-gate
2407c478bd9Sstevel@tonic-gate if (ret != CRYPTO_BUFFER_TOO_SMALL)
2417c478bd9Sstevel@tonic-gate (void) rc4_free_context(ctx);
2427c478bd9Sstevel@tonic-gate
2437c478bd9Sstevel@tonic-gate return (ret);
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate
2467c478bd9Sstevel@tonic-gate /* ARGSUSED */
2477c478bd9Sstevel@tonic-gate static int
rc4_crypt_update(crypto_ctx_t * ctx,crypto_data_t * input,crypto_data_t * output,crypto_req_handle_t req)2487c478bd9Sstevel@tonic-gate rc4_crypt_update(crypto_ctx_t *ctx, crypto_data_t *input, crypto_data_t *output,
2497c478bd9Sstevel@tonic-gate crypto_req_handle_t req)
2507c478bd9Sstevel@tonic-gate {
2517c478bd9Sstevel@tonic-gate int ret = CRYPTO_SUCCESS;
2527c478bd9Sstevel@tonic-gate
2537c478bd9Sstevel@tonic-gate ARCFour_key *key;
2547c478bd9Sstevel@tonic-gate off_t saveoffset;
2557c478bd9Sstevel@tonic-gate
2567c478bd9Sstevel@tonic-gate ASSERT(ctx->cc_provider_private != NULL);
2577c478bd9Sstevel@tonic-gate
2586a1073f8Skrishna if ((ctx->cc_flags & CRYPTO_USE_OPSTATE) && ctx->cc_opstate != NULL)
2596a1073f8Skrishna key = ctx->cc_opstate;
2606a1073f8Skrishna else
2617c478bd9Sstevel@tonic-gate key = ctx->cc_provider_private;
2627c478bd9Sstevel@tonic-gate
2637c478bd9Sstevel@tonic-gate /* Simple case: in-line encipherment */
2647c478bd9Sstevel@tonic-gate
2657c478bd9Sstevel@tonic-gate if (output == NULL) {
2667c478bd9Sstevel@tonic-gate switch (input->cd_format) {
2677c478bd9Sstevel@tonic-gate case CRYPTO_DATA_RAW: {
2687c478bd9Sstevel@tonic-gate char *start, *end;
2697c478bd9Sstevel@tonic-gate start = input->cd_raw.iov_base + input->cd_offset;
2707c478bd9Sstevel@tonic-gate
2717c478bd9Sstevel@tonic-gate end = input->cd_raw.iov_base + input->cd_raw.iov_len;
2727c478bd9Sstevel@tonic-gate
2737c478bd9Sstevel@tonic-gate if (start + input->cd_length > end)
2747c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_INVALID);
2757c478bd9Sstevel@tonic-gate
2767c478bd9Sstevel@tonic-gate arcfour_crypt(key, (uchar_t *)start, (uchar_t *)start,
2777c478bd9Sstevel@tonic-gate input->cd_length);
2787c478bd9Sstevel@tonic-gate break;
2797c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate case CRYPTO_DATA_MBLK: {
2817c478bd9Sstevel@tonic-gate uchar_t *start, *end;
2827c478bd9Sstevel@tonic-gate size_t len, left;
2837c478bd9Sstevel@tonic-gate mblk_t *mp = input->cd_mp, *mp1, *mp2;
2847c478bd9Sstevel@tonic-gate
2857c478bd9Sstevel@tonic-gate ASSERT(mp != NULL);
2867c478bd9Sstevel@tonic-gate
2877c478bd9Sstevel@tonic-gate mp1 = advance_position(mp, input->cd_offset, &start);
2887c478bd9Sstevel@tonic-gate
2897c478bd9Sstevel@tonic-gate if (mp1 == NULL)
2907c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
2917c478bd9Sstevel@tonic-gate
2927c478bd9Sstevel@tonic-gate mp2 = advance_position(mp, input->cd_offset +
2937c478bd9Sstevel@tonic-gate input->cd_length, &end);
2947c478bd9Sstevel@tonic-gate
2957c478bd9Sstevel@tonic-gate if (mp2 == NULL)
2967c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
2977c478bd9Sstevel@tonic-gate
2987c478bd9Sstevel@tonic-gate left = input->cd_length;
2997c478bd9Sstevel@tonic-gate while (mp1 != NULL) {
30092a8e44dSDan OpenSolaris Anderson if (_PTRDIFF(mp1->b_wptr, start) > left) {
3017c478bd9Sstevel@tonic-gate len = left;
3027c478bd9Sstevel@tonic-gate arcfour_crypt(key, start, start, len);
3037c478bd9Sstevel@tonic-gate mp1 = NULL;
3047c478bd9Sstevel@tonic-gate } else {
30592a8e44dSDan OpenSolaris Anderson len = _PTRDIFF(mp1->b_wptr, start);
3067c478bd9Sstevel@tonic-gate arcfour_crypt(key, start, start, len);
3077c478bd9Sstevel@tonic-gate mp1 = mp1->b_cont;
3087c478bd9Sstevel@tonic-gate start = mp1->b_rptr;
3097c478bd9Sstevel@tonic-gate left -= len;
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate }
3127c478bd9Sstevel@tonic-gate break;
3137c478bd9Sstevel@tonic-gate }
3147c478bd9Sstevel@tonic-gate case CRYPTO_DATA_UIO: {
3157c478bd9Sstevel@tonic-gate uio_t *uiop = input->cd_uio;
3167c478bd9Sstevel@tonic-gate off_t offset = input->cd_offset;
3177c478bd9Sstevel@tonic-gate size_t length = input->cd_length;
3187c478bd9Sstevel@tonic-gate uint_t vec_idx;
3197c478bd9Sstevel@tonic-gate size_t cur_len;
3207c478bd9Sstevel@tonic-gate
3217c478bd9Sstevel@tonic-gate /*
3227c478bd9Sstevel@tonic-gate * Jump to the first iovec containing data to be
3237c478bd9Sstevel@tonic-gate * processed.
3247c478bd9Sstevel@tonic-gate */
3257c478bd9Sstevel@tonic-gate for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
3267c478bd9Sstevel@tonic-gate offset >= uiop->uio_iov[vec_idx].iov_len;
327d2b32306Smcpowers offset -= uiop->uio_iov[vec_idx++].iov_len)
328d2b32306Smcpowers ;
3297c478bd9Sstevel@tonic-gate if (vec_idx == uiop->uio_iovcnt) {
3307c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
3317c478bd9Sstevel@tonic-gate }
3327c478bd9Sstevel@tonic-gate
3337c478bd9Sstevel@tonic-gate /*
3347c478bd9Sstevel@tonic-gate * Now process the iovecs.
3357c478bd9Sstevel@tonic-gate */
3367c478bd9Sstevel@tonic-gate while (vec_idx < uiop->uio_iovcnt && length > 0) {
3377c478bd9Sstevel@tonic-gate uchar_t *start;
3387c478bd9Sstevel@tonic-gate iovec_t *iovp = &(uiop->uio_iov[vec_idx]);
3397c478bd9Sstevel@tonic-gate
3407c478bd9Sstevel@tonic-gate cur_len = MIN(iovp->iov_len - offset, length);
3417c478bd9Sstevel@tonic-gate
3427c478bd9Sstevel@tonic-gate start = (uchar_t *)(iovp->iov_base + offset);
3437c478bd9Sstevel@tonic-gate arcfour_crypt(key, start + offset,
3447c478bd9Sstevel@tonic-gate start + offset, cur_len);
3457c478bd9Sstevel@tonic-gate
3467c478bd9Sstevel@tonic-gate length -= cur_len;
3477c478bd9Sstevel@tonic-gate vec_idx++;
3487c478bd9Sstevel@tonic-gate offset = 0;
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate
3517c478bd9Sstevel@tonic-gate if (vec_idx == uiop->uio_iovcnt && length > 0) {
3527c478bd9Sstevel@tonic-gate
3537c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
3547c478bd9Sstevel@tonic-gate }
3557c478bd9Sstevel@tonic-gate break;
3567c478bd9Sstevel@tonic-gate }
3577c478bd9Sstevel@tonic-gate }
3587c478bd9Sstevel@tonic-gate return (CRYPTO_SUCCESS);
3597c478bd9Sstevel@tonic-gate }
3607c478bd9Sstevel@tonic-gate
3617c478bd9Sstevel@tonic-gate /*
3627c478bd9Sstevel@tonic-gate * We need to just return the length needed to store the output.
3637c478bd9Sstevel@tonic-gate * We should not destroy the context for the following case.
3647c478bd9Sstevel@tonic-gate */
3657c478bd9Sstevel@tonic-gate
3667c478bd9Sstevel@tonic-gate if (input->cd_length > output->cd_length) {
3677c478bd9Sstevel@tonic-gate output->cd_length = input->cd_length;
3687c478bd9Sstevel@tonic-gate return (CRYPTO_BUFFER_TOO_SMALL);
3697c478bd9Sstevel@tonic-gate }
3707c478bd9Sstevel@tonic-gate
3717c478bd9Sstevel@tonic-gate saveoffset = output->cd_offset;
3727c478bd9Sstevel@tonic-gate
3737c478bd9Sstevel@tonic-gate switch (input->cd_format) {
3747c478bd9Sstevel@tonic-gate case CRYPTO_DATA_RAW: {
3757c478bd9Sstevel@tonic-gate char *start, *end;
3767c478bd9Sstevel@tonic-gate start = input->cd_raw.iov_base + input->cd_offset;
3777c478bd9Sstevel@tonic-gate
3787c478bd9Sstevel@tonic-gate end = input->cd_raw.iov_base + input->cd_raw.iov_len;
3797c478bd9Sstevel@tonic-gate
3807c478bd9Sstevel@tonic-gate if (start + input->cd_length > end)
3817c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
3827c478bd9Sstevel@tonic-gate
3837c478bd9Sstevel@tonic-gate ret = crypto_arcfour_crypt(key, (uchar_t *)start, output,
3847c478bd9Sstevel@tonic-gate input->cd_length);
3857c478bd9Sstevel@tonic-gate
3867c478bd9Sstevel@tonic-gate if (ret != CRYPTO_SUCCESS)
3877c478bd9Sstevel@tonic-gate return (ret);
3887c478bd9Sstevel@tonic-gate break;
3897c478bd9Sstevel@tonic-gate }
3907c478bd9Sstevel@tonic-gate case CRYPTO_DATA_MBLK: {
3917c478bd9Sstevel@tonic-gate uchar_t *start, *end;
3927c478bd9Sstevel@tonic-gate size_t len, left;
3937c478bd9Sstevel@tonic-gate mblk_t *mp = input->cd_mp, *mp1, *mp2;
3947c478bd9Sstevel@tonic-gate
3957c478bd9Sstevel@tonic-gate ASSERT(mp != NULL);
3967c478bd9Sstevel@tonic-gate
3977c478bd9Sstevel@tonic-gate mp1 = advance_position(mp, input->cd_offset, &start);
3987c478bd9Sstevel@tonic-gate
3997c478bd9Sstevel@tonic-gate if (mp1 == NULL)
4007c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
4017c478bd9Sstevel@tonic-gate
4027c478bd9Sstevel@tonic-gate mp2 = advance_position(mp, input->cd_offset + input->cd_length,
4037c478bd9Sstevel@tonic-gate &end);
4047c478bd9Sstevel@tonic-gate
4057c478bd9Sstevel@tonic-gate if (mp2 == NULL)
4067c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
4077c478bd9Sstevel@tonic-gate
4087c478bd9Sstevel@tonic-gate left = input->cd_length;
4097c478bd9Sstevel@tonic-gate while (mp1 != NULL) {
41092a8e44dSDan OpenSolaris Anderson if (_PTRDIFF(mp1->b_wptr, start) > left) {
4117c478bd9Sstevel@tonic-gate len = left;
4127c478bd9Sstevel@tonic-gate ret = crypto_arcfour_crypt(key, start, output,
4137c478bd9Sstevel@tonic-gate len);
4147c478bd9Sstevel@tonic-gate if (ret != CRYPTO_SUCCESS)
4157c478bd9Sstevel@tonic-gate return (ret);
4167c478bd9Sstevel@tonic-gate mp1 = NULL;
4177c478bd9Sstevel@tonic-gate } else {
41892a8e44dSDan OpenSolaris Anderson len = _PTRDIFF(mp1->b_wptr, start);
4197c478bd9Sstevel@tonic-gate ret = crypto_arcfour_crypt(key, start, output,
4207c478bd9Sstevel@tonic-gate len);
4217c478bd9Sstevel@tonic-gate if (ret != CRYPTO_SUCCESS)
4227c478bd9Sstevel@tonic-gate return (ret);
4237c478bd9Sstevel@tonic-gate mp1 = mp1->b_cont;
4247c478bd9Sstevel@tonic-gate start = mp1->b_rptr;
4257c478bd9Sstevel@tonic-gate left -= len;
4267c478bd9Sstevel@tonic-gate output->cd_offset += len;
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate }
4297c478bd9Sstevel@tonic-gate break;
4307c478bd9Sstevel@tonic-gate }
4317c478bd9Sstevel@tonic-gate case CRYPTO_DATA_UIO: {
4327c478bd9Sstevel@tonic-gate uio_t *uiop = input->cd_uio;
4337c478bd9Sstevel@tonic-gate off_t offset = input->cd_offset;
4347c478bd9Sstevel@tonic-gate size_t length = input->cd_length;
4357c478bd9Sstevel@tonic-gate uint_t vec_idx;
4367c478bd9Sstevel@tonic-gate size_t cur_len;
4377c478bd9Sstevel@tonic-gate
4387c478bd9Sstevel@tonic-gate /*
4397c478bd9Sstevel@tonic-gate * Jump to the first iovec containing data to be
4407c478bd9Sstevel@tonic-gate * processed.
4417c478bd9Sstevel@tonic-gate */
4427c478bd9Sstevel@tonic-gate for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
4437c478bd9Sstevel@tonic-gate offset >= uiop->uio_iov[vec_idx].iov_len;
444d2b32306Smcpowers offset -= uiop->uio_iov[vec_idx++].iov_len)
445d2b32306Smcpowers ;
4467c478bd9Sstevel@tonic-gate if (vec_idx == uiop->uio_iovcnt) {
4477c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
4487c478bd9Sstevel@tonic-gate }
4497c478bd9Sstevel@tonic-gate
4507c478bd9Sstevel@tonic-gate /*
4517c478bd9Sstevel@tonic-gate * Now process the iovecs.
4527c478bd9Sstevel@tonic-gate */
4537c478bd9Sstevel@tonic-gate while (vec_idx < uiop->uio_iovcnt && length > 0) {
4547c478bd9Sstevel@tonic-gate uchar_t *start;
4557c478bd9Sstevel@tonic-gate iovec_t *iovp = &(uiop->uio_iov[vec_idx]);
4567c478bd9Sstevel@tonic-gate cur_len = MIN(iovp->iov_len - offset, length);
4577c478bd9Sstevel@tonic-gate
4587c478bd9Sstevel@tonic-gate start = (uchar_t *)(iovp->iov_base + offset);
4597c478bd9Sstevel@tonic-gate ret = crypto_arcfour_crypt(key, start + offset,
4607c478bd9Sstevel@tonic-gate output, cur_len);
4617c478bd9Sstevel@tonic-gate if (ret != CRYPTO_SUCCESS)
4627c478bd9Sstevel@tonic-gate return (ret);
4637c478bd9Sstevel@tonic-gate
4647c478bd9Sstevel@tonic-gate length -= cur_len;
4657c478bd9Sstevel@tonic-gate vec_idx++;
4667c478bd9Sstevel@tonic-gate offset = 0;
4677c478bd9Sstevel@tonic-gate output->cd_offset += cur_len;
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate
4707c478bd9Sstevel@tonic-gate if (vec_idx == uiop->uio_iovcnt && length > 0) {
4717c478bd9Sstevel@tonic-gate
4727c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
4737c478bd9Sstevel@tonic-gate }
4747c478bd9Sstevel@tonic-gate }
4757c478bd9Sstevel@tonic-gate }
4767c478bd9Sstevel@tonic-gate
4777c478bd9Sstevel@tonic-gate output->cd_offset = saveoffset;
4787c478bd9Sstevel@tonic-gate output->cd_length = input->cd_length;
4797c478bd9Sstevel@tonic-gate
4807c478bd9Sstevel@tonic-gate return (ret);
4817c478bd9Sstevel@tonic-gate }
4827c478bd9Sstevel@tonic-gate
4837c478bd9Sstevel@tonic-gate /* ARGSUSED */
rc4_crypt_final(crypto_ctx_t * ctx,crypto_data_t * data,crypto_req_handle_t req)4847c478bd9Sstevel@tonic-gate static int rc4_crypt_final(crypto_ctx_t *ctx, crypto_data_t *data,
4857c478bd9Sstevel@tonic-gate crypto_req_handle_t req)
4867c478bd9Sstevel@tonic-gate {
4877c478bd9Sstevel@tonic-gate /* No final part for streams ciphers. Just free the context */
4887c478bd9Sstevel@tonic-gate if (data != NULL)
4897c478bd9Sstevel@tonic-gate data->cd_length = 0;
4907c478bd9Sstevel@tonic-gate
4917c478bd9Sstevel@tonic-gate return (rc4_free_context(ctx));
4927c478bd9Sstevel@tonic-gate }
4937c478bd9Sstevel@tonic-gate
4947c478bd9Sstevel@tonic-gate /* ARGSUSED */
4957c478bd9Sstevel@tonic-gate static int
rc4_crypt_atomic(crypto_provider_handle_t handle,crypto_session_id_t session,crypto_mechanism_t * mechanism,crypto_key_t * key,crypto_data_t * input,crypto_data_t * output,crypto_spi_ctx_template_t template,crypto_req_handle_t req)4967c478bd9Sstevel@tonic-gate rc4_crypt_atomic(crypto_provider_handle_t handle, crypto_session_id_t session,
4977c478bd9Sstevel@tonic-gate crypto_mechanism_t *mechanism, crypto_key_t *key, crypto_data_t *input,
4987c478bd9Sstevel@tonic-gate crypto_data_t *output, crypto_spi_ctx_template_t template,
4997c478bd9Sstevel@tonic-gate crypto_req_handle_t req)
5007c478bd9Sstevel@tonic-gate {
5017c478bd9Sstevel@tonic-gate crypto_ctx_t ctx;
5027c478bd9Sstevel@tonic-gate int ret;
5037c478bd9Sstevel@tonic-gate
5046a1073f8Skrishna bzero(&ctx, sizeof (crypto_ctx_t));
5057c478bd9Sstevel@tonic-gate ret = rc4_common_init(&ctx, mechanism, key, template, req);
5067c478bd9Sstevel@tonic-gate
5077c478bd9Sstevel@tonic-gate if (ret != CRYPTO_SUCCESS)
5087c478bd9Sstevel@tonic-gate return (ret);
5097c478bd9Sstevel@tonic-gate
5107c478bd9Sstevel@tonic-gate ret = rc4_crypt_update(&ctx, input, output, req);
5117c478bd9Sstevel@tonic-gate
5127c478bd9Sstevel@tonic-gate (void) rc4_free_context(&ctx);
5137c478bd9Sstevel@tonic-gate
5147c478bd9Sstevel@tonic-gate return (ret);
5157c478bd9Sstevel@tonic-gate }
5167c478bd9Sstevel@tonic-gate
5177c478bd9Sstevel@tonic-gate /* ARGSUSED */
5187c478bd9Sstevel@tonic-gate static int
rc4_free_context(crypto_ctx_t * ctx)5197c478bd9Sstevel@tonic-gate rc4_free_context(crypto_ctx_t *ctx)
5207c478bd9Sstevel@tonic-gate {
5217c478bd9Sstevel@tonic-gate ARCFour_key *keystream = ctx->cc_provider_private;
5227c478bd9Sstevel@tonic-gate
5237c478bd9Sstevel@tonic-gate if (keystream != NULL) {
5247c478bd9Sstevel@tonic-gate bzero(keystream, sizeof (ARCFour_key));
5257c478bd9Sstevel@tonic-gate kmem_free(keystream, sizeof (ARCFour_key));
5267c478bd9Sstevel@tonic-gate ctx->cc_provider_private = NULL;
5277c478bd9Sstevel@tonic-gate }
5287c478bd9Sstevel@tonic-gate
5297c478bd9Sstevel@tonic-gate return (CRYPTO_SUCCESS);
5307c478bd9Sstevel@tonic-gate }
5317c478bd9Sstevel@tonic-gate
5327c478bd9Sstevel@tonic-gate /* Encrypts a contiguous input 'in' into the 'out' crypto_data_t */
5337c478bd9Sstevel@tonic-gate
5347c478bd9Sstevel@tonic-gate static int
crypto_arcfour_crypt(ARCFour_key * key,uchar_t * in,crypto_data_t * out,int length)5357c478bd9Sstevel@tonic-gate crypto_arcfour_crypt(ARCFour_key *key, uchar_t *in, crypto_data_t *out,
5367c478bd9Sstevel@tonic-gate int length)
5377c478bd9Sstevel@tonic-gate {
5387c478bd9Sstevel@tonic-gate switch (out->cd_format) {
5397c478bd9Sstevel@tonic-gate case CRYPTO_DATA_RAW: {
5407c478bd9Sstevel@tonic-gate uchar_t *start, *end;
5417c478bd9Sstevel@tonic-gate start = (uchar_t *)(out->cd_raw.iov_base +
5427c478bd9Sstevel@tonic-gate out->cd_offset);
5437c478bd9Sstevel@tonic-gate
5447c478bd9Sstevel@tonic-gate end = (uchar_t *)(out->cd_raw.iov_base +
5457c478bd9Sstevel@tonic-gate out->cd_raw.iov_len);
5467c478bd9Sstevel@tonic-gate
5477c478bd9Sstevel@tonic-gate if (start + out->cd_length > end)
5487c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
5497c478bd9Sstevel@tonic-gate
5507c478bd9Sstevel@tonic-gate arcfour_crypt(key, in, start, length);
5517c478bd9Sstevel@tonic-gate
5527c478bd9Sstevel@tonic-gate return (CRYPTO_SUCCESS);
5537c478bd9Sstevel@tonic-gate }
5547c478bd9Sstevel@tonic-gate case CRYPTO_DATA_MBLK: {
5557c478bd9Sstevel@tonic-gate uchar_t *start, *end;
5567c478bd9Sstevel@tonic-gate size_t len, left;
5577c478bd9Sstevel@tonic-gate mblk_t *mp = out->cd_mp, *mp1, *mp2;
5587c478bd9Sstevel@tonic-gate
5597c478bd9Sstevel@tonic-gate ASSERT(mp != NULL);
5607c478bd9Sstevel@tonic-gate
5617c478bd9Sstevel@tonic-gate mp1 = advance_position(mp, out->cd_offset, &start);
5627c478bd9Sstevel@tonic-gate
5637c478bd9Sstevel@tonic-gate if (mp1 == NULL)
5647c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
5657c478bd9Sstevel@tonic-gate
5667c478bd9Sstevel@tonic-gate mp2 = advance_position(mp, out->cd_offset +
5677c478bd9Sstevel@tonic-gate out->cd_length, &end);
5687c478bd9Sstevel@tonic-gate
5697c478bd9Sstevel@tonic-gate if (mp2 == NULL)
5707c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
5717c478bd9Sstevel@tonic-gate
5727c478bd9Sstevel@tonic-gate left = length;
5737c478bd9Sstevel@tonic-gate while (mp1 != NULL) {
57492a8e44dSDan OpenSolaris Anderson if (_PTRDIFF(mp1->b_wptr, start) > left) {
5757c478bd9Sstevel@tonic-gate len = left;
5767c478bd9Sstevel@tonic-gate arcfour_crypt(key, in, start, len);
5777c478bd9Sstevel@tonic-gate mp1 = NULL;
5787c478bd9Sstevel@tonic-gate } else {
57992a8e44dSDan OpenSolaris Anderson len = _PTRDIFF(mp1->b_wptr, start);
5807c478bd9Sstevel@tonic-gate arcfour_crypt(key, in, start, len);
5817c478bd9Sstevel@tonic-gate mp1 = mp1->b_cont;
5827c478bd9Sstevel@tonic-gate start = mp1->b_rptr;
5837c478bd9Sstevel@tonic-gate left -= len;
5847c478bd9Sstevel@tonic-gate }
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate break;
5877c478bd9Sstevel@tonic-gate }
5887c478bd9Sstevel@tonic-gate case CRYPTO_DATA_UIO: {
5897c478bd9Sstevel@tonic-gate uio_t *uiop = out->cd_uio;
5907c478bd9Sstevel@tonic-gate off_t offset = out->cd_offset;
5917c478bd9Sstevel@tonic-gate size_t len = length;
5927c478bd9Sstevel@tonic-gate uint_t vec_idx;
5937c478bd9Sstevel@tonic-gate size_t cur_len;
5947c478bd9Sstevel@tonic-gate
5957c478bd9Sstevel@tonic-gate /*
5967c478bd9Sstevel@tonic-gate * Jump to the first iovec containing data to be
5977c478bd9Sstevel@tonic-gate * processed.
5987c478bd9Sstevel@tonic-gate */
5997c478bd9Sstevel@tonic-gate for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
6007c478bd9Sstevel@tonic-gate offset >= uiop->uio_iov[vec_idx].iov_len;
601d2b32306Smcpowers offset -= uiop->uio_iov[vec_idx++].iov_len)
602d2b32306Smcpowers ;
6037c478bd9Sstevel@tonic-gate if (vec_idx == uiop->uio_iovcnt) {
6047c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
6057c478bd9Sstevel@tonic-gate }
6067c478bd9Sstevel@tonic-gate
6077c478bd9Sstevel@tonic-gate /*
6087c478bd9Sstevel@tonic-gate * Now process the iovecs.
6097c478bd9Sstevel@tonic-gate */
6107c478bd9Sstevel@tonic-gate while (vec_idx < uiop->uio_iovcnt && len > 0) {
6117c478bd9Sstevel@tonic-gate uchar_t *start;
6127c478bd9Sstevel@tonic-gate iovec_t *iovp = &(uiop->uio_iov[vec_idx]);
6137c478bd9Sstevel@tonic-gate cur_len = MIN(iovp->iov_len - offset, len);
6147c478bd9Sstevel@tonic-gate
6157c478bd9Sstevel@tonic-gate start = (uchar_t *)(iovp->iov_base + offset);
6167c478bd9Sstevel@tonic-gate arcfour_crypt(key, start + offset,
6177c478bd9Sstevel@tonic-gate start + offset, cur_len);
6187c478bd9Sstevel@tonic-gate
6197c478bd9Sstevel@tonic-gate len -= cur_len;
6207c478bd9Sstevel@tonic-gate vec_idx++;
6217c478bd9Sstevel@tonic-gate offset = 0;
6227c478bd9Sstevel@tonic-gate }
6237c478bd9Sstevel@tonic-gate
6247c478bd9Sstevel@tonic-gate if (vec_idx == uiop->uio_iovcnt && len > 0) {
6257c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_LEN_RANGE);
6267c478bd9Sstevel@tonic-gate }
6277c478bd9Sstevel@tonic-gate break;
6287c478bd9Sstevel@tonic-gate }
6297c478bd9Sstevel@tonic-gate default:
6307c478bd9Sstevel@tonic-gate return (CRYPTO_DATA_INVALID);
6317c478bd9Sstevel@tonic-gate }
6327c478bd9Sstevel@tonic-gate return (CRYPTO_SUCCESS);
6337c478bd9Sstevel@tonic-gate }
6347c478bd9Sstevel@tonic-gate
6357c478bd9Sstevel@tonic-gate /*
6367c478bd9Sstevel@tonic-gate * Advances 'offset' bytes from the beginning of the first block in 'mp',
6377c478bd9Sstevel@tonic-gate * possibly jumping across b_cont boundary
6387c478bd9Sstevel@tonic-gate * '*cpp' is set to the position of the byte we want, and the block where
6397c478bd9Sstevel@tonic-gate * 'cpp' is returned.
6407c478bd9Sstevel@tonic-gate */
6417c478bd9Sstevel@tonic-gate static mblk_t *
advance_position(mblk_t * mp,off_t offset,uchar_t ** cpp)6427c478bd9Sstevel@tonic-gate advance_position(mblk_t *mp, off_t offset, uchar_t **cpp)
6437c478bd9Sstevel@tonic-gate {
6447c478bd9Sstevel@tonic-gate mblk_t *mp1 = mp;
6457c478bd9Sstevel@tonic-gate size_t l;
6467c478bd9Sstevel@tonic-gate off_t o = offset;
6477c478bd9Sstevel@tonic-gate
6487c478bd9Sstevel@tonic-gate while (mp1 != NULL) {
6497c478bd9Sstevel@tonic-gate l = MBLKL(mp1);
6507c478bd9Sstevel@tonic-gate
6517c478bd9Sstevel@tonic-gate if (l <= o) {
6527c478bd9Sstevel@tonic-gate o -= l;
6537c478bd9Sstevel@tonic-gate mp1 = mp1->b_cont;
6547c478bd9Sstevel@tonic-gate } else {
6557c478bd9Sstevel@tonic-gate *cpp = (uchar_t *)(mp1->b_rptr + o);
6567c478bd9Sstevel@tonic-gate break;
6577c478bd9Sstevel@tonic-gate }
6587c478bd9Sstevel@tonic-gate }
6597c478bd9Sstevel@tonic-gate return (mp1);
6607c478bd9Sstevel@tonic-gate }
661