xref: /dragonfly/lib/libtcplay/tcplay_api.c (revision 91dc43dd)
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 <stdlib.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <stdarg.h>
36 
37 #include "tcplay.h"
38 #include "tcplay_api.h"
39 #include "tcplay_api_internal.h"
40 
41 
42 int
43 tc_api_init(int verbose)
44 {
45 	int error;
46 
47 	tc_internal_verbose = verbose;
48 
49 	if ((error = tc_play_init()) != 0)
50 		return TC_ERR;
51 	else
52 		return TC_OK;
53 }
54 
55 int
56 tc_api_uninit(void)
57 {
58 	check_and_purge_safe_mem();
59 	return TC_OK;
60 }
61 
62 
63 static const char *_caps[] = {
64 	"trim",
65 	NULL
66 };
67 
68 int
69 tc_api_has(const char *feature)
70 {
71 	const char *cap;
72 	int i;
73 
74 	for (cap = _caps[0], i = 0; cap != NULL; cap = _caps[++i]) {
75 		if ((strcmp(cap, feature)) == 0)
76 			return TC_OK;
77 	}
78 
79 	return TC_ERR_UNIMPL;
80 }
81 
82 int
83 tc_api_cipher_iterate(tc_api_cipher_iterator_fn fn, void *priv)
84 {
85 	int i;
86 	struct tc_cipher_chain *chain;
87 	int klen;
88 	int length;
89 	char buf[1024];
90 
91 	if (fn == NULL) {
92 		errno = EFAULT;
93 		return TC_ERR;
94 	}
95 
96 	for (i = 0, chain = tc_cipher_chains[0]; chain != NULL;
97 	     chain = tc_cipher_chains[++i]) {
98 		tc_cipher_chain_sprint(buf, sizeof(buf), chain);
99 		klen = tc_cipher_chain_klen(chain);
100 		length = tc_cipher_chain_length(chain);
101 		if ((fn(priv, buf, klen, length)) < 0)
102 			break;
103 	}
104 
105 	return TC_OK;
106 }
107 
108 int
109 tc_api_prf_iterate(tc_api_prf_iterator_fn fn, void *priv)
110 {
111 	int i;
112 
113 	if (fn == NULL) {
114 		errno = EFAULT;
115 		return TC_ERR;
116 	}
117 
118 	/* start at 1 due to RIPEMD weirdness... */
119 	for (i = 1; pbkdf_prf_algos[i].name != NULL; i++) {
120 		if ((fn(priv, pbkdf_prf_algos[i].name)) < 0)
121 			break;
122 	}
123 
124 	return TC_OK;
125 }
126 
127 
128 const char *
129 tc_api_task_get_error(tc_api_task task __unused)
130 {
131 	return tc_internal_log_buffer;
132 }
133 
134 
135 #define _match(k, v) (strcmp(k, v) == 0)
136 
137 tc_api_task
138 tc_api_task_init(const char *op)
139 {
140 	tc_api_task task = NULL;
141 	int fail = 1;
142 
143 	if ((task = alloc_safe_mem(sizeof(*task))) == NULL) {
144 		errno = ENOMEM;
145 		goto out;
146 	}
147 
148 	if ((task->opts = opts_init()) == NULL) {
149 		errno = ENOMEM;
150 		goto out;
151 	}
152 
153 	if (_match(op, "create")) {
154 		task->op = TC_OP_CREATE;
155 	} else if (_match(op, "map")) {
156 		task->op = TC_OP_MAP;
157 	} else if (_match(op, "unmap")) {
158 		task->op = TC_OP_UNMAP;
159 	} else if (_match(op, "info")) {
160 		task->op = TC_OP_INFO;
161 	} else if (_match(op, "info_mapped")) {
162 		task->op = TC_OP_INFO_MAPPED;
163 	} else if (_match(op, "modify")) {
164 		task->op = TC_OP_MODIFY;
165 	} else if (_match(op, "restore")) {
166 		task->op = TC_OP_RESTORE;
167 	} else {
168 		errno = EINVAL;
169 		goto out;
170 	}
171 
172 	fail = 0;
173 
174 out:
175 	if (fail && task != NULL) {
176 		if (task->opts != NULL)
177 			opts_free(task->opts);
178 		free_safe_mem(task);
179 	}
180 
181 	return fail ? NULL : task;
182 }
183 
184 int
185 tc_api_task_uninit(tc_api_task task)
186 {
187 	if (task->last_info != NULL)
188 		free_info(task->last_info);
189 	opts_free(task->opts);
190 	free_safe_mem(task);
191 
192 	return TC_OK;
193 }
194 
195 
196 #define _set_str(k) \
197 	do {							\
198 		if ((opts->k = strdup_safe_mem(s)) == NULL) {	\
199 			errno = ENOMEM;				\
200 			r = TC_ERR;				\
201 			goto out;				\
202 		}						\
203 	} while (0)
204 
205 #define _clr_str(k) \
206 	do {							\
207 		if (opts->k)					\
208 			free_safe_mem(opts->k);			\
209 		opts->k = NULL;					\
210 	} while (0)
211 
212 int
213 tc_api_task_set(tc_api_task task, const char *key, ...)
214 {
215 	struct tcplay_opts *opts;
216 	va_list ap;
217 	const char *s;
218 	int64_t i64;
219 	int i;
220 	tc_api_state_change_fn sc_fn;
221 	void *vp;
222 	int r = TC_OK;
223 
224 	if (task == NULL || ((opts = task->opts) == NULL)) {
225 		errno = EFAULT;
226 		return TC_ERR;
227 	}
228 
229 	va_start(ap, key);
230 
231 	if (_match(key, "interactive")) {
232 		i = va_arg(ap, int);
233 		opts->interactive = i;
234 	} else if (_match(key, "weak_keys_and_salt")) {
235 		i = va_arg(ap, int);
236 		opts->weak_keys_and_salt = i;
237 	} else if (_match(key, "secure_erase")) {
238 		i = va_arg(ap, int);
239 		opts->secure_erase = i;
240 	} else if (_match(key, "protect_hidden")) {
241 		i = va_arg(ap, int);
242 		opts->protect_hidden = i;
243 	} else if (_match(key, "fde")) {
244 		i = va_arg(ap, int);
245 		if (i)
246 			opts->flags |= TC_FLAG_FDE;
247 		else
248 			opts->flags &= ~TC_FLAG_FDE;
249 	} else if (_match(key, "use_backup_header")) {
250 		i = va_arg(ap, int);
251 		if (i)
252 			opts->flags |= TC_FLAG_BACKUP;
253 		else
254 			opts->flags &= ~TC_FLAG_BACKUP;
255 	} else if (_match(key, "allow_trim")) {
256 		i = va_arg(ap, int);
257 		if (i)
258 			opts->flags |= TC_FLAG_ALLOW_TRIM;
259 		else
260 			opts->flags &= ~TC_FLAG_ALLOW_TRIM;
261 	} else if (_match(key, "hidden_size_bytes")) {
262 		i64 = va_arg(ap, int64_t);
263 		opts->hidden_size_bytes = (disksz_t)i64;
264 		opts->hidden = (i64 > 0);
265 	} else if (_match(key, "retries")) {
266 		i = va_arg(ap, int);
267 		opts->retries = i;
268 	} else if (_match(key, "timeout")) {
269 		i = va_arg(ap, int);
270 		opts->timeout = (time_t)i;
271 	} else if (_match(key, "save_header_to_file")) {
272 		s = va_arg(ap, const char *);
273 		if (s != NULL) {
274 			_set_str(hdr_file_out);
275 			opts->flags |= TC_FLAG_SAVE_TO_FILE;
276 		} else {
277 			_clr_str(hdr_file_out);
278 			opts->flags &= ~TC_FLAG_SAVE_TO_FILE;
279 		}
280 	} else if (_match(key, "header_from_file")) {
281 		s = va_arg(ap, const char *);
282 		if (s != NULL) {
283 			_set_str(hdr_file_in);
284 			opts->flags |= TC_FLAG_HDR_FROM_FILE;
285 		} else {
286 			_clr_str(hdr_file_in);
287 			opts->flags &= ~TC_FLAG_HDR_FROM_FILE;
288 		}
289 	} else if (_match(key, "hidden_header_from_file")) {
290 		s = va_arg(ap, const char *);
291 		if (s != NULL) {
292 			_set_str(h_hdr_file_in);
293 			opts->flags |= TC_FLAG_H_HDR_FROM_FILE;
294 		} else {
295 			_clr_str(h_hdr_file_in);
296 			opts->flags &= ~TC_FLAG_H_HDR_FROM_FILE;
297 		}
298 	} else if (_match(key, "sys")) {
299 		s = va_arg(ap, const char *);
300 		if (s != NULL) {
301 			_set_str(sys_dev);
302 			opts->flags |= TC_FLAG_SYS;
303 		} else {
304 			_clr_str(sys_dev);
305 			opts->flags &= ~TC_FLAG_SYS;
306 		}
307 	} else if (_match(key, "passphrase")) {
308 		s = va_arg(ap, const char *);
309 		if (s != NULL) {
310 			_set_str(passphrase);
311 		} else {
312 			_clr_str(passphrase);
313 		}
314 	} else if (_match(key, "h_passphrase")) {
315 		s = va_arg(ap, const char *);
316 		if (s != NULL) {
317 			_set_str(h_passphrase);
318 		} else {
319 			_clr_str(h_passphrase);
320 		}
321 	} else if (_match(key, "new_passphrase")) {
322 		s = va_arg(ap, const char *);
323 		if (s != NULL) {
324 			_set_str(new_passphrase);
325 		} else {
326 			_clr_str(new_passphrase);
327 		}
328 	} else if (_match(key, "dev")) {
329 		s = va_arg(ap, const char *);
330 		if (s != NULL) {
331 			_set_str(dev);
332 		} else {
333 			_clr_str(dev);
334 		}
335 	} else if (_match(key, "map_name")) {
336 		s = va_arg(ap, const char *);
337 		if (s != NULL) {
338 			_set_str(map_name);
339 		} else {
340 			_clr_str(map_name);
341 		}
342 	} else if (_match(key, "keyfiles")) {
343 		s = va_arg(ap, const char *);
344 		if (s != NULL) {
345 			opts_add_keyfile(opts, s);
346 		} else {
347 			opts_clear_keyfile(opts);
348 		}
349 	} else if (_match(key, "h_keyfiles")) {
350 		s = va_arg(ap, const char *);
351 		if (s != NULL) {
352 			opts_add_keyfile_hidden(opts, s);
353 		} else {
354 			opts_clear_keyfile_hidden(opts);
355 		}
356 	} else if (_match(key, "new_keyfiles")) {
357 		s = va_arg(ap, const char *);
358 		if (s != NULL) {
359 			opts_add_keyfile_new(opts, s);
360 		} else {
361 			opts_clear_keyfile_new(opts);
362 		}
363 	} else if (_match(key, "prf_algo")) {
364 		s = va_arg(ap, const char *);
365 		if (s != NULL) {
366 			if ((opts->prf_algo = check_prf_algo(s, 1)) == NULL) {
367 				errno = ENOENT;
368 				r = TC_ERR;
369 				goto out;
370 			}
371 		} else {
372 			opts->prf_algo = NULL;
373 		}
374 	} else if (_match(key, "h_prf_algo")) {
375 		s = va_arg(ap, const char *);
376 		if (s != NULL) {
377 			if ((opts->h_prf_algo = check_prf_algo(s, 1)) == NULL) {
378 				errno = ENOENT;
379 				r = TC_ERR;
380 				goto out;
381 			}
382 		} else {
383 			opts->h_prf_algo = NULL;
384 		}
385 	} else if (_match(key, "new_prf_algo")) {
386 		s = va_arg(ap, const char *);
387 		if (s != NULL) {
388 			if ((opts->new_prf_algo = check_prf_algo(s, 1)) == NULL) {
389 				errno = ENOENT;
390 				r = TC_ERR;
391 				goto out;
392 			}
393 		} else {
394 			opts->new_prf_algo = NULL;
395 		}
396 	} else if (_match(key, "cipher_chain")) {
397 		s = va_arg(ap, const char *);
398 		if (s != NULL) {
399 			if ((opts->cipher_chain = check_cipher_chain(s, 1)) == NULL) {
400 				errno = ENOENT;
401 				r = TC_ERR;
402 				goto out;
403 			}
404 		} else {
405 			opts->cipher_chain = NULL;
406 		}
407 	} else if (_match(key, "h_cipher_chain")) {
408 		s = va_arg(ap, const char *);
409 		if (s != NULL) {
410 			if ((opts->h_cipher_chain = check_cipher_chain(s, 1)) == NULL) {
411 				errno = ENOENT;
412 				r = TC_ERR;
413 				goto out;
414 			}
415 		} else {
416 			opts->h_cipher_chain = NULL;
417 		}
418 	} else if (_match(key, "state_change_fn")) {
419 		sc_fn = va_arg(ap, tc_api_state_change_fn);
420 		opts->state_change_fn = sc_fn;
421 		vp = va_arg(ap, void *);
422 		opts->api_ctx = vp;
423 	} else {
424 		r = TC_ERR_UNIMPL;
425 	}
426 
427 out:
428 	va_end(ap);
429 
430 	return r;
431 }
432 
433 #define _not_null(x) \
434 	if (opts->x == NULL) {	\
435 		return -1;	\
436 	}
437 
438 #define _null(x) \
439 	if (opts->x != NULL) {	\
440 		return -1;	\
441 	}
442 
443 #define _zero(x) \
444 	if (opts->x != 0) {	\
445 		return -1;	\
446 	}
447 
448 #define _not_set(x) \
449 	if (TC_FLAG_SET(opts->flags, x)) {	\
450 		return -1;			\
451 	}
452 
453 static
454 int
455 _opts_check_create(struct tcplay_opts *opts)
456 {
457 	_not_null(dev);
458 	_not_set(SYS);
459 	_not_set(FDE);
460 	_not_set(BACKUP);
461 	_not_set(ONLY_RESTORE);
462 	_not_set(ALLOW_TRIM);
463 	_not_set(SAVE_TO_FILE);
464 	_not_set(HDR_FROM_FILE);
465 	_not_set(H_HDR_FROM_FILE);
466 
467 	_null(map_name);
468 	_zero(protect_hidden);
469 	_null(new_passphrase);
470 	_null(new_prf_algo);
471 	_zero(n_newkeyfiles);
472 
473 	if (opts->hidden_size_bytes && !opts->hidden) {
474 		return -1;
475 	}
476 
477 	return 0;
478 }
479 
480 static
481 int
482 _opts_check_map(struct tcplay_opts *opts)
483 {
484 	_not_null(dev);
485 	_not_null(map_name);
486 	_not_set(ONLY_RESTORE);
487 	_not_set(SAVE_TO_FILE);
488 	_zero(hidden);
489 	_zero(hidden_size_bytes);
490 	_null(new_passphrase);
491 	_null(new_prf_algo);
492 	_zero(n_newkeyfiles);
493 	_null(prf_algo);
494 	_null(h_prf_algo);
495 	_null(cipher_chain);
496 	_null(h_cipher_chain);
497 
498 	if (!opts->protect_hidden) {
499 		_zero(n_hkeyfiles);
500 		//_null(h_passphrase);
501 	}
502 
503 	return 0;
504 }
505 
506 static
507 int
508 _opts_check_unmap(struct tcplay_opts *opts)
509 {
510 	_not_null(map_name);
511 	/* XXX: _not_null(dev); ? */
512 	_zero(nkeyfiles);
513 	_zero(n_hkeyfiles);
514 	_null(prf_algo);
515 	_null(cipher_chain);
516 	_null(h_prf_algo);
517 	_null(h_cipher_chain);
518 	_null(passphrase);
519 	_null(h_passphrase);
520 	_zero(hidden);
521 	_zero(protect_hidden);
522 	_null(new_prf_algo);
523 	_null(new_passphrase);
524 	_zero(n_newkeyfiles);
525 	_not_set(SYS);
526 	_not_set(FDE);
527 	_not_set(BACKUP);
528 	_not_set(ONLY_RESTORE);
529 	_not_set(ALLOW_TRIM);
530 	_not_set(SAVE_TO_FILE);
531 	_not_set(HDR_FROM_FILE);
532 	_not_set(H_HDR_FROM_FILE);
533 
534 	return 0;
535 }
536 
537 static
538 int
539 _opts_check_info(struct tcplay_opts *opts)
540 {
541 	_not_null(dev);
542 	_null(map_name);
543 	_not_set(ONLY_RESTORE);
544 	_not_set(SAVE_TO_FILE);
545 	_zero(hidden);
546 	_zero(hidden_size_bytes);
547 	_null(new_passphrase);
548 	_null(new_prf_algo);
549 	_zero(n_newkeyfiles);
550 	_null(prf_algo);
551 	_null(h_prf_algo);
552 	_null(cipher_chain);
553 	_null(h_cipher_chain);
554 
555 	if (!opts->protect_hidden) {
556 		_zero(n_hkeyfiles);
557 		//_null(h_passphrase);
558 	}
559 
560 	return 0;
561 }
562 
563 static
564 int
565 _opts_check_info_mapped(struct tcplay_opts *opts)
566 {
567 	_not_null(map_name);
568 	/* XXX: _not_null(dev); ? */
569 	_zero(nkeyfiles);
570 	_zero(n_hkeyfiles);
571 	_null(prf_algo);
572 	_null(cipher_chain);
573 	_null(h_prf_algo);
574 	_null(h_cipher_chain);
575 	_null(passphrase);
576 	_null(h_passphrase);
577 	_zero(hidden);
578 	_zero(protect_hidden);
579 	_null(new_prf_algo);
580 	_null(new_passphrase);
581 	_zero(n_newkeyfiles);
582 	_not_set(SYS);
583 	_not_set(FDE);
584 	_not_set(BACKUP);
585 	_not_set(ONLY_RESTORE);
586 	_not_set(ALLOW_TRIM);
587 	_not_set(SAVE_TO_FILE);
588 	_not_set(HDR_FROM_FILE);
589 	_not_set(H_HDR_FROM_FILE);
590 
591 	return 0;
592 }
593 
594 static
595 int
596 _opts_check_modify(struct tcplay_opts *opts)
597 {
598 	_not_null(dev);
599 	_null(map_name);
600 	_zero(hidden);
601 	_zero(hidden_size_bytes);
602 	_null(prf_algo);
603 	_null(h_prf_algo);
604 	_null(cipher_chain);
605 	_null(h_cipher_chain);
606 
607 	if (!opts->protect_hidden) {
608 		_zero(n_hkeyfiles);
609 		_null(h_passphrase);
610 	}
611 
612 	return 0;
613 }
614 
615 
616 static
617 int
618 _opts_check_restore(struct tcplay_opts *opts)
619 {
620 	if ((_opts_check_modify(opts)) < 0)
621 		return -1;
622 
623 	_null(new_prf_algo);
624 	_zero(n_newkeyfiles);
625 	_null(new_passphrase);
626 
627 	return 0;
628 }
629 
630 int
631 tc_api_task_do(tc_api_task task)
632 {
633 	struct tcplay_opts *opts;
634 	int r = TC_OK;
635 
636 	if (task == NULL || ((opts = task->opts) == NULL)) {
637 		errno = EFAULT;
638 		return TC_ERR;
639 	}
640 
641 	if (task->last_info != NULL) {
642 		free_info(task->last_info);
643 	}
644 
645 	switch (task->op) {
646 	case TC_OP_CREATE:
647 		if ((r = _opts_check_create(task->opts)) != 0) {
648 			errno = EINVAL;
649 			return r;
650 		}
651 		r = create_volume(opts);
652 		break;
653 
654 	case TC_OP_MAP:
655 		if ((r = _opts_check_map(task->opts)) != 0) {
656 			errno = EINVAL;
657 			return r;
658 		}
659 		r = map_volume(opts);
660 		break;
661 
662 	case TC_OP_UNMAP:
663 		if ((r = _opts_check_unmap(task->opts)) != 0) {
664 			errno = EINVAL;
665 			return r;
666 		}
667 		r = dm_teardown(opts->map_name, opts->dev);
668 		break;
669 
670 	case TC_OP_INFO:
671 		if ((r = _opts_check_info(task->opts)) != 0) {
672 			errno = EINVAL;
673 			return r;
674 		}
675 		if ((task->last_info = info_map_common(opts, NULL)) == NULL) {
676 			r = TC_ERR;
677 		}
678 		break;
679 
680 	case TC_OP_INFO_MAPPED:
681 		if ((r = _opts_check_info_mapped(task->opts)) != 0) {
682 			errno = EINVAL;
683 			return r;
684 		}
685 		if ((task->last_info = dm_info_map(opts->map_name)) == NULL) {
686 			r = TC_ERR;
687 		}
688 		break;
689 
690 	case TC_OP_MODIFY:
691 		if ((r = _opts_check_modify(task->opts)) != 0) {
692 			errno = EINVAL;
693 			return r;
694 		}
695 		r = modify_volume(opts);
696 		break;
697 
698 	case TC_OP_RESTORE:
699 		if ((r = _opts_check_restore(task->opts)) != 0) {
700 			errno = EINVAL;
701 			return r;
702 		}
703 		opts->flags |= TC_FLAG_ONLY_RESTORE;
704 		r = modify_volume(opts);
705 		opts->flags &= ~TC_FLAG_ONLY_RESTORE;
706 		break;
707 	}
708 
709 	return r;
710 }
711 
712 
713 int
714 tc_api_task_info_get(tc_api_task task, const char *key, ...)
715 {
716 	char buf[1024];
717 	va_list ap;
718 	struct tcplay_info *info;
719 	char *s;
720 	int *ip;
721 	int64_t *i64p;
722 	int r = TC_OK;
723 	size_t sz;
724 
725 	if (task == NULL || ((info = task->last_info) == NULL)) {
726 		errno = EFAULT;
727 		return TC_ERR;
728 	}
729 
730 	va_start(ap, key);
731 	sz = va_arg(ap, size_t);
732 	if (sz < 1) {
733 		errno = EINVAL;
734 		r = TC_ERR;
735 		goto out;
736 	}
737 
738 	if (_match(key, "device")) {
739 		s = va_arg(ap, char *);
740 		strncpy(s, info->dev, sz);
741 		s[sz-1] = '\0';
742 	} else if (_match(key, "cipher")) {
743 		s = va_arg(ap, char *);
744 		tc_cipher_chain_sprint(buf, sizeof(buf), info->cipher_chain);
745 		strncpy(s, buf, sz);
746 		s[sz-1] = '\0';
747 	} else if (_match(key, "prf")) {
748 		s = va_arg(ap, char *);
749 		if (info->pbkdf_prf)
750 			strncpy(s, info->pbkdf_prf->name, sz);
751 		else
752 			strncpy(s, "(unknown)", sz);
753 		s[sz-1] = '\0';
754 	} else if (_match(key, "key_bits")) {
755 		if (sz != sizeof(int)) {
756 			errno = EFAULT;
757 			r = TC_ERR;
758 			goto out;
759 		}
760 		ip = va_arg(ap, int *);
761 		*ip = 8*tc_cipher_chain_klen(info->cipher_chain);
762 	} else if (_match(key, "size")) {
763 		if (sz != sizeof(int64_t)) {
764 			errno = EFAULT;
765 			r = TC_ERR;
766 			goto out;
767 		}
768 		i64p = va_arg(ap, int64_t *);
769 		if (info->hdr)
770 			*i64p = (int64_t)info->size * (int64_t)info->hdr->sec_sz;
771 		else
772 			*i64p = (int64_t)info->size * (int64_t)info->blk_sz;
773 	} else if (_match(key, "iv_offset")) {
774 		if (sz != sizeof(int64_t)) {
775 			errno = EFAULT;
776 			r = TC_ERR;
777 			goto out;
778 		}
779 		i64p = va_arg(ap, int64_t *);
780 		if (info->hdr)
781 			*i64p = (int64_t)info->skip * (int64_t)info->hdr->sec_sz;
782 		else
783 			*i64p = (int64_t)info->skip * (int64_t)info->blk_sz;
784 	} else if (_match(key, "block_offset")) {
785 		if (sz != sizeof(int64_t)) {
786 			errno = EFAULT;
787 			r = TC_ERR;
788 			goto out;
789 		}
790 		i64p = va_arg(ap, int64_t *);
791 		if (info->hdr)
792 			*i64p = (int64_t)info->offset * (int64_t)info->hdr->sec_sz;
793 		else
794 			*i64p = (int64_t)info->offset * (int64_t)info->blk_sz;
795 	} else {
796 		r = TC_ERR_UNIMPL;
797 	}
798 
799 out:
800 	va_end(ap);
801 
802 	return r;
803 }
804