/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* STREAMS Crypto Module
*
* This module is used to facilitate Kerberos encryption
* operations for the telnet daemon and rlogin daemon.
* Because the Solaris telnet and rlogin daemons run mostly
* in-kernel via 'telmod' and 'rlmod', this module must be
* pushed on the STREAM *below* telmod or rlmod.
*
* Parts of the 3DES key derivation code are covered by the
* following copyright.
*
* Copyright (C) 1998 by the FundsXpress, INC.
*
* All rights reserved.
*
* Export of this software from the United States of America may require
* a specific license from the United States Government. It is the
* responsibility of any person or organization contemplating export to
* obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of FundsXpress. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. FundsXpress makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <sys/sysmacros.h>
#include <sys/byteorder.h>
#include <sys/cryptmod.h>
/*
* Function prototypes.
*/
static int cryptmodclose(queue_t *);
static int cryptmodwsrv(queue_t *);
static int cryptmodrsrv(queue_t *);
CRYPTMOD_ID, /* mi_idnum */
"cryptmod", /* mi_idname */
0, /* mi_minpsz */
INFPSZ, /* mi_maxpsz */
65536, /* mi_hiwat */
1024 /* mi_lowat */
};
(int (*)())cryptmodrput, /* qi_putp */
cryptmodrsrv, /* qi_svc */
cryptmodopen, /* qi_qopen */
cryptmodclose, /* qi_qclose */
NULL, /* qi_qadmin */
&cryptmod_minfo, /* qi_minfo */
NULL /* qi_mstat */
};
(int (*)())cryptmodwput, /* qi_putp */
cryptmodwsrv, /* qi_srvp */
NULL, /* qi_qopen */
NULL, /* qi_qclose */
NULL, /* qi_qadmin */
&cryptmod_minfo, /* qi_minfo */
NULL /* qi_mstat */
};
&cryptmod_rinit, /* st_rdinit */
&cryptmod_winit, /* st_wrinit */
NULL, /* st_muxrinit */
NULL /* st_muxwinit */
};
typedef struct {
int (*hashfunc)();
} hash_info_t;
static int kef_crypt(struct cipher_data_t *, void *,
crypto_data_format_t, size_t, int);
static mblk_t *
mblk_t *, hash_info_t *);
static mblk_t *
mblk_t *, hash_info_t *);
static int
/*
* This is the loadable module wrapper.
*/
"cryptmod",
};
/*
* Module linkage information for the kernel.
*/
"STREAMS encryption module",
&fsw
};
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
static void
{
}
/*
* ckey is a crypto_key_t structure which references
* "cd->key" for its raw key data. Since that was already
* cleared out, we don't need another "bzero" here.
*/
}
}
}
}
}
}
}
}
/* ARGSUSED */
static int
{
return (EINVAL);
"cryptmodopen: opening module(PID %d)",
ddi_get_pid()));
return (0);
}
/*
* Allocate and initialize per-Stream structure.
*/
KM_SLEEP);
return (0);
}
static int
{
return (0);
}
/*
* plaintext_offset
*
* Calculate exactly how much space is needed in front
* of the "plaintext" in an mbuf so it can be positioned
* 1 time instead of potentially moving the data multiple
* times.
*/
static int
{
int headspace = 0;
/* 4 byte length prepended to all RCMD msgs */
headspace += RCMD_LEN_SZ;
/* RCMD V2 mode adds an additional 4 byte plaintext length */
headspace += RCMD_LEN_SZ;
/* Need extra space for hash and counfounder */
break;
case CRYPT_METHOD_DES_CBC_CRC:
break;
case CRYPT_METHOD_DES_CBC_MD5:
break;
break;
break;
case CRYPT_METHOD_AES128:
case CRYPT_METHOD_AES256:
break;
case CRYPT_METHOD_DES_CFB:
case CRYPT_METHOD_NONE:
break;
}
return (headspace);
}
/*
* encrypt_size
*
* Calculate the resulting size when encrypting 'plainlen' bytes
* of data.
*/
static size_t
{
plainlen, 8);
break;
case CRYPT_METHOD_DES_CBC_MD5:
plainlen, 8);
break;
case CRYPT_METHOD_DES_CBC_CRC:
plainlen, 8);
break;
plainlen, 8) +
break;
break;
case CRYPT_METHOD_AES128:
case CRYPT_METHOD_AES256:
/* No roundup for AES-CBC-CTS */
break;
case CRYPT_METHOD_DES_CFB:
case CRYPT_METHOD_NONE:
break;
}
return (cipherlen);
}
/*
* des_cfb_encrypt
*
* Encrypt the mblk data using DES with cipher feedback.
*
* Given that V[i] is the initial 64 bit vector, V[n] is the nth 64 bit
* vector, D[n] is the nth chunk of 64 bits of data to encrypt
* (decrypt), and O[n] is the nth chunk of 64 bits of encrypted
* (decrypted) data, then:
*
* V[0] = DES(V[i], key)
* O[n] = D[n] <exclusive or > V[n]
* V[n+1] = DES(O[n], key)
*
* The size of the message being encrypted does not change in this
* algorithm, num_bytes in == num_bytes out.
*/
static mblk_t *
{
int savedbytes;
/*
* Do DES-ECB.
* The first time this runs, the 'tmi->enc_data.block' will
* contain the initialization vector that should have been
* passed in with the SETUP ioctl.
*
* V[n] = DES(V[n-1], key)
*/
int retval = 0;
if (retval != CRYPTO_SUCCESS) {
#ifdef DEBUG
"failed - error 0x%0x", retval);
#endif
return (NULL);
}
}
/* O[n] = I[n] ^ V[n] */
/*
* Feedback the encrypted output as the input to next DES call.
*/
/*
* Get the last bits of input from the previous
* msg block that we haven't yet used as feedback input.
*/
if (savedbytes > 0) {
dbptr += savedbytes;
}
/*
* Now copy the correct bytes from the current input
* stream and update the 'lastoutput' ptr
*/
savedbytes = 0;
}
}
/*
* If there are bytes of input here that we need in the next
* block to build an ivec, save them off here.
*/
if (lastoutput < optr) {
}
return (mp);
}
/*
* des_cfb_decrypt
*
* Decrypt the data in the mblk using DES in Cipher Feedback mode
*
* # bytes in == # bytes out, no padding, confounding, or hashing
* is added.
*
*/
static mblk_t *
{
char *iptr;
char *lastinput;
/* decrypted output goes into the new data buffer */
/*
* Save the input CFB_BLKSZ bytes at a time.
* We are trying to decrypt in-place, but need to keep
* a small sliding window of encrypted text to be
* used to construct the feedback buffer.
*/
savedbytes += cp;
/*
* Do DES-ECB.
* The first time this runs, the 'tmi->dec_data.block' will
* contain the initialization vector that should have been
* passed in with the SETUP ioctl.
*/
int retval;
if (retval != CRYPTO_SUCCESS) {
#ifdef DEBUG
"failed - status 0x%0x", retval);
#endif
return (NULL);
}
}
/*
* To decrypt, XOR the input with the output from the DES call
*/
/*
* Feedback the encrypted input for next DES call.
*/
/*
* Get the last bits of input from the previous block
* that we haven't yet processed.
*/
if (savedbytes > 0) {
dbptr, savedbytes);
dbptr += savedbytes;
}
savedbytes = 0;
/*
* This block makes sure that our local
* buffer of input data is full and can
* be accessed from the beginning.
*/
/* How many bytes are left in the mblk? */
/* copy what we need */
cp);
savedbytes = cp;
}
}
}
return (mp);
}
/*
* crc32_calc
*
* Compute a CRC32 checksum on the input
*/
static int
{
return (CRYPTO_SUCCESS);
}
static int
{
int rv;
mech.cm_param_len = 0;
return (rv);
}
/*
* sha1_calc
*
* Get a SHA1 hash on the input data.
*/
static int
{
int rv;
return (rv);
}
/*
* Get an MD5 hash on the input data.
* md5_calc
*
*/
static int
{
int rv;
return (rv);
}
/*
* nfold
* duplicate the functionality of the krb5_nfold function from
* the userland kerberos mech.
* ciphers.
*/
static void
{
int a, b, c, lcm;
inbits >>= 3;
outbits >>= 3;
/* first compute lcm(n,k) */
a = outbits;
b = inbits;
while (b != 0) {
c = b;
b = a%b;
a = c;
}
/* now do the real work */
byte = 0;
/*
* Compute the msbit in k which gets added into this byte
* first, start with the msbit in the first, unrotated byte
* then, for each byte, shift to the right for each repetition
* last, pick out the correct byte within that shifted repetition
*/
for (i = lcm-1; i >= 0; i--) {
/* pull out the byte value itself */
/* do the addition */
byte >>= 8;
}
/* if there's a carry bit left over, add it back in */
if (byte) {
for (i = outbits-1; i >= 0; i--) {
/* do the addition */
/* keep around the carry bit, if any */
byte >>= 8;
}
}
}
/*
* Duplicate the functionality of the "dk_derive_key" function
* in the Kerberos mechanism.
*/
static int
int blocklen)
{
int rv = 0;
int n = 0, i;
char *inblock;
char *rawkey;
char *zeroblock;
char *saveblock;
else
/*
* zeroblock is an IV of all 0's.
*
* The "block" section of the cdata record is used as the
* IV for crypto operations in the kef_crypt function.
*
* We use 'block' as a generic IV data buffer because it
* is attached to the stream state data and thus can
* be used to hold information that must carry over
* from processing of one mblk to another.
*
* Here, we save the current IV and replace it with
* and empty IV (all 0's) for use when deriving the
* keys. Once the key derivation is done, we swap the
* old IV back into place.
*/
while (n < keybytes) {
if (rv != CRYPTO_SUCCESS) {
/* put the original IV block back in place */
goto cleanup;
}
break;
}
n += blocklen;
}
/* put the original IV block back in place */
/* finally, make the key */
/*
* 3DES key derivation requires that we make sure the
* key has the proper parity.
*/
for (i = 0; i < 3; i++) {
/* 'dkey' is our derived key output buffer */
for (n = 0; n < 8; n++) {
}
}
}
return (rv);
}
/*
* create_derived_keys
*
* Algorithm for deriving a new key and an HMAC key
* before computing the 3DES-SHA1-HMAC operation on the plaintext
* This algorithm matches the work done by Kerberos mechanism
* in userland.
*/
static int
{
int keybytes;
int rv;
/* Use "0xAA" for deriving encryption key */
case CRYPT_METHOD_DES_CFB:
case CRYPT_METHOD_DES_CBC_MD5:
case CRYPT_METHOD_DES_CBC_CRC:
keybytes = 8;
break;
break;
break;
case CRYPT_METHOD_AES128:
break;
case CRYPT_METHOD_AES256:
break;
}
/* derive main crypto key */
if (rv == CRYPTO_SUCCESS) {
/* Use "0x55" for deriving mac key */
} else {
}
return (rv);
}
/*
* Compute 3-DES crypto and HMAC.
*/
static int
{
/*
* cdata->block holds the IVEC
*/
else
encr_mech.cm_param_len = 0;
if (rv != CRYPTO_SUCCESS) {
return (rv);
}
mac_mech.cm_param_len = 0;
/*
* Compute MAC of the plaintext decrypted above.
*/
if (rv != CRYPTO_SUCCESS) {
}
return (rv);
}
/*
* Compute 3-DES crypto and HMAC.
*/
static int
{
/*
* cdata->block holds the IVEC
*/
else
encr_mech.cm_param_len = 0;
mac_mech.cm_param_len = 0;
if (rv != CRYPTO_SUCCESS) {
return (rv);
}
if (rv != CRYPTO_SUCCESS) {
}
return (rv);
}
/*
* kef_crypt
*
* Use the Kernel encryption framework to provide the
* crypto operations for the indicated data.
*/
static int
{
/* keys are measured in bits, not bytes, so multiply by 8 */
if (fmt == CRYPTO_DATA_RAW) {
}
if (fmt == CRYPTO_DATA_RAW)
else if (fmt == CRYPTO_DATA_MBLK)
/*
* cdata->block holds the IVEC
*/
else
mech.cm_param_len = 0;
/*
* encrypt and decrypt in-place
*/
if (mode == CRYPT_ENCRYPT)
else
if (rv != CRYPTO_SUCCESS) {
"crypto_decrypt"), rv);
return (CRYPTO_FAILED);
}
return (rv);
}
static int
{
int rv = 0;
mac_mech.cm_param_len = 0;
/*
* Compute MAC of the plaintext decrypted above.
*/
if (rv != CRYPTO_SUCCESS) {
}
return (rv);
}
static int
{
} else {
}
mech.cm_param_len = 0;
if (nblocks == 1) {
if (result != CRYPTO_SUCCESS) {
"crypto_encrypt failed: %0x", result);
}
} else {
if (result != CRYPTO_SUCCESS) {
"crypto_encrypt_init failed: %0x", result);
goto cleanup;
}
if (result != CRYPTO_SUCCESS) {
"crypto_encrypt_update failed: %0x",
result);
goto cleanup;
}
/* copy result over original bytes */
/* make another copy for the next XOR step */
}
/* XOR cipher text from n-3 with plain text from n-2 */
/* encrypt XOR-ed block N-2 */
if (result != CRYPTO_SUCCESS) {
"crypto_encrypt_update(2) failed: %0x",
result);
goto cleanup;
}
/* Save final plaintext bytes from n-1 */
nleft);
/* Overwrite n-1 with cipher text from n-2 */
nleft);
/* XOR cipher text from n-1 with plain text from n-1 */
/* encrypt block N-2 */
if (result != CRYPTO_SUCCESS) {
"crypto_encrypt_update(3) failed: %0x",
result);
goto cleanup;
}
/*
* Ignore the output on the final step.
*/
if (result != CRYPTO_SUCCESS) {
"crypto_encrypt_final(3) failed: %0x",
result);
}
}
return (result);
}
static int
{
} else {
}
mech.cm_param_len = 0;
if (nblocks == 1) {
if (result != CRYPTO_SUCCESS) {
"crypto_decrypt failed: %0x", result);
goto cleanup;
}
} else {
if (result != CRYPTO_SUCCESS) {
"crypto_decrypt_init failed: %0x", result);
goto cleanup;
}
/*
* Save the input to the decrypt so it can
* be used later for an XOR operation
*/
if (result != CRYPTO_SUCCESS) {
"crypto_decrypt_update(1) error - "
"result = 0x%08x", result);
goto cleanup;
}
/*
* The original cipher text is used as the xor
* for the next block, save it here.
*/
}
if (result != CRYPTO_SUCCESS) {
"aes_cbc_cts_decrypt: "
"crypto_decrypt_update(2) error -"
" result = 0x%08x", result);
goto cleanup;
}
/* 2nd to last block ... */
if (result != CRYPTO_SUCCESS) {
"aes_cbc_cts_decrypt: "
"crypto_decrypt_update(3) error - "
"result = 0x%08x", result);
goto cleanup;
}
/* Finally, update the 2nd to last block and we are done. */
/* Do Final step, but ignore output */
if (result != CRYPTO_SUCCESS) {
"crypto_decrypt_final error - "
"result = 0x%0x", result);
}
}
return (result);
}
/*
* AES decrypt
*
* format of ciphertext when using AES
* +-------------+------------+------------+
* | confounder | msg-data | hmac |
* +-------------+------------+------------+
*/
static mblk_t *
{
int result;
}
/* AES Decrypt */
if (result != CRYPTO_SUCCESS) {
"aes_decrypt: aes_cbc_cts_decrypt "
"failed - error %0x", result);
goto cleanup;
}
/* Verify the HMAC */
if (result != CRYPTO_SUCCESS) {
"aes_decrypt: do_hmac failed - error %0x", result);
goto cleanup;
}
AES_TRUNCATED_HMAC_LEN) != 0) {
result = -1;
goto cleanup;
}
/* truncate the mblk at the end of the decrypted text */
/* Adjust the beginning of the buffer to skip the confounder */
if (result != CRYPTO_SUCCESS) {
return (NULL);
}
return (mp);
}
/*
* AES encrypt
*
* format of ciphertext when using AES
* +-------------+------------+------------+
* | confounder | msg-data | hmac |
* +-------------+------------+------------+
*/
static mblk_t *
{
int result;
/*
* Shift the rptr back enough to insert the confounder.
*/
/* Get random data for confounder */
/*
* Because we encrypt in-place, we need to calculate
* the HMAC of the plaintext now, then stick it on
* the end of the ciphertext down below.
*/
if (result != CRYPTO_SUCCESS) {
result);
goto cleanup;
}
/* Encrypt using AES-CBC-CTS */
if (result != CRYPTO_SUCCESS) {
"failed - error %0x", result);
goto cleanup;
}
/* copy the truncated HMAC to the end of the mblk */
/*
* The final block of cipher text (not the HMAC) is used
* as the next IV.
*/
}
if (result != CRYPTO_SUCCESS) {
return (NULL);
}
return (mp);
}
/*
* ARCFOUR-HMAC-MD5 decrypt
*
* format of ciphertext when using ARCFOUR-HMAC-MD5
* +-----------+------------+------------+
* | hmac | confounder | msg-data |
* +-----------+------------+------------+
*
*/
static mblk_t *
{
int result;
0xab, 0xab, 0xab, 0xab };
int usage;
/* The usage constant is 1026 for all "old" rcmd mode operations */
else
/*
* The size at this point should be the size of
* all the plaintext plus the optional plaintext length
* needed for RCMD V2 mode. There should also be room
* at the head of the mblk for the confounder and hash info.
*/
/*
* The cipherlen does not include the HMAC at the
* head of the buffer.
*/
saltdata[9] = 0;
saltlen = 14;
} else {
saltlen = 4;
}
/*
* Use the salt value to create a key to be used
* for subsequent HMAC operations.
*/
if (result != CRYPTO_SUCCESS) {
"arcfour_hmac_md5_decrypt: do_hmac(k1)"
"failed - error %0x", result);
goto cleanup;
}
/*
* For the neutered MS RC4 encryption type,
* set the trailing 9 bytes to 0xab per the
* RC4-HMAC spec.
*/
}
mech.cm_param_len = 0;
/*
* If we have not yet initialized the decryption key,
* context, and template, do it now.
*/
/*
* HMAC operation creates the encryption
* key to be used for the decrypt operations.
*/
if (result != CRYPTO_SUCCESS) {
"arcfour_hmac_md5_decrypt: do_hmac(k3)"
"failed - error %0x", result);
goto cleanup;
}
}
/*
* Only create a template if we are doing
* chaining from block to block.
*/
KM_SLEEP);
if (result == CRYPTO_NOT_SUPPORTED) {
} else if (result != CRYPTO_SUCCESS) {
"arcfour_hmac_md5_decrypt: "
"failed to create dec template "
"for RC4 encrypt: %0x", result);
goto cleanup;
}
if (result != CRYPTO_SUCCESS) {
" %0x", result);
goto cleanup;
}
}
/* adjust the rptr so we don't decrypt the original hmac field */
else
if (result != CRYPTO_SUCCESS) {
" %0x", result);
goto cleanup;
}
&k2,
if (result != CRYPTO_SUCCESS) {
"arcfour_hmac_md5_decrypt: do_hmac(k2)"
"failed - error %0x", result);
goto cleanup;
}
result = -1;
goto cleanup;
}
/*
* adjust the start of the mblk to skip over the
* hash and confounder.
*/
if (result != CRYPTO_SUCCESS) {
return (NULL);
}
return (mp);
}
/*
* ARCFOUR-HMAC-MD5 encrypt
*
* format of ciphertext when using ARCFOUR-HMAC-MD5
* +-----------+------------+------------+
* | hmac | confounder | msg-data |
* +-----------+------------+------------+
*
*/
static mblk_t *
{
int result;
0xab, 0xab, 0xab, 0xab };
int usage;
/* The usage constant is 1026 for all "old" rcmd mode operations */
else
mech.cm_param_len = 0;
/*
* The size at this point should be the size of
* all the plaintext plus the optional plaintext length
* needed for RCMD V2 mode. There should also be room
* at the head of the mblk for the confounder and hash info.
*/
/*
* Shift the rptr back enough to insert
* the confounder and hash.
*/
/* zero out the hash area */
}
saltdata[9] = 0;
saltlen = 14;
} else {
saltlen = 4;
}
/*
* Use the salt value to create a key to be used
* for subsequent HMAC operations.
*/
if (result != CRYPTO_SUCCESS) {
"arcfour_hmac_md5_encrypt: do_hmac(k1)"
"failed - error %0x", result);
goto cleanup;
}
/*
* For the neutered MS RC4 encryption type,
* set the trailing 9 bytes to 0xab per the
* RC4-HMAC spec.
*/
}
/*
* Get the confounder bytes.
*/
(void) random_get_pseudo_bytes(
/*
* This writes the HMAC to the hash area in the
* mblk. The key used is the one just created by
* the previous HMAC operation.
* The data being processed is the confounder bytes
* PLUS the input plaintext.
*/
if (result != CRYPTO_SUCCESS) {
"arcfour_hmac_md5_encrypt: do_hmac(k2)"
"failed - error %0x", result);
goto cleanup;
}
/*
* Because of the odd way that MIT uses RC4 keys
* on the rlogin stream, we only need to create
* this key once.
* However, if using "old" rcmd mode, we need to do
* it every time.
*/
/*
* The final HMAC operation creates the encryption
* key to be used for the encrypt operation.
*/
if (result != CRYPTO_SUCCESS) {
"arcfour_hmac_md5_encrypt: do_hmac(k3)"
"failed - error %0x", result);
goto cleanup;
}
}
/*
* If the context has not been initialized, do it now.
*/
/*
* Only create a template if we are doing
* chaining from block to block.
*/
KM_SLEEP);
if (result == CRYPTO_NOT_SUPPORTED) {
} else if (result != CRYPTO_SUCCESS) {
"for RC4 encrypt: %0x", result);
goto cleanup;
}
if (result != CRYPTO_SUCCESS) {
" %0x", result);
goto cleanup;
}
}
else
if (result != CRYPTO_SUCCESS) {
result);
}
if (result != CRYPTO_SUCCESS) {
return (NULL);
}
return (mp);
}
/*
* DES-CBC-[HASH] encrypt
*
* Needed to support userland apps that must support Kerberos V5
* encryption DES-CBC encryption modes.
*
* The HASH values supported are RAW(NULL), MD5, CRC32, and SHA1
*
* format of ciphertext for DES-CBC functions, per RFC1510 is:
* +-----------+----------+-------------+-----+
* |confounder | cksum | msg-data | pad |
* +-----------+----------+-------------+-----+
*
* format of ciphertext when using DES3-SHA1-HMAC
* +-----------+----------+-------------+-----+
* |confounder | msg-data | hmac | pad |
* +-----------+----------+-------------+-----+
*
* The confounder is 8 bytes of random data.
* The cksum depends on the hash being used.
* 4 bytes for CRC32
* 16 bytes for MD5
* 20 bytes for SHA1
* 0 bytes for RAW
*
*/
static mblk_t *
{
int result;
/*
* The size at this point should be the size of
* all the plaintext plus the optional plaintext length
* needed for RCMD V2 mode. There should also be room
* at the head of the mblk for the confounder and hash info.
*/
/*
* The output size will be a multiple of 8 because this algorithm
* only works on 8 byte chunks.
*/
}
/*
* Shift the rptr back enough to insert
* the confounder and hash.
*/
} else {
/* zero out the hash area */
}
/* get random confounder from our friend, the 'random' module */
if (hash->confound_len > 0) {
}
/*
* For 3DES we calculate an HMAC later.
*/
/* calculate chksum of confounder + input */
if (result != CRYPTO_SUCCESS) {
goto failure;
}
/* put hash in place right after the confounder */
}
}
/*
* In order to support the "old" Kerberos RCMD protocol,
* we must use the IVEC 3 different ways:
* IVEC_REUSE = keep using the same IV each time, this is
* ugly and insecure, but necessary for
* backwards compatibility with existing MIT code.
* IVEC_ONETIME = Use the ivec as initialized when the crypto
* was setup (see setup_crypto routine).
* IVEC_NEVER = never use an IVEC, use a bunch of 0's as the IV (yuk).
*/
}
/*
* The input length already included the hash size,
* don't include this in the plaintext length
* calculations.
*/
} else {
}
if (result != CRYPTO_SUCCESS) {
#ifdef DEBUG
"des_cbc_encrypt: kef_crypt encrypt "
"failed (len: %ld) - error %0x",
#endif
return (NULL);
/*
* Because we are using KEF, we must manually
* update our IV.
*/
}
}
return (mp);
}
/*
* des_cbc_decrypt
*
*
* Needed to support userland apps that must support Kerberos V5
* encryption DES-CBC decryption modes.
*
* The HASH values supported are RAW(NULL), MD5, CRC32, and SHA1
*
* format of ciphertext for DES-CBC functions, per RFC1510 is:
* +-----------+----------+-------------+-----+
* |confounder | cksum | msg-data | pad |
* +-----------+----------+-------------+-----+
*
* format of ciphertext when using DES3-SHA1-HMAC
* +-----------+----------+-------------+-----+
* |confounder | msg-data | hmac | pad |
* +-----------+----------+-------------+-----+
*
* The confounder is 8 bytes of random data.
* The cksum depends on the hash being used.
* 4 bytes for CRC32
* 16 bytes for MD5
* 20 bytes for SHA1
* 0 bytes for RAW
*
*/
static mblk_t *
{
int result = 0;
/* Compute adjusted size */
/*
* In order to support the "old" Kerberos RCMD protocol,
* we must use the IVEC 3 different ways:
* IVEC_REUSE = keep using the same IV each time, this is
* ugly and insecure, but necessary for
* backwards compatibility with existing MIT code.
* IVEC_ONETIME = Use the ivec as initialized when the crypto
* was setup (see setup_crypto routine).
* IVEC_NEVER = never use an IVEC, use a bunch of 0's as the IV (yuk).
*/
/*
* Do not decrypt the HMAC at the end
*/
/*
* Move the wptr so the mblk appears to end
* BEFORE the HMAC section.
*/
/*
* Because we are using KEF, we must manually update our
* IV.
*/
}
} else {
/*
* Because we are using KEF, we must manually update our
* IV.
*/
}
}
if (result != CRYPTO_SUCCESS) {
#ifdef DEBUG
"des_cbc_decrypt: kef_crypt decrypt "
"failed - error %0x", result);
#endif
return (NULL);
}
/*
* Manually update the IV, KEF does not track this for us.
*/
}
/* Verify the checksum(if necessary) */
} else {
/* zero the cksum in the buffer */
/* calculate MD5 chksum of confounder + input */
}
}
#ifdef DEBUG
"verification failed");
#endif
return (NULL);
}
}
/* Move just the decrypted input into place if necessary */
else
}
return (mp);
}
static mblk_t *
{
case CRYPT_METHOD_DES_CFB:
break;
case CRYPT_METHOD_NONE:
break;
break;
case CRYPT_METHOD_DES_CBC_MD5:
break;
case CRYPT_METHOD_DES_CBC_CRC:
break;
break;
break;
case CRYPT_METHOD_AES128:
case CRYPT_METHOD_AES256:
break;
}
return (outmp);
}
/*
* do_encrypt
*
* Generic encryption routine for a single message block.
* The input mblk may be replaced by some encrypt routines
* because they add extra data in some cases that may exceed
* the input mblk_t size limit.
*/
static mblk_t *
{
case CRYPT_METHOD_DES_CFB:
break;
break;
case CRYPT_METHOD_DES_CBC_MD5:
break;
case CRYPT_METHOD_DES_CBC_CRC:
break;
break;
break;
case CRYPT_METHOD_AES128:
case CRYPT_METHOD_AES256:
break;
case CRYPT_METHOD_NONE:
break;
}
return (outmp);
}
/*
* setup_crypto
*
* This takes the data from the CRYPTIOCSETUP ioctl
* and sets up a cipher_data_t structure for either
* encryption or decryption. This is where the
* key and initialization vector data get stored
* prior to beginning any crypto functions.
*
* Special note:
* Some applications(e.g. telnetd) have ability to switch
* the CRYPTIOCSETUP ioctl many times for the same stream.
* If the CRYPTIOCSETUP is called with 0 length key or ivec fields
* assume that the key, block, and saveblock fields that are already
* set from a previous CRIOCSETUP call are still valid. This helps avoid
* a rekeying error that could occur if we overwrite these fields
* with each CRYPTIOCSETUP call.
* without resetting the original crypto parameters.
*
*/
static int
{
int rv;
/*
* Initial sanity checks
*/
ci->crypto_method);
return (EINVAL);
}
ci->option_mask);
return (EINVAL);
}
ci->ivec_usage);
return (EINVAL);
}
}
/*
* cd->key holds the copy of the raw key bytes passed in
* from the userland app.
*/
}
/*
* Configure the block size based on the type of cipher.
*/
case CRYPT_METHOD_NONE:
newblocklen = 0;
break;
case CRYPT_METHOD_DES_CFB:
break;
case CRYPT_METHOD_DES_CBC_MD5:
case CRYPT_METHOD_DES_CBC_CRC:
break;
/* 3DES always uses the old usage constant */
break;
newblocklen = 0;
break;
case CRYPT_METHOD_AES128:
case CRYPT_METHOD_AES256:
break;
}
return (CRYPTO_FAILED);
}
/*
* If RC4, initialize the master crypto key used by
* the RC4 algorithm to derive the final encrypt and decrypt keys.
*/
/*
* cd->ckey is a kernel crypto key structure used as the
* master key in the RC4-HMAC crypto operations.
*/
sizeof (crypto_key_t), KM_SLEEP);
}
/* key length for EF is measured in bits */
}
/*
* cd->block and cd->saveblock are used as temporary storage for
* in some of the "feedback" modes.
*/
}
}
KM_SLEEP);
}
KM_SLEEP);
else
}
}
KM_SLEEP);
} else {
}
}
/*
* Old protocol requires a static 'usage' value for
* deriving keys. Yuk.
*/
}
return (EINVAL);
}
/*
* If we are using an IVEC "correctly" (i.e. set it once)
* copy it here.
*/
/* Save the original IVEC in case we need it later */
}
/*
* Special handling for 3DES-SHA1-HMAC and AES crypto:
* generate derived keys and context templates
* for better performance.
*/
}
}
hmac_mech.cm_param_len = 0;
/*
* Create the derived keys.
*/
if (rv != CRYPTO_SUCCESS) {
"keys: %0x", rv);
return (CRYPTO_FAILED);
}
&cd->d_encr_key,
if (rv == CRYPTO_MECH_NOT_SUPPORTED) {
} else if (rv != CRYPTO_SUCCESS) {
"for d_encr_key: %0x", rv);
return (CRYPTO_FAILED);
}
&cd->d_hmac_key,
if (rv == CRYPTO_MECH_NOT_SUPPORTED) {
} else if (rv != CRYPTO_SUCCESS) {
" %0x", rv);
return (CRYPTO_FAILED);
}
}
/* Final sanity checks, make sure no fields are NULL */
#ifdef DEBUG
"setup_crypto: IV block not allocated");
#endif
return (ENOMEM);
}
#ifdef DEBUG
"setup_crypto: key block not allocated");
#endif
return (ENOMEM);
}
#ifdef DEBUG
"setup_crypto: save block not allocated");
#endif
return (ENOMEM);
}
#ifdef DEBUG
"setup_crypto: IV not allocated");
#endif
return (ENOMEM);
}
}
return (0);
}
/*
* RCMDS require a 4 byte, clear text
* length field before each message.
* Add it now.
*/
static mblk_t *
{
}
}
return (bp);
}
static mblk_t *
{
/*
* If we are using the "NEW" RCMD mode,
* add 4 bytes to the plaintext for the
* plaintext length that gets prepended
* before encrypting.
*/
ptlen += 4;
/*
* if we must allocb, then make sure its enough
* to hold the length field so we dont have to allocb
* again down below in 'mklenmp'
*/
}
/*
* Calculate how much space is needed in front of
* the data.
*/
/*
* If the current block is too small, reallocate
* one large enough to hold the hdr, tail, and
* ciphertext.
*/
"allocb (%d bytes) failed", sz);
return (NULL);
}
/*
* headspace includes the length fields needed
* for the RCMD modes (v1 == 4 bytes, V2 = 8)
*/
} else {
/*
* Some ciphers add HMAC after the final block
* of the ciphertext, not at the beginning like the
* 1-DES ciphers.
*/
}
/*
* Make sure the rptr is positioned correctly so that
* routines later do not have to shift this data around
*/
plainlen);
}
}
/*
* If using RCMD_MODE_V2 (new rcmd mode), prepend
* the plaintext length before the actual plaintext.
*/
/* put plaintext length at head of buffer */
}
/*
* Add length field, required when this is
* used to encrypt "r*" commands(rlogin, rsh)
* with Kerberos.
*/
return (NULL);
} else {
}
}
return (newmp);
}
/*
* encrypt_msgb
*
* encrypt a single message. This routine adds the
* RCMD overhead bytes when necessary.
*/
static mblk_t *
{
/* If not encrypting, do nothing */
return (mp);
}
if (plainlen == 0)
return (NULL);
/*
* If the block is too big, we encrypt in 4K chunks so that
* older rlogin clients do not choke on the larger buffers.
*/
/*
* Allocate a new buffer that is only 4K bytes, the
* extra bytes are for crypto overhead.
*/
"allocb (%d bytes) failed",
(int)(outlen + CONFOUNDER_BYTES));
return (NULL);
}
/* Copy the next 4K bytes from the old block. */
/* Advance the old block. */
/* encrypt the new block */
return (NULL);
}
/* If there is data left (< MSGBUF_SIZE), encrypt it. */
return (newmp);
}
/*
* cryptmodwsrv
*
* Service routine for the write queue.
*
* Because data may be placed in the queue to hold between
* the CRYPTIOCSTOP and CRYPTIOCSTART ioctls, the service routine is needed.
*/
static int
{
default:
/*
* wput does not queue anything > QPCTL
*/
if (!canputnext(q) ||
}
return (0);
}
break;
case M_DATA:
/*
* If multiple msgs, concat into 1
* to minimize crypto operations later.
*/
}
}
} else {
}
return (0);
}
break;
}
}
return (0);
}
static void
{
if (dir == CRYPT_ENCRYPT) {
} else if (dir == CRYPT_DECRYPT) {
/*
* put any extra data in the RD
* queue to be processed and
* sent back up.
*/
"start_stream: restart "
}
}
/*
* Write-side put procedure. Its main task is to detect ioctls and
* FLUSH operations. Other message types are passed on through.
*/
static void
{
case M_DATA:
return;
}
/* else, put it in the service queue */
}
break;
case M_FLUSH:
}
break;
case M_IOCTL:
case CRYPTIOCSETUP:
ret = 0;
"wput: got CRYPTIOCSETUP "
sizeof (struct cr_info_t))) != 0) {
"wput: miocpullup failed for cr_info_t");
} else {
}
if (ret == 0 &&
}
if (ret == 0 &&
sizeof (tmi->rcmd_state));
}
if (ret == 0) {
} else {
"wput: setup_crypto failed");
}
"wput: done with SETUP "
"ioctl"));
}
break;
case CRYPTIOCSTOP:
"wput: got CRYPTIOCSTOP "
"wput: CRYPTIOCSTOP ioctl wrong "
"size (%d should be %d)",
(int)sizeof (uint32_t));
} else {
if (!CR_DIRECTION_OK(*stopdir)) {
return;
}
/* disable the queues until further notice */
if (*stopdir & CRYPT_ENCRYPT) {
}
if (*stopdir & CRYPT_DECRYPT) {
}
}
break;
case CRYPTIOCSTARTDEC:
"wput: got CRYPTIOCSTARTDEC "
break;
case CRYPTIOCSTARTENC:
"wput: got CRYPTIOCSTARTENC "
break;
default:
break;
}
break;
default:
return;
}
}
break;
}
}
/*
* decrypt_rcmd_mblks
*
* Because kerberized r* commands(rsh, rlogin, etc)
* use a 4 byte length field to indicate the # of
* PLAINTEXT bytes that are encrypted in the field
* that follows, we must parse out each message and
* break out the length fields prior to sending them
* NOT understand this format.
*
* Kerberized/encrypted message format:
* -------------------------------
* | XXXX | N bytes of ciphertext|
* -------------------------------
*
* Where: XXXX = number of plaintext bytes that were encrypted in
* to make the ciphertext field. This is done
* because we are using a cipher that pads out to
* an 8 byte boundary. We only want the application
* layer to see the correct number of plain text bytes,
* not plaintext + pad. So, after we decrypt, we
* must trim the output block down to the intended
* plaintext length and eliminate the pad bytes.
*
* This routine takes the entire input message, breaks it into
* a new message that does not contain these length fields and
* returns a message consisting of mblks filled with just ciphertext.
*
*/
static mblk_t *
{
/*
* If we need the length field, get it here.
* Test the "plaintext length" indicator.
*/
int tocopy;
/*
* Make sure we have recieved all 4 bytes of the
* length field.
*/
break;
}
}
return (NULL);
}
/*
* recalculate the msglen now that we've read the
* length and adjusted the bufptr (b_rptr).
*/
/*
* Return an IO error to break the connection. there
* is no way to recover from this. Usually it means
* the app has incorrectly requested decryption on
* a non-encrypted stream, thus the "pt_len" field
* is negative.
*/
return (NULL);
}
/*
* If this is V2 mode, then the encrypted data is actually
* 4 bytes bigger than the indicated len because the plaintext
* length is encrypted for an additional security check, but
* its not counted as part of the overall length we just read.
* Strange and confusing, but true.
*/
else
/*
* Allocate an mblk to hold the cipher text until it is
* all ready to be processed.
*/
BPRI_HI);
#ifdef DEBUG
"for %d bytes",
#endif
/*
* Return an IO error to break the connection.
*/
return (NULL);
}
}
/*
* If this entire message was just the length field,
* free and return. The actual data will probably be next.
*/
if (msglen == 0) {
return (NULL);
}
/*
* Copy as much of the cipher text as possible into
* the new msgb (c_msg).
*
* Logic: if we got some bytes (msglen) and we still
* "need" some bytes (len-rcvd), get them here.
*/
if (msglen > 0 &&
size_t n;
/*
* Walk the mblks and copy just as many bytes as we need
* for this particular block of cipher text.
*/
/* Copy to end of new mblk */
tocopy);
/*
* If we used this whole block, free it and
* move on.
*/
}
/* If we got what we needed, stop the loop */
/*
* If there is more data in the message,
* its for another block of cipher text,
* put it back in the queue for next time.
*/
if (bp) {
/*
* If there is more, put it back in the
* queue for another pass thru.
*/
}
break;
}
}
}
/*
* Finally, if we received all the cipher text data for
* this message, decrypt it into a new msg and send it up
* to the app.
*/
/*
* Now we can use our msg that we created when the
* initial message boundary was detected.
*/
/*
* If using RCMD_MODE_V2 ("new" mode),
* look at the 4 byte plaintext length that
* was just decrypted and compare with the
* original pt_len value that was received.
*/
/*
* Make sure the 2 pt len fields agree.
*/
"Inconsistent length fields"
" received %d != %d",
(int)pt_len2);
return (NULL);
}
}
/*
* Trim the decrypted block the length originally
* indicated by the sender. This is to remove any
* padding bytes that the sender added to satisfy
* requirements of the crypto algorithm.
*/
/*
* Reset our state to indicate we are ready
* for a new message.
*/
} else {
#ifdef DEBUG
"decrypt_rcmd: do_decrypt on %d bytes failed",
#endif
/*
* do_decrypt already handled failures, just
* return NULL.
*/
return (NULL);
}
}
/*
* return the new message with the 'length' fields removed
*/
return (newmp);
}
/*
* cryptmodrsrv
*
* Read queue service routine
* Necessary because if the ready flag is not set
* (via CRYPTIOCSTOP/CRYPTIOCSTART ioctls) then the data
* must remain on queue and not be passed along.
*/
static int
{
case M_DATA:
/*
* Process "rcmd" messages differently because
* they contain a 4 byte plaintext length
* id that needs to be removed.
*/
if (mp)
continue;
}
}
}
} else {
}
return (0);
}
break;
default:
/*
* rput does not queue anything > QPCTL, so we don't
* need to check for it here.
*/
if (!canputnext(q)) {
return (0);
}
break;
}
}
return (0);
}
/*
* Read-side put procedure.
*/
static void
{
case M_DATA:
}
break;
case M_FLUSH:
}
break;
default:
return;
}
}
break;
}
}