diff options
Diffstat (limited to 'security')
29 files changed, 461 insertions, 311 deletions
diff --git a/security/Kconfig b/security/Kconfig index 95accd4..e0f08b5 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -167,6 +167,7 @@ config INTEL_TXT config LSM_MMAP_MIN_ADDR int "Low address space for LSM to protect from user allocation" depends on SECURITY && SECURITY_SELINUX + default 32768 if ARM default 65536 help This is the portion of low virtual memory which should be protected diff --git a/security/commoncap.c b/security/commoncap.c index f20e984..a93b3b7 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -529,15 +529,10 @@ skip: new->suid = new->fsuid = new->euid; new->sgid = new->fsgid = new->egid; - /* For init, we want to retain the capabilities set in the initial - * task. Thus we skip the usual capability rules - */ - if (!is_global_init(current)) { - if (effective) - new->cap_effective = new->cap_permitted; - else - cap_clear(new->cap_effective); - } + if (effective) + new->cap_effective = new->cap_permitted; + else + cap_clear(new->cap_effective); bprm->cap_effective = effective; /* diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 8d9c48f..cd1f779 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -62,8 +62,7 @@ static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) struct cgroup_subsys devices_subsys; static int devcgroup_can_attach(struct cgroup_subsys *ss, - struct cgroup *new_cgroup, struct task_struct *task, - bool threadgroup) + struct cgroup *new_cgroup, struct task_struct *task) { if (current != task && !capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/security/keys/internal.h b/security/keys/internal.h index 07a025f..f375152 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -109,11 +109,13 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, const struct cred *cred, struct key_type *type, const void *description, - key_match_func_t match); + key_match_func_t match, + bool no_state_check); extern key_ref_t search_my_process_keyrings(struct key_type *type, const void *description, key_match_func_t match, + bool no_state_check, const struct cred *cred); extern key_ref_t search_process_keyrings(struct key_type *type, const void *description, diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 427fddc..eca5191 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -206,8 +206,14 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type, goto error5; } + /* wait for the key to finish being constructed */ + ret = wait_for_key_construction(key, 1); + if (ret < 0) + goto error6; + ret = key->serial; +error6: key_put(key); error5: key_type_put(ktype); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index cdd2f3f..a06ffab 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -176,13 +176,15 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m) else seq_puts(m, "[anon]"); - rcu_read_lock(); - klist = rcu_dereference(keyring->payload.subscriptions); - if (klist) - seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys); - else - seq_puts(m, ": empty"); - rcu_read_unlock(); + if (key_is_instantiated(keyring)) { + rcu_read_lock(); + klist = rcu_dereference(keyring->payload.subscriptions); + if (klist) + seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys); + else + seq_puts(m, ": empty"); + rcu_read_unlock(); + } } /* @@ -271,6 +273,7 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, * @type: The type of key to search for. * @description: Parameter for @match. * @match: Function to rule on whether or not a key is the one required. + * @no_state_check: Don't check if a matching key is bad * * Search the supplied keyring tree for a key that matches the criteria given. * The root keyring and any linked keyrings must grant Search permission to the @@ -303,7 +306,8 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, const struct cred *cred, struct key_type *type, const void *description, - key_match_func_t match) + key_match_func_t match, + bool no_state_check) { struct { struct keyring_list *keylist; @@ -345,6 +349,8 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, kflags = keyring->flags; if (keyring->type == type && match(keyring, description)) { key = keyring; + if (no_state_check) + goto found; /* check it isn't negative and hasn't expired or been * revoked */ @@ -384,11 +390,13 @@ descend: continue; /* skip revoked keys and expired keys */ - if (kflags & (1 << KEY_FLAG_REVOKED)) - continue; + if (!no_state_check) { + if (kflags & (1 << KEY_FLAG_REVOKED)) + continue; - if (key->expiry && now.tv_sec >= key->expiry) - continue; + if (key->expiry && now.tv_sec >= key->expiry) + continue; + } /* keys that don't match */ if (!match(key, description)) @@ -399,6 +407,9 @@ descend: cred, KEY_SEARCH) < 0) continue; + if (no_state_check) + goto found; + /* we set a different error code if we pass a negative key */ if (kflags & (1 << KEY_FLAG_NEGATIVE)) { err = key->type_data.reject_error; @@ -478,7 +489,7 @@ key_ref_t keyring_search(key_ref_t keyring, return ERR_PTR(-ENOKEY); return keyring_search_aux(keyring, current->cred, - type, description, type->match); + type, description, type->match, false); } EXPORT_SYMBOL(keyring_search); diff --git a/security/keys/proc.c b/security/keys/proc.c index 525cf8a..49bbc97 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -199,7 +199,7 @@ static int proc_keys_show(struct seq_file *m, void *v) if (key->perm & KEY_POS_VIEW) { skey_ref = search_my_process_keyrings(key->type, key, lookup_user_key_possessed, - cred); + true, cred); if (!IS_ERR(skey_ref)) { key_ref_put(skey_ref); key_ref = make_key_ref(key, 1); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 930634e..a3063eb 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -331,6 +331,7 @@ void key_fsgid_changed(struct task_struct *tsk) key_ref_t search_my_process_keyrings(struct key_type *type, const void *description, key_match_func_t match, + bool no_state_check, const struct cred *cred) { key_ref_t key_ref, ret, err; @@ -350,7 +351,7 @@ key_ref_t search_my_process_keyrings(struct key_type *type, if (cred->thread_keyring) { key_ref = keyring_search_aux( make_key_ref(cred->thread_keyring, 1), - cred, type, description, match); + cred, type, description, match, no_state_check); if (!IS_ERR(key_ref)) goto found; @@ -371,7 +372,7 @@ key_ref_t search_my_process_keyrings(struct key_type *type, if (cred->tgcred->process_keyring) { key_ref = keyring_search_aux( make_key_ref(cred->tgcred->process_keyring, 1), - cred, type, description, match); + cred, type, description, match, no_state_check); if (!IS_ERR(key_ref)) goto found; @@ -395,7 +396,7 @@ key_ref_t search_my_process_keyrings(struct key_type *type, make_key_ref(rcu_dereference( cred->tgcred->session_keyring), 1), - cred, type, description, match); + cred, type, description, match, no_state_check); rcu_read_unlock(); if (!IS_ERR(key_ref)) @@ -417,7 +418,7 @@ key_ref_t search_my_process_keyrings(struct key_type *type, else if (cred->user->session_keyring) { key_ref = keyring_search_aux( make_key_ref(cred->user->session_keyring, 1), - cred, type, description, match); + cred, type, description, match, no_state_check); if (!IS_ERR(key_ref)) goto found; @@ -459,7 +460,8 @@ key_ref_t search_process_keyrings(struct key_type *type, might_sleep(); - key_ref = search_my_process_keyrings(type, description, match, cred); + key_ref = search_my_process_keyrings(type, description, match, + false, cred); if (!IS_ERR(key_ref)) goto found; err = key_ref; @@ -845,6 +847,7 @@ void key_replace_session_keyring(void) new-> sgid = old-> sgid; new->fsgid = old->fsgid; new->user = get_uid(old->user); + new->user_ns = new->user->user_ns; new->group_info = get_group_info(old->group_info); new->securebits = old->securebits; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index d41cc15..d31862e 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -530,8 +530,7 @@ struct key *request_key_and_link(struct key_type *type, dest_keyring, flags); /* search all the process keyrings for a key */ - key_ref = search_process_keyrings(type, description, type->match, - cred); + key_ref = search_process_keyrings(type, description, type->match, cred); if (!IS_ERR(key_ref)) { key = key_ref_to_ptr(key_ref); diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 3c0cfde..6cff375 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -59,7 +59,8 @@ static void request_key_auth_describe(const struct key *key, seq_puts(m, "key:"); seq_puts(m, key->description); - seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len); + if (key_is_instantiated(key)) + seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len); } /* diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index c6ca866..5b366d7 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -69,18 +69,6 @@ error: EXPORT_SYMBOL_GPL(user_instantiate); /* - * dispose of the old data from an updated user defined key - */ -static void user_update_rcu_disposal(struct rcu_head *rcu) -{ - struct user_key_payload *upayload; - - upayload = container_of(rcu, struct user_key_payload, rcu); - - kfree(upayload); -} - -/* * update a user defined key * - the key's semaphore is write-locked */ @@ -114,7 +102,7 @@ int user_update(struct key *key, const void *data, size_t datalen) key->expiry = 0; } - call_rcu(&zap->rcu, user_update_rcu_disposal); + kfree_rcu(zap, rcu); error: return ret; @@ -145,7 +133,7 @@ void user_revoke(struct key *key) if (upayload) { rcu_assign_pointer(key->payload.data, NULL); - call_rcu(&upayload->rcu, user_update_rcu_disposal); + kfree_rcu(upayload, rcu); } } @@ -169,8 +157,8 @@ EXPORT_SYMBOL_GPL(user_destroy); void user_describe(const struct key *key, struct seq_file *m) { seq_puts(m, key->description); - - seq_printf(m, ": %u", key->datalen); + if (key_is_instantiated(key)) + seq_printf(m, ": %u", key->datalen); } EXPORT_SYMBOL_GPL(user_describe); diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 908aa71..893af8a 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -210,7 +210,6 @@ static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr, static void dump_common_audit_data(struct audit_buffer *ab, struct common_audit_data *a) { - struct inode *inode = NULL; struct task_struct *tsk = current; if (a->tsk) @@ -229,33 +228,47 @@ static void dump_common_audit_data(struct audit_buffer *ab, case LSM_AUDIT_DATA_CAP: audit_log_format(ab, " capability=%d ", a->u.cap); break; - case LSM_AUDIT_DATA_FS: - if (a->u.fs.path.dentry) { - struct dentry *dentry = a->u.fs.path.dentry; - if (a->u.fs.path.mnt) { - audit_log_d_path(ab, "path=", &a->u.fs.path); - } else { - audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, - dentry->d_name.name); - } - inode = dentry->d_inode; - } else if (a->u.fs.inode) { - struct dentry *dentry; - inode = a->u.fs.inode; - dentry = d_find_alias(inode); - if (dentry) { - audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, - dentry->d_name.name); - dput(dentry); - } - } + case LSM_AUDIT_DATA_PATH: { + struct inode *inode; + + audit_log_d_path(ab, "path=", &a->u.path); + + inode = a->u.path.dentry->d_inode; if (inode) audit_log_format(ab, " dev=%s ino=%lu", inode->i_sb->s_id, inode->i_ino); break; + } + case LSM_AUDIT_DATA_DENTRY: { + struct inode *inode; + + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, a->u.dentry->d_name.name); + + inode = a->u.dentry->d_inode; + if (inode) + audit_log_format(ab, " dev=%s ino=%lu", + inode->i_sb->s_id, + inode->i_ino); + break; + } + case LSM_AUDIT_DATA_INODE: { + struct dentry *dentry; + struct inode *inode; + + inode = a->u.inode; + dentry = d_find_alias(inode); + if (dentry) { + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, + dentry->d_name.name); + dput(dentry); + } + audit_log_format(ab, " dev=%s ino=%lu", inode->i_sb->s_id, + inode->i_ino); + break; + } case LSM_AUDIT_DATA_TASK: tsk = a->u.tsk; if (tsk && tsk->pid) { diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 1d027e2..d515b21 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -38,11 +38,7 @@ #define AVC_CACHE_RECLAIM 16 #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS -#define avc_cache_stats_incr(field) \ -do { \ - per_cpu(avc_cache_stats, get_cpu()).field++; \ - put_cpu(); \ -} while (0) +#define avc_cache_stats_incr(field) this_cpu_inc(avc_cache_stats.field) #else #define avc_cache_stats_incr(field) do {} while (0) #endif @@ -347,11 +343,10 @@ static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass) node = avc_search_node(ssid, tsid, tclass); if (node) - avc_cache_stats_incr(hits); - else - avc_cache_stats_incr(misses); + return node; - return node; + avc_cache_stats_incr(misses); + return NULL; } static int avc_latest_notif_update(int seqno, int is_insert) @@ -531,7 +526,7 @@ int avc_audit(u32 ssid, u32 tsid, * during retry. However this is logically just as if the operation * happened a little later. */ - if ((a->type == LSM_AUDIT_DATA_FS) && + if ((a->type == LSM_AUDIT_DATA_INODE) && (flags & IPERM_FLAG_RCU)) return -ECHILD; @@ -757,10 +752,9 @@ int avc_ss_reset(u32 seqno) int avc_has_perm_noaudit(u32 ssid, u32 tsid, u16 tclass, u32 requested, unsigned flags, - struct av_decision *in_avd) + struct av_decision *avd) { struct avc_node *node; - struct av_decision avd_entry, *avd; int rc = 0; u32 denied; @@ -769,20 +763,13 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, rcu_read_lock(); node = avc_lookup(ssid, tsid, tclass); - if (!node) { + if (unlikely(!node)) { rcu_read_unlock(); - - if (in_avd) - avd = in_avd; - else - avd = &avd_entry; - security_compute_av(ssid, tsid, tclass, avd); rcu_read_lock(); node = avc_insert(ssid, tsid, tclass, avd); } else { - if (in_avd) - memcpy(in_avd, &node->ae.avd, sizeof(*in_avd)); + memcpy(avd, &node->ae.avd, sizeof(*avd)); avd = &node->ae.avd; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8fb2488..a0d3845 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -990,6 +990,7 @@ static void selinux_write_opts(struct seq_file *m, continue; default: BUG(); + return; }; /* we need a comma before each option */ seq_putc(m, ','); @@ -1443,6 +1444,7 @@ static int task_has_capability(struct task_struct *tsk, printk(KERN_ERR "SELinux: out of range capability %d\n", cap); BUG(); + return -EINVAL; } rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); @@ -1487,8 +1489,8 @@ static int inode_has_perm(const struct cred *cred, if (!adp) { adp = &ad; - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.inode = inode; + COMMON_AUDIT_DATA_INIT(&ad, INODE); + ad.u.inode = inode; } return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags); @@ -1498,16 +1500,29 @@ static int inode_has_perm(const struct cred *cred, the dentry to help the auditing code to more easily generate the pathname if needed. */ static inline int dentry_has_perm(const struct cred *cred, - struct vfsmount *mnt, struct dentry *dentry, u32 av) { struct inode *inode = dentry->d_inode; struct common_audit_data ad; - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.path.mnt = mnt; - ad.u.fs.path.dentry = dentry; + COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.u.dentry = dentry; + return inode_has_perm(cred, inode, av, &ad, 0); +} + +/* Same as inode_has_perm, but pass explicit audit data containing + the path to help the auditing code to more easily generate the + pathname if needed. */ +static inline int path_has_perm(const struct cred *cred, + struct path *path, + u32 av) +{ + struct inode *inode = path->dentry->d_inode; + struct common_audit_data ad; + + COMMON_AUDIT_DATA_INIT(&ad, PATH); + ad.u.path = *path; return inode_has_perm(cred, inode, av, &ad, 0); } @@ -1529,8 +1544,8 @@ static int file_has_perm(const struct cred *cred, u32 sid = cred_sid(cred); int rc; - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.path = file->f_path; + COMMON_AUDIT_DATA_INIT(&ad, PATH); + ad.u.path = file->f_path; if (sid != fsec->sid) { rc = avc_has_perm(sid, fsec->sid, @@ -1568,8 +1583,8 @@ static int may_create(struct inode *dir, sid = tsec->sid; newsid = tsec->create_sid; - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.path.dentry = dentry; + COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.u.dentry = dentry; rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, DIR__ADD_NAME | DIR__SEARCH, @@ -1621,8 +1636,8 @@ static int may_link(struct inode *dir, dsec = dir->i_security; isec = dentry->d_inode->i_security; - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.path.dentry = dentry; + COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.u.dentry = dentry; av = DIR__SEARCH; av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); @@ -1667,9 +1682,9 @@ static inline int may_rename(struct inode *old_dir, old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode); new_dsec = new_dir->i_security; - COMMON_AUDIT_DATA_INIT(&ad, FS); + COMMON_AUDIT_DATA_INIT(&ad, DENTRY); - ad.u.fs.path.dentry = old_dentry; + ad.u.dentry = old_dentry; rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR, DIR__REMOVE_NAME | DIR__SEARCH, &ad); if (rc) @@ -1685,7 +1700,7 @@ static inline int may_rename(struct inode *old_dir, return rc; } - ad.u.fs.path.dentry = new_dentry; + ad.u.dentry = new_dentry; av = DIR__ADD_NAME | DIR__SEARCH; if (new_dentry->d_inode) av |= DIR__REMOVE_NAME; @@ -1895,7 +1910,7 @@ static int selinux_quota_on(struct dentry *dentry) { const struct cred *cred = current_cred(); - return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON); + return dentry_has_perm(cred, dentry, FILE__QUOTAON); } static int selinux_syslog(int type) @@ -1992,8 +2007,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) return rc; } - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.path = bprm->file->f_path; + COMMON_AUDIT_DATA_INIT(&ad, PATH); + ad.u.path = bprm->file->f_path; if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) new_tsec->sid = old_tsec->sid; @@ -2121,7 +2136,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, /* Revalidate access to inherited open files. */ - COMMON_AUDIT_DATA_INIT(&ad, FS); + COMMON_AUDIT_DATA_INIT(&ad, INODE); spin_lock(&files->file_lock); for (;;) { @@ -2469,8 +2484,8 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) if (flags & MS_KERNMOUNT) return 0; - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.path.dentry = sb->s_root; + COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.u.dentry = sb->s_root; return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); } @@ -2479,8 +2494,8 @@ static int selinux_sb_statfs(struct dentry *dentry) const struct cred *cred = current_cred(); struct common_audit_data ad; - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.path.dentry = dentry->d_sb->s_root; + COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.u.dentry = dentry->d_sb->s_root; return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); } @@ -2496,8 +2511,7 @@ static int selinux_mount(char *dev_name, return superblock_has_perm(cred, path->mnt->mnt_sb, FILESYSTEM__REMOUNT, NULL); else - return dentry_has_perm(cred, path->mnt, path->dentry, - FILE__MOUNTON); + return path_has_perm(cred, path, FILE__MOUNTON); } static int selinux_umount(struct vfsmount *mnt, int flags) @@ -2630,14 +2644,14 @@ static int selinux_inode_readlink(struct dentry *dentry) { const struct cred *cred = current_cred(); - return dentry_has_perm(cred, NULL, dentry, FILE__READ); + return dentry_has_perm(cred, dentry, FILE__READ); } static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata) { const struct cred *cred = current_cred(); - return dentry_has_perm(cred, NULL, dentry, FILE__READ); + return dentry_has_perm(cred, dentry, FILE__READ); } static int selinux_inode_permission(struct inode *inode, int mask, unsigned flags) @@ -2654,8 +2668,8 @@ static int selinux_inode_permission(struct inode *inode, int mask, unsigned flag if (!mask) return 0; - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.inode = inode; + COMMON_AUDIT_DATA_INIT(&ad, INODE); + ad.u.inode = inode; if (from_access) ad.selinux_audit_data.auditdeny |= FILE__AUDIT_ACCESS; @@ -2680,16 +2694,20 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) - return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR); + return dentry_has_perm(cred, dentry, FILE__SETATTR); - return dentry_has_perm(cred, NULL, dentry, FILE__WRITE); + return dentry_has_perm(cred, dentry, FILE__WRITE); } static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) { const struct cred *cred = current_cred(); + struct path path; + + path.dentry = dentry; + path.mnt = mnt; - return dentry_has_perm(cred, mnt, dentry, FILE__GETATTR); + return path_has_perm(cred, &path, FILE__GETATTR); } static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name) @@ -2710,7 +2728,7 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name) /* Not an attribute we recognize, so just check the ordinary setattr permission. */ - return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR); + return dentry_has_perm(cred, dentry, FILE__SETATTR); } static int selinux_inode_setxattr(struct dentry *dentry, const char *name, @@ -2733,8 +2751,8 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, if (!inode_owner_or_capable(inode)) return -EPERM; - COMMON_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.path.dentry = dentry; + COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + ad.u.dentry = dentry; rc = avc_has_perm(sid, isec->sid, isec->sclass, FILE__RELABELFROM, &ad); @@ -2797,14 +2815,14 @@ static int selinux_inode_getxattr(struct dentry *dentry, const char *name) { const struct cred *cred = current_cred(); - return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR); + return dentry_has_perm(cred, dentry, FILE__GETATTR); } static int selinux_inode_listxattr(struct dentry *dentry) { const struct cred *cred = current_cred(); - return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR); + return dentry_has_perm(cred, dentry, FILE__GETATTR); } static int selinux_inode_removexattr(struct dentry *dentry, const char *name) diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index e77b2ac..47fda96 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -41,7 +41,6 @@ struct sk_buff; */ struct avc_cache_stats { unsigned int lookups; - unsigned int hits; unsigned int misses; unsigned int allocations; unsigned int reclaims; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 348eb00..3ba4feb 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -30,13 +30,14 @@ #define POLICYDB_VERSION_PERMISSIVE 23 #define POLICYDB_VERSION_BOUNDARY 24 #define POLICYDB_VERSION_FILENAME_TRANS 25 +#define POLICYDB_VERSION_ROLETRANS 26 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_FILENAME_TRANS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_ROLETRANS #endif /* Mask for just the mount related flags */ @@ -85,7 +86,7 @@ extern int selinux_policycap_openperm; int security_mls_enabled(void); int security_load_policy(void *data, size_t len); -int security_read_policy(void **data, ssize_t *len); +int security_read_policy(void **data, size_t *len); size_t security_policydb_len(void); int security_policycap_supported(unsigned int req_cap); @@ -111,8 +112,8 @@ void security_compute_av_user(u32 ssid, u32 tsid, int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, const struct qstr *qstr, u32 *out_sid); -int security_transition_sid_user(u32 ssid, u32 tsid, - u16 tclass, u32 *out_sid); +int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, + const char *objname, u32 *out_sid); int security_member_sid(u32 ssid, u32 tsid, u16 tclass, u32 *out_sid); diff --git a/security/selinux/netif.c b/security/selinux/netif.c index d6095d6..58cc481 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -104,22 +104,6 @@ static int sel_netif_insert(struct sel_netif *netif) } /** - * sel_netif_free - Frees an interface entry - * @p: the entry's RCU field - * - * Description: - * This function is designed to be used as a callback to the call_rcu() - * function so that memory allocated to a hash table interface entry can be - * released safely. - * - */ -static void sel_netif_free(struct rcu_head *p) -{ - struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head); - kfree(netif); -} - -/** * sel_netif_destroy - Remove an interface record from the table * @netif: the existing interface record * @@ -131,7 +115,7 @@ static void sel_netif_destroy(struct sel_netif *netif) { list_del_rcu(&netif->list); sel_netif_total--; - call_rcu(&netif->rcu_head, sel_netif_free); + kfree_rcu(netif, rcu_head); } /** diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index 65ebfe9..3618251 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -141,6 +141,7 @@ static struct sel_netnode *sel_netnode_find(const void *addr, u16 family) break; default: BUG(); + return NULL; } list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list) diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index ea39cb7..77d4413 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -28,6 +28,7 @@ #include <linux/percpu.h> #include <linux/audit.h> #include <linux/uaccess.h> +#include <linux/kobject.h> /* selinuxfs pseudo filesystem for exporting the security policy API. Based on the proc code and the fs/nfsd/nfsctl.c code. */ @@ -280,7 +281,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, length = -ENOMEM; if (count >= PAGE_SIZE) - goto out;; + goto out; /* No partial writes. */ length = -EINVAL; @@ -753,11 +754,13 @@ out: static ssize_t sel_write_create(struct file *file, char *buf, size_t size) { char *scon = NULL, *tcon = NULL; + char *namebuf = NULL, *objname = NULL; u32 ssid, tsid, newsid; u16 tclass; ssize_t length; char *newcon = NULL; u32 len; + int nargs; length = task_has_security(current, SECURITY__COMPUTE_CREATE); if (length) @@ -773,9 +776,17 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) if (!tcon) goto out; + length = -ENOMEM; + namebuf = kzalloc(size + 1, GFP_KERNEL); + if (!namebuf) + goto out; + length = -EINVAL; - if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) + nargs = sscanf(buf, "%s %s %hu %s", scon, tcon, &tclass, namebuf); + if (nargs < 3 || nargs > 4) goto out; + if (nargs == 4) + objname = namebuf; length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); if (length) @@ -785,7 +796,8 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) if (length) goto out; - length = security_transition_sid_user(ssid, tsid, tclass, &newsid); + length = security_transition_sid_user(ssid, tsid, tclass, + objname, &newsid); if (length) goto out; @@ -804,6 +816,7 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) length = len; out: kfree(newcon); + kfree(namebuf); kfree(tcon); kfree(scon); return length; @@ -876,12 +889,12 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) length = task_has_security(current, SECURITY__COMPUTE_USER); if (length) - goto out;; + goto out; length = -ENOMEM; con = kzalloc(size + 1, GFP_KERNEL); if (!con) - goto out;; + goto out; length = -ENOMEM; user = kzalloc(size + 1, GFP_KERNEL); @@ -941,7 +954,7 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size) length = -ENOMEM; scon = kzalloc(size + 1, GFP_KERNEL); if (!scon) - goto out;; + goto out; length = -ENOMEM; tcon = kzalloc(size + 1, GFP_KERNEL); @@ -1380,10 +1393,14 @@ static int sel_avc_stats_seq_show(struct seq_file *seq, void *v) if (v == SEQ_START_TOKEN) seq_printf(seq, "lookups hits misses allocations reclaims " "frees\n"); - else - seq_printf(seq, "%u %u %u %u %u %u\n", st->lookups, - st->hits, st->misses, st->allocations, + else { + unsigned int lookups = st->lookups; + unsigned int misses = st->misses; + unsigned int hits = lookups - misses; + seq_printf(seq, "%u %u %u %u %u %u\n", lookups, + hits, misses, st->allocations, st->reclaims, st->frees); + } return 0; } @@ -1897,6 +1914,7 @@ static struct file_system_type sel_fs_type = { }; struct vfsmount *selinuxfs_mount; +static struct kobject *selinuxfs_kobj; static int __init init_sel_fs(void) { @@ -1904,9 +1922,16 @@ static int __init init_sel_fs(void) if (!selinux_enabled) return 0; + + selinuxfs_kobj = kobject_create_and_add("selinux", fs_kobj); + if (!selinuxfs_kobj) + return -ENOMEM; + err = register_filesystem(&sel_fs_type); - if (err) + if (err) { + kobject_put(selinuxfs_kobj); return err; + } selinuxfs_mount = kern_mount(&sel_fs_type); if (IS_ERR(selinuxfs_mount)) { @@ -1923,6 +1948,7 @@ __initcall(init_sel_fs); #ifdef CONFIG_SECURITY_SELINUX_DISABLE void exit_sel_fs(void) { + kobject_put(selinuxfs_kobj); unregister_filesystem(&sel_fs_type); } #endif diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 7102457..102e9ec 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -128,6 +128,11 @@ static struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_ROLETRANS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -179,6 +184,43 @@ out: return rc; } +static u32 filenametr_hash(struct hashtab *h, const void *k) +{ + const struct filename_trans *ft = k; + unsigned long hash; + unsigned int byte_num; + unsigned char focus; + + hash = ft->stype ^ ft->ttype ^ ft->tclass; + + byte_num = 0; + while ((focus = ft->name[byte_num++])) + hash = partial_name_hash(focus, hash); + return hash & (h->size - 1); +} + +static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2) +{ + const struct filename_trans *ft1 = k1; + const struct filename_trans *ft2 = k2; + int v; + + v = ft1->stype - ft2->stype; + if (v) + return v; + + v = ft1->ttype - ft2->ttype; + if (v) + return v; + + v = ft1->tclass - ft2->tclass; + if (v) + return v; + + return strcmp(ft1->name, ft2->name); + +} + static u32 rangetr_hash(struct hashtab *h, const void *k) { const struct range_trans *key = k; @@ -231,15 +273,22 @@ static int policydb_init(struct policydb *p) if (rc) goto out; + p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10)); + if (!p->filename_trans) + goto out; + p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); if (!p->range_tr) goto out; + ebitmap_init(&p->filename_trans_ttypes); ebitmap_init(&p->policycaps); ebitmap_init(&p->permissive_map); return 0; out: + hashtab_destroy(p->filename_trans); + hashtab_destroy(p->range_tr); for (i = 0; i < SYM_NUM; i++) hashtab_destroy(p->symtab[i].table); return rc; @@ -417,32 +466,26 @@ static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) = }; #ifdef DEBUG_HASHES -static void symtab_hash_eval(struct symtab *s) +static void hash_eval(struct hashtab *h, const char *hash_name) { - int i; - - for (i = 0; i < SYM_NUM; i++) { - struct hashtab *h = s[i].table; - struct hashtab_info info; + struct hashtab_info info; - hashtab_stat(h, &info); - printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " - "longest chain length %d\n", symtab_name[i], h->nel, - info.slots_used, h->size, info.max_chain_len); - } + hashtab_stat(h, &info); + printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " + "longest chain length %d\n", hash_name, h->nel, + info.slots_used, h->size, info.max_chain_len); } -static void rangetr_hash_eval(struct hashtab *h) +static void symtab_hash_eval(struct symtab *s) { - struct hashtab_info info; + int i; - hashtab_stat(h, &info); - printk(KERN_DEBUG "SELinux: rangetr: %d entries and %d/%d buckets used, " - "longest chain length %d\n", h->nel, - info.slots_used, h->size, info.max_chain_len); + for (i = 0; i < SYM_NUM; i++) + hash_eval(s[i].table, symtab_name[i]); } + #else -static inline void rangetr_hash_eval(struct hashtab *h) +static inline void hash_eval(struct hashtab *h, char *hash_name) { } #endif @@ -675,6 +718,16 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = cat_destroy, }; +static int filenametr_destroy(void *key, void *datum, void *p) +{ + struct filename_trans *ft = key; + kfree(ft->name); + kfree(key); + kfree(datum); + cond_resched(); + return 0; +} + static int range_tr_destroy(void *key, void *datum, void *p) { struct mls_range *rt = datum; @@ -709,7 +762,6 @@ void policydb_destroy(struct policydb *p) int i; struct role_allow *ra, *lra = NULL; struct role_trans *tr, *ltr = NULL; - struct filename_trans *ft, *nft; for (i = 0; i < SYM_NUM; i++) { cond_resched(); @@ -773,6 +825,9 @@ void policydb_destroy(struct policydb *p) } kfree(lra); + hashtab_map(p->filename_trans, filenametr_destroy, NULL); + hashtab_destroy(p->filename_trans); + hashtab_map(p->range_tr, range_tr_destroy, NULL); hashtab_destroy(p->range_tr); @@ -788,14 +843,7 @@ void policydb_destroy(struct policydb *p) flex_array_free(p->type_attr_map_array); } - ft = p->filename_trans; - while (ft) { - nft = ft->next; - kfree(ft->name); - kfree(ft); - ft = nft; - } - + ebitmap_destroy(&p->filename_trans_ttypes); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); @@ -1795,7 +1843,7 @@ static int range_read(struct policydb *p, void *fp) rt = NULL; r = NULL; } - rangetr_hash_eval(p->range_tr); + hash_eval(p->range_tr, "rangetr"); rc = 0; out: kfree(rt); @@ -1805,9 +1853,10 @@ out: static int filename_trans_read(struct policydb *p, void *fp) { - struct filename_trans *ft, *last; - u32 nel, len; + struct filename_trans *ft; + struct filename_trans_datum *otype; char *name; + u32 nel, len; __le32 buf[4]; int rc, i; @@ -1816,25 +1865,23 @@ static int filename_trans_read(struct policydb *p, void *fp) rc = next_entry(buf, fp, sizeof(u32)); if (rc) - goto out; + return rc; nel = le32_to_cpu(buf[0]); - last = p->filename_trans; - while (last && last->next) - last = last->next; - for (i = 0; i < nel; i++) { + ft = NULL; + otype = NULL; + name = NULL; + rc = -ENOMEM; ft = kzalloc(sizeof(*ft), GFP_KERNEL); if (!ft) goto out; - /* add it to the tail of the list */ - if (!last) - p->filename_trans = ft; - else - last->next = ft; - last = ft; + rc = -ENOMEM; + otype = kmalloc(sizeof(*otype), GFP_KERNEL); + if (!otype) + goto out; /* length of the path component string */ rc = next_entry(buf, fp, sizeof(u32)); @@ -1862,10 +1909,22 @@ static int filename_trans_read(struct policydb *p, void *fp) ft->stype = le32_to_cpu(buf[0]); ft->ttype = le32_to_cpu(buf[1]); ft->tclass = le32_to_cpu(buf[2]); - ft->otype = le32_to_cpu(buf[3]); + + otype->otype = le32_to_cpu(buf[3]); + + rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1); + if (rc) + goto out; + + hashtab_insert(p->filename_trans, ft, otype); } - rc = 0; + hash_eval(p->filename_trans, "filenametr"); + return 0; out: + kfree(ft); + kfree(name); + kfree(otype); + return rc; } @@ -2266,6 +2325,11 @@ int policydb_read(struct policydb *p, void *fp) p->symtab[i].nprim = nprim; } + rc = -EINVAL; + p->process_class = string_to_security_class(p, "process"); + if (!p->process_class) + goto bad; + rc = avtab_read(&p->te_avtab, fp, p); if (rc) goto bad; @@ -2298,8 +2362,17 @@ int policydb_read(struct policydb *p, void *fp) tr->role = le32_to_cpu(buf[0]); tr->type = le32_to_cpu(buf[1]); tr->new_role = le32_to_cpu(buf[2]); + if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto bad; + tr->tclass = le32_to_cpu(buf[0]); + } else + tr->tclass = p->process_class; + if (!policydb_role_isvalid(p, tr->role) || !policydb_type_isvalid(p, tr->type) || + !policydb_class_isvalid(p, tr->tclass) || !policydb_role_isvalid(p, tr->new_role)) goto bad; ltr = tr; @@ -2341,11 +2414,6 @@ int policydb_read(struct policydb *p, void *fp) goto bad; rc = -EINVAL; - p->process_class = string_to_security_class(p, "process"); - if (!p->process_class) - goto bad; - - rc = -EINVAL; p->process_trans_perms = string_to_av_perm(p, p->process_class, "transition"); p->process_trans_perms |= string_to_av_perm(p, p->process_class, "dyntransition"); if (!p->process_trans_perms) @@ -2517,8 +2585,9 @@ static int cat_write(void *vkey, void *datum, void *ptr) return 0; } -static int role_trans_write(struct role_trans *r, void *fp) +static int role_trans_write(struct policydb *p, void *fp) { + struct role_trans *r = p->role_tr; struct role_trans *tr; u32 buf[3]; size_t nel; @@ -2538,6 +2607,12 @@ static int role_trans_write(struct role_trans *r, void *fp) rc = put_entry(buf, sizeof(u32), 3, fp); if (rc) return rc; + if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { + buf[0] = cpu_to_le32(tr->tclass); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + } } return 0; @@ -3045,7 +3120,7 @@ static int genfs_write(struct policydb *p, void *fp) return 0; } -static int range_count(void *key, void *data, void *ptr) +static int hashtab_cnt(void *key, void *data, void *ptr) { int *cnt = ptr; *cnt = *cnt + 1; @@ -3093,7 +3168,7 @@ static int range_write(struct policydb *p, void *fp) /* count the number of entries in the hashtab */ nel = 0; - rc = hashtab_map(p->range_tr, range_count, &nel); + rc = hashtab_map(p->range_tr, hashtab_cnt, &nel); if (rc) return rc; @@ -3110,43 +3185,60 @@ static int range_write(struct policydb *p, void *fp) return 0; } -static int filename_trans_write(struct policydb *p, void *fp) +static int filename_write_helper(void *key, void *data, void *ptr) { - struct filename_trans *ft; - u32 len, nel = 0; __le32 buf[4]; + struct filename_trans *ft = key; + struct filename_trans_datum *otype = data; + void *fp = ptr; int rc; + u32 len; - for (ft = p->filename_trans; ft; ft = ft->next) - nel++; - - buf[0] = cpu_to_le32(nel); + len = strlen(ft->name); + buf[0] = cpu_to_le32(len); rc = put_entry(buf, sizeof(u32), 1, fp); if (rc) return rc; - for (ft = p->filename_trans; ft; ft = ft->next) { - len = strlen(ft->name); - buf[0] = cpu_to_le32(len); - rc = put_entry(buf, sizeof(u32), 1, fp); - if (rc) - return rc; + rc = put_entry(ft->name, sizeof(char), len, fp); + if (rc) + return rc; - rc = put_entry(ft->name, sizeof(char), len, fp); - if (rc) - return rc; + buf[0] = ft->stype; + buf[1] = ft->ttype; + buf[2] = ft->tclass; + buf[3] = otype->otype; - buf[0] = ft->stype; - buf[1] = ft->ttype; - buf[2] = ft->tclass; - buf[3] = ft->otype; + rc = put_entry(buf, sizeof(u32), 4, fp); + if (rc) + return rc; - rc = put_entry(buf, sizeof(u32), 4, fp); - if (rc) - return rc; - } return 0; } + +static int filename_trans_write(struct policydb *p, void *fp) +{ + u32 nel; + __le32 buf[1]; + int rc; + + nel = 0; + rc = hashtab_map(p->filename_trans, hashtab_cnt, &nel); + if (rc) + return rc; + + buf[0] = cpu_to_le32(nel); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return rc; + + rc = hashtab_map(p->filename_trans, filename_write_helper, fp); + if (rc) + return rc; + + return 0; +} + /* * Write the configuration data in a policy database * structure to a policy database binary representation @@ -3249,7 +3341,7 @@ int policydb_write(struct policydb *p, void *fp) if (rc) return rc; - rc = role_trans_write(p->role_tr, fp); + rc = role_trans_write(p, fp); if (rc) return rc; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 732ea4a..b846c03 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -72,17 +72,20 @@ struct role_datum { struct role_trans { u32 role; /* current role */ - u32 type; /* program executable type */ + u32 type; /* program executable type, or new object type */ + u32 tclass; /* process class, or new object class */ u32 new_role; /* new role */ struct role_trans *next; }; struct filename_trans { - struct filename_trans *next; u32 stype; /* current process */ u32 ttype; /* parent dir context */ u16 tclass; /* class of new object */ const char *name; /* last path component */ +}; + +struct filename_trans_datum { u32 otype; /* expected of new object */ }; @@ -227,7 +230,10 @@ struct policydb { struct role_trans *role_tr; /* file transitions with the last path component */ - struct filename_trans *filename_trans; + /* quickly exclude lookups when parent ttype has no rules */ + struct ebitmap filename_trans_ttypes; + /* actual set of filename_trans rules */ + struct hashtab *filename_trans; /* bools indexed by (value - 1) */ struct cond_bool_datum **bool_val_to_struct; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 6ef4af4..973e00e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1359,26 +1359,35 @@ out: } static void filename_compute_type(struct policydb *p, struct context *newcontext, - u32 scon, u32 tcon, u16 tclass, - const struct qstr *qstr) -{ - struct filename_trans *ft; - for (ft = p->filename_trans; ft; ft = ft->next) { - if (ft->stype == scon && - ft->ttype == tcon && - ft->tclass == tclass && - !strcmp(ft->name, qstr->name)) { - newcontext->type = ft->otype; - return; - } - } + u32 stype, u32 ttype, u16 tclass, + const char *objname) +{ + struct filename_trans ft; + struct filename_trans_datum *otype; + + /* + * Most filename trans rules are going to live in specific directories + * like /dev or /var/run. This bitmap will quickly skip rule searches + * if the ttype does not contain any rules. + */ + if (!ebitmap_get_bit(&p->filename_trans_ttypes, ttype)) + return; + + ft.stype = stype; + ft.ttype = ttype; + ft.tclass = tclass; + ft.name = objname; + + otype = hashtab_search(p->filename_trans, &ft); + if (otype) + newcontext->type = otype->otype; } static int security_compute_sid(u32 ssid, u32 tsid, u16 orig_tclass, u32 specified, - const struct qstr *qstr, + const char *objname, u32 *out_sid, bool kern) { @@ -1478,23 +1487,21 @@ static int security_compute_sid(u32 ssid, newcontext.type = avdatum->data; } - /* if we have a qstr this is a file trans check so check those rules */ - if (qstr) + /* if we have a objname this is a file trans check so check those rules */ + if (objname) filename_compute_type(&policydb, &newcontext, scontext->type, - tcontext->type, tclass, qstr); + tcontext->type, tclass, objname); /* Check for class-specific changes. */ - if (tclass == policydb.process_class) { - if (specified & AVTAB_TRANSITION) { - /* Look for a role transition rule. */ - for (roletr = policydb.role_tr; roletr; - roletr = roletr->next) { - if (roletr->role == scontext->role && - roletr->type == tcontext->type) { - /* Use the role transition rule. */ - newcontext.role = roletr->new_role; - break; - } + if (specified & AVTAB_TRANSITION) { + /* Look for a role transition rule. */ + for (roletr = policydb.role_tr; roletr; roletr = roletr->next) { + if ((roletr->role == scontext->role) && + (roletr->type == tcontext->type) && + (roletr->tclass == tclass)) { + /* Use the role transition rule. */ + newcontext.role = roletr->new_role; + break; } } } @@ -1541,13 +1548,14 @@ int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, const struct qstr *qstr, u32 *out_sid) { return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, - qstr, out_sid, true); + qstr ? qstr->name : NULL, out_sid, true); } -int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, u32 *out_sid) +int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, + const char *objname, u32 *out_sid) { return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, - NULL, out_sid, false); + objname, out_sid, false); } /** @@ -2209,10 +2217,11 @@ out_unlock: goto out; } for (i = 0, j = 0; i < mynel; i++) { + struct av_decision dummy_avd; rc = avc_has_perm_noaudit(fromsid, mysids[i], SECCLASS_PROCESS, /* kernel value */ PROCESS__TRANSITION, AVC_STRICT, - NULL); + &dummy_avd); if (!rc) mysids2[j++] = mysids[i]; cond_resched(); @@ -3190,7 +3199,7 @@ out: * @len: length of data in bytes * */ -int security_read_policy(void **data, ssize_t *len) +int security_read_policy(void **data, size_t *len) { int rc; struct policy_file fp; diff --git a/security/smack/smack.h b/security/smack/smack.h index b449cfd..2b6c6a5 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -316,22 +316,17 @@ static inline void smk_ad_setfield_u_tsk(struct smk_audit_info *a, static inline void smk_ad_setfield_u_fs_path_dentry(struct smk_audit_info *a, struct dentry *d) { - a->a.u.fs.path.dentry = d; -} -static inline void smk_ad_setfield_u_fs_path_mnt(struct smk_audit_info *a, - struct vfsmount *m) -{ - a->a.u.fs.path.mnt = m; + a->a.u.dentry = d; } static inline void smk_ad_setfield_u_fs_inode(struct smk_audit_info *a, struct inode *i) { - a->a.u.fs.inode = i; + a->a.u.inode = i; } static inline void smk_ad_setfield_u_fs_path(struct smk_audit_info *a, struct path p) { - a->a.u.fs.path = p; + a->a.u.path = p; } static inline void smk_ad_setfield_u_net_sk(struct smk_audit_info *a, struct sock *sk) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 400a5d5..9831a39 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -383,7 +383,7 @@ static int smack_sb_statfs(struct dentry *dentry) int rc; struct smk_audit_info ad; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); rc = smk_curacc(sbp->smk_floor, MAY_READ, &ad); @@ -407,7 +407,7 @@ static int smack_sb_mount(char *dev_name, struct path *path, struct superblock_smack *sbp = path->mnt->mnt_sb->s_security; struct smk_audit_info ad; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, *path); return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad); @@ -425,10 +425,13 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags) { struct superblock_smack *sbp; struct smk_audit_info ad; + struct path path; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); - smk_ad_setfield_u_fs_path_dentry(&ad, mnt->mnt_root); - smk_ad_setfield_u_fs_path_mnt(&ad, mnt); + path.dentry = mnt->mnt_root; + path.mnt = mnt; + + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); + smk_ad_setfield_u_fs_path(&ad, path); sbp = mnt->mnt_sb->s_security; return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad); @@ -563,7 +566,7 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, struct smk_audit_info ad; int rc; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry); isp = smk_of_inode(old_dentry->d_inode); @@ -592,7 +595,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) struct smk_audit_info ad; int rc; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); /* @@ -623,7 +626,7 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) struct smk_audit_info ad; int rc; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); /* @@ -663,7 +666,7 @@ static int smack_inode_rename(struct inode *old_inode, char *isp; struct smk_audit_info ad; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry); isp = smk_of_inode(old_dentry->d_inode); @@ -700,7 +703,7 @@ static int smack_inode_permission(struct inode *inode, int mask, unsigned flags) /* May be droppable after audit */ if (flags & IPERM_FLAG_RCU) return -ECHILD; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, inode); return smk_curacc(smk_of_inode(inode), mask, &ad); } @@ -720,7 +723,7 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) */ if (iattr->ia_valid & ATTR_FORCE) return 0; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); return smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); @@ -736,10 +739,13 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) { struct smk_audit_info ad; + struct path path; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); - smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - smk_ad_setfield_u_fs_path_mnt(&ad, mnt); + path.dentry = dentry; + path.mnt = mnt; + + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); + smk_ad_setfield_u_fs_path(&ad, path); return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); } @@ -784,7 +790,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, } else rc = cap_inode_setxattr(dentry, name, value, size, flags); - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); if (rc == 0) @@ -845,7 +851,7 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name) { struct smk_audit_info ad; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); @@ -877,7 +883,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) } else rc = cap_inode_removexattr(dentry, name); - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); if (rc == 0) rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); @@ -1047,7 +1053,7 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, int rc = 0; struct smk_audit_info ad; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); if (_IOC_DIR(cmd) & _IOC_WRITE) @@ -1070,8 +1076,8 @@ static int smack_file_lock(struct file *file, unsigned int cmd) { struct smk_audit_info ad; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); - smk_ad_setfield_u_fs_path_dentry(&ad, file->f_path.dentry); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); + smk_ad_setfield_u_fs_path(&ad, file->f_path); return smk_curacc(file->f_security, MAY_WRITE, &ad); } @@ -1089,7 +1095,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, struct smk_audit_info ad; int rc; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); switch (cmd) { diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 7556315..a0d09e5 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -108,10 +108,9 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head) head->read_user_buf += len; w += len; } - if (*w) { - head->r.w[0] = w; + head->r.w[0] = w; + if (*w) return false; - } /* Add '\0' for query. */ if (head->poll) { if (!head->read_user_buf_avail || @@ -459,8 +458,16 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) if (profile == &tomoyo_default_profile) return -EINVAL; if (!strcmp(data, "COMMENT")) { - const struct tomoyo_path_info *old_comment = profile->comment; - profile->comment = tomoyo_get_name(cp); + static DEFINE_SPINLOCK(lock); + const struct tomoyo_path_info *new_comment + = tomoyo_get_name(cp); + const struct tomoyo_path_info *old_comment; + if (!new_comment) + return -ENOMEM; + spin_lock(&lock); + old_comment = profile->comment; + profile->comment = new_comment; + spin_unlock(&lock); tomoyo_put_name(old_comment); return 0; } diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index cb09f1f..d64e8ec 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -1011,7 +1011,6 @@ int tomoyo_path_perm(const u8 operation, struct path *path) break; case TOMOYO_TYPE_RMDIR: case TOMOYO_TYPE_CHROOT: - case TOMOYO_TYPE_UMOUNT: tomoyo_add_slash(&buf); break; } diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 2976126..42a7b1b 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -75,6 +75,7 @@ void *tomoyo_commit_ok(void *data, const unsigned int size) memset(data, 0, size); return ptr; } + kfree(ptr); return NULL; } diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 82bf8c2..162a864 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -143,6 +143,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, goto out; } requested_dev_name = tomoyo_realpath_from_path(&path); + path_put(&path); if (!requested_dev_name) { error = -ENOENT; goto out; diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 9bfc1ee..6d53932 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -390,7 +390,7 @@ bool tomoyo_correct_domain(const unsigned char *domainname) if (!cp) break; if (*domainname != '/' || - !tomoyo_correct_word2(domainname, cp - domainname - 1)) + !tomoyo_correct_word2(domainname, cp - domainname)) goto out; domainname = cp + 1; } |