diff options
Diffstat (limited to 'security/smack')
-rw-r--r-- | security/smack/smack.h | 5 | ||||
-rw-r--r-- | security/smack/smack_lsm.c | 145 | ||||
-rw-r--r-- | security/smack/smackfs.c | 134 |
3 files changed, 183 insertions, 101 deletions
diff --git a/security/smack/smack.h b/security/smack/smack.h index 364cc64fce71..d072fd32212d 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -241,7 +241,8 @@ u32 smack_to_secid(const char *); extern int smack_cipso_direct; extern int smack_cipso_mapped; extern struct smack_known *smack_net_ambient; -extern char *smack_onlycap; +extern struct smack_known *smack_onlycap; +extern struct smack_known *smack_syslog_label; extern const char *smack_cipso_option; extern struct smack_known smack_known_floor; @@ -312,7 +313,7 @@ static inline int smack_privileged(int cap) if (!capable(cap)) return 0; - if (smack_onlycap == NULL || smack_onlycap == skp->smk_known) + if (smack_onlycap == NULL || smack_onlycap == skp) return 1; return 0; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index b0be893ad44d..14f52be78c75 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -219,8 +219,6 @@ static int smack_ptrace_traceme(struct task_struct *ptp) * smack_syslog - Smack approval on syslog * @type: message type * - * Require that the task has the floor label - * * Returns 0 on success, error code otherwise. */ static int smack_syslog(int typefrom_file) @@ -231,7 +229,7 @@ static int smack_syslog(int typefrom_file) if (smack_privileged(CAP_MAC_OVERRIDE)) return 0; - if (skp != &smack_known_floor) + if (smack_syslog_label != NULL && smack_syslog_label != skp) rc = -EACCES; return rc; @@ -341,10 +339,12 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) struct inode *inode = root->d_inode; struct superblock_smack *sp = sb->s_security; struct inode_smack *isp; + struct smack_known *skp; char *op; char *commap; char *nsp; int transmute = 0; + int specified = 0; if (sp->smk_initialized) return 0; @@ -359,34 +359,56 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) { op += strlen(SMK_FSHAT); nsp = smk_import(op, 0); - if (nsp != NULL) + if (nsp != NULL) { sp->smk_hat = nsp; + specified = 1; + } } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) { op += strlen(SMK_FSFLOOR); nsp = smk_import(op, 0); - if (nsp != NULL) + if (nsp != NULL) { sp->smk_floor = nsp; + specified = 1; + } } else if (strncmp(op, SMK_FSDEFAULT, strlen(SMK_FSDEFAULT)) == 0) { op += strlen(SMK_FSDEFAULT); nsp = smk_import(op, 0); - if (nsp != NULL) + if (nsp != NULL) { sp->smk_default = nsp; + specified = 1; + } } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) { op += strlen(SMK_FSROOT); nsp = smk_import(op, 0); - if (nsp != NULL) + if (nsp != NULL) { sp->smk_root = nsp; + specified = 1; + } } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) { op += strlen(SMK_FSTRANS); nsp = smk_import(op, 0); if (nsp != NULL) { sp->smk_root = nsp; transmute = 1; + specified = 1; } } } + if (!smack_privileged(CAP_MAC_ADMIN)) { + /* + * Unprivileged mounts don't get to specify Smack values. + */ + if (specified) + return -EPERM; + /* + * Unprivileged mounts get root and default from the caller. + */ + skp = smk_of_current(); + sp->smk_root = skp->smk_known; + sp->smk_default = skp->smk_known; + } /* * Initialize the root inode. */ @@ -423,53 +445,6 @@ static int smack_sb_statfs(struct dentry *dentry) return rc; } -/** - * smack_sb_mount - Smack check for mounting - * @dev_name: unused - * @path: mount point - * @type: unused - * @flags: unused - * @data: unused - * - * Returns 0 if current can write the floor of the filesystem - * being mounted on, an error code otherwise. - */ -static int smack_sb_mount(const char *dev_name, struct path *path, - const char *type, unsigned long flags, void *data) -{ - struct superblock_smack *sbp = path->dentry->d_sb->s_security; - struct smk_audit_info ad; - - 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); -} - -/** - * smack_sb_umount - Smack check for unmounting - * @mnt: file system to unmount - * @flags: unused - * - * Returns 0 if current can write the floor of the filesystem - * being unmounted, an error code otherwise. - */ -static int smack_sb_umount(struct vfsmount *mnt, int flags) -{ - struct superblock_smack *sbp; - struct smk_audit_info ad; - struct path path; - - 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 = path.dentry->d_sb->s_security; - return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad); -} - /* * BPRM hooks */ @@ -837,31 +812,43 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct smk_audit_info ad; + struct smack_known *skp; + int check_priv = 0; + int check_import = 0; + int check_star = 0; int rc = 0; + /* + * Check label validity here so import won't fail in post_setxattr + */ if (strcmp(name, XATTR_NAME_SMACK) == 0 || strcmp(name, XATTR_NAME_SMACKIPIN) == 0 || - strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 || - strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || - strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { - if (!smack_privileged(CAP_MAC_ADMIN)) - rc = -EPERM; - /* - * check label validity here so import wont fail on - * post_setxattr - */ - if (size == 0 || size >= SMK_LONGLABEL || - smk_import(value, size) == NULL) - rc = -EINVAL; + strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) { + check_priv = 1; + check_import = 1; + } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || + strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { + check_priv = 1; + check_import = 1; + check_star = 1; } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { - if (!smack_privileged(CAP_MAC_ADMIN)) - rc = -EPERM; + check_priv = 1; if (size != TRANS_TRUE_SIZE || strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0) rc = -EINVAL; } else rc = cap_inode_setxattr(dentry, name, value, size, flags); + if (check_priv && !smack_privileged(CAP_MAC_ADMIN)) + rc = -EPERM; + + if (rc == 0 && check_import) { + skp = smk_import_entry(value, size); + if (skp == NULL || (check_star && + (skp == &smack_known_star || skp == &smack_known_web))) + rc = -EINVAL; + } + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); @@ -1364,7 +1351,7 @@ static int smack_file_receive(struct file *file) int may = 0; struct smk_audit_info ad; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); /* * This code relies on bitmasks. @@ -2847,8 +2834,17 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) if (rc >= 0) transflag = SMK_INODE_TRANSMUTE; } - isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); - isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); + /* + * Don't let the exec or mmap label be "*" or "@". + */ + skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); + if (skp == &smack_known_star || skp == &smack_known_web) + skp = NULL; + isp->smk_task = skp; + skp = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); + if (skp == &smack_known_star || skp == &smack_known_web) + skp = NULL; + isp->smk_mmap = skp; dput(dp); break; @@ -3620,9 +3616,8 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, struct smack_known *skp; char *rule = vrule; - if (!rule) { - audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, - "Smack: missing rule\n"); + if (unlikely(!rule)) { + WARN_ONCE(1, "Smack: missing rule\n"); return -ENOENT; } @@ -3743,8 +3738,6 @@ struct security_operations smack_ops = { .sb_copy_data = smack_sb_copy_data, .sb_kern_mount = smack_sb_kern_mount, .sb_statfs = smack_sb_statfs, - .sb_mount = smack_sb_mount, - .sb_umount = smack_sb_umount, .bprm_set_creds = smack_bprm_set_creds, .bprm_committing_creds = smack_bprm_committing_creds, diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 160aa08e3cd5..3198cfe1dcc6 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -52,6 +52,7 @@ enum smk_inos { SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */ SMK_CHANGE_RULE = 19, /* change or add rules (long labels) */ + SMK_SYSLOG = 20, /* change syslog label) */ }; /* @@ -59,6 +60,7 @@ enum smk_inos { */ static DEFINE_MUTEX(smack_cipso_lock); static DEFINE_MUTEX(smack_ambient_lock); +static DEFINE_MUTEX(smack_syslog_lock); static DEFINE_MUTEX(smk_netlbladdr_lock); /* @@ -90,7 +92,13 @@ int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT; * everyone. It is expected that the hat (^) label * will be used if any label is used. */ -char *smack_onlycap; +struct smack_known *smack_onlycap; + +/* + * If this value is set restrict syslog use to the label specified. + * It can be reset via smackfs/syslog + */ +struct smack_known *smack_syslog_label; /* * Certain IP addresses may be designated as single label hosts. @@ -301,7 +309,8 @@ static int smk_perm_from_str(const char *string) * @import: if non-zero, import labels * @len: label length limit * - * Returns 0 on success, -1 on failure + * Returns 0 on success, -EINVAL on failure and -ENOENT when either subject + * or object is missing. */ static int smk_fill_rule(const char *subject, const char *object, const char *access1, const char *access2, @@ -314,28 +323,28 @@ static int smk_fill_rule(const char *subject, const char *object, if (import) { rule->smk_subject = smk_import_entry(subject, len); if (rule->smk_subject == NULL) - return -1; + return -EINVAL; rule->smk_object = smk_import(object, len); if (rule->smk_object == NULL) - return -1; + return -EINVAL; } else { cp = smk_parse_smack(subject, len); if (cp == NULL) - return -1; + return -EINVAL; skp = smk_find_entry(cp); kfree(cp); if (skp == NULL) - return -1; + return -ENOENT; rule->smk_subject = skp; cp = smk_parse_smack(object, len); if (cp == NULL) - return -1; + return -EINVAL; skp = smk_find_entry(cp); kfree(cp); if (skp == NULL) - return -1; + return -ENOENT; rule->smk_object = skp->smk_known; } @@ -381,6 +390,7 @@ static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule, { ssize_t cnt = 0; char *tok[4]; + int rc; int i; /* @@ -405,10 +415,8 @@ static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule, while (i < 4) tok[i++] = NULL; - if (smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0)) - return -1; - - return cnt; + rc = smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0); + return rc == 0 ? cnt : rc; } #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ @@ -1603,7 +1611,7 @@ static const struct file_operations smk_ambient_ops = { }; /** - * smk_read_onlycap - read() for /smack/onlycap + * smk_read_onlycap - read() for smackfs/onlycap * @filp: file pointer, not actually used * @buf: where to put the result * @cn: maximum to send along @@ -1622,7 +1630,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf, return 0; if (smack_onlycap != NULL) - smack = smack_onlycap; + smack = smack_onlycap->smk_known; asize = strlen(smack) + 1; @@ -1633,7 +1641,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf, } /** - * smk_write_onlycap - write() for /smack/onlycap + * smk_write_onlycap - write() for smackfs/onlycap * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent @@ -1656,7 +1664,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, * explicitly for clarity. The smk_access() implementation * would use smk_access(smack_onlycap, MAY_WRITE) */ - if (smack_onlycap != NULL && smack_onlycap != skp->smk_known) + if (smack_onlycap != NULL && smack_onlycap != skp) return -EPERM; data = kzalloc(count, GFP_KERNEL); @@ -1676,7 +1684,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, if (copy_from_user(data, buf, count) != 0) rc = -EFAULT; else - smack_onlycap = smk_import(data, count); + smack_onlycap = smk_import_entry(data, count); kfree(data); return rc; @@ -1856,11 +1864,12 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, res = smk_parse_long_rule(data, &rule, 0, 3); } - if (res < 0) + if (res >= 0) + res = smk_access(rule.smk_subject, rule.smk_object, + rule.smk_access1, NULL); + else if (res != -ENOENT) return -EINVAL; - res = smk_access(rule.smk_subject, rule.smk_object, - rule.smk_access1, NULL); data[0] = res == 0 ? '1' : '0'; data[1] = '\0'; @@ -2143,7 +2152,7 @@ static ssize_t smk_write_change_rule(struct file *file, const char __user *buf, /* * Must have privilege. */ - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, @@ -2158,12 +2167,89 @@ static const struct file_operations smk_change_rule_ops = { }; /** - * smk_fill_super - fill the /smackfs superblock + * smk_read_syslog - read() for smackfs/syslog + * @filp: file pointer, not actually used + * @buf: where to put the result + * @cn: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_syslog(struct file *filp, char __user *buf, + size_t cn, loff_t *ppos) +{ + struct smack_known *skp; + ssize_t rc = -EINVAL; + int asize; + + if (*ppos != 0) + return 0; + + if (smack_syslog_label == NULL) + skp = &smack_known_star; + else + skp = smack_syslog_label; + + asize = strlen(skp->smk_known) + 1; + + if (cn >= asize) + rc = simple_read_from_buffer(buf, cn, ppos, skp->smk_known, + asize); + + return rc; +} + +/** + * smk_write_syslog - write() for smackfs/syslog + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_syslog(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char *data; + struct smack_known *skp; + int rc = count; + + if (!smack_privileged(CAP_MAC_ADMIN)) + return -EPERM; + + data = kzalloc(count, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) + rc = -EFAULT; + else { + skp = smk_import_entry(data, count); + if (skp == NULL) + rc = -EINVAL; + else + smack_syslog_label = smk_import_entry(data, count); + } + + kfree(data); + return rc; +} + +static const struct file_operations smk_syslog_ops = { + .read = smk_read_syslog, + .write = smk_write_syslog, + .llseek = default_llseek, +}; + + +/** + * smk_fill_super - fill the smackfs superblock * @sb: the empty superblock * @data: unused * @silent: unused * - * Fill in the well known entries for /smack + * Fill in the well known entries for the smack filesystem * * Returns 0 on success, an error code on failure */ @@ -2208,6 +2294,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) S_IRUGO|S_IWUSR}, [SMK_CHANGE_RULE] = { "change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR}, + [SMK_SYSLOG] = { + "syslog", &smk_syslog_ops, S_IRUGO|S_IWUSR}, /* last one */ {""} }; |