]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
0cf66b0f | 2 | /* |
c304c84f | 3 | * Copyright 2016, 2019 Google LLC |
0cf66b0f | 4 | * Author: Eric Biggers <ebiggers@google.com> |
0cf66b0f EB |
5 | */ |
6 | ||
dfe209d8 EB |
7 | #ifdef OVERRIDE_SYSTEM_FSCRYPT_ADD_KEY_ARG |
8 | # define fscrypt_add_key_arg sys_fscrypt_add_key_arg | |
9 | #endif | |
e97caf71 EB |
10 | #ifdef OVERRIDE_SYSTEM_FSCRYPT_POLICY_V2 |
11 | # define fscrypt_policy_v2 sys_fscrypt_policy_v2 | |
12 | # define fscrypt_get_policy_ex_arg sys_fscrypt_get_policy_ex_arg | |
13 | #endif | |
0cf66b0f EB |
14 | #include "platform_defs.h" |
15 | #include "command.h" | |
16 | #include "init.h" | |
42b4c8e8 | 17 | #include "libfrog/paths.h" |
0cf66b0f EB |
18 | #include "io.h" |
19 | ||
20 | #ifndef ARRAY_SIZE | |
21 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | |
22 | #endif | |
23 | ||
24 | /* | |
336e7c19 EB |
25 | * Declare the fscrypt ioctls if needed, since someone may be compiling xfsprogs |
26 | * with old kernel headers. But <linux/fs.h> has already been included, so be | |
27 | * careful not to declare things twice. | |
0cf66b0f EB |
28 | */ |
29 | ||
336e7c19 | 30 | /* first batch of ioctls (Linux headers v4.6+) */ |
0cf66b0f | 31 | #ifndef FS_IOC_SET_ENCRYPTION_POLICY |
336e7c19 EB |
32 | #define fscrypt_policy fscrypt_policy_v1 |
33 | #define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) | |
34 | #define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) | |
35 | #define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) | |
36 | #endif | |
37 | ||
e97caf71 EB |
38 | /* |
39 | * Since the log2_data_unit_size field was added later than fscrypt_policy_v2 | |
40 | * itself, we may need to override the system definition to get that field. | |
41 | * And also fscrypt_get_policy_ex_arg since it contains fscrypt_policy_v2. | |
42 | */ | |
43 | #if !defined(FS_IOC_GET_ENCRYPTION_POLICY_EX) || \ | |
44 | defined(OVERRIDE_SYSTEM_FSCRYPT_POLICY_V2) | |
45 | #undef fscrypt_policy_v2 | |
46 | struct fscrypt_policy_v2 { | |
47 | __u8 version; | |
48 | __u8 contents_encryption_mode; | |
49 | __u8 filenames_encryption_mode; | |
50 | __u8 flags; | |
51 | __u8 log2_data_unit_size; | |
52 | __u8 __reserved[3]; | |
53 | __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; | |
54 | }; | |
55 | ||
56 | #undef fscrypt_get_policy_ex_arg | |
57 | struct fscrypt_get_policy_ex_arg { | |
58 | __u64 policy_size; /* input/output */ | |
59 | union { | |
60 | __u8 version; | |
61 | struct fscrypt_policy_v1 v1; | |
62 | struct fscrypt_policy_v2 v2; | |
63 | } policy; /* output */ | |
64 | }; | |
65 | #endif | |
66 | ||
336e7c19 EB |
67 | /* |
68 | * Second batch of ioctls (Linux headers v5.4+), plus some renamings from FS_ to | |
69 | * FSCRYPT_. We don't bother defining the old names here. | |
70 | */ | |
71 | #ifndef FS_IOC_GET_ENCRYPTION_POLICY_EX | |
72 | ||
73 | #define FSCRYPT_POLICY_FLAGS_PAD_4 0x00 | |
74 | #define FSCRYPT_POLICY_FLAGS_PAD_8 0x01 | |
75 | #define FSCRYPT_POLICY_FLAGS_PAD_16 0x02 | |
76 | #define FSCRYPT_POLICY_FLAGS_PAD_32 0x03 | |
77 | #define FSCRYPT_POLICY_FLAGS_PAD_MASK 0x03 | |
78 | #define FSCRYPT_POLICY_FLAG_DIRECT_KEY 0x04 | |
79 | ||
80 | #define FSCRYPT_MODE_AES_256_XTS 1 | |
81 | #define FSCRYPT_MODE_AES_256_CTS 4 | |
82 | #define FSCRYPT_MODE_AES_128_CBC 5 | |
83 | #define FSCRYPT_MODE_AES_128_CTS 6 | |
84 | #define FSCRYPT_MODE_ADIANTUM 9 | |
85 | ||
86 | /* | |
87 | * In the headers for Linux v4.6 through v5.3, 'struct fscrypt_policy_v1' is | |
88 | * already defined under its old name, 'struct fscrypt_policy'. But it's fine | |
89 | * to define it under its new name too. | |
90 | * | |
91 | * Note: "v1" policies really are version "0" in the API. | |
92 | */ | |
93 | #define FSCRYPT_POLICY_V1 0 | |
94 | #define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 | |
95 | struct fscrypt_policy_v1 { | |
96 | __u8 version; | |
97 | __u8 contents_encryption_mode; | |
98 | __u8 filenames_encryption_mode; | |
99 | __u8 flags; | |
100 | __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; | |
101 | }; | |
0cf66b0f | 102 | |
336e7c19 EB |
103 | #define FSCRYPT_POLICY_V2 2 |
104 | #define FSCRYPT_KEY_IDENTIFIER_SIZE 16 | |
e97caf71 | 105 | /* struct fscrypt_policy_v2 was defined earlier */ |
336e7c19 EB |
106 | |
107 | #define FSCRYPT_MAX_KEY_SIZE 64 | |
108 | ||
109 | #define FS_IOC_GET_ENCRYPTION_POLICY_EX _IOWR('f', 22, __u8[9]) /* size + version */ | |
e97caf71 | 110 | /* struct fscrypt_get_policy_ex_arg was defined earlier */ |
336e7c19 EB |
111 | |
112 | #define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1 | |
113 | #define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2 | |
114 | struct fscrypt_key_specifier { | |
115 | __u32 type; /* one of FSCRYPT_KEY_SPEC_TYPE_* */ | |
116 | __u32 __reserved; | |
117 | union { | |
118 | __u8 __reserved[32]; /* reserve some extra space */ | |
119 | __u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; | |
120 | __u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; | |
121 | } u; | |
122 | }; | |
123 | ||
dfe209d8 | 124 | /* FS_IOC_ADD_ENCRYPTION_KEY is defined later */ |
336e7c19 EB |
125 | |
126 | #define FS_IOC_REMOVE_ENCRYPTION_KEY _IOWR('f', 24, struct fscrypt_remove_key_arg) | |
127 | #define FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS _IOWR('f', 25, struct fscrypt_remove_key_arg) | |
128 | struct fscrypt_remove_key_arg { | |
129 | struct fscrypt_key_specifier key_spec; | |
130 | #define FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY 0x00000001 | |
131 | #define FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS 0x00000002 | |
132 | __u32 removal_status_flags; /* output */ | |
133 | __u32 __reserved[5]; | |
134 | }; | |
135 | ||
136 | #define FS_IOC_GET_ENCRYPTION_KEY_STATUS _IOWR('f', 26, struct fscrypt_get_key_status_arg) | |
137 | struct fscrypt_get_key_status_arg { | |
138 | /* input */ | |
139 | struct fscrypt_key_specifier key_spec; | |
140 | __u32 __reserved[6]; | |
141 | ||
142 | /* output */ | |
143 | #define FSCRYPT_KEY_STATUS_ABSENT 1 | |
144 | #define FSCRYPT_KEY_STATUS_PRESENT 2 | |
145 | #define FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED 3 | |
146 | __u32 status; | |
147 | #define FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF 0x00000001 | |
148 | __u32 status_flags; | |
149 | __u32 user_count; | |
150 | __u32 __out_reserved[13]; | |
151 | }; | |
152 | ||
153 | #endif /* !FS_IOC_GET_ENCRYPTION_POLICY_EX */ | |
0cf66b0f | 154 | |
dfe209d8 EB |
155 | /* |
156 | * Since the key_id field was added later than struct fscrypt_add_key_arg | |
157 | * itself, we may need to override the system definition to get that field. | |
158 | */ | |
159 | #if !defined(FS_IOC_ADD_ENCRYPTION_KEY) || \ | |
160 | defined(OVERRIDE_SYSTEM_FSCRYPT_ADD_KEY_ARG) | |
161 | #undef fscrypt_add_key_arg | |
162 | struct fscrypt_add_key_arg { | |
163 | struct fscrypt_key_specifier key_spec; | |
164 | __u32 raw_size; | |
165 | __u32 key_id; | |
166 | __u32 __reserved[8]; | |
167 | __u8 raw[]; | |
168 | }; | |
169 | #endif | |
170 | ||
171 | #ifndef FS_IOC_ADD_ENCRYPTION_KEY | |
172 | # define FS_IOC_ADD_ENCRYPTION_KEY _IOWR('f', 23, struct fscrypt_add_key_arg) | |
173 | #endif | |
174 | ||
eb6c66e6 EB |
175 | static const struct { |
176 | __u8 mode; | |
177 | const char *name; | |
178 | } available_modes[] = { | |
179 | {FSCRYPT_MODE_AES_256_XTS, "AES-256-XTS"}, | |
180 | {FSCRYPT_MODE_AES_256_CTS, "AES-256-CTS"}, | |
7cde2c28 EB |
181 | {FSCRYPT_MODE_AES_128_CBC, "AES-128-CBC"}, |
182 | {FSCRYPT_MODE_AES_128_CTS, "AES-128-CTS"}, | |
183 | {FSCRYPT_MODE_ADIANTUM, "Adiantum"}, | |
eb6c66e6 EB |
184 | }; |
185 | ||
0cf66b0f EB |
186 | static cmdinfo_t get_encpolicy_cmd; |
187 | static cmdinfo_t set_encpolicy_cmd; | |
ba71de04 | 188 | static cmdinfo_t add_enckey_cmd; |
c808a097 | 189 | static cmdinfo_t rm_enckey_cmd; |
dafb55f9 | 190 | static cmdinfo_t enckey_status_cmd; |
0cf66b0f | 191 | |
c304c84f EB |
192 | static void |
193 | get_encpolicy_help(void) | |
194 | { | |
195 | printf(_( | |
196 | "\n" | |
197 | " display the encryption policy of the current file\n" | |
198 | "\n" | |
199 | " -1 -- Use only the old ioctl to get the encryption policy.\n" | |
200 | " This only works if the file has a v1 encryption policy.\n" | |
201 | " -t -- Test whether v2 encryption policies are supported.\n" | |
202 | " Prints \"supported\", \"unsupported\", or an error message.\n" | |
203 | "\n")); | |
204 | } | |
205 | ||
0cf66b0f EB |
206 | static void |
207 | set_encpolicy_help(void) | |
208 | { | |
eb6c66e6 EB |
209 | int i; |
210 | ||
0cf66b0f EB |
211 | printf(_( |
212 | "\n" | |
213 | " assign an encryption policy to the currently open file\n" | |
214 | "\n" | |
215 | " Examples:\n" | |
a7a5e44c EB |
216 | " 'set_encpolicy' - assign v1 policy with default key descriptor\n" |
217 | " (0000000000000000)\n" | |
218 | " 'set_encpolicy -v 2' - assign v2 policy with default key identifier\n" | |
219 | " (00000000000000000000000000000000)\n" | |
220 | " 'set_encpolicy 0000111122223333' - assign v1 policy with given key descriptor\n" | |
221 | " 'set_encpolicy 00001111222233334444555566667777' - assign v2 policy with given\n" | |
222 | " key identifier\n" | |
0cf66b0f EB |
223 | "\n" |
224 | " -c MODE -- contents encryption mode\n" | |
225 | " -n MODE -- filenames encryption mode\n" | |
226 | " -f FLAGS -- policy flags\n" | |
e97caf71 | 227 | " -s LOG2_DUSIZE -- log2 of data unit size\n" |
a7a5e44c | 228 | " -v VERSION -- policy version\n" |
0cf66b0f | 229 | "\n" |
eb6c66e6 EB |
230 | " MODE can be numeric or one of the following predefined values:\n")); |
231 | printf(" "); | |
232 | for (i = 0; i < ARRAY_SIZE(available_modes); i++) { | |
233 | printf("%s", available_modes[i].name); | |
234 | if (i != ARRAY_SIZE(available_modes) - 1) | |
235 | printf(", "); | |
236 | } | |
237 | printf("\n"); | |
238 | printf(_( | |
0cf66b0f EB |
239 | " FLAGS and VERSION must be numeric.\n" |
240 | "\n" | |
241 | " Note that it's only possible to set an encryption policy on an empty\n" | |
242 | " directory. It's then inherited by new files and subdirectories.\n" | |
243 | "\n")); | |
244 | } | |
245 | ||
ba71de04 EB |
246 | static void |
247 | add_enckey_help(void) | |
248 | { | |
249 | printf(_( | |
250 | "\n" | |
251 | " add an encryption key to the filesystem\n" | |
252 | "\n" | |
253 | " Examples:\n" | |
254 | " 'add_enckey' - add key for v2 policies\n" | |
255 | " 'add_enckey -d 0000111122223333' - add key for v1 policies w/ given descriptor\n" | |
256 | "\n" | |
dfe209d8 | 257 | "Unless -k is given, the key in binary is read from standard input.\n" |
ba71de04 | 258 | " -d DESCRIPTOR -- master_key_descriptor\n" |
dfe209d8 | 259 | " -k KEY_ID -- ID of fscrypt-provisioning key containing the raw key\n" |
ba71de04 EB |
260 | "\n")); |
261 | } | |
262 | ||
c808a097 EB |
263 | static void |
264 | rm_enckey_help(void) | |
265 | { | |
266 | printf(_( | |
267 | "\n" | |
268 | " remove an encryption key from the filesystem\n" | |
269 | "\n" | |
270 | " Examples:\n" | |
271 | " 'rm_enckey 0000111122223333' - remove key for v1 policies w/ given descriptor\n" | |
272 | " 'rm_enckey 00001111222233334444555566667777' - remove key for v2 policies w/ given identifier\n" | |
273 | "\n" | |
274 | " -a -- remove key for all users who have added it (privileged operation)\n" | |
275 | "\n")); | |
276 | } | |
277 | ||
dafb55f9 EB |
278 | static void |
279 | enckey_status_help(void) | |
280 | { | |
281 | printf(_( | |
282 | "\n" | |
283 | " get the status of a filesystem encryption key\n" | |
284 | "\n" | |
285 | " Examples:\n" | |
286 | " 'enckey_status 0000111122223333' - get status of v1 policy key\n" | |
287 | " 'enckey_status 00001111222233334444555566667777' - get status of v2 policy key\n" | |
288 | "\n")); | |
289 | } | |
290 | ||
0cf66b0f EB |
291 | static bool |
292 | parse_byte_value(const char *arg, __u8 *value_ret) | |
293 | { | |
294 | long value; | |
295 | char *tmp; | |
296 | ||
297 | value = strtol(arg, &tmp, 0); | |
298 | if (value < 0 || value > 255 || tmp == arg || *tmp != '\0') | |
299 | return false; | |
300 | *value_ret = value; | |
301 | return true; | |
302 | } | |
303 | ||
304 | static bool | |
305 | parse_mode(const char *arg, __u8 *mode_ret) | |
306 | { | |
307 | int i; | |
308 | ||
309 | for (i = 0; i < ARRAY_SIZE(available_modes); i++) { | |
310 | if (strcmp(arg, available_modes[i].name) == 0) { | |
311 | *mode_ret = available_modes[i].mode; | |
312 | return true; | |
313 | } | |
314 | } | |
315 | ||
316 | return parse_byte_value(arg, mode_ret); | |
317 | } | |
318 | ||
319 | static const char * | |
320 | mode2str(__u8 mode) | |
321 | { | |
322 | static char buf[32]; | |
323 | int i; | |
324 | ||
325 | for (i = 0; i < ARRAY_SIZE(available_modes); i++) | |
326 | if (mode == available_modes[i].mode) | |
327 | return available_modes[i].name; | |
328 | ||
329 | sprintf(buf, "0x%02x", mode); | |
330 | return buf; | |
331 | } | |
332 | ||
a7a5e44c EB |
333 | static int |
334 | hexchar2bin(char c) | |
335 | { | |
336 | if (c >= '0' && c <= '9') | |
337 | return c - '0'; | |
338 | if (c >= 'a' && c <= 'f') | |
339 | return 10 + (c - 'a'); | |
340 | if (c >= 'A' && c <= 'F') | |
341 | return 10 + (c - 'A'); | |
342 | return -1; | |
343 | } | |
344 | ||
345 | static bool | |
346 | hex2bin(const char *hex, __u8 *bin, size_t bin_len) | |
347 | { | |
348 | if (strlen(hex) != 2 * bin_len) | |
349 | return false; | |
350 | ||
351 | while (bin_len--) { | |
352 | int hi = hexchar2bin(*hex++); | |
353 | int lo = hexchar2bin(*hex++); | |
354 | ||
355 | if (hi < 0 || lo < 0) | |
356 | return false; | |
357 | *bin++ = (hi << 4) | lo; | |
358 | } | |
359 | return true; | |
360 | } | |
361 | ||
0cf66b0f | 362 | static const char * |
c304c84f | 363 | keydesc2str(const __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]) |
0cf66b0f | 364 | { |
336e7c19 | 365 | static char buf[2 * FSCRYPT_KEY_DESCRIPTOR_SIZE + 1]; |
0cf66b0f EB |
366 | int i; |
367 | ||
336e7c19 | 368 | for (i = 0; i < FSCRYPT_KEY_DESCRIPTOR_SIZE; i++) |
0cf66b0f EB |
369 | sprintf(&buf[2 * i], "%02x", master_key_descriptor[i]); |
370 | ||
371 | return buf; | |
372 | } | |
373 | ||
c304c84f EB |
374 | static const char * |
375 | keyid2str(const __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) | |
376 | { | |
377 | static char buf[2 * FSCRYPT_KEY_IDENTIFIER_SIZE + 1]; | |
378 | int i; | |
379 | ||
380 | for (i = 0; i < FSCRYPT_KEY_IDENTIFIER_SIZE; i++) | |
381 | sprintf(&buf[2 * i], "%02x", master_key_identifier[i]); | |
382 | ||
383 | return buf; | |
384 | } | |
385 | ||
a7a5e44c EB |
386 | static const char * |
387 | keyspectype(const struct fscrypt_key_specifier *key_spec) | |
388 | { | |
389 | switch (key_spec->type) { | |
390 | case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR: | |
391 | return _("descriptor"); | |
392 | case FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER: | |
393 | return _("identifier"); | |
394 | } | |
395 | return _("[unknown]"); | |
396 | } | |
397 | ||
398 | static const char * | |
399 | keyspec2str(const struct fscrypt_key_specifier *key_spec) | |
400 | { | |
401 | switch (key_spec->type) { | |
402 | case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR: | |
403 | return keydesc2str(key_spec->u.descriptor); | |
404 | case FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER: | |
405 | return keyid2str(key_spec->u.identifier); | |
406 | } | |
407 | return _("[unknown]"); | |
408 | } | |
409 | ||
410 | static bool | |
411 | str2keydesc(const char *str, | |
412 | __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]) | |
413 | { | |
414 | if (!hex2bin(str, master_key_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE)) { | |
415 | fprintf(stderr, _("invalid key descriptor: %s\n"), str); | |
416 | return false; | |
417 | } | |
418 | return true; | |
419 | } | |
420 | ||
421 | static bool | |
422 | str2keyid(const char *str, | |
423 | __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) | |
424 | { | |
425 | if (!hex2bin(str, master_key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE)) { | |
426 | fprintf(stderr, _("invalid key identifier: %s\n"), str); | |
427 | return false; | |
428 | } | |
429 | return true; | |
430 | } | |
431 | ||
432 | /* | |
433 | * Parse a key specifier (descriptor or identifier) given as a hex string. | |
434 | * | |
435 | * 8 bytes (16 hex chars) == key descriptor == v1 encryption policy. | |
436 | * 16 bytes (32 hex chars) == key identifier == v2 encryption policy. | |
437 | * | |
438 | * If a policy_version is given (>= 0), then the corresponding type of key | |
439 | * specifier is required. Otherwise the specifier type and policy_version are | |
440 | * determined based on the length of the given hex string. | |
441 | * | |
442 | * Returns the policy version, or -1 on error. | |
443 | */ | |
444 | static int | |
445 | str2keyspec(const char *str, int policy_version, | |
446 | struct fscrypt_key_specifier *key_spec) | |
447 | { | |
448 | if (policy_version < 0) { /* version unspecified? */ | |
449 | size_t len = strlen(str); | |
450 | ||
451 | if (len == 2 * FSCRYPT_KEY_DESCRIPTOR_SIZE) { | |
452 | policy_version = FSCRYPT_POLICY_V1; | |
453 | } else if (len == 2 * FSCRYPT_KEY_IDENTIFIER_SIZE) { | |
454 | policy_version = FSCRYPT_POLICY_V2; | |
455 | } else { | |
456 | fprintf(stderr, _("invalid key specifier: %s\n"), str); | |
457 | return -1; | |
458 | } | |
459 | } | |
460 | if (policy_version == FSCRYPT_POLICY_V2) { | |
461 | if (!str2keyid(str, key_spec->u.identifier)) | |
462 | return -1; | |
463 | key_spec->type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; | |
464 | } else { | |
465 | if (!str2keydesc(str, key_spec->u.descriptor)) | |
466 | return -1; | |
467 | key_spec->type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; | |
468 | } | |
469 | return policy_version; | |
470 | } | |
471 | ||
dfe209d8 EB |
472 | static int |
473 | parse_key_id(const char *arg) | |
474 | { | |
475 | long value; | |
476 | char *tmp; | |
477 | ||
478 | value = strtol(arg, &tmp, 0); | |
479 | if (value <= 0 || value > INT_MAX || tmp == arg || *tmp != '\0') { | |
480 | fprintf(stderr, _("invalid key ID: %s\n"), arg); | |
481 | /* 0 is never a valid Linux key ID. */ | |
482 | return 0; | |
483 | } | |
484 | return value; | |
485 | } | |
486 | ||
c304c84f EB |
487 | static void |
488 | test_for_v2_policy_support(void) | |
489 | { | |
490 | struct fscrypt_get_policy_ex_arg arg; | |
491 | ||
492 | arg.policy_size = sizeof(arg.policy); | |
493 | ||
494 | if (ioctl(file->fd, FS_IOC_GET_ENCRYPTION_POLICY_EX, &arg) == 0 || | |
495 | errno == ENODATA /* file unencrypted */) { | |
496 | printf(_("supported\n")); | |
497 | return; | |
498 | } | |
499 | if (errno == ENOTTY) { | |
500 | printf(_("unsupported\n")); | |
501 | return; | |
502 | } | |
503 | fprintf(stderr, | |
504 | _("%s: unexpected error checking for FS_IOC_GET_ENCRYPTION_POLICY_EX support: %s\n"), | |
505 | file->name, strerror(errno)); | |
506 | exitcode = 1; | |
507 | } | |
508 | ||
509 | static void | |
510 | show_v1_encryption_policy(const struct fscrypt_policy_v1 *policy) | |
511 | { | |
512 | printf(_("Encryption policy for %s:\n"), file->name); | |
513 | printf(_("\tPolicy version: %u\n"), policy->version); | |
514 | printf(_("\tMaster key descriptor: %s\n"), | |
515 | keydesc2str(policy->master_key_descriptor)); | |
516 | printf(_("\tContents encryption mode: %u (%s)\n"), | |
517 | policy->contents_encryption_mode, | |
518 | mode2str(policy->contents_encryption_mode)); | |
519 | printf(_("\tFilenames encryption mode: %u (%s)\n"), | |
520 | policy->filenames_encryption_mode, | |
521 | mode2str(policy->filenames_encryption_mode)); | |
522 | printf(_("\tFlags: 0x%02x\n"), policy->flags); | |
523 | } | |
524 | ||
525 | static void | |
526 | show_v2_encryption_policy(const struct fscrypt_policy_v2 *policy) | |
527 | { | |
528 | printf(_("Encryption policy for %s:\n"), file->name); | |
529 | printf(_("\tPolicy version: %u\n"), policy->version); | |
530 | printf(_("\tMaster key identifier: %s\n"), | |
531 | keyid2str(policy->master_key_identifier)); | |
532 | printf(_("\tContents encryption mode: %u (%s)\n"), | |
533 | policy->contents_encryption_mode, | |
534 | mode2str(policy->contents_encryption_mode)); | |
535 | printf(_("\tFilenames encryption mode: %u (%s)\n"), | |
536 | policy->filenames_encryption_mode, | |
537 | mode2str(policy->filenames_encryption_mode)); | |
538 | printf(_("\tFlags: 0x%02x\n"), policy->flags); | |
539 | } | |
540 | ||
0cf66b0f EB |
541 | static int |
542 | get_encpolicy_f(int argc, char **argv) | |
543 | { | |
c304c84f EB |
544 | int c; |
545 | struct fscrypt_get_policy_ex_arg arg; | |
546 | bool only_use_v1_ioctl = false; | |
547 | int res; | |
548 | ||
549 | while ((c = getopt(argc, argv, "1t")) != EOF) { | |
550 | switch (c) { | |
551 | case '1': | |
552 | only_use_v1_ioctl = true; | |
553 | break; | |
554 | case 't': | |
555 | test_for_v2_policy_support(); | |
556 | return 0; | |
557 | default: | |
558 | return command_usage(&get_encpolicy_cmd); | |
559 | } | |
560 | } | |
561 | argc -= optind; | |
562 | argv += optind; | |
563 | ||
564 | if (argc != 0) | |
565 | return command_usage(&get_encpolicy_cmd); | |
566 | ||
567 | /* first try the new ioctl */ | |
568 | if (only_use_v1_ioctl) { | |
569 | res = -1; | |
570 | errno = ENOTTY; | |
571 | } else { | |
572 | arg.policy_size = sizeof(arg.policy); | |
573 | res = ioctl(file->fd, FS_IOC_GET_ENCRYPTION_POLICY_EX, &arg); | |
574 | } | |
575 | ||
576 | /* fall back to the old ioctl */ | |
577 | if (res != 0 && errno == ENOTTY) | |
578 | res = ioctl(file->fd, FS_IOC_GET_ENCRYPTION_POLICY, | |
579 | &arg.policy.v1); | |
0cf66b0f | 580 | |
c304c84f EB |
581 | if (res != 0) { |
582 | fprintf(stderr, _("%s: failed to get encryption policy: %s\n"), | |
0cf66b0f EB |
583 | file->name, strerror(errno)); |
584 | exitcode = 1; | |
585 | return 0; | |
586 | } | |
587 | ||
c304c84f EB |
588 | switch (arg.policy.version) { |
589 | case FSCRYPT_POLICY_V1: | |
590 | show_v1_encryption_policy(&arg.policy.v1); | |
591 | break; | |
592 | case FSCRYPT_POLICY_V2: | |
593 | show_v2_encryption_policy(&arg.policy.v2); | |
594 | break; | |
595 | default: | |
596 | printf(_("Encryption policy for %s:\n"), file->name); | |
597 | printf(_("\tPolicy version: %u (unknown)\n"), | |
598 | arg.policy.version); | |
599 | break; | |
600 | } | |
0cf66b0f EB |
601 | return 0; |
602 | } | |
603 | ||
604 | static int | |
605 | set_encpolicy_f(int argc, char **argv) | |
606 | { | |
607 | int c; | |
a7a5e44c EB |
608 | __u8 contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS; |
609 | __u8 filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS; | |
610 | __u8 flags = FSCRYPT_POLICY_FLAGS_PAD_16; | |
e97caf71 | 611 | __u8 log2_data_unit_size = 0; |
a7a5e44c EB |
612 | int version = -1; /* unspecified */ |
613 | struct fscrypt_key_specifier key_spec; | |
614 | union { | |
615 | __u8 version; | |
616 | struct fscrypt_policy_v1 v1; | |
617 | struct fscrypt_policy_v2 v2; | |
618 | } policy; | |
0cf66b0f | 619 | |
e97caf71 | 620 | while ((c = getopt(argc, argv, "c:n:f:s:v:")) != EOF) { |
0cf66b0f EB |
621 | switch (c) { |
622 | case 'c': | |
a7a5e44c EB |
623 | if (!parse_mode(optarg, &contents_encryption_mode)) { |
624 | fprintf(stderr, | |
625 | _("invalid contents encryption mode: %s\n"), | |
626 | optarg); | |
9e1595e6 | 627 | exitcode = 1; |
0cf66b0f EB |
628 | return 0; |
629 | } | |
630 | break; | |
631 | case 'n': | |
a7a5e44c EB |
632 | if (!parse_mode(optarg, &filenames_encryption_mode)) { |
633 | fprintf(stderr, | |
634 | _("invalid filenames encryption mode: %s\n"), | |
635 | optarg); | |
9e1595e6 | 636 | exitcode = 1; |
0cf66b0f EB |
637 | return 0; |
638 | } | |
639 | break; | |
640 | case 'f': | |
a7a5e44c EB |
641 | if (!parse_byte_value(optarg, &flags)) { |
642 | fprintf(stderr, _("invalid flags: %s\n"), | |
643 | optarg); | |
9e1595e6 | 644 | exitcode = 1; |
0cf66b0f EB |
645 | return 0; |
646 | } | |
647 | break; | |
e97caf71 EB |
648 | case 's': |
649 | if (!parse_byte_value(optarg, &log2_data_unit_size)) { | |
650 | fprintf(stderr, _("invalid log2_dusize: %s\n"), | |
651 | optarg); | |
652 | exitcode = 1; | |
653 | return 0; | |
654 | } | |
655 | break; | |
a7a5e44c EB |
656 | case 'v': { |
657 | __u8 val; | |
658 | ||
659 | if (!parse_byte_value(optarg, &val)) { | |
660 | fprintf(stderr, | |
661 | _("invalid policy version: %s\n"), | |
0cf66b0f | 662 | optarg); |
9e1595e6 | 663 | exitcode = 1; |
0cf66b0f EB |
664 | return 0; |
665 | } | |
a7a5e44c EB |
666 | if (val == 1) /* Just to avoid annoying people... */ |
667 | val = FSCRYPT_POLICY_V1; | |
668 | version = val; | |
0cf66b0f | 669 | break; |
a7a5e44c | 670 | } |
0cf66b0f | 671 | default: |
9e1595e6 | 672 | exitcode = 1; |
0cf66b0f EB |
673 | return command_usage(&set_encpolicy_cmd); |
674 | } | |
675 | } | |
676 | argc -= optind; | |
677 | argv += optind; | |
678 | ||
9e1595e6 DC |
679 | if (argc > 1) { |
680 | exitcode = 1; | |
0cf66b0f | 681 | return command_usage(&set_encpolicy_cmd); |
9e1595e6 | 682 | } |
0cf66b0f | 683 | |
a7a5e44c EB |
684 | /* |
685 | * If unspecified, the key descriptor or identifier defaults to all 0's. | |
686 | * If the policy version is additionally unspecified, it defaults to v1. | |
687 | */ | |
688 | memset(&key_spec, 0, sizeof(key_spec)); | |
0cf66b0f | 689 | if (argc > 0) { |
a7a5e44c | 690 | version = str2keyspec(argv[0], version, &key_spec); |
9e1595e6 DC |
691 | if (version < 0) { |
692 | exitcode = 1; | |
0cf66b0f | 693 | return 0; |
9e1595e6 | 694 | } |
a7a5e44c EB |
695 | } |
696 | if (version < 0) /* version unspecified? */ | |
697 | version = FSCRYPT_POLICY_V1; | |
0cf66b0f | 698 | |
a7a5e44c EB |
699 | memset(&policy, 0, sizeof(policy)); |
700 | policy.version = version; | |
701 | if (version == FSCRYPT_POLICY_V2) { | |
702 | policy.v2.contents_encryption_mode = contents_encryption_mode; | |
703 | policy.v2.filenames_encryption_mode = filenames_encryption_mode; | |
704 | policy.v2.flags = flags; | |
e97caf71 | 705 | policy.v2.log2_data_unit_size = log2_data_unit_size; |
a7a5e44c EB |
706 | memcpy(policy.v2.master_key_identifier, key_spec.u.identifier, |
707 | FSCRYPT_KEY_IDENTIFIER_SIZE); | |
708 | } else { | |
e97caf71 EB |
709 | if (log2_data_unit_size != 0) { |
710 | fprintf(stderr, | |
711 | "v1 policy does not support selecting the data unit size\n"); | |
712 | exitcode = 1; | |
713 | return 0; | |
714 | } | |
a7a5e44c EB |
715 | /* |
716 | * xfstests passes .version = 255 for testing. Just use | |
717 | * 'struct fscrypt_policy_v1' for both v1 and unknown versions. | |
718 | */ | |
719 | policy.v1.contents_encryption_mode = contents_encryption_mode; | |
720 | policy.v1.filenames_encryption_mode = filenames_encryption_mode; | |
721 | policy.v1.flags = flags; | |
722 | memcpy(policy.v1.master_key_descriptor, key_spec.u.descriptor, | |
723 | FSCRYPT_KEY_DESCRIPTOR_SIZE); | |
0cf66b0f EB |
724 | } |
725 | ||
a7a5e44c EB |
726 | if (ioctl(file->fd, FS_IOC_SET_ENCRYPTION_POLICY, &policy) != 0) { |
727 | fprintf(stderr, _("%s: failed to set encryption policy: %s\n"), | |
0cf66b0f EB |
728 | file->name, strerror(errno)); |
729 | exitcode = 1; | |
0cf66b0f | 730 | } |
0cf66b0f EB |
731 | return 0; |
732 | } | |
733 | ||
ba71de04 EB |
734 | static ssize_t |
735 | read_until_limit_or_eof(int fd, void *buf, size_t limit) | |
736 | { | |
737 | size_t bytes_read = 0; | |
738 | ssize_t res; | |
739 | ||
740 | while (limit) { | |
741 | res = read(fd, buf, limit); | |
742 | if (res < 0) | |
743 | return res; | |
744 | if (res == 0) | |
745 | break; | |
746 | buf += res; | |
747 | bytes_read += res; | |
748 | limit -= res; | |
749 | } | |
750 | return bytes_read; | |
751 | } | |
752 | ||
753 | static int | |
754 | add_enckey_f(int argc, char **argv) | |
755 | { | |
756 | int c; | |
757 | struct fscrypt_add_key_arg *arg; | |
758 | ssize_t raw_size; | |
4aaa3af1 | 759 | int retval = 0; |
ba71de04 EB |
760 | |
761 | arg = calloc(1, sizeof(*arg) + FSCRYPT_MAX_KEY_SIZE + 1); | |
762 | if (!arg) { | |
763 | perror("calloc"); | |
764 | exitcode = 1; | |
765 | return 0; | |
766 | } | |
767 | ||
768 | arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; | |
769 | ||
dfe209d8 | 770 | while ((c = getopt(argc, argv, "d:k:")) != EOF) { |
ba71de04 EB |
771 | switch (c) { |
772 | case 'd': | |
773 | arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; | |
774 | if (!str2keydesc(optarg, arg->key_spec.u.descriptor)) | |
775 | goto out; | |
776 | break; | |
dfe209d8 EB |
777 | case 'k': |
778 | arg->key_id = parse_key_id(optarg); | |
779 | if (arg->key_id == 0) | |
780 | goto out; | |
781 | break; | |
ba71de04 | 782 | default: |
9e1595e6 | 783 | exitcode = 1; |
4aaa3af1 ES |
784 | retval = command_usage(&add_enckey_cmd); |
785 | goto out; | |
ba71de04 EB |
786 | } |
787 | } | |
788 | argc -= optind; | |
789 | argv += optind; | |
790 | ||
4aaa3af1 | 791 | if (argc != 0) { |
9e1595e6 | 792 | exitcode = 1; |
4aaa3af1 ES |
793 | retval = command_usage(&add_enckey_cmd); |
794 | goto out; | |
795 | } | |
ba71de04 | 796 | |
dfe209d8 EB |
797 | if (arg->key_id == 0) { |
798 | raw_size = read_until_limit_or_eof(STDIN_FILENO, arg->raw, | |
799 | FSCRYPT_MAX_KEY_SIZE + 1); | |
800 | if (raw_size < 0) { | |
801 | fprintf(stderr, _("Error reading key from stdin: %s\n"), | |
802 | strerror(errno)); | |
803 | exitcode = 1; | |
804 | goto out; | |
805 | } | |
806 | if (raw_size > FSCRYPT_MAX_KEY_SIZE) { | |
807 | fprintf(stderr, | |
808 | _("Invalid key; got > FSCRYPT_MAX_KEY_SIZE (%d) bytes on stdin!\n"), | |
809 | FSCRYPT_MAX_KEY_SIZE); | |
9e1595e6 | 810 | exitcode = 1; |
dfe209d8 EB |
811 | goto out; |
812 | } | |
813 | arg->raw_size = raw_size; | |
814 | } /* else, raw key is given via key with ID 'key_id' */ | |
ba71de04 EB |
815 | |
816 | if (ioctl(file->fd, FS_IOC_ADD_ENCRYPTION_KEY, arg) != 0) { | |
817 | fprintf(stderr, _("Error adding encryption key: %s\n"), | |
818 | strerror(errno)); | |
819 | exitcode = 1; | |
820 | goto out; | |
821 | } | |
822 | printf(_("Added encryption key with %s %s\n"), | |
823 | keyspectype(&arg->key_spec), keyspec2str(&arg->key_spec)); | |
824 | out: | |
825 | memset(arg->raw, 0, FSCRYPT_MAX_KEY_SIZE + 1); | |
826 | free(arg); | |
4aaa3af1 | 827 | return retval; |
ba71de04 EB |
828 | } |
829 | ||
c808a097 EB |
830 | static int |
831 | rm_enckey_f(int argc, char **argv) | |
832 | { | |
833 | int c; | |
834 | struct fscrypt_remove_key_arg arg; | |
835 | int ioc = FS_IOC_REMOVE_ENCRYPTION_KEY; | |
836 | ||
837 | memset(&arg, 0, sizeof(arg)); | |
838 | ||
839 | while ((c = getopt(argc, argv, "a")) != EOF) { | |
840 | switch (c) { | |
841 | case 'a': | |
842 | ioc = FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS; | |
843 | break; | |
844 | default: | |
9e1595e6 | 845 | exitcode = 1; |
c808a097 EB |
846 | return command_usage(&rm_enckey_cmd); |
847 | } | |
848 | } | |
849 | argc -= optind; | |
850 | argv += optind; | |
851 | ||
9e1595e6 DC |
852 | if (argc != 1) { |
853 | exitcode = 1; | |
c808a097 | 854 | return command_usage(&rm_enckey_cmd); |
9e1595e6 | 855 | } |
c808a097 | 856 | |
9e1595e6 DC |
857 | if (str2keyspec(argv[0], -1, &arg.key_spec) < 0) { |
858 | exitcode = 1; | |
c808a097 | 859 | return 0; |
9e1595e6 | 860 | } |
c808a097 EB |
861 | |
862 | if (ioctl(file->fd, ioc, &arg) != 0) { | |
863 | fprintf(stderr, _("Error removing encryption key: %s\n"), | |
864 | strerror(errno)); | |
865 | exitcode = 1; | |
866 | return 0; | |
867 | } | |
868 | if (arg.removal_status_flags & | |
869 | FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS) { | |
870 | printf(_("Removed user's claim to encryption key with %s %s\n"), | |
871 | keyspectype(&arg.key_spec), keyspec2str(&arg.key_spec)); | |
872 | } else if (arg.removal_status_flags & | |
873 | FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY) { | |
874 | printf(_("Removed encryption key with %s %s, but files still busy\n"), | |
875 | keyspectype(&arg.key_spec), keyspec2str(&arg.key_spec)); | |
876 | } else { | |
877 | printf(_("Removed encryption key with %s %s\n"), | |
878 | keyspectype(&arg.key_spec), keyspec2str(&arg.key_spec)); | |
879 | } | |
880 | return 0; | |
881 | } | |
882 | ||
dafb55f9 EB |
883 | static int |
884 | enckey_status_f(int argc, char **argv) | |
885 | { | |
886 | struct fscrypt_get_key_status_arg arg; | |
887 | ||
888 | memset(&arg, 0, sizeof(arg)); | |
889 | ||
9e1595e6 DC |
890 | if (str2keyspec(argv[1], -1, &arg.key_spec) < 0) { |
891 | exitcode = 1; | |
dafb55f9 | 892 | return 0; |
9e1595e6 | 893 | } |
dafb55f9 EB |
894 | |
895 | if (ioctl(file->fd, FS_IOC_GET_ENCRYPTION_KEY_STATUS, &arg) != 0) { | |
896 | fprintf(stderr, _("Error getting encryption key status: %s\n"), | |
897 | strerror(errno)); | |
898 | exitcode = 1; | |
899 | return 0; | |
900 | } | |
901 | ||
902 | switch (arg.status) { | |
903 | case FSCRYPT_KEY_STATUS_PRESENT: | |
904 | printf(_("Present")); | |
905 | if (arg.user_count || arg.status_flags) { | |
906 | printf(" (user_count=%u", arg.user_count); | |
907 | if (arg.status_flags & | |
908 | FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF) | |
909 | printf(", added_by_self"); | |
910 | arg.status_flags &= | |
911 | ~FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF; | |
912 | if (arg.status_flags) | |
913 | printf(", unknown_flags=0x%08x", | |
914 | arg.status_flags); | |
915 | printf(")"); | |
916 | } | |
917 | printf("\n"); | |
918 | return 0; | |
919 | case FSCRYPT_KEY_STATUS_ABSENT: | |
920 | printf(_("Absent\n")); | |
921 | return 0; | |
922 | case FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED: | |
923 | printf(_("Incompletely removed\n")); | |
924 | return 0; | |
925 | default: | |
926 | printf(_("Unknown status (%u)\n"), arg.status); | |
927 | return 0; | |
928 | } | |
929 | } | |
930 | ||
0cf66b0f EB |
931 | void |
932 | encrypt_init(void) | |
933 | { | |
934 | get_encpolicy_cmd.name = "get_encpolicy"; | |
935 | get_encpolicy_cmd.cfunc = get_encpolicy_f; | |
c304c84f | 936 | get_encpolicy_cmd.args = _("[-1] [-t]"); |
0cf66b0f | 937 | get_encpolicy_cmd.argmin = 0; |
c304c84f | 938 | get_encpolicy_cmd.argmax = -1; |
0cf66b0f EB |
939 | get_encpolicy_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; |
940 | get_encpolicy_cmd.oneline = | |
941 | _("display the encryption policy of the current file"); | |
c304c84f | 942 | get_encpolicy_cmd.help = get_encpolicy_help; |
0cf66b0f EB |
943 | |
944 | set_encpolicy_cmd.name = "set_encpolicy"; | |
945 | set_encpolicy_cmd.cfunc = set_encpolicy_f; | |
946 | set_encpolicy_cmd.args = | |
e97caf71 | 947 | _("[-c mode] [-n mode] [-f flags] [-s log2_dusize] [-v version] [keyspec]"); |
0cf66b0f EB |
948 | set_encpolicy_cmd.argmin = 0; |
949 | set_encpolicy_cmd.argmax = -1; | |
950 | set_encpolicy_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | |
951 | set_encpolicy_cmd.oneline = | |
952 | _("assign an encryption policy to the current file"); | |
953 | set_encpolicy_cmd.help = set_encpolicy_help; | |
954 | ||
ba71de04 EB |
955 | add_enckey_cmd.name = "add_enckey"; |
956 | add_enckey_cmd.cfunc = add_enckey_f; | |
dfe209d8 | 957 | add_enckey_cmd.args = _("[-d descriptor] [-k key_id]"); |
ba71de04 EB |
958 | add_enckey_cmd.argmin = 0; |
959 | add_enckey_cmd.argmax = -1; | |
960 | add_enckey_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | |
961 | add_enckey_cmd.oneline = _("add an encryption key to the filesystem"); | |
962 | add_enckey_cmd.help = add_enckey_help; | |
963 | ||
c808a097 EB |
964 | rm_enckey_cmd.name = "rm_enckey"; |
965 | rm_enckey_cmd.cfunc = rm_enckey_f; | |
966 | rm_enckey_cmd.args = _("[-a] keyspec"); | |
967 | rm_enckey_cmd.argmin = 0; | |
968 | rm_enckey_cmd.argmax = -1; | |
969 | rm_enckey_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | |
970 | rm_enckey_cmd.oneline = | |
971 | _("remove an encryption key from the filesystem"); | |
972 | rm_enckey_cmd.help = rm_enckey_help; | |
973 | ||
dafb55f9 EB |
974 | enckey_status_cmd.name = "enckey_status"; |
975 | enckey_status_cmd.cfunc = enckey_status_f; | |
976 | enckey_status_cmd.args = _("keyspec"); | |
977 | enckey_status_cmd.argmin = 1; | |
978 | enckey_status_cmd.argmax = 1; | |
979 | enckey_status_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | |
980 | enckey_status_cmd.oneline = | |
981 | _("get the status of a filesystem encryption key"); | |
982 | enckey_status_cmd.help = enckey_status_help; | |
983 | ||
0cf66b0f EB |
984 | add_command(&get_encpolicy_cmd); |
985 | add_command(&set_encpolicy_cmd); | |
ba71de04 | 986 | add_command(&add_enckey_cmd); |
c808a097 | 987 | add_command(&rm_enckey_cmd); |
dafb55f9 | 988 | add_command(&enckey_status_cmd); |
0cf66b0f | 989 | } |