M: Eric Paris <eparis@parisplace.org>
L: selinux@tycho.nsa.gov (subscribers-only, general discussion)
W: http://selinuxproject.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6.git
+T: git git://git.infradead.org/users/eparis/selinux.git
S: Supported
F: include/linux/selinux*
F: security/selinux/
+F: scripts/selinux/
APPARMOR SECURITY MODULE
M: John Johansen <john.johansen@canonical.com>
/* Auxiliary data to use in generating the audit record. */
struct common_audit_data {
char type;
-#define LSM_AUDIT_DATA_FS 1
+#define LSM_AUDIT_DATA_PATH 1
#define LSM_AUDIT_DATA_NET 2
#define LSM_AUDIT_DATA_CAP 3
#define LSM_AUDIT_DATA_IPC 4
#define LSM_AUDIT_DATA_KEY 6
#define LSM_AUDIT_DATA_NONE 7
#define LSM_AUDIT_DATA_KMOD 8
+#define LSM_AUDIT_DATA_INODE 9
+#define LSM_AUDIT_DATA_DENTRY 10
struct task_struct *tsk;
union {
- struct {
- struct path path;
- struct inode *inode;
- } fs;
+ struct path path;
+ struct dentry *dentry;
+ struct inode *inode;
struct {
int netif;
struct sock *sk;
gfp_t flags)
{
struct flex_array *ret;
- int max_size = FLEX_ARRAY_NR_BASE_PTRS *
- FLEX_ARRAY_ELEMENTS_PER_PART(element_size);
+ int max_size = 0;
+
+ if (element_size)
+ max_size = FLEX_ARRAY_NR_BASE_PTRS *
+ FLEX_ARRAY_ELEMENTS_PER_PART(element_size);
/* max_size will end up 0 if element_size > PAGE_SIZE */
if (total > max_size)
int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src,
gfp_t flags)
{
- int part_nr = fa_element_to_part_nr(fa, element_nr);
+ int part_nr;
struct flex_array_part *part;
void *dst;
if (element_nr >= fa->total_nr_elements)
return -ENOSPC;
+ if (!fa->element_size)
+ return 0;
if (elements_fit_in_base(fa))
part = (struct flex_array_part *)&fa->parts[0];
else {
+ part_nr = fa_element_to_part_nr(fa, element_nr);
part = __fa_get_part(fa, part_nr, flags);
if (!part)
return -ENOMEM;
*/
int flex_array_clear(struct flex_array *fa, unsigned int element_nr)
{
- int part_nr = fa_element_to_part_nr(fa, element_nr);
+ int part_nr;
struct flex_array_part *part;
void *dst;
if (element_nr >= fa->total_nr_elements)
return -ENOSPC;
+ if (!fa->element_size)
+ return 0;
if (elements_fit_in_base(fa))
part = (struct flex_array_part *)&fa->parts[0];
else {
+ part_nr = fa_element_to_part_nr(fa, element_nr);
part = fa->parts[part_nr];
if (!part)
return -EINVAL;
if (end >= fa->total_nr_elements)
return -ENOSPC;
+ if (!fa->element_size)
+ return 0;
if (elements_fit_in_base(fa))
return 0;
start_part = fa_element_to_part_nr(fa, start);
*/
void *flex_array_get(struct flex_array *fa, unsigned int element_nr)
{
- int part_nr = fa_element_to_part_nr(fa, element_nr);
+ int part_nr;
struct flex_array_part *part;
+ if (!fa->element_size)
+ return NULL;
if (element_nr >= fa->total_nr_elements)
return NULL;
if (elements_fit_in_base(fa))
part = (struct flex_array_part *)&fa->parts[0];
else {
+ part_nr = fa_element_to_part_nr(fa, element_nr);
part = fa->parts[part_nr];
if (!part)
return NULL;
int part_nr;
int ret = 0;
- if (!fa->total_nr_elements)
+ if (!fa->total_nr_elements || !fa->element_size)
return 0;
if (elements_fit_in_base(fa))
return ret;
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)
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) {
* 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;
continue;
default:
BUG();
+ return;
};
/* we need a comma before each option */
seq_putc(m, ',');
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);
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);
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);
}
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,
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,
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);
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)
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;
{
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)
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;
/* Revalidate access to inherited open files. */
- COMMON_AUDIT_DATA_INIT(&ad, FS);
+ COMMON_AUDIT_DATA_INIT(&ad, INODE);
spin_lock(&files->file_lock);
for (;;) {
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);
}
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);
}
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)
{
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)
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;
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)
/* 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,
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);
{
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)
#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 */
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);
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);
break;
default:
BUG();
+ return NULL;
}
list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
#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. */
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)
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)
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;
length = len;
out:
kfree(newcon);
+ kfree(namebuf);
kfree(tcon);
kfree(scon);
return length;
};
struct vfsmount *selinuxfs_mount;
+static struct kobject *selinuxfs_kobj;
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)) {
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
void exit_sel_fs(void)
{
+ kobject_put(selinuxfs_kobj);
unregister_filesystem(&sel_fs_type);
}
#endif
.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)
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;
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;
};
#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
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;
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();
}
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);
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);
rt = NULL;
r = NULL;
}
- rangetr_hash_eval(p->range_tr);
+ hash_eval(p->range_tr, "rangetr");
rc = 0;
out:
kfree(rt);
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;
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));
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;
}
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;
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;
if (rc)
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");
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;
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;
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;
/* 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;
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
if (rc)
return rc;
- rc = role_trans_write(p->role_tr, fp);
+ rc = role_trans_write(p, fp);
if (rc)
return rc;
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 */
};
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;
}
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)
{
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;
}
}
}
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);
}
/**
* @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;
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)
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);
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);
{
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);
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);
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);
/*
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);
/*
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);
/* 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);
}
*/
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);
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);
}
} 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)
{
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);
} 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);
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)
{
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);
}
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) {