diff --git a/bcachefs.c b/bcachefs.c index 9b6b057f..d57a46ea 100644 --- a/bcachefs.c +++ b/bcachefs.c @@ -36,7 +36,6 @@ static void usage(void) " fsck Check an existing filesystem for errors\n" "\n" "Startup/shutdown, assembly of multi device filesystems:\n" - " unlock Unlock an encrypted filesystem prior to running/mounting\n" " assemble Assemble an existing multi device filesystem\n" " incremental Incrementally assemble an existing multi device filesystem\n" " run Start a partially assembled filesystem\n" @@ -54,6 +53,11 @@ static void usage(void) " device evacuate Migrate data off of a specific device\n" " device set-state Mark a device as failed\n" "\n" + "Encryption:\n" + " unlock Unlock an encrypted filesystem prior to running/mounting\n" + " set-passphrase Change passphrase on an existing (unmounted) filesystem\n" + " remove-passphrase Remove passphrase on an existing (unmounted) filesystem\n" + "\n" "Migrate:\n" " migrate Migrate an existing filesystem to bcachefs, in place\n" " migrate-superblock\n" @@ -133,8 +137,6 @@ int main(int argc, char *argv[]) if (!strcmp(cmd, "fsck")) return cmd_fsck(argc, argv); - if (!strcmp(cmd, "unlock")) - return cmd_unlock(argc, argv); if (!strcmp(cmd, "assemble")) return cmd_assemble(argc, argv); if (!strcmp(cmd, "incremental")) @@ -150,6 +152,13 @@ int main(int argc, char *argv[]) if (!strcmp(cmd, "device")) return device_cmds(argc, argv); + if (!strcmp(cmd, "unlock")) + return cmd_unlock(argc, argv); + if (!strcmp(cmd, "set-passphrase")) + return cmd_set_passphrase(argc, argv); + if (!strcmp(cmd, "remove-passphrase")) + return cmd_remove_passphrase(argc, argv); + if (!strcmp(cmd, "migrate")) return cmd_migrate(argc, argv); if (!strcmp(cmd, "migrate-superblock")) diff --git a/cmd_format.c b/cmd_format.c index 40b75e74..fbe75d08 100644 --- a/cmd_format.c +++ b/cmd_format.c @@ -25,6 +25,7 @@ #include "libbcachefs.h" #include "crypto.h" #include "opts.h" +#include "super-io.h" #include "util.h" #define OPTS \ @@ -250,24 +251,8 @@ int cmd_format(int argc, char *argv[]) if (!darray_size(devices)) die("Please supply a device"); - if (opts.encrypted && !no_passphrase) { - opts.passphrase = read_passphrase("Enter passphrase: "); - - if (isatty(STDIN_FILENO)) { - char *pass2 = - read_passphrase("Enter same passphrase again: "); - - if (strcmp(opts.passphrase, pass2)) { - memzero_explicit(opts.passphrase, - strlen(opts.passphrase)); - memzero_explicit(pass2, strlen(pass2)); - die("Passphrases do not match"); - } - - memzero_explicit(pass2, strlen(pass2)); - free(pass2); - } - } + if (opts.encrypted && !no_passphrase) + opts.passphrase = read_passphrase_twice("Enter passphrase: "); darray_foreach(dev, devices) dev->fd = open_for_format(dev->path, force); @@ -289,12 +274,16 @@ int cmd_format(int argc, char *argv[]) int cmd_show_super(int argc, char *argv[]) { - struct bch_sb *sb; + struct bch_sb_handle sb; + const char *err; if (argc != 2) die("please supply a single device"); - sb = bch2_super_read(argv[1]); - bch2_super_print(sb, HUMAN_READABLE); + err = bch2_read_super(argv[1], bch2_opts_empty(), &sb); + if (err) + die("Error opening %s: %s", argv[1], err); + + bch2_super_print(sb.sb, HUMAN_READABLE); return 0; } diff --git a/cmd_key.c b/cmd_key.c index b22de815..b5bacf1c 100644 --- a/cmd_key.c +++ b/cmd_key.c @@ -9,19 +9,92 @@ int cmd_unlock(int argc, char *argv[]) { - struct bch_sb *sb; + struct bch_sb_handle sb; + const char *err; char *passphrase; if (argc != 2) - die("please supply a single device"); + die("Please supply a single device"); - sb = bch2_super_read(argv[1]); + err = bch2_read_super(argv[1], bch2_opts_empty(), &sb); + if (err) + die("Error opening %s: %s", argv[1], err); passphrase = read_passphrase("Enter passphrase: "); - bch2_add_key(sb, passphrase); + bch2_add_key(sb.sb, passphrase); memzero_explicit(passphrase, strlen(passphrase)); free(passphrase); return 0; } + +int cmd_set_passphrase(int argc, char *argv[]) +{ + struct bch_opts opts = bch2_opts_empty(); + struct bch_fs *c = NULL; + const char *err; + + if (argc < 2) + die("Please supply one or more devices"); + + opt_set(opts, nostart, true); + err = bch2_fs_open(argv + 1, argc - 1, opts, &c); + if (err) + die("Error opening %s: %s", argv[1], err); + + struct bch_sb_field_crypt *crypt = bch2_sb_get_crypt(c->disk_sb); + if (!crypt) + die("Filesystem does not have encryption enabled"); + + struct bch_encrypted_key new_key; + new_key.magic = BCH_KEY_MAGIC; + + int ret = bch2_decrypt_sb_key(c, crypt, &new_key.key); + if (ret) + die("Error getting current key"); + + char *new_passphrase = read_passphrase_twice("Enter new passphrase: "); + struct bch_key passphrase_key = derive_passphrase(crypt, new_passphrase); + + if (bch2_chacha_encrypt_key(&passphrase_key, __bch2_sb_key_nonce(c->disk_sb), + &new_key, sizeof(new_key))) + die("error encrypting key"); + crypt->key = new_key; + + bch2_write_super(c); + bch2_fs_stop(c); + return 0; +} + +int cmd_remove_passphrase(int argc, char *argv[]) +{ + struct bch_opts opts = bch2_opts_empty(); + struct bch_fs *c = NULL; + const char *err; + + if (argc < 2) + die("Please supply one or more devices"); + + opt_set(opts, nostart, true); + err = bch2_fs_open(argv + 1, argc - 1, opts, &c); + if (err) + die("Error opening %s: %s", argv[1], err); + + struct bch_sb_field_crypt *crypt = bch2_sb_get_crypt(c->disk_sb); + if (!crypt) + die("Filesystem does not have encryption enabled"); + + struct bch_encrypted_key new_key; + new_key.magic = BCH_KEY_MAGIC; + + int ret = bch2_decrypt_sb_key(c, crypt, &new_key.key); + if (ret) + die("Error getting current key"); + + crypt->key = new_key; + + bch2_write_super(c); + bch2_fs_stop(c); + return 0; +} diff --git a/cmd_migrate.c b/cmd_migrate.c index 4b0dd9bc..519e85da 100644 --- a/cmd_migrate.c +++ b/cmd_migrate.c @@ -701,24 +701,8 @@ int cmd_migrate(int argc, char *argv[]) find_superblock_space(extents, &dev); - if (format_opts.encrypted && !no_passphrase) { - format_opts.passphrase = read_passphrase("Enter passphrase: "); - - if (isatty(STDIN_FILENO)) { - char *pass2 = - read_passphrase("Enter same passphrase again: "); - - if (strcmp(format_opts.passphrase, pass2)) { - memzero_explicit(format_opts.passphrase, - strlen(format_opts.passphrase)); - memzero_explicit(pass2, strlen(pass2)); - die("Passphrases do not match"); - } - - memzero_explicit(pass2, strlen(pass2)); - free(pass2); - } - } + if (format_opts.encrypted && !no_passphrase) + format_opts.passphrase = read_passphrase_twice("Enter passphrase: "); struct bch_sb *sb = bch2_format(format_opts, &dev, 1); u64 sb_offset = le64_to_cpu(sb->layout.sb_offset[0]); diff --git a/cmds.h b/cmds.h index 7e4712a5..fa28904c 100644 --- a/cmds.h +++ b/cmds.h @@ -12,7 +12,6 @@ int cmd_format(int argc, char *argv[]); int cmd_show_super(int argc, char *argv[]); -int cmd_unlock(int argc, char *argv[]); int cmd_assemble(int argc, char *argv[]); int cmd_incremental(int argc, char *argv[]); int cmd_run(int argc, char *argv[]); @@ -28,6 +27,10 @@ int cmd_device_offline(int argc, char *argv[]); int cmd_device_evacuate(int argc, char *argv[]); int cmd_device_set_state(int argc, char *argv[]); +int cmd_unlock(int argc, char *argv[]); +int cmd_set_passphrase(int argc, char *argv[]); +int cmd_remove_passphrase(int argc, char *argv[]); + int cmd_fsck(int argc, char *argv[]); int cmd_dump(int argc, char *argv[]); diff --git a/crypto.c b/crypto.c index f9ac57f7..306071f9 100644 --- a/crypto.c +++ b/crypto.c @@ -54,11 +54,32 @@ char *read_passphrase(const char *prompt) return buf; } -void derive_passphrase(struct bch_sb_field_crypt *crypt, - struct bch_key *key, - const char *passphrase) +char *read_passphrase_twice(const char *prompt) +{ + char *pass = read_passphrase(prompt); + + if (!isatty(STDIN_FILENO)) + return pass; + + char *pass2 = read_passphrase("Enter same passphrase again: "); + + if (strcmp(pass, pass2)) { + memzero_explicit(pass, strlen(pass)); + memzero_explicit(pass2, strlen(pass2)); + die("Passphrases do not match"); + } + + memzero_explicit(pass2, strlen(pass2)); + free(pass2); + + return pass; +} + +struct bch_key derive_passphrase(struct bch_sb_field_crypt *crypt, + const char *passphrase) { const unsigned char salt[] = "bcache"; + struct bch_key key; int ret; switch (BCH_CRYPT_KDF_TYPE(crypt)) { @@ -68,35 +89,49 @@ void derive_passphrase(struct bch_sb_field_crypt *crypt, 1ULL << BCH_KDF_SCRYPT_N(crypt), 1ULL << BCH_KDF_SCRYPT_R(crypt), 1ULL << BCH_KDF_SCRYPT_P(crypt), - (void *) key, sizeof(*key)); + (void *) &key, sizeof(key)); if (ret) die("scrypt error: %i", ret); break; default: die("unknown kdf type %llu", BCH_CRYPT_KDF_TYPE(crypt)); } + + return key; } -void bch2_add_key(struct bch_sb *sb, const char *passphrase) +void bch2_passphrase_check(struct bch_sb *sb, const char *passphrase, + struct bch_key *passphrase_key, + struct bch_encrypted_key *sb_key) { struct bch_sb_field_crypt *crypt = bch2_sb_get_crypt(sb); if (!crypt) die("filesystem is not encrypted"); - struct bch_encrypted_key sb_key = crypt->key; - if (!bch2_key_is_encrypted(&sb_key)) + *sb_key = crypt->key; + + if (!bch2_key_is_encrypted(sb_key)) die("filesystem does not have encryption key"); - struct bch_key passphrase_key; - derive_passphrase(crypt, &passphrase_key, passphrase); + *passphrase_key = derive_passphrase(crypt, passphrase); /* Check if the user supplied the correct passphrase: */ - if (bch2_chacha_encrypt_key(&passphrase_key, __bch2_sb_key_nonce(sb), - &sb_key, sizeof(sb_key))) + if (bch2_chacha_encrypt_key(passphrase_key, __bch2_sb_key_nonce(sb), + sb_key, sizeof(*sb_key))) die("error encrypting key"); - if (bch2_key_is_encrypted(&sb_key)) + if (bch2_key_is_encrypted(sb_key)) die("incorrect passphrase"); +} + +void bch2_add_key(struct bch_sb *sb, const char *passphrase) +{ + struct bch_key passphrase_key; + struct bch_encrypted_key sb_key; + + bch2_passphrase_check(sb, passphrase, + &passphrase_key, + &sb_key); char uuid[40]; uuid_unparse_lower(sb->user_uuid.b, uuid); @@ -125,14 +160,13 @@ void bch_sb_crypt_init(struct bch_sb *sb, get_random_bytes(&crypt->key.key, sizeof(crypt->key.key)); if (passphrase) { - struct bch_key passphrase_key; SET_BCH_CRYPT_KDF_TYPE(crypt, BCH_KDF_SCRYPT); SET_BCH_KDF_SCRYPT_N(crypt, ilog2(SCRYPT_N)); SET_BCH_KDF_SCRYPT_R(crypt, ilog2(SCRYPT_r)); SET_BCH_KDF_SCRYPT_P(crypt, ilog2(SCRYPT_p)); - derive_passphrase(crypt, &passphrase_key, passphrase); + struct bch_key passphrase_key = derive_passphrase(crypt, passphrase); assert(!bch2_key_is_encrypted(&crypt->key)); diff --git a/crypto.h b/crypto.h index 1e66a433..d6c20619 100644 --- a/crypto.h +++ b/crypto.h @@ -6,10 +6,14 @@ struct bch_sb; struct bch_sb_field_crypt; struct bch_key; +struct bch_encrypted_key; char *read_passphrase(const char *); -void derive_passphrase(struct bch_sb_field_crypt *, - struct bch_key *, const char *); +char *read_passphrase_twice(const char *); + +struct bch_key derive_passphrase(struct bch_sb_field_crypt *, const char *); +void bch2_passphrase_check(struct bch_sb *, const char *, + struct bch_key *, struct bch_encrypted_key *); void bch2_add_key(struct bch_sb *, const char *); void bch_sb_crypt_init(struct bch_sb *sb, struct bch_sb_field_crypt *, const char *); diff --git a/libbcachefs.c b/libbcachefs.c index 56851a04..b9c3d57a 100644 --- a/libbcachefs.c +++ b/libbcachefs.c @@ -293,14 +293,6 @@ struct bch_sb *__bch2_super_read(int fd, u64 sector) return ret; } -struct bch_sb *bch2_super_read(const char *path) -{ - int fd = xopen(path, O_RDONLY); - struct bch_sb *sb = __bch2_super_read(fd, BCH_SB_SECTOR); - close(fd); - return sb; -} - static unsigned get_dev_has_data(struct bch_sb *sb, unsigned dev) { struct bch_sb_field_replicas *replicas; diff --git a/libbcachefs.h b/libbcachefs.h index 21dc8598..bea80bb9 100644 --- a/libbcachefs.h +++ b/libbcachefs.h @@ -73,7 +73,6 @@ struct bch_sb *bch2_format(struct format_opts, struct dev_opts *, size_t); void bch2_super_write(int, struct bch_sb *); struct bch_sb *__bch2_super_read(int, u64); -struct bch_sb *bch2_super_read(const char *); void bch2_super_print(struct bch_sb *, int);