X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Fllite%2Fcrypto.c;h=cc80aea8cfbd3ec1641aa944cb59ff10f683c37f;hb=fdbf2ffd41fa5660782d5ca8489ec2eb644c8113;hp=110928abdd878c8b6c7c2a11881e538b86606fd8;hpb=40d91eafe257fb407d27c54cd2f7ae9961672f60;p=fs%2Flustre-release.git diff --git a/lustre/llite/crypto.c b/lustre/llite/crypto.c index 110928a..cc80aea 100644 --- a/lustre/llite/crypto.c +++ b/lustre/llite/crypto.c @@ -29,22 +29,34 @@ #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; + struct dentry *dentry = d_find_any_alias(inode); + struct lu_env *env; + __u16 refcheck; int rc; - if (hlist_empty(&inode->i_dentry)) - return -ENODATA; + env = cl_env_get(&refcheck); + if (IS_ERR(env)) + return PTR_ERR(env); - hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { - break; - } + /* Set lcc_getencctx=1 to allow this thread to read + * LL_XATTR_NAME_ENCRYPTION_CONTEXT xattr, as requested by llcrypt. + */ + ll_cl_add(inode, env, NULL, LCC_RW); + ll_env_info(env)->lti_io_ctx.lcc_getencctx = 1; rc = ll_vfs_getxattr(dentry, inode, LL_XATTR_NAME_ENCRYPTION_CONTEXT, ctx, len); + ll_cl_remove(inode, env); + cl_env_put(env, &refcheck); + + if (dentry) + dput(dentry); + /* used as encryption unit size */ if (S_ISREG(inode->i_mode)) inode->i_blkbits = LUSTRE_ENCRYPTION_BLOCKBITS; @@ -106,10 +118,11 @@ static int ll_set_context(struct inode *inode, const void *ctx, size_t len, } /* Encrypting the root directory is not allowed */ - if (inode->i_ino == inode->i_sb->s_root->d_inode->i_ino) + 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); if (rc) @@ -118,15 +131,43 @@ static int ll_set_context(struct inode *inode, const void *ctx, size_t len, return ll_set_encflags(inode, (void *)ctx, len, false); } -inline void llcrypt_free_ctx(void *encctx, __u32 size) +/** + * 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_FILE_ENC. + * 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_FILE_ENC) == O_FILE_ENC && + filp->f_flags & O_DIRECT) + /* allow file open with O_FILE_ENC flag when we have O_DIRECT */ + rc = 0; + + return rc; +} + +void llcrypt_free_ctx(void *encctx, __u32 size) { if (encctx) OBD_FREE(encctx, size); } -inline bool ll_sbi_has_test_dummy_encryption(struct ll_sb_info *sbi) +bool ll_sbi_has_test_dummy_encryption(struct ll_sb_info *sbi) { - return unlikely(sbi->ll_flags & LL_SBI_TEST_DUMMY_ENCRYPTION); + return unlikely(test_bit(LL_SBI_TEST_DUMMY_ENCRYPTION, sbi->ll_flags)); } static bool ll_dummy_context(struct inode *inode) @@ -136,18 +177,19 @@ static bool ll_dummy_context(struct inode *inode) return sbi ? ll_sbi_has_test_dummy_encryption(sbi) : false; } -inline bool ll_sbi_has_encrypt(struct ll_sb_info *sbi) +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); } -inline void ll_sbi_set_encrypt(struct ll_sb_info *sbi, bool set) +void ll_sbi_set_encrypt(struct ll_sb_info *sbi, bool set) { - if (set) - sbi->ll_flags |= LL_SBI_ENCRYPT; - else - sbi->ll_flags &= - ~(LL_SBI_ENCRYPT | LL_SBI_TEST_DUMMY_ENCRYPTION); + 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); + } } static bool ll_empty_dir(struct inode *inode) @@ -162,6 +204,244 @@ static bool ll_empty_dir(struct inode *inode) return true; } +/** + * 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_has_encryption_key(dir) && + iname->name[0] == '_') + 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; + } + rc = llcrypt_setup_filename(dir, &dname, lookup, fname); + if (rc == -ENOENT && lookup && + !llcrypt_has_encryption_key(dir) && + unlikely(filename_is_volatile(iname->name, iname->len, NULL))) { + /* 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; + } + if (rc) + return rc; + + 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->crypto_buf.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 = LLCRYPT_FNAME_DIGEST_SIZE; + } + 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; + } + + return rc; + +out_free: + llcrypt_free_filename(fname); + return rc; +} + +/** + * 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 > LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE && + !llcrypt_has_encryption_key(inode) && + likely(llcrypt_policy_has_filename_enc(inode))) { + 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_FNAME_DIGEST(lltr.name, lltr.len), + LLCRYPT_FNAME_DIGEST_SIZE); + + lltr.name = (char *)&digest; + lltr.len = sizeof(digest); + + oname->name[0] = '_'; + 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; +} + +/* 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 ll_revalidate_d_crypto(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 (!(dentry->d_flags & DCACHE_ENCRYPTED_NAME)) + 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_get_encryption_info(d_inode(dir)); + valid = !llcrypt_has_encryption_key(d_inode(dir)); + dput(dir); + + if (err < 0) + return err; + + return valid; +} + const struct llcrypt_operations lustre_cryptops = { .key_prefix = "lustre:", .get_context = ll_get_context, @@ -177,22 +457,53 @@ int ll_set_encflags(struct inode *inode, void *encctx, __u32 encctxlen, return 0; } -inline void llcrypt_free_ctx(void *encctx, __u32 size) +int ll_file_open_encrypt(struct inode *inode, struct file *filp) { + return llcrypt_file_open(inode, filp); } -inline bool ll_sbi_has_test_dummy_encryption(struct ll_sb_info *sbi) +void llcrypt_free_ctx(void *encctx, __u32 size) +{ +} + +bool ll_sbi_has_test_dummy_encryption(struct ll_sb_info *sbi) { return false; } -inline bool ll_sbi_has_encrypt(struct ll_sb_info *sbi) +bool ll_sbi_has_encrypt(struct ll_sb_info *sbi) { return false; } -inline void ll_sbi_set_encrypt(struct ll_sb_info *sbi, bool set) +void ll_sbi_set_encrypt(struct ll_sb_info *sbi, bool set) +{ +} + +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 ll_revalidate_d_crypto(struct dentry *dentry, unsigned int flags) { + return 1; } #endif