diff -urN -X dontdiff linux-2.6.2-rc1.p/security/selinux/hooks.c linux-2.6.2-rc1.w/security/selinux/hooks.c --- linux-2.6.2-rc1.p/security/selinux/hooks.c 2004-01-21 13:39:03.000000000 -0500 +++ linux-2.6.2-rc1.w/security/selinux/hooks.c 2004-01-23 12:13:50.000000000 -0500 @@ -56,6 +56,8 @@ #include #include /* for Unix socket types */ #include /* for Unix socket types */ +#include +#include #include "avc.h" #include "objsec.h" @@ -298,7 +300,107 @@ return inode_doinit_with_dentry(inode, NULL); } -static int superblock_doinit(struct super_block *sb) +enum { + Opt_context, + Opt_fscontext, + Opt_defcontext, +}; + +static match_table_t tokens = { + {Opt_context, "context=%s"}, + {Opt_fscontext, "fscontext=%s"}, + {Opt_defcontext, "defcontext=%s"}, +}; + +static int try_context_mount(struct super_block *sb, void *data) +{ + char *context = NULL; + const char *name; + u32 sid; + int alloc = 0, rc = 0; + struct task_security_struct *tsec = current->security; + struct superblock_security_struct *sbsec = sb->s_security; + + if (!data || sbsec->proc || sbsec->behavior != SECURITY_FS_USE_GENFS) + goto out; + + /* XXX: todo handle EA, fscontext, defcontext */ + + name = sb->s_type->name; + + /* Ignore these fileystems with binary mount option data. */ + if (!strcmp(name, "coda") || + !strcmp(name, "afs") || !strcmp(name, "smbfs")) + goto out; + + /* NFS we understand. */ + if (!strcmp(name, "nfs")) { + struct nfs_mount_data *d = data; + + if (d->version < NFS_MOUNT_VERSION) + goto out; + + if (d->context[0]) + context = d->context; + } else { + char *p, *options = data; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + substring_t args[MAX_OPT_ARGS]; + + if (!*p) + continue; + + token = match_token(p, tokens, args); + if (token == Opt_context) { + context = match_strdup(&args[0]); + if (!context) { + rc = -ENOMEM; + goto out; + } + alloc = 1; + break; + } + } + } + + if (!context) + goto out; + + rc = security_context_to_sid(context, strlen(context), &sid); + if (rc) { + printk(KERN_WARNING "SELinux: security_context_to_sid(%s) " + "failed for (dev %s, type %s) errno=%d\n", + context, sb->s_id, name, rc); + goto out_free; + } + + if (sid == sbsec->sid) + goto out_free; + + rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELFROM, NULL, NULL); + if (rc) + goto out_free; + + rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELTO, NULL, NULL); + if (rc) + goto out_free; + + sbsec->sid = sid; + printk(KERN_INFO "SELinux: mount context %s for (dev %s, type %s)\n", + context, sb->s_id, name); + +out_free: + if (alloc) + kfree(context); +out: + return rc; +} + +static int superblock_doinit(struct super_block *sb, void *data) { struct superblock_security_struct *sbsec = sb->s_security; struct dentry *root = sb->s_root; @@ -357,6 +459,10 @@ if (strcmp(sb->s_type->name, "proc") == 0) sbsec->proc = 1; + rc = try_context_mount(sb, data); + if (rc) + goto out; + sbsec->initialized = 1; if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) { @@ -1660,12 +1766,83 @@ superblock_free_security(sb); } -static int selinux_sb_kern_mount(struct super_block *sb) +static inline int match_prefix(char *prefix, int plen, char *option, int olen) +{ + if (plen > olen) + return 0; + + return !memcmp(prefix, option, plen); +} + +static inline int selinux_option(char *option, int len) +{ + return (match_prefix("context=", sizeof("context=")-1, option, len) || + match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || + match_prefix("defcontext=", sizeof("defcontext=")-1, option, len)); +} + +static inline void take_option(char **to, char *from, int *first, int len) +{ + if (!*first) { + **to = ','; + *to += 1; + } + else + *first = 0; + memcpy(*to, from, len); + *to += len; +} + +static int selinux_sb_copy_data(const char *fstype, void *orig, void *copy) +{ + int fnosec, fsec, rc = 0; + char *in_save, *in_curr, *in_end; + char *sec_curr, *nosec_save, *nosec; + + in_curr = orig; + sec_curr = copy; + + /* Binary mount data: just copy */ + if (!strcmp(fstype, "nfs") || !strcmp(fstype, "coda") || + !strcmp(fstype, "smbfs") || !strcmp(fstype, "afs")) { + copy_page(sec_curr, in_curr); + goto out; + } + + nosec = (char *)get_zeroed_page(GFP_KERNEL); + if (!nosec) { + rc = -ENOMEM; + goto out; + } + + nosec_save = nosec; + fnosec = fsec = 1; + in_save = in_end = orig; + + do { + if (*in_end == ',' || *in_end == '\0') { + int len = in_end - in_curr; + + if (selinux_option(in_curr, len)) + take_option(&sec_curr, in_curr, &fsec, len); + else + take_option(&nosec, in_curr, &fnosec, len); + + in_curr = in_end + 1; + } + } while (*in_end++); + + copy_page(in_save, nosec_save); +out: + return rc; +} + +static int selinux_sb_kern_mount(struct super_block *sb, void *data) { struct avc_audit_data ad; int rc; - rc = superblock_doinit(sb); + rc = superblock_doinit(sb, data); if (rc) return rc; @@ -3555,6 +3732,7 @@ .sb_alloc_security = selinux_sb_alloc_security, .sb_free_security = selinux_sb_free_security, + .sb_copy_data = selinux_sb_copy_data, .sb_kern_mount = selinux_sb_kern_mount, .sb_statfs = selinux_sb_statfs, .sb_mount = selinux_mount, @@ -3731,7 +3909,7 @@ spin_unlock(&sb_security_lock); down_read(&sb->s_umount); if (sb->s_root) - superblock_doinit(sb); + superblock_doinit(sb, NULL); drop_super(sb); spin_lock(&sb_security_lock); list_del_init(&sbsec->list);