diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/Kconfig linux-2.6.22/security/Kconfig --- linux-2.6.22-base/security/Kconfig 2007-07-08 16:32:17.000000000 -0700 +++ linux-2.6.22/security/Kconfig 2007-07-10 01:08:05.000000000 -0700 @@ -94,6 +94,7 @@ config SECURITY_ROOTPLUG If you are unsure how to answer this question, answer N. source security/selinux/Kconfig +source security/smack/Kconfig endmenu diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/Makefile linux-2.6.22/security/Makefile --- linux-2.6.22-base/security/Makefile 2007-07-08 16:32:17.000000000 -0700 +++ linux-2.6.22/security/Makefile 2007-07-10 01:08:05.000000000 -0700 @@ -4,6 +4,7 @@ obj-$(CONFIG_KEYS) += keys/ subdir-$(CONFIG_SECURITY_SELINUX) += selinux +subdir-$(CONFIG_SECURITY_SMACK) += smack # if we don't select a security model, use the default capabilities ifneq ($(CONFIG_SECURITY),y) @@ -14,5 +15,6 @@ endif obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o # Must precede capability.o in order to stack properly. obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o +obj-$(CONFIG_SECURITY_SMACK) += commoncap.o smack/built-in.o obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/Kconfig linux-2.6.22/security/smack/Kconfig --- linux-2.6.22-base/security/smack/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.22/security/smack/Kconfig 2007-07-10 01:08:05.000000000 -0700 @@ -0,0 +1,10 @@ +config SECURITY_SMACK + bool "Simplified Mandatory Access Control Kernel Support" + depends on NETLABEL && SECURITY_NETWORK + default n + help + This selects the Simplified Mandatory Access Control Kernel. + SMACK is useful for sensitivity, integrity, and a variety + of other madatory security schemes. + If you are unsure how to answer this question, answer N. + diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/Makefile linux-2.6.22/security/smack/Makefile --- linux-2.6.22-base/security/smack/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.22/security/smack/Makefile 2007-07-10 01:08:05.000000000 -0700 @@ -0,0 +1,8 @@ +# +# Makefile for the SMACK LSM +# + +obj-$(CONFIG_SECURITY_SMACK) := smack.o + +smack-y := smack_lsm.o smack_access.o smackfs.o + diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smack_access.c linux-2.6.22/security/smack/smack_access.c --- linux-2.6.22-base/security/smack/smack_access.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.22/security/smack/smack_access.c 2007-07-10 01:08:05.000000000 -0700 @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2007 Casey Schaufler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + * Author: + * Casey Schaufler + * + */ + +#include +#include +#include +#include "smack.h" + +struct smk_list_entry *smack_list = NULL; + +static smack_access_t smk_get_access(smack_t *sub_label, smack_t *obj_label) +{ + struct smk_list_entry *sp = smack_list; + + for (; sp != NULL; sp = sp->smk_next) + if (sp->smk_rule.smk_subject == *sub_label && + sp->smk_rule.smk_object == *obj_label) + return sp->smk_rule.smk_access; + /* + * No access is explicitly defined for this pair. + */ + return MAY_NOT; +} + +int smk_access(smack_t *sub_label, smack_t *obj_label, int requested) +{ + /* + * Hardcoded comparisons. + * + * A star subject can't access any object. + * A dash subject can't access any object. + */ + if (*sub_label == SMK_STAR) + return -EACCES; + if (*sub_label == SMK_DASH) + return -EACCES; + /* + * A star object can be accessed by any subject. + */ + if (*obj_label == SMK_STAR) + return 0; + /* + * A dash object can be accessed by any subject. + * This should be restricted to redirecting symlinks. + */ + if (*obj_label == SMK_DASH) + return 0; + /* + * An object can be accessed in any way by a subject + * with the same label. + */ + if (*sub_label == *obj_label) + return 0; + /* + * A hat subject can read any object. + * A floor object can be read by any subject. + */ + if (*sub_label == SMK_HAT && + ((requested & (MAY_READ | MAY_EXEC)) == requested)) + return 0; + + if (*obj_label == SMK_FLOOR && + ((requested & (MAY_READ | MAY_EXEC)) == requested)) + return 0; + /* + * Beyond here an explicit relationship is required. + * If the requested access is contained in the available + * access (e.g. read is included in readwrite) it's + * good. + * + * Yes, that is supposed to be a single ampersand. + */ + if ((requested & smk_get_access(sub_label, obj_label)) == requested) + return 0; + + return -EACCES; +} + +int smk_curacc(smack_t *obj_label, u32 mode) +{ + struct task_smack *tsp = current->security; + int rc; + + rc = smk_access(&tsp->smk_task, obj_label, mode); + if (rc == 0) + return 0; + + if (capable(CAP_MAC_OVERRIDE)) + return 0; + + return rc; +} + +/* + * Allocate and free functions for the security blob. + */ +smack_t *new_smack_t(smack_t value) +{ + smack_t *bp; + + bp = kzalloc(sizeof(smack_t), GFP_KERNEL); + if (bp != NULL) + *bp = value; + + return bp; +} + +smack_t smk_from_string(char *str) +{ + smack_t smack = 0LL; + char *cp; + int i; + + for (cp = (char *)&smack, i = 0; i < sizeof(smack_t); str++,cp++,i++) { + if (*str <= ' ' || *str > '~') + return smack; + *cp = *str; + } + /* + * Too long. + */ + return SMK_INVALID; +} diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smack_access.h linux-2.6.22/security/smack/smack_access.h --- linux-2.6.22-base/security/smack/smack_access.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.22/security/smack/smack_access.h 2007-07-10 01:08:05.000000000 -0700 @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2007 Casey Schaufler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + * Author: + * Casey Schaufler + * + */ +smack_t smk_from_string(char *); + +int smk_access(smack_t *, smack_t *, smack_access_t); +int smk_curacc(smack_t *, u32); + +smack_access_t smk_get_access(smack_t *, smack_t *); + +smack_t *new_smack_t(smack_t); + +static inline smack_t *smk_of_task(struct task_struct *tsp) +{ + struct task_smack *stp = tsp->security; + return &stp->smk_task; +} diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smackfs.c linux-2.6.22/security/smack/smackfs.c --- linux-2.6.22-base/security/smack/smackfs.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.22/security/smack/smackfs.c 2007-07-10 01:08:05.000000000 -0700 @@ -0,0 +1,1077 @@ +/* + * Copyright (C) 2007 Casey Schaufler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + * Author: + * Casey Schaufler + * + * Special thanks to the authors of selinuxfs. They had a good idea. + * + * Karl MacMillan + * James Morris + * + */ + +#include +#include +#include +#include +#include "../../net/netlabel/netlabel_domainhash.h" +#include +#include "smack.h" +#include "smack_access.h" + +/* + * smackfs pseudo filesystem. + */ + +extern struct smk_list_entry *smack_list; +extern struct smk_cipso_entry *smack_cipso; +extern smack_t smack_net_ambient; +extern int smack_net_nltype; + +enum smk_inos { + SMK_ROOT_INO = 2, + SMK_LOAD = 3, /* load policy */ + SMK_SELF = 4, /* get/set own smack information */ + SMK_PACKET = 5, /* packet internet label */ + SMK_LINKS = 6, + SMK_CIPSO = 7, /* load label -> CIPSO mapping */ + SMK_AMBIENT = 8, /* internet ambient label */ + SMK_IPIN = 9, /* incoming internet label */ + SMK_IPOUT = 10, /* outgoing internet label */ + SMK_IPOPTION = 11, /* outgoing internet label option */ + SMK_NLTYPE = 12, /* label scheme to use by default */ + SMK_TMP = 100, /* MUST BE LAST! /smack/tmp */ +}; + +static ssize_t smk_read_self(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + smack_t *sp = smk_of_task(current); + ssize_t rc; + + if (count < sizeof(smack_t)) + return -EINVAL; + + if (*ppos != 0) + return 0; + + rc = simple_read_from_buffer(buf, count, ppos, sp, strlen((char *)sp)); + + return rc; +} + +static ssize_t smk_write_self(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + smack_t smack = 0LL; + struct task_smack *tsp = current->security; + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + + if (count > sizeof(smack_t)) + return -EINVAL; + + if (copy_from_user(&smack, buf, count) != 0) + return -EFAULT; + + smack = smk_from_string((char *)&smack); + if (smack == SMK_INVALID) + return -EINVAL; + /* + * Better check to be sure this is OK. + */ + tsp->smk_task = smack; + tsp->smk_out = smack; + tsp->smk_in = smack; + + return count; +} + +static struct file_operations smk_self_ops = { + .read = smk_read_self, + .write = smk_write_self, +}; + +static int smack_list_count; + +static ssize_t smk_read_load(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t bytes; + struct smk_list_entry *slp = smack_list; + struct smack_rule *srp; + char *result; + char *cp; + int realbytes = 0; + + bytes = (sizeof(struct smack_rule) + 4) * smack_list_count; + if (bytes == 0) + return 0; + + result = kzalloc(bytes, GFP_KERNEL); + if (result == NULL) + return -ENOMEM; + + for (cp = result; slp != NULL; slp = slp->smk_next) { + srp = &slp->smk_rule; + sprintf(cp, "%-8s %-8s", + (char *)&srp->smk_subject, (char *)&srp->smk_object); + cp += strlen(cp); + if (srp->smk_access != 0) + *cp++ = ' '; + if ((srp->smk_access & MAY_READ) != 0) + *cp++ = 'r'; + if ((srp->smk_access & MAY_WRITE) != 0) + *cp++ = 'w'; + if ((srp->smk_access & MAY_EXEC) != 0) + *cp++ = 'x'; + if ((srp->smk_access & MAY_APPEND) != 0) + *cp++ = 'a'; + *cp++ = '\n'; + } + *cp++ = '\0'; + realbytes = strlen(result); + + bytes = simple_read_from_buffer(buf, count, ppos, result, realbytes); + + kfree(result); + + return bytes; +} + +/* + * For purposes of SMACK: + * append is a form of write + * exec is a form of read + * This isn't reflected here, but I thought I should mention it. + */ +static void smk_set_access(struct smack_rule *srp) +{ + struct smk_list_entry *sp = smack_list; + struct smk_list_entry *newp; + + for (; sp != NULL; sp = sp->smk_next) + if (sp->smk_rule.smk_subject == srp->smk_subject && + sp->smk_rule.smk_object == srp->smk_object) { + sp->smk_rule.smk_access = srp->smk_access; + return; + } + + newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL); + newp->smk_rule = *srp; + newp->smk_prev = NULL; + newp->smk_next = smack_list; + if (smack_list != NULL) + smack_list->smk_prev = newp; + smack_list = newp; + smack_list_count++; + + return; +} + + +static ssize_t smk_write_load(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct smack_rule rule; + ssize_t rc = count; + char *data = NULL; + char modestr[8]; + char *cp; + + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + /* + * No partial writes. + */ + if (*ppos != 0) + return -EINVAL; + /* + * Casey wonders about this number. + */ + if ((count > 64 * 1024 * 1024) || (data = vmalloc(count + 1)) == NULL) + return -ENOMEM; + *(data + count) = '\0'; + + if (copy_from_user(data, buf, count) == 0) { + for (cp = data - 1; cp != NULL; cp = strchr(cp + 1, '\n')) { + if (*++cp == '\0') + break; + if (sscanf(cp, "%7s %7s %7s\n", + (char *)&rule.smk_subject, + (char *)&rule.smk_object, modestr) != 3) { + printk("%s:%d bad scan\n", + __FUNCTION__, __LINE__); + break; + } + rule.smk_subject = + smk_from_string((char *)&rule.smk_subject); + if (rule.smk_subject == SMK_INVALID) + break; + rule.smk_object = + smk_from_string((char *)&rule.smk_object); + if (rule.smk_object == SMK_INVALID) + break; + rule.smk_access = 0; + if (strchr(modestr, 'r') || strchr(modestr, 'R')) + rule.smk_access |= MAY_READ; + if (strchr(modestr, 'w') || strchr(modestr, 'W')) + rule.smk_access |= MAY_WRITE; + if (strchr(modestr, 'x') || strchr(modestr, 'X')) + rule.smk_access |= MAY_EXEC; + if (strchr(modestr, 'a') || strchr(modestr, 'A')) + rule.smk_access |= MAY_APPEND; + smk_set_access(&rule); + printk("%s:%d rule %s %s 0x%x\n", + __FUNCTION__, __LINE__, + (char *)&rule.smk_subject, + (char *)&rule.smk_object, + rule.smk_access); + } + } + else + rc = -EFAULT; + + vfree(data); + return rc; +} + +static struct file_operations smk_load_ops = { + .read = smk_read_load, + .write = smk_write_load, +}; + +static char *smk_digit(char *cp) +{ + for (; *cp != '\0'; cp++) + if (*cp >= '0' && *cp <= '9') + return cp; + + return NULL; +} + +static int smk_cipso_doied; +static int smk_cipso_written; + +static void smk_cipso_doi(void) +{ + int rc; + struct cipso_v4_doi *doip; + struct netlbl_dom_map *ndmp; + struct netlbl_audit audit_info; + + if (smk_cipso_doied != 0) + return; + smk_cipso_doied = 1; + + doip = kmalloc(sizeof(struct cipso_v4_doi), GFP_KERNEL); + if (doip == NULL) + panic("smack: Failed to initialize cipso DOI.\n"); + doip->map.std = NULL; + + ndmp = kmalloc(sizeof(struct netlbl_dom_map), GFP_KERNEL); + if (ndmp == NULL) + panic("smack: Failed to initialize cipso ndmp.\n"); + + doip->doi = SMACK_CIPSO_DOI; + doip->type = CIPSO_V4_MAP_PASS; + doip->tags[0] = CIPSO_V4_TAG_RBITMAP; + for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++) + doip->tags[rc] = CIPSO_V4_TAG_INVALID; + + rc = cipso_v4_doi_add(doip); + printk("%s:%d add doi rc = %d\n", __FUNCTION__, __LINE__, rc); + + /* + rc = cipso_v4_doi_domhsh_add(doip, SMACK_CIPSO_DOMAIN_NAME); + printk("%s:%d hash doi rc = %d\n", __FUNCTION__, __LINE__, rc); + */ + ndmp->domain = SMACK_CIPSO_DOMAIN_NAME; + ndmp->type = NETLBL_NLTYPE_CIPSOV4; + ndmp->type_def.cipsov4 = doip; + rc = netlbl_domhsh_add(ndmp, &audit_info); + printk("%s:%d hash doi rc = %d\n", __FUNCTION__, __LINE__, rc); +} + +/* + * label level[/cat[,cat]] + */ +static ssize_t smk_read_cipso(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t bytes; + struct smk_cipso_entry *slp = smack_cipso; + char sep; + char *result; + char *cp; + char *cbp; + int realbytes = 0; + int cat = -1; + unsigned char m; + + smk_cipso_doi(); + + result = kzalloc(smk_cipso_written, GFP_KERNEL); + if (result == NULL) + return -ENOMEM; + cp = result; + + for (slp = smack_cipso; slp != NULL; slp = slp->smk_next) { + sprintf(cp, "%-8s %3d", (char *)&slp->smk_smack,slp->smk_level); + cp += strlen(cp); + cat = 1; + sep = '/'; + for (cbp = (char *)&slp->smk_catset; *cbp != 0; cbp++) { + for (m = 0x80; m != 0; m >>= 1) { + if ((m & *cbp) != 0) { + sprintf(cp, "%c%d", sep, cat); + cp += strlen(cp); + sep = ','; + } + cat++; + } + } + *cp++ = '\n'; + } + *cp++ = '\0'; + realbytes = strlen(result); + + bytes = simple_read_from_buffer(buf, count, ppos, result, realbytes); + + kfree(result); + + return bytes; +} + +static ssize_t smk_write_cipso(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct smk_cipso_entry *scp; + smack_t mapsmack; + smack_t mapcatset; + int maplevel; + ssize_t rc = count; + char *data = NULL; + char *cp; + char *eolp; + char *linep; + int cat; + int i; + + smk_cipso_doi(); + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + /* + * No partial writes. + */ + if (*ppos != 0) + return -EINVAL; + /* + * Casey wonders about this number. + */ + if ((count > 64 * 1024 * 1024) || (data = vmalloc(count + 1)) == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) { + vfree(data); + return -EFAULT; + } + + *(data + count) = '\0'; + smk_cipso_written += count; + + for (eolp = strchr(data, '\n'), linep = data; eolp != NULL; + linep = eolp + 1, eolp = strchr(linep, '\n')) { + + if (eolp == linep) + continue; + *eolp = '\0'; + + mapsmack = smk_from_string(linep); + mapcatset = 0ll; + + if (mapsmack == SMK_INVALID) + continue; + + cp = smk_digit(linep + strlen((char *)&mapsmack)); + if (cp == NULL) + continue; + + i = sscanf(cp, "%d", &maplevel); + if (i != 1) + continue; + + cp = strchr(cp, '/'); + if (cp != NULL) { + cp = smk_digit(cp); + if (cp == NULL) + continue; + + do { + i = sscanf(cp, "%d", &cat); + if (i != 1) + break; + + smack_catset_bit(cat, &mapcatset); + + cp = strchr(cp, ','); + if (cp != NULL) + cp = smk_digit(cp); + } while (cp != NULL); + } + + for (scp = smack_cipso; scp != NULL; scp = scp->smk_next) + if (mapsmack == scp->smk_smack) + break; + + if (scp == NULL) { + scp = kzalloc(sizeof(struct smk_cipso_entry), + GFP_KERNEL); + if (scp == NULL) { + rc = -ENOMEM; + break; + } + scp->smk_prev = NULL; + scp->smk_next = smack_cipso; + scp->smk_smack = mapsmack; + scp->smk_level = maplevel; + scp->smk_catset = mapcatset; + smack_cipso = scp; + /* + * Add this to ensure that there are enough bytes + * for the regurgitation + */ + smk_cipso_written += sizeof(smack_t); + } + } + + vfree(data); + return rc; +} + +static struct file_operations smk_cipso_ops = { + .read = smk_read_cipso, + .write = smk_write_cipso, +}; + +static ssize_t smk_read_ambient(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t rc; + + if (count < sizeof(smack_t)) + return -EINVAL; + + if (*ppos != 0) + return 0; + + rc = simple_read_from_buffer(buf, count, ppos, &smack_net_ambient, + sizeof(smack_t)); + + return rc; +} + +static ssize_t smk_write_ambient(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + smack_t smack; + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + + if (count > sizeof(smack_t)) + return -EINVAL; + + if (copy_from_user(&smack, buf, count) != 0) + return -EFAULT; + + smack = smk_from_string((char *)&smack); + if (smack == SMK_INVALID) + return -EINVAL; + /* + * Better check to be sure this is OK. + */ + smack_net_ambient = smack; + + return count; +} + +static struct file_operations smk_ambient_ops = { + .read = smk_read_ambient, + .write = smk_write_ambient, +}; + +static ssize_t smk_read_ipin(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_smack *tsp = current->security; + ssize_t rc; + + if (count < sizeof(smack_t)) + return -EINVAL; + + if (*ppos != 0) + return 0; + + rc = simple_read_from_buffer(buf, count, ppos, + &tsp->smk_in , sizeof(smack_t)); + + return rc; +} + +static ssize_t smk_write_ipin(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + smack_t smack; + struct task_smack *tsp = current->security; + smack_t *isp = &tsp->smk_in; + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + + if (count > sizeof(smack_t)) + return -EINVAL; + + if (copy_from_user(&smack, buf, count) != 0) + return -EFAULT; + + smack = smk_from_string((char *)&smack); + if (smack == SMK_INVALID) + return -EINVAL; + /* + * Better check to be sure this is OK. + */ + *isp = smack; + + return count; +} + +static struct file_operations smk_ipin_ops = { + .read = smk_read_ipin, + .write = smk_write_ipin, +}; + +static ssize_t smk_read_ipout(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_smack *tsp = current->security; + ssize_t rc; + + if (count < sizeof(smack_t)) + return -EINVAL; + + if (*ppos != 0) + return 0; + + rc = simple_read_from_buffer(buf, count, ppos, + &tsp->smk_out , sizeof(smack_t)); + + return rc; +} + +static ssize_t smk_write_ipout(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + smack_t smack; + struct task_smack *tsp = current->security; + smack_t *isp = &tsp->smk_out; + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + + if (count > sizeof(smack_t)) + return -EINVAL; + + if (copy_from_user(&smack, buf, count) != 0) + return -EFAULT; + + smack = smk_from_string((char *)&smack); + if (smack == SMK_INVALID) + return -EINVAL; + /* + * Better check to be sure this is OK. + */ + *isp = smack; + tsp->smk_flags |= SMK_OUT_RESET; + + return count; +} + +static struct file_operations smk_ipout_ops = { + .read = smk_read_ipout, + .write = smk_write_ipout, +}; + +static ssize_t smk_read_packet(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_smack *tsp = current->security; + ssize_t rc; + + if (count < sizeof(smack_t)) + return -EINVAL; + + if (*ppos != 0) + return 0; + + rc = simple_read_from_buffer(buf, count, ppos, + &tsp->smk_packet , sizeof(smack_t)); + + return rc; +} + +static struct file_operations smk_packet_ops = { + .read = smk_read_packet, +}; + +struct option_names { + int o_number; + char *o_name; + char *o_alias; +}; + +static struct option_names netlbl_choices[] = { + { NETLBL_NLTYPE_NONE, + "NLBL_NONE", "none" }, + { NETLBL_NLTYPE_MGMT, + NETLBL_NLTYPE_MGMT_NAME, "mgmt" }, + { NETLBL_NLTYPE_RIPSO, + NETLBL_NLTYPE_RIPSO_NAME, "ripso" }, + { NETLBL_NLTYPE_CIPSOV4, + NETLBL_NLTYPE_CIPSOV4_NAME, "cipsov4" }, + { NETLBL_NLTYPE_CIPSOV4, + NETLBL_NLTYPE_CIPSOV4_NAME, "cipso" }, + { NETLBL_NLTYPE_CIPSOV6, + NETLBL_NLTYPE_CIPSOV6_NAME, "cipsov6" }, + { NETLBL_NLTYPE_UNLABELED, + NETLBL_NLTYPE_UNLABELED_NAME, "unlabeled" }, +}; +#define NCHOICES (sizeof(netlbl_choices) / sizeof(struct option_names)) + +static ssize_t smk_read_ipopt(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_smack *tsp = current->security; + char bound[40]; + ssize_t rc; + int i; + + if (count < sizeof(smack_t)) + return -EINVAL; + + if (*ppos != 0) + return 0; + + sprintf(bound, "unknown"); + + for (i = 0; i < NCHOICES; i++) + if (tsp->smk_ipscheme == netlbl_choices[i].o_number) { + sprintf(bound, "%s", netlbl_choices[i].o_name); + break; + } + + rc = simple_read_from_buffer(buf, count, ppos, bound, strlen(bound)); + + return rc; +} + +static ssize_t smk_write_ipopt(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_smack *tsp = current->security; + char bound[40]; + char *cp; + int i; + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + + if (count >= 40) + return -EINVAL; + + if (copy_from_user(bound, buf, count) != 0) + return -EFAULT; + + bound[count] = '\0'; + cp = strchr(bound, ' '); + if (cp != NULL) + *cp = '\0'; + cp = strchr(bound, '\n'); + if (cp != NULL) + *cp = '\0'; + + for (i = 0; i < NCHOICES; i++) + if ((strcmp(bound, netlbl_choices[i].o_name) == 0) || + (strcmp(bound, netlbl_choices[i].o_alias) == 0)) { + tsp->smk_ipscheme = netlbl_choices[i].o_number; + return count; + } + /* + * Not a valid choice. + */ + return -EINVAL; +} + +static struct file_operations smk_ipopt_ops = { + .read = smk_read_ipopt, + .write = smk_write_ipopt, +}; + +static ssize_t smk_read_nltype(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char bound[40]; + ssize_t rc; + int i; + + if (count < sizeof(smack_t)) + return -EINVAL; + + if (*ppos != 0) + return 0; + + sprintf(bound, "unknown"); + + for (i = 0; i < NCHOICES; i++) + if (smack_net_nltype == netlbl_choices[i].o_number) { + sprintf(bound, "%s", netlbl_choices[i].o_name); + break; + } + + rc = simple_read_from_buffer(buf, count, ppos, bound, strlen(bound)); + + return rc; +} + +static ssize_t smk_write_nltype(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char bound[40]; + char *cp; + int i; + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + + if (count >= 40) + return -EINVAL; + + if (copy_from_user(bound, buf, count) != 0) + return -EFAULT; + + bound[count] = '\0'; + cp = strchr(bound, ' '); + if (cp != NULL) + *cp = '\0'; + cp = strchr(bound, '\n'); + if (cp != NULL) + *cp = '\0'; + + for (i = 0; i < NCHOICES; i++) + if ((strcmp(bound, netlbl_choices[i].o_name) == 0) || + (strcmp(bound, netlbl_choices[i].o_alias) == 0)) { + smack_net_nltype = netlbl_choices[i].o_number; + return count; + } + /* + * Not a valid choice. + */ + return -EINVAL; +} + +static struct file_operations smk_nltype_ops = { + .read = smk_read_nltype, + .write = smk_write_nltype, +}; + +/* + * mapping for symlinks + */ +#define SMK_TMPPATH_SIZE 32 +#define SMK_TMPPATH_ROOT "/moldy/" + +struct smk_link { + struct smk_link *sl_next; + int sl_inum; + char sl_name[SMK_TMPPATH_SIZE]; + char sl_target[SMK_TMPPATH_SIZE]; +}; + +static struct super_block *smk_sb = NULL; +static struct smk_link *smk_links = NULL; +static int smk_links_count = 0; + +static void *smackfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct task_smack *tsp = current->security; + smack_t *sp = &tsp->smk_task; + char *cp; + int inum = dentry->d_inode->i_ino; + struct smk_link *slp; + + for (slp = smk_links; slp != NULL; slp = slp->sl_next) + if (slp->sl_inum == inum) + break; + + if (slp == NULL) { + printk("%s:%d failed\n", __FUNCTION__, __LINE__); + return NULL; + } + cp = kzalloc(SMK_TMPPATH_SIZE, GFP_KERNEL); + if (cp == NULL) + return NULL; + + strcpy(cp, slp->sl_target); + strcat(cp, (char *)sp); + nd_set_link(nd, cp); + + kfree(cp); + return NULL; +} + +static int smackfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) +{ + smack_t *csp = smk_of_task(current); + char *cp; + int len; + int inum = dentry->d_inode->i_ino; + struct smk_link *slp; + + for (slp = smk_links; slp != NULL; slp = slp->sl_next) + if (slp->sl_inum == inum) + break; + + if (slp == NULL) { + printk("%s:%d failed\n", __FUNCTION__, __LINE__); + return -EACCES; + } + + cp = kzalloc(SMK_TMPPATH_SIZE, GFP_KERNEL); + if (cp == NULL) + return -ENOMEM; + + strcpy(cp, slp->sl_target); + strcat(cp, (char *)csp); + len = strlen(cp); + len = (len > buflen) ? buflen : len; + + if (copy_to_user(buffer, cp, len) != 0) + len = -EFAULT; + + kfree(cp); + return len; +} + +static struct inode_operations smackfs_symlink_inode_operations = { + .readlink = smackfs_readlink, + .follow_link = smackfs_follow_link, +}; + +static void smk_add_symlink(char *name, char *target) +{ + static int inum = SMK_TMP; + struct inode *inode; + struct dentry *dentry; + struct smk_link *slp; + + for (slp = smk_links; slp != NULL; slp = slp->sl_next) { + if (strcmp(slp->sl_name, name) != 0) + continue; + strcpy(slp->sl_target, target); + return; + } + + slp = kzalloc(sizeof(struct smk_link), GFP_KERNEL); + if (slp == NULL) + return; + + dentry = d_alloc_name(smk_sb->s_root, name); + if (dentry == NULL) { + printk("%s:%d link dentry failed\n", __FUNCTION__, __LINE__); + return; + } + + inode = new_inode(smk_sb); + if (inode == NULL) { + printk("%s:%d link inode failed\n", __FUNCTION__, __LINE__); + return; + } + + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_blocks = 0; + inode->i_atime = CURRENT_TIME; + inode->i_mtime = inode->i_atime; + inode->i_ctime = inode->i_atime; + inode->i_ino = inum++; + inode->i_op = &smackfs_symlink_inode_operations; + d_add(dentry, inode); + + strcpy(slp->sl_name, name); + strcpy(slp->sl_target, target); + slp->sl_inum = inode->i_ino; + slp->sl_next = smk_links; + smk_links = slp; + smk_links_count++; + + return; +} + +static ssize_t smk_read_links(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t bytes = sizeof(struct smk_link) * smk_links_count; + struct smk_link *slp; + char *result; + char *cp; + + + result = kzalloc(bytes, GFP_KERNEL); + if (result == NULL) + return -ENOMEM; + *result = '\0'; + + for (slp = smk_links, cp = result; slp != NULL; slp = slp->sl_next) { + sprintf(cp, "%s %s\n", slp->sl_name, slp->sl_target); + cp += strlen(slp->sl_name) + strlen(slp->sl_target); + } + + bytes = simple_read_from_buffer(buf,count,ppos,result,strlen(result)); + + kfree(result); + + return bytes; +} + +static ssize_t smk_write_links(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t rc = count; + char *data; + char *cp; + char name[SMK_TMPPATH_SIZE]; + char target[SMK_TMPPATH_SIZE]; + + /* + * No partial writes. + */ + if (*ppos != 0) + return -EINVAL; + /* + * Casey wonders about this number. + */ + if ((count > 64 * 1024 * 1024) || (data = vmalloc(count + 1)) == NULL) + return -ENOMEM; + + *(data + count) = '\0'; + + if (copy_from_user(data, buf, count) == 0) { + for (cp = data - 1; cp != NULL; cp = strchr(cp + 1, '\n')) { + if (*++cp == '\0') + break; + if (sscanf(cp, "%14s %30s\n", name, target) != 2) { + printk("%s:%d bad scan\n", + __FUNCTION__, __LINE__); + break; + } + smk_add_symlink(name, target); + printk("%s:%d add %s -> %s\n", + __FUNCTION__, __LINE__, name, target); + } + } + else + rc = -EFAULT; + + + vfree(data); + return rc; +} + +static struct file_operations smk_links_ops = { + .read = smk_read_links, + .write = smk_write_links, +}; + +static int smk_fill_super(struct super_block *sb, void * data, int silent) +{ + int rc; + struct inode *root_inode; + + static struct tree_descr smack_files[] = { + [SMK_LOAD] = {"load", &smk_load_ops, S_IRUGO|S_IWUSR}, + [SMK_SELF] = {"self", &smk_self_ops, S_IRUGO|S_IWUGO}, + [SMK_PACKET] = {"packet", &smk_packet_ops, S_IRUGO|S_IWUGO}, + [SMK_LINKS] = {"links", &smk_links_ops, S_IRUGO|S_IWUGO}, + [SMK_CIPSO] = {"cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR}, + [SMK_AMBIENT] = {"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR}, + [SMK_IPIN] = {"ipin", &smk_ipin_ops, S_IRUGO|S_IWUGO}, + [SMK_IPOUT] = {"ipout", &smk_ipout_ops, S_IRUGO|S_IWUGO}, + [SMK_IPOPTION] = {"ipoption", &smk_ipopt_ops, S_IRUGO|S_IWUGO}, + [SMK_NLTYPE] = {"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR}, + /* last one */ {""} + }; + + /* + * There will be only one smackfs. Casey says so. + */ + smk_sb = sb; + + rc = simple_fill_super(sb, SMACK_MAGIC, smack_files); + if (rc != 0) { + printk(KERN_ERR "%s failed %d while creating inodes\n", + __FUNCTION__, rc); + return rc; + } + + root_inode = sb->s_root->d_inode; + root_inode->i_security = new_smack_t(SMK_FLOOR); + + /* + * Create a directory for /smack/tmp + */ + smk_add_symlink("tmp", "/moldy/"); + + return 0; +} + +static int smk_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) +{ + return get_sb_single(fs_type, flags, data, smk_fill_super, mnt); +} + +static struct file_system_type smk_fs_type = { + .name = "smackfs", + .get_sb = smk_get_sb, + .kill_sb = kill_litter_super, +}; + +static struct vfsmount *smackfs_mount; + +static int __init init_smk_fs(void) +{ + int err; + + err = register_filesystem(&smk_fs_type); + if (!err) { + smackfs_mount = kern_mount(&smk_fs_type); + if (IS_ERR(smackfs_mount)) { + printk(KERN_ERR "smackfs: could not mount!\n"); + err = PTR_ERR(smackfs_mount); + smackfs_mount = NULL; + } + } + return err; +} + +__initcall(init_smk_fs); diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smack.h linux-2.6.22/security/smack/smack.h --- linux-2.6.22-base/security/smack/smack.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.22/security/smack/smack.h 2007-07-10 01:08:05.000000000 -0700 @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2007 Casey Schaufler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + * Author: + * Casey Schaufler + * + */ + +#ifndef _SECURITY_SMACK_H +#define _SECURITY_SMACK_H + +#include +#include + +/* + * A smack_t contains an 8 byte string, 7 characters + * and a terminating NULL. Yes, it's short and 12.5% + * wasted. I'm sure someone will decide to fix it and + * slow the whole thing down. + */ +typedef u64 smack_t; +typedef u32 smack_access_t; + +struct superblock_smack { + smack_t smk_root; + smack_t smk_floor; + smack_t smk_hat; + smack_t smk_default; + int smk_initialized; +}; + +/* + * Setting the network labels on the task instead of the socket + * is unconventional. Casey says that in usually won't matter and + * the cases where is does are odd ducks anyway. + */ +struct task_smack { + smack_t smk_task; /* label of the task */ + smack_t smk_out; /* label to associate with outbound packets */ + smack_t smk_in; /* label to check on incoming packets */ + smack_t smk_packet; /* label of incoming packet on request */ + int smk_ipscheme; /* How to deal with packets */ + int smk_flags; /* State issues */ +}; + +#define SMK_OUT_RESET 0x0001 /* The smk_out has been reset */ + +/* + * A label access rule. + */ +struct smack_rule { + smack_t smk_subject; + smack_t smk_object; + smack_access_t smk_access; +}; + +/* + * An entry in the table of permitted label accesses. + */ +struct smk_list_entry { + struct smk_list_entry *smk_next; + struct smk_list_entry *smk_prev; + struct smack_rule smk_rule; +}; + +/* + * An entry in the table mapping smack values to + * CIPSO level/category-set values. + */ +struct smk_cipso_entry { + struct smk_cipso_entry *smk_next; + struct smk_cipso_entry *smk_prev; + int smk_level; /* for CIPSO */ + smack_t smk_smack; + smack_t smk_catset; +}; + +/* + * Mount options + */ +#define SMK_FSDEFAULT "smackfsdef=" +#define SMK_FSFLOOR "smackfsfloor=" +#define SMK_FSHAT "smackfshat=" +#define SMK_FSROOT "smackfsroot=" + +/* + * xattr name + */ +#define XATTR_SMACK_SUFFIX "SMACK64" +#define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX + +/* + * smackfs macic number + */ +#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ + +/* + * CIPSO domain information. + * Casey says that these may wind up being configurable + * at some point. + */ +#define SMACK_CIPSO_DOMAIN_NAME "SmackCIPSO" +#define SMACK_CIPSO_DOI 3 +#define SMACK_CIPSO_DIRECT 250 /* Arbitrary and bad */ + +/* + * Pre-defined smack label values. + */ +#define SMK_INVALID 0x00LL /* NULLSTRING */ +#define SMK_HAT 0x5ELL /* "^" */ +#define SMK_FLOOR 0x5FLL /* "_" */ +#define SMK_STAR 0x2ALL /* "*" */ +#define SMK_HUH 0x3FLL /* "?" */ +#define SMK_DASH 0x2DLL /* "-" */ +#define SMK_UNSET 0x5445534e55LL /* "UNSET" */ +/* + * There's a place in the CIPSO initialization that + * wants this. + */ +#define SMK32_FLOOR 0x5F /* "_" */ + +#define SMK_MAXLEN (sizeof(smack_t) - 1) /* NULL terminated */ +/* + * Just to make the common cases easier to deal with + */ +#define MAY_ANY (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) +#define MAY_ANYREAD (MAY_READ | MAY_EXEC) +#define MAY_ANYWRITE (MAY_WRITE | MAY_APPEND) +#define MAY_READWRITE (MAY_READ | MAY_WRITE) +#define MAY_NOT 0 + +/* + * There are not enough CAP bits available to make this + * real, so Casey borrowed the capability that looks to + * him like it has the best balance of similarity amd + * low use. + */ +#define CAP_MAC_OVERRIDE CAP_LINUX_IMMUTABLE + +static inline void smack_catset_bit(int cat, smack_t *catsetp) +{ + char *cp = (char *)catsetp; + + if (cat > sizeof(smack_t) * 8) + return; + + cp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8); +} + + +#endif /* _SECURITY_SMACK_H */ diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smack_lsm.c linux-2.6.22/security/smack/smack_lsm.c --- linux-2.6.22-base/security/smack/smack_lsm.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.22/security/smack/smack_lsm.c 2007-07-10 01:34:08.000000000 -0700 @@ -0,0 +1,1872 @@ +/* + * Simplified MAC Kernel (smack) security module + * + * This file contains the smack hook function implementations. + * + * Author: + * Casey Schaufler + * + * Copyright (C) 2007 Casey Schaufler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smack.h" +#include "smack_access.h" + +static struct smk_cipso_entry smack_cipso_floor = { + .smk_next = NULL, + .smk_prev = NULL, + .smk_smack = SMK_FLOOR, + .smk_level = 0, + .smk_catset = 0LL, +}; +struct smk_cipso_entry *smack_cipso = &smack_cipso_floor; + +static int smk_fetch(struct inode *ip, struct dentry *dp, smack_t *isp) +{ + int i; + int rc; + int found; + smack_t smack; + char *nuller = (char *)&smack; + + if (ip->i_op->getxattr == NULL) + return -EOPNOTSUPP; + + rc = ip->i_op->getxattr(dp, XATTR_NAME_SMACK, &smack, sizeof(smack_t)); + + if (rc > 0) { + for (i = 0, found = 0; i < sizeof(smack_t); i++) { + if (found) + nuller[i] = '\0'; + else if (nuller[i] == '\0') + found = 1; + else if (i >= rc) { + nuller[i] = '\0'; + found = 1; + } + } + *isp = smack; + } + + return rc; +} + +static smack_t *free_smack_t(smack_t *sp) +{ + if (sp != NULL) + kfree(sp); + + return NULL; +} + +/* + * LSM hooks. + * We he, that is fun! + */ +static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp) +{ + smack_t *psp = smk_of_task(ptp); + smack_t *csp = smk_of_task(ctp); + int rc; + + rc = cap_ptrace(ptp, ctp); + if (rc != 0) + return rc; + + rc = smk_access(psp, csp, MAY_READWRITE); + if (rc != 0 && __capable(ptp, CAP_MAC_OVERRIDE)) + return 0; + + return rc; +} + +static int smack_syslog(int type) +{ + int rc; + smack_t *sp = smk_of_task(current); + + rc = cap_syslog(type); + if (rc == 0) + if (*sp != SMK_FLOOR) + rc = -EACCES; + + return rc; +} + +static int smack_task_alloc_security(struct task_struct *tsk) +{ + struct task_smack *tsp; + struct task_smack *ctsp = current->security; + + tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL); + if (tsp == NULL) + return -ENOMEM; + + *tsp = *ctsp; + tsk->security = tsp; + + return 0; +} + +static void smack_task_free_security(struct task_struct *task) +{ + kfree(task->security); + task->security = NULL; +} + +static int smack_task_setpgid(struct task_struct *p, pid_t pgid) +{ + int rc; + + rc = smk_curacc(smk_of_task(p), MAY_WRITE); + return rc; +} + +static int smack_task_setnice(struct task_struct *p, int nice) +{ + int rc; + + rc = smk_curacc(smk_of_task(p), MAY_WRITE); + return rc; +} + +static int smack_task_setioprio(struct task_struct *p, int ioprio) +{ + int rc; + + rc = smk_curacc(smk_of_task(p), MAY_WRITE); + return rc; +} + +static int smack_task_getioprio(struct task_struct *p) +{ + int rc; + + rc = smk_curacc(smk_of_task(p), MAY_READ); + return rc; +} + +static int smack_task_setscheduler(struct task_struct *p, int policy, + struct sched_param *lp) +{ + int rc; + + rc = smk_curacc(smk_of_task(p), MAY_WRITE); + return rc; +} + +static int smack_task_getscheduler(struct task_struct *p) +{ + int rc; + + rc = smk_curacc(smk_of_task(p), MAY_READ); + return rc; +} + +static int smack_task_movememory(struct task_struct *p) +{ + int rc; + + rc = smk_curacc(smk_of_task(p), MAY_WRITE); + return rc; +} + +static int smack_task_kill(struct task_struct *p, struct siginfo *info, + int sig, u32 secid) +{ + smack_t *tsp = smk_of_task(p); + int rc; + + rc = smk_curacc(tsp, MAY_READWRITE); + return rc; +} + +/* + * Superblock Hooks. + */ +static int smack_sb_alloc_security(struct super_block *sb) +{ + struct superblock_smack *sbsp; + + sbsp = kzalloc(sizeof(struct superblock_smack), GFP_KERNEL); + + if (sbsp == NULL) + return -ENOMEM; + + sbsp->smk_root = SMK_FLOOR; + sbsp->smk_default = SMK_FLOOR; + sbsp->smk_floor = SMK_FLOOR; + sbsp->smk_hat = SMK_HAT; + sbsp->smk_initialized = 0; + + sb->s_security = sbsp; + + return 0; +} + +static void smack_sb_free_security(struct super_block *sb) +{ + if (sb->s_security == NULL) + return; + + kfree(sb->s_security); + sb->s_security = NULL; +} + +static int smack_sb_copy_data(struct file_system_type *type, void *orig, + void *smackopts) +{ + char *cp, *commap, *otheropts, *dp; + + /* Binary mount data: just copy */ + if (type->fs_flags & FS_BINARY_MOUNTDATA) { + copy_page(smackopts, orig); + return 0; + } + + otheropts = (char *)get_zeroed_page(GFP_KERNEL); + if (otheropts == NULL) + return -ENOMEM; + + for (cp = orig, commap = orig; commap != NULL; cp = commap + 1) { + if (strstr(cp, SMK_FSDEFAULT) == cp) + dp = smackopts; + else if (strstr(cp, SMK_FSFLOOR) == cp) + dp = smackopts; + else if (strstr(cp, SMK_FSHAT) == cp) + dp = smackopts; + else if (strstr(cp, SMK_FSROOT) == cp) + dp = smackopts; + else + dp = otheropts; + + commap = strchr(cp, ','); + if (commap != NULL) + *commap = '\0'; + + if (*dp != '\0') + strcat(dp, ","); + strcat(dp, cp); + } + + strcpy(orig, otheropts); + free_page((unsigned long)otheropts); + + return 0; +} + +static int smack_sb_kern_mount(struct super_block *sb, void *data) +{ + int rc; + struct dentry *root = sb->s_root; + struct inode *inode = root->d_inode; + struct superblock_smack *sp = sb->s_security; + smack_t *isp; + smack_t smack = SMK_UNSET; + char *op; + char *commap; + + if (sp == NULL) { + rc = smack_sb_alloc_security(sb); + if (rc != 0) + return rc; + } + if (sp->smk_initialized != 0) + return 0; + if (inode == NULL) + return 0; + + sp->smk_initialized = 1; + + /* + * Not everyone supports extended attributes. + */ + if (inode->i_op->getxattr != NULL) { + rc = smk_fetch(inode, root, &smack); + switch (rc) { + case 0: + case -ENODATA: + case -EOPNOTSUPP: + break; + case sizeof(smack_t): + default: + printk(KERN_WARNING "%s:%d (dev %s, type " + "%s) \"%s\" rc = %d\n", __FUNCTION__, __LINE__, + sb->s_id, sb->s_type->name, (char *)&smack, rc); + break; + } + } + + for (op = data; op != NULL; op = commap) { + commap = strchr(op, ','); + if (commap != NULL) + *commap++ = '\0'; + + if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) { + op += strlen(SMK_FSHAT); + if (strlen(op) <= SMK_MAXLEN) + sp->smk_hat = smk_from_string(op); + } + else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) { + op += strlen(SMK_FSFLOOR); + if (strlen(op) <= SMK_MAXLEN) + sp->smk_floor = smk_from_string(op); + } + else if (strncmp(op,SMK_FSDEFAULT,strlen(SMK_FSDEFAULT)) == 0) { + op += strlen(SMK_FSDEFAULT); + if (strlen(op) <= SMK_MAXLEN) + sp->smk_default = smk_from_string(op); + } + else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) { + op += strlen(SMK_FSROOT); + if (strlen(op) <= SMK_MAXLEN) + sp->smk_root = smk_from_string(op); + } + } + + /* + * Initialize the root inode. + */ + if (inode->i_security == NULL) + inode->i_security = new_smack_t(sp->smk_root); + else { + isp = inode->i_security; + *isp = sp->smk_root; + } + + return 0; +} + +static int smack_sb_statfs(struct dentry *dentry) +{ + struct superblock_smack *sbp; + + if (dentry == NULL || dentry->d_sb == NULL || + dentry->d_sb->s_security == NULL) + return 0; + + sbp = dentry->d_sb->s_security; + + return smk_curacc(&sbp->smk_floor, MAY_READ); +} + +static int smack_sb_mount(char *dev_name, struct nameidata *nd, + char *type, unsigned long flags, void *data) +{ + struct superblock_smack *sbp; + int rc; + + if (nd == NULL || nd->mnt == NULL || nd->mnt->mnt_sb == NULL || + nd->mnt->mnt_sb->s_security == NULL) + return 0; + + sbp = nd->mnt->mnt_sb->s_security; + + rc = smk_curacc(&sbp->smk_floor, MAY_WRITE); + return rc; +} + +static int smack_sb_umount(struct vfsmount *mnt, int flags) +{ + struct superblock_smack *sbp; + int rc; + + sbp = mnt->mnt_sb->s_security; + + rc = smk_curacc(&sbp->smk_floor, MAY_WRITE); + return rc; +} + +/* + * Inode hooks + */ +static int smack_inode_alloc_security(struct inode *inode) +{ + smack_t *csp = smk_of_task(current); + + inode->i_security = new_smack_t(*csp); + if (inode->i_security == NULL) + return -ENOMEM; + return 0; +} + +static void smack_inode_free_security(struct inode *inode) +{ + inode->i_security = free_smack_t(inode->i_security); +} + +static int smack_inode_init_security(struct inode *inode, struct inode *dir, + char **name, void **value, size_t *len) +{ + smack_t *isp = inode->i_security; + + if (name && (*name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if (value && (*value = kstrdup((char *)isp, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if (len) + *len = strlen((char *)isp) + 1; + + return 0; +} + +static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + int rc; + + rc = smk_curacc(dir->i_security, MAY_WRITE); + return rc; +} + +static int smack_inode_symlink(struct inode *dir, struct dentry *dentry, + const char *name) +{ + int rc; + + rc = smk_curacc(dir->i_security, MAY_WRITE); + return rc; +} + +static int smack_inode_mknod(struct inode *dir, struct dentry *dentry, + int mode, dev_t dev) +{ + int rc; + + rc = smk_curacc(dir->i_security, MAY_WRITE); + return rc; +} + +static int smack_inode_rename(struct inode *old_inode, + struct dentry *old_dentry, + struct inode *new_inode, + struct dentry *new_dentry) +{ + int rc; + + rc = smk_curacc(old_inode->i_security, MAY_READWRITE); + if (rc == 0) + rc = smk_curacc(new_inode->i_security, MAY_READWRITE); + return rc; +} + +static int smack_inode_readlink(struct dentry *dentry) +{ + int rc; + + rc = smk_curacc(dentry->d_inode->i_security, MAY_READ); + return rc; +} + +static int smack_inode_follow_link(struct dentry *dentry, + struct nameidata *nameidata) +{ + int rc; + + rc = smk_curacc(dentry->d_inode->i_security, MAY_READ); + return rc; +} + +static int smack_inode_permission(struct inode *inode, int mask, + struct nameidata *nd) +{ + int rc; + + /* + * No permission to check. Existence test. Yup, it's there. + */ + if (mask == 0) + return 0; + + rc = smk_curacc(inode->i_security, mask); + return rc; +} + +static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) +{ + int rc; + + rc = smk_curacc(dentry->d_inode->i_security, MAY_WRITE); + return rc; +} + +static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) +{ + int rc = smk_curacc(dentry->d_inode->i_security, MAY_READ); + + return rc; +} + +static int smack_inode_setxattr(struct dentry *dentry, char *name, + void *value, size_t size, int flags) +{ + int rc; + + rc = cap_inode_setxattr(dentry, name, value, size, flags); + if (rc == 0) + rc = smk_curacc(dentry->d_inode->i_security, MAY_WRITE); + + return rc; +} + +static void smack_inode_post_setxattr(struct dentry *dentry, char *name, + void *value, size_t size, int flags) +{ + int i; + smack_t *vsp; + smack_t *isp; + char *nuller; + + /* + * Not SMACK + */ + if (strcmp(name, XATTR_NAME_SMACK)) + return; + + if (size >= sizeof(smack_t)) + return; + + vsp = (smack_t *)value; + isp = (smack_t *)dentry->d_inode->i_security; + nuller = (char *)isp; + + *isp = *vsp; + + for (i = size; i < sizeof(smack_t); i++) + nuller[i] = '\0'; + + return; +} + +static int smack_inode_getxattr(struct dentry *dentry, char *name) +{ + int rc; + + rc = smk_curacc(dentry->d_inode->i_security, MAY_READ); + return rc; +} + +static int smack_inode_listxattr(struct dentry *dentry) +{ + int rc; + + rc = smk_curacc(dentry->d_inode->i_security, MAY_READ); + return rc; +} + +static int smack_inode_removexattr(struct dentry *dentry, char *name) +{ + int rc; + + rc = cap_inode_removexattr(dentry, name); + if (rc == 0) + rc = smk_curacc(dentry->d_inode->i_security, MAY_WRITE); + + return rc; +} + +static const char *smack_inode_xattr_getsuffix(void) +{ + return XATTR_SMACK_SUFFIX; +} + +static int smack_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err) +{ + smack_t *bsp = buffer; + smack_t *isp; + + if (size < sizeof(smack_t) || name == NULL || bsp == NULL || + inode == NULL || inode->i_security == NULL) + return 0; + + if (strcmp(name, XATTR_SMACK_SUFFIX)) + return -EOPNOTSUPP; + + isp = inode->i_security; + *bsp = *isp; + + return strlen((char *)isp) + 1; +} + +static int smack_inode_setsecurity(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + smack_t smack = 0LL; + smack_t *isp = inode->i_security; + char *cp = (char *)&smack; + + if (strcmp(name, XATTR_SMACK_SUFFIX)) + return -EOPNOTSUPP; + + if (value == NULL || size > sizeof(smack_t)) + return -EACCES; + + strncpy(cp, value, size); + + *isp = smack; + return 0; +} + +static int smack_inode_listsecurity(struct inode *inode, char *buffer, + size_t buffer_size) +{ + int len = strlen(XATTR_NAME_SMACK); + + if (buffer != NULL && len <= buffer_size) { + memcpy(buffer, XATTR_NAME_SMACK, len); + return len; + } + return -EINVAL; +} + +/* + * I hope these are the hokeyist lines of code in the module. Casey. + */ +#define DEVPTS_SUPER_MAGIC 0x1cd1 +#define SOCKFS_MAGIC 0x534F434B +#define PIPEFS_MAGIC 0x50495045 + +static void smack_d_instantiate (struct dentry *opt_dentry, struct inode *inode) +{ + struct super_block *sbp; + struct superblock_smack *sbsp; + smack_t *csp = smk_of_task(current); + smack_t *isp; + smack_t sbs; + struct dentry *dp; + int rc; + + if (inode == NULL) + return; + + if (inode->i_security == NULL) + inode->i_security = new_smack_t(SMK_UNSET); + + isp = inode->i_security; + sbp = inode->i_sb; + sbsp = sbp->s_security; + /* + * We're going to use the superblock default label + * if there's no label on the file. + */ + sbs = sbsp->smk_default; + + /* + * This is pretty hackish. + * Casey says that we shouldn't have to do + * file system specific code, but it does help + * with keeping it simple. + */ + switch (sbp->s_magic) { + case SMACK_MAGIC: + /* + * Casey says that it's a little embarassing + * that the smack file system doesn't do + * extended attributes. + */ + *isp = SMK_STAR; + return; + case PIPEFS_MAGIC: + /* + * Casey says pipes are easy (?) + */ + *isp = SMK_STAR; + return; + case DEVPTS_SUPER_MAGIC: + /* + * Casey says this is here because + * devpts is "clean" of security hooks. + * + * pty's need to be handled for real. + * Using "*" is expedient for bring-up. + * + *isp = SMK_STAR; + */ + *isp = *csp; + return; + case SOCKFS_MAGIC: + /* + * Casey says sockets get the smack of the task. + */ + *isp = *csp; + return; + case PROC_SUPER_MAGIC: + /* + * Casey says procfs appears not to care. + */ + *isp = sbs; + return; + default: + break; + } + /* + * No xattr support means, alas, no SMACK label. + * Use the aforeapplied default. + * It would be curious if the label of the task + * does not match that assigned. + */ + if (inode->i_op->getxattr == NULL) { + *isp = sbs; + return; + } + /* + * Get the dentry for xattr. + */ + if (opt_dentry == NULL) { + dp = d_find_alias(inode); + if (dp == NULL) { + *isp = sbs; + return; + } + } + else { + dp = dget(opt_dentry); + if (dp == NULL) { + *isp = sbs; + return; + } + } + + rc = smk_fetch(inode, dp, isp); + if (rc < 0) + *isp = sbs; + + dput(dp); + + return; +} + +/* + * File Hooks + */ + +static int smack_file_alloc_security(struct file *file) +{ + smack_t *csp = smk_of_task(current); + + file->f_security = new_smack_t(*csp); + if (file->f_security == NULL) + return -ENOMEM; + return 0; +} + +static void smack_file_free_security(struct file *file) +{ + file->f_security = free_smack_t(file->f_security); +} + +/* + * Should access checks be done on each read or write? + * UNICOS and SELinux say yes. + * Trusted Solaris, Trusted Irix, and just about everyone else says no. + */ +static int smack_file_permission(struct file *file, int mask) +{ +#ifdef SMACKONREADWRITE + smack_t *fsp; + int rc; + + if (mask == 0) + return 0; + + fsp = (smack_t *)file->f_dentry->d_inode->i_security; + + rc = smk_curacc(fsp, mask); + return rc; +#else /* SMACKONREADWRITE */ + return 0; +#endif /* SMACKONREADWRITE */ +} + +static int smack_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int error = 0; + + switch (cmd) { + case FIONREAD: + case FIBMAP: + case FIGETBSZ: + case EXT2_IOC_GETFLAGS: + case EXT2_IOC_GETVERSION: + error = smk_curacc(file->f_security, MAY_READ); + break; + + case EXT2_IOC_SETFLAGS: + case EXT2_IOC_SETVERSION: + error = smk_curacc(file->f_security, MAY_WRITE); + break; + + /* sys_ioctl() checks */ + case FIONBIO: + case FIOASYNC: + error = smk_curacc(file->f_security, MAY_READ); + break; + + case KDSKBENT: + case KDSKBSENT: + break; + + /* + * default case assumes that the command will go + * to the file's ioctl() function. + */ + default: + error = smk_curacc(file->f_security, MAY_READ); + + } + + return error; +} + +static int smack_file_mmap(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) +{ + int rc; + + if (file == NULL) + return 0; + + rc = smk_curacc(file->f_security, MAY_READWRITE); + return rc; +} + +static int smack_file_lock(struct file *file, unsigned int cmd) +{ + int rc; + + rc = smk_curacc(file->f_security, MAY_WRITE); + return rc; +} + +static int smack_file_fcntl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc; + + switch (cmd) { + case F_DUPFD: + case F_GETFD: + case F_GETFL: + case F_GETLK: + case F_GETOWN: + case F_GETSIG: + rc = smk_curacc(file->f_security, MAY_READ); + break; + case F_SETFD: + case F_SETFL: + case F_SETLK: + case F_SETLKW: + case F_SETOWN: + case F_SETSIG: + rc = smk_curacc(file->f_security, MAY_WRITE); + break; + default: + rc = smk_curacc(file->f_security, MAY_READWRITE); + } + + return rc; +} + +static int smack_file_send_sigiotask(struct task_struct *tsk, + struct fown_struct *fown, int signum) +{ + struct file *file; + int rc; + + /* + * struct fown_struct is never outside the context of a struct file + */ + file = (struct file *)((long)fown - offsetof(struct file,f_owner)); + rc = smk_access(file->f_security, tsk->security, MAY_WRITE); + if (rc != 0 && __capable(tsk, CAP_MAC_OVERRIDE)) + return 0; + return rc; +} + +static inline int file_may(struct file *file) +{ + if (file->f_mode & FMODE_READ) + return (file->f_mode & FMODE_WRITE) ? MAY_READWRITE : MAY_READ; + + return (file->f_mode & FMODE_WRITE) ? MAY_WRITE : 0; +} + +static int smack_file_receive(struct file *file) +{ + int may = 0; + int rc; + + /* + * This code relies on bitmasks. + */ + if (file->f_mode & FMODE_READ) + may = MAY_READ; + if (file->f_mode & FMODE_WRITE) + may |= MAY_WRITE; + + rc = smk_curacc(file->f_security, may); + return rc; +} + +/* + * Socket hooks. + */ + +/* + * In smack sockets are not security policy elements. + * The task associated with the socket is the policy + * element, so point the socket security blob at the + * task blob. + */ +static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t priority) +{ + sk->sk_security = current->security; + return 0; +} + +/* + * Casey says that this hook is probably unnecessary. + */ +static void smack_sk_free_security(struct sock *sk) +{ + sk->sk_security = NULL; +} + +static void smack_set_catset(smack_t catset, struct netlbl_lsm_secattr *sap) +{ + unsigned char *cp; + unsigned char m; + int cat; + int rc; + + if (catset == 0LL) + return; + + sap->flags |= NETLBL_SECATTR_MLS_CAT; + sap->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + sap->mls_cat->startbit = 0; + + for (cat = 1, cp = (unsigned char *)&catset; *cp != 0; cp++) + for (m = 0x80; m != 0; m >>= 1, cat++) { + if ((m & *cp) == 0) + continue; + rc = netlbl_secattr_catmap_setbit(sap->mls_cat, cat, + GFP_ATOMIC); + } +} + +/* + * Casey says that CIPSO is good enough for now. + * It can be used to effect. + * It can also be abused to effect when necessary. + * Appologies to the TSIG group in general and GW in particular. + */ +static void smack_to_secattr(smack_t smack, int scheme, + struct netlbl_lsm_secattr *nlsp) +{ + struct smk_cipso_entry *scp; + + switch (scheme) { + case NETLBL_NLTYPE_CIPSOV4: + nlsp->domain = kstrdup(SMACK_CIPSO_DOMAIN_NAME, GFP_ATOMIC); + nlsp->flags = NETLBL_SECATTR_DOMAIN; + nlsp->flags |= NETLBL_SECATTR_MLS_LVL; + + for (scp = smack_cipso; scp != NULL; scp = scp->smk_next) + if (scp->smk_smack == smack) + break; + + if (scp != NULL) { + nlsp->mls_lvl = scp->smk_level; + smack_set_catset(scp->smk_catset, nlsp); + } + else { + nlsp->mls_lvl = SMACK_CIPSO_DIRECT; + smack_set_catset(smack, nlsp); + } + break; + case NETLBL_NLTYPE_NONE: + case NETLBL_NLTYPE_UNLABELED: + break; + default: + break; + } +} + +static int smack_task_to_sock(struct sock *sk, struct task_smack *tsp) +{ + struct netlbl_lsm_secattr secattr; + int rc; + + netlbl_secattr_init(&secattr); + smack_to_secattr(tsp->smk_out, tsp->smk_ipscheme, &secattr); + if (secattr.flags != 0) + rc = netlbl_sock_setattr(sk, &secattr); + else + rc = -EINVAL; + + netlbl_secattr_destroy(&secattr); + return rc; +} + +static int smack_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern) +{ + smack_t *smp; + struct task_smack *tsp = current->security; + int rc = 0; + + if (SOCK_INODE(sock)->i_security == NULL) { + if (kern) + smp = new_smack_t(SMK_FLOOR); + else + smp = new_smack_t(tsp->smk_task); + SOCK_INODE(sock)->i_security = smp; + } + + if (family != PF_INET) + return 0; + /* + * Set the outbound netlbl. + */ + rc = smack_task_to_sock(sock->sk, tsp); + + return rc; +} + +static int smack_inode_create(struct inode *dir, struct dentry *dentry, + int mode) +{ + int rc; + + rc = smk_curacc(dir->i_security, MAY_WRITE); + return rc; +} + +static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *ip = dentry->d_inode; + int rc; + + /* + * You need write access to the thing you're unlinking + */ + rc = smk_curacc(ip->i_security, MAY_WRITE); + if (rc == 0) + /* + * You also need write access to the containing directory + */ + rc = smk_curacc(dir->i_security, MAY_WRITE); + + return rc; +} + +static int smack_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int rc; + + rc = smk_curacc(dir->i_security, MAY_WRITE); + return rc; +} + +static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *ip = dentry->d_inode; + int rc; + + /* + * You need write access to the thing you're removing + */ + rc = smk_curacc(ip->i_security, MAY_WRITE); + if (rc == 0) + /* + * You also need write access to the containing directory + */ + rc = smk_curacc(dir->i_security, MAY_WRITE); + + return rc; +} + +static int smack_file_set_fowner(struct file *file) +{ + smack_t *fsp = file->f_security; + smack_t *csp = smk_of_task(current); + + *fsp = *csp; + + return 0; +} + +static int smack_task_getpgid(struct task_struct *p) +{ + int rc; + + rc = smk_curacc(smk_of_task(p), MAY_READ); + return rc; +} + +static int smack_task_getsid(struct task_struct *p) +{ + int rc; + + rc = smk_curacc(smk_of_task(p), MAY_READ); + return rc; +} + +static void smack_task_getsecid(struct task_struct *p, u32 *secid) +{ + /* + * This is supposed to be called once, at boot, + * by the netlbl system. + */ + *secid = SMK32_FLOOR; +} + +static int smack_msg_msg_alloc_security(struct msg_msg *msg) +{ + smack_t *csp = smk_of_task(current); + + msg->security = new_smack_t(*csp); + if (msg->security == NULL) + return -ENOMEM; + return 0; +} + +static void smack_msg_msg_free_security(struct msg_msg *msg) +{ + msg->security = free_smack_t(msg->security); +} + + +static smack_t *smack_of_shm(struct shmid_kernel *shp) +{ + if (shp == NULL) + return NULL; + + return (smack_t *)shp->shm_perm.security; +} + +static int smack_shm_alloc_security(struct shmid_kernel *shp) +{ + smack_t *csp = smk_of_task(current); + struct kern_ipc_perm *isp = &shp->shm_perm; + + isp->security = new_smack_t(*csp); + if (isp->security == NULL) + return -ENOMEM; + return 0; +} + +static void smack_shm_free_security(struct shmid_kernel *shp) +{ + struct kern_ipc_perm *isp = &shp->shm_perm; + + if (isp->security != NULL) + isp->security = free_smack_t(isp->security); +} + +static int smack_shm_associate(struct shmid_kernel *shp, int shmflg) +{ + smack_t *ssp = smack_of_shm(shp); + int rc; + + if (ssp == NULL) + return 0; + + rc = smk_curacc(ssp, MAY_READWRITE); + return rc; +} + +static int smack_shm_shmctl(struct shmid_kernel *shp, int cmd) +{ + smack_t *ssp = smack_of_shm(shp); + int rc; + + if (ssp == NULL) + return 0; + + rc = smk_curacc(ssp, MAY_READWRITE); + return rc; +} + +static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmflg) +{ + smack_t *ssp = smack_of_shm(shp); + int rc; + + if (ssp == NULL) + return 0; + + rc = smk_curacc(ssp, MAY_READWRITE); + return rc; +} + +static smack_t *smack_of_sem(struct sem_array *sma) +{ + if (sma == NULL) + return NULL; + + return (smack_t *)sma->sem_perm.security; +} + +static int smack_sem_alloc_security(struct sem_array *sma) +{ + smack_t *csp = smk_of_task(current); + struct kern_ipc_perm *isp = &sma->sem_perm; + + isp->security = new_smack_t(*csp); + if (isp->security == NULL) + return -ENOMEM; + return 0; +} + +static void smack_sem_free_security(struct sem_array *sma) +{ + struct kern_ipc_perm *isp = &sma->sem_perm; + + if (isp->security != NULL) + isp->security = free_smack_t(isp->security); +} + + +static int smack_sem_associate(struct sem_array *sma, int semflg) +{ + smack_t *ssp = smack_of_sem(sma); + int rc; + + if (ssp == NULL) + return 0; + + rc = smk_curacc(ssp, MAY_READWRITE); + return rc; +} + +static int smack_sem_semctl(struct sem_array *sma, int cmd) +{ + smack_t *ssp = smack_of_sem(sma); + int rc; + + if (ssp == NULL) + return 0; + + rc = smk_curacc(ssp, MAY_READWRITE); + return rc; +} + +static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops, unsigned nsops, int alter) +{ + smack_t *ssp = smack_of_sem(sma); + int rc; + + if (ssp == NULL) + return 0; + + rc = smk_curacc(ssp, MAY_READWRITE); + return rc; +} + +static int smack_msg_queue_alloc_security(struct msg_queue *msq) +{ + smack_t *csp = smk_of_task(current); + struct kern_ipc_perm *kisp = &msq->q_perm; + + kisp->security = new_smack_t(*csp); + if (kisp->security == NULL) + return -ENOMEM; + return 0; +} + +static void smack_msg_queue_free_security(struct msg_queue *msq) +{ + struct kern_ipc_perm *kisp = &msq->q_perm; + + if (kisp->security != NULL) + kisp->security = free_smack_t(kisp->security); +} + +static smack_t *smack_of_msq(struct msg_queue *msq) +{ + if (msq == NULL) + return NULL; + + return (smack_t *)msq->q_perm.security; +} + +static int smack_msg_queue_associate(struct msg_queue *msq, int msqflg) +{ + smack_t *msp = smack_of_msq(msq); + int rc; + + if (msp == NULL) + return 0; + + rc = smk_curacc(msp, MAY_READWRITE); + return rc; +} + +static int smack_msg_queue_msgctl(struct msg_queue *msq, int cmd) +{ + smack_t *msp = smack_of_msq(msq); + int rc; + + if (msp == NULL) + return 0; + + rc = smk_curacc(msp, MAY_READWRITE); + return rc; +} + +static int smack_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg) +{ + smack_t *msp = smack_of_msq(msq); + int rc; + + if (msp == NULL) + return 0; + + rc = smk_curacc(msp, MAY_READWRITE); + return rc; +} + +static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, struct task_struct *target, long type, int mode) +{ + smack_t *msp = smack_of_msq(msq); + int rc; + + if (msp == NULL) + return 0; + + rc = smk_curacc(msp, MAY_READWRITE); + return rc; +} + +static void smack_task_to_inode(struct task_struct *p, struct inode *inode) +{ + smack_t *tsp = smk_of_task(p); + smack_t *isp = inode->i_security; + + *isp = *tsp; +} + +static int smack_task_wait(struct task_struct *p) +{ + smack_t *tsp = smk_of_task(p); + smack_t *csp = smk_of_task(current); + int rc; + + rc = smk_access(csp, tsp, MAY_WRITE); + if (rc != 0 && __capable(p, CAP_MAC_OVERRIDE)) + return 0; + /* + * Don't wait for children who can't send + * a signal. Sad state of our society. + * This is not yet right + */ + + return rc; +} + +static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) +{ + smack_t *isp = ipp->security; + int rc; + + + rc = smk_curacc(isp, MAY_READWRITE); + return rc; +} + +static void smack_release_secctx(char *secdata, u32 seclen) +{ + smack_t *sp = (smack_t *)secdata; + + if (sp != NULL) + free_smack_t(sp); +} + +static int smack_getprocattr(struct task_struct *p, char *name, char **value) +{ + smack_t *sp = smk_of_task(p); + int slen = strlen((char *)sp); + + if (strcmp(name, "current") == 0) { + *value = (char *)new_smack_t(*sp); + return slen; + } + + return -EINVAL; +} + +static int smack_setprocattr(struct task_struct *p, char *name, + void *value, size_t size) +{ + smack_t *psp = smk_of_task(p); + smack_t newsmack; + char tb[sizeof(smack_t)]; + + if (value == NULL || size == 0 || size >= sizeof(smack_t)) + return -EINVAL; + + strncpy(tb, (char *)value, size); + tb[size] = '\0'; + if (strcmp(name, "current") == 0) { + newsmack = smk_from_string(tb); + if (newsmack == SMK_INVALID) + return -EINVAL; + *psp = newsmack; + return size; + } + return -EINVAL; +} + +static int smack_unix_stream_connect(struct socket *sock, + struct socket *other, struct sock *newsk) +{ + smack_t *ssp = SOCK_INODE(sock)->i_security; + smack_t *osp = SOCK_INODE(other)->i_security; + int rc; + + rc = smk_access(ssp, osp, MAY_READWRITE); + return rc; +} + +static int smack_unix_may_send(struct socket *sock, struct socket *other) +{ + smack_t *ssp = SOCK_INODE(sock)->i_security; + smack_t *osp = SOCK_INODE(other)->i_security; + int rc; + + rc = smk_access(ssp, osp, MAY_WRITE); + return rc; +} + +/* + * This is the "ambient" label for network traffic. + * If it isn't somehow marked, use this. + * It can be reset via smackfs/ambient + */ +smack_t smack_net_ambient = SMK_FLOOR; +/* + * This is the default packet marking scheme for network traffic. + * It can be reset via smackfs/nltype + */ +int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4; + +/* + * Convert a netlabel mls_lvl/mls_cat pair into a smack value. + */ + +static smack_t smack_from_secattr(struct netlbl_lsm_secattr *sap) +{ + struct smk_cipso_entry *scp; + smack_t smack = 0LL; + int pcat; + + if ((sap->flags & NETLBL_SECATTR_MLS_LVL) == 0) { + /* + * If there are flags but no level netlabel isn't + * behaving the way we expect it to. + * + * Without guidance regarding the smack value + * for the packet fall back on the network + * ambient value. + */ + return smack_net_ambient; + } + /* + * Get the categories, if any + */ + if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0) + for (pcat = -1;;) { + pcat = netlbl_secattr_catmap_walk(sap->mls_cat, pcat+1); + if (pcat < 0) + break; + smack_catset_bit(pcat, &smack); + } + /* + * If it is CIPSO using smack direct mapping + * we are already done. WeeHee. + */ + if (sap->mls_lvl == SMACK_CIPSO_DIRECT) + return smack; + + /* + * Look it up in the supplied table if it is not a direct mapping. + */ + for (scp = smack_cipso; scp != NULL; scp = scp->smk_next) + if (scp->smk_level == sap->mls_lvl && scp->smk_catset == smack) + return scp->smk_smack; + /* + * It is CIPSO, but not one we know. + */ + + return SMK_HUH; +} + +static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + struct netlbl_lsm_secattr secattr; + struct task_smack *ssp = sk->sk_security; + smack_t si; + int rc; + + if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) + return 0; + + /* + * Translate what netlabel gave us. + */ + netlbl_secattr_init(&secattr); + rc = netlbl_skbuff_getattr(skb, &secattr); + if (rc == 0) + si = smack_from_secattr(&secattr); + else + si = smack_net_ambient; + netlbl_secattr_destroy(&secattr); + /* + * Receiving a packet requires that the other end + * be able to write here. Read access is not required. + * This is the simplist possible security model + * for networking. + */ + rc = smk_access(&si, &ssp->smk_in, MAY_WRITE); + + /* + * Set the receive packet on success. + */ + if (rc == 0) + ssp->smk_packet = si; + + return rc; +} + +/* + * The only reason for this hook is to reset the + * socket attributes if the smk_out has changed. + */ +static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, + int size) +{ + struct task_smack *ssp = sock->sk->sk_security; + int rc; + + if ((ssp->smk_flags & SMK_OUT_RESET) != 0) { + rc = smack_task_to_sock(sock->sk, ssp); + if (rc == 0) + ssp->smk_flags ^= SMK_OUT_RESET; + } + + return 0; +} + +static void smack_sock_graft(struct sock *sk, struct socket *parent) +{ + struct task_smack *ssp; + struct netlbl_lsm_secattr secattr; + smack_t ssi; + int rc; + + if (sk == NULL || parent == NULL || parent->sk == NULL) + return; + + if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) + return; + + ssp = parent->sk->sk_security; + + netlbl_secattr_init(&secattr); + rc = netlbl_sock_getattr(sk, &secattr); + if (rc == 0) + ssi = smack_from_secattr(&secattr); + else + ssi = SMK_HUH; + netlbl_secattr_destroy(&secattr); + + netlbl_secattr_init(&secattr); + smack_to_secattr(ssi, ssp->smk_ipscheme, &secattr); + if (secattr.flags != 0) + rc = netlbl_sock_setattr(parent->sk, &secattr); + netlbl_secattr_destroy(&secattr); +} + +static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, + struct request_sock *req) +{ + struct netlbl_lsm_secattr skb_secattr; + struct task_smack *ssp = sk->sk_security; + smack_t sb; + int rc; + + if (skb == NULL) + return -EACCES; + + netlbl_secattr_init(&skb_secattr); + rc = netlbl_skbuff_getattr(skb, &skb_secattr); + if (rc == 0) + sb = smack_from_secattr(&skb_secattr); + else + sb = SMK_HUH; + netlbl_secattr_destroy(&skb_secattr); + /* + * Receiving a packet requires that the other end + * be able to write here. Read access is not required. + */ + rc = smk_access(&sb, &ssp->smk_in, MAY_WRITE); + return rc; +} + +/* + * "Done" above here. + */ + +/* key management security hooks */ +#ifdef CONFIG_KEYS +static int smack_key_alloc(struct key *key, struct task_struct *tsk, unsigned long flags) +{ + printk("%s:%d \n", __FUNCTION__, __LINE__); +} + +static void smack_key_free(struct key *key) +{ + printk("%s:%d \n", __FUNCTION__, __LINE__); +} + +static int smack_key_permission(key_ref_t key_ref, struct task_struct *context, key_perm_t perm) +{ + printk("%s:%d \n", __FUNCTION__, __LINE__); +} +#endif /* CONFIG_KEYS */ + +static struct security_operations smack_ops = { + .ptrace = smack_ptrace, + .capget = cap_capget, + .capset_check = cap_capset_check, + .capset_set = cap_capset_set, + .capable = cap_capable, + /* .acct No hook required */ + /* .sysctl No hook required */ + /* .quotactl No hook required */ + /* .quota_on No hook required */ + .syslog = smack_syslog, + .settime = cap_settime, + .vm_enough_memory = cap_vm_enough_memory, + + /* .bprm_alloc_security No hook required */ + /* .bprm_free_security No hook required */ + .bprm_apply_creds = cap_bprm_apply_creds, + /* .bprm_post_apply_creds No hook required */ + .bprm_set_security = cap_bprm_set_security, + /* .bprm_check_security No hook required */ + .bprm_secureexec = cap_bprm_secureexec, + + .sb_alloc_security = smack_sb_alloc_security, + .sb_free_security = smack_sb_free_security, + .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_check_sb No hook required */ + .sb_umount = smack_sb_umount, + /* .sb_umount_close No hook required */ + /* .sb_umount_busy No hook required */ + /* .sb_post_remount No hook required */ + /* .sb_post_mountroot No hook required */ + /* .sb_post_addmount No hook required */ + /* .sb_pivotroot No hook required */ + /* .sb_post_pivotroot No hook required */ + + .inode_alloc_security = smack_inode_alloc_security, + .inode_free_security = smack_inode_free_security, + .inode_init_security = smack_inode_init_security, + .inode_create = smack_inode_create, + .inode_link = smack_inode_link, + .inode_unlink = smack_inode_unlink, + .inode_symlink = smack_inode_symlink, + .inode_mkdir = smack_inode_mkdir, + .inode_rmdir = smack_inode_rmdir, + .inode_mknod = smack_inode_mknod, + .inode_rename = smack_inode_rename, + .inode_readlink = smack_inode_readlink, + .inode_follow_link = smack_inode_follow_link, + .inode_permission = smack_inode_permission, + .inode_setattr = smack_inode_setattr, + .inode_getattr = smack_inode_getattr, + /* .inode_delete No hook required */ + .inode_setxattr = smack_inode_setxattr, + .inode_post_setxattr = smack_inode_post_setxattr, + .inode_getxattr = smack_inode_getxattr, + .inode_listxattr = smack_inode_listxattr, + .inode_removexattr = smack_inode_removexattr, + .inode_xattr_getsuffix = smack_inode_xattr_getsuffix, + .inode_getsecurity = smack_inode_getsecurity, + .inode_setsecurity = smack_inode_setsecurity, + .inode_listsecurity = smack_inode_listsecurity, + + .file_permission = smack_file_permission, + .file_alloc_security = smack_file_alloc_security, + .file_free_security = smack_file_free_security, + .file_ioctl = smack_file_ioctl, + .file_mmap = smack_file_mmap, + /* .file_mprotect No hook required */ + .file_lock = smack_file_lock, + .file_fcntl = smack_file_fcntl, + .file_set_fowner = smack_file_set_fowner, + .file_send_sigiotask = smack_file_send_sigiotask, + .file_receive = smack_file_receive, + + /* .task_create No hook required */ + .task_alloc_security = smack_task_alloc_security, + .task_free_security = smack_task_free_security, + /* .task_setuid No hook required */ + .task_post_setuid = cap_task_post_setuid, + /* .task_setgid No hook required */ + .task_setpgid = smack_task_setpgid, + .task_getpgid = smack_task_getpgid, + .task_getsid = smack_task_getsid, + .task_getsecid = smack_task_getsecid, + /* .task_setgroups No hook required */ + .task_setnice = smack_task_setnice, + .task_setioprio = smack_task_setioprio, + .task_getioprio = smack_task_getioprio, + /* .task_setrlimit No hook required */ + .task_setscheduler = smack_task_setscheduler, + .task_getscheduler = smack_task_getscheduler, + .task_movememory = smack_task_movememory, + .task_kill = smack_task_kill, + .task_wait = smack_task_wait, + /* .task_prctl No hook required */ + .task_reparent_to_init = cap_task_reparent_to_init, + .task_to_inode = smack_task_to_inode, + + .ipc_permission = smack_ipc_permission, + + .msg_msg_alloc_security = smack_msg_msg_alloc_security, + .msg_msg_free_security = smack_msg_msg_free_security, + + .msg_queue_alloc_security = smack_msg_queue_alloc_security, + .msg_queue_free_security = smack_msg_queue_free_security, + .msg_queue_associate = smack_msg_queue_associate, + .msg_queue_msgctl = smack_msg_queue_msgctl, + .msg_queue_msgsnd = smack_msg_queue_msgsnd, + .msg_queue_msgrcv = smack_msg_queue_msgrcv, + + .shm_alloc_security = smack_shm_alloc_security, + .shm_free_security = smack_shm_free_security, + .shm_associate = smack_shm_associate, + .shm_shmctl = smack_shm_shmctl, + .shm_shmat = smack_shm_shmat, + + .sem_alloc_security = smack_sem_alloc_security, + .sem_free_security = smack_sem_free_security, + .sem_associate = smack_sem_associate, + .sem_semctl = smack_sem_semctl, + .sem_semop = smack_sem_semop, + + .netlink_send = cap_netlink_send, + .netlink_recv = cap_netlink_recv, + + /* .register_security No hook required */ + /* .unregister_security No hook required */ + + .d_instantiate = smack_d_instantiate, + + .getprocattr = smack_getprocattr, + .setprocattr = smack_setprocattr, + /* .secid_to_secctx No hook required */ + .release_secctx = smack_release_secctx, + + .unix_stream_connect = smack_unix_stream_connect, + .unix_may_send = smack_unix_may_send, + + /* .socket_create No hook required */ + .socket_post_create = smack_socket_post_create, + /* .socket_bind No hook required */ + /* .socket_connect No hook required */ + /* .socket_listen No hook required */ + /* .socket_accept No hook required */ + /* .socket_post_accept No hook required */ + .socket_sendmsg = smack_socket_sendmsg, + /* .socket_recvmsg No hook required */ + /* .socket_getsockname No hook required */ + /* .socket_getpeername No hook required */ + /* .socket_getsockopt No hook required */ + /* .socket_setsockopt No hook required */ + /* .socket_shutdown No hook required */ + .socket_sock_rcv_skb = smack_socket_sock_rcv_skb, + /* .socket_getpeersec_stream No hook required */ + /* .socket_getpeersec_dgram No hook required */ + .sk_alloc_security = smack_sk_alloc_security, + .sk_free_security = smack_sk_free_security, + /* .sk_clone_security No hook required */ + /* .sk_getsecid No hook required */ + .sock_graft = smack_sock_graft, + .inet_conn_request = smack_inet_conn_request, + /* .inet_csk_clone No hook required */ + /* .inet_conn_established No hook required */ + + /* .req_classify_flow No hook required */ + /* .xfrm_policy_alloc_security no xfrm for smack */ + /* .xfrm_policy_clone_security no xfrm for smack */ + /* .xfrm_policy_free_security no xfrm for smack */ + /* .xfrm_policy_delete_security no xfrm for smack */ + /* .xfrm_state_alloc_security no xfrm for smack */ + /* .xfrm_state_free_security no xfrm for smack */ + /* .xfrm_state_delete_security no xfrm for smack */ + /* .xfrm_policy_lookup no xfrm for smack */ + /* .xfrm_state_pol_flow_match no xfrm for smack */ + /* .xfrm_decode_session no xfrm for smack */ + + /* key management security hooks */ +#ifdef CONFIG_KEYS + .key_alloc = smack_key_alloc, + .key_free = smack_key_free, + .key_permission = smack_key_permission, +#endif /* CONFIG_KEYS */ + +}; + +static __init int smack_init(void) +{ + struct task_smack *tsp; + printk(KERN_INFO "Smack: Initializing.\n"); + + /* Set the security state for the initial task. */ + + tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL); + if (tsp == NULL) + panic("smack: Failed to initialize initial task.\n"); + + tsp->smk_task = SMK_FLOOR; + tsp->smk_in = SMK_FLOOR; + tsp->smk_out = SMK_FLOOR; + tsp->smk_ipscheme = NETLBL_NLTYPE_CIPSOV4; + current->security = tsp; + + if (register_security(&smack_ops)) + panic("smack: Unable to register with kernel.\n"); + + return 0; +} + +/* smack requires early initialization in order to label + all processes and objects when they are created. */ +security_initcall(smack_init); +