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 https://opensource.org/licenses/CDDL-1.0. 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 COPY_TO_DATA to the 37 * data in the uio structure. 38 */ 39 static int 40 crypto_uio_copy_to_data(crypto_data_t *data, uchar_t *buf, int len) 41 { 42 zfs_uio_t *uiop = data->cd_uio; 43 off_t offset = data->cd_offset; 44 size_t length = len; 45 uint_t vec_idx; 46 size_t cur_len; 47 uchar_t *datap; 48 49 ASSERT(data->cd_format == CRYPTO_DATA_UIO); 50 if (zfs_uio_segflg(uiop) != UIO_SYSSPACE) { 51 return (CRYPTO_ARGUMENTS_BAD); 52 } 53 54 /* 55 * Jump to the first iovec containing data to be 56 * processed. 57 */ 58 offset = zfs_uio_index_at_offset(uiop, offset, &vec_idx); 59 60 if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) { 61 /* 62 * The caller specified an offset that is larger than 63 * the total size of the buffers it provided. 64 */ 65 return (CRYPTO_DATA_LEN_RANGE); 66 } 67 68 while (vec_idx < zfs_uio_iovcnt(uiop) && length > 0) { 69 cur_len = MIN(zfs_uio_iovlen(uiop, vec_idx) - 70 offset, length); 71 72 datap = (uchar_t *)(zfs_uio_iovbase(uiop, vec_idx) + offset); 73 memcpy(datap, buf, cur_len); 74 buf += cur_len; 75 76 length -= cur_len; 77 vec_idx++; 78 offset = 0; 79 } 80 81 if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) { 82 /* 83 * The end of the specified iovecs was reached but 84 * the length requested could not be processed. 85 */ 86 data->cd_length = len; 87 return (CRYPTO_BUFFER_TOO_SMALL); 88 } 89 90 return (CRYPTO_SUCCESS); 91 } 92 93 int 94 crypto_put_output_data(uchar_t *buf, crypto_data_t *output, int len) 95 { 96 switch (output->cd_format) { 97 case CRYPTO_DATA_RAW: 98 if (output->cd_raw.iov_len < len) { 99 output->cd_length = len; 100 return (CRYPTO_BUFFER_TOO_SMALL); 101 } 102 memcpy((uchar_t *)(output->cd_raw.iov_base + 103 output->cd_offset), buf, len); 104 break; 105 106 case CRYPTO_DATA_UIO: 107 return (crypto_uio_copy_to_data(output, buf, len)); 108 default: 109 return (CRYPTO_ARGUMENTS_BAD); 110 } 111 112 return (CRYPTO_SUCCESS); 113 } 114 115 int 116 crypto_update_iov(void *ctx, crypto_data_t *input, crypto_data_t *output, 117 int (*cipher)(void *, caddr_t, size_t, crypto_data_t *)) 118 { 119 ASSERT(input != output); 120 121 if (input->cd_raw.iov_len < input->cd_length) 122 return (CRYPTO_ARGUMENTS_BAD); 123 124 return ((cipher)(ctx, input->cd_raw.iov_base + input->cd_offset, 125 input->cd_length, output)); 126 } 127 128 int 129 crypto_update_uio(void *ctx, crypto_data_t *input, crypto_data_t *output, 130 int (*cipher)(void *, caddr_t, size_t, crypto_data_t *)) 131 { 132 zfs_uio_t *uiop = input->cd_uio; 133 off_t offset = input->cd_offset; 134 size_t length = input->cd_length; 135 uint_t vec_idx; 136 size_t cur_len; 137 138 ASSERT(input != output); 139 140 if (zfs_uio_segflg(input->cd_uio) != UIO_SYSSPACE) { 141 return (CRYPTO_ARGUMENTS_BAD); 142 } 143 144 /* 145 * Jump to the first iovec containing data to be 146 * processed. 147 */ 148 offset = zfs_uio_index_at_offset(uiop, offset, &vec_idx); 149 if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) { 150 /* 151 * The caller specified an offset that is larger than the 152 * total size of the buffers it provided. 153 */ 154 return (CRYPTO_DATA_LEN_RANGE); 155 } 156 157 /* 158 * Now process the iovecs. 159 */ 160 while (vec_idx < zfs_uio_iovcnt(uiop) && length > 0) { 161 cur_len = MIN(zfs_uio_iovlen(uiop, vec_idx) - 162 offset, length); 163 164 int rv = (cipher)(ctx, zfs_uio_iovbase(uiop, vec_idx) + offset, 165 cur_len, output); 166 167 if (rv != CRYPTO_SUCCESS) { 168 return (rv); 169 } 170 length -= cur_len; 171 vec_idx++; 172 offset = 0; 173 } 174 175 if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) { 176 /* 177 * The end of the specified iovec's was reached but 178 * the length requested could not be processed, i.e. 179 * The caller requested to digest more data than it provided. 180 */ 181 182 return (CRYPTO_DATA_LEN_RANGE); 183 } 184 185 return (CRYPTO_SUCCESS); 186 } 187