X-Git-Url: http://pilppa.org/gitweb/?a=blobdiff_plain;f=fs%2Fecryptfs%2Fmain.c;h=0249aa4ae181886cd2a26921a465feb5f8d27d41;hb=197b12d6796a3bca187f22a8978a33d51e2bcd79;hp=a98497264fe8594d696ccaf9ffec4595133b9f87;hpb=973ed7c49a5c2271a10ce54ac06bba5617fca3a8;p=linux-2.6-omap-h63xx.git diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index a98497264fe..0249aa4ae18 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -98,6 +97,67 @@ void __ecryptfs_printk(const char *fmt, ...) va_end(args); } +/** + * ecryptfs_init_persistent_file + * @ecryptfs_dentry: Fully initialized eCryptfs dentry object, with + * the lower dentry and the lower mount set + * + * eCryptfs only ever keeps a single open file for every lower + * inode. All I/O operations to the lower inode occur through that + * file. When the first eCryptfs dentry that interposes with the first + * lower dentry for that inode is created, this function creates the + * persistent file struct and associates it with the eCryptfs + * inode. When the eCryptfs inode is destroyed, the file is closed. + * + * The persistent file will be opened with read/write permissions, if + * possible. Otherwise, it is opened read-only. + * + * This function does nothing if a lower persistent file is already + * associated with the eCryptfs inode. + * + * Returns zero on success; non-zero otherwise + */ +int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry) +{ + struct ecryptfs_inode_info *inode_info = + ecryptfs_inode_to_private(ecryptfs_dentry->d_inode); + int rc = 0; + + mutex_lock(&inode_info->lower_file_mutex); + if (!inode_info->lower_file) { + struct dentry *lower_dentry; + struct vfsmount *lower_mnt = + ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); + + lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); + /* Corresponding dput() and mntput() are done when the + * persistent file is fput() when the eCryptfs inode + * is destroyed. */ + dget(lower_dentry); + mntget(lower_mnt); + inode_info->lower_file = dentry_open(lower_dentry, + lower_mnt, + (O_RDWR | O_LARGEFILE)); + if (IS_ERR(inode_info->lower_file)) { + dget(lower_dentry); + mntget(lower_mnt); + inode_info->lower_file = dentry_open(lower_dentry, + lower_mnt, + (O_RDONLY + | O_LARGEFILE)); + } + if (IS_ERR(inode_info->lower_file)) { + printk(KERN_ERR "Error opening lower persistent file " + "for lower_dentry [0x%p] and lower_mnt [0x%p]\n", + lower_dentry, lower_mnt); + rc = PTR_ERR(inode_info->lower_file); + inode_info->lower_file = NULL; + } + } + mutex_unlock(&inode_info->lower_file_mutex); + return rc; +} + /** * ecryptfs_interpose * @lower_dentry: Existing dentry in the lower filesystem @@ -155,6 +215,13 @@ int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry, /* This size will be overwritten for real files w/ headers and * other metadata */ fsstack_copy_inode_size(inode, lower_inode); + rc = ecryptfs_init_persistent_file(dentry); + if (rc) { + printk(KERN_ERR "%s: Error attempting to initialize the " + "persistent file for the dentry with name [%s]; " + "rc = [%d]\n", __FUNCTION__, dentry->d_name.name, rc); + goto out; + } out: return rc; } @@ -179,38 +246,41 @@ static match_table_t tokens = { {ecryptfs_opt_err, NULL} }; -/** - * ecryptfs_verify_version - * @version: The version number to confirm - * - * Returns zero on good version; non-zero otherwise - */ -static int ecryptfs_verify_version(u16 version) +static int ecryptfs_init_global_auth_toks( + struct ecryptfs_mount_crypt_stat *mount_crypt_stat) { + struct ecryptfs_global_auth_tok *global_auth_tok; int rc = 0; - unsigned char major; - unsigned char minor; - - major = ((version >> 8) & 0xFF); - minor = (version & 0xFF); - if (major != ECRYPTFS_VERSION_MAJOR) { - ecryptfs_printk(KERN_ERR, "Major version number mismatch. " - "Expected [%d]; got [%d]\n", - ECRYPTFS_VERSION_MAJOR, major); - rc = -EINVAL; - goto out; - } - if (minor != ECRYPTFS_VERSION_MINOR) { - ecryptfs_printk(KERN_ERR, "Minor version number mismatch. " - "Expected [%d]; got [%d]\n", - ECRYPTFS_VERSION_MINOR, minor); - rc = -EINVAL; - goto out; + + list_for_each_entry(global_auth_tok, + &mount_crypt_stat->global_auth_tok_list, + mount_crypt_stat_list) { + rc = ecryptfs_keyring_auth_tok_for_sig( + &global_auth_tok->global_auth_tok_key, + &global_auth_tok->global_auth_tok, + global_auth_tok->sig); + if (rc) { + printk(KERN_ERR "Could not find valid key in user " + "session keyring for sig specified in mount " + "option: [%s]\n", global_auth_tok->sig); + global_auth_tok->flags |= ECRYPTFS_AUTH_TOK_INVALID; + rc = 0; + } else + global_auth_tok->flags &= ~ECRYPTFS_AUTH_TOK_INVALID; } -out: return rc; } +static void ecryptfs_init_mount_crypt_stat( + struct ecryptfs_mount_crypt_stat *mount_crypt_stat) +{ + memset((void *)mount_crypt_stat, 0, + sizeof(struct ecryptfs_mount_crypt_stat)); + INIT_LIST_HEAD(&mount_crypt_stat->global_auth_tok_list); + mutex_init(&mount_crypt_stat->global_auth_tok_list_mutex); + mount_crypt_stat->flags |= ECRYPTFS_MOUNT_CRYPT_STAT_INITIALIZED; +} + /** * ecryptfs_parse_options * @sb: The ecryptfs super block @@ -238,14 +308,11 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options) int cipher_name_set = 0; int cipher_key_bytes; int cipher_key_bytes_set = 0; - struct key *auth_tok_key = NULL; - struct ecryptfs_auth_tok *auth_tok = NULL; struct ecryptfs_mount_crypt_stat *mount_crypt_stat = &ecryptfs_superblock_to_private(sb)->mount_crypt_stat; substring_t args[MAX_OPT_ARGS]; int token; char *sig_src; - char *sig_dst; char *debug_src; char *cipher_name_dst; char *cipher_name_src; @@ -256,6 +323,7 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options) rc = -EINVAL; goto out; } + ecryptfs_init_mount_crypt_stat(mount_crypt_stat); while ((p = strsep(&options, ",")) != NULL) { if (!*p) continue; @@ -264,14 +332,13 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options) case ecryptfs_opt_sig: case ecryptfs_opt_ecryptfs_sig: sig_src = args[0].from; - sig_dst = - mount_crypt_stat->global_auth_tok_sig; - memcpy(sig_dst, sig_src, ECRYPTFS_SIG_SIZE_HEX); - sig_dst[ECRYPTFS_SIG_SIZE_HEX] = '\0'; - ecryptfs_printk(KERN_DEBUG, - "The mount_crypt_stat " - "global_auth_tok_sig set to: " - "[%s]\n", sig_dst); + rc = ecryptfs_add_global_auth_tok(mount_crypt_stat, + sig_src); + if (rc) { + printk(KERN_ERR "Error attempting to register " + "global sig; rc = [%d]\n", rc); + goto out; + } sig_set = 1; break; case ecryptfs_opt_debug: @@ -333,12 +400,10 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options) p); } } - /* Do not support lack of mount-wide signature in 0.1 - * release */ if (!sig_set) { rc = -EINVAL; - ecryptfs_printk(KERN_ERR, "You must supply a valid " - "passphrase auth tok signature as a mount " + ecryptfs_printk(KERN_ERR, "You must supply at least one valid " + "auth tok signature as a mount " "parameter; see the eCryptfs README\n"); goto out; } @@ -358,55 +423,23 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options) if (!cipher_key_bytes_set) { mount_crypt_stat->global_default_cipher_key_size = 0; } - rc = ecryptfs_process_cipher( - &mount_crypt_stat->global_key_tfm, - mount_crypt_stat->global_default_cipher_name, - &mount_crypt_stat->global_default_cipher_key_size); + rc = ecryptfs_add_new_key_tfm( + NULL, mount_crypt_stat->global_default_cipher_name, + mount_crypt_stat->global_default_cipher_key_size); if (rc) { - printk(KERN_ERR "Error attempting to initialize cipher [%s] " - "with key size [%Zd] bytes; rc = [%d]\n", + printk(KERN_ERR "Error attempting to initialize cipher with " + "name = [%s] and key size = [%td]; rc = [%d]\n", mount_crypt_stat->global_default_cipher_name, mount_crypt_stat->global_default_cipher_key_size, rc); - mount_crypt_stat->global_key_tfm = NULL; - mount_crypt_stat->global_auth_tok_key = NULL; - rc = -EINVAL; - goto out; - } - mutex_init(&mount_crypt_stat->global_key_tfm_mutex); - ecryptfs_printk(KERN_DEBUG, "Requesting the key with description: " - "[%s]\n", mount_crypt_stat->global_auth_tok_sig); - /* The reference to this key is held until umount is done The - * call to key_put is done in ecryptfs_put_super() */ - auth_tok_key = request_key(&key_type_user, - mount_crypt_stat->global_auth_tok_sig, - NULL); - if (!auth_tok_key || IS_ERR(auth_tok_key)) { - ecryptfs_printk(KERN_ERR, "Could not find key with " - "description: [%s]\n", - mount_crypt_stat->global_auth_tok_sig); - process_request_key_err(PTR_ERR(auth_tok_key)); rc = -EINVAL; goto out; } - auth_tok = ecryptfs_get_key_payload_data(auth_tok_key); - if (ecryptfs_verify_version(auth_tok->version)) { - ecryptfs_printk(KERN_ERR, "Data structure version mismatch. " - "Userspace tools must match eCryptfs kernel " - "module with major version [%d] and minor " - "version [%d]\n", ECRYPTFS_VERSION_MAJOR, - ECRYPTFS_VERSION_MINOR); - rc = -EINVAL; - goto out; - } - if (auth_tok->token_type != ECRYPTFS_PASSWORD - && auth_tok->token_type != ECRYPTFS_PRIVATE_KEY) { - ecryptfs_printk(KERN_ERR, "Invalid auth_tok structure " - "returned from key query\n"); - rc = -EINVAL; - goto out; + rc = ecryptfs_init_global_auth_toks(mount_crypt_stat); + if (rc) { + printk(KERN_WARNING "One or more global auth toks could not " + "properly register; rc = [%d]\n", rc); } - mount_crypt_stat->global_auth_tok_key = auth_tok_key; - mount_crypt_stat->global_auth_tok = auth_tok; + rc = 0; out: return rc; } @@ -493,9 +526,11 @@ static int ecryptfs_read_super(struct super_block *sb, const char *dev_name) lower_mnt = nd.mnt; ecryptfs_set_superblock_lower(sb, lower_root->d_sb); sb->s_maxbytes = lower_root->d_sb->s_maxbytes; + sb->s_blocksize = lower_root->d_sb->s_blocksize; ecryptfs_set_dentry_lower(sb->s_root, lower_root); ecryptfs_set_dentry_lower_mnt(sb->s_root, lower_mnt); - if ((rc = ecryptfs_interpose(lower_root, sb->s_root, sb, 0))) + rc = ecryptfs_interpose(lower_root, sb->s_root, sb, 0); + if (rc) goto out_free; rc = 0; goto out; @@ -579,7 +614,7 @@ static struct file_system_type ecryptfs_fs_type = { * Initializes the ecryptfs_inode_info_cache when it is created */ static void -inode_info_init_once(void *vptr, struct kmem_cache *cachep, unsigned long flags) +inode_info_init_once(struct kmem_cache *cachep, void *vptr) { struct ecryptfs_inode_info *ei = (struct ecryptfs_inode_info *)vptr; @@ -590,7 +625,7 @@ static struct ecryptfs_cache_info { struct kmem_cache **cache; const char *name; size_t size; - void (*ctor)(void*, struct kmem_cache *, unsigned long); + void (*ctor)(struct kmem_cache *cache, void *obj); } ecryptfs_cache_infos[] = { { .cache = &ecryptfs_auth_tok_list_item_cache, @@ -638,16 +673,26 @@ static struct ecryptfs_cache_info { .name = "ecryptfs_xattr_cache", .size = PAGE_CACHE_SIZE, }, - { - .cache = &ecryptfs_lower_page_cache, - .name = "ecryptfs_lower_page_cache", - .size = PAGE_CACHE_SIZE, - }, { .cache = &ecryptfs_key_record_cache, .name = "ecryptfs_key_record_cache", .size = sizeof(struct ecryptfs_key_record), }, + { + .cache = &ecryptfs_key_sig_cache, + .name = "ecryptfs_key_sig_cache", + .size = sizeof(struct ecryptfs_key_sig), + }, + { + .cache = &ecryptfs_global_auth_tok_cache, + .name = "ecryptfs_global_auth_tok_cache", + .size = sizeof(struct ecryptfs_global_auth_tok), + }, + { + .cache = &ecryptfs_key_tfm_cache, + .name = "ecryptfs_key_tfm_cache", + .size = sizeof(struct ecryptfs_key_tfm), + }, }; static void ecryptfs_free_kmem_caches(void) @@ -689,125 +734,40 @@ static int ecryptfs_init_kmem_caches(void) return 0; } -struct ecryptfs_obj { - char *name; - struct list_head slot_list; - struct kobject kobj; -}; - -struct ecryptfs_attribute { - struct attribute attr; - ssize_t(*show) (struct ecryptfs_obj *, char *); - ssize_t(*store) (struct ecryptfs_obj *, const char *, size_t); -}; +static struct kobject *ecryptfs_kobj; -static ssize_t -ecryptfs_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t len) +static ssize_t version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) { - struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj, - kobj); - struct ecryptfs_attribute *attribute = - container_of(attr, struct ecryptfs_attribute, attr); - - return (attribute->store ? attribute->store(obj, buf, len) : 0); + return snprintf(buff, PAGE_SIZE, "%d\n", ECRYPTFS_VERSIONING_MASK); } -static ssize_t -ecryptfs_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) -{ - struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj, - kobj); - struct ecryptfs_attribute *attribute = - container_of(attr, struct ecryptfs_attribute, attr); - - return (attribute->show ? attribute->show(obj, buf) : 0); -} +static struct kobj_attribute version_attr = __ATTR_RO(version); -static struct sysfs_ops ecryptfs_sysfs_ops = { - .show = ecryptfs_attr_show, - .store = ecryptfs_attr_store +static struct attribute *attributes[] = { + &version_attr.attr, + NULL, }; -static struct kobj_type ecryptfs_ktype = { - .sysfs_ops = &ecryptfs_sysfs_ops +static struct attribute_group attr_group = { + .attrs = attributes, }; -static decl_subsys(ecryptfs, &ecryptfs_ktype, NULL); - -static ssize_t version_show(struct ecryptfs_obj *obj, char *buff) -{ - return snprintf(buff, PAGE_SIZE, "%d\n", ECRYPTFS_VERSIONING_MASK); -} - -static struct ecryptfs_attribute sysfs_attr_version = __ATTR_RO(version); - -static struct ecryptfs_version_str_map_elem { - u32 flag; - char *str; -} ecryptfs_version_str_map[] = { - {ECRYPTFS_VERSIONING_PASSPHRASE, "passphrase"}, - {ECRYPTFS_VERSIONING_PUBKEY, "pubkey"}, - {ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH, "plaintext passthrough"}, - {ECRYPTFS_VERSIONING_POLICY, "policy"}, - {ECRYPTFS_VERSIONING_XATTR, "metadata in extended attribute"} -}; - -static ssize_t version_str_show(struct ecryptfs_obj *obj, char *buff) -{ - int i; - int remaining = PAGE_SIZE; - int total_written = 0; - - buff[0] = '\0'; - for (i = 0; i < ARRAY_SIZE(ecryptfs_version_str_map); i++) { - int entry_size; - - if (!(ECRYPTFS_VERSIONING_MASK - & ecryptfs_version_str_map[i].flag)) - continue; - entry_size = strlen(ecryptfs_version_str_map[i].str); - if ((entry_size + 2) > remaining) - goto out; - memcpy(buff, ecryptfs_version_str_map[i].str, entry_size); - buff[entry_size++] = '\n'; - buff[entry_size] = '\0'; - buff += entry_size; - total_written += entry_size; - remaining -= entry_size; - } -out: - return total_written; -} - -static struct ecryptfs_attribute sysfs_attr_version_str = __ATTR_RO(version_str); - static int do_sysfs_registration(void) { int rc; - if ((rc = subsystem_register(&ecryptfs_subsys))) { - printk(KERN_ERR - "Unable to register ecryptfs sysfs subsystem\n"); - goto out; - } - rc = sysfs_create_file(&ecryptfs_subsys.kobj, - &sysfs_attr_version.attr); - if (rc) { - printk(KERN_ERR - "Unable to create ecryptfs version attribute\n"); - subsystem_unregister(&ecryptfs_subsys); + ecryptfs_kobj = kobject_create_and_add("ecryptfs", fs_kobj); + if (!ecryptfs_kobj) { + printk(KERN_ERR "Unable to create ecryptfs kset\n"); + rc = -ENOMEM; goto out; } - rc = sysfs_create_file(&ecryptfs_subsys.kobj, - &sysfs_attr_version_str.attr); + rc = sysfs_create_group(ecryptfs_kobj, &attr_group); if (rc) { printk(KERN_ERR - "Unable to create ecryptfs version_str attribute\n"); - sysfs_remove_file(&ecryptfs_subsys.kobj, - &sysfs_attr_version.attr); - subsystem_unregister(&ecryptfs_subsys); - goto out; + "Unable to create ecryptfs version attributes\n"); + kobject_put(ecryptfs_kobj); } out: return rc; @@ -815,11 +775,8 @@ out: static void do_sysfs_unregistration(void) { - sysfs_remove_file(&ecryptfs_subsys.kobj, - &sysfs_attr_version.attr); - sysfs_remove_file(&ecryptfs_subsys.kobj, - &sysfs_attr_version_str.attr); - subsystem_unregister(&ecryptfs_subsys); + sysfs_remove_group(ecryptfs_kobj, &attr_group); + kobject_put(ecryptfs_kobj); } static int __init ecryptfs_init(void) @@ -845,33 +802,48 @@ static int __init ecryptfs_init(void) rc = register_filesystem(&ecryptfs_fs_type); if (rc) { printk(KERN_ERR "Failed to register filesystem\n"); - ecryptfs_free_kmem_caches(); - goto out; + goto out_free_kmem_caches; } - kobj_set_kset_s(&ecryptfs_subsys, fs_subsys); rc = do_sysfs_registration(); if (rc) { printk(KERN_ERR "sysfs registration failed\n"); - unregister_filesystem(&ecryptfs_fs_type); - ecryptfs_free_kmem_caches(); - goto out; + goto out_unregister_filesystem; } rc = ecryptfs_init_messaging(ecryptfs_transport); if (rc) { ecryptfs_printk(KERN_ERR, "Failure occured while attempting to " "initialize the eCryptfs netlink socket\n"); - do_sysfs_unregistration(); - unregister_filesystem(&ecryptfs_fs_type); - ecryptfs_free_kmem_caches(); + goto out_do_sysfs_unregistration; + } + rc = ecryptfs_init_crypto(); + if (rc) { + printk(KERN_ERR "Failure whilst attempting to init crypto; " + "rc = [%d]\n", rc); + goto out_release_messaging; } + goto out; +out_release_messaging: + ecryptfs_release_messaging(ecryptfs_transport); +out_do_sysfs_unregistration: + do_sysfs_unregistration(); +out_unregister_filesystem: + unregister_filesystem(&ecryptfs_fs_type); +out_free_kmem_caches: + ecryptfs_free_kmem_caches(); out: return rc; } static void __exit ecryptfs_exit(void) { - do_sysfs_unregistration(); + int rc; + + rc = ecryptfs_destroy_crypto(); + if (rc) + printk(KERN_ERR "Failure whilst attempting to destroy crypto; " + "rc = [%d]\n", rc); ecryptfs_release_messaging(ecryptfs_transport); + do_sysfs_unregistration(); unregister_filesystem(&ecryptfs_fs_type); ecryptfs_free_kmem_caches(); }