From bfa4b350b1710f74c7c4e232392227b70c21a4d4 Mon Sep 17 00:00:00 2001 From: Ildar Muslukhov Date: Fri, 6 Feb 2015 16:30:11 -0800 Subject: [PATCH] misc: add e4crypt tool This patch adds new e4crypt tool for encryption management in the ext4 filesystem. Signed-off-by: Ildar Muslukhov Signed-off-by: Theodore Ts'o --- lib/ext2fs/ext2_fs.h | 31 +++- misc/Makefile.in | 28 ++- misc/e4crypt.8.in | 59 ++++++ misc/e4crypt.c | 494 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 607 insertions(+), 5 deletions(-) create mode 100644 misc/e4crypt.8.in create mode 100644 misc/e4crypt.c diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index 6715d4e..0109551 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -565,12 +565,41 @@ struct ext2_inode_large { /* Metadata checksum algorithms */ #define EXT2_CRC32C_CHKSUM 1 -/* Encryption algorithms */ +/* Encryption algorithms, key size and key reference len */ #define EXT4_ENCRYPTION_MODE_INVALID 0 #define EXT4_ENCRYPTION_MODE_AES_256_XTS 1 #define EXT4_ENCRYPTION_MODE_AES_256_GCM 2 #define EXT4_ENCRYPTION_MODE_AES_256_CBC 3 +#define EXT4_AES_256_XTS_KEY_SIZE 64 +#define EXT4_AES_256_GCM_KEY_SIZE 32 +#define EXT4_AES_256_CBC_KEY_SIZE 32 +#define EXT4_MAX_KEY_SIZE 64 + +#define EXT4_KEY_DESCRIPTOR_SIZE 8 + +/* Password derivation constants */ +#define EXT4_MAX_PASSPHRASE_SIZE 1024 +#define EXT4_MAX_SALT_SIZE 256 +#define EXT4_PBKDF2_ITERATIONS 0xFFFF + +/* + * Policy provided via an ioctl on the topmost directory. This + * structure is also in the kernel. + */ +struct ext4_encryption_policy { + char version; + char contents_encryption_mode; + char filenames_encryption_mode; + char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE]; +} __attribute__((__packed__)); + +struct ext4_encryption_key { + __u32 mode; + char raw[EXT4_MAX_KEY_SIZE]; + __u32 size; +} __attribute__((__packed__)); + /* * Structure of the super block */ diff --git a/misc/Makefile.in b/misc/Makefile.in index dbe524a..5646685 100644 --- a/misc/Makefile.in +++ b/misc/Makefile.in @@ -14,6 +14,9 @@ INSTALL = @INSTALL@ @DEFRAG_CMT@@LINUX_CMT@E4DEFRAG_PROG= e4defrag @DEFRAG_CMT@@LINUX_CMT@E4DEFRAG_MAN= e4defrag.8 +@LINUX_CMT@E4CRYPT_PROG = e4crypt +@LINUX_CMT@E4CRYPT_MAN= e4crypt.8 + @IMAGER_CMT@E2IMAGE_PROG= e2image @IMAGER_CMT@E2IMAGE_MAN= e2image.8 @@ -29,11 +32,12 @@ INSTALL = @INSTALL@ SPROGS= mke2fs badblocks tune2fs dumpe2fs $(BLKID_PROG) logsave \ $(E2IMAGE_PROG) @FSCK_PROG@ e2undo -USPROGS= mklost+found filefrag e2freefrag $(UUIDD_PROG) $(E4DEFRAG_PROG) +USPROGS= mklost+found filefrag e2freefrag $(UUIDD_PROG) \ + $(E4DEFRAG_PROG) $(E4CRYPT_PROG) SMANPAGES= tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \ e2label.8 $(FINDFS_MAN) $(BLKID_MAN) $(E2IMAGE_MAN) \ logsave.8 filefrag.8 e2freefrag.8 e2undo.8 \ - $(UUIDD_MAN) $(E4DEFRAG_MAN) @FSCK_MAN@ + $(UUIDD_MAN) $(E4DEFRAG_MAN) $(E4CRYPT_MAN) @FSCK_MAN@ FMANPAGES= mke2fs.conf.5 ext4.5 UPROGS= chattr lsattr @UUID_CMT@ uuidgen @@ -57,6 +61,7 @@ BLKID_OBJS= blkid.o FILEFRAG_OBJS= filefrag.o E2UNDO_OBJS= e2undo.o E4DEFRAG_OBJS= e4defrag.o +E4CRYPT_OBJS= e4crypt.o E2FREEFRAG_OBJS= e2freefrag.o E2FUZZ_OBJS= e2fuzz.o @@ -81,6 +86,7 @@ PROFILED_FILEFRAG_OBJS= profiled/filefrag.o PROFILED_E2FREEFRAG_OBJS= profiled/e2freefrag.o PROFILED_E2UNDO_OBJS= profiled/e2undo.o PROFILED_E4DEFRAG_OBJS= profiled/e4defrag.o +PROFILED_E4CRYPT_OBJS= profiled/e4crypt.o SRCS= $(srcdir)/tune2fs.c $(srcdir)/mklost+found.c $(srcdir)/mke2fs.c $(srcdir)/mk_hugefiles.c \ $(srcdir)/chattr.c $(srcdir)/lsattr.c $(srcdir)/dumpe2fs.c \ @@ -112,12 +118,13 @@ COMPILE_ET=$(top_builddir)/lib/et/compile_et --build-tree @PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $< all:: profiled $(SPROGS) $(UPROGS) $(USPROGS) $(SMANPAGES) $(UMANPAGES) \ - $(FMANPAGES) $(LPROGS) $(E4DEFRAG_PROG) e2fuzz + $(FMANPAGES) $(LPROGS) $(E4DEFRAG_PROG) $(E4CRYPT_PROGS) e2fuzz @PROFILE_CMT@all:: tune2fs.profiled blkid.profiled e2image.profiled \ e2undo.profiled mke2fs.profiled dumpe2fs.profiled fsck.profiled \ logsave.profiled filefrag.profiled uuidgen.profiled $(UUIDD_PROFILED) \ - e2image.profiled e4defrag.profiled e2freefrag.profiled + e2image.profiled e4defrag.profiled e4crypt.profiled \ + e2freefrag.profiled profiled: @PROFILE_CMT@ $(E) " MKDIR $@" @@ -227,11 +234,20 @@ e4defrag: $(E4DEFRAG_OBJS) $(DEPLIBS) $(Q) $(CC) $(ALL_LDFLAGS) -o e4defrag $(E4DEFRAG_OBJS) $(LIBS) \ $(SYSLIBS) +e4crypt: $(E4CRYPT_OBJS) $(DEPLIBS) + $(E) " LD $@" + $(Q) $(CC) $(ALL_LDFLAGS) -o e4crypt $(E4CRYPT_OBJS) $(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) + $(E) " LD $@" + $(Q) $(CC) $(ALL_LDFLAGS) -g -pg -o e4crypt.profiled \ + $(PROFILED_E4CRYPT_OBJS) $(PROFILED_LIBS) $(SYSLIBS) + base_device: base_device.c $(E) " LD $@" $(Q) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(srcdir)/base_device.c \ @@ -415,6 +431,10 @@ e4defrag.8: $(DEP_SUBSTITUTE) $(srcdir)/e4defrag.8.in $(E) " SUBST $@" $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e4defrag.8.in e4defrag.8 +e4crypt.8: $(DEP_SUBSTITUTE) $(srcdir)/e4crypt.8.in + $(E) " SUBST $@" + $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e4crypt.8.in e4crypt.8 + dumpe2fs.8: $(DEP_SUBSTITUTE) $(srcdir)/dumpe2fs.8.in $(E) " SUBST $@" $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/dumpe2fs.8.in dumpe2fs.8 diff --git a/misc/e4crypt.8.in b/misc/e4crypt.8.in new file mode 100644 index 0000000..c3246a2 --- /dev/null +++ b/misc/e4crypt.8.in @@ -0,0 +1,59 @@ +.TH E4CRYPT 8 "Mar 2015" "e4crypt version 1" +.SH NAME +e4crypt \- ext4 filesystem encryption utility +.SH SYNOPSIS +.B e4crypt \-a \-n +.I salt +[ +.B \-k +.I keyring +] +[ +.I path\fR ... +] +.br +.B e4crypt \-s +.I policy +.I path\fR ... +.SH DESCRIPTION +.B e4crypt +performs encryption management for ext4 file systems. +.SH OPTIONS +.TP +.B \-a +Prompts the user for a passphrase and transforms it into an ecryption +key for use by ext4. The encryption key will be added to the specified +keyring, with a type "logon" (which makes the key accessible to the +user, but which does not allow the key to be returned to userspace), and +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 +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 +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 +The salt must be specified, and it should be unique for each +passphrase. The salt consists of up to 256 hexadecimal bytes. +.TP +.B \-s +Sets a policy for the directories specified on the command line. +All directories must be empty to set the policy; if the directory +already has a policy established, +.B e4crypt +will validate that the policy is what was specified. A policy is an +encryption key identifier of length 16 hexadecimal characters. +.SH AUTHOR +Written by Michael Halcrow and Ildar Muslukhov +. +.SH SEE ALSO +.BR mke2fs (8), +.BR mount (8). diff --git a/misc/e4crypt.c b/misc/e4crypt.c new file mode 100644 index 0000000..7221180 --- /dev/null +++ b/misc/e4crypt.c @@ -0,0 +1,494 @@ +/* + * e4crypt.c - ext4 encryption management utility + * + * Copyright (c) 2014 Google, Inc. + * SHA512 implementation from libtomcrypt. + * + * Authors: Michael Halcrow , + * Ildar Muslukhov + */ + +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE +#endif + +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ext2fs/ext2_fs.h" + +/* special process keyring shortcut IDs */ +#define KEY_SPEC_THREAD_KEYRING -1 +#define KEY_SPEC_PROCESS_KEYRING -2 +#define KEY_SPEC_SESSION_KEYRING -3 +#define KEY_SPEC_USER_KEYRING -4 +#define KEY_SPEC_USER_SESSION_KEYRING -5 +#define KEY_SPEC_GROUP_KEYRING -6 + +#define KEYCTL_GET_KEYRING_ID 0 +#define KEYCTL_DESCRIBE 6 +#define KEYCTL_SEARCH 10 + +typedef __s32 key_serial_t; + +#define EXT4_KEY_REF_STR_BUF_SIZE ((EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1) + +static long keyctl(int cmd, ...) +{ + va_list va; + unsigned long arg2, arg3, arg4, arg5; + + va_start(va, cmd); + arg2 = va_arg(va, unsigned long); + arg3 = va_arg(va, unsigned long); + arg4 = va_arg(va, unsigned long); + arg5 = va_arg(va, unsigned long); + va_end(va); + return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); +} + +static const char *hexchars = "0123456789abcdef"; +static const size_t hexchars_size = 16; + +#define SHA512_LENGTH 64 +#define EXT2FS_KEY_TYPE_LOGON "logon" +#define EXT2FS_KEY_DESC_PREFIX "ext4:" +#define EXT2FS_KEY_DESC_PREFIX_SIZE 5 + +#define MSG_USAGE \ +"Usage:\te4crypt -a -n 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) +{ + int x; + int valid = 1; + + if (path_start_index == argc) { + printf("At least one path option must be provided.\n"); + return 0; + } + + 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; + } + } + + return valid; +} + +static int hex2byte(const char *hex, size_t hex_size, char *bytes, + size_t bytes_size) +{ + int x; + char *h, *l; + + if (hex_size % 2) + return -EINVAL; + for (x = 0; x < hex_size; x += 2) { + h = memchr(hexchars, hex[x], hexchars_size); + if (!h) + return -EINVAL; + l = memchr(hexchars, hex[x + 1], hexchars_size); + if (!l) + return -EINVAL; + if ((x >> 1) >= bytes_size) + return -EINVAL; + bytes[x >> 1] = (((unsigned char)(h - hexchars) << 4) + + (unsigned char)(l - hexchars)); + } + return 0; +} + +static void set_policy(const char key_descriptor[EXT4_KEY_REF_STR_BUF_SIZE], + int argc, char *argv[], int path_start_index) +{ + struct stat st; + struct ext4_encryption_policy policy; + int fd; + int x; + int rc; + + if (!is_path_valid(argc, argv, path_start_index)) { + printf("Invalid path.\n"); + exit(1); + } + + 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)); + exit(1); + } + + 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"); + 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); + } + 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); + } + printf("Key with descriptor [%s%s] successfully applied " + "to directory [%s].\n", EXT2FS_KEY_DESC_PREFIX, + key_descriptor, argv[x]); + } +} + +static void pbkdf2_sha512(const char *passphrase, const char *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}; + char final[SHA512_LENGTH] = {0}; + char saltbuf[EXT4_MAX_SALT_SIZE + EXT4_MAX_PASSPHRASE_SIZE] = {0}; + int actual_buf_len = SHA512_LENGTH + passphrase_size; + int actual_saltbuf_len = EXT4_MAX_SALT_SIZE + passphrase_size; + int x, y; + __u32 *final_u32 = (__u32 *)final; + __u32 *temp_u32 = (__u32 *)tempbuf; + + if (passphrase_size > EXT4_MAX_PASSPHRASE_SIZE) { + printf("Salt 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, + 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[EXT4_MAX_SALT_SIZE], passphrase, passphrase_size); + + memcpy(&buf[SHA512_LENGTH], passphrase, passphrase_size); + + for (x = 0; x < count; ++x) { + if (x == 0) { + ext2fs_sha512(saltbuf, actual_saltbuf_len, tempbuf); + } else { + /* + * buf: [previous hash || passphrase] + */ + memcpy(buf, tempbuf, SHA512_LENGTH); + ext2fs_sha512(buf, actual_buf_len, tempbuf); + } + for (y = 0; y < (sizeof(final) / sizeof(*final_u32)); ++y) + final_u32[y] = final_u32[y] ^ temp_u32[y]; + } + memcpy(derived_key, final, EXT4_MAX_KEY_SIZE); +} + +static int disable_echo(struct termios *saved_settings) +{ + struct termios current_settings; + int rc = 0; + + rc = tcgetattr(0, ¤t_settings); + if (rc) + return rc; + *saved_settings = current_settings; + current_settings.c_lflag &= ~ECHO; + rc = tcsetattr(0, TCSANOW, ¤t_settings); + + return rc; +} + +struct keyring_map { + char name[4]; + size_t name_len; + int code; +}; + +static const struct keyring_map keyrings[] = { + {"@us", 3, KEY_SPEC_USER_SESSION_KEYRING}, + {"@u", 2, KEY_SPEC_USER_KEYRING}, + {"@s", 2, KEY_SPEC_SESSION_KEYRING}, + {"@g", 2, KEY_SPEC_GROUP_KEYRING}, + {"@p", 2, KEY_SPEC_PROCESS_KEYRING}, + {"@t", 2, KEY_SPEC_THREAD_KEYRING}, +}; + +static int get_keyring_id(const char *keyring) +{ + int x; + char *end; + + /* + * If no keyring is specified, by default use either the user + * session key ring or the session keyring. Fetching the + * session keyring will return the user session keyring if no + * session keyring has been set. + * + * We need to do this instead of simply adding the key to + * KEY_SPEC_SESSION_KEYRING since trying to add a key to a + * session keyring that does not yet exist will cause the + * kernel to create a session keyring --- which wil then get + * garbage collected as soon as e4crypt exits. + * + * The fact that the keyctl system call and the add_key system + * call treats KEY_SPEC_SESSION_KEYRING differently when a + * session keyring does not exist is very unfortunate and + * confusing, but so it goes... + */ + if (keyring == NULL) + return keyctl(KEYCTL_GET_KEYRING_ID, + KEY_SPEC_SESSION_KEYRING, 0); + for (x = 0; x < (sizeof(keyrings) / sizeof(keyrings[0])); ++x) { + if (strcmp(keyring, keyrings[x].name) == 0) { + return keyrings[x].code; + } + } + x = strtol(keyring, &end, 10); + if (*end == '\0') { + if (keyctl(KEYCTL_DESCRIBE, x, NULL, 0) < 0) + return 0; + return x; + } + 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]) +{ + int keyring_id = get_keyring_id(keyring); + struct ext4_encryption_key key; + char key_ref_full[EXT2FS_KEY_DESC_PREFIX_SIZE + + EXT4_KEY_REF_STR_BUF_SIZE]; + int rc; + + if (keyring_id == 0) { + 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); + 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); + } else if ((rc == -1) && (errno != ENOKEY)) { + printf("keyctl_search failed: %s\n", strerror(errno)); + if (errno == -EINVAL) + printf("Keyring [%s] is not available.\n", keyring); + exit(1); + } + key.mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; + memcpy(key.raw, raw_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); + if (rc == -1) { + if (errno == EDQUOT) { + 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)); + } + exit(1); + } else { + printf("Key with descriptor [%s] successfully inserted into " + "keyring\n", 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(¤t_settings); + p = fgets(passphrase, sizeof(passphrase), stdin); + tcsetattr(0, TCSANOW, ¤t_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) +{ + 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); + } + insert_passphrase_into_keyring(keyring, salt, key_ref_str); + if (path_start_index != argc) + set_policy(key_ref_str, argc, argv, path_start_index); +} + +int main(int argc, char *argv[]) +{ + char *key_ref_str = NULL; + char *keyring = NULL; + char *salt = NULL; + int add_passphrase = 0; + int opt; + + if (argc == 1) + goto fail; + while ((opt = getopt(argc, argv, "ak:s:n:")) != -1) { + switch (opt) { + case 'k': + /* Specify a keyring. */ + keyring = optarg; + break; + case 'a': + /* Add passphrase-based key to keyring. */ + add_passphrase = 1; + break; + case 's': + /* Set policy on a directory. */ + key_ref_str = optarg; + break; + case 'n': + /* Salt value for passphrase. */ + salt = optarg; + break; + default: + printf("Unrecognized option: %c\n", opt); + goto fail; + } + } + if (key_ref_str) { + if (add_passphrase) { + printf("-s option invalid with -a\n"); + goto fail; + } + if (keyring) { + printf("-s option invalid with -k\n"); + goto fail; + } + if (salt) { + printf("-s option invalid with -n\n"); + goto fail; + } + set_policy(key_ref_str, argc, argv, optind); + exit(0); + } + if (add_passphrase) { + if (!salt) { + printf("-a option requires -n\n"); + goto fail; + } + if (key_ref_str) { + printf("-a option invalid with -s\n"); + goto fail; + } + process_passphrase(keyring, salt, argc, argv, optind); + exit(0); + } +fail: + printf(MSG_USAGE); + return 1; +} -- 1.8.3.1