tcplay - update to version 0.9
authorAlex Hornung <ahornung@gmail.com>
Tue, 26 Jul 2011 14:13:18 +0000 (15:13 +0100)
committerAlex Hornung <ahornung@gmail.com>
Tue, 26 Jul 2011 14:54:31 +0000 (15:54 +0100)
new stuff of relevance to DragonFly:
 * Backup header is now written on volume creation.

 * custom humanize function that is more flexible in what it understands
   and produces nicer results for tcplay, as well as being portable.

 * support for non-512 byte sector size

 * better memory freeing. Instead of just doing lazy freeing at end of
   API operations or execution, free up as much as possible after each
   individual operation.

 * Bug fix: adjust volume size so backup header is never overwritten.

21 files changed:
lib/libtcplay/Makefile
lib/libtcplay/crc32.c
lib/libtcplay/crc32.h
lib/libtcplay/crypto-dev.c
lib/libtcplay/crypto.c [copied from lib/libtcplay/crypto-dev.c with 63% similarity]
lib/libtcplay/generic_xts.c [new file with mode: 0644]
lib/libtcplay/generic_xts.h [new file with mode: 0644]
lib/libtcplay/hdr.c
lib/libtcplay/humanize.c [new file with mode: 0644]
lib/libtcplay/humanize.h [copied from lib/libtcplay/tcplay_api.h with 60% similarity]
lib/libtcplay/io.c
lib/libtcplay/pbkdf2-openssl.c [copied from lib/libtcplay/tcplay_api.h with 61% similarity]
lib/libtcplay/safe_mem.c
lib/libtcplay/tcplay.3
lib/libtcplay/tcplay.c
lib/libtcplay/tcplay.h
lib/libtcplay/tcplay_api.c
lib/libtcplay/tcplay_api.h
lib/libtcplay/tcplay_api_test.c
sbin/tcplay/Makefile
sbin/tcplay/main.c

index fddccc6..67240b3 100644 (file)
@@ -15,6 +15,7 @@ MLINKS+=      tcplay.3        tc_api_get_summary.3
 
 
 SRCS+=         tcplay_api.c tcplay.c crc32.c safe_mem.c \
+               crypto.c generic_xts.c humanize.c pbkdf2-openssl.c \
                io.c crypto-dev.c hdr.c
 INCS+=         tcplay_api.h
 
index a18468d..ac8f821 100644 (file)
@@ -38,6 +38,7 @@
  */
 
 #include <stdlib.h>
+#include <stdint.h>
 #include "crc32.h"
 
 uint32_t crc32_tab[] = {
index 2761f5d..2ad34ae 100644 (file)
@@ -1,2 +1,2 @@
 uint32_t crc32(const void *buf, size_t size);
-uint32_t crc32_intermediate(uint32_t crc, uint8_t d);
\ No newline at end of file
+uint32_t crc32_intermediate(uint32_t crc, uint8_t d);
index f4c493f..b61fcc9 100644 (file)
@@ -36,9 +36,8 @@
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
-#include <openssl/evp.h>
+#include <stdio.h>
 
-#include "crc32.h"
 #include "tcplay.h"
 
 static
@@ -73,13 +72,40 @@ setallowsoft(int new)
 
 static
 int
-syscrypt(int cipher, unsigned char *key, size_t klen, unsigned char *iv,
+get_cryptodev_cipher_id(struct tc_crypto_algo *cipher)
+{
+       if      (strcmp(cipher->name, "AES-128-XTS") == 0)
+               return CRYPTO_AES_XTS;
+       else if (strcmp(cipher->name, "AES-256-XTS") == 0)
+               return CRYPTO_AES_XTS;
+       else if (strcmp(cipher->name, "TWOFISH-128-XTS") == 0)
+               return CRYPTO_TWOFISH_XTS;
+       else if (strcmp(cipher->name, "TWOFISH-256-XTS") == 0)
+               return CRYPTO_TWOFISH_XTS;
+       else if (strcmp(cipher->name, "SERPENT-128-XTS") == 0)
+               return CRYPTO_SERPENT_XTS;
+       else if (strcmp(cipher->name, "SERPENT-256-XTS") == 0)
+               return CRYPTO_SERPENT_XTS;
+       else
+               return -1;
+}
+
+int
+syscrypt(struct tc_crypto_algo *cipher, unsigned char *key, size_t klen, unsigned char *iv,
     unsigned char *in, unsigned char *out, size_t len, int do_encrypt)
 {
        struct session_op session;
        struct crypt_op cryp;
+       int cipher_id;
        int cryptodev_fd = -1, fd = -1;
 
+       cipher_id = get_cryptodev_cipher_id(cipher);
+       if (cipher_id < 0) {
+               tc_log(1, "Cipher %s not found\n",
+                   cipher->name);
+               return ENOENT;
+       }
+
        if ((cryptodev_fd = open("/dev/crypto", O_RDWR, 0)) < 0) {
                perror("Could not open /dev/crypto");
                goto err;
@@ -89,7 +115,7 @@ syscrypt(int cipher, unsigned char *key, size_t klen, unsigned char *iv,
                goto err;
        }
        memset(&session, 0, sizeof(session));
-       session.cipher = cipher;
+       session.cipher = cipher_id;
        session.key = (caddr_t) key;
        session.keylen = klen;
        if (ioctl(fd, CIOCGSESSION, &session) == -1) {
@@ -125,33 +151,11 @@ err:
        return (-1);
 }
 
-static
-int
-get_cryptodev_cipher_id(struct tc_crypto_algo *cipher)
-{
-       if      (strcmp(cipher->name, "AES-128-XTS") == 0)
-               return CRYPTO_AES_XTS;
-       else if (strcmp(cipher->name, "AES-256-XTS") == 0)
-               return CRYPTO_AES_XTS;
-       else if (strcmp(cipher->name, "TWOFISH-128-XTS") == 0)
-               return CRYPTO_TWOFISH_XTS;
-       else if (strcmp(cipher->name, "TWOFISH-256-XTS") == 0)
-               return CRYPTO_TWOFISH_XTS;
-       else if (strcmp(cipher->name, "SERPENT-128-XTS") == 0)
-               return CRYPTO_SERPENT_XTS;
-       else if (strcmp(cipher->name, "SERPENT-256-XTS") == 0)
-               return CRYPTO_SERPENT_XTS;
-       else
-               return -1;
-}
-
 int
 tc_crypto_init(void)
 {
        int allowed;
 
-       OpenSSL_add_all_algorithms();
-
        allowed = getallowsoft();
        if (allowed == 0)
                setallowsoft(1);
@@ -159,244 +163,3 @@ tc_crypto_init(void)
        return 0;
 }
 
-int
-tc_cipher_chain_populate_keys(struct tc_cipher_chain *cipher_chain,
-    unsigned char *key)
-{
-       int total_key_bytes, used_key_bytes;
-       struct tc_cipher_chain *dummy_chain;
-
-       /*
-        * We need to determine the total key bytes as the key locations
-        * depend on it.
-        */
-       total_key_bytes = 0;
-       for (dummy_chain = cipher_chain;
-           dummy_chain != NULL;
-           dummy_chain = dummy_chain->next) {
-               total_key_bytes += dummy_chain->cipher->klen;
-       }
-
-       /*
-        * Now we need to get prepare the keys, as the keys are in
-        * forward order with respect to the cipher cascade, but
-        * the actual decryption is in reverse cipher cascade order.
-        */
-       used_key_bytes = 0;
-       for (dummy_chain = cipher_chain;
-           dummy_chain != NULL;
-           dummy_chain = dummy_chain->next) {
-               dummy_chain->key = alloc_safe_mem(dummy_chain->cipher->klen);
-               if (dummy_chain->key == NULL) {
-                       tc_log(1, "tc_decrypt: Could not allocate key "
-                           "memory\n");
-                       return ENOMEM;
-               }
-
-               /* XXX: here we assume XTS operation! */
-               memcpy(dummy_chain->key,
-                   key + used_key_bytes/2,
-                   dummy_chain->cipher->klen/2);
-               memcpy(dummy_chain->key + dummy_chain->cipher->klen/2,
-                   key + (total_key_bytes/2) + used_key_bytes/2,
-                   dummy_chain->cipher->klen/2);
-
-               /* Remember how many key bytes we've seen */
-               used_key_bytes += dummy_chain->cipher->klen;
-       }
-
-       return 0;
-}
-
-int
-tc_encrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
-    unsigned char *iv,
-    unsigned char *in, int in_len, unsigned char *out)
-{
-       int cipher_id;
-       int err;
-
-       if ((err = tc_cipher_chain_populate_keys(cipher_chain, key)))
-               return err;
-
-#ifdef DEBUG
-       printf("tc_encrypt: starting chain\n");
-#endif
-
-       /*
-        * Now process the actual decryption, in forward cascade order.
-        */
-       for (;
-           cipher_chain != NULL;
-           cipher_chain = cipher_chain->next) {
-               cipher_id = get_cryptodev_cipher_id(cipher_chain->cipher);
-               if (cipher_id < 0) {
-                       tc_log(1, "Cipher %s not found\n",
-                           cipher_chain->cipher->name);
-                       return ENOENT;
-               }
-
-#ifdef DEBUG
-               printf("tc_encrypt: Currently using cipher %s\n",
-                   cipher_chain->cipher->name);
-#endif
-
-               err = syscrypt(cipher_id, cipher_chain->key,
-                   cipher_chain->cipher->klen, iv, in, out, in_len, 1);
-
-               /* Deallocate this key, since we won't need it anymore */
-               free_safe_mem(cipher_chain->key);
-
-               if (err != 0)
-                       return err;
-
-               /* Set next input buffer as current output buffer */
-               in = out;
-       }
-
-       return 0;
-}
-
-int
-tc_decrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
-    unsigned char *iv,
-    unsigned char *in, int in_len, unsigned char *out)
-{
-       int cipher_id;
-       int err;
-
-       if ((err = tc_cipher_chain_populate_keys(cipher_chain, key)))
-               return err;
-
-#ifdef DEBUG
-       printf("tc_decrypt: starting chain!\n");
-#endif
-
-       /*
-        * Now process the actual decryption, in reverse cascade order; so
-        * first find the last element in the chain.
-        */
-       for (; cipher_chain->next != NULL; cipher_chain = cipher_chain->next)
-               ;
-       for (;
-           cipher_chain != NULL;
-           cipher_chain = cipher_chain->prev) {
-               cipher_id = get_cryptodev_cipher_id(cipher_chain->cipher);
-               if (cipher_id < 0) {
-                       tc_log(1, "Cipher %s not found\n",
-                           cipher_chain->cipher->name);
-                       return ENOENT;
-               }
-
-#ifdef DEBUG
-               printf("tc_decrypt: Currently using cipher %s\n",
-                   cipher_chain->cipher->name);
-#endif
-
-               err = syscrypt(cipher_id, cipher_chain->key,
-                   cipher_chain->cipher->klen, iv, in, out, in_len, 0);
-
-               /* Deallocate this key, since we won't need it anymore */
-               free_safe_mem(cipher_chain->key);
-
-               if (err != 0)
-                       return err;
-
-               /* Set next input buffer as current output buffer */
-               in = out;
-       }
-
-       return 0;
-}
-
-int
-pbkdf2(const char *pass, int passlen, const unsigned char *salt, int saltlen,
-    int iter, const char *hash_name, int keylen, unsigned char *out)
-{
-       const EVP_MD *md;
-       int r;
-
-       md = EVP_get_digestbyname(hash_name);
-       if (md == NULL) {
-               printf("Hash %s not found\n", hash_name);
-               return ENOENT;
-       }
-       r = PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, md,
-           keylen, out);
-
-       if (r == 0) {
-               printf("Error in PBKDF2\n");
-               return EINVAL;
-       }
-
-       return 0;
-}
-
-int
-apply_keyfiles(unsigned char *pass, size_t pass_memsz, const char *keyfiles[],
-    int nkeyfiles)
-{
-       int pl, k;
-       unsigned char *kpool;
-       unsigned char *kdata;
-       int kpool_idx;
-       size_t i, kdata_sz;
-       uint32_t crc;
-
-       if (pass_memsz < MAX_PASSSZ) {
-               tc_log(1, "Not enough memory for password manipluation\n");
-               return ENOMEM;
-       }
-
-       pl = strlen(pass);
-       memset(pass+pl, 0, MAX_PASSSZ-pl);
-
-       if ((kpool = alloc_safe_mem(KPOOL_SZ)) == NULL) {
-               tc_log(1, "Error allocating memory for keyfile pool\n");
-               return ENOMEM;
-       }
-
-       memset(kpool, 0, KPOOL_SZ);
-
-       for (k = 0; k < nkeyfiles; k++) {
-#ifdef DEBUG
-               printf("Loading keyfile %s into kpool\n", keyfiles[k]);
-#endif
-               kpool_idx = 0;
-               crc = ~0U;
-               kdata_sz = MAX_KFILE_SZ;
-
-               if ((kdata = read_to_safe_mem(keyfiles[k], 0, &kdata_sz)) == NULL) {
-                       tc_log(1, "Error reading keyfile %s content\n",
-                           keyfiles[k]);
-                       free_safe_mem(kpool);
-                       return EIO;
-               }
-
-               for (i = 0; i < kdata_sz; i++) {
-                       crc = crc32_intermediate(crc, kdata[i]);
-
-                       kpool[kpool_idx++] += (unsigned char)(crc >> 24);
-                       kpool[kpool_idx++] += (unsigned char)(crc >> 16);
-                       kpool[kpool_idx++] += (unsigned char)(crc >> 8);
-                       kpool[kpool_idx++] += (unsigned char)(crc);
-
-                       /* Wrap around */
-                       if (kpool_idx == KPOOL_SZ)
-                               kpool_idx = 0;
-               }
-
-               free_safe_mem(kdata);
-       }
-
-#ifdef DEBUG
-       printf("Applying kpool to passphrase\n");
-#endif
-       /* Apply keyfile pool to passphrase */
-       for (i = 0; i < KPOOL_SZ; i++)
-               pass[i] += kpool[i];
-
-       free_safe_mem(kpool);
-
-       return 0;
-}
similarity index 63%
copy from lib/libtcplay/crypto-dev.c
copy to lib/libtcplay/crypto.c
index f4c493f..05da5d0 100644 (file)
  */
 #include <sys/types.h>
 #include <sys/param.h>
-#include <sys/ioctl.h>
-#include <sys/sysctl.h>
-#include <crypto/cryptodev.h>
+#include <inttypes.h>
 
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
-#include <openssl/evp.h>
+#include <stdio.h>
 
 #include "crc32.h"
 #include "tcplay.h"
 
-static
-int
-getallowsoft(void)
-{
-       int old;
-       size_t olen;
-
-       olen = sizeof(old);
-
-       if (sysctlbyname("kern.cryptodevallowsoft", &old, &olen, NULL, 0) < 0) {
-               perror("accessing sysctl kern.cryptodevallowsoft failed");
-       }
-
-       return old;
-}
-
-static
-void
-setallowsoft(int new)
-{
-       int old;
-       size_t olen, nlen;
-
-       olen = nlen = sizeof(new);
-
-       if (sysctlbyname("kern.cryptodevallowsoft", &old, &olen, &new, nlen) < 0) {
-               perror("accessing sysctl kern.cryptodevallowsoft failed");
-       }
-}
-
-static
-int
-syscrypt(int cipher, unsigned char *key, size_t klen, unsigned char *iv,
-    unsigned char *in, unsigned char *out, size_t len, int do_encrypt)
-{
-       struct session_op session;
-       struct crypt_op cryp;
-       int cryptodev_fd = -1, fd = -1;
-
-       if ((cryptodev_fd = open("/dev/crypto", O_RDWR, 0)) < 0) {
-               perror("Could not open /dev/crypto");
-               goto err;
-       }
-       if (ioctl(cryptodev_fd, CRIOGET, &fd) == -1) {
-               perror("CRIOGET failed");
-               goto err;
-       }
-       memset(&session, 0, sizeof(session));
-       session.cipher = cipher;
-       session.key = (caddr_t) key;
-       session.keylen = klen;
-       if (ioctl(fd, CIOCGSESSION, &session) == -1) {
-               perror("CIOCGSESSION failed");
-               goto err;
-       }
-       memset(&cryp, 0, sizeof(cryp));
-       cryp.ses = session.ses;
-       cryp.op = do_encrypt ? COP_ENCRYPT : COP_DECRYPT;
-       cryp.flags = 0;
-       cryp.len = len;
-       cryp.src = (caddr_t) in;
-       cryp.dst = (caddr_t) out;
-       cryp.iv = (caddr_t) iv;
-       cryp.mac = 0;
-       if (ioctl(fd, CIOCCRYPT, &cryp) == -1) {
-               perror("CIOCCRYPT failed");
-               goto err;
-       }
-       if (ioctl(fd, CIOCFSESSION, &session.ses) == -1) {
-               perror("CIOCFSESSION failed");
-               goto err;
-       }
-       close(fd);
-       close(cryptodev_fd);
-       return (0);
-
-err:
-       if (fd != -1)
-               close(fd);
-       if (cryptodev_fd != -1)
-               close(cryptodev_fd);
-       return (-1);
-}
-
-static
-int
-get_cryptodev_cipher_id(struct tc_crypto_algo *cipher)
-{
-       if      (strcmp(cipher->name, "AES-128-XTS") == 0)
-               return CRYPTO_AES_XTS;
-       else if (strcmp(cipher->name, "AES-256-XTS") == 0)
-               return CRYPTO_AES_XTS;
-       else if (strcmp(cipher->name, "TWOFISH-128-XTS") == 0)
-               return CRYPTO_TWOFISH_XTS;
-       else if (strcmp(cipher->name, "TWOFISH-256-XTS") == 0)
-               return CRYPTO_TWOFISH_XTS;
-       else if (strcmp(cipher->name, "SERPENT-128-XTS") == 0)
-               return CRYPTO_SERPENT_XTS;
-       else if (strcmp(cipher->name, "SERPENT-256-XTS") == 0)
-               return CRYPTO_SERPENT_XTS;
-       else
-               return -1;
-}
-
-int
-tc_crypto_init(void)
-{
-       int allowed;
-
-       OpenSSL_add_all_algorithms();
-
-       allowed = getallowsoft();
-       if (allowed == 0)
-               setallowsoft(1);
-
-       return 0;
-}
-
 int
 tc_cipher_chain_populate_keys(struct tc_cipher_chain *cipher_chain,
     unsigned char *key)
@@ -209,13 +89,28 @@ tc_cipher_chain_populate_keys(struct tc_cipher_chain *cipher_chain,
 }
 
 int
+tc_cipher_chain_free_keys(struct tc_cipher_chain *cipher_chain)
+{
+       for (; cipher_chain != NULL; cipher_chain = cipher_chain->next) {
+               if (cipher_chain->key != NULL) {
+                       free_safe_mem(cipher_chain->key);
+                       cipher_chain->key = NULL;
+               }
+       }
+
+       return 0;
+}
+
+int
 tc_encrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
     unsigned char *iv,
     unsigned char *in, int in_len, unsigned char *out)
 {
-       int cipher_id;
+       struct tc_cipher_chain *chain_start;
        int err;
 
+       chain_start = cipher_chain;
+
        if ((err = tc_cipher_chain_populate_keys(cipher_chain, key)))
                return err;
 
@@ -229,31 +124,29 @@ tc_encrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
        for (;
            cipher_chain != NULL;
            cipher_chain = cipher_chain->next) {
-               cipher_id = get_cryptodev_cipher_id(cipher_chain->cipher);
-               if (cipher_id < 0) {
-                       tc_log(1, "Cipher %s not found\n",
-                           cipher_chain->cipher->name);
-                       return ENOENT;
-               }
-
 #ifdef DEBUG
                printf("tc_encrypt: Currently using cipher %s\n",
                    cipher_chain->cipher->name);
 #endif
 
-               err = syscrypt(cipher_id, cipher_chain->key,
+               err = syscrypt(cipher_chain->cipher, cipher_chain->key,
                    cipher_chain->cipher->klen, iv, in, out, in_len, 1);
 
                /* Deallocate this key, since we won't need it anymore */
                free_safe_mem(cipher_chain->key);
+               cipher_chain->key = NULL;
 
-               if (err != 0)
+               if (err != 0) {
+                       tc_cipher_chain_free_keys(chain_start);
                        return err;
+               }
 
                /* Set next input buffer as current output buffer */
                in = out;
        }
 
+       tc_cipher_chain_free_keys(chain_start);
+
        return 0;
 }
 
@@ -262,9 +155,11 @@ tc_decrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
     unsigned char *iv,
     unsigned char *in, int in_len, unsigned char *out)
 {
-       int cipher_id;
+       struct tc_cipher_chain *chain_start;
        int err;
 
+       chain_start = cipher_chain;
+
        if ((err = tc_cipher_chain_populate_keys(cipher_chain, key)))
                return err;
 
@@ -281,53 +176,28 @@ tc_decrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
        for (;
            cipher_chain != NULL;
            cipher_chain = cipher_chain->prev) {
-               cipher_id = get_cryptodev_cipher_id(cipher_chain->cipher);
-               if (cipher_id < 0) {
-                       tc_log(1, "Cipher %s not found\n",
-                           cipher_chain->cipher->name);
-                       return ENOENT;
-               }
-
 #ifdef DEBUG
                printf("tc_decrypt: Currently using cipher %s\n",
                    cipher_chain->cipher->name);
 #endif
 
-               err = syscrypt(cipher_id, cipher_chain->key,
+               err = syscrypt(cipher_chain->cipher, cipher_chain->key,
                    cipher_chain->cipher->klen, iv, in, out, in_len, 0);
 
                /* Deallocate this key, since we won't need it anymore */
                free_safe_mem(cipher_chain->key);
+               cipher_chain->key = NULL;
 
-               if (err != 0)
+               if (err != 0) {
+                       tc_cipher_chain_free_keys(chain_start);
                        return err;
+               }
 
                /* Set next input buffer as current output buffer */
                in = out;
        }
 
-       return 0;
-}
-
-int
-pbkdf2(const char *pass, int passlen, const unsigned char *salt, int saltlen,
-    int iter, const char *hash_name, int keylen, unsigned char *out)
-{
-       const EVP_MD *md;
-       int r;
-
-       md = EVP_get_digestbyname(hash_name);
-       if (md == NULL) {
-               printf("Hash %s not found\n", hash_name);
-               return ENOENT;
-       }
-       r = PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, md,
-           keylen, out);
-
-       if (r == 0) {
-               printf("Error in PBKDF2\n");
-               return EINVAL;
-       }
+       tc_cipher_chain_free_keys(chain_start);
 
        return 0;
 }
@@ -348,7 +218,7 @@ apply_keyfiles(unsigned char *pass, size_t pass_memsz, const char *keyfiles[],
                return ENOMEM;
        }
 
-       pl = strlen(pass);
+       pl = strlen((char *)pass);
        memset(pass+pl, 0, MAX_PASSSZ-pl);
 
        if ((kpool = alloc_safe_mem(KPOOL_SZ)) == NULL) {
diff --git a/lib/libtcplay/generic_xts.c b/lib/libtcplay/generic_xts.c
new file mode 100644 (file)
index 0000000..8202040
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2008, Damien Miller
+ * Copyright (C) 2011, Alex Hornung
+ *
+ * Permission to use, copy, and modify this software with or without fee
+ * is hereby granted, provided that this entire notice is included in
+ * all copies of any software which is or includes a copy or
+ * modification of this software.
+ * You may use this code under the GNU public license if you so wish. Please
+ * contribute changes back to the authors under this freer than GPL license
+ * so that we may further the use of strong encryption without limitations to
+ * all.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
+ * PURPOSE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "tcplay.h"
+#include "generic_xts.h"
+
+
+
+static int
+xts_reinit(struct xts_ctx *ctx, u_int64_t blocknum)
+{
+       u_int i;
+
+       /*
+        * Prepare tweak as E_k2(IV). IV is specified as LE representation
+        * of a 64-bit block number which we allow to be passed in directly.
+        */
+       for (i = 0; i < XTS_IVSIZE; i++) {
+               ctx->tweak[i] = blocknum & 0xff;
+               blocknum >>= 8;
+       }
+       /* Last 64 bits of IV are always zero */
+       bzero(ctx->tweak + XTS_IVSIZE, XTS_IVSIZE);
+
+       return ctx->encrypt_fn(ctx->ctx2, ctx->blk_sz, ctx->tweak, ctx->tweak);
+}
+
+static int
+xts_crypt(struct xts_ctx *ctx, u_int8_t *data, u_int do_encrypt)
+{
+       u_int8_t block[XTS_MAX_BLOCKSIZE];
+       u_int i, carry_in, carry_out;
+       int err;
+
+       for (i = 0; i < ctx->blk_sz; i++)
+               block[i] = data[i] ^ ctx->tweak[i];
+
+       if (do_encrypt)
+               err = ctx->encrypt_fn(ctx->ctx1, ctx->blk_sz, block, data);
+       else
+               err = ctx->decrypt_fn(ctx->ctx1, ctx->blk_sz, block, data);
+
+       if (err)
+               goto out;
+
+       for (i = 0; i < ctx->blk_sz; i++)
+               data[i] ^= ctx->tweak[i];
+
+       /* Exponentiate tweak */
+       carry_in = 0;
+       for (i = 0; i < ctx->blk_sz; i++) {
+               carry_out = ctx->tweak[i] & 0x80;
+               ctx->tweak[i] = (ctx->tweak[i] << 1) | (carry_in ? 1 : 0);
+               carry_in = carry_out;
+       }
+       if (carry_in)
+               ctx->tweak[0] ^= XTS_ALPHA;
+
+out:
+       bzero(block, sizeof(block));
+       return err;
+}
+
+int
+xts_init(struct xts_ctx *ctx, void *arg1, void *arg2, set_key_fn _set_key_fn,
+    zero_key_fn _zero_key_fn, encrypt_decrypt_fn _encrypt_fn,
+    encrypt_decrypt_fn _decrypt_fn, u_int blk_sz, u_int8_t *key, int len)
+{
+       int err;
+
+       if (len != 32 && len != 64)
+               return -1;
+
+       ctx->blk_sz = blk_sz;
+       ctx->encrypt_fn = _encrypt_fn;
+       ctx->decrypt_fn = _decrypt_fn;
+       ctx->set_key_fn = _set_key_fn;
+       ctx->zero_key_fn = _zero_key_fn;
+
+       err = ctx->set_key_fn(&ctx->ctx1, arg1, arg2, key, len * 4);
+       if (err)
+               return -1;
+
+       err = ctx->set_key_fn(&ctx->ctx2, arg1, arg2, key + (len / 2),
+           len * 4);
+       if (err) {
+               ctx->zero_key_fn(&ctx->ctx1);
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+xts_encrypt(struct xts_ctx *ctx, u_int8_t *data, size_t len, uint8_t *iv)
+{
+       uint64_t sector = *((uint64_t *)iv);
+       int err;
+
+       if ((len % ctx->blk_sz) != 0)
+               return -1;
+
+       err = xts_reinit(ctx, sector);
+       if (err)
+               return err;
+
+       while (len > 0) {
+               err = xts_crypt(ctx, data, 1);
+               if (err)
+                       return -1;
+
+               data += ctx->blk_sz;
+               len -= ctx->blk_sz;
+       }
+
+       return err;
+}
+
+int
+xts_decrypt(struct xts_ctx *ctx, u_int8_t *data, size_t len, uint8_t *iv)
+{
+       uint64_t sector = *((uint64_t *)iv);
+       int err;
+
+       if ((len % ctx->blk_sz) != 0)
+               return -1;
+
+       err = xts_reinit(ctx, sector);
+       if (err)
+               return err;
+
+       while (len > 0) {
+               err = xts_crypt(ctx, data, 0);
+               if (err)
+                       return -1;
+
+               data += ctx->blk_sz;
+               len -= ctx->blk_sz;
+       }
+
+       return err;
+}
+
+int
+xts_uninit(struct xts_ctx *ctx)
+{
+       ctx->zero_key_fn(&ctx->ctx1);
+       ctx->zero_key_fn(&ctx->ctx2);
+
+       return 0;
+}
+
diff --git a/lib/libtcplay/generic_xts.h b/lib/libtcplay/generic_xts.h
new file mode 100644 (file)
index 0000000..1c31450
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008, Damien Miller
+ * Copyright (C) 2011, Alex Hornung
+ *
+ * Permission to use, copy, and modify this software with or without fee
+ * is hereby granted, provided that this entire notice is included in
+ * all copies of any software which is or includes a copy or
+ * modification of this software.
+ * You may use this code under the GNU public license if you so wish. Please
+ * contribute changes back to the authors under this freer than GPL license
+ * so that we may further the use of strong encryption without limitations to
+ * all.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
+ * PURPOSE.
+ */
+
+#include <inttypes.h>
+#define XTS_MAX_BLOCKSIZE      16
+#define XTS_IVSIZE             8
+#define XTS_ALPHA              0x87    /* GF(2^128) generator polynomial */
+
+typedef int (*encrypt_decrypt_fn)(void *, size_t, const uint8_t *, uint8_t *);
+typedef int (*set_key_fn)(void **, void *, void *, const uint8_t *, int);
+typedef int (*zero_key_fn)(void **);
+
+
+struct xts_ctx {
+       encrypt_decrypt_fn      encrypt_fn;
+       encrypt_decrypt_fn      decrypt_fn;
+       set_key_fn              set_key_fn;
+       zero_key_fn             zero_key_fn;
+
+       void            *ctx1;
+       void            *ctx2;
+       uint8_t         tweak[XTS_MAX_BLOCKSIZE];
+       uint32_t        blk_sz;
+};
+
+int xts_init(struct xts_ctx *ctxp, void *arg1, void *arg2, set_key_fn set_key_fn,
+    zero_key_fn zero_key_fn, encrypt_decrypt_fn encrypt_fn,
+    encrypt_decrypt_fn decrypt_fn, uint32_t blk_sz, uint8_t *key, int len);
+int xts_encrypt(struct xts_ctx *ctx, uint8_t *data, size_t len, uint8_t *iv);
+int xts_decrypt(struct xts_ctx *ctx, uint8_t *data, size_t len, uint8_t *iv);
+int xts_uninit(struct xts_ctx *ctxp);
+
index fab20f2..9fc4c20 100644 (file)
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #include <sys/types.h>
+
+#if defined(__DragonFly__)
 #include <sys/endian.h>
+#elif defined(__linux__)
+#include <endian.h>
+#endif
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <inttypes.h>
 #include <string.h>
 
 #include "crc32.h"
@@ -121,48 +128,79 @@ struct tchdr_enc *
 create_hdr(unsigned char *pass, int passlen, struct pbkdf_prf_algo *prf_algo,
     struct tc_cipher_chain *cipher_chain, size_t sec_sz,
     size_t total_blocks __unused,
-    off_t offset, size_t blocks, int hidden)
+    off_t offset, size_t blocks, int hidden, struct tchdr_enc **backup_hdr)
 {
-       struct tchdr_enc *ehdr;
+       struct tchdr_enc *ehdr, *ehdr_backup;
        struct tchdr_dec *dhdr;
-       unsigned char *key;
+       unsigned char *key, *key_backup;
        unsigned char iv[128];
        int error;
 
+       key = key_backup = NULL;
+       dhdr = NULL;
+       ehdr = ehdr_backup = NULL;
+
+       if (backup_hdr != NULL)
+               *backup_hdr = NULL;
+
        if ((dhdr = (struct tchdr_dec *)alloc_safe_mem(sizeof(*dhdr))) == NULL) {
                tc_log(1, "could not allocate safe dhdr memory\n");
-               return NULL;
+               goto error;
        }
 
        if ((ehdr = (struct tchdr_enc *)alloc_safe_mem(sizeof(*ehdr))) == NULL) {
                tc_log(1, "could not allocate safe ehdr memory\n");
-               return NULL;
+               goto error;
+       }
+
+       if ((ehdr_backup = (struct tchdr_enc *)alloc_safe_mem
+           (sizeof(*ehdr_backup))) == NULL) {
+               tc_log(1, "could not allocate safe ehdr_backup memory\n");
+               goto error;
        }
 
        if ((key = alloc_safe_mem(MAX_KEYSZ)) == NULL) {
                tc_log(1, "could not allocate safe key memory\n");
-               return NULL;
+               goto error;
+       }
+
+       if ((key_backup = alloc_safe_mem(MAX_KEYSZ)) == NULL) {
+               tc_log(1, "could not allocate safe backup key memory\n");
+               goto error;
        }
 
        if ((error = get_random(ehdr->salt, sizeof(ehdr->salt))) != 0) {
                tc_log(1, "could not get salt\n");
-               return NULL;
+               goto error;
+       }
+
+       if ((error = get_random(ehdr_backup->salt, sizeof(ehdr_backup->salt)))
+           != 0) {
+               tc_log(1, "could not get salt for backup header\n");
+               goto error;
        }
 
-       error = pbkdf2(pass, passlen,
+       error = pbkdf2(prf_algo, (char *)pass, passlen,
            ehdr->salt, sizeof(ehdr->salt),
-           prf_algo->iteration_count,
-           prf_algo->name, MAX_KEYSZ, key);
+           MAX_KEYSZ, key);
        if (error) {
                tc_log(1, "could not derive key\n");
-               return NULL;
+               goto error;
+       }
+
+       error = pbkdf2(prf_algo, (char *)pass, passlen,
+           ehdr_backup->salt, sizeof(ehdr_backup->salt),
+           MAX_KEYSZ, key_backup);
+       if (error) {
+               tc_log(1, "could not derive backup key\n");
+               goto error;
        }
 
        memset(dhdr, 0, sizeof(*dhdr));
 
        if ((error = get_random(dhdr->keys, sizeof(dhdr->keys))) != 0) {
                tc_log(1, "could not get key random bits\n");
-               return NULL;
+               goto error;
        }
 
        memcpy(dhdr->tc_str, "TRUE", 4);
@@ -195,10 +233,41 @@ create_hdr(unsigned char *pass, int passlen, struct pbkdf_prf_algo *prf_algo,
            sizeof(struct tchdr_dec), ehdr->enc);
        if (error) {
                tc_log(1, "Header encryption failed\n");
-               free_safe_mem(dhdr);
-               return NULL;
+               goto error;
        }
 
+       memset(iv, 0, sizeof(iv));
+       error = tc_encrypt(cipher_chain, key_backup, iv,
+           (unsigned char *)dhdr,
+           sizeof(struct tchdr_dec), ehdr_backup->enc);
+       if (error) {
+               tc_log(1, "Backup header encryption failed\n");
+               goto error;
+       }
+
+       free_safe_mem(key);
+       free_safe_mem(key_backup);
        free_safe_mem(dhdr);
+
+       if (backup_hdr != NULL)
+               *backup_hdr = ehdr_backup;
+       else
+               free_safe_mem(ehdr_backup);
+
        return ehdr;
+       /* NOT REACHED */
+
+error:
+       if (key)
+               free_safe_mem(key);
+       if (key_backup)
+               free_safe_mem(key_backup);
+       if (dhdr)
+               free_safe_mem(dhdr);
+       if (ehdr)
+               free_safe_mem(ehdr);
+       if (ehdr_backup)
+               free_safe_mem(ehdr_backup);
+
+       return NULL;
 }
diff --git a/lib/libtcplay/humanize.c b/lib/libtcplay/humanize.c
new file mode 100644 (file)
index 0000000..1c716b8
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "humanize.h"
+
+static const char prefixes[] = " KMGTPE";
+int
+_humanize_number(char *buf, size_t bufsz, uint64_t num)
+{
+       const char *prefixp;
+       int ret;
+       uint64_t i, d;
+
+       prefixp = prefixes;
+       i = num;
+       d = 0;
+
+       while ((i > 1024) && (*prefixp != '\0')) {
+               d = (i % 1024)/10;
+               i /= 1024;
+               ++prefixp;
+       }
+
+       if (d > 0)
+               ret = snprintf(buf, bufsz, "%"PRIu64".%"PRIu64" %c",
+                   i, d, *prefixp);
+       else
+               ret = snprintf(buf, bufsz, "%"PRIu64" %c", i, *prefixp);
+
+
+       if ((ret < 0) || ((size_t)ret >= bufsz)) {
+               errno = ENOMEM;
+               return -1;
+       } else {
+               return 0;
+       }
+}
+
+int
+_dehumanize_number(const char *buf, uint64_t *dest)
+{
+       char *endptr;
+       uint64_t n, n_check, d;
+       uint64_t multiplier;
+       size_t len;
+
+       if (*buf == '\0') {
+               errno = EINVAL;
+               return -1;
+       }
+
+       len = strlen(buf);
+       if (tolower(buf[len-1]) == 'b')
+               --len;
+
+       multiplier = 1;
+
+       switch (tolower(buf[len-1])) {
+       case 'y':
+               multiplier *= 1024;
+       case 'z':
+               multiplier *= 1024;
+       case 'e':
+               multiplier *= 1024;
+       case 'p':
+               multiplier *= 1024;
+       case 't':
+               multiplier *= 1024;
+       case 'g':
+               multiplier *= 1024;
+       case 'm':
+               multiplier *= 1024;
+       case 'k':
+               multiplier *= 1024;
+               break;
+       default:
+               /*
+                * only set error if string ends in a character that
+                * is not a valid unit.
+                */
+               if (isalpha(buf[len-1])) {
+                       errno = EINVAL;
+                       return -1;
+               }
+       }
+
+       d = 0;
+       n = n_check = strtoull(buf, &endptr, 10);
+       if (endptr) {
+               if ((*endptr != '.') && (*endptr != '\0') &&
+                   (*endptr != ' ') && (endptr != &buf[len-1])) {
+                       errno = EINVAL;
+                       return -1;
+               }
+
+               if (*endptr == '.') {
+                       d = strtoull(endptr+1, &endptr, 10);
+                       if (endptr && (*endptr != '\0') &&
+                           (*endptr != ' ') &&
+                           (endptr != &buf[len-1])) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+               }
+       }
+
+       if (d != 0) {
+               while (d < 100)
+                       d *= 10;
+
+               while (d > 1000)
+                       d /= 10;
+       }
+
+       d *= (multiplier/1024);
+       n *= multiplier;
+
+       if ((uint64_t)(n/multiplier) != n_check) {
+               errno = ERANGE;
+               return -1;
+       }
+
+       n += d;
+       *dest = n;
+
+       return 0;
+}
+
+#ifdef __TEST_HUMANIZE__
+
+#include <assert.h>
+int main(int argc, char *argv[])
+{
+       char buf[1024];
+       uint64_t n, out;
+       int err;
+
+       if (argc < 3)
+               return -1;
+
+       n = strtoull(argv[1], NULL, 10);
+
+       err = _humanize_number(buf, 1024, n);
+       assert(err == 0);
+
+       err = _dehumanize_number(buf, &out);
+       assert(err == 0);
+
+       printf("Converting: %"PRIu64" => %s => %"PRIu64"\n", n, buf, out);
+
+       err = _dehumanize_number(argv[2], &out);
+       assert (err == 0);
+
+       printf("Converting: %s => %"PRIu64"\n", argv[2], out);
+
+       return 0;
+}
+
+#endif
similarity index 60%
copy from lib/libtcplay/tcplay_api.h
copy to lib/libtcplay/humanize.h
index 3a0fe4a..6a45b1d 100644 (file)
  * SUCH DAMAGE.
  */
 
-#define TC_OK  0
-#define TC_ERR -1
-
-typedef struct tc_api_opts {
-       /* Common fields */
-       char            *tc_device;
-       char            *tc_passphrase;
-       const char      **tc_keyfiles;
-
-       /* Fields for mapping */
-       char            *tc_map_name;
-       int             tc_password_retries;
-       int             tc_interactive_prompt;
-       unsigned long   tc_prompt_timeout;
-
-       /* Fields for creation */
-       char            *tc_cipher;
-       char            *tc_prf_hash;
-       char            *tc_cipher_hidden;
-       char            *tc_prf_hash_hidden;
-       size_t          tc_size_hidden_in_blocks;
-       char            *tc_passphrase_hidden;
-       const char      **tc_keyfiles_hidden;
-} tc_api_opts;
-
-int tc_api_init(int verbose);
-int tc_api_uninit(void);
-int tc_api_create_volume(tc_api_opts *api_opts);
-int tc_api_map_volume(tc_api_opts *api_opts);
-int tc_api_unmap_volume(tc_api_opts *api_opts);
-int tc_api_check_cipher(tc_api_opts *api_opts);
-int tc_api_check_prf_hash(tc_api_opts *api_opts);
-const char *tc_api_get_error_msg(void);
-const char *tc_api_get_summary(void);
+int _humanize_number(char *buf, size_t bufsz, uint64_t num);
+int _dehumanize_number(const char *buf, uint64_t *dest);
 
index 7d96fa5..e43c824 100644 (file)
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #include <sys/types.h>
+#if defined(__DragonFly__)
 #include <sys/diskslice.h>
+#elif defined(__linux__)
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#endif
 #include <sys/uio.h>
 #include <sys/select.h>
 #include <errno.h>
@@ -79,12 +85,27 @@ m_err:
        return NULL;
 }
 
+static size_t get_random_total_bytes = 0;
+static size_t get_random_read_bytes = 0;
+
+static
+void
+get_random_summary(void)
+{
+       float pct_done;
+
+       pct_done = (1.0 * get_random_read_bytes) /
+           (1.0 * get_random_total_bytes) * 100.0;
+       tc_log(0, "Gathering true randomness, %.0f%% done.\n", pct_done);
+}
+
 int
 get_random(unsigned char *buf, size_t len)
 {
        int fd;
        ssize_t r;
        size_t rd = 0;
+       size_t sz;
        struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; /* 10 ms */
 
 
@@ -93,10 +114,22 @@ get_random(unsigned char *buf, size_t len)
                return -1;
        }
 
+       summary_fn = get_random_summary;
+       get_random_total_bytes = len;
+
+       /* Get random data in 16-byte chunks */
+       sz = 16;
        while (rd < len) {
-               if ((r = read(fd, buf+rd, len-rd)) < 0) {
-                       tc_log(1, "Error reading from /dev/random\n");
+               get_random_read_bytes = rd;
+
+               if ((len - rd) < sz)
+                       sz = (len - rd);
+
+               if ((r = read(fd, buf+rd, sz)) < 0) {
+                       tc_log(1, "Error reading from /dev/random(%d): %s\n",
+                           fd, strerror(errno));
                        close(fd);
+                       summary_fn = NULL;
                        return -1;
                }
                rd += r;
@@ -104,6 +137,8 @@ get_random(unsigned char *buf, size_t len)
        }
 
        close(fd);
+       summary_fn = NULL;
+
        return 0;
 }
 
@@ -186,6 +221,7 @@ secure_erase(const char *dev, size_t bytes, size_t blksz)
        return 0;
 }
 
+#if defined(__DragonFly__)
 int
 get_disk_info(const char *dev, size_t *blocks, size_t *bsize)
 {
@@ -210,14 +246,72 @@ get_disk_info(const char *dev, size_t *blocks, size_t *bsize)
        close(fd);
        return 0;
 }
+#elif defined(__linux__)
+int
+get_disk_info(const char *dev, size_t *blocks, size_t *bsize)
+{
+       uint64_t nbytes;
+       int blocksz;
+       int fd;
+
+       if ((fd = open(dev, O_RDONLY)) < 0) {
+               tc_log(1, "Error opening %s\n", dev);
+               return -1;
+       }
+
+       if ((ioctl(fd, BLKSSZGET, &blocksz)) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       if ((ioctl(fd, BLKGETSIZE64, &nbytes)) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       *blocks = (size_t)(nbytes / blocksz);
+       *bsize = (size_t)(blocksz);
+
+       close(fd);
+       return 0;
+}
+#endif
 
 int
-write_mem(const char *dev, off_t offset, size_t blksz __unused, void *mem,
+write_to_disk(const char *dev, off_t offset, size_t blksz, void *mem,
     size_t bytes)
 {
+       unsigned char *mem_buf = NULL;
        ssize_t w;
+       size_t sz;
+       off_t internal_off;
        int fd;
 
+       /* Align to block sizes */
+       internal_off = offset % blksz;
+#ifdef DEBUG
+       printf("offset: %"PRIu64", internal offset: %"PRIu64"\n",
+           (uint64_t)offset, (uint64_t)internal_off);
+#endif
+       offset = (offset/blksz) * blksz;
+
+       if ((internal_off + bytes) > blksz) {
+               tc_log(1, "This should never happen: internal_off + bytes > "
+                   "blksz (write_to_disk)\n");
+               return -1;
+       }
+
+       if ((bytes < blksz) || (internal_off != 0)) {
+               sz = blksz;
+               if ((mem_buf = read_to_safe_mem(dev, offset, &sz)) == NULL) {
+                       tc_log(1, "Error buffering data on "
+                           "write_to_disk(%s)\n", dev);
+                       return -1;
+               }
+
+               memcpy(mem_buf + internal_off, mem, bytes);
+       }
+
        if ((fd = open(dev, O_WRONLY)) < 0) {
                tc_log(1, "Error opening device %s\n", dev);
                return -1;
@@ -229,13 +323,16 @@ write_mem(const char *dev, off_t offset, size_t blksz __unused, void *mem,
                return -1;
        }
 
-       if ((w = write(fd, mem, bytes)) <= 0) {
+       if ((w = write(fd, (mem_buf != NULL) ? mem_buf : mem, bytes)) <= 0) {
                tc_log(1, "Error writing to device %s\n", dev);
                close(fd);
                return -1;
        }
 
        close(fd);
+
+       if (mem_buf != NULL)
+               free_safe_mem(mem_buf);
        return 0;
 }
 
similarity index 61%
copy from lib/libtcplay/tcplay_api.h
copy to lib/libtcplay/pbkdf2-openssl.c
index 3a0fe4a..6fb27e1 100644 (file)
  * SUCH DAMAGE.
  */
 
-#define TC_OK  0
-#define TC_ERR -1
+#include <errno.h>
+#include <openssl/evp.h>
 
-typedef struct tc_api_opts {
-       /* Common fields */
-       char            *tc_device;
-       char            *tc_passphrase;
-       const char      **tc_keyfiles;
+#include "tcplay.h"
 
-       /* Fields for mapping */
-       char            *tc_map_name;
-       int             tc_password_retries;
-       int             tc_interactive_prompt;
-       unsigned long   tc_prompt_timeout;
 
-       /* Fields for creation */
-       char            *tc_cipher;
-       char            *tc_prf_hash;
-       char            *tc_cipher_hidden;
-       char            *tc_prf_hash_hidden;
-       size_t          tc_size_hidden_in_blocks;
-       char            *tc_passphrase_hidden;
-       const char      **tc_keyfiles_hidden;
-} tc_api_opts;
+int
+pbkdf2(struct pbkdf_prf_algo *hash, const char *pass, int passlen,
+    const unsigned char *salt, int saltlen,
+    int keylen, unsigned char *out)
+{
+       const EVP_MD *md;
+       int r;
 
-int tc_api_init(int verbose);
-int tc_api_uninit(void);
-int tc_api_create_volume(tc_api_opts *api_opts);
-int tc_api_map_volume(tc_api_opts *api_opts);
-int tc_api_unmap_volume(tc_api_opts *api_opts);
-int tc_api_check_cipher(tc_api_opts *api_opts);
-int tc_api_check_prf_hash(tc_api_opts *api_opts);
-const char *tc_api_get_error_msg(void);
-const char *tc_api_get_summary(void);
+       OpenSSL_add_all_algorithms();
+
+       md = EVP_get_digestbyname(hash->name);
+       if (md == NULL) {
+               tc_log(1, "Hash %s not found\n", hash->name);
+               return ENOENT;
+       }
+       r = PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen,
+           hash->iteration_count, md, keylen, out);
+
+       if (r == 0) {
+               tc_log(1, "Error in PBKDF2\n");
+               return EINVAL;
+       }
+
+       return 0;
+}
 
index eb557bb..a07f5a9 100644 (file)
@@ -26,6 +26,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #include <sys/types.h>
 #include <sys/mman.h>
 #include <stdlib.h>
index ddea610..ab268c5 100644 (file)
@@ -99,7 +99,7 @@ typedef struct tc_api_opts {
        char            *tc_prf_hash;
        char            *tc_cipher_hidden;
        char            *tc_prf_hash_hidden;
-       size_t          tc_size_hidden_in_blocks;
+       size_t          tc_size_hidden_in_bytes;
        char            *tc_passphrase_hidden;
        const char      **tc_keyfiles_hidden;
 } tc_api_opts;
@@ -145,7 +145,7 @@ and
 .Fa tc_keyfiles
 respectively.
 If
-.Fa tc_size_hidden_in_blocks
+.Fa tc_size_hidden_in_bytes
 is not zero, a hidden volume of the given size will be created, using
 the cipher specified by
 .Fa tc_cipher_hidden
index 0858c92..d83a265 100644 (file)
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include <machine/inttypes.h>
+
+#if defined(__linux__)
+#define _GNU_SOURCE /* for asprintf */
+#endif
+
 #include <sys/types.h>
+
+#if defined(__DragonFly__)
 #include <sys/param.h>
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
+#include <inttypes.h>
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
 #include <err.h>
 #include <time.h>
+#if defined(__linux__)
+#include <libdevmapper.h>
+#include <uuid/uuid.h>
+#elif defined(__DragonFly__)
 #include <libdm.h>
-#include <libutil.h>
+#include <uuid.h>
+#endif
 
 #include "crc32.h"
 #include "tcplay.h"
+#include "humanize.h"
 
 
 /* XXX TODO:
@@ -56,7 +72,7 @@ char tc_internal_log_buffer[LOG_BUFFER_SZ];
 void
 tc_log(int is_err, const char *fmt, ...)
 {
-       __va_list ap;
+       va_list ap;
        FILE *fp;
 
        if (is_err)
@@ -64,14 +80,14 @@ tc_log(int is_err, const char *fmt, ...)
        else
                fp = stdout;
 
-        __va_start(ap, fmt);
+        va_start(ap, fmt);
 
        vsnprintf(tc_internal_log_buffer, LOG_BUFFER_SZ, fmt, ap);
 
-       if (tc_internal_verbose)
-                vfprintf(fp, fmt, ap);
+       va_end(ap);
 
-       __va_end(ap);
+       if (tc_internal_verbose)
+           fprintf(fp, "%s", tc_internal_log_buffer);
 }
 
 /* Supported algorithms */
@@ -214,6 +230,12 @@ print_info(struct tcplay_info *info)
        printf("CRC Key Data:\t\t%#x\n", info->hdr->crc_keys);
        printf("Sector size:\t\t%d\n", info->hdr->sec_sz);
        printf("Volume size:\t\t%zu sectors\n", info->size);
+#if 0
+       /* Don't print this; it's always 0 and is rather confusing */
+       printf("Volume offset:\t\t%"PRIu64"\n", (uint64_t)info->start);
+#endif
+       printf("IV offset:\t\t%"PRIu64"\n", (uint64_t)info->skip);
+       printf("Block offset:\t\t%"PRIu64"\n", (uint64_t)info->offset);
 }
 
 static
@@ -221,10 +243,13 @@ struct tcplay_info *
 new_info(const char *dev, struct tc_cipher_chain *cipher_chain,
     struct pbkdf_prf_algo *prf, struct tchdr_dec *hdr, off_t start)
 {
+       struct tc_cipher_chain *chain_start;
        struct tcplay_info *info;
        int i;
        int error;
 
+       chain_start = cipher_chain;
+
        if ((info = (struct tcplay_info *)alloc_safe_mem(sizeof(*info))) == NULL) {
                tc_log(1, "could not allocate safe info memory\n");
                return NULL;
@@ -252,6 +277,8 @@ new_info(const char *dev, struct tc_cipher_chain *cipher_chain,
                            cipher_chain->key[i]);
        }
 
+       tc_cipher_chain_free_keys(chain_start);
+
        return info;
 }
 
@@ -290,14 +317,14 @@ process_hdr(const char *dev, unsigned char *pass, int passlen,
                printf("Salt: ");
                print_hex(ehdr->salt, 0, sizeof(ehdr->salt));
 #endif
-               error = pbkdf2(pass, passlen,
+               error = pbkdf2(&pbkdf_prf_algos[i], (char *)pass, passlen,
                    ehdr->salt, sizeof(ehdr->salt),
-                   pbkdf_prf_algos[i].iteration_count,
-                   pbkdf_prf_algos[i].name, MAX_KEYSZ, key);
+                   MAX_KEYSZ, key);
 
                if (error) {
                        tc_log(1, "pbkdf failed for algorithm %s\n",
                            pbkdf_prf_algos[i].name);
+                       free_safe_mem(key);
                        return EINVAL;
                }
 
@@ -315,12 +342,13 @@ process_hdr(const char *dev, unsigned char *pass, int passlen,
                        if (dhdr == NULL) {
                                tc_log(1, "hdr decryption failed for cipher "
                                    "chain %d\n", j);
+                               free_safe_mem(key);
                                return EINVAL;
                        }
 
                        if (verify_hdr(dhdr)) {
 #ifdef DEBUG
-                               printf("tc_str: %.4s, tc_ver: %zd, tc_min_ver: %zd, "
+                               printf("tc_str: %.4s, tc_ver: %d, tc_min_ver: %d, "
                                    "crc_keys: %d, sz_vol: %"PRIu64", "
                                    "off_mk_scope: %"PRIu64", sz_mk_scope: %"PRIu64", "
                                    "flags: %d, sec_sz: %d crc_dhdr: %d\n",
@@ -343,10 +371,12 @@ process_hdr(const char *dev, unsigned char *pass, int passlen,
 
        if ((info = new_info(dev, tc_cipher_chains[j-1], &pbkdf_prf_algos[i-1],
            dhdr, 0)) == NULL) {
+               free_safe_mem(dhdr);
                return ENOMEM;
        }
 
        *pinfo = info;
+
        return 0;
 }
 
@@ -355,15 +385,21 @@ create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles
     const char *h_keyfiles[], int n_hkeyfiles, struct pbkdf_prf_algo *prf_algo,
     struct tc_cipher_chain *cipher_chain, struct pbkdf_prf_algo *h_prf_algo,
     struct tc_cipher_chain *h_cipher_chain, char *passphrase,
-    char *h_passphrase, size_t hidden_blocks_in, int interactive)
+    char *h_passphrase, size_t size_hidden_bytes_in, int interactive)
 {
        char *pass, *pass_again;
        char *h_pass = NULL;
        char buf[1024];
        size_t blocks, blksz, hidden_blocks = 0;
-       struct tchdr_enc *ehdr, *hehdr = NULL;
-       int64_t tmp;
-       int error, r;
+       struct tchdr_enc *ehdr, *hehdr;
+       struct tchdr_enc *ehdr_backup, *hehdr_backup;
+       uint64_t tmp;
+       int error, r, ret;
+
+       pass = h_pass = pass_again = NULL;
+       ehdr = hehdr = NULL;
+       ehdr_backup = hehdr_backup = NULL;
+       ret = -1; /* Default to returning error */
 
        if (cipher_chain == NULL)
                cipher_chain = tc_cipher_chains[0];
@@ -379,9 +415,9 @@ create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles
                return -1;
        }
 
-       if (blocks <= MIN_VOL_BLOCKS) {
+       if ((blocks*blksz) <= MIN_VOL_BYTES) {
                tc_log(1, "Cannot create volumes on devices with less "
-                   "than %d blocks/sectors\n", MIN_VOL_BLOCKS);
+                   "than %d bytes\n", MIN_VOL_BYTES);
                return -1;
        }
 
@@ -389,28 +425,29 @@ create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles
                if (((pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) ||
                   ((pass_again = alloc_safe_mem(MAX_PASSSZ)) == NULL)) {
                        tc_log(1, "could not allocate safe passphrase memory\n");
-                       return -1;
+                       goto out;
                }
 
                if ((error = read_passphrase("Passphrase: ", pass, MAX_PASSSZ, 0) ||
                   (read_passphrase("Repeat passphrase: ", pass_again,
                   MAX_PASSSZ, 0)))) {
                        tc_log(1, "could not read passphrase\n");
-                       return -1;
+                       goto out;
                }
 
                if (strcmp(pass, pass_again) != 0) {
                        tc_log(1, "Passphrases don't match\n");
-                       return -1;
+                       goto out;
                }
 
                free_safe_mem(pass_again);
+               pass_again = NULL;
        } else {
                /* In batch mode, use provided passphrase */
                if ((pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) {
                        tc_log(1, "could not allocate safe "
                            "passphrase memory");
-                       return -1;
+                       goto out;
                }
 
                if (passphrase != NULL)
@@ -419,9 +456,10 @@ create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles
 
        if (nkeyfiles > 0) {
                /* Apply keyfiles to 'pass' */
-               if ((error = apply_keyfiles(pass, MAX_PASSSZ, keyfiles,
-                   nkeyfiles))) {
+               if ((error = apply_keyfiles((unsigned char *)pass, MAX_PASSSZ,
+                   keyfiles, nkeyfiles))) {
                        tc_log(1, "could not apply keyfiles\n");
+                       goto out;
                }
        }
 
@@ -431,7 +469,7 @@ create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles
                           ((pass_again = alloc_safe_mem(MAX_PASSSZ)) == NULL)) {
                                tc_log(1, "could not allocate safe "
                                    "passphrase memory\n");
-                               return -1;
+                               goto out;
                        }
 
                        if ((error = read_passphrase("Passphrase for hidden volume: ",
@@ -439,22 +477,23 @@ create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles
                           (read_passphrase("Repeat passphrase: ", pass_again,
                           MAX_PASSSZ, 0)))) {
                                tc_log(1, "could not read passphrase\n");
-                               return -1;
+                               goto out;
                        }
 
                        if (strcmp(h_pass, pass_again) != 0) {
                                tc_log(1, "Passphrases for hidden volume don't "
                                    "match\n");
-                               return -1;
+                               goto out;
                        }
 
                        free_safe_mem(pass_again);
+                       pass_again = NULL;
                } else {
                        /* In batch mode, use provided passphrase */
                        if ((h_pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) {
                                tc_log(1, "could not allocate safe "
                                    "passphrase memory");
-                               return -1;
+                               goto out;
                        }
 
                        if (h_passphrase != NULL)
@@ -463,57 +502,65 @@ create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles
 
                if (n_hkeyfiles > 0) {
                        /* Apply keyfiles to 'h_pass' */
-                       if ((error = apply_keyfiles(h_pass, MAX_PASSSZ, h_keyfiles,
-                       n_hkeyfiles))) {
+                       if ((error = apply_keyfiles((unsigned char *)h_pass,
+                           MAX_PASSSZ, h_keyfiles, n_hkeyfiles))) {
                                tc_log(1, "could not apply keyfiles\n");
-                               return -1;
+                               goto out;
                        }
                }
 
                if (interactive) {
                        hidden_blocks = 0;
                } else {
-                       hidden_blocks = hidden_blocks_in;
+                       hidden_blocks = size_hidden_bytes_in/blksz;
                        if (hidden_blocks == 0) {
                                tc_log(1, "hidden_blocks to create volume "
                                    "cannot be zero!\n");
-                               return -1;
+                               goto out;
+                       }
+
+                       if (size_hidden_bytes_in >=
+                           (blocks*blksz) - MIN_VOL_BYTES) {
+                               tc_log(1, "Hidden volume needs to be "
+                                   "smaller than the outer volume\n");
+                               goto out;
                        }
                }
 
                /* This only happens in interactive mode */
                while (hidden_blocks == 0) {
-                       if ((r = humanize_number(buf, strlen("XXX MB"),
-                           (int64_t)(blocks * blksz), "B", 0, 0)) < 0) {
+                       if ((r = _humanize_number(buf, sizeof(buf),
+                           (uint64_t)(blocks * blksz))) < 0) {
                                sprintf(buf, "%zu bytes", (blocks * blksz));
                        }
 
                        printf("The total volume size of %s is %s (bytes)\n", dev, buf);
                        memset(buf, 0, sizeof(buf));
-                       printf("Size of hidden volume (e.g. 127M): ");
+                       printf("Size of hidden volume (e.g. 127M):  ");
                        fflush(stdout);
 
                        if ((fgets(buf, sizeof(buf), stdin)) == NULL) {
                                tc_log(1, "Could not read from stdin\n");
-                               return -1;
+                               goto out;
                        }
 
                        /* get rid of trailing newline */
                        buf[strlen(buf)-1] = '\0';
-                       if ((error = dehumanize_number(buf,
+                       if ((error = _dehumanize_number(buf,
                            &tmp)) != 0) {
                                tc_log(1, "Could not interpret input: %s\n", buf);
-                               return -1;
+                               continue;
                        }
 
-                       hidden_blocks = (size_t)tmp;
-                       hidden_blocks /= blksz;
-                       if (hidden_blocks >= blocks - MIN_VOL_BLOCKS) {
+                       if (tmp >= (blocks*blksz) - MIN_VOL_BYTES) {
                                tc_log(1, "Hidden volume needs to be "
                                    "smaller than the outer volume\n");
                                hidden_blocks = 0;
                                continue;
                        }
+
+                       hidden_blocks = (size_t)tmp;
+                       hidden_blocks /= blksz;
                }
        }
 
@@ -532,56 +579,105 @@ create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles
                fflush(stdout);
                if ((fgets(buf, sizeof(buf), stdin)) == NULL) {
                        tc_log(1, "Could not read from stdin\n");
-                       return -1;
+                       goto out;
                }
 
                if ((buf[0] != 'y') && (buf[0] != 'Y')) {
                        tc_log(1, "User cancelled action(s)\n");
-                       return -1;
+                       goto out;
                }
        }
 
+       tc_log(0, "Securely erasing the volume...\nThis process may take "
+           "some time depending on the size of the volume\n");
+
        /* erase volume */
        if ((error = secure_erase(dev, blocks * blksz, blksz)) != 0) {
                tc_log(1, "could not securely erase device %s\n", dev);
-               return -1;
+               goto out;
        }
 
+       tc_log(0, "Creating volume headers...\nDepending on your system, this "
+           "process may take a few minutes as it uses true random data which "
+           "might take a while to refill\n");
+
        /* create encrypted headers */
-       ehdr = create_hdr(pass, (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass),
-           prf_algo, cipher_chain, blksz, blocks, MIN_VOL_BLOCKS,
-           blocks-MIN_VOL_BLOCKS, 0);
+       ehdr = create_hdr((unsigned char *)pass,
+           (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass),
+           prf_algo, cipher_chain, blksz, blocks, VOL_RSVD_BYTES_START/blksz,
+           blocks - (MIN_VOL_BYTES/blksz), 0, &ehdr_backup);
        if (ehdr == NULL) {
                tc_log(1, "Could not create header\n");
-               return -1;
+               goto out;
        }
 
        if (hidden) {
-               hehdr = create_hdr(h_pass,
+               hehdr = create_hdr((unsigned char *)h_pass,
                    (n_hkeyfiles > 0)?MAX_PASSSZ:strlen(h_pass), h_prf_algo,
                    h_cipher_chain,
-                   blksz, blocks, blocks - hidden_blocks, hidden_blocks, 1);
+                   blksz, blocks,
+                   blocks - (VOL_RSVD_BYTES_END/blksz) - hidden_blocks,
+                   hidden_blocks, 1, &hehdr_backup);
                if (hehdr == NULL) {
                        tc_log(1, "Could not create hidden volume header\n");
-                       return -1;
+                       goto out;
                }
        }
 
-       if ((error = write_mem(dev, 0, blksz, ehdr, sizeof(*ehdr))) != 0) {
+       tc_log(0, "Writing volume headers to disk...\n");
+
+       if ((error = write_to_disk(dev, 0, blksz, ehdr, sizeof(*ehdr))) != 0) {
                tc_log(1, "Could not write volume header to device\n");
-               return -1;
+               goto out;
+       }
+
+       /* Write backup header; it's offset is relative to the end */
+       if ((error = write_to_disk(dev, (blocks*blksz - BACKUP_HDR_OFFSET_END),
+           blksz, ehdr_backup, sizeof(*ehdr_backup))) != 0) {
+               tc_log(1, "Could not write backup volume header to device\n");
+               goto out;
        }
 
        if (hidden) {
-               if ((error = write_mem(dev, HDR_OFFSET_HIDDEN, blksz, hehdr,
+               if ((error = write_to_disk(dev, HDR_OFFSET_HIDDEN, blksz, hehdr,
                    sizeof(*hehdr))) != 0) {
                        tc_log(1, "Could not write hidden volume header to "
                            "device\n");
-                       return -1;
+                       goto out;
+               }
+
+               /* Write backup hidden header; offset is relative to end */
+               if ((error = write_to_disk(dev,
+                   (blocks*blksz - BACKUP_HDR_HIDDEN_OFFSET_END), blksz,
+                   hehdr_backup, sizeof(*hehdr_backup))) != 0) {
+                       tc_log(1, "Could not write backup hidden volume "
+                           "header to device\n");
+                       goto out;
                }
        }
 
-       return 0;
+       /* Everything went ok */
+       tc_log(0, "All done!\n");
+
+       ret = 0;
+
+out:
+       if (pass)
+               free_safe_mem(pass);
+       if (h_pass)
+               free_safe_mem(h_pass);
+       if (pass_again)
+               free_safe_mem(pass_again);
+       if (ehdr)
+               free_safe_mem(ehdr);
+       if (hehdr)
+               free_safe_mem(hehdr);
+       if (ehdr_backup)
+               free_safe_mem(ehdr_backup);
+       if (hehdr_backup)
+               free_safe_mem(hehdr_backup);
+
+       return ret;
 }
 
 static
@@ -597,27 +693,38 @@ info_map_common(const char *dev, int sflag, const char *sys_dev,
        char *h_pass;
        int error, error2 = 0;
        size_t sz;
+       size_t blocks, blksz;
+
+       if ((error = get_disk_info(dev, &blocks, &blksz)) != 0) {
+               tc_log(1, "could not get disk information\n");
+               return NULL;
+       }
 
-       info = NULL;
        if (retries < 1)
                retries = 1;
 
+       info = NULL;
+
+       ehdr = NULL;
+       pass = h_pass = NULL;
+
        while ((info == NULL) && retries-- > 0)
        {
-               h_pass = NULL;
-               ehdr = NULL;
-               hehdr = NULL;
+               pass = h_pass = NULL;
+               ehdr = hehdr = NULL;
+               info = hinfo = NULL;
 
                if ((pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) {
                        tc_log(1, "could not allocate safe passphrase memory\n");
-                       return NULL;
+                       goto out;
                }
 
                if (interactive) {
                        if ((error = read_passphrase("Passphrase: ", pass,
                            MAX_PASSSZ, timeout))) {
                                tc_log(1, "could not read passphrase\n");
-                               return NULL;
+                               /* XXX: handle timeout differently? */
+                               goto out;
                        }
                } else {
                        /* In batch mode, use provided passphrase */
@@ -627,17 +734,17 @@ info_map_common(const char *dev, int sflag, const char *sys_dev,
 
                if (nkeyfiles > 0) {
                        /* Apply keyfiles to 'pass' */
-                       if ((error = apply_keyfiles(pass, MAX_PASSSZ, keyfiles,
-                           nkeyfiles))) {
+                       if ((error = apply_keyfiles((unsigned char *)pass, MAX_PASSSZ,
+                           keyfiles, nkeyfiles))) {
                                tc_log(1, "could not apply keyfiles");
-                               return NULL;
+                               goto out;
                        }
                }
 
                if (protect_hidden) {
                        if ((h_pass = alloc_safe_mem(MAX_PASSSZ)) == NULL) {
                                tc_log(1, "could not allocate safe passphrase memory\n");
-                               return NULL;
+                               goto out;
                        }
 
                        if (interactive) {
@@ -645,7 +752,7 @@ info_map_common(const char *dev, int sflag, const char *sys_dev,
                                    "Passphrase for hidden volume: ", h_pass,
                                    MAX_PASSSZ, timeout))) {
                                        tc_log(1, "could not read passphrase\n");
-                                       return NULL;
+                                       goto out;
                                }
                        } else {
                                /* In batch mode, use provided passphrase */
@@ -655,35 +762,40 @@ info_map_common(const char *dev, int sflag, const char *sys_dev,
 
                        if (n_hkeyfiles > 0) {
                                /* Apply keyfiles to 'pass' */
-                               if ((error = apply_keyfiles(h_pass, MAX_PASSSZ, h_keyfiles,
-                                   n_hkeyfiles))) {
+                               if ((error = apply_keyfiles((unsigned char *)h_pass, MAX_PASSSZ,
+                                   h_keyfiles, n_hkeyfiles))) {
                                        tc_log(1, "could not apply keyfiles");
-                                       return NULL;
+                                       goto out;
                                }
                        }
                }
 
-               sz = HDRSZ;
+               /* Always read blksz-sized chunks */
+               sz = blksz;
+
                ehdr = (struct tchdr_enc *)read_to_safe_mem((sflag) ? sys_dev : dev,
                    (sflag) ? HDR_OFFSET_SYS : 0, &sz);
                if (ehdr == NULL) {
                        tc_log(1, "error read hdr_enc: %s", dev);
-                       return NULL;
+                       goto out;
                }
 
                if (!sflag) {
-                       sz = HDRSZ;
+                       /* Always read blksz-sized chunks */
+                       sz = blksz;
+
                        hehdr = (struct tchdr_enc *)read_to_safe_mem(dev,
                            HDR_OFFSET_HIDDEN, &sz);
                        if (hehdr == NULL) {
                                tc_log(1, "error read hdr_enc: %s", dev);
-                               return NULL;
+                               goto out;
                        }
                } else {
                        hehdr = NULL;
                }
 
-               error = process_hdr(dev, pass, (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass),
+               error = process_hdr(dev, (unsigned char *)pass,
+                   (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass),
                    ehdr, &info);
 
                /*
@@ -693,11 +805,11 @@ info_map_common(const char *dev, int sflag, const char *sys_dev,
                 */
                if (hehdr && (error || protect_hidden)) {
                        if (error) {
-                               error2 = process_hdr(dev, pass,
+                               error2 = process_hdr(dev, (unsigned char *)pass,
                                    (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass), hehdr,
                                    &info);
                        } else if (protect_hidden) {
-                               error2 = process_hdr(dev, h_pass,
+                               error2 = process_hdr(dev, (unsigned char *)h_pass,
                                    (n_hkeyfiles > 0)?MAX_PASSSZ:strlen(h_pass), hehdr,
                                    &hinfo);
                        }
@@ -707,28 +819,75 @@ info_map_common(const char *dev, int sflag, const char *sys_dev,
                if ((protect_hidden && (error || error2)) ||
                    (error && error2)) {
                        tc_log(1, "Incorrect password or not a TrueCrypt volume\n");
-                       info = NULL;
-                       hinfo = NULL;
+
+                       if (info) {
+                               if (info->hdr)
+                                       free_safe_mem(info->hdr);
+                               free_safe_mem(info);
+                               info = NULL;
+                       }
+                       if (hinfo) {
+                               if (hinfo->hdr)
+                                       free_safe_mem(hinfo->hdr);
+                               free_safe_mem(hinfo);
+                               hinfo = NULL;
+                       }
 
                        /* Try again (or finish) */
                        free_safe_mem(pass);
-                       if (h_pass)
+                       pass = NULL;
+
+                       if (h_pass) {
                                free_safe_mem(h_pass);
-                       if (ehdr)
+                               h_pass = NULL;
+                       }
+                       if (ehdr) {
                                free_safe_mem(ehdr);
-                       if (hehdr)
+                               ehdr = NULL;
+                       }
+                       if (hehdr) {
                                free_safe_mem(hehdr);
+                               hehdr = NULL;
+                       }
                        continue;
                }
 
                if (protect_hidden) {
                        if (adjust_info(info, hinfo) != 0) {
                                tc_log(1, "Could not protect hidden volume\n");
-                               return NULL;
+                               if (info) {
+                                       if (info->hdr)
+                                               free_safe_mem(info->hdr);
+                                       free_safe_mem(info);
+                               }
+                               info = NULL;
+
+                               if (hinfo->hdr)
+                                       free_safe_mem(hinfo->hdr);
+                               free_safe_mem(hinfo);
+                               hinfo = NULL;
+                               goto out;
                        }
+
+                       if (hinfo->hdr)
+                               free_safe_mem(hinfo->hdr);
+                       free_safe_mem(hinfo);
+                       hinfo = NULL;
                }
         }
 
+out:
+       if (hinfo)
+               free_safe_mem(hinfo);
+       if (pass)
+               free_safe_mem(pass);
+       if (h_pass)
+               free_safe_mem(h_pass);
+       if (ehdr)
+               free_safe_mem(ehdr);
+       if (hehdr)
+               free_safe_mem(hehdr);
+
        return info;
 }
 
@@ -748,9 +907,15 @@ info_volume(const char *device, int sflag, const char *sys_dev,
        if (info != NULL) {
                if (interactive)
                        print_info(info);
+               if (info->hdr)
+                       free_safe_mem(info->hdr);
+               free_safe_mem(info);
+
+               return 0;
+               /* NOT REACHED */
        }
 
-       return (info != NULL) ? 0 : -1;
+       return -1;
 }
 
 int
@@ -773,11 +938,16 @@ map_volume(const char *map_name, const char *device, int sflag,
 
        if ((error = dm_setup(map_name, info)) != 0) {
                tc_log(1, "Could not set up mapping %s\n", map_name);
+               if (info->hdr)
+                       free_safe_mem(info->hdr);
+               free_safe_mem(info);
                return -1;
        }
 
        if (interactive)
-               printf("All ok!");
+               printf("All ok!\n");
+
+       free_safe_mem(info);
 
        return 0;
 }
@@ -816,12 +986,17 @@ dm_setup(const char *mapname, struct tcplay_info *info)
        char *uu;
        char *uu_stack[64];
        int uu_stack_idx;
+#if defined(__DragonFly__)
        uint32_t status;
-       int ret = 0;
+#endif
+       int r, ret = 0;
        int j;
        off_t start, offset;
        char dev[PATH_MAX];
        char map[PATH_MAX];
+       uint32_t cookie;
+
+       dm_udev_set_sync_support(1);
 
        if ((params = alloc_safe_mem(512)) == NULL) {
                tc_log(1, "could not allocate safe parameters memory");
@@ -840,11 +1015,14 @@ dm_setup(const char *mapname, struct tcplay_info *info)
 
        for (j= 0; cipher_chain != NULL;
            cipher_chain = cipher_chain->prev, j++) {
+
+               cookie = 0;
+
                /* aes-cbc-essiv:sha256 7997f8af... 0 /dev/ad0s0a 8 */
                /*                         iv off---^  block off--^ */
                snprintf(params, 512, "%s %s %"PRIu64 " %s %"PRIu64,
                    cipher_chain->cipher->dm_crypt_str, cipher_chain->dm_key,
-                   info->skip, dev, offset);
+                   (uint64_t)info->skip, dev, (uint64_t)offset);
 #ifdef DEBUG
                printf("Params: %s\n", params);
 #endif
@@ -870,6 +1048,15 @@ dm_setup(const char *mapname, struct tcplay_info *info)
                        goto out;
                }
 
+#if defined(__linux__)
+               uuid_generate(info->uuid);
+               if ((uu = malloc(1024)) == NULL) {
+                       tc_log(1, "uuid_unparse memory failed\n");
+                       ret = -1;
+                       goto out;
+               }
+               uuid_unparse(info->uuid, uu);
+#elif defined(__DragonFly__)
                uuid_create(&info->uuid, &status);
                if (status != uuid_s_ok) {
                        tc_log(1, "uuid_create failed\n");
@@ -883,6 +1070,7 @@ dm_setup(const char *mapname, struct tcplay_info *info)
                        ret = -1;
                        goto out;
                }
+#endif
 
                if ((dm_task_set_uuid(dmt, uu)) == 0) {
                        free(uu);
@@ -899,19 +1087,28 @@ dm_setup(const char *mapname, struct tcplay_info *info)
                        goto out;
                }
 
+               if ((dm_task_set_cookie(dmt, &cookie, 0)) == 0) {
+                       tc_log(1, "dm_task_set_cookie failed\n");
+                       ret = -1;
+                       goto out;
+               }
+
                if ((dm_task_run(dmt)) == 0) {
+                       dm_udev_wait(cookie);
                        tc_log(1, "dm_task_task_run failed\n");
                        ret = -1;
                        goto out;
                }
 
                if ((dm_task_get_info(dmt, &dmi)) == 0) {
+                       dm_udev_wait(cookie);
                        tc_log(1, "dm_task_get info failed\n");
-                       /* XXX: probably do more than just erroring out... */
                        ret = -1;
                        goto out;
                }
 
+               dm_udev_wait(cookie);
+
                asprintf(&uu_stack[uu_stack_idx++], "%s", map);
 
                offset = 0;
@@ -919,6 +1116,7 @@ dm_setup(const char *mapname, struct tcplay_info *info)
                sprintf(dev, "/dev/mapper/%s.%d", mapname, j);
 
                dm_task_destroy(dmt);
+               dm_task_update_nodes();
        }
 
 out:
@@ -933,7 +1131,7 @@ out:
                        printf("Unrolling dm changes! j = %d (%s)\n", j-1,
                            uu_stack[j-1]);
 #endif
-                       if ((ret = dm_remove_device(uu_stack[--j])) != 0) {
+                       if ((r = dm_remove_device(uu_stack[--j])) != 0) {
                                tc_log(1, "Tried to unroll dm changes, "
                                    "giving up.\n");
                                break;
index 4cebb0e..ce7cb2d 100644 (file)
@@ -29,7 +29,7 @@
 
 /* Version of tcplay */
 #define MAJ_VER                        0
-#define MIN_VER                        8
+#define MIN_VER                        9
 
 
 #define MAX_BLKSZ              4096
 #define MAX_KFILE_SZ           1048576 /* 1 MB */
 #define MAX_KEYFILES           256
 #define HDR_OFFSET_HIDDEN      65536
+#define BACKUP_HDR_HIDDEN_OFFSET_END   65536
+#define BACKUP_HDR_OFFSET_END  131072
 #define SALT_LEN               64
-#define MIN_VOL_BLOCKS         256
+#define VOL_RSVD_BYTES_START   (256*512) /* Reserved bytes at vol. start */
+#define VOL_RSVD_BYTES_END     (256*512) /* Reserved bytes at vol. end */
+#define MIN_VOL_BYTES          (VOL_RSVD_BYTES_START + VOL_RSVD_BYTES_END)
+
 #define MAX_CIPHER_CHAINS      64
 #define DEFAULT_RETRIES                3
 #define ERASE_BUFFER_SIZE      4*1024*1024 /* 4 MB */
 #define DEBUG 1
 #endif
 
+#include <inttypes.h>
+
+#if defined(__DragonFly__)
 #include <uuid.h>
+#elif defined(__linux__)
+#include <uuid/uuid.h>
+#endif
 
 struct pbkdf_prf_algo {
        const char *name;
@@ -129,28 +140,38 @@ void *read_to_safe_mem(const char *file, off_t offset, size_t *sz);
 int get_random(unsigned char *buf, size_t len);
 int secure_erase(const char *dev, size_t bytes, size_t blksz);
 int get_disk_info(const char *dev, size_t *blocks, size_t *bsize);
-int write_mem(const char *dev, off_t offset, size_t blksz, void *mem, size_t bytes);
+int write_to_disk(const char *dev, off_t offset, size_t blksz, void *mem,
+    size_t bytes);
 int read_passphrase(const char *prompt, char *pass, size_t passlen,
     time_t timeout);
 
 int tc_crypto_init(void);
 int tc_cipher_chain_populate_keys(struct tc_cipher_chain *cipher_chain,
     unsigned char *key);
+int tc_cipher_chain_free_keys(struct tc_cipher_chain *cipher_chain);
 int tc_encrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
     unsigned char *iv,
     unsigned char *in, int in_len, unsigned char *out);
 int tc_decrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key,
     unsigned char *iv,
     unsigned char *in, int in_len, unsigned char *out);
-int pbkdf2(const char *pass, int passlen, const unsigned char *salt, int saltlen,
-    int iter, const char *hash_name, int keylen, unsigned char *out);
+
+/* The following two are platform dependent */
+int syscrypt(struct tc_crypto_algo *cipher, unsigned char *key, size_t klen,
+    unsigned char *iv, unsigned char *in, unsigned char *out, size_t len,
+    int do_encrypt);
+int pbkdf2(struct pbkdf_prf_algo *hash, const char *pass, int passlen,
+    const unsigned char *salt, int saltlen,
+    int keylen, unsigned char *out);
+
 int apply_keyfiles(unsigned char *pass, size_t pass_memsz, const char *keyfiles[],
     int nkeyfiles);
 
 struct tchdr_enc *create_hdr(unsigned char *pass, int passlen,
     struct pbkdf_prf_algo *prf_algo, struct tc_cipher_chain *cipher_chain,
     size_t sec_sz, size_t total_blocks,
-    off_t offset, size_t blocks, int hidden);
+    off_t offset, size_t blocks, int hidden,
+    struct tchdr_enc **backup_hdr);
 struct tchdr_dec *decrypt_hdr(struct tchdr_enc *ehdr,
     struct tc_cipher_chain *cipher_chain, unsigned char *key);
 int verify_hdr(struct tchdr_dec *hdr);
@@ -173,7 +194,7 @@ int create_volume(const char *dev, int hidden, const char *keyfiles[],
     int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles,
     struct pbkdf_prf_algo *prf_algo, struct tc_cipher_chain *cipher_chain,
     struct pbkdf_prf_algo *h_prf_algo, struct tc_cipher_chain *h_cipher_chain,
-    char *passphrase, char *h_passphrase, size_t hidden_blocks_in,
+    char *passphrase, char *h_passphrase, size_t hidden_bytes_in,
     int interactive);
 int info_volume(const char *device, int sflag, const char *sys_dev,
     int protect_hidden, const char *keyfiles[], int nkeyfiles,
@@ -199,3 +220,5 @@ extern summary_fn_t summary_fn;
 
 #define free_safe_mem(x) \
        _free_safe_mem(x, __FILE__, __LINE__)
+
+#define __unused       __attribute__((__unused__))
index 79f0ea1..c83c295 100644 (file)
@@ -97,7 +97,7 @@ tc_api_create_volume(tc_api_opts *api_opts)
 
        create_hidden = 0;
 
-       if (api_opts->tc_size_hidden_in_blocks > 0) {
+       if (api_opts->tc_size_hidden_in_bytes > 0) {
                create_hidden = 1;
                for (n_hkeyfiles = 0; (n_hkeyfiles < MAX_KEYFILES) &&
                    (api_opts->tc_keyfiles_hidden != NULL) &&
@@ -114,7 +114,7 @@ tc_api_create_volume(tc_api_opts *api_opts)
            check_prf_algo(api_opts->tc_prf_hash_hidden, 1),
            check_cipher_chain(api_opts->tc_cipher_hidden, 1),
            api_opts->tc_passphrase, api_opts->tc_passphrase_hidden,
-           api_opts->tc_size_hidden_in_blocks, 0 /* non-interactive */);
+           api_opts->tc_size_hidden_in_bytes, 0 /* non-interactive */);
 
        return (err) ? TC_ERR : TC_OK;
 }
index 3a0fe4a..4743643 100644 (file)
@@ -47,7 +47,7 @@ typedef struct tc_api_opts {
        char            *tc_prf_hash;
        char            *tc_cipher_hidden;
        char            *tc_prf_hash_hidden;
-       size_t          tc_size_hidden_in_blocks;
+       size_t          tc_size_hidden_in_bytes;
        char            *tc_passphrase_hidden;
        const char      **tc_keyfiles_hidden;
 } tc_api_opts;
index a8776ea..3be7ae6 100644 (file)
@@ -19,7 +19,7 @@ main(void)
        api_opts.tc_passphrase = "apitest2";
        api_opts.tc_keyfiles = NULL;
        api_opts.tc_keyfiles_hidden = NULL;
-       api_opts.tc_size_hidden_in_blocks = 12000;
+       api_opts.tc_size_hidden_in_bytes = 12000*512;
        api_opts.tc_passphrase_hidden = "apihidden";
        api_opts.tc_cipher = "AES-256-XTS,TWOFISH-256-XTS,SERPENT-256-XTS";
        api_opts.tc_cipher_hidden = "SERPENT-256-XTS,TWOFISH-256-XTS";
index e1f8904..de81a62 100644 (file)
@@ -6,6 +6,7 @@ WARNS?= 6
 
 SRCS=  main.c
 SRCS+= tcplay.c crc32.c safe_mem.c io.c crypto-dev.c hdr.c
+SRCS+= crypto.c generic_xts.c humanize.c pbkdf2-openssl.c
 
 LDADD= -lcrypto -ldm -lprop -lutil
 DPADD= ${LIBCRYPTO} ${LIBDM} ${LIBPROP} ${LIBUTIL}
index 024f62d..5077b14 100644 (file)
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #include <sys/types.h>
 #include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
+#include <inttypes.h>
 #include <errno.h>
 #include <string.h>
 #include <signal.h>
-#include <err.h>
 #include <time.h>
-#include <libutil.h>
 
 #include "tcplay.h"
 
+#ifndef SIGINFO
+#define SIGINFO SIGUSR1
+#endif
+
 static
 void
 sig_handler(int sig)