xref: /dragonfly/lib/libtcplay/io.c (revision 926deccb)
1 /*
2  * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/types.h>
31 #if defined(__DragonFly__)
32 #include <sys/diskslice.h>
33 #elif defined(__linux__)
34 #include <linux/fs.h>
35 #include <sys/ioctl.h>
36 #endif
37 #include <sys/uio.h>
38 #include <sys/select.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <termios.h>
45 #include <unistd.h>
46 
47 #include "tcplay.h"
48 
49 void *
50 read_to_safe_mem(const char *file, off_t offset, size_t *sz)
51 {
52 	void *mem = NULL;
53 	ssize_t r = 0;
54 	int fd;
55 
56 	if ((fd = open(file, O_RDONLY)) < 0) {
57 		tc_log(1, "Error opening file %s\n", file);
58 		return NULL;
59 	}
60 
61 	if ((mem = alloc_safe_mem(*sz)) == NULL) {
62 		tc_log(1, "Error allocating memory\n");
63 		goto out;
64 	}
65 
66 	if ((lseek(fd, offset, SEEK_SET) < 0)) {
67 		tc_log(1, "Error seeking on file %s\n", file);
68 		goto m_err;
69 	}
70 
71 	if ((r = read(fd, mem, *sz)) <= 0) {
72 		tc_log(1, "Error reading from file %s\n", file);
73 		goto m_err;
74 	}
75 
76 out:
77 	*sz = r;
78 	close(fd);
79 	return mem;
80 	/* NOT REACHED */
81 
82 m_err:
83 	free_safe_mem(mem);
84 	close(fd);
85 	return NULL;
86 }
87 
88 static size_t get_random_total_bytes = 0;
89 static size_t get_random_read_bytes = 0;
90 
91 static
92 void
93 get_random_summary(void)
94 {
95 	float pct_done;
96 
97 	pct_done = (1.0 * get_random_read_bytes) /
98 	    (1.0 * get_random_total_bytes) * 100.0;
99 	tc_log(0, "Gathering true randomness, %.0f%% done.\n", pct_done);
100 }
101 
102 int
103 get_random(unsigned char *buf, size_t len)
104 {
105 	int fd;
106 	ssize_t r;
107 	size_t rd = 0;
108 	size_t sz;
109 	struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; /* 10 ms */
110 
111 
112 	if ((fd = open("/dev/random", O_RDONLY)) < 0) {
113 		tc_log(1, "Error opening /dev/random\n");
114 		return -1;
115 	}
116 
117 	summary_fn = get_random_summary;
118 	get_random_total_bytes = len;
119 
120 	/* Get random data in 16-byte chunks */
121 	sz = 16;
122 	while (rd < len) {
123 		get_random_read_bytes = rd;
124 
125 		if ((len - rd) < sz)
126 			sz = (len - rd);
127 
128 		if ((r = read(fd, buf+rd, sz)) < 0) {
129 			tc_log(1, "Error reading from /dev/random(%d): %s\n",
130 			    fd, strerror(errno));
131 			close(fd);
132 			summary_fn = NULL;
133 			return -1;
134 		}
135 		rd += r;
136 		nanosleep(&ts, NULL);
137 	}
138 
139 	close(fd);
140 	summary_fn = NULL;
141 
142 	return 0;
143 }
144 
145 static size_t secure_erase_total_bytes = 0;
146 static size_t secure_erase_erased_bytes = 0;
147 
148 static
149 void
150 secure_erase_summary(void)
151 {
152 	float pct_done;
153 
154 	pct_done = (1.0 * secure_erase_erased_bytes) /
155 	    (1.0 * secure_erase_total_bytes) * 100.0;
156 	tc_log(0, "Securely erasing, %.0f%% done.\n", pct_done);
157 }
158 
159 int
160 secure_erase(const char *dev, size_t bytes, size_t blksz)
161 {
162 	size_t erased = 0;
163 	int fd_rand, fd;
164 	char buf[ERASE_BUFFER_SIZE];
165 	ssize_t r, w;
166 	size_t sz;
167 
168 	if (blksz > MAX_BLKSZ) {
169 		tc_log(1, "blksz > MAX_BLKSZ\n");
170 		return -1;
171 	}
172 
173 	if ((fd_rand = open("/dev/urandom", O_RDONLY)) < 0) {
174 		tc_log(1, "Error opening /dev/urandom\n");
175 		return -1;
176 	}
177 
178 	if ((fd = open(dev, O_WRONLY)) < 0) {
179 		close(fd_rand);
180 		tc_log(1, "Error opening %s\n", dev);
181 		return -1;
182 	}
183 
184 	summary_fn = secure_erase_summary;
185 	secure_erase_total_bytes = bytes;
186 
187 	sz = ERASE_BUFFER_SIZE;
188 	while (erased < bytes) {
189 		secure_erase_erased_bytes = erased;
190 		/* Switch to block size when not much is remaining */
191 		if ((bytes - erased) <= ERASE_BUFFER_SIZE)
192 			sz = blksz;
193 
194 		if ((r = read(fd_rand, buf, sz)) < 0) {
195 			tc_log(1, "Error reading from /dev/urandom\n");
196 			close(fd);
197 			close(fd_rand);
198 			summary_fn = NULL;
199 			return -1;
200 		}
201 
202 		if (r < (ssize_t)blksz)
203 			continue;
204 
205 		if ((w = write(fd, buf, r)) < 0) {
206 			tc_log(1, "Error writing to %s\n", dev);
207 			close(fd);
208 			close(fd_rand);
209 			summary_fn = NULL;
210 			return -1;
211 		}
212 
213 		erased += (size_t)w;
214 	}
215 
216 	close(fd);
217 	close(fd_rand);
218 
219 	summary_fn = NULL;
220 
221 	return 0;
222 }
223 
224 #if defined(__DragonFly__)
225 int
226 get_disk_info(const char *dev, size_t *blocks, size_t *bsize)
227 {
228 	struct partinfo pinfo;
229 	int fd;
230 
231 	if ((fd = open(dev, O_RDONLY)) < 0) {
232 		tc_log(1, "Error opening %s\n", dev);
233 		return -1;
234 	}
235 
236 	memset(&pinfo, 0, sizeof(struct partinfo));
237 
238 	if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
239 		close(fd);
240 		return -1;
241 	}
242 
243 	*blocks = pinfo.media_blocks;
244 	*bsize = pinfo.media_blksize;
245 
246 	close(fd);
247 	return 0;
248 }
249 #elif defined(__linux__)
250 int
251 get_disk_info(const char *dev, size_t *blocks, size_t *bsize)
252 {
253 	uint64_t nbytes;
254 	int blocksz;
255 	int fd;
256 
257 	if ((fd = open(dev, O_RDONLY)) < 0) {
258 		tc_log(1, "Error opening %s\n", dev);
259 		return -1;
260 	}
261 
262 	if ((ioctl(fd, BLKSSZGET, &blocksz)) < 0) {
263 		close(fd);
264 		return -1;
265 	}
266 
267 	if ((ioctl(fd, BLKGETSIZE64, &nbytes)) < 0) {
268 		close(fd);
269 		return -1;
270 	}
271 
272 	*blocks = (size_t)(nbytes / blocksz);
273 	*bsize = (size_t)(blocksz);
274 
275 	close(fd);
276 	return 0;
277 }
278 #endif
279 
280 int
281 write_to_disk(const char *dev, off_t offset, size_t blksz, void *mem,
282     size_t bytes)
283 {
284 	unsigned char *mem_buf = NULL;
285 	ssize_t w;
286 	size_t sz;
287 	off_t internal_off;
288 	int fd;
289 
290 	/* Align to block sizes */
291 	internal_off = offset % blksz;
292 #ifdef DEBUG
293 	printf("offset: %"PRIu64", internal offset: %"PRIu64"\n",
294 	    (uint64_t)offset, (uint64_t)internal_off);
295 #endif
296 	offset = (offset/blksz) * blksz;
297 
298 	if ((internal_off + bytes) > blksz) {
299 		tc_log(1, "This should never happen: internal_off + bytes > "
300 		    "blksz (write_to_disk)\n");
301 		return -1;
302 	}
303 
304 	if ((bytes < blksz) || (internal_off != 0)) {
305 		sz = blksz;
306 		if ((mem_buf = read_to_safe_mem(dev, offset, &sz)) == NULL) {
307 			tc_log(1, "Error buffering data on "
308 			    "write_to_disk(%s)\n", dev);
309 			return -1;
310 		}
311 
312 		memcpy(mem_buf + internal_off, mem, bytes);
313 	}
314 
315 	if ((fd = open(dev, O_WRONLY)) < 0) {
316 		tc_log(1, "Error opening device %s\n", dev);
317 		return -1;
318 	}
319 
320 	if ((lseek(fd, offset, SEEK_SET) < 0)) {
321 		tc_log(1, "Error seeking on device %s\n", dev);
322 		close(fd);
323 		return -1;
324 	}
325 
326 	if ((w = write(fd, (mem_buf != NULL) ? mem_buf : mem, bytes)) <= 0) {
327 		tc_log(1, "Error writing to device %s\n", dev);
328 		close(fd);
329 		return -1;
330 	}
331 
332 	close(fd);
333 
334 	if (mem_buf != NULL)
335 		free_safe_mem(mem_buf);
336 	return 0;
337 }
338 
339 int
340 read_passphrase(const char *prompt, char *pass, size_t passlen, time_t timeout)
341 {
342 	struct termios termios_old, termios_new;
343 	struct timeval to;
344 	fd_set fds;
345 	ssize_t n;
346 	int fd, r = 0, cfd = 0, nready;
347 
348 	if ((fd = open("/dev/tty", O_RDONLY)) == -1) {
349 		fd = STDIN_FILENO;
350 		cfd = 1;
351 	}
352 
353 	printf(prompt);
354 	fflush(stdout);
355 
356 	memset(pass, 0, passlen);
357 
358 	tcgetattr(fd, &termios_old);
359 	memcpy(&termios_new, &termios_old, sizeof(termios_new));
360 	termios_new.c_lflag &= ~ECHO;
361 	tcsetattr(fd, TCSAFLUSH, &termios_new);
362 
363 	if (timeout > 0) {
364 		memset(&to, 0, sizeof(to));
365 		to.tv_sec = timeout;
366 
367 		FD_ZERO(&fds);
368 		FD_SET(fd, &fds);
369 		nready = select(fd + 1, &fds, NULL, NULL, &to);
370 		if (nready <= 0) {
371 			r = EINTR;
372 			goto out;
373 		}
374 	}
375 
376 	n = read(fd, pass, passlen-1);
377 	if (n > 0) {
378 		pass[n-1] = '\0'; /* Strip trailing \n */
379 	} else {
380 		r = EIO;
381 	}
382 
383 out:
384 	if (cfd)
385 		close(fd);
386 
387 	tcsetattr(fd, TCSAFLUSH, &termios_old);
388 	putchar('\n');
389 
390 	return r;
391 }
392