| Paul Lawrence | 92da49d | 2015-02-25 15:11:13 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2015 Google, Inc. |
| 3 | */ |
| 4 | |
| Elliott Hughes | ee29686 | 2015-03-28 10:39:46 -0700 | [diff] [blame] | 5 | #define TAG "ext4_utils" |
| 6 | |
| Paul Lawrence | 92da49d | 2015-02-25 15:11:13 -0800 | [diff] [blame] | 7 | #include <dirent.h> |
| 8 | #include <errno.h> |
| 9 | #include <string.h> |
| 10 | #include <unistd.h> |
| 11 | |
| 12 | #include <sys/xattr.h> |
| 13 | #include <sys/syscall.h> |
| 14 | #include <sys/stat.h> |
| 15 | |
| Elliott Hughes | ee29686 | 2015-03-28 10:39:46 -0700 | [diff] [blame] | 16 | #include <cutils/klog.h> |
| Paul Lawrence | 92da49d | 2015-02-25 15:11:13 -0800 | [diff] [blame] | 17 | |
| 18 | #include "ext4_crypt.h" |
| 19 | |
| 20 | /* keyring keyctl commands */ |
| 21 | #define KEYCTL_SETPERM 5 /* set permissions for a key in a keyring */ |
| 22 | #define KEYCTL_UNLINK 9 /* unlink a key from a keyring */ |
| 23 | #define KEYCTL_SEARCH 10 /* search for a key in a keyring */ |
| 24 | |
| 25 | #define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy" |
| 26 | #define EXT4_KEYREF_DELIMITER ((char)'.') |
| 27 | |
| 28 | /* Validate that all path items are available and accessible. */ |
| 29 | static int is_path_valid(const char *path) |
| 30 | { |
| 31 | if (access(path, W_OK)) { |
| Elliott Hughes | ee29686 | 2015-03-28 10:39:46 -0700 | [diff] [blame] | 32 | KLOG_ERROR(TAG, "Can't access %s: %s\n",strerror(errno), path); |
| Paul Lawrence | 92da49d | 2015-02-25 15:11:13 -0800 | [diff] [blame] | 33 | return 0; |
| 34 | } |
| 35 | |
| 36 | return 1; |
| 37 | } |
| 38 | |
| 39 | /* Checks whether the policy provided is valid */ |
| 40 | static int is_keyref_valid(const char *keyref) |
| 41 | { |
| 42 | char *period = 0; |
| 43 | size_t key_location_len = 0; |
| 44 | |
| 45 | /* Key ref must have a key and location delimiter character. */ |
| 46 | period = strchr(keyref, EXT4_KEYREF_DELIMITER); |
| 47 | if (!period) { |
| 48 | return 0; |
| 49 | } |
| 50 | |
| 51 | /* period must be >= keyref. */ |
| 52 | key_location_len = period - keyref; |
| 53 | |
| 54 | if (strncmp(keyref, "@t", key_location_len) == 0 || |
| 55 | strncmp(keyref, "@p", key_location_len) == 0 || |
| 56 | strncmp(keyref, "@s", key_location_len) == 0 || |
| 57 | strncmp(keyref, "@u", key_location_len) == 0 || |
| 58 | strncmp(keyref, "@g", key_location_len) == 0 || |
| 59 | strncmp(keyref, "@us", key_location_len) == 0) |
| 60 | return 1; |
| 61 | |
| 62 | return 0; |
| 63 | } |
| 64 | |
| 65 | static int is_dir_empty(const char *dirname) |
| 66 | { |
| 67 | int n = 0; |
| 68 | struct dirent *d; |
| 69 | DIR *dir; |
| 70 | |
| 71 | dir = opendir(dirname); |
| 72 | while ((d = readdir(dir)) != NULL) { |
| 73 | if (strcmp(d->d_name, "lost+found") == 0) { |
| 74 | // Skip lost+found directory |
| 75 | } else if (++n > 2) { |
| 76 | break; |
| 77 | } |
| 78 | } |
| 79 | closedir(dir); |
| 80 | return n <= 2; |
| 81 | } |
| 82 | |
| 83 | int do_policy_set(const char *directory, const char *policy) |
| 84 | { |
| 85 | struct stat st; |
| 86 | ssize_t ret; |
| 87 | |
| 88 | if (!is_keyref_valid(policy)) { |
| Elliott Hughes | ee29686 | 2015-03-28 10:39:46 -0700 | [diff] [blame] | 89 | KLOG_ERROR(TAG, "Policy has invalid format.\n"); |
| Paul Lawrence | 92da49d | 2015-02-25 15:11:13 -0800 | [diff] [blame] | 90 | return -EINVAL; |
| 91 | } |
| 92 | |
| 93 | if (!is_path_valid(directory)) { |
| 94 | return -EINVAL; |
| 95 | } |
| 96 | |
| 97 | stat(directory, &st); |
| 98 | if (!S_ISDIR(st.st_mode)) { |
| Elliott Hughes | ee29686 | 2015-03-28 10:39:46 -0700 | [diff] [blame] | 99 | KLOG_ERROR(TAG, "Can only set policy on a directory (%s)\n", directory); |
| Paul Lawrence | 92da49d | 2015-02-25 15:11:13 -0800 | [diff] [blame] | 100 | return -EINVAL; |
| 101 | } |
| 102 | |
| 103 | if (!is_dir_empty(directory)) { |
| Elliott Hughes | ee29686 | 2015-03-28 10:39:46 -0700 | [diff] [blame] | 104 | KLOG_ERROR(TAG, "Can only set policy on an empty directory (%s)\n", directory); |
| Paul Lawrence | 92da49d | 2015-02-25 15:11:13 -0800 | [diff] [blame] | 105 | return -EINVAL; |
| 106 | } |
| 107 | |
| 108 | ret = lsetxattr(directory, XATTR_NAME_ENCRYPTION_POLICY, policy, |
| 109 | strlen(policy), 0); |
| 110 | |
| 111 | if (ret) { |
| Elliott Hughes | ee29686 | 2015-03-28 10:39:46 -0700 | [diff] [blame] | 112 | KLOG_ERROR(TAG, "Failed to set encryption policy for %s: %s\n", |
| 113 | directory, strerror(errno)); |
| Paul Lawrence | 92da49d | 2015-02-25 15:11:13 -0800 | [diff] [blame] | 114 | return -EINVAL; |
| 115 | } |
| 116 | |
| Elliott Hughes | ee29686 | 2015-03-28 10:39:46 -0700 | [diff] [blame] | 117 | KLOG_INFO(TAG, "Encryption policy for %s is set to %s\n", directory, policy); |
| Paul Lawrence | 92da49d | 2015-02-25 15:11:13 -0800 | [diff] [blame] | 118 | return 0; |
| 119 | } |
| 120 | |
| 121 | static long keyctl(int cmd, ...) |
| 122 | { |
| 123 | va_list va; |
| 124 | unsigned long arg2, arg3, arg4, arg5; |
| 125 | |
| 126 | va_start(va, cmd); |
| 127 | arg2 = va_arg(va, unsigned long); |
| 128 | arg3 = va_arg(va, unsigned long); |
| 129 | arg4 = va_arg(va, unsigned long); |
| 130 | arg5 = va_arg(va, unsigned long); |
| 131 | va_end(va); |
| 132 | return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); |
| 133 | } |
| 134 | |
| 135 | key_serial_t add_key(const char *type, |
| 136 | const char *description, |
| 137 | const void *payload, |
| 138 | size_t plen, |
| 139 | key_serial_t ringid) |
| 140 | { |
| 141 | return syscall(__NR_add_key, type, description, payload, plen, ringid); |
| 142 | } |
| 143 | |
| 144 | long keyctl_setperm(key_serial_t id, int permissions) |
| 145 | { |
| 146 | return keyctl(KEYCTL_SETPERM, id, permissions); |
| 147 | } |