|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright 2016, 2019 Google LLC | 
|  | * Author: Eric Biggers <ebiggers@google.com> | 
|  | */ | 
|  |  | 
|  | #ifdef OVERRIDE_SYSTEM_FSCRYPT_ADD_KEY_ARG | 
|  | #  define fscrypt_add_key_arg sys_fscrypt_add_key_arg | 
|  | #endif | 
|  | #include "platform_defs.h" | 
|  | #include "command.h" | 
|  | #include "init.h" | 
|  | #include "libfrog/paths.h" | 
|  | #include "io.h" | 
|  |  | 
|  | #ifndef ARRAY_SIZE | 
|  | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Declare the fscrypt ioctls if needed, since someone may be compiling xfsprogs | 
|  | * with old kernel headers.  But <linux/fs.h> has already been included, so be | 
|  | * careful not to declare things twice. | 
|  | */ | 
|  |  | 
|  | /* first batch of ioctls (Linux headers v4.6+) */ | 
|  | #ifndef FS_IOC_SET_ENCRYPTION_POLICY | 
|  | #define fscrypt_policy fscrypt_policy_v1 | 
|  | #define FS_IOC_SET_ENCRYPTION_POLICY		_IOR('f', 19, struct fscrypt_policy) | 
|  | #define FS_IOC_GET_ENCRYPTION_PWSALT		_IOW('f', 20, __u8[16]) | 
|  | #define FS_IOC_GET_ENCRYPTION_POLICY		_IOW('f', 21, struct fscrypt_policy) | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Second batch of ioctls (Linux headers v5.4+), plus some renamings from FS_ to | 
|  | * FSCRYPT_.  We don't bother defining the old names here. | 
|  | */ | 
|  | #ifndef FS_IOC_GET_ENCRYPTION_POLICY_EX | 
|  |  | 
|  | #define FSCRYPT_POLICY_FLAGS_PAD_4		0x00 | 
|  | #define FSCRYPT_POLICY_FLAGS_PAD_8		0x01 | 
|  | #define FSCRYPT_POLICY_FLAGS_PAD_16		0x02 | 
|  | #define FSCRYPT_POLICY_FLAGS_PAD_32		0x03 | 
|  | #define FSCRYPT_POLICY_FLAGS_PAD_MASK		0x03 | 
|  | #define FSCRYPT_POLICY_FLAG_DIRECT_KEY		0x04 | 
|  |  | 
|  | #define FSCRYPT_MODE_AES_256_XTS		1 | 
|  | #define FSCRYPT_MODE_AES_256_CTS		4 | 
|  | #define FSCRYPT_MODE_AES_128_CBC		5 | 
|  | #define FSCRYPT_MODE_AES_128_CTS		6 | 
|  | #define FSCRYPT_MODE_ADIANTUM			9 | 
|  |  | 
|  | /* | 
|  | * In the headers for Linux v4.6 through v5.3, 'struct fscrypt_policy_v1' is | 
|  | * already defined under its old name, 'struct fscrypt_policy'.  But it's fine | 
|  | * to define it under its new name too. | 
|  | * | 
|  | * Note: "v1" policies really are version "0" in the API. | 
|  | */ | 
|  | #define FSCRYPT_POLICY_V1		0 | 
|  | #define FSCRYPT_KEY_DESCRIPTOR_SIZE	8 | 
|  | struct fscrypt_policy_v1 { | 
|  | __u8 version; | 
|  | __u8 contents_encryption_mode; | 
|  | __u8 filenames_encryption_mode; | 
|  | __u8 flags; | 
|  | __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; | 
|  | }; | 
|  |  | 
|  | #define FSCRYPT_POLICY_V2		2 | 
|  | #define FSCRYPT_KEY_IDENTIFIER_SIZE	16 | 
|  | struct fscrypt_policy_v2 { | 
|  | __u8 version; | 
|  | __u8 contents_encryption_mode; | 
|  | __u8 filenames_encryption_mode; | 
|  | __u8 flags; | 
|  | __u8 __reserved[4]; | 
|  | __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; | 
|  | }; | 
|  |  | 
|  | #define FSCRYPT_MAX_KEY_SIZE		64 | 
|  |  | 
|  | #define FS_IOC_GET_ENCRYPTION_POLICY_EX		_IOWR('f', 22, __u8[9]) /* size + version */ | 
|  | struct fscrypt_get_policy_ex_arg { | 
|  | __u64 policy_size; /* input/output */ | 
|  | union { | 
|  | __u8 version; | 
|  | struct fscrypt_policy_v1 v1; | 
|  | struct fscrypt_policy_v2 v2; | 
|  | } policy; /* output */ | 
|  | }; | 
|  |  | 
|  | #define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR	1 | 
|  | #define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER	2 | 
|  | struct fscrypt_key_specifier { | 
|  | __u32 type;	/* one of FSCRYPT_KEY_SPEC_TYPE_* */ | 
|  | __u32 __reserved; | 
|  | union { | 
|  | __u8 __reserved[32]; /* reserve some extra space */ | 
|  | __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; | 
|  | __u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; | 
|  | } u; | 
|  | }; | 
|  |  | 
|  | /* FS_IOC_ADD_ENCRYPTION_KEY is defined later */ | 
|  |  | 
|  | #define FS_IOC_REMOVE_ENCRYPTION_KEY		_IOWR('f', 24, struct fscrypt_remove_key_arg) | 
|  | #define FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS	_IOWR('f', 25, struct fscrypt_remove_key_arg) | 
|  | struct fscrypt_remove_key_arg { | 
|  | struct fscrypt_key_specifier key_spec; | 
|  | #define FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY	0x00000001 | 
|  | #define FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS	0x00000002 | 
|  | __u32 removal_status_flags;	/* output */ | 
|  | __u32 __reserved[5]; | 
|  | }; | 
|  |  | 
|  | #define FS_IOC_GET_ENCRYPTION_KEY_STATUS	_IOWR('f', 26, struct fscrypt_get_key_status_arg) | 
|  | struct fscrypt_get_key_status_arg { | 
|  | /* input */ | 
|  | struct fscrypt_key_specifier key_spec; | 
|  | __u32 __reserved[6]; | 
|  |  | 
|  | /* output */ | 
|  | #define FSCRYPT_KEY_STATUS_ABSENT		1 | 
|  | #define FSCRYPT_KEY_STATUS_PRESENT		2 | 
|  | #define FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED	3 | 
|  | __u32 status; | 
|  | #define FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF   0x00000001 | 
|  | __u32 status_flags; | 
|  | __u32 user_count; | 
|  | __u32 __out_reserved[13]; | 
|  | }; | 
|  |  | 
|  | #endif /* !FS_IOC_GET_ENCRYPTION_POLICY_EX */ | 
|  |  | 
|  | /* | 
|  | * Since the key_id field was added later than struct fscrypt_add_key_arg | 
|  | * itself, we may need to override the system definition to get that field. | 
|  | */ | 
|  | #if !defined(FS_IOC_ADD_ENCRYPTION_KEY) || \ | 
|  | defined(OVERRIDE_SYSTEM_FSCRYPT_ADD_KEY_ARG) | 
|  | #undef fscrypt_add_key_arg | 
|  | struct fscrypt_add_key_arg { | 
|  | struct fscrypt_key_specifier key_spec; | 
|  | __u32 raw_size; | 
|  | __u32 key_id; | 
|  | __u32 __reserved[8]; | 
|  | __u8 raw[]; | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | #ifndef FS_IOC_ADD_ENCRYPTION_KEY | 
|  | #  define FS_IOC_ADD_ENCRYPTION_KEY		_IOWR('f', 23, struct fscrypt_add_key_arg) | 
|  | #endif | 
|  |  | 
|  | static const struct { | 
|  | __u8 mode; | 
|  | const char *name; | 
|  | } available_modes[] = { | 
|  | {FSCRYPT_MODE_AES_256_XTS, "AES-256-XTS"}, | 
|  | {FSCRYPT_MODE_AES_256_CTS, "AES-256-CTS"}, | 
|  | {FSCRYPT_MODE_AES_128_CBC, "AES-128-CBC"}, | 
|  | {FSCRYPT_MODE_AES_128_CTS, "AES-128-CTS"}, | 
|  | {FSCRYPT_MODE_ADIANTUM, "Adiantum"}, | 
|  | }; | 
|  |  | 
|  | static cmdinfo_t get_encpolicy_cmd; | 
|  | static cmdinfo_t set_encpolicy_cmd; | 
|  | static cmdinfo_t add_enckey_cmd; | 
|  | static cmdinfo_t rm_enckey_cmd; | 
|  | static cmdinfo_t enckey_status_cmd; | 
|  |  | 
|  | static void | 
|  | get_encpolicy_help(void) | 
|  | { | 
|  | printf(_( | 
|  | "\n" | 
|  | " display the encryption policy of the current file\n" | 
|  | "\n" | 
|  | " -1 -- Use only the old ioctl to get the encryption policy.\n" | 
|  | "       This only works if the file has a v1 encryption policy.\n" | 
|  | " -t -- Test whether v2 encryption policies are supported.\n" | 
|  | "       Prints \"supported\", \"unsupported\", or an error message.\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | set_encpolicy_help(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | printf(_( | 
|  | "\n" | 
|  | " assign an encryption policy to the currently open file\n" | 
|  | "\n" | 
|  | " Examples:\n" | 
|  | " 'set_encpolicy' - assign v1 policy with default key descriptor\n" | 
|  | "                   (0000000000000000)\n" | 
|  | " 'set_encpolicy -v 2' - assign v2 policy with default key identifier\n" | 
|  | "                        (00000000000000000000000000000000)\n" | 
|  | " 'set_encpolicy 0000111122223333' - assign v1 policy with given key descriptor\n" | 
|  | " 'set_encpolicy 00001111222233334444555566667777' - assign v2 policy with given\n" | 
|  | "                                                    key identifier\n" | 
|  | "\n" | 
|  | " -c MODE -- contents encryption mode\n" | 
|  | " -n MODE -- filenames encryption mode\n" | 
|  | " -f FLAGS -- policy flags\n" | 
|  | " -v VERSION -- policy version\n" | 
|  | "\n" | 
|  | " MODE can be numeric or one of the following predefined values:\n")); | 
|  | printf("    "); | 
|  | for (i = 0; i < ARRAY_SIZE(available_modes); i++) { | 
|  | printf("%s", available_modes[i].name); | 
|  | if (i != ARRAY_SIZE(available_modes) - 1) | 
|  | printf(", "); | 
|  | } | 
|  | printf("\n"); | 
|  | printf(_( | 
|  | " FLAGS and VERSION must be numeric.\n" | 
|  | "\n" | 
|  | " Note that it's only possible to set an encryption policy on an empty\n" | 
|  | " directory.  It's then inherited by new files and subdirectories.\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | add_enckey_help(void) | 
|  | { | 
|  | printf(_( | 
|  | "\n" | 
|  | " add an encryption key to the filesystem\n" | 
|  | "\n" | 
|  | " Examples:\n" | 
|  | " 'add_enckey' - add key for v2 policies\n" | 
|  | " 'add_enckey -d 0000111122223333' - add key for v1 policies w/ given descriptor\n" | 
|  | "\n" | 
|  | "Unless -k is given, the key in binary is read from standard input.\n" | 
|  | " -d DESCRIPTOR -- master_key_descriptor\n" | 
|  | " -k KEY_ID -- ID of fscrypt-provisioning key containing the raw key\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | rm_enckey_help(void) | 
|  | { | 
|  | printf(_( | 
|  | "\n" | 
|  | " remove an encryption key from the filesystem\n" | 
|  | "\n" | 
|  | " Examples:\n" | 
|  | " 'rm_enckey 0000111122223333' - remove key for v1 policies w/ given descriptor\n" | 
|  | " 'rm_enckey 00001111222233334444555566667777' - remove key for v2 policies w/ given identifier\n" | 
|  | "\n" | 
|  | " -a -- remove key for all users who have added it (privileged operation)\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | enckey_status_help(void) | 
|  | { | 
|  | printf(_( | 
|  | "\n" | 
|  | " get the status of a filesystem encryption key\n" | 
|  | "\n" | 
|  | " Examples:\n" | 
|  | " 'enckey_status 0000111122223333' - get status of v1 policy key\n" | 
|  | " 'enckey_status 00001111222233334444555566667777' - get status of v2 policy key\n" | 
|  | "\n")); | 
|  | } | 
|  |  | 
|  | static bool | 
|  | parse_byte_value(const char *arg, __u8 *value_ret) | 
|  | { | 
|  | long value; | 
|  | char *tmp; | 
|  |  | 
|  | value = strtol(arg, &tmp, 0); | 
|  | if (value < 0 || value > 255 || tmp == arg || *tmp != '\0') | 
|  | return false; | 
|  | *value_ret = value; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool | 
|  | parse_mode(const char *arg, __u8 *mode_ret) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < ARRAY_SIZE(available_modes); i++) { | 
|  | if (strcmp(arg, available_modes[i].name) == 0) { | 
|  | *mode_ret = available_modes[i].mode; | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return parse_byte_value(arg, mode_ret); | 
|  | } | 
|  |  | 
|  | static const char * | 
|  | mode2str(__u8 mode) | 
|  | { | 
|  | static char buf[32]; | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < ARRAY_SIZE(available_modes); i++) | 
|  | if (mode == available_modes[i].mode) | 
|  | return available_modes[i].name; | 
|  |  | 
|  | sprintf(buf, "0x%02x", mode); | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static int | 
|  | hexchar2bin(char c) | 
|  | { | 
|  | if (c >= '0' && c <= '9') | 
|  | return c - '0'; | 
|  | if (c >= 'a' && c <= 'f') | 
|  | return 10 + (c - 'a'); | 
|  | if (c >= 'A' && c <= 'F') | 
|  | return 10 + (c - 'A'); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static bool | 
|  | hex2bin(const char *hex, __u8 *bin, size_t bin_len) | 
|  | { | 
|  | if (strlen(hex) != 2 * bin_len) | 
|  | return false; | 
|  |  | 
|  | while (bin_len--) { | 
|  | int hi = hexchar2bin(*hex++); | 
|  | int lo = hexchar2bin(*hex++); | 
|  |  | 
|  | if (hi < 0 || lo < 0) | 
|  | return false; | 
|  | *bin++ = (hi << 4) | lo; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static const char * | 
|  | keydesc2str(const __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]) | 
|  | { | 
|  | static char buf[2 * FSCRYPT_KEY_DESCRIPTOR_SIZE + 1]; | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < FSCRYPT_KEY_DESCRIPTOR_SIZE; i++) | 
|  | sprintf(&buf[2 * i], "%02x", master_key_descriptor[i]); | 
|  |  | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static const char * | 
|  | keyid2str(const __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) | 
|  | { | 
|  | static char buf[2 * FSCRYPT_KEY_IDENTIFIER_SIZE + 1]; | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < FSCRYPT_KEY_IDENTIFIER_SIZE; i++) | 
|  | sprintf(&buf[2 * i], "%02x", master_key_identifier[i]); | 
|  |  | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static const char * | 
|  | keyspectype(const struct fscrypt_key_specifier *key_spec) | 
|  | { | 
|  | switch (key_spec->type) { | 
|  | case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR: | 
|  | return _("descriptor"); | 
|  | case FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER: | 
|  | return _("identifier"); | 
|  | } | 
|  | return _("[unknown]"); | 
|  | } | 
|  |  | 
|  | static const char * | 
|  | keyspec2str(const struct fscrypt_key_specifier *key_spec) | 
|  | { | 
|  | switch (key_spec->type) { | 
|  | case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR: | 
|  | return keydesc2str(key_spec->u.descriptor); | 
|  | case FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER: | 
|  | return keyid2str(key_spec->u.identifier); | 
|  | } | 
|  | return _("[unknown]"); | 
|  | } | 
|  |  | 
|  | static bool | 
|  | str2keydesc(const char *str, | 
|  | __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]) | 
|  | { | 
|  | if (!hex2bin(str, master_key_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE)) { | 
|  | fprintf(stderr, _("invalid key descriptor: %s\n"), str); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool | 
|  | str2keyid(const char *str, | 
|  | __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) | 
|  | { | 
|  | if (!hex2bin(str, master_key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE)) { | 
|  | fprintf(stderr, _("invalid key identifier: %s\n"), str); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Parse a key specifier (descriptor or identifier) given as a hex string. | 
|  | * | 
|  | *  8 bytes (16 hex chars) == key descriptor == v1 encryption policy. | 
|  | * 16 bytes (32 hex chars) == key identifier == v2 encryption policy. | 
|  | * | 
|  | * If a policy_version is given (>= 0), then the corresponding type of key | 
|  | * specifier is required.  Otherwise the specifier type and policy_version are | 
|  | * determined based on the length of the given hex string. | 
|  | * | 
|  | * Returns the policy version, or -1 on error. | 
|  | */ | 
|  | static int | 
|  | str2keyspec(const char *str, int policy_version, | 
|  | struct fscrypt_key_specifier *key_spec) | 
|  | { | 
|  | if (policy_version < 0) { /* version unspecified? */ | 
|  | size_t len = strlen(str); | 
|  |  | 
|  | if (len == 2 * FSCRYPT_KEY_DESCRIPTOR_SIZE) { | 
|  | policy_version = FSCRYPT_POLICY_V1; | 
|  | } else if (len == 2 * FSCRYPT_KEY_IDENTIFIER_SIZE) { | 
|  | policy_version = FSCRYPT_POLICY_V2; | 
|  | } else { | 
|  | fprintf(stderr, _("invalid key specifier: %s\n"), str); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | if (policy_version == FSCRYPT_POLICY_V2) { | 
|  | if (!str2keyid(str, key_spec->u.identifier)) | 
|  | return -1; | 
|  | key_spec->type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; | 
|  | } else { | 
|  | if (!str2keydesc(str, key_spec->u.descriptor)) | 
|  | return -1; | 
|  | key_spec->type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; | 
|  | } | 
|  | return policy_version; | 
|  | } | 
|  |  | 
|  | static int | 
|  | parse_key_id(const char *arg) | 
|  | { | 
|  | long value; | 
|  | char *tmp; | 
|  |  | 
|  | value = strtol(arg, &tmp, 0); | 
|  | if (value <= 0 || value > INT_MAX || tmp == arg || *tmp != '\0') { | 
|  | fprintf(stderr, _("invalid key ID: %s\n"), arg); | 
|  | /* 0 is never a valid Linux key ID. */ | 
|  | return 0; | 
|  | } | 
|  | return value; | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_for_v2_policy_support(void) | 
|  | { | 
|  | struct fscrypt_get_policy_ex_arg arg; | 
|  |  | 
|  | arg.policy_size = sizeof(arg.policy); | 
|  |  | 
|  | if (ioctl(file->fd, FS_IOC_GET_ENCRYPTION_POLICY_EX, &arg) == 0 || | 
|  | errno == ENODATA /* file unencrypted */) { | 
|  | printf(_("supported\n")); | 
|  | return; | 
|  | } | 
|  | if (errno == ENOTTY) { | 
|  | printf(_("unsupported\n")); | 
|  | return; | 
|  | } | 
|  | fprintf(stderr, | 
|  | _("%s: unexpected error checking for FS_IOC_GET_ENCRYPTION_POLICY_EX support: %s\n"), | 
|  | file->name, strerror(errno)); | 
|  | exitcode = 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | show_v1_encryption_policy(const struct fscrypt_policy_v1 *policy) | 
|  | { | 
|  | printf(_("Encryption policy for %s:\n"), file->name); | 
|  | printf(_("\tPolicy version: %u\n"), policy->version); | 
|  | printf(_("\tMaster key descriptor: %s\n"), | 
|  | keydesc2str(policy->master_key_descriptor)); | 
|  | printf(_("\tContents encryption mode: %u (%s)\n"), | 
|  | policy->contents_encryption_mode, | 
|  | mode2str(policy->contents_encryption_mode)); | 
|  | printf(_("\tFilenames encryption mode: %u (%s)\n"), | 
|  | policy->filenames_encryption_mode, | 
|  | mode2str(policy->filenames_encryption_mode)); | 
|  | printf(_("\tFlags: 0x%02x\n"), policy->flags); | 
|  | } | 
|  |  | 
|  | static void | 
|  | show_v2_encryption_policy(const struct fscrypt_policy_v2 *policy) | 
|  | { | 
|  | printf(_("Encryption policy for %s:\n"), file->name); | 
|  | printf(_("\tPolicy version: %u\n"), policy->version); | 
|  | printf(_("\tMaster key identifier: %s\n"), | 
|  | keyid2str(policy->master_key_identifier)); | 
|  | printf(_("\tContents encryption mode: %u (%s)\n"), | 
|  | policy->contents_encryption_mode, | 
|  | mode2str(policy->contents_encryption_mode)); | 
|  | printf(_("\tFilenames encryption mode: %u (%s)\n"), | 
|  | policy->filenames_encryption_mode, | 
|  | mode2str(policy->filenames_encryption_mode)); | 
|  | printf(_("\tFlags: 0x%02x\n"), policy->flags); | 
|  | } | 
|  |  | 
|  | static int | 
|  | get_encpolicy_f(int argc, char **argv) | 
|  | { | 
|  | int c; | 
|  | struct fscrypt_get_policy_ex_arg arg; | 
|  | bool only_use_v1_ioctl = false; | 
|  | int res; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "1t")) != EOF) { | 
|  | switch (c) { | 
|  | case '1': | 
|  | only_use_v1_ioctl = true; | 
|  | break; | 
|  | case 't': | 
|  | test_for_v2_policy_support(); | 
|  | return 0; | 
|  | default: | 
|  | return command_usage(&get_encpolicy_cmd); | 
|  | } | 
|  | } | 
|  | argc -= optind; | 
|  | argv += optind; | 
|  |  | 
|  | if (argc != 0) | 
|  | return command_usage(&get_encpolicy_cmd); | 
|  |  | 
|  | /* first try the new ioctl */ | 
|  | if (only_use_v1_ioctl) { | 
|  | res = -1; | 
|  | errno = ENOTTY; | 
|  | } else { | 
|  | arg.policy_size = sizeof(arg.policy); | 
|  | res = ioctl(file->fd, FS_IOC_GET_ENCRYPTION_POLICY_EX, &arg); | 
|  | } | 
|  |  | 
|  | /* fall back to the old ioctl */ | 
|  | if (res != 0 && errno == ENOTTY) | 
|  | res = ioctl(file->fd, FS_IOC_GET_ENCRYPTION_POLICY, | 
|  | &arg.policy.v1); | 
|  |  | 
|  | if (res != 0) { | 
|  | fprintf(stderr, _("%s: failed to get encryption policy: %s\n"), | 
|  | file->name, strerror(errno)); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | switch (arg.policy.version) { | 
|  | case FSCRYPT_POLICY_V1: | 
|  | show_v1_encryption_policy(&arg.policy.v1); | 
|  | break; | 
|  | case FSCRYPT_POLICY_V2: | 
|  | show_v2_encryption_policy(&arg.policy.v2); | 
|  | break; | 
|  | default: | 
|  | printf(_("Encryption policy for %s:\n"), file->name); | 
|  | printf(_("\tPolicy version: %u (unknown)\n"), | 
|  | arg.policy.version); | 
|  | break; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | set_encpolicy_f(int argc, char **argv) | 
|  | { | 
|  | int c; | 
|  | __u8 contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS; | 
|  | __u8 filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS; | 
|  | __u8 flags = FSCRYPT_POLICY_FLAGS_PAD_16; | 
|  | int version = -1; /* unspecified */ | 
|  | struct fscrypt_key_specifier key_spec; | 
|  | union { | 
|  | __u8 version; | 
|  | struct fscrypt_policy_v1 v1; | 
|  | struct fscrypt_policy_v2 v2; | 
|  | } policy; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "c:n:f:v:")) != EOF) { | 
|  | switch (c) { | 
|  | case 'c': | 
|  | if (!parse_mode(optarg, &contents_encryption_mode)) { | 
|  | fprintf(stderr, | 
|  | _("invalid contents encryption mode: %s\n"), | 
|  | optarg); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  | break; | 
|  | case 'n': | 
|  | if (!parse_mode(optarg, &filenames_encryption_mode)) { | 
|  | fprintf(stderr, | 
|  | _("invalid filenames encryption mode: %s\n"), | 
|  | optarg); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  | break; | 
|  | case 'f': | 
|  | if (!parse_byte_value(optarg, &flags)) { | 
|  | fprintf(stderr, _("invalid flags: %s\n"), | 
|  | optarg); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  | break; | 
|  | case 'v': { | 
|  | __u8 val; | 
|  |  | 
|  | if (!parse_byte_value(optarg, &val)) { | 
|  | fprintf(stderr, | 
|  | _("invalid policy version: %s\n"), | 
|  | optarg); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  | if (val == 1) /* Just to avoid annoying people... */ | 
|  | val = FSCRYPT_POLICY_V1; | 
|  | version = val; | 
|  | break; | 
|  | } | 
|  | default: | 
|  | exitcode = 1; | 
|  | return command_usage(&set_encpolicy_cmd); | 
|  | } | 
|  | } | 
|  | argc -= optind; | 
|  | argv += optind; | 
|  |  | 
|  | if (argc > 1) { | 
|  | exitcode = 1; | 
|  | return command_usage(&set_encpolicy_cmd); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If unspecified, the key descriptor or identifier defaults to all 0's. | 
|  | * If the policy version is additionally unspecified, it defaults to v1. | 
|  | */ | 
|  | memset(&key_spec, 0, sizeof(key_spec)); | 
|  | if (argc > 0) { | 
|  | version = str2keyspec(argv[0], version, &key_spec); | 
|  | if (version < 0) { | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | if (version < 0) /* version unspecified? */ | 
|  | version = FSCRYPT_POLICY_V1; | 
|  |  | 
|  | memset(&policy, 0, sizeof(policy)); | 
|  | policy.version = version; | 
|  | if (version == FSCRYPT_POLICY_V2) { | 
|  | policy.v2.contents_encryption_mode = contents_encryption_mode; | 
|  | policy.v2.filenames_encryption_mode = filenames_encryption_mode; | 
|  | policy.v2.flags = flags; | 
|  | memcpy(policy.v2.master_key_identifier, key_spec.u.identifier, | 
|  | FSCRYPT_KEY_IDENTIFIER_SIZE); | 
|  | } else { | 
|  | /* | 
|  | * xfstests passes .version = 255 for testing.  Just use | 
|  | * 'struct fscrypt_policy_v1' for both v1 and unknown versions. | 
|  | */ | 
|  | policy.v1.contents_encryption_mode = contents_encryption_mode; | 
|  | policy.v1.filenames_encryption_mode = filenames_encryption_mode; | 
|  | policy.v1.flags = flags; | 
|  | memcpy(policy.v1.master_key_descriptor, key_spec.u.descriptor, | 
|  | FSCRYPT_KEY_DESCRIPTOR_SIZE); | 
|  | } | 
|  |  | 
|  | if (ioctl(file->fd, FS_IOC_SET_ENCRYPTION_POLICY, &policy) != 0) { | 
|  | fprintf(stderr, _("%s: failed to set encryption policy: %s\n"), | 
|  | file->name, strerror(errno)); | 
|  | exitcode = 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static ssize_t | 
|  | read_until_limit_or_eof(int fd, void *buf, size_t limit) | 
|  | { | 
|  | size_t bytes_read = 0; | 
|  | ssize_t res; | 
|  |  | 
|  | while (limit) { | 
|  | res = read(fd, buf, limit); | 
|  | if (res < 0) | 
|  | return res; | 
|  | if (res == 0) | 
|  | break; | 
|  | buf += res; | 
|  | bytes_read += res; | 
|  | limit -= res; | 
|  | } | 
|  | return bytes_read; | 
|  | } | 
|  |  | 
|  | static int | 
|  | add_enckey_f(int argc, char **argv) | 
|  | { | 
|  | int c; | 
|  | struct fscrypt_add_key_arg *arg; | 
|  | ssize_t raw_size; | 
|  | int retval = 0; | 
|  |  | 
|  | arg = calloc(1, sizeof(*arg) + FSCRYPT_MAX_KEY_SIZE + 1); | 
|  | if (!arg) { | 
|  | perror("calloc"); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "d:k:")) != EOF) { | 
|  | switch (c) { | 
|  | case 'd': | 
|  | arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; | 
|  | if (!str2keydesc(optarg, arg->key_spec.u.descriptor)) | 
|  | goto out; | 
|  | break; | 
|  | case 'k': | 
|  | arg->key_id = parse_key_id(optarg); | 
|  | if (arg->key_id == 0) | 
|  | goto out; | 
|  | break; | 
|  | default: | 
|  | exitcode = 1; | 
|  | retval = command_usage(&add_enckey_cmd); | 
|  | goto out; | 
|  | } | 
|  | } | 
|  | argc -= optind; | 
|  | argv += optind; | 
|  |  | 
|  | if (argc != 0) { | 
|  | exitcode = 1; | 
|  | retval = command_usage(&add_enckey_cmd); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (arg->key_id == 0) { | 
|  | raw_size = read_until_limit_or_eof(STDIN_FILENO, arg->raw, | 
|  | FSCRYPT_MAX_KEY_SIZE + 1); | 
|  | if (raw_size < 0) { | 
|  | fprintf(stderr, _("Error reading key from stdin: %s\n"), | 
|  | strerror(errno)); | 
|  | exitcode = 1; | 
|  | goto out; | 
|  | } | 
|  | if (raw_size > FSCRYPT_MAX_KEY_SIZE) { | 
|  | fprintf(stderr, | 
|  | _("Invalid key; got > FSCRYPT_MAX_KEY_SIZE (%d) bytes on stdin!\n"), | 
|  | FSCRYPT_MAX_KEY_SIZE); | 
|  | exitcode = 1; | 
|  | goto out; | 
|  | } | 
|  | arg->raw_size = raw_size; | 
|  | } /* else, raw key is given via key with ID 'key_id' */ | 
|  |  | 
|  | if (ioctl(file->fd, FS_IOC_ADD_ENCRYPTION_KEY, arg) != 0) { | 
|  | fprintf(stderr, _("Error adding encryption key: %s\n"), | 
|  | strerror(errno)); | 
|  | exitcode = 1; | 
|  | goto out; | 
|  | } | 
|  | printf(_("Added encryption key with %s %s\n"), | 
|  | keyspectype(&arg->key_spec), keyspec2str(&arg->key_spec)); | 
|  | out: | 
|  | memset(arg->raw, 0, FSCRYPT_MAX_KEY_SIZE + 1); | 
|  | free(arg); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static int | 
|  | rm_enckey_f(int argc, char **argv) | 
|  | { | 
|  | int c; | 
|  | struct fscrypt_remove_key_arg arg; | 
|  | int ioc = FS_IOC_REMOVE_ENCRYPTION_KEY; | 
|  |  | 
|  | memset(&arg, 0, sizeof(arg)); | 
|  |  | 
|  | while ((c = getopt(argc, argv, "a")) != EOF) { | 
|  | switch (c) { | 
|  | case 'a': | 
|  | ioc = FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS; | 
|  | break; | 
|  | default: | 
|  | exitcode = 1; | 
|  | return command_usage(&rm_enckey_cmd); | 
|  | } | 
|  | } | 
|  | argc -= optind; | 
|  | argv += optind; | 
|  |  | 
|  | if (argc != 1) { | 
|  | exitcode = 1; | 
|  | return command_usage(&rm_enckey_cmd); | 
|  | } | 
|  |  | 
|  | if (str2keyspec(argv[0], -1, &arg.key_spec) < 0) { | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (ioctl(file->fd, ioc, &arg) != 0) { | 
|  | fprintf(stderr, _("Error removing encryption key: %s\n"), | 
|  | strerror(errno)); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  | if (arg.removal_status_flags & | 
|  | FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS) { | 
|  | printf(_("Removed user's claim to encryption key with %s %s\n"), | 
|  | keyspectype(&arg.key_spec), keyspec2str(&arg.key_spec)); | 
|  | } else if (arg.removal_status_flags & | 
|  | FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY) { | 
|  | printf(_("Removed encryption key with %s %s, but files still busy\n"), | 
|  | keyspectype(&arg.key_spec), keyspec2str(&arg.key_spec)); | 
|  | } else { | 
|  | printf(_("Removed encryption key with %s %s\n"), | 
|  | keyspectype(&arg.key_spec), keyspec2str(&arg.key_spec)); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | enckey_status_f(int argc, char **argv) | 
|  | { | 
|  | struct fscrypt_get_key_status_arg arg; | 
|  |  | 
|  | memset(&arg, 0, sizeof(arg)); | 
|  |  | 
|  | if (str2keyspec(argv[1], -1, &arg.key_spec) < 0) { | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (ioctl(file->fd, FS_IOC_GET_ENCRYPTION_KEY_STATUS, &arg) != 0) { | 
|  | fprintf(stderr, _("Error getting encryption key status: %s\n"), | 
|  | strerror(errno)); | 
|  | exitcode = 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | switch (arg.status) { | 
|  | case FSCRYPT_KEY_STATUS_PRESENT: | 
|  | printf(_("Present")); | 
|  | if (arg.user_count || arg.status_flags) { | 
|  | printf(" (user_count=%u", arg.user_count); | 
|  | if (arg.status_flags & | 
|  | FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF) | 
|  | printf(", added_by_self"); | 
|  | arg.status_flags &= | 
|  | ~FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF; | 
|  | if (arg.status_flags) | 
|  | printf(", unknown_flags=0x%08x", | 
|  | arg.status_flags); | 
|  | printf(")"); | 
|  | } | 
|  | printf("\n"); | 
|  | return 0; | 
|  | case FSCRYPT_KEY_STATUS_ABSENT: | 
|  | printf(_("Absent\n")); | 
|  | return 0; | 
|  | case FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED: | 
|  | printf(_("Incompletely removed\n")); | 
|  | return 0; | 
|  | default: | 
|  | printf(_("Unknown status (%u)\n"), arg.status); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | encrypt_init(void) | 
|  | { | 
|  | get_encpolicy_cmd.name = "get_encpolicy"; | 
|  | get_encpolicy_cmd.cfunc = get_encpolicy_f; | 
|  | get_encpolicy_cmd.args = _("[-1] [-t]"); | 
|  | get_encpolicy_cmd.argmin = 0; | 
|  | get_encpolicy_cmd.argmax = -1; | 
|  | get_encpolicy_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | 
|  | get_encpolicy_cmd.oneline = | 
|  | _("display the encryption policy of the current file"); | 
|  | get_encpolicy_cmd.help = get_encpolicy_help; | 
|  |  | 
|  | set_encpolicy_cmd.name = "set_encpolicy"; | 
|  | set_encpolicy_cmd.cfunc = set_encpolicy_f; | 
|  | set_encpolicy_cmd.args = | 
|  | _("[-c mode] [-n mode] [-f flags] [-v version] [keyspec]"); | 
|  | set_encpolicy_cmd.argmin = 0; | 
|  | set_encpolicy_cmd.argmax = -1; | 
|  | set_encpolicy_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | 
|  | set_encpolicy_cmd.oneline = | 
|  | _("assign an encryption policy to the current file"); | 
|  | set_encpolicy_cmd.help = set_encpolicy_help; | 
|  |  | 
|  | add_enckey_cmd.name = "add_enckey"; | 
|  | add_enckey_cmd.cfunc = add_enckey_f; | 
|  | add_enckey_cmd.args = _("[-d descriptor] [-k key_id]"); | 
|  | add_enckey_cmd.argmin = 0; | 
|  | add_enckey_cmd.argmax = -1; | 
|  | add_enckey_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | 
|  | add_enckey_cmd.oneline = _("add an encryption key to the filesystem"); | 
|  | add_enckey_cmd.help = add_enckey_help; | 
|  |  | 
|  | rm_enckey_cmd.name = "rm_enckey"; | 
|  | rm_enckey_cmd.cfunc = rm_enckey_f; | 
|  | rm_enckey_cmd.args = _("[-a] keyspec"); | 
|  | rm_enckey_cmd.argmin = 0; | 
|  | rm_enckey_cmd.argmax = -1; | 
|  | rm_enckey_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | 
|  | rm_enckey_cmd.oneline = | 
|  | _("remove an encryption key from the filesystem"); | 
|  | rm_enckey_cmd.help = rm_enckey_help; | 
|  |  | 
|  | enckey_status_cmd.name = "enckey_status"; | 
|  | enckey_status_cmd.cfunc = enckey_status_f; | 
|  | enckey_status_cmd.args = _("keyspec"); | 
|  | enckey_status_cmd.argmin = 1; | 
|  | enckey_status_cmd.argmax = 1; | 
|  | enckey_status_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | 
|  | enckey_status_cmd.oneline = | 
|  | _("get the status of a filesystem encryption key"); | 
|  | enckey_status_cmd.help = enckey_status_help; | 
|  |  | 
|  | add_command(&get_encpolicy_cmd); | 
|  | add_command(&set_encpolicy_cmd); | 
|  | add_command(&add_enckey_cmd); | 
|  | add_command(&rm_enckey_cmd); | 
|  | add_command(&enckey_status_cmd); | 
|  | } |