| // 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); |
| } |