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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/zfs_context.h> 27 #include <modes/modes.h> 28 #include <sys/crypto/common.h> 29 #include <sys/crypto/impl.h> 30 31 /* 32 * Utility routine to copy a buffer to a crypto_data structure. 33 */ 34 35 /* 36 * Utility routine to apply the command, 'cmd', to the 37 * data in the uio structure. 38 */ 39 int 40 crypto_uio_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd, 41 void *digest_ctx, void (*update)(void)) 42 { 43 zfs_uio_t *uiop = data->cd_uio; 44 off_t offset = data->cd_offset; 45 size_t length = len; 46 uint_t vec_idx; 47 size_t cur_len; 48 uchar_t *datap; 49 50 ASSERT(data->cd_format == CRYPTO_DATA_UIO); 51 if (zfs_uio_segflg(uiop) != UIO_SYSSPACE) { 52 return (CRYPTO_ARGUMENTS_BAD); 53 } 54 55 /* 56 * Jump to the first iovec containing data to be 57 * processed. 58 */ 59 offset = zfs_uio_index_at_offset(uiop, offset, &vec_idx); 60 61 if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) { 62 /* 63 * The caller specified an offset that is larger than 64 * the total size of the buffers it provided. 65 */ 66 return (CRYPTO_DATA_LEN_RANGE); 67 } 68 69 while (vec_idx < zfs_uio_iovcnt(uiop) && length > 0) { 70 cur_len = MIN(zfs_uio_iovlen(uiop, vec_idx) - 71 offset, length); 72 73 datap = (uchar_t *)(zfs_uio_iovbase(uiop, vec_idx) + offset); 74 switch (cmd) { 75 case COPY_FROM_DATA: 76 bcopy(datap, buf, cur_len); 77 buf += cur_len; 78 break; 79 case COPY_TO_DATA: 80 bcopy(buf, datap, cur_len); 81 buf += cur_len; 82 break; 83 case COMPARE_TO_DATA: 84 if (bcmp(datap, buf, cur_len)) 85 return (CRYPTO_SIGNATURE_INVALID); 86 buf += cur_len; 87 break; 88 case MD5_DIGEST_DATA: 89 case SHA1_DIGEST_DATA: 90 case SHA2_DIGEST_DATA: 91 case GHASH_DATA: 92 return (CRYPTO_ARGUMENTS_BAD); 93 } 94 95 length -= cur_len; 96 vec_idx++; 97 offset = 0; 98 } 99 100 if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) { 101 /* 102 * The end of the specified iovec's was reached but 103 * the length requested could not be processed. 104 */ 105 switch (cmd) { 106 case COPY_TO_DATA: 107 data->cd_length = len; 108 return (CRYPTO_BUFFER_TOO_SMALL); 109 default: 110 return (CRYPTO_DATA_LEN_RANGE); 111 } 112 } 113 114 return (CRYPTO_SUCCESS); 115 } 116 117 int 118 crypto_put_output_data(uchar_t *buf, crypto_data_t *output, int len) 119 { 120 switch (output->cd_format) { 121 case CRYPTO_DATA_RAW: 122 if (output->cd_raw.iov_len < len) { 123 output->cd_length = len; 124 return (CRYPTO_BUFFER_TOO_SMALL); 125 } 126 bcopy(buf, (uchar_t *)(output->cd_raw.iov_base + 127 output->cd_offset), len); 128 break; 129 130 case CRYPTO_DATA_UIO: 131 return (crypto_uio_data(output, buf, len, 132 COPY_TO_DATA, NULL, NULL)); 133 default: 134 return (CRYPTO_ARGUMENTS_BAD); 135 } 136 137 return (CRYPTO_SUCCESS); 138 } 139 140 int 141 crypto_update_iov(void *ctx, crypto_data_t *input, crypto_data_t *output, 142 int (*cipher)(void *, caddr_t, size_t, crypto_data_t *), 143 void (*copy_block)(uint8_t *, uint64_t *)) 144 { 145 common_ctx_t *common_ctx = ctx; 146 int rv; 147 148 ASSERT(input != output); 149 if (input->cd_miscdata != NULL) { 150 copy_block((uint8_t *)input->cd_miscdata, 151 &common_ctx->cc_iv[0]); 152 } 153 154 if (input->cd_raw.iov_len < input->cd_length) 155 return (CRYPTO_ARGUMENTS_BAD); 156 157 rv = (cipher)(ctx, input->cd_raw.iov_base + input->cd_offset, 158 input->cd_length, output); 159 160 return (rv); 161 } 162 163 int 164 crypto_update_uio(void *ctx, crypto_data_t *input, crypto_data_t *output, 165 int (*cipher)(void *, caddr_t, size_t, crypto_data_t *), 166 void (*copy_block)(uint8_t *, uint64_t *)) 167 { 168 common_ctx_t *common_ctx = ctx; 169 zfs_uio_t *uiop = input->cd_uio; 170 off_t offset = input->cd_offset; 171 size_t length = input->cd_length; 172 uint_t vec_idx; 173 size_t cur_len; 174 175 ASSERT(input != output); 176 if (input->cd_miscdata != NULL) { 177 copy_block((uint8_t *)input->cd_miscdata, 178 &common_ctx->cc_iv[0]); 179 } 180 181 if (zfs_uio_segflg(input->cd_uio) != UIO_SYSSPACE) { 182 return (CRYPTO_ARGUMENTS_BAD); 183 } 184 185 /* 186 * Jump to the first iovec containing data to be 187 * processed. 188 */ 189 offset = zfs_uio_index_at_offset(uiop, offset, &vec_idx); 190 if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) { 191 /* 192 * The caller specified an offset that is larger than the 193 * total size of the buffers it provided. 194 */ 195 return (CRYPTO_DATA_LEN_RANGE); 196 } 197 198 /* 199 * Now process the iovecs. 200 */ 201 while (vec_idx < zfs_uio_iovcnt(uiop) && length > 0) { 202 cur_len = MIN(zfs_uio_iovlen(uiop, vec_idx) - 203 offset, length); 204 205 int rv = (cipher)(ctx, zfs_uio_iovbase(uiop, vec_idx) + offset, 206 cur_len, output); 207 208 if (rv != CRYPTO_SUCCESS) { 209 return (rv); 210 } 211 length -= cur_len; 212 vec_idx++; 213 offset = 0; 214 } 215 216 if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) { 217 /* 218 * The end of the specified iovec's was reached but 219 * the length requested could not be processed, i.e. 220 * The caller requested to digest more data than it provided. 221 */ 222 223 return (CRYPTO_DATA_LEN_RANGE); 224 } 225 226 return (CRYPTO_SUCCESS); 227 } 228