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