xref: /dragonfly/lib/libtcplay/tcplay_api.c (revision 7b1e1c8e1e00f6479eba04708b37a13383f7e197)
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