Whamcloud - gitweb
Add support for a password salt stored in the superblock
authorTheodore Ts'o <tytso@mit.edu>
Sun, 29 Mar 2015 00:15:02 +0000 (20:15 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 29 Mar 2015 00:15:02 +0000 (20:15 -0400)
Previously, e4crypt required the user to manually specify the salt
used for their passphrase.  This was user unfriendly to say the least.
The e4crypt program can now request the salt using an ioctl, which
will automatically generate the salt if necessary, and keep it in the
ext4 superblock.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
debugfs/set_fields.c
lib/e2p/ls.c
lib/ext2fs/ext2_fs.h
lib/ext2fs/tst_super_size.c
misc/Makefile.in
misc/e4crypt.8.in
misc/e4crypt.c

index 60695ad..902b0a6 100644 (file)
@@ -167,6 +167,7 @@ static struct field_set_info super_fields[] = {
        { "last_error_line", &set_sb.s_last_error_ino, NULL, 4, parse_uint },
        { "encrypt_algos", &set_sb.s_encrypt_algos, NULL, 1, parse_uint,
          FLAG_ARRAY, 4 },
+       { "encrypt_pw_salt", &set_sb.s_encrypt_pw_salt, NULL, 16, parse_uuid },
        { 0, 0, 0, 0 }
 };
 
index a7ea38a..2e98c14 100644 (file)
@@ -447,6 +447,9 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
                fprintf(f, "Checksum:                 0x%08x\n",
                        sb->s_checksum);
        }
+       if (!e2p_is_null_uuid(sb->s_encrypt_pw_salt))
+               fprintf(f, "Encryption PW Salt:       %s\n",
+                       e2p_uuid2str(sb->s_encrypt_pw_salt));
 }
 
 void list_super (struct ext2_super_block * s)
index 0109551..9f069e2 100644 (file)
@@ -712,7 +712,8 @@ struct ext2_super_block {
        __u32   s_overhead_blocks;      /* overhead blocks/clusters in fs */
        __u32   s_backup_bgs[2];        /* If sparse_super2 enabled */
        __u8    s_encrypt_algos[4];     /* Encryption algorithms in use  */
-       __u32   s_reserved[105];        /* Padding to the end of the block */
+       __u8    s_encrypt_pw_salt[16];  /* Salt used for string2key algorithm */
+       __u32   s_reserved[101];        /* Padding to the end of the block */
        __u32   s_checksum;             /* crc32c(superblock) */
 };
 
index f6c74f7..c536abd 100644 (file)
@@ -137,7 +137,8 @@ int main(int argc, char **argv)
        check_field(s_overhead_blocks, 4);
        check_field(s_backup_bgs, 8);
        check_field(s_encrypt_algos, 4);
-       check_field(s_reserved, 105 * 4);
+       check_field(s_encrypt_pw_salt, 16);
+       check_field(s_reserved, 101 * 4);
        check_field(s_checksum, 4);
        do_field("Superblock end", 0, 0, cur_offset, 1024);
 #endif
index 5646685..872d8b7 100644 (file)
@@ -234,19 +234,21 @@ e4defrag: $(E4DEFRAG_OBJS) $(DEPLIBS)
        $(Q) $(CC) $(ALL_LDFLAGS) -o e4defrag $(E4DEFRAG_OBJS) $(LIBS) \
                $(SYSLIBS)
 
-e4crypt: $(E4CRYPT_OBJS) $(DEPLIBS)
+e4crypt: $(E4CRYPT_OBJS) $(DEPLIBS) $(DEPSTATIC_LIBUUID)
        $(E) "  LD $@"
-       $(Q) $(CC) $(ALL_LDFLAGS) -o e4crypt $(E4CRYPT_OBJS) $(STATIC_LIBS)
+       $(Q) $(CC) $(ALL_LDFLAGS) -o e4crypt $(E4CRYPT_OBJS) \
+               $(STATIC_LIBUUID) $(STATIC_LIBS)
 
 e4defrag.profiled: $(E4DEFRAG_OBJS) $(PROFILED_DEPLIBS)
        $(E) "  LD $@"
        $(Q) $(CC) $(ALL_LDFLAGS) -g -pg -o e4defrag.profiled \
                $(PROFILED_E4DEFRAG_OBJS) $(PROFILED_LIBS) $(SYSLIBS)
 
-e4crypt.profiled: $(E4CRYPT_OBJS) $(PROFILED_DEPLIBS)
+e4crypt.profiled: $(E4CRYPT_OBJS) $(DEPPROFILED_LIBUUID) $(PROFILED_DEPLIBS)
        $(E) "  LD $@"
        $(Q) $(CC) $(ALL_LDFLAGS) -g -pg -o e4crypt.profiled \
-               $(PROFILED_E4CRYPT_OBJS) $(PROFILED_LIBS) $(SYSLIBS)
+               $(PROFILED_E4CRYPT_OBJS) $(PROFILED_LIBUUID) $(PROFILED_LIBS) \
+               $(SYSLIBS)
 
 base_device: base_device.c
        $(E) "  LD $@"
index c3246a2..ed7e951 100644 (file)
@@ -2,7 +2,7 @@
 .SH NAME
 e4crypt \- ext4 filesystem encryption utility
 .SH SYNOPSIS
-.B e4crypt \-a \-n
+.B e4crypt \-a \-S
 .I salt
 [
 .B \-k
@@ -29,18 +29,16 @@ with the descrpiptor of the key set to "ext4:" followed the encryption
 key identifer, which is composed of 16 hexadecimal characters calculated
 by taking a cryptographic hash of the key.
 .TP
-.B \-k
-.I keyring
+.BI \-k " keyring"
 This option specifies the keyring to which the key will be added.  See
 the keyctl man page for more details, but the only keyring which only
-makes sence is @u, @s, and @us, which specifies the user, session, and
+makes sense is @u, @s, and @us, which specifies the user, session, and
 user session keyrings, respectively.   By default,
 .B e4crypt
 will add the key to the session keyring if it has been establishd for
 the current process, or the user session keyring if it has not.
 .TP
-.B \-n
-.I salt
+.BI \-S " salt"
 The salt must be specified, and it should be unique for each
 passphrase.  The salt consists of up to 256 hexadecimal bytes.
 .TP
index 7221180..1a9ce96 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <mntent.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <fcntl.h>
 #include <termios.h>
 #include <unistd.h>
+#include <signal.h>
 #include <asm/unistd.h>
 
 #include "ext2fs/ext2_fs.h"
+#include "uuid/uuid.h"
 
 /* special process keyring shortcut IDs */
 #define KEY_SPEC_THREAD_KEYRING                -1
@@ -56,6 +59,15 @@ typedef __s32 key_serial_t;
 
 #define EXT4_KEY_REF_STR_BUF_SIZE ((EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1)
 
+#ifndef EXT4_IOC_GET_ENCRYPTION_PWSALT
+#define EXT4_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
+#endif
+
+#define OPT_VERBOSE    0x0001
+#define OPT_QUIET      0x0002
+
+int options;
+
 static long keyctl(int cmd, ...)
 {
        va_list va;
@@ -79,30 +91,35 @@ static const size_t hexchars_size = 16;
 #define EXT2FS_KEY_DESC_PREFIX_SIZE 5
 
 #define MSG_USAGE \
-"Usage:\te4crypt -a -n salt [ -k keyring ] [ path ...  ]\n" \
+"Usage:\te4crypt -a -S salt [ -k keyring ] [ path ...  ]\n" \
 "\te4crypt -s policy path ...\n"
 
 #define EXT4_IOC_ENCRYPTION_POLICY      _IOW('f', 19, struct ext4_encryption_policy)
 
-static int is_path_valid(int argc, char *argv[], int path_start_index)
+static void validate_paths(int argc, char *argv[], int path_start_index)
 {
        int x;
        int valid = 1;
-
-       if (path_start_index == argc) {
-               printf("At least one path option must be provided.\n");
-               return 0;
-       }
+       struct stat st;
 
        for (x = path_start_index; x < argc; x++) {
                int ret = access(argv[x], W_OK);
                if (ret) {
-                         printf("%s: %s\n", strerror(errno), argv[x]);
-                         valid = 0;
+               invalid:
+                       perror(argv[x]);
+                       valid = 0;
+                       continue;
+               }
+               ret = stat(argv[x], &st);
+               if (ret < 0)
+                       goto invalid;
+               if (!S_ISDIR(st.st_mode)) {
+                       fprintf(stderr, "%s is not a directory\n", argv[x]);
+                       goto invalid;
                }
        }
-
-       return valid;
+       if (!valid)
+               exit(1);
 }
 
 static int hex2byte(const char *hex, size_t hex_size, char *bytes,
@@ -128,72 +145,229 @@ static int hex2byte(const char *hex, size_t hex_size, char *bytes,
        return 0;
 }
 
-static void set_policy(const char key_descriptor[EXT4_KEY_REF_STR_BUF_SIZE],
-                      int argc, char *argv[], int path_start_index)
+/*
+ * Salt handling
+ */
+struct salt {
+       unsigned char *salt;
+       char key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE];
+       unsigned char key_desc[EXT4_KEY_DESCRIPTOR_SIZE];
+       unsigned char key[EXT4_MAX_KEY_SIZE];
+       size_t salt_len;
+};
+struct salt *salt_list;
+unsigned num_salt;
+unsigned max_salt;
+char passphrase[EXT4_MAX_PASSPHRASE_SIZE];
+
+static struct salt *find_by_salt(unsigned char *salt, size_t salt_len)
 {
-       struct stat st;
-       struct ext4_encryption_policy policy;
-       int fd;
-       int x;
-       int rc;
+       int i;
+       struct salt *p;
+
+       for (i = 0, p = salt_list; i < num_salt; i++, p++)
+               if ((p->salt_len == salt_len) &&
+                   !memcmp(p->salt, salt, salt_len))
+                       return p;
+       return NULL;
+}
 
-       if (!is_path_valid(argc, argv, path_start_index)) {
-               printf("Invalid path.\n");
-               exit(1);
+static void add_salt(unsigned char *salt, size_t salt_len)
+{
+       if (find_by_salt(salt, salt_len))
+               return;
+       if (num_salt >= max_salt) {
+               max_salt = num_salt + 10;
+               salt_list = realloc(salt_list, max_salt * sizeof(struct salt));
+               if (!salt_list) {
+                       fprintf(stderr, "Couldn't allocate salt list\n");
+                       exit(1);
+               }
        }
+       salt_list[num_salt].salt = salt;
+       salt_list[num_salt].salt_len = salt_len;
+       num_salt++;
+}
+
+static void clear_secrets(void)
+{
+       if (salt_list) {
+               memset(salt_list, 0, sizeof(struct salt) * max_salt);
+               free(salt_list);
+               salt_list = NULL;
+       }
+       memset(passphrase, 0, sizeof(passphrase));
+}
+
+static void die_signal_handler(int signum, siginfo_t *siginfo,
+                              void *context)
+{
+       clear_secrets();
+       exit(-1);
+}
+
+void sigcatcher_setup(void)
+{
+       struct sigaction        sa;
+
+       memset(&sa, 0, sizeof(struct sigaction));
+       sa.sa_sigaction = die_signal_handler;
+       sa.sa_flags = SA_SIGINFO;
+
+       sigaction(SIGHUP, &sa, 0);
+       sigaction(SIGINT, &sa, 0);
+       sigaction(SIGQUIT, &sa, 0);
+       sigaction(SIGFPE, &sa, 0);
+       sigaction(SIGILL, &sa, 0);
+       sigaction(SIGBUS, &sa, 0);
+       sigaction(SIGSEGV, &sa, 0);
+       sigaction(SIGABRT, &sa, 0);
+       sigaction(SIGPIPE, &sa, 0);
+       sigaction(SIGALRM, &sa, 0);
+       sigaction(SIGTERM, &sa, 0);
+       sigaction(SIGUSR1, &sa, 0);
+       sigaction(SIGUSR2, &sa, 0);
+       sigaction(SIGPOLL, &sa, 0);
+       sigaction(SIGPROF, &sa, 0);
+       sigaction(SIGSYS, &sa, 0);
+       sigaction(SIGTRAP, &sa, 0);
+       sigaction(SIGVTALRM, &sa, 0);
+       sigaction(SIGXCPU, &sa, 0);
+       sigaction(SIGXFSZ, &sa, 0);
+}
+
 
-       if (!key_descriptor || 
-           (strlen(key_descriptor) != (EXT4_KEY_DESCRIPTOR_SIZE * 2))) {
-               printf("Invalid key descriptor [%s]. Valid characters are "
-                      "0-9 and a-f, lower case. Length must be %d.\n",
-                      key_descriptor, (EXT4_KEY_DESCRIPTOR_SIZE * 2));
+#define PARSE_FLAGS_NOTSUPP_OK 0x0001
+#define PARSE_FLAGS_FORCE_FN   0x0002
+
+static void parse_salt(char *salt_str, int flags)
+{
+       unsigned char buf[EXT4_MAX_SALT_SIZE];
+       unsigned char *salt_buf, *cp = salt_str;
+       char tmp[80];
+       int i, fd, ret, salt_len = 0;
+
+       if (flags & PARSE_FLAGS_FORCE_FN)
+               goto salt_from_filename;
+       if (strncmp(cp, "s:", 2) == 0) {
+               cp += 2;
+               salt_len = strlen(cp);
+               if (salt_len >= EXT4_MAX_SALT_SIZE)
+                       goto invalid_salt;
+               strncpy(buf, cp, sizeof(buf));
+       } else if (cp[0] == '/') {
+       salt_from_filename:
+               fd = open(cp, O_RDONLY | O_DIRECTORY);
+               if (fd == -1 && errno == ENOTDIR)
+                       fd = open(cp, O_RDONLY);
+               if (fd == -1) {
+                       perror(cp);
+                       exit(1);
+               }
+               ret = ioctl(fd, EXT4_IOC_GET_ENCRYPTION_PWSALT, &buf);
+               close(fd);
+               if (ret < 0) {
+                       if (flags & PARSE_FLAGS_NOTSUPP_OK)
+                               return;
+                       perror("EXT4_IOC_GET_ENCRYPTION_PWSALT");
+                       exit(1);
+               }
+               if (options & OPT_VERBOSE) {
+                       char tmp[80];
+                       uuid_unparse(buf, tmp);
+                       printf("%s has pw salt %s\n", cp, tmp);
+               }
+               salt_len = 16;
+       } else if (strncmp(cp, "f:", 2) == 0) {
+               cp += 2;
+               goto salt_from_filename;
+       } else if (strncmp(cp, "0x", 2) == 0) {
+               char *h, *l;
+
+               cp += 2;
+               if (strlen(cp) & 1)
+                       goto invalid_salt;
+               while (*cp) {
+                       if (salt_len >= EXT4_MAX_SALT_SIZE)
+                               goto invalid_salt;
+                       h = memchr(hexchars, *cp++, sizeof(hexchars));
+                       l = memchr(hexchars, *cp++, hexchars_size);
+                       if (!h || !l)
+                               goto invalid_salt;
+                       buf[salt_len++] =
+                               (((unsigned char)(h - hexchars) << 4) +
+                                (unsigned char)(l - hexchars));
+               }
+       } else if (uuid_parse(cp, buf) == 0) {
+               salt_len = 16;
+       } else {
+       invalid_salt:
+               fprintf(stderr, "Invalid salt: %s\n", salt_str);
                exit(1);
        }
+       salt_buf = malloc(salt_len);
+       if (!salt_buf) {
+               fprintf(stderr, "Couldn't allocate salt\n");
+               exit(1);
+       }
+       memcpy(salt_buf, buf, salt_len);
+       add_salt(salt_buf, salt_len);
+}
+
+static void set_policy(struct salt *set_salt,
+                      int argc, char *argv[], int path_start_index)
+{
+       struct salt *salt;
+       struct ext4_encryption_policy policy;
+       uuid_t  uu;
+       int fd;
+       int x;
+       int rc;
 
        for (x = path_start_index; x < argc; x++) {
-               stat(argv[x], &st);
-               if (!S_ISDIR(st.st_mode)) {
-                       printf("You may only set policy on directories.\n");
+               fd = open(argv[x], O_DIRECTORY);
+               if (fd == -1) {
+                       perror(argv[x]);
                        exit(1);
                }
+               if (set_salt)
+                       salt = set_salt;
+               else {
+                       if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_PWSALT,
+                                 &uu) < 0) {
+                               perror("EXT4_IOC_GET_ENCRYPTION_PWSALT");
+                               exit(1);
+                       }
+                       salt = find_by_salt(uu, sizeof(uu));
+                       if (!salt) {
+                               fprintf(stderr, "Couldn't find salt!?!\n");
+                               exit(1);
+                       }
+               }
                policy.version = 0;
                policy.contents_encryption_mode =
                        EXT4_ENCRYPTION_MODE_AES_256_XTS;
                policy.filenames_encryption_mode =
                        EXT4_ENCRYPTION_MODE_AES_256_CBC;
-               if (hex2byte(key_descriptor, (EXT4_KEY_DESCRIPTOR_SIZE * 2),
-                            policy.master_key_descriptor,
-                            EXT4_KEY_DESCRIPTOR_SIZE)) {
-                       printf("Invalid key descriptor [%s]. Valid characters "
-                              "are 0-9 and a-f, lower case.\n",
-                              key_descriptor);
-                       exit(1);
-               }
-               fd = open(argv[x], O_DIRECTORY);
-               if (fd == -1) {
-                       printf("Cannot open directory [%s]: [%s].\n", argv[x],
-                              strerror(errno));
-                       exit(1);
-               }
+               memcpy(policy.master_key_descriptor, salt->key_desc,
+                      EXT4_KEY_DESCRIPTOR_SIZE);
                rc = ioctl(fd, EXT4_IOC_ENCRYPTION_POLICY, &policy);
                close(fd);
                if (rc) {
                        printf("Error [%s] setting policy.\nThe key descriptor "
                               "[%s] may not match the existing encryption "
                               "context for directory [%s].\n",
-                              strerror(errno), key_descriptor, argv[x]);
-                       exit(1);
+                              strerror(errno), salt->key_ref_str, argv[x]);
+                       continue;
                }
-               printf("Key with descriptor [%s%s] successfully applied "
-                      "to directory [%s].\n", EXT2FS_KEY_DESC_PREFIX,
-                      key_descriptor, argv[x]);
+               printf("Key with descriptor [%s] applied to %s.\n",
+                      salt->key_ref_str, argv[x]);
        }
 }
 
-static void pbkdf2_sha512(const char *passphrase, const char *salt, int count,
+static void pbkdf2_sha512(const char *passphrase, struct salt *salt, int count,
                          char derived_key[EXT4_MAX_KEY_SIZE])
 {
-       size_t salt_size = strlen(salt);
        size_t passphrase_size = strlen(passphrase);
        char buf[SHA512_LENGTH + EXT4_MAX_PASSPHRASE_SIZE] = {0};
        char tempbuf[SHA512_LENGTH] = {0};
@@ -206,22 +380,18 @@ static void pbkdf2_sha512(const char *passphrase, const char *salt, int count,
        __u32 *temp_u32 = (__u32 *)tempbuf;
 
        if (passphrase_size > EXT4_MAX_PASSPHRASE_SIZE) {
-               printf("Salt size is %d; max is %d.\n", passphrase_size,
+               printf("Passphrase size is %d; max is %d.\n", passphrase_size,
                       EXT4_MAX_PASSPHRASE_SIZE);
                exit(1);
        }
-       if (salt_size > EXT4_MAX_SALT_SIZE) {
-               printf("Salt size is %d; max is %d.\n", salt_size,
+       if (salt->salt_len > EXT4_MAX_SALT_SIZE) {
+               printf("Salt size is %d; max is %d.\n", salt->salt_len,
                       EXT4_MAX_SALT_SIZE);
                exit(1);
        }
        assert(EXT4_MAX_KEY_SIZE <= SHA512_LENGTH);
 
-       if (hex2byte(salt, strlen(salt), saltbuf, sizeof(saltbuf))) {
-               printf("Invalid salt hex value: [%s]. Valid characters are "
-                      "0-9 and a-f, lower case.\n", salt);
-               exit(1);
-       }
+       memcpy(saltbuf, salt->salt, salt->salt_len);
        memcpy(&saltbuf[EXT4_MAX_SALT_SIZE], passphrase, passphrase_size);
 
        memcpy(&buf[SHA512_LENGTH], passphrase, passphrase_size);
@@ -257,6 +427,26 @@ static int disable_echo(struct termios *saved_settings)
        return rc;
 }
 
+void get_passphrase(char *passphrase, int len)
+{
+       char *p;
+       struct termios current_settings;
+
+       assert(len > 0);
+       disable_echo(&current_settings);
+       p = fgets(passphrase, len, stdin);
+       tcsetattr(0, TCSANOW, &current_settings);
+       printf("\n");
+       if (!p) {
+               printf("Aborting.\n");
+               exit(1);
+       }
+       p = strrchr(passphrase, '\n');
+       if (!p)
+               p = passphrase + len - 1;
+       *p = '\0';
+}
+
 struct keyring_map {
        char name[4];
        size_t name_len;
@@ -311,9 +501,23 @@ static int get_keyring_id(const char *keyring)
        return 0;
 }
 
-static void insert_key_into_keyring(
-       const char *keyring, const char raw_key[EXT4_MAX_KEY_SIZE],
-       const char key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE])
+static void generate_key_ref_str(struct salt *salt)
+{
+       char key_ref1[SHA512_LENGTH];
+       char key_ref2[SHA512_LENGTH];
+       int x;
+
+       ext2fs_sha512(salt->key, EXT4_MAX_KEY_SIZE, key_ref1);
+       ext2fs_sha512(key_ref1, SHA512_LENGTH, key_ref2);
+       memcpy(salt->key_desc, key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
+       for (x = 0; x < EXT4_KEY_DESCRIPTOR_SIZE; ++x) {
+               sprintf(&salt->key_ref_str[x * 2], "%02x",
+                       salt->key_desc[x]);
+       }
+       salt->key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE - 1] = '\0';
+}
+
+static void insert_key_into_keyring(const char *keyring, struct salt *salt)
 {
        int keyring_id = get_keyring_id(keyring);
        struct ext4_encryption_key key;
@@ -325,14 +529,15 @@ static void insert_key_into_keyring(
                printf("Invalid keyring [%s].\n", keyring);
                exit(1);
        }
-       strcpy(key_ref_full, EXT2FS_KEY_DESC_PREFIX);
-       strcpy(&key_ref_full[EXT2FS_KEY_DESC_PREFIX_SIZE], key_ref_str);
+       sprintf(key_ref_full, "%s%s", EXT2FS_KEY_DESC_PREFIX,
+               salt->key_ref_str);
        rc = keyctl(KEYCTL_SEARCH, keyring_id, EXT2FS_KEY_TYPE_LOGON,
                    key_ref_full, 0);
        if (rc != -1) {
-               printf("Key with descriptor [%s] already exists\n",
-                      key_ref_str);
-               exit(1);
+               if ((options & OPT_QUIET) == 0)
+                       printf("Key with descriptor [%s] already exists\n",
+                              salt->key_ref_str);
+               return;
        } else if ((rc == -1) && (errno != ENOKEY)) {
                printf("keyctl_search failed: %s\n", strerror(errno));
                if (errno == -EINVAL)
@@ -340,7 +545,7 @@ static void insert_key_into_keyring(
                exit(1);
        }
        key.mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
-       memcpy(key.raw, raw_key, EXT4_MAX_KEY_SIZE);
+       memcpy(key.raw, salt->key, EXT4_MAX_KEY_SIZE);
        key.size = EXT4_MAX_KEY_SIZE;
        rc = syscall(__NR_add_key, EXT2FS_KEY_TYPE_LOGON, key_ref_full,
                      (void *)&key, sizeof(key), keyring_id);
@@ -349,95 +554,47 @@ static void insert_key_into_keyring(
                        printf("Error adding key to keyring; quota exceeded\n");
                } else {
                        printf("Error adding key with key descriptor [%s]: "
-                              "%s\n", key_ref_str, strerror(errno));
+                              "%s\n", salt->key_ref_str, strerror(errno));
                }
                exit(1);
        } else {
-               printf("Key with descriptor [%s] successfully inserted into "
-                      "keyring\n", key_ref_str);
+               if ((options & OPT_QUIET) == 0)
+                       printf("Added key with descriptor [%s]\n",
+                              salt->key_ref_str);
        }
 }
 
-static void generate_key_ref_str_from_raw_key(
-       const char raw_key[EXT4_MAX_KEY_SIZE],
-       char key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE])
-{
-       char key_ref1[SHA512_LENGTH];
-       char key_ref2[SHA512_LENGTH];
-       int x;
-
-       ext2fs_sha512(raw_key, EXT4_MAX_KEY_SIZE, key_ref1);
-       ext2fs_sha512(key_ref1, SHA512_LENGTH, key_ref2);
-        for (x = 0; x < EXT4_KEY_DESCRIPTOR_SIZE; ++x) {
-                sprintf(&key_ref_str[x * 2], "%.2x",
-                       (unsigned char)key_ref2[x]);
-       }
-       key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE - 1] = '\0';
-}
-
-static void insert_passphrase_into_keyring(
-       const char *keyring, const char *salt,
-       char key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE])
-{
-       char *p;
-       char raw_key[EXT4_MAX_KEY_SIZE];
-       char passphrase[EXT4_MAX_PASSPHRASE_SIZE];
-        struct termios current_settings;
-
-       if (!salt) {
-               printf("Please provide a salt.\n");
-               exit(1);
-       }
-       printf("Enter passphrase (echo disabled): ");
-       disable_echo(&current_settings);
-       p = fgets(passphrase, sizeof(passphrase), stdin);
-       tcsetattr(0, TCSANOW, &current_settings);
-       printf("\n");
-       if (!p) {
-               printf("Aborting.\n");
-               exit(1);
-       }
-       p = strrchr(passphrase, '\n');
-       if (p)
-               *p = '\0';
-       pbkdf2_sha512(passphrase, salt, EXT4_PBKDF2_ITERATIONS, raw_key);
-       generate_key_ref_str_from_raw_key(raw_key, key_ref_str);
-       insert_key_into_keyring(keyring, raw_key, key_ref_str);
-       memset(passphrase, 0, sizeof(passphrase));
-       memset(raw_key, 0, sizeof(raw_key));
-}
-
 static int is_keyring_valid(const char *keyring)
 {
        return (get_keyring_id(keyring) != 0);
 }
 
-static void process_passphrase(const char *keyring, const char *salt,
-                              int argc, char *argv[], int path_start_index)
+void get_default_salts(void)
 {
-       char key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE];
-
-       if (!is_keyring_valid(keyring)) {
-               printf("Invalid keyring name [%s]. Consult keyctl "
-                      "documentation for valid names.\n", keyring);
-               exit(1);
+       FILE    *f = setmntent("/etc/mtab", "r");
+       struct mntent *mnt;
+
+       while (f && ((mnt = getmntent(f)) != NULL)) {
+               if (strcmp(mnt->mnt_type, "ext4") ||
+                   access(mnt->mnt_dir, R_OK))
+                       continue;
+               parse_salt(mnt->mnt_dir, PARSE_FLAGS_NOTSUPP_OK);
        }
-       insert_passphrase_into_keyring(keyring, salt, key_ref_str);
-       if (path_start_index != argc)
-               set_policy(key_ref_str, argc, argv, path_start_index);
+       endmntent(f);
 }
 
 int main(int argc, char *argv[])
 {
+       struct salt *salt, saltbuf;
        char *key_ref_str = NULL;
        char *keyring = NULL;
-       char *salt = NULL;
        int add_passphrase = 0;
-       int opt;
+       int i, opt;
 
+       atexit(clear_secrets);
        if (argc == 1)
                goto fail;
-       while ((opt = getopt(argc, argv, "ak:s:n:")) != -1) {
+       while ((opt = getopt(argc, argv, "ak:s:S:t:vq")) != -1) {
                switch (opt) {
                case 'k':
                        /* Specify a keyring. */
@@ -451,9 +608,33 @@ int main(int argc, char *argv[])
                        /* Set policy on a directory. */
                        key_ref_str = optarg;
                        break;
-               case 'n':
+               case 'S':
                        /* Salt value for passphrase. */
-                       salt = optarg;
+                       parse_salt(optarg, 0);
+                       break;
+               case 't': {
+                       uuid_t  uu;
+                       char str[40];
+                       int fd;
+
+                       fd = open(optarg, O_RDONLY | O_DIRECTORY);
+                       if (fd < 0) {
+                               perror(optarg);
+                               exit(1);
+                       }
+                       if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_PWSALT, &uu) < 0) {
+                               perror("EXT4_IOC_GET_ENCRYPTION_PWSALT");
+                               exit(1);
+                       }
+                       uuid_unparse(uu, str);
+                       printf("Encryption PW Salt: %s\n", str);
+                       exit(0);
+               }
+               case 'v':
+                       options |= OPT_VERBOSE;
+                       break;
+               case 'q':
+                       options |= OPT_QUIET;
                        break;
                default:
                        printf("Unrecognized option: %c\n", opt);
@@ -469,23 +650,49 @@ int main(int argc, char *argv[])
                        printf("-s option invalid with -k\n");
                        goto fail;
                }
-               if (salt) {
+               if (num_salt) {
                        printf("-s option invalid with -n\n");
                        goto fail;
                }
-               set_policy(key_ref_str, argc, argv, optind);
+               strcpy(saltbuf.key_ref_str, key_ref_str);
+               if ((strlen(key_ref_str) != (EXT4_KEY_DESCRIPTOR_SIZE * 2)) ||
+                    hex2byte(key_ref_str, (EXT4_KEY_DESCRIPTOR_SIZE * 2),
+                             saltbuf.key_desc, EXT4_KEY_DESCRIPTOR_SIZE)) {
+                       printf("Invalid key descriptor [%s]. Valid characters "
+                              "are 0-9 and a-f, lower case.  "
+                              "Length must be %d.\n",
+                              key_ref_str, (EXT4_KEY_DESCRIPTOR_SIZE * 2));
+                       exit(1);
+               }
+               if (optind == argc) {
+                       printf("At least one path option must be provided.\n");
+                       exit(1);
+               }
+               validate_paths(argc, argv, optind);
+               set_policy(&saltbuf, argc, argv, optind);
                exit(0);
        }
        if (add_passphrase) {
-               if (!salt) {
-                       printf("-a option requires -n\n");
-                       goto fail;
+               if (num_salt == 0)
+                       get_default_salts();
+               if (num_salt == 0) {
+                       fprintf(stderr, "No salt values available\n");
+                       exit(1);
                }
-               if (key_ref_str) {
-                       printf("-a option invalid with -s\n");
-                       goto fail;
+               validate_paths(argc, argv, optind);
+               for (i = optind; i < argc; i++)
+                       parse_salt(argv[i], PARSE_FLAGS_FORCE_FN);
+               printf("Enter passphrase (echo disabled): ");
+               get_passphrase(passphrase, sizeof(passphrase));
+               for (i = 0, salt = salt_list; i < num_salt; i++, salt++) {
+                       pbkdf2_sha512(passphrase, salt,
+                                     EXT4_PBKDF2_ITERATIONS, salt->key);
+                       generate_key_ref_str(salt);
+                       insert_key_into_keyring(keyring, salt);
                }
-               process_passphrase(keyring, salt, argc, argv, optind);
+               if (optind != argc)
+                       set_policy(NULL, argc, argv, optind);
+               clear_secrets();
                exit(0);
        }
 fail: