xref: /dragonfly/lib/libtcplay/io.c (revision b4fe153d9d3d5c35a5aa592966ba6981e93f2d74)
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/param.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/stat.h>
38 #include <sys/uio.h>
39 #include <sys/select.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <termios.h>
46 #include <unistd.h>
47 #include <signal.h>
48 
49 #include "tcplay.h"
50 
51 void *
read_to_safe_mem(const char * file,off_t offset,size_t * sz)52 read_to_safe_mem(const char *file, off_t offset, size_t *sz)
53 {
54           void *mem = NULL;
55           ssize_t r = 0;
56           int fd;
57 
58           if ((fd = open(file, O_RDONLY)) < 0) {
59                     tc_log(1, "Error opening file %s\n", file);
60                     return NULL;
61           }
62 
63           if ((mem = alloc_safe_mem(*sz)) == NULL) {
64                     tc_log(1, "Error allocating memory\n");
65                     goto out;
66           }
67 
68           if ((lseek(fd, offset, (offset >= 0) ? SEEK_SET : SEEK_END) < 0)) {
69                     tc_log(1, "Error seeking on file %s\n", file);
70                     goto m_err;
71           }
72 
73           if ((r = read(fd, mem, *sz)) <= 0) {
74                     tc_log(1, "Error reading from file %s\n", file);
75                     goto m_err;
76           }
77 
78 out:
79           *sz = r;
80           close(fd);
81           return mem;
82           /* NOT REACHED */
83 
84 m_err:
85           free_safe_mem(mem);
86           close(fd);
87           return NULL;
88 }
89 
90 static size_t get_random_total_bytes = 0;
91 static size_t get_random_read_bytes = 0;
92 
93 
94 float
get_random_read_progress(void)95 get_random_read_progress(void)
96 {
97           return (get_random_total_bytes == 0) ? 0.0 :
98               (1.0 * get_random_read_bytes) /
99               (1.0 * get_random_total_bytes) * 100.0;
100 }
101 
102 static
103 void
get_random_summary(void)104 get_random_summary(void)
105 {
106           float pct_done = get_random_read_progress();
107 
108           tc_log(0, "Gathering true randomness, %.0f%% done.\n", pct_done);
109 }
110 
111 int
get_random(unsigned char * buf,size_t len,int weak)112 get_random(unsigned char *buf, size_t len, int weak)
113 {
114           int fd;
115           ssize_t r;
116           size_t rd = 0;
117           size_t sz;
118           struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; /* 10 ms */
119 
120 
121           if ((fd = open((weak) ? "/dev/urandom" : "/dev/random", O_RDONLY)) < 0) {
122                     tc_log(1, "Error opening /dev/random\n");
123                     return -1;
124           }
125 
126           summary_fn = get_random_summary;
127           get_random_total_bytes = len;
128           tc_internal_state = STATE_GET_RANDOM;
129 
130           /* Get random data in 16-byte chunks */
131           sz = 16;
132           while (rd < len) {
133                     get_random_read_bytes = rd;
134 
135                     if ((len - rd) < sz)
136                               sz = (len - rd);
137 
138                     if ((r = read(fd, buf+rd, sz)) < 0) {
139                               tc_log(1, "Error reading from /dev/random(%d): %s\n",
140                                   fd, strerror(errno));
141                               close(fd);
142                               summary_fn = NULL;
143                               tc_internal_state = STATE_UNKNOWN;
144                               return -1;
145                     }
146                     rd += r;
147                     nanosleep(&ts, NULL);
148           }
149 
150           close(fd);
151           summary_fn = NULL;
152 
153           tc_internal_state = STATE_UNKNOWN;
154           return 0;
155 }
156 
157 static disksz_t secure_erase_total_bytes = 0;
158 static disksz_t secure_erase_erased_bytes = 0;
159 
160 float
get_secure_erase_progress(void)161 get_secure_erase_progress(void)
162 {
163           return (secure_erase_total_bytes == 0) ? 0.0 :
164               (1.0 * secure_erase_erased_bytes) /
165               (1.0 * secure_erase_total_bytes) * 100.0;
166 }
167 
168 static
169 void
secure_erase_summary(void)170 secure_erase_summary(void)
171 {
172           float pct_done = get_secure_erase_progress();
173           tc_log(0, "Securely erasing, %.0f%% done.\n", pct_done);
174 }
175 
176 int
secure_erase(const char * dev,disksz_t bytes,size_t blksz)177 secure_erase(const char *dev, disksz_t bytes, size_t blksz)
178 {
179           size_t erased = 0;
180           int fd_rand, fd;
181           char buf[ERASE_BUFFER_SIZE];
182           ssize_t r, w;
183           size_t sz;
184 
185           if (blksz > MAX_BLKSZ) {
186                     tc_log(1, "blksz > MAX_BLKSZ\n");
187                     return -1;
188           }
189 
190           if ((fd_rand = open("/dev/urandom", O_RDONLY)) < 0) {
191                     tc_log(1, "Error opening /dev/urandom\n");
192                     return -1;
193           }
194 
195           if ((fd = open(dev, O_WRONLY)) < 0) {
196                     close(fd_rand);
197                     tc_log(1, "Error opening %s\n", dev);
198                     return -1;
199           }
200 
201           summary_fn = secure_erase_summary;
202           secure_erase_total_bytes = bytes;
203 
204           tc_internal_state = STATE_ERASE;
205 
206           sz = ERASE_BUFFER_SIZE;
207           while (erased < bytes) {
208                     secure_erase_erased_bytes = erased;
209                     /* Switch to block size when not much is remaining */
210                     if ((bytes - erased) <= ERASE_BUFFER_SIZE)
211                               sz = blksz;
212 
213                     if ((r = read(fd_rand, buf, sz)) < 0) {
214                               tc_log(1, "Error reading from /dev/urandom\n");
215                               close(fd);
216                               close(fd_rand);
217                               summary_fn = NULL;
218                               tc_internal_state = STATE_UNKNOWN;
219                               return -1;
220                     }
221 
222                     if (r < (ssize_t)blksz)
223                               continue;
224 
225                     if ((w = write(fd, buf, r)) < 0) {
226                               tc_log(1, "Error writing to %s\n", dev);
227                               close(fd);
228                               close(fd_rand);
229                               summary_fn = NULL;
230                               tc_internal_state = STATE_UNKNOWN;
231                               return -1;
232                     }
233 
234                     erased += (size_t)w;
235           }
236 
237           close(fd);
238           close(fd_rand);
239 
240           summary_fn = NULL;
241 
242           tc_internal_state = STATE_UNKNOWN;
243           return 0;
244 }
245 
246 #if defined(__DragonFly__)
247 int
get_disk_info(const char * dev,disksz_t * blocks,size_t * bsize)248 get_disk_info(const char *dev, disksz_t *blocks, size_t *bsize)
249 {
250           struct partinfo pinfo;
251           int fd;
252 
253           if ((fd = open(dev, O_RDONLY)) < 0) {
254                     tc_log(1, "Error opening %s\n", dev);
255                     return -1;
256           }
257 
258           memset(&pinfo, 0, sizeof(struct partinfo));
259 
260           if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
261                     close(fd);
262                     return -1;
263           }
264 
265           *blocks = (disksz_t)pinfo.media_blocks;
266           *bsize = pinfo.media_blksize;
267 
268           close(fd);
269           return 0;
270 }
271 #elif defined(__linux__)
272 int
get_disk_info(const char * dev,disksz_t * blocks,size_t * bsize)273 get_disk_info(const char *dev, disksz_t *blocks, size_t *bsize)
274 {
275           uint64_t nbytes;
276           int blocksz;
277           int fd;
278 
279           if ((fd = open(dev, O_RDONLY)) < 0) {
280                     tc_log(1, "Error opening %s\n", dev);
281                     return -1;
282           }
283 
284           if ((ioctl(fd, BLKSSZGET, &blocksz)) < 0) {
285                     close(fd);
286                     return -1;
287           }
288 
289           if ((ioctl(fd, BLKGETSIZE64, &nbytes)) < 0) {
290                     close(fd);
291                     return -1;
292           }
293 
294           *blocks = (disksz_t)(nbytes / blocksz);
295           *bsize = (size_t)(blocksz);
296 
297           close(fd);
298           return 0;
299 }
300 #endif
301 
302 int
write_to_disk(const char * dev,off_t offset,size_t blksz,void * mem,size_t bytes)303 write_to_disk(const char *dev, off_t offset, size_t blksz, void *mem,
304     size_t bytes)
305 {
306           unsigned char *mem_buf = NULL;
307           ssize_t w;
308           size_t sz;
309           off_t internal_off;
310           int fd;
311 
312           /* Align to block sizes */
313           internal_off = offset % blksz;
314 #ifdef DEBUG
315           printf("offset: %"PRIu64", internal offset: %"PRIu64"\n",
316               (uint64_t)offset, (uint64_t)internal_off);
317 #endif
318           offset = rounddown(offset, blksz);
319 
320           if ((internal_off + bytes) > blksz) {
321                     tc_log(1, "This should never happen: internal_off + bytes > "
322                         "blksz (write_to_disk)\n");
323                     return -1;
324           }
325 
326           if ((bytes < blksz) || (internal_off != 0)) {
327                     sz = blksz;
328                     if ((mem_buf = read_to_safe_mem(dev, offset, &sz)) == NULL) {
329                               tc_log(1, "Error buffering data on "
330                                   "write_to_disk(%s)\n", dev);
331                               return -1;
332                     }
333 
334                     memcpy(mem_buf + internal_off, mem, bytes);
335           }
336 
337           if ((fd = open(dev, O_WRONLY)) < 0) {
338                     tc_log(1, "Error opening device %s\n", dev);
339                     return -1;
340           }
341 
342           if ((lseek(fd, offset, (offset >= 0) ? SEEK_SET : SEEK_END) < 0)) {
343                     tc_log(1, "Error seeking on device %s\n", dev);
344                     close(fd);
345                     return -1;
346           }
347 
348           if ((w = write(fd, (mem_buf != NULL) ? mem_buf : mem, bytes)) <= 0) {
349                     tc_log(1, "Error writing to device %s\n", dev);
350                     close(fd);
351                     return -1;
352           }
353 
354           close(fd);
355 
356           if (mem_buf != NULL)
357                     free_safe_mem(mem_buf);
358           return 0;
359 }
360 
361 
362 int
write_to_file(const char * file,void * mem,size_t bytes)363 write_to_file(const char *file, void *mem, size_t bytes)
364 {
365           int fd;
366           ssize_t w;
367 
368           if ((fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
369                     tc_log(1, "Error opening file %s\n", file);
370                     return -1;
371           }
372 
373           if ((w = write(fd, mem, bytes)) < 0) {
374                     tc_log(1, "Error writing to file %s\n", file);
375                     close(fd);
376                     return -1;
377           }
378 
379           close(fd);
380           return 0;
381 }
382 
383 
384 static struct termios termios_old;
385 static int tty_fd;
386 
sigint_termios(int sa)387 static void sigint_termios(int sa)
388 {
389           tcsetattr(tty_fd, TCSAFLUSH, &termios_old);
390           exit(sa);
391 }
392 
393 int
read_passphrase(const char * prompt,char * pass,size_t passlen,size_t bufsz,time_t timeout)394 read_passphrase(const char *prompt, char *pass, size_t passlen, size_t bufsz, time_t timeout)
395 {
396           struct termios termios_new;
397           struct timeval to;
398           fd_set fds;
399           ssize_t n;
400           int fd = STDIN_FILENO, r = 0, nready;
401           struct sigaction act, old_act;
402           int is_tty = isatty(fd);
403 
404           if (is_tty == 0)
405                     errno = 0;
406 
407           memset(pass, 0, bufsz);
408 
409           printf("%s", prompt);
410           fflush(stdout);
411 
412           /* If input is being provided by something which is not a terminal, don't
413            * change the settings. */
414           if (is_tty) {
415                     tcgetattr(fd, &termios_old);
416                     memcpy(&termios_new, &termios_old, sizeof(termios_new));
417                     termios_new.c_lflag &= ~ECHO;
418 
419                     act.sa_handler = sigint_termios;
420                     act.sa_flags   = SA_RESETHAND;
421                     sigemptyset(&act.sa_mask);
422 
423                     tty_fd = fd;
424                     sigaction(SIGINT, &act, &old_act);
425 
426                     tcsetattr(fd, TCSAFLUSH, &termios_new);
427           }
428 
429           if (timeout > 0) {
430                     memset(&to, 0, sizeof(to));
431                     to.tv_sec = timeout;
432 
433                     FD_ZERO(&fds);
434                     FD_SET(fd, &fds);
435                     nready = select(fd + 1, &fds, NULL, NULL, &to);
436                     if (nready <= 0) {
437                               r = EINTR;
438                               goto out;
439                     }
440           }
441 
442           n = read(fd, pass, bufsz-1);
443           if (n > 0) {
444                     pass[n-1] = '\0'; /* Strip trailing \n */
445           } else {
446                     r = EIO;
447           }
448 
449           /* Warn about passphrase trimming */
450           if (strlen(pass) > MAX_PASSSZ)
451                     tc_log(0, "WARNING: Passphrase is being trimmed to %zu "
452                         "characters, discarding rest.\n", passlen);
453 
454           /* Cut off after passlen characters */
455           pass[passlen] = '\0';
456 
457 out:
458           if (is_tty) {
459                     tcsetattr(fd, TCSAFLUSH, &termios_old);
460                     putchar('\n');
461 
462                     sigaction(SIGINT, &old_act, NULL);
463           }
464 
465           return r;
466 }
467