X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=lustre%2Fllite%2Fcrypto.c;h=66627595d58d7c7e1521cbe0c79638bbb8256f85;hp=3f69d390e6450e97f370ec897901de72b1754241;hb=HEAD;hpb=76b7ce7c6faa1860fe489c7e5e2ee302d6af6bcc diff --git a/lustre/llite/crypto.c b/lustre/llite/crypto.c index 3f69d39..728fa81 100644 --- a/lustre/llite/crypto.c +++ b/lustre/llite/crypto.c @@ -29,16 +29,17 @@ #include "llite_internal.h" #ifdef HAVE_LUSTRE_CRYPTO +#include static int ll_get_context(struct inode *inode, void *ctx, size_t len) { - struct dentry *dentry = d_find_any_alias(inode); int rc; - rc = ll_vfs_getxattr(dentry, inode, LL_XATTR_NAME_ENCRYPTION_CONTEXT, - ctx, len); - if (dentry) - dput(dentry); + /* Get enc context xattr directly instead of going through the VFS, + * as there is no xattr handler for "encryption.". + */ + rc = ll_xattr_list(inode, xattr_for_enc(inode), + XATTR_ENCRYPTION_T, ctx, len, OBD_MD_FLXATTR); /* used as encryption unit size */ if (S_ISREG(inode->i_mode)) @@ -60,12 +61,12 @@ int ll_set_encflags(struct inode *inode, void *encctx, __u32 encctxlen, if (encctx && encctxlen) rc = ll_xattr_cache_insert(inode, - LL_XATTR_NAME_ENCRYPTION_CONTEXT, + xattr_for_enc(inode), encctx, encctxlen); if (rc) return rc; - return preload ? llcrypt_get_encryption_info(inode) : 0; + return preload ? llcrypt_prepare_readdir(inode) : 0; } /* ll_set_context has 2 distinct behaviors, depending on the value of inode @@ -75,15 +76,15 @@ int ll_set_encflags(struct inode *inode, void *encctx, __u32 encctxlen, * op_data, so that it will be sent along to the server with the request that * the caller is preparing, thus saving a setxattr request. * - inode is not NULL: - * normal case in which passed fs_data is a struct dentry *, letting proceed - * with setxattr operation. + * normal case, letting proceed with setxattr operation. * This use case should only be used when explicitly setting a new encryption * policy on an existing, empty directory. */ static int ll_set_context(struct inode *inode, const void *ctx, size_t len, void *fs_data) { - struct dentry *dentry; + struct ptlrpc_request *req = NULL; + struct ll_sb_info *sbi; int rc; if (inode == NULL) { @@ -104,46 +105,113 @@ static int ll_set_context(struct inode *inode, const void *ctx, size_t len, if (is_root_inode(inode)) return -EPERM; - dentry = (struct dentry *)fs_data; - set_bit(LLIF_SET_ENC_CTX, &ll_i2info(inode)->lli_flags); - rc = ll_vfs_setxattr(dentry, inode, LL_XATTR_NAME_ENCRYPTION_CONTEXT, - ctx, len, XATTR_CREATE); + sbi = ll_i2sbi(inode); + /* Send setxattr request to lower layers directly instead of going + * through the VFS, as there is no xattr handler for "encryption.". + */ + rc = md_setxattr(sbi->ll_md_exp, ll_inode2fid(inode), + OBD_MD_FLXATTR, xattr_for_enc(inode), + ctx, len, XATTR_CREATE, ll_i2suppgid(inode), &req); if (rc) return rc; + ptlrpc_req_finished(req); return ll_set_encflags(inode, (void *)ctx, len, false); } +/** + * ll_file_open_encrypt() - overlay to llcrypt_file_open + * @inode: the inode being opened + * @filp: the struct file being set up + * + * This overlay function is necessary to handle encrypted file open without + * the key. We allow this access pattern to applications that know what they + * are doing, by using the specific flag O_CIPHERTEXT. + * This flag is only compatible with O_DIRECT IOs, to make sure ciphertext + * data is wiped from page cache once IOs are finished. + */ +int ll_file_open_encrypt(struct inode *inode, struct file *filp) +{ + int rc; + + rc = llcrypt_file_open(inode, filp); + if (likely(rc != -ENOKEY)) + return rc; + + if (rc == -ENOKEY && + (filp->f_flags & O_CIPHERTEXT) == O_CIPHERTEXT && + filp->f_flags & O_DIRECT) + /* allow open with O_CIPHERTEXT flag when we have O_DIRECT */ + rc = 0; + + return rc; +} + void llcrypt_free_ctx(void *encctx, __u32 size) { if (encctx) OBD_FREE(encctx, size); } -bool ll_sbi_has_test_dummy_encryption(struct ll_sb_info *sbi) +#ifdef HAVE_FSCRYPT_DUMMY_CONTEXT_ENABLED +bool ll_sb_has_test_dummy_encryption(struct super_block *sb) { - return unlikely(sbi->ll_flags & LL_SBI_TEST_DUMMY_ENCRYPTION); + struct ll_sb_info *sbi = s2lsi(sb)->lsi_llsbi; + + return sbi ? + unlikely(test_bit(LL_SBI_TEST_DUMMY_ENCRYPTION, sbi->ll_flags)) : + false; } static bool ll_dummy_context(struct inode *inode) { - struct ll_sb_info *sbi = ll_i2sbi(inode); + return ll_sb_has_test_dummy_encryption(inode->i_sb); +} +#else +static const union llcrypt_policy * +ll_get_dummy_policy(struct super_block *sb) +{ + struct lustre_sb_info *lsi = s2lsi(sb); + +#ifdef HAVE_FSCRYPT_DUMMY_POLICY + return lsi ? lsi->lsi_dummy_enc_policy.policy : NULL; +#else + return lsi ? lsi->lsi_dummy_enc_policy.ctx : NULL; +#endif +} - return sbi ? ll_sbi_has_test_dummy_encryption(sbi) : false; +bool ll_sb_has_test_dummy_encryption(struct super_block *sb) +{ + return ll_get_dummy_policy(sb) != NULL; } +#endif bool ll_sbi_has_encrypt(struct ll_sb_info *sbi) { - return sbi->ll_flags & LL_SBI_ENCRYPT; + return test_bit(LL_SBI_ENCRYPT, sbi->ll_flags); } void ll_sbi_set_encrypt(struct ll_sb_info *sbi, bool set) { + if (set) { + set_bit(LL_SBI_ENCRYPT, sbi->ll_flags); + } else { + clear_bit(LL_SBI_ENCRYPT, sbi->ll_flags); + clear_bit(LL_SBI_TEST_DUMMY_ENCRYPTION, sbi->ll_flags); + } +} + +bool ll_sbi_has_name_encrypt(struct ll_sb_info *sbi) +{ + return test_bit(LL_SBI_ENCRYPT_NAME, sbi->ll_flags); +} + +void ll_sbi_set_name_encrypt(struct ll_sb_info *sbi, bool set) +{ if (set) - sbi->ll_flags |= LL_SBI_ENCRYPT; + set_bit(LL_SBI_ENCRYPT_NAME, sbi->ll_flags); else - sbi->ll_flags &= - ~(LL_SBI_ENCRYPT | LL_SBI_TEST_DUMMY_ENCRYPTION); + clear_bit(LL_SBI_ENCRYPT_NAME, sbi->ll_flags); } static bool ll_empty_dir(struct inode *inode) @@ -158,11 +226,390 @@ static bool ll_empty_dir(struct inode *inode) return true; } +static int ll_digest_long_name(struct inode *dir, struct llcrypt_name *fname, + struct lu_fid *fid, int digested) +{ + int rc = 0; + + if (digested) { + /* Without the key, for long names user should have struct + * ll_digest_filename representation of the dentry instead of + * the name. So make sure it is valid, return fid and put + * excerpt of cipher text name in disk_name. + */ + struct ll_digest_filename *digest; + + if (fname->crypto_buf.len < sizeof(struct ll_digest_filename)) { + rc = -EINVAL; + goto out_free; + } + digest = (struct ll_digest_filename *)fname->disk_name.name; + *fid = digest->ldf_fid; + if (!fid_is_sane(fid)) { + rc = -EINVAL; + goto out_free; + } + fname->disk_name.name = digest->ldf_excerpt; + fname->disk_name.len = sizeof(digest->ldf_excerpt); + } + if (IS_ENCRYPTED(dir) && + !name_is_dot_or_dotdot(fname->disk_name.name, + fname->disk_name.len)) { + int presented_len = critical_chars(fname->disk_name.name, + fname->disk_name.len); + char *buf; + + buf = kmalloc(presented_len + 1, GFP_NOFS); + if (!buf) { + rc = -ENOMEM; + goto out_free; + } + + if (presented_len == fname->disk_name.len) + memcpy(buf, fname->disk_name.name, presented_len); + else + critical_encode(fname->disk_name.name, + fname->disk_name.len, buf); + buf[presented_len] = '\0'; + kfree(fname->crypto_buf.name); + fname->crypto_buf.name = buf; + fname->crypto_buf.len = presented_len; + fname->disk_name.name = fname->crypto_buf.name; + fname->disk_name.len = fname->crypto_buf.len; + } +out_free: + if (rc < 0) + llcrypt_free_filename(fname); + + return rc; +} + +/** + * ll_prepare_lookup() - overlay to llcrypt_prepare_lookup + * @dir: the directory that will be searched + * @de: the dentry contain the user-provided filename being searched for + * @fname: the filename information to be filled in + * @fid: fid retrieved from user-provided filename + * + * This overlay function is necessary to properly encode @fname after + * encryption, as it will be sent over the wire. + * This overlay function is also necessary to handle the case of operations + * carried out without the key. Normally llcrypt makes use of digested names in + * that case. Having a digested name works for local file systems that can call + * llcrypt_match_name(), but Lustre server side is not aware of encryption. + * FID and name hash can then easily be extracted and put into the + * requests sent to servers. + */ +int ll_prepare_lookup(struct inode *dir, struct dentry *de, + struct llcrypt_name *fname, struct lu_fid *fid) +{ + struct qstr iname = QSTR_INIT(de->d_name.name, de->d_name.len); + int digested = 0; + int rc; + + if (fid && IS_ENCRYPTED(dir) && llcrypt_policy_has_filename_enc(dir) && + !llcrypt_has_encryption_key(dir)) { + struct lustre_sb_info *lsi = s2lsi(dir->i_sb); + + if ((!(lsi->lsi_flags & LSI_FILENAME_ENC_B64_OLD_CLI) && + iname.name[0] == LLCRYPT_DIGESTED_CHAR) || + ((lsi->lsi_flags & LSI_FILENAME_ENC_B64_OLD_CLI) && + iname.name[0] == LLCRYPT_DIGESTED_CHAR_OLD)) + digested = 1; + } + + iname.name += digested; + iname.len -= digested; + + if (fid) { + fid->f_seq = 0; + fid->f_oid = 0; + fid->f_ver = 0; + } + if (unlikely(filename_is_volatile(iname.name, + iname.len, NULL))) { + /* keep volatile name as-is, matters for server side */ + memset(fname, 0, sizeof(struct llcrypt_name)); + fname->disk_name.name = (unsigned char *)iname.name; + fname->disk_name.len = iname.len; + rc = 0; + } else { + /* We should use ll_prepare_lookup() but Lustre handles the + * digested form its own way, incompatible with llcrypt's + * digested form. + */ + rc = llcrypt_setup_filename(dir, &iname, 1, fname); + if ((rc == 0 || rc == -ENOENT) && +#if defined(HAVE_FSCRYPT_NOKEY_NAME) && !defined(CONFIG_LL_ENCRYPTION) + fname->is_nokey_name) { +#else + fname->is_ciphertext_name) { +#endif + spin_lock(&de->d_lock); + de->d_flags |= DCACHE_NOKEY_NAME; + spin_unlock(&de->d_lock); + } + } + if (rc == -ENOENT) { + if (((is_root_inode(dir) && + iname.len == strlen(dot_fscrypt_name) && + strncmp(iname.name, dot_fscrypt_name, iname.len) == 0) || + (!llcrypt_has_encryption_key(dir) && + unlikely(filename_is_volatile(iname.name, + iname.len, NULL))))) { + /* In case of subdir mount of an encrypted directory, + * we allow lookup of /.fscrypt directory. + */ + /* For purpose of migration or mirroring without enc key + * we allow lookup of volatile file without enc context. + */ + memset(fname, 0, sizeof(struct llcrypt_name)); + fname->disk_name.name = (unsigned char *)iname.name; + fname->disk_name.len = iname.len; + rc = 0; + } else if (!llcrypt_has_encryption_key(dir)) { + rc = -ENOKEY; + } + } + if (rc) + return rc; + + return ll_digest_long_name(dir, fname, fid, digested); +} + +/** + * ll_setup_filename() - overlay to llcrypt_setup_filename + * @dir: the directory that will be searched + * @iname: the user-provided filename being searched for + * @lookup: 1 if we're allowed to proceed without the key because it's + * ->lookup() or we're finding the dir_entry for deletion; 0 if we cannot + * proceed without the key because we're going to create the dir_entry. + * @fname: the filename information to be filled in + * @fid: fid retrieved from user-provided filename + * + * This overlay function is necessary to properly encode @fname after + * encryption, as it will be sent over the wire. + * This overlay function is also necessary to handle the case of operations + * carried out without the key. Normally llcrypt makes use of digested names in + * that case. Having a digested name works for local file systems that can call + * llcrypt_match_name(), but Lustre server side is not aware of encryption. + * So for keyless @lookup operations on long names, for Lustre we choose to + * present to users the encoded struct ll_digest_filename, instead of a digested + * name. FID and name hash can then easily be extracted and put into the + * requests sent to servers. + */ +int ll_setup_filename(struct inode *dir, const struct qstr *iname, + int lookup, struct llcrypt_name *fname, + struct lu_fid *fid) +{ + int digested = 0; + struct qstr dname; + int rc; + + if (fid && IS_ENCRYPTED(dir) && llcrypt_policy_has_filename_enc(dir) && + !llcrypt_has_encryption_key(dir)) { + struct lustre_sb_info *lsi = s2lsi(dir->i_sb); + + if ((!(lsi->lsi_flags & LSI_FILENAME_ENC_B64_OLD_CLI) && + iname->name[0] == LLCRYPT_DIGESTED_CHAR) || + ((lsi->lsi_flags & LSI_FILENAME_ENC_B64_OLD_CLI) && + iname->name[0] == LLCRYPT_DIGESTED_CHAR_OLD)) + digested = 1; + } + + dname.name = iname->name + digested; + dname.len = iname->len - digested; + + if (fid) { + fid->f_seq = 0; + fid->f_oid = 0; + fid->f_ver = 0; + } + if (unlikely(filename_is_volatile(iname->name, + iname->len, NULL))) { + /* keep volatile name as-is, matters for server side */ + memset(fname, 0, sizeof(struct llcrypt_name)); + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + rc = 0; + } else { + rc = llcrypt_setup_filename(dir, &dname, lookup, fname); + } + if (rc == -ENOENT && lookup) { + if (((is_root_inode(dir) && + iname->len == strlen(dot_fscrypt_name) && + strncmp(iname->name, dot_fscrypt_name, iname->len) == 0) || + (!llcrypt_has_encryption_key(dir) && + unlikely(filename_is_volatile(iname->name, + iname->len, NULL))))) { + /* In case of subdir mount of an encrypted directory, + * we allow lookup of /.fscrypt directory. + */ + /* For purpose of migration or mirroring without enc key + * we allow lookup of volatile file without enc context. + */ + memset(fname, 0, sizeof(struct llcrypt_name)); + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + rc = 0; + } else if (!llcrypt_has_encryption_key(dir)) { + rc = -ENOKEY; + } + } + if (rc) + return rc; + + return ll_digest_long_name(dir, fname, fid, digested); +} + +/** + * ll_fname_disk_to_usr() - overlay to llcrypt_fname_disk_to_usr + * @inode: the inode to convert name + * @hash: major hash for inode + * @minor_hash: minor hash for inode + * @iname: the user-provided filename needing conversion + * @oname: the filename information to be filled in + * @fid: the user-provided fid for filename + * + * The caller must have allocated sufficient memory for the @oname string. + * + * This overlay function is necessary to properly decode @iname before + * decryption, as it comes from the wire. + * This overlay function is also necessary to handle the case of operations + * carried out without the key. Normally llcrypt makes use of digested names in + * that case. Having a digested name works for local file systems that can call + * llcrypt_match_name(), but Lustre server side is not aware of encryption. + * So for keyless @lookup operations on long names, for Lustre we choose to + * present to users the encoded struct ll_digest_filename, instead of a digested + * name. FID and name hash can then easily be extracted and put into the + * requests sent to servers. + */ +int ll_fname_disk_to_usr(struct inode *inode, + u32 hash, u32 minor_hash, + struct llcrypt_str *iname, struct llcrypt_str *oname, + struct lu_fid *fid) +{ + struct llcrypt_str lltr = LLTR_INIT(iname->name, iname->len); + struct ll_digest_filename digest; + int digested = 0; + char *buf = NULL; + int rc; + + if (IS_ENCRYPTED(inode)) { + if (!name_is_dot_or_dotdot(lltr.name, lltr.len) && + strnchr(lltr.name, lltr.len, '=')) { + /* Only proceed to critical decode if + * iname contains espace char '='. + */ + int len = lltr.len; + + buf = kmalloc(len, GFP_NOFS); + if (!buf) + return -ENOMEM; + + len = critical_decode(lltr.name, len, buf); + lltr.name = buf; + lltr.len = len; + } + if (lltr.len > LL_CRYPTO_BLOCK_SIZE * 2 && + !llcrypt_has_encryption_key(inode) && + llcrypt_policy_has_filename_enc(inode)) { + struct lustre_sb_info *lsi = s2lsi(inode->i_sb); + + digested = 1; + /* Without the key for long names, set the dentry name + * to the representing struct ll_digest_filename. It + * will be encoded by llcrypt for display, and will + * enable further lookup requests. + */ + if (!fid) + return -EINVAL; + digest.ldf_fid = *fid; + memcpy(digest.ldf_excerpt, + LLCRYPT_EXTRACT_DIGEST(lltr.name, lltr.len), + sizeof(digest.ldf_excerpt)); + + lltr.name = (char *)&digest; + lltr.len = sizeof(digest); + + if (!(lsi->lsi_flags & LSI_FILENAME_ENC_B64_OLD_CLI)) + oname->name[0] = LLCRYPT_DIGESTED_CHAR; + else + oname->name[0] = LLCRYPT_DIGESTED_CHAR_OLD; + oname->name = oname->name + 1; + oname->len--; + } + } + + rc = llcrypt_fname_disk_to_usr(inode, hash, minor_hash, &lltr, oname); + + kfree(buf); + oname->name = oname->name - digested; + oname->len = oname->len + digested; + + return rc; +} + +#if !defined(HAVE_FSCRYPT_D_REVALIDATE) || defined(CONFIG_LL_ENCRYPTION) +/* Copied from llcrypt_d_revalidate, as it is not exported */ +/* + * Validate dentries in encrypted directories to make sure we aren't potentially + * caching stale dentries after a key has been added. + */ +int llcrypt_d_revalidate(struct dentry *dentry, unsigned int flags) +{ + struct dentry *dir; + int err; + int valid; + + /* + * Plaintext names are always valid, since llcrypt doesn't support + * reverting to ciphertext names without evicting the directory's inode + * -- which implies eviction of the dentries in the directory. + */ + if (!llcrypt_is_nokey_name(dentry)) + return 1; + + /* + * Ciphertext name; valid if the directory's key is still unavailable. + * + * Although llcrypt forbids rename() on ciphertext names, we still must + * use dget_parent() here rather than use ->d_parent directly. That's + * because a corrupted fs image may contain directory hard links, which + * the VFS handles by moving the directory's dentry tree in the dcache + * each time ->lookup() finds the directory and it already has a dentry + * elsewhere. Thus ->d_parent can be changing, and we must safely grab + * a reference to some ->d_parent to prevent it from being freed. + */ + + if (flags & LOOKUP_RCU) + return -ECHILD; + + dir = dget_parent(dentry); + err = llcrypt_prepare_readdir(d_inode(dir)); + valid = !llcrypt_has_encryption_key(d_inode(dir)); + dput(dir); + + if (err < 0) + return err; + + return valid; +} +#endif /* !HAVE_FSCRYPT_D_REVALIDATE || CONFIG_LL_ENCRYPTION */ + const struct llcrypt_operations lustre_cryptops = { .key_prefix = "lustre:", .get_context = ll_get_context, .set_context = ll_set_context, +#ifdef HAVE_FSCRYPT_DUMMY_CONTEXT_ENABLED .dummy_context = ll_dummy_context, +#else +#ifdef HAVE_FSCRYPT_DUMMY_POLICY + .get_dummy_policy = ll_get_dummy_policy, +#else + .get_dummy_context = ll_get_dummy_policy, +#endif +#endif /* !HAVE_FSCRYPT_DUMMY_CONTEXT_ENABLED */ .empty_dir = ll_empty_dir, .max_namelen = NAME_MAX, }; @@ -173,11 +620,16 @@ int ll_set_encflags(struct inode *inode, void *encctx, __u32 encctxlen, return 0; } +int ll_file_open_encrypt(struct inode *inode, struct file *filp) +{ + return llcrypt_file_open(inode, filp); +} + void llcrypt_free_ctx(void *encctx, __u32 size) { } -bool ll_sbi_has_test_dummy_encryption(struct ll_sb_info *sbi) +bool ll_sb_has_test_dummy_encryption(struct super_block *sb) { return false; } @@ -190,5 +642,54 @@ bool ll_sbi_has_encrypt(struct ll_sb_info *sbi) void ll_sbi_set_encrypt(struct ll_sb_info *sbi, bool set) { } + +bool ll_sbi_has_name_encrypt(struct ll_sb_info *sbi) +{ + return false; +} + +void ll_sbi_set_name_encrypt(struct ll_sb_info *sbi, bool set) +{ +} + +int ll_prepare_lookup(struct inode *dir, struct dentry *de, + struct llcrypt_name *fname, struct lu_fid *fid) +{ + const struct qstr *iname = &de->d_name; + + if (fid) { + fid->f_seq = 0; + fid->f_oid = 0; + fid->f_ver = 0; + } + + return llcrypt_setup_filename(dir, iname, 1, fname); +} + +int ll_setup_filename(struct inode *dir, const struct qstr *iname, + int lookup, struct llcrypt_name *fname, + struct lu_fid *fid) +{ + if (fid) { + fid->f_seq = 0; + fid->f_oid = 0; + fid->f_ver = 0; + } + + return llcrypt_setup_filename(dir, iname, lookup, fname); +} + +int ll_fname_disk_to_usr(struct inode *inode, + u32 hash, u32 minor_hash, + struct llcrypt_str *iname, struct llcrypt_str *oname, + struct lu_fid *fid) +{ + return llcrypt_fname_disk_to_usr(inode, hash, minor_hash, iname, oname); +} + +int llcrypt_d_revalidate(struct dentry *dentry, unsigned int flags) +{ + return 1; +} #endif