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