diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/Documentation/Smack.txt linux-2.6.23-rc4-mm1-audit/Documentation/Smack.txt --- linux-2.6.23-rc4-mm1-base/Documentation/Smack.txt 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23-rc4-mm1-audit/Documentation/Smack.txt 2007-09-02 22:22:34.000000000 -0700 @@ -0,0 +1,104 @@ + + + "Good for you, you've decided to clean the elevator!" + - The Elevator, from Dark Star + +Smack is the the Simplified Mandatory Access Control Kernel. +Smack is a kernel based implementation of mandatory access +control that includes simplicity in its primary design goals. + +Smack does not implement Domain Type Enforcement (DTE). If +you want DTE Linux has an implementation called SELinux. +Those who really want DTE are encouraged to use SELinux. +Those who don't know what DTE is are encouraged to compare +SELinux with Smack to determine which mechanisms are best +suited to the problem at hand. + +Smack consists of three major components: + - The kernel + - A start-up script and a few modified applications + - Configuration data + +The kernel component of Smack is implemented as a Linux +Security Modules (LSM) module. It requires netlabel and +works best with file systems that support extended attributes, +although xattr support is not strictly required. The Smack +patch is (currently) self contained, except for Kconfig +and Makefile changes in the security directory. It is safe +to run a Smack kernel under a "vanilla" distribution. +Smack kernels use the CIPSO IP option. Some network +configurations are intolerant of IP options and can impede +access to systems that use them as Smack does. + +The startup script etc-init.d-smack should be installed +in /etc/init.d/smack and should be invoked early in the +start-up process. On Fedora rc5.d/S02smack is recommended. +This script ensures that certain devices have the correct +Smack attributes and loads the Smack configuration if +any is defined. + +A version of "ls" that provides a "-M" option to display +Smack labels on long listing is available. + +A hacked version of sshd that allows network logins by users +with specific Smack labels is available. This version does +not work for scp. You must set the /etc/ssh/sshd_config +line: + UsePrivilegeSeparation no + +The format of /etc/smack/usr is: + + username smack + +In keeping with the intent of Smack, configuration data is +minimal and not strictly required. The most important +configuration step is mounting the smackfs pseudo filesystem. + +Add this line to /etc/fstab: + + smackfs /smack smackfs smackfsdef=* 0 0 + +and create the /smack directory for mounting. + +Smack uses extended attributes (xattrs) to store file labels. +The command to set a Smack label on a file is: + + # attr -S -s SMACK64 -V "value" path + +NOTE: Smack labels are limited to 7 characters. The attr command + does not enforce this restriction and can be used to set + invalid Smack labels on files. + +The smackfs filesystem treats symbolic links in a special way. +The Smack label of the process is appended to the link value +during resolution. To provide a /tmp that is available to users +with multiple labels: + + # mkdir /moldy + # mv /tmp /moldy/_ + # ln -s /smack/tmp /tmp + +For each label users are going to use: + + # mkdir /moldy/label + # chmod /moldy/label + # attr -S -s SMACK64 -V "label" /moldy/label + +If you don't do anything special all users will get the floor ("_") +label when they log in. If you do want to log in via the hacked ssh +at other labels use the attr command to set the smack value on the +home directory and it's contents. + +You can add access rules in /etc/smack/accesses. They take the form: + + subjectlabel objectlabel access + +access is a combination of the letters rwxa which specify the +kind of access permitted a subject with subjectlabel on an +object with objectlabel. If there is no rule no access is allowed. + +A process can see the smack label it is running with by +reading /proc/self/attr/current. A privileged process can +set the process smack by writing there. + + diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/include/linux/audit.h linux-2.6.23-rc4-mm1-audit/include/linux/audit.h --- linux-2.6.23-rc4-mm1-base/include/linux/audit.h 2007-08-31 15:58:55.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/include/linux/audit.h 2007-09-02 18:04:55.000000000 -0700 @@ -336,20 +336,64 @@ struct audit_rule { /* for AUDIT_LIST, #ifdef __KERNEL__ #include -struct audit_sig_info { - uid_t uid; - pid_t pid; - char ctx[0]; -}; - struct audit_buffer; struct audit_context; +struct audit_parent; struct inode; struct netlink_skb_parms; struct linux_binprm; struct mq_attr; struct mqstat; +struct audit_sig_info { + uid_t uid; + pid_t pid; + char ctx[0]; +}; + +struct audit_watch { + atomic_t count; /* reference count */ + char *path; /* insertion path */ + dev_t dev; /* associated superblock device */ + unsigned long ino; /* associated inode number */ + struct audit_parent *parent; /* associated parent */ + struct list_head wlist; /* entry in parent->watches list */ + struct list_head rules; /* associated rules */ +}; + +struct audit_field { + u32 type; + u32 val; + u32 op; + char *se_str; + void *se_rule; +}; + +struct audit_krule { + int vers_ops; + u32 flags; + u32 listnr; + u32 action; + u32 mask[AUDIT_BITMASK_SIZE]; + u32 buflen; /* for data alloc on list rules */ + u32 field_count; + char *filterkey; /* ties events to rules */ + struct audit_field *fields; + struct audit_field *arch_f; /* quick access to arch field */ + struct audit_field *inode_f; /* quick access to an inode field */ + struct audit_watch *watch; /* associated watch */ + struct audit_tree *tree; /* associated watched tree */ + struct list_head rlist; /* entry in audit_watch.rules list */ +}; + +struct audit_entry { + struct list_head list; + struct rcu_head rcu; + struct audit_krule rule; +}; + +extern int audit_rule_update_callout(void); + #define AUDITSC_INVALID 0 #define AUDITSC_SUCCESS 1 #define AUDITSC_FAILURE 2 diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/include/linux/security.h linux-2.6.23-rc4-mm1-audit/include/linux/security.h --- linux-2.6.23-rc4-mm1-base/include/linux/security.h 2007-08-31 15:58:55.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/include/linux/security.h 2007-09-01 16:23:53.000000000 -0700 @@ -42,6 +42,7 @@ extern kernel_cap_t cap_bset; extern unsigned securebits; struct ctl_table; +struct audit_krule; /* * These functions are in security/capability.c and are used @@ -1159,15 +1160,47 @@ struct request_sock; * @name contains the name of the security module being stacked. * @ops contains a pointer to the struct security_operations of the module to stack. * + * Hooks for the export of secids + * * @secid_to_secctx: * Convert secid to security context. * @secid contains the security ID. * @secdata contains the pointer that stores the converted security context. - * * @release_secctx: * Release the security context. * @secdata contains the security context. * @seclen contains the length of the security context. + * * @inode_getsecid: + * get the secid associated with the inode + * @inode contains a pointer to the inode + * @secid contains a pointer to the location to store the result + * @ipc_getsecid: + * get the secid associated with the ipc object + * @inode contains a pointer to the ipc object + * @secid contains a pointer to the location to store the result + * + * Security hooks for the audit system + * + * @audit_rule_supplied: + * identify audit rules interesting to this LSM + * @rule contains a pointer to the rule in question + * @audit_rule_match: + * checks if the LSM would have a record generated + * @sid contains the secid in question + * @field contains what to check + * @op contains the operation + * @rule contains a pointer to the audit rule + * @actx contains a pointer to the audit context + * @audit_rule_init: + * establish an audit rule + * @field contains the action of interest + * @op contains the operation + * @rulestr contains a pointer to the rule string + * @rule contains a pointer to receive the result + * @audit_rule_free: + * reclaim the rule + * @rule the data to free + * * * This is the main security structure. */ @@ -1351,6 +1384,13 @@ struct security_operations { int (*setprocattr)(struct task_struct *p, char *name, void *value, size_t size); int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen); void (*release_secctx)(char *secdata, u32 seclen); + void (*inode_getsecid) (const struct inode *inode, u32 *secid); + void (*ipc_getsecid) (const struct kern_ipc_perm *p, u32 *secid); + int (*audit_rule_supplied) (struct audit_krule *rule); + int (*audit_rule_match) (u32 sid, u32 field, u32 op, void *rule, + struct audit_context *actx); + int (*audit_rule_init) (u32 field, u32 op, char *rulestr, void **rule); + void (*audit_rule_free) (void *rule); #ifdef CONFIG_SECURITY_NETWORK int (*unix_stream_connect) (struct socket * sock, @@ -1582,6 +1622,13 @@ int security_netlink_send(struct sock *s int security_netlink_recv(struct sk_buff *skb, int cap); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); void security_release_secctx(char *secdata, u32 seclen); +void security_inode_getsecid(const struct inode *, u32 *); +void security_ipc_getsecid(struct kern_ipc_perm *, u32 *); +int security_audit_rule_supplied(struct audit_krule *); +int security_audit_rule_match(u32, u32, u32, void *, struct audit_context *); +int security_audit_rule_init(u32, u32, char *, void **); +void security_audit_rule_free(void *); + #else /* CONFIG_SECURITY */ @@ -2256,6 +2303,38 @@ static inline int security_secid_to_secc static inline void security_release_secctx(char *secdata, u32 seclen) { } + +static inline void security_inode_getsecid(const struct inode *inode, + u32 *secid) +{ +} + +static inline void security_ipc_getsecid(struct kern_ipc_perm *p, u32 *secid) +{ +} + +static inline int security_audit_rule_supplied(struct audit_krule *rule) +{ + return 0; +} + +static inline int security_audit_rule_match(u32 sid, u32 field, u32 op, + void *rule, + struct audit_context *actx) +{ + return 0; +} + +static inline int security_audit_rule_init(u32 field, u32 op, char *rulestr, + void **rule) +{ + return 0; +} + +static inline void security_audit_rule_free(void *rule) +{ +} + #endif /* CONFIG_SECURITY */ #ifdef CONFIG_SECURITY_NETWORK diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/include/linux/selinux.h linux-2.6.23-rc4-mm1-audit/include/linux/selinux.h --- linux-2.6.23-rc4-mm1-base/include/linux/selinux.h 2007-07-08 16:32:17.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/include/linux/selinux.h 2007-09-02 16:48:47.000000000 -0700 @@ -14,101 +14,9 @@ #ifndef _LINUX_SELINUX_H #define _LINUX_SELINUX_H -struct selinux_audit_rule; -struct audit_context; -struct inode; -struct kern_ipc_perm; - #ifdef CONFIG_SECURITY_SELINUX /** - * selinux_audit_rule_init - alloc/init an selinux audit rule structure. - * @field: the field this rule refers to - * @op: the operater the rule uses - * @rulestr: the text "target" of the rule - * @rule: pointer to the new rule structure returned via this - * - * Returns 0 if successful, -errno if not. On success, the rule structure - * will be allocated internally. The caller must free this structure with - * selinux_audit_rule_free() after use. - */ -int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, - struct selinux_audit_rule **rule); - -/** - * selinux_audit_rule_free - free an selinux audit rule structure. - * @rule: pointer to the audit rule to be freed - * - * This will free all memory associated with the given rule. - * If @rule is NULL, no operation is performed. - */ -void selinux_audit_rule_free(struct selinux_audit_rule *rule); - -/** - * selinux_audit_rule_match - determine if a context ID matches a rule. - * @sid: the context ID to check - * @field: the field this rule refers to - * @op: the operater the rule uses - * @rule: pointer to the audit rule to check against - * @actx: the audit context (can be NULL) associated with the check - * - * Returns 1 if the context id matches the rule, 0 if it does not, and - * -errno on failure. - */ -int selinux_audit_rule_match(u32 sid, u32 field, u32 op, - struct selinux_audit_rule *rule, - struct audit_context *actx); - -/** - * selinux_audit_set_callback - set the callback for policy reloads. - * @callback: the function to call when the policy is reloaded - * - * This sets the function callback function that will update the rules - * upon policy reloads. This callback should rebuild all existing rules - * using selinux_audit_rule_init(). - */ -void selinux_audit_set_callback(int (*callback)(void)); - -/** - * selinux_sid_to_string - map a security context ID to a string - * @sid: security context ID to be converted. - * @ctx: address of context string to be returned - * @ctxlen: length of returned context string. - * - * Returns 0 if successful, -errno if not. On success, the context - * string will be allocated internally, and the caller must call - * kfree() on it after use. - */ -int selinux_sid_to_string(u32 sid, char **ctx, u32 *ctxlen); - -/** - * selinux_get_inode_sid - get the inode's security context ID - * @inode: inode structure to get the sid from. - * @sid: pointer to security context ID to be filled in. - * - * Returns nothing - */ -void selinux_get_inode_sid(const struct inode *inode, u32 *sid); - -/** - * selinux_get_ipc_sid - get the ipc security context ID - * @ipcp: ipc structure to get the sid from. - * @sid: pointer to security context ID to be filled in. - * - * Returns nothing - */ -void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid); - -/** - * selinux_get_task_sid - return the SID of task - * @tsk: the task whose SID will be returned - * @sid: pointer to security context ID to be filled in. - * - * Returns nothing - */ -void selinux_get_task_sid(struct task_struct *tsk, u32 *sid); - -/** * selinux_string_to_sid - map a security context string to a security ID * @str: the security context string to be mapped * @sid: ID value returned via this. @@ -132,52 +40,6 @@ int selinux_relabel_packet_permission(u3 #else -static inline int selinux_audit_rule_init(u32 field, u32 op, - char *rulestr, - struct selinux_audit_rule **rule) -{ - return -ENOTSUPP; -} - -static inline void selinux_audit_rule_free(struct selinux_audit_rule *rule) -{ - return; -} - -static inline int selinux_audit_rule_match(u32 sid, u32 field, u32 op, - struct selinux_audit_rule *rule, - struct audit_context *actx) -{ - return 0; -} - -static inline void selinux_audit_set_callback(int (*callback)(void)) -{ - return; -} - -static inline int selinux_sid_to_string(u32 sid, char **ctx, u32 *ctxlen) -{ - *ctx = NULL; - *ctxlen = 0; - return 0; -} - -static inline void selinux_get_inode_sid(const struct inode *inode, u32 *sid) -{ - *sid = 0; -} - -static inline void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid) -{ - *sid = 0; -} - -static inline void selinux_get_task_sid(struct task_struct *tsk, u32 *sid) -{ - *sid = 0; -} - static inline int selinux_string_to_sid(const char *str, u32 *sid) { *sid = 0; diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/kernel/audit.c linux-2.6.23-rc4-mm1-audit/kernel/audit.c --- linux-2.6.23-rc4-mm1-base/kernel/audit.c 2007-08-31 15:58:55.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/kernel/audit.c 2007-09-02 21:29:20.000000000 -0700 @@ -55,7 +55,6 @@ #include #include #include -#include #include #include #include @@ -253,12 +252,12 @@ static int audit_set_rate_limit(int limi if (sid) { char *ctx = NULL; u32 len; - if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { + if ((rc = security_secid_to_secctx(sid, &ctx, &len)) == 0) { audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_rate_limit=%d old=%d by auid=%u" " subj=%s res=%d", limit, old, loginuid, ctx, res); - kfree(ctx); + security_release_secctx(ctx, len); } else res = 0; /* Something weird, deny request */ } @@ -288,12 +287,12 @@ static int audit_set_backlog_limit(int l if (sid) { char *ctx = NULL; u32 len; - if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { + if ((rc = security_secid_to_secctx(sid, &ctx, &len)) == 0) { audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_backlog_limit=%d old=%d by auid=%u" " subj=%s res=%d", limit, old, loginuid, ctx, res); - kfree(ctx); + security_release_secctx(ctx, len); } else res = 0; /* Something weird, deny request */ } @@ -326,12 +325,12 @@ static int audit_set_enabled(int state, if (sid) { char *ctx = NULL; u32 len; - if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { + if ((rc = security_secid_to_secctx(sid, &ctx, &len)) == 0) { audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_enabled=%d old=%d by auid=%u" " subj=%s res=%d", state, old, loginuid, ctx, res); - kfree(ctx); + security_release_secctx(ctx, len); } else res = 0; /* Something weird, deny request */ } @@ -366,12 +365,12 @@ static int audit_set_failure(int state, if (sid) { char *ctx = NULL; u32 len; - if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { + if ((rc = security_secid_to_secctx(sid, &ctx, &len)) == 0) { audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_failure=%d old=%d by auid=%u" " subj=%s res=%d", state, old, loginuid, ctx, res); - kfree(ctx); + security_release_secctx(ctx, len); } else res = 0; /* Something weird, deny request */ } @@ -636,7 +635,7 @@ static int audit_receive_msg(struct sk_b if (status_get->mask & AUDIT_STATUS_PID) { int old = audit_pid; if (sid) { - if ((err = selinux_sid_to_string( + if ((err = security_secid_to_secctx( sid, &ctx, &len))) return err; else @@ -645,7 +644,7 @@ static int audit_receive_msg(struct sk_b "audit_pid=%d old=%d by auid=%u subj=%s", status_get->pid, old, loginuid, ctx); - kfree(ctx); + security_release_secctx(ctx, len); } else audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_pid=%d old=%d by auid=%u", @@ -679,7 +678,7 @@ static int audit_receive_msg(struct sk_b "user pid=%d uid=%u auid=%u", pid, uid, loginuid); if (sid) { - if (selinux_sid_to_string( + if (security_secid_to_secctx( sid, &ctx, &len)) { audit_log_format(ab, " ssid=%u", sid); @@ -687,7 +686,7 @@ static int audit_receive_msg(struct sk_b } else audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); } if (msg_type != AUDIT_USER_TTY) audit_log_format(ab, " msg='%.1024s'", @@ -717,7 +716,7 @@ static int audit_receive_msg(struct sk_b "pid=%d uid=%u auid=%u", pid, uid, loginuid); if (sid) { - if (selinux_sid_to_string( + if (security_secid_to_secctx( sid, &ctx, &len)) { audit_log_format(ab, " ssid=%u", sid); @@ -725,7 +724,7 @@ static int audit_receive_msg(struct sk_b } else audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); } audit_log_format(ab, " audit_enabled=%d res=0", audit_enabled); @@ -751,7 +750,7 @@ static int audit_receive_msg(struct sk_b "pid=%d uid=%u auid=%u", pid, uid, loginuid); if (sid) { - if (selinux_sid_to_string( + if (security_secid_to_secctx( sid, &ctx, &len)) { audit_log_format(ab, " ssid=%u", sid); @@ -759,7 +758,7 @@ static int audit_receive_msg(struct sk_b } else audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); } audit_log_format(ab, " audit_enabled=%d res=0", audit_enabled); @@ -782,11 +781,11 @@ static int audit_receive_msg(struct sk_b if (sid) { u32 len; ctx = NULL; - if (selinux_sid_to_string(sid, &ctx, &len)) + if (security_secid_to_secctx(sid, &ctx, &len)) audit_log_format(ab, " ssid=%u", sid); else audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); } audit_log_format(ab, " op=trim res=1"); audit_log_end(ab); @@ -824,11 +823,11 @@ static int audit_receive_msg(struct sk_b if (sid) { u32 len; ctx = NULL; - if (selinux_sid_to_string(sid, &ctx, &len)) + if (security_secid_to_secctx(audit_sig_sid, &ctx, &len)) audit_log_format(ab, " ssid=%u", sid); else audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); } audit_log_format(ab, " op=make_equiv old="); audit_log_untrustedstring(ab, old); @@ -841,18 +840,18 @@ static int audit_receive_msg(struct sk_b break; } case AUDIT_SIGNAL_INFO: - err = selinux_sid_to_string(audit_sig_sid, &ctx, &len); + err = security_secid_to_secctx(audit_sig_sid, &ctx, &len); if (err) return err; sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL); if (!sig_data) { - kfree(ctx); + security_release_secctx(ctx, len); return -ENOMEM; } sig_data->uid = audit_sig_uid; sig_data->pid = audit_sig_pid; memcpy(sig_data->ctx, ctx, len); - kfree(ctx); + security_release_secctx(ctx, len); audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO, 0, 0, sig_data, sizeof(*sig_data) + len); kfree(sig_data); @@ -971,10 +970,6 @@ static int __init audit_init(void) audit_initialized = 1; audit_enabled = audit_default; - /* Register the callback with selinux. This callback will be invoked - * when a new policy is loaded. */ - selinux_audit_set_callback(&selinux_audit_rule_update); - audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized"); #ifdef CONFIG_AUDITSYSCALL diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/kernel/auditfilter.c linux-2.6.23-rc4-mm1-audit/kernel/auditfilter.c --- linux-2.6.23-rc4-mm1-base/kernel/auditfilter.c 2007-08-31 15:58:55.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/kernel/auditfilter.c 2007-09-02 16:48:47.000000000 -0700 @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "audit.h" /* @@ -38,7 +38,7 @@ * Synchronizes writes and blocking reads of audit's filterlist * data. Rcu is used to traverse the filterlist and access * contents of structs audit_entry, audit_watch and opaque - * selinux rules during filtering. If modified, these structures + * LSM rules during filtering. If modified, these structures * must be copied and replace their counterparts in the filterlist. * An audit_parent struct is not accessed during filtering, so may * be written directly provided audit_filter_mutex is held. @@ -138,7 +138,7 @@ static inline void audit_free_rule(struc for (i = 0; i < e->rule.field_count; i++) { struct audit_field *f = &e->rule.fields[i]; kfree(f->se_str); - selinux_audit_rule_free(f->se_rule); + security_audit_rule_free(f->se_rule); } kfree(e->rule.fields); kfree(e->rule.filterkey); @@ -595,12 +595,12 @@ static struct audit_entry *audit_data_to goto exit_free; entry->rule.buflen += f->val; - err = selinux_audit_rule_init(f->type, f->op, str, + err = security_audit_rule_init(f->type, f->op, str, &f->se_rule); /* Keep currently invalid fields around in case they * become valid after a policy reload. */ if (err == -EINVAL) { - printk(KERN_WARNING "audit rule for selinux " + printk(KERN_WARNING "audit rule for LSM " "\'%s\' is invalid\n", str); err = 0; } @@ -860,10 +860,10 @@ out: return new; } -/* Duplicate selinux field information. The se_rule is opaque, so must be - * re-initialized. */ -static inline int audit_dupe_selinux_field(struct audit_field *df, - struct audit_field *sf) +/* Duplicate security module field information. + * The se_rule is opaque, so must be re-initialized. */ +static inline int audit_dupe_security_field(struct audit_field *df, + struct audit_field *sf) { int ret = 0; char *se_str; @@ -875,12 +875,12 @@ static inline int audit_dupe_selinux_fie df->se_str = se_str; /* our own (refreshed) copy of se_rule */ - ret = selinux_audit_rule_init(df->type, df->op, df->se_str, - &df->se_rule); + ret = security_audit_rule_init(df->type, df->op, df->se_str, + &df->se_rule); /* Keep currently invalid fields around in case they * become valid after a policy reload. */ if (ret == -EINVAL) { - printk(KERN_WARNING "audit rule for selinux \'%s\' is " + printk(KERN_WARNING "audit rule for LSM \'%s\' is " "invalid\n", df->se_str); ret = 0; } @@ -889,7 +889,7 @@ static inline int audit_dupe_selinux_fie } /* Duplicate an audit rule. This will be a deep copy with the exception - * of the watch - that pointer is carried over. The selinux specific fields + * of the watch - that pointer is carried over. The LSM specific fields * will be updated in the copy. The point is to be able to replace the old * rule with the new rule in the filterlist, then free the old rule. * The rlist element is undefined; list manipulations are handled apart from @@ -942,8 +942,8 @@ static struct audit_entry *audit_dupe_ru case AUDIT_OBJ_TYPE: case AUDIT_OBJ_LEV_LOW: case AUDIT_OBJ_LEV_HIGH: - err = audit_dupe_selinux_field(&new->fields[i], - &old->fields[i]); + err = audit_dupe_security_field(&new->fields[i], + &old->fields[i]); break; case AUDIT_FILTERKEY: fk = kstrdup(old->filterkey, GFP_KERNEL); @@ -1502,11 +1502,11 @@ static void audit_log_rule_change(uid_t if (sid) { char *ctx = NULL; u32 len; - if (selinux_sid_to_string(sid, &ctx, &len)) + if (security_secid_to_secctx(sid, &ctx, &len)) audit_log_format(ab, " ssid=%u", sid); else audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); } audit_log_format(ab, " op=%s rule key=", action); if (rule->filterkey) @@ -1748,38 +1748,12 @@ unlock_and_return: return result; } -/* Check to see if the rule contains any selinux fields. Returns 1 if there - are selinux fields specified in the rule, 0 otherwise. */ -static inline int audit_rule_has_selinux(struct audit_krule *rule) -{ - int i; - - for (i = 0; i < rule->field_count; i++) { - struct audit_field *f = &rule->fields[i]; - switch (f->type) { - case AUDIT_SUBJ_USER: - case AUDIT_SUBJ_ROLE: - case AUDIT_SUBJ_TYPE: - case AUDIT_SUBJ_SEN: - case AUDIT_SUBJ_CLR: - case AUDIT_OBJ_USER: - case AUDIT_OBJ_ROLE: - case AUDIT_OBJ_TYPE: - case AUDIT_OBJ_LEV_LOW: - case AUDIT_OBJ_LEV_HIGH: - return 1; - } - } - - return 0; -} - /* This function will re-initialize the se_rule field of all applicable rules. - * It will traverse the filter lists serarching for rules that contain selinux + * It will traverse the filter lists serarching for rules that contain LSM * specific filter fields. When such a rule is found, it is copied, the - * selinux field is re-initialized, and the old rule is replaced with the + * LSM field is re-initialized, and the old rule is replaced with the * updated rule. */ -int selinux_audit_rule_update(void) +int audit_rule_update_callout(void) { struct audit_entry *entry, *n, *nentry; struct audit_watch *watch; @@ -1791,7 +1765,7 @@ int selinux_audit_rule_update(void) for (i = 0; i < AUDIT_NR_FILTERS; i++) { list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) { - if (!audit_rule_has_selinux(&entry->rule)) + if (!security_audit_rule_supplied(&entry->rule)) continue; watch = entry->rule.watch; @@ -1802,7 +1776,7 @@ int selinux_audit_rule_update(void) * return value */ if (!err) err = PTR_ERR(nentry); - audit_panic("error updating selinux filters"); + audit_panic("error updating LSM filters"); if (watch) list_del(&entry->rule.rlist); list_del_rcu(&entry->list); diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/kernel/audit.h linux-2.6.23-rc4-mm1-audit/kernel/audit.h --- linux-2.6.23-rc4-mm1-base/kernel/audit.h 2007-08-31 15:58:55.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/kernel/audit.h 2007-09-02 18:45:45.000000000 -0700 @@ -55,50 +55,6 @@ enum audit_state { /* Rule lists */ struct audit_parent; -struct audit_watch { - atomic_t count; /* reference count */ - char *path; /* insertion path */ - dev_t dev; /* associated superblock device */ - unsigned long ino; /* associated inode number */ - struct audit_parent *parent; /* associated parent */ - struct list_head wlist; /* entry in parent->watches list */ - struct list_head rules; /* associated rules */ -}; - -struct audit_field { - u32 type; - u32 val; - u32 op; - char *se_str; - struct selinux_audit_rule *se_rule; -}; - -struct audit_tree; -struct audit_chunk; - -struct audit_krule { - int vers_ops; - u32 flags; - u32 listnr; - u32 action; - u32 mask[AUDIT_BITMASK_SIZE]; - u32 buflen; /* for data alloc on list rules */ - u32 field_count; - char *filterkey; /* ties events to rules */ - struct audit_field *fields; - struct audit_field *arch_f; /* quick access to arch field */ - struct audit_field *inode_f; /* quick access to an inode field */ - struct audit_watch *watch; /* associated watch */ - struct audit_tree *tree; /* associated watched tree */ - struct list_head rlist; /* entry in audit_{watch,tree}.rules list */ -}; - -struct audit_entry { - struct list_head list; - struct rcu_head rcu; - struct audit_krule rule; -}; - extern int audit_pid; #define AUDIT_INODE_BUCKETS 32 @@ -128,12 +84,11 @@ struct audit_netlink_list { int audit_send_list(void *); +struct audit_tree; struct inotify_watch; extern void audit_free_parent(struct inotify_watch *); extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32, const char *, struct inode *); -extern int selinux_audit_rule_update(void); - extern struct mutex audit_filter_mutex; extern void audit_free_rule_rcu(struct rcu_head *); diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/kernel/auditsc.c linux-2.6.23-rc4-mm1-audit/kernel/auditsc.c --- linux-2.6.23-rc4-mm1-base/kernel/auditsc.c 2007-08-31 15:58:55.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/kernel/auditsc.c 2007-09-02 16:48:47.000000000 -0700 @@ -61,7 +61,6 @@ #include #include #include -#include #include #include #include @@ -520,10 +519,10 @@ static int audit_filter_rules(struct tas logged upon error */ if (f->se_rule) { if (need_sid) { - selinux_get_task_sid(tsk, &sid); + security_task_getsecid(tsk, &sid); need_sid = 0; } - result = selinux_audit_rule_match(sid, f->type, + result = security_audit_rule_match(sid, f->type, f->op, f->se_rule, ctx); @@ -539,12 +538,12 @@ static int audit_filter_rules(struct tas if (f->se_rule) { /* Find files that match */ if (name) { - result = selinux_audit_rule_match( + result = security_audit_rule_match( name->osid, f->type, f->op, f->se_rule, ctx); } else if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (selinux_audit_rule_match( + if (security_audit_rule_match( ctx->names[j].osid, f->type, f->op, f->se_rule, ctx)) { @@ -560,7 +559,7 @@ static int audit_filter_rules(struct tas aux = aux->next) { if (aux->type == AUDIT_IPC) { struct audit_aux_data_ipcctl *axi = (void *)aux; - if (selinux_audit_rule_match(axi->osid, f->type, f->op, f->se_rule, ctx)) { + if (security_audit_rule_match(axi->osid, f->type, f->op, f->se_rule, ctx)) { ++result; break; } @@ -869,11 +868,11 @@ void audit_log_task_context(struct audit int error; u32 sid; - selinux_get_task_sid(current, &sid); + security_task_getsecid(current, &sid); if (!sid) return; - error = selinux_sid_to_string(sid, &ctx, &len); + error = security_secid_to_secctx(sid, &ctx, &len); if (error) { if (error != -EINVAL) goto error_path; @@ -881,7 +880,7 @@ void audit_log_task_context(struct audit } audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); return; error_path: @@ -933,7 +932,7 @@ static int audit_log_pid_context(struct if (!ab) return 1; - if (selinux_sid_to_string(sid, &s, &len)) { + if (security_secid_to_secctx(sid, &s, &len)) { audit_log_format(ab, "opid=%d obj=(none)", pid); rc = 1; } else @@ -1115,14 +1114,14 @@ static void audit_log_exit(struct audit_ if (axi->osid != 0) { char *ctx = NULL; u32 len; - if (selinux_sid_to_string( + if (security_secid_to_secctx( axi->osid, &ctx, &len)) { audit_log_format(ab, " osid=%u", axi->osid); call_panic = 1; } else audit_log_format(ab, " obj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); } break; } @@ -1231,13 +1230,13 @@ static void audit_log_exit(struct audit_ if (n->osid != 0) { char *ctx = NULL; u32 len; - if (selinux_sid_to_string( + if (security_secid_to_secctx( n->osid, &ctx, &len)) { audit_log_format(ab, " osid=%u", n->osid); call_panic = 2; } else audit_log_format(ab, " obj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); } audit_log_end(ab); @@ -1609,7 +1608,7 @@ static void audit_copy_inode(struct audi name->uid = inode->i_uid; name->gid = inode->i_gid; name->rdev = inode->i_rdev; - selinux_get_inode_sid(inode, &name->osid); + security_inode_getsecid(inode, &name->osid); } /** @@ -2033,7 +2032,7 @@ int __audit_ipc_obj(struct kern_ipc_perm ax->uid = ipcp->uid; ax->gid = ipcp->gid; ax->mode = ipcp->mode; - selinux_get_ipc_sid(ipcp, &ax->osid); + security_ipc_getsecid(ipcp, &ax->osid); ax->d.type = AUDIT_IPC; ax->d.next = context->aux; @@ -2193,7 +2192,7 @@ void __audit_ptrace(struct task_struct * struct audit_context *context = current->audit_context; context->target_pid = t->pid; - selinux_get_task_sid(t, &context->target_sid); + security_task_getsecid(t, &context->target_sid); } /** @@ -2220,7 +2219,7 @@ int __audit_signal_info(int sig, struct audit_sig_uid = ctx->loginuid; else audit_sig_uid = tsk->uid; - selinux_get_task_sid(tsk, &audit_sig_sid); + security_task_getsecid(tsk, &audit_sig_sid); } if (!audit_signals || audit_dummy_context()) return 0; @@ -2230,7 +2229,7 @@ int __audit_signal_info(int sig, struct * in audit_context */ if (!ctx->target_pid) { ctx->target_pid = t->tgid; - selinux_get_task_sid(t, &ctx->target_sid); + security_task_getsecid(t, &ctx->target_sid); return 0; } @@ -2247,7 +2246,7 @@ int __audit_signal_info(int sig, struct BUG_ON(axp->pid_count >= AUDIT_AUX_PIDS); axp->target_pid[axp->pid_count] = t->tgid; - selinux_get_task_sid(t, &axp->target_sid[axp->pid_count]); + security_task_getsecid(t, &axp->target_sid[axp->pid_count]); axp->pid_count++; return 0; @@ -2275,16 +2274,16 @@ void audit_core_dumps(long signr) audit_log_format(ab, "auid=%u uid=%u gid=%u", audit_get_loginuid(current->audit_context), current->uid, current->gid); - selinux_get_task_sid(current, &sid); + security_task_getsecid(current, &sid); if (sid) { char *ctx = NULL; u32 len; - if (selinux_sid_to_string(sid, &ctx, &len)) + if (security_secid_to_secctx(sid, &ctx, &len)) audit_log_format(ab, " ssid=%u", sid); else audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); } audit_log_format(ab, " pid=%d comm=", current->pid); audit_log_untrustedstring(ab, current->comm); diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/net/netlink/af_netlink.c linux-2.6.23-rc4-mm1-audit/net/netlink/af_netlink.c --- linux-2.6.23-rc4-mm1-base/net/netlink/af_netlink.c 2007-08-31 15:58:56.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/net/netlink/af_netlink.c 2007-09-02 16:48:47.000000000 -0700 @@ -54,7 +54,6 @@ #include #include #include -#include #include #include @@ -1190,7 +1189,7 @@ static int netlink_sendmsg(struct kiocb NETLINK_CB(skb).pid = nlk->pid; NETLINK_CB(skb).dst_group = dst_group; NETLINK_CB(skb).loginuid = audit_get_loginuid(current->audit_context); - selinux_get_task_sid(current, &(NETLINK_CB(skb).sid)); + security_task_getsecid(current, &(NETLINK_CB(skb).sid)); memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); /* What can I do? Netlink is asynchronous, so that diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/Kconfig linux-2.6.23-rc4-mm1-audit/security/Kconfig --- linux-2.6.23-rc4-mm1-base/security/Kconfig 2007-08-31 15:58:56.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/security/Kconfig 2007-09-02 22:22:34.000000000 -0700 @@ -104,6 +104,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.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/Makefile linux-2.6.23-rc4-mm1-audit/security/Makefile --- linux-2.6.23-rc4-mm1-base/security/Makefile 2007-07-08 16:32:17.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/security/Makefile 2007-09-02 22:22:34.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.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/security.c linux-2.6.23-rc4-mm1-audit/security/security.c --- linux-2.6.23-rc4-mm1-base/security/security.c 2007-08-31 15:58:56.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/security/security.c 2007-09-02 20:57:01.000000000 -0700 @@ -819,6 +819,50 @@ void security_release_secctx(char *secda } EXPORT_SYMBOL(security_release_secctx); +void security_inode_getsecid(const struct inode *inode, + u32 *secid) +{ + security_ops->inode_getsecid(inode, secid); + return; +} +EXPORT_SYMBOL(security_inode_getsecid); + +void security_ipc_getsecid(struct kern_ipc_perm *p, u32 *secid) +{ + security_ops->ipc_getsecid(p, secid); + return; +} +EXPORT_SYMBOL(security_ipc_getsecid); + +int security_audit_rule_supplied(struct audit_krule *rule) +{ + return security_ops->audit_rule_supplied(rule); +} +EXPORT_SYMBOL(security_audit_rule_supplied); + +int security_audit_rule_match(u32 sid, u32 field, u32 op, + void *rule, + struct audit_context *actx) +{ + return security_ops->audit_rule_match(sid, field, op, rule, actx); +} +EXPORT_SYMBOL(security_audit_rule_match); + +int security_audit_rule_init(u32 field, u32 op, char *rulestr, + void **rule) +{ + return security_ops->audit_rule_init(field, op, rulestr, rule); +} +EXPORT_SYMBOL(security_audit_rule_init); + +void security_audit_rule_free(void *rule) +{ + security_ops->audit_rule_free(rule); + return; +} +EXPORT_SYMBOL(security_audit_rule_free); + + #ifdef CONFIG_SECURITY_NETWORK int security_unix_stream_connect(struct socket *sock, struct socket *other, diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/selinux/exports.c linux-2.6.23-rc4-mm1-audit/security/selinux/exports.c --- linux-2.6.23-rc4-mm1-base/security/selinux/exports.c 2007-07-08 16:32:17.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/security/selinux/exports.c 2007-09-02 16:48:47.000000000 -0700 @@ -21,48 +21,6 @@ #include "security.h" #include "objsec.h" -int selinux_sid_to_string(u32 sid, char **ctx, u32 *ctxlen) -{ - if (selinux_enabled) - return security_sid_to_context(sid, ctx, ctxlen); - else { - *ctx = NULL; - *ctxlen = 0; - } - - return 0; -} - -void selinux_get_inode_sid(const struct inode *inode, u32 *sid) -{ - if (selinux_enabled) { - struct inode_security_struct *isec = inode->i_security; - *sid = isec->sid; - return; - } - *sid = 0; -} - -void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid) -{ - if (selinux_enabled) { - struct ipc_security_struct *isec = ipcp->security; - *sid = isec->sid; - return; - } - *sid = 0; -} - -void selinux_get_task_sid(struct task_struct *tsk, u32 *sid) -{ - if (selinux_enabled) { - struct task_security_struct *tsec = tsk->security; - *sid = tsec->sid; - return; - } - *sid = 0; -} - int selinux_string_to_sid(char *str, u32 *sid) { if (selinux_enabled) diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/selinux/hooks.c linux-2.6.23-rc4-mm1-audit/security/selinux/hooks.c --- linux-2.6.23-rc4-mm1-base/security/selinux/hooks.c 2007-08-31 15:58:56.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/security/selinux/hooks.c 2007-09-02 16:48:47.000000000 -0700 @@ -2726,6 +2726,13 @@ static int selinux_file_receive(struct f /* task security operations */ +static void selinux_get_task_sid(struct task_struct *tsk, u32 *sid) +{ + struct task_security_struct *tsec = tsk->security; + *sid = tsec->sid; + return; +} + static int selinux_task_create(unsigned long clone_flags) { int rc; @@ -3704,6 +3711,13 @@ out: return err; } +static void selinux_get_inode_sid(const struct inode *inode, u32 *sid) +{ + struct inode_security_struct *isec = inode->i_security; + *sid = isec->sid; + return; +} + static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid) { u32 peer_secid = SECSID_NULL; @@ -4036,6 +4050,13 @@ static int selinux_netlink_recv(struct s SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad); } +static void selinux_get_ipc_sid(const struct kern_ipc_perm *ipcp, u32 *sid) +{ + struct ipc_security_struct *isec = ipcp->security; + *sid = isec->sid; + return; +} + static int ipc_alloc_security(struct task_struct *task, struct kern_ipc_perm *perm, u16 sclass) @@ -4664,6 +4685,30 @@ static void selinux_release_secctx(char kfree(secdata); } +static int selinux_audit_rule_supplied(struct audit_krule *rule) +{ + int i; + + for (i = 0; i < rule->field_count; i++) { + struct audit_field *f = &rule->fields[i]; + switch (f->type) { + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + return 1; + } + } + + return 0; +} + #ifdef CONFIG_KEYS static int selinux_key_alloc(struct key *k, struct task_struct *tsk, @@ -4846,6 +4891,12 @@ static struct security_operations selinu .secid_to_secctx = selinux_secid_to_secctx, .release_secctx = selinux_release_secctx, + .inode_getsecid = selinux_get_inode_sid, + .ipc_getsecid = selinux_get_ipc_sid, + .audit_rule_supplied = selinux_audit_rule_supplied, + .audit_rule_match = selinux_audit_rule_match, + .audit_rule_init = selinux_audit_rule_init, + .audit_rule_free = selinux_audit_rule_free, .unix_stream_connect = selinux_socket_unix_stream_connect, .unix_may_send = selinux_socket_unix_may_send, diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/selinux/include/security.h linux-2.6.23-rc4-mm1-audit/security/selinux/include/security.h --- linux-2.6.23-rc4-mm1-base/security/selinux/include/security.h 2007-08-26 23:51:50.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/security/selinux/include/security.h 2007-09-02 16:48:47.000000000 -0700 @@ -129,5 +129,43 @@ static inline int security_netlbl_sid_to const char *security_get_initial_sid_context(u32 sid); +/** + * selinux_audit_rule_init - alloc/init an selinux audit rule structure. + * @field: the field this rule refers to + * @op: the operater the rule uses + * @rulestr: the text "target" of the rule + * @vpp: pointer to the new rule structure returned via this + * + * Returns 0 if successful, -errno if not. On success, the rule structure + * will be allocated internally. The caller must free this structure with + * selinux_audit_rule_free() after use. + */ +int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vpp); + +/** + * selinux_audit_rule_free - free an selinux audit rule structure. + * @vp: pointer to the audit rule to be freed + * + * This will free all memory associated with the given rule. + * If @rule is NULL, no operation is performed. + */ +void selinux_audit_rule_free(void *vp); + +struct audit_context; +/** + * selinux_audit_rule_match - determine if a context ID matches a rule. + * @sid: the context ID to check + * @field: the field this rule refers to + * @op: the operater the rule uses + * @vp: pointer to the audit rule to check against + * @actx: the audit context (can be NULL) associated with the check + * + * Returns 1 if the context id matches the rule, 0 if it does not, and + * -errno on failure. + */ +int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vp, + struct audit_context *actx); + + #endif /* _SELINUX_SECURITY_H_ */ diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/selinux/ss/services.c linux-2.6.23-rc4-mm1-audit/security/selinux/ss/services.c --- linux-2.6.23-rc4-mm1-base/security/selinux/ss/services.c 2007-08-26 23:51:50.000000000 -0700 +++ linux-2.6.23-rc4-mm1-audit/security/selinux/ss/services.c 2007-09-02 16:48:47.000000000 -0700 @@ -2107,17 +2107,19 @@ struct selinux_audit_rule { struct context au_ctxt; }; -void selinux_audit_rule_free(struct selinux_audit_rule *rule) +void selinux_audit_rule_free(void *vp) { + struct selinux_audit_rule *rule = (struct selinux_audit_rule*)vp; + if (rule) { context_destroy(&rule->au_ctxt); kfree(rule); } } -int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, - struct selinux_audit_rule **rule) +int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vpp) { + struct selinux_audit_rule **rule = (struct selinux_audit_rule**)vpp; struct selinux_audit_rule *tmprule; struct role_datum *roledatum; struct type_datum *typedatum; @@ -2208,10 +2210,10 @@ int selinux_audit_rule_init(u32 field, u return rc; } -int selinux_audit_rule_match(u32 sid, u32 field, u32 op, - struct selinux_audit_rule *rule, +int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vp, struct audit_context *actx) { + struct selinux_audit_rule *rule = (struct selinux_audit_rule*)vp; struct context *ctxt; struct mls_level *level; int match = 0; @@ -2320,7 +2322,7 @@ out: return match; } -static int (*aurule_callback)(void) = NULL; +static int (*aurule_callback)(void) = audit_rule_update_callout; static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid, u16 class, u32 perms, u32 *retained) @@ -2345,11 +2347,6 @@ static int __init aurule_init(void) } __initcall(aurule_init); -void selinux_audit_set_callback(int (*callback)(void)) -{ - aurule_callback = callback; -} - #ifdef CONFIG_NETLABEL /* * NetLabel cache structure diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/smack/Kconfig linux-2.6.23-rc4-mm1-audit/security/smack/Kconfig --- linux-2.6.23-rc4-mm1-base/security/smack/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23-rc4-mm1-audit/security/smack/Kconfig 2007-09-02 22:22:34.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 mandatory security schemes. + If you are unsure how to answer this question, answer N. + diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/smack/Makefile linux-2.6.23-rc4-mm1-audit/security/smack/Makefile --- linux-2.6.23-rc4-mm1-base/security/smack/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23-rc4-mm1-audit/security/smack/Makefile 2007-09-02 22:22:34.000000000 -0700 @@ -0,0 +1,9 @@ +# +# Makefile for the SMACK LSM +# + +obj-$(CONFIG_SECURITY_SMACK) := smack.o + +smack-y := smack_lsm.o smack_access.o smackfs.o + +EXTRA_CFLAGS += -Inet/netlabel diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/smack/smack_access.c linux-2.6.23-rc4-mm1-audit/security/smack/smack_access.c --- linux-2.6.23-rc4-mm1-base/security/smack/smack_access.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23-rc4-mm1-audit/security/smack/smack_access.c 2007-09-02 22:22:34.000000000 -0700 @@ -0,0 +1,310 @@ +/* + * 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" + +extern struct smk_list_entry *smack_list; + +/** + * smk_access - determine if a subject has a specific access to an object + * @subject_label: a pointer to the subject's Smack label + * @object_label: a pointer to the object's Smack label + * @request: the access requested, in "MAY" format + * + * This function looks up the subject/object pair in the + * access rule list and returns 0 if the access is permitted, + * non zero otherwise. + * + * Even though Smack labels are usually shared on smack_list + * labels that come in off the network can't be imported + * and added to the list for locking reasons. + * + * Therefore, it is necessary to check the contents of the labels, + * not just the pointer values. Of course, in most cases the labels + * will be on the list, so checking the pointers may be a worthwhile + * optimization. + */ +int smk_access(smack_t *subject_label, smack_t *object_label, int request) +{ + u32 may = MAY_NOT; + struct smk_list_entry *sp; + + /* + * Hardcoded comparisons. + * + * A star subject can't access any object. + */ + if (*subject_label == SMK_STAR) + return -EACCES; + /* + * A star object can be accessed by any subject. + */ + if (*object_label == SMK_STAR) + return 0; + /* + * An object can be accessed in any way by a subject + * with the same label. + */ + if (*subject_label == *object_label) + return 0; + /* + * A hat subject can read any object. + * A floor object can be read by any subject. + */ + if (*subject_label == SMK_HAT && (request & MAY_ANYREAD) == request) + return 0; + + if (*object_label == SMK_FLOOR && (request & MAY_ANYREAD) == request) + 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. + */ + for (sp = smack_list; sp != NULL; sp = sp->smk_next) { + if (*sp->smk_rule.smk_subject == *subject_label && + *sp->smk_rule.smk_object == *object_label) { + may = sp->smk_rule.smk_access; + break; + } + } + /* + * Yes, that is supposed to be a single ampersand. + */ + if ((request & may) == request) + return 0; + + return -EACCES; +} + +/** + * smk_curacc - determine if current has a specific access to an object + * @object_label: a pointer to the object's Smack label + * @request: the access requested, in "MAY" format + * + * This function checks the current subject label/object label pair + * in the access rule list and returns 0 if the access is permitted, + * non zero otherwise. It allows that current my have the capability + * to override the rules. + */ +int smk_curacc(smack_t *obj_label, u32 mode) +{ + int rc; + + rc = smk_access(current->security, obj_label, mode); + if (rc == 0) + return 0; + + if (capable(CAP_MAC_OVERRIDE)) + return 0; + + return rc; +} + +struct smack_known smack_known_unset = { + .smk_next = NULL, + .smk_known = SMK_UNSET, + .smk_secid = 1, + .smk_cipso = NULL, +}; + +struct smack_known smack_known_huh = { + .smk_next = &smack_known_unset, + .smk_known = SMK_HUH, + .smk_secid = 2, + .smk_cipso = NULL, +}; + +struct smack_known smack_known_hat = { + .smk_next = &smack_known_huh, + .smk_known = SMK_HAT, + .smk_secid = 3, + .smk_cipso = NULL, +}; + +struct smack_known smack_known_star = { + .smk_next = &smack_known_hat, + .smk_known = SMK_STAR, + .smk_secid = 4, + .smk_cipso = NULL, +}; + +struct smack_known smack_known_floor = { + .smk_next = &smack_known_star, + .smk_known = SMK_FLOOR, + .smk_secid = 5, + .smk_cipso = NULL, +}; + +struct smack_known smack_known_invalid = { + .smk_next = &smack_known_floor, + .smk_known = SMK_INVALID, + .smk_secid = 6, + .smk_cipso = NULL, +}; + +struct smack_known *smack_known = &smack_known_invalid; +/* + * The initial value needs to be bigger than any of the + * known values above. + */ +static u32 smack_next_secid = 10; + +extern struct mutex smack_known_lock; + +/** + * smk_import - determine if current has a specific access to an object + * @string: a text string that might be a Smack label + * @len: the maximum size, or zero if it is NULL terminated. + * + * Returns a pointer to the label in the label list that + * matches the passed string, adding it if necessary. If + * the string passed in is invalid return a pointer to the + * invalid label from the table. + */ +smack_t *smk_import(const char *string, int len) +{ + struct smack_known *skp; + smack_t smack; + char *to = (char *)&smack; + int found; + int i; + + if (len <= 0 || len > sizeof(smack_t)) + len = sizeof(smack_t); + + for (i = 0, found = 0; i < sizeof(smack_t); i++) { + if (found) + to[i] = '\0'; + else if (i >= len || string[i] > '~' || string[i] <= ' ') { + to[i] = '\0'; + found = 1; + } + else + to[i] = string[i]; + } + + if (to[0] == '\0') + return &smack_known_invalid.smk_known; + + mutex_lock(&smack_known_lock); + + for (skp = smack_known; skp != NULL; skp = skp->smk_next) + if (skp->smk_known == smack) + break; + + if (skp == NULL) { + skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL); + if (skp == NULL) + skp = &smack_known_invalid; + else { + skp->smk_next = smack_known; + skp->smk_known = smack; + skp->smk_secid = smack_next_secid++; + skp->smk_cipso = NULL; + spin_lock_init(&skp->smk_cipsolock); + smack_known = skp; + } + } + + mutex_unlock(&smack_known_lock); + + return &skp->smk_known; +} + +/** + * smack_from_secid - find the Smack label associated with a secid + * @secid: an integer that might be associated with a Smack label + * + * Returns a pointer to the appropraite Smack label if there is one, + * otherwise a pointer to the invalid Smack label. + */ +smack_t *smack_from_secid(const u32 secid) +{ + struct smack_known *skp; + + for (skp = smack_known; skp != NULL; skp = skp->smk_next) + if (skp->smk_secid == secid) + return &skp->smk_known; + + /* + * If we got this far someone asked for the translation + * of a secid that is not on the list. + */ + return &smack_known_invalid.smk_known; +} + +/** + * smack_from_cipso - find the Smack label associated with a CIPSO option + * @level: Bell & LaPadula level from the network + * @catset: Bell & LaPadula categories from the network + * @result: where to put the Smack value + * + * This is a simple lookup in the label table. + * + * This is an odd duck as far as smack handling goes in that + * it sends back a copy of the smack label rather than a pointer + * to the master list. This is done because it is possible for + * a foreign host to send a smack label that is new to this + * machine and hence not on the list. That would not be an + * issue except that adding an entry to the master list can't + * be done at that point. + */ +void smack_from_cipso(u32 level, smack_t *catset, smack_t *result) +{ + struct smack_known *kp; + smack_t *final = NULL; + + for (kp = smack_known; final == NULL && kp != NULL; kp = kp->smk_next) { + if (kp->smk_cipso == NULL) + continue; + + spin_lock_bh(&kp->smk_cipsolock); + + if (kp->smk_cipso->smk_level == level && + kp->smk_cipso->smk_catset == *catset) + final = &kp->smk_known; + + spin_unlock_bh(&kp->smk_cipsolock); + } + if (final == NULL) + *result = smack_known_huh.smk_known; + else + *result = *final; + return; +} + +/** + * smack_to_cipso - find the CIPSO option to go with a Smack label + * @smack: a pointer to the smack label in question + * @cp: where to put the result + * + * Returns zero if a value is available, non-zero otherwise. + */ +int smack_to_cipso(const smack_t *smack, struct smack_cipso *cp) +{ + struct smack_known *kp; + + for (kp = smack_known; kp != NULL; kp = kp->smk_next) + if (kp->smk_known == *smack) + break; + + if (kp == NULL || kp->smk_cipso == NULL) + return -ENOENT; + + *cp = *kp->smk_cipso; + return 0; +} diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/smack/smackfs.c linux-2.6.23-rc4-mm1-audit/security/smack/smackfs.c --- linux-2.6.23-rc4-mm1-base/security/smack/smackfs.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23-rc4-mm1-audit/security/smack/smackfs.c 2007-09-02 22:22:34.000000000 -0700 @@ -0,0 +1,1209 @@ +/* + * 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. + * + * Karl MacMillan + * James Morris + * + */ + +#include +#include +#include +#include +#include +/* + * This include could get changed when the internal + * netlabel interface for initializing a netlabel domain + * has been worked out. + */ +#include "netlabel_domainhash.h" +#include +#include "smack.h" + +extern struct mutex smack_list_lock; +extern struct smack_known smack_known_floor; +extern struct smack_known smack_known_star; +extern struct smack_known *smack_known; + +/* + * smackfs pseudo filesystem. + */ + +enum smk_inos { + SMK_ROOT_INO = 2, + SMK_LOAD = 3, /* load policy */ + SMK_LINKS = 4, /* symlinks */ + SMK_CIPSO = 5, /* load label -> CIPSO mapping */ + SMK_DOI = 6, /* CIPSO DOI */ + SMK_DIRECT = 7, /* CIPSO level indicating direct label */ + SMK_AMBIENT = 8, /* internet ambient label */ + SMK_NLTYPE = 9, /* label scheme to use by default */ + SMK_TMP = 100, /* MUST BE LAST! /smack/tmp */ +}; + +/* + * 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 = &smack_known_floor.smk_known; + +/* + * This is the default packet marking scheme for network traffic. + * It can be reset via smackfs/nltype + */ +int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4; + +/* + * This is the level in a CIPSO header that indicates a + * smack label is contained directly in the category set. + * It can be reset via smackfs/direct + */ +int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT; + +static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; +static int smack_list_count; +struct smk_list_entry *smack_list; + +/* + * 'ssssssss oooooooo mmmm\n\0' + */ +#define SMACK_RULE_LINE_SIZE (2 * (sizeof(smack_t) + 1) + 6) + +/** + * smk_read_load - read() for /smack/load + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +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 = SMACK_RULE_LINE_SIZE * 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; +} + +/** + * smk_set_access - add a rule to the rule list + * @srp: the new rule to add + * + * Looks through the current subject/object/access list for + * the subject/object pair and replaces the access that was + * there. If the pair isn't found add it with the specified + * access. + */ +static void smk_set_access(struct smack_rule *srp) +{ + struct smk_list_entry *sp; + struct smk_list_entry *newp; + + mutex_lock(&smack_list_lock); + + for (sp = smack_list; 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; + break; + } + + if (sp == NULL) { + newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL); + newp->smk_rule = *srp; + newp->smk_next = smack_list; + smack_list = newp; + smack_list_count++; + } + + mutex_unlock(&smack_list_lock); + + return; +} + + +/** + * smk_write_load - write() for /smack/load + * @filp: 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_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 subjectstr[sizeof(smack_t)]; + char objectstr[sizeof(smack_t)]; + char modestr[8]; + char *cp; + + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + /* + * No partial writes. + */ + if (*ppos != 0) + return -EINVAL; + + /* + * 80 characters per line ought to be enough. + */ + if (count > SMACK_LIST_MAX * 80) + return -ENOMEM; + + data = kzalloc(count + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) { + kfree(data); + return -EFAULT; + } + + *(data + count) = '\0'; + + for (cp = data - 1; cp != NULL; cp = strchr(cp + 1, '\n')) { + if (*++cp == '\0') + break; + if (sscanf(cp, "%7s %7s %7s\n", subjectstr, objectstr, + modestr) != 3) { + printk("%s:%d bad scan\n", __func__, __LINE__); + break; + } + rule.smk_subject = smk_import(subjectstr, 0); + if (*rule.smk_subject == SMK_INVALID) + break; + rule.smk_object = smk_import(objectstr, 0); + if (*rule.smk_object == SMK_INVALID) + break; + rule.smk_access = 0; + if (strpbrk(modestr, "rR") != NULL) + rule.smk_access |= MAY_READ; + if (strpbrk(modestr, "wW") != NULL) + rule.smk_access |= MAY_WRITE; + if (strpbrk(modestr, "xX") != NULL) + rule.smk_access |= MAY_EXEC; + if (strpbrk(modestr, "aA") != NULL) + rule.smk_access |= MAY_APPEND; + smk_set_access(&rule); + printk("%s:%d rule %s %s 0x%x\n", __func__, __LINE__, + (char *)rule.smk_subject, (char *)rule.smk_object, + rule.smk_access); + } + + kfree(data); + return rc; +} + +static const struct file_operations smk_load_ops = { + .read = smk_read_load, + .write = smk_write_load, +}; + +/** + * smk_digit - return a pointer to the next digit + * @cp: where to start + * + * Returns a pointer to the next digit in the string, or NULL + */ +static inline char *smk_digit(const char *cp) +{ + return strpbrk(cp, "0123456789"); +} + +static unsigned int smk_cipso_doied; +static unsigned int smk_cipso_written; + +/** + * smk_cipso_doi - initialize the CIPSO domain + * + * This code reaches too deeply into netlabel internals + * for comfort, however there is no netlabel KAPI that + * allows for kernel based initialization of a CIPSO DOI. + * Until Paul and Casey can work out an appropriate + * interface Smack will do it this way. + */ +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 = smk_cipso_doi_value; + 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); + if (rc != 0) + printk("%s:%d add doi rc = %d\n", __func__, __LINE__, rc); + + ndmp->domain = NULL; + ndmp->type = NETLBL_NLTYPE_CIPSOV4; + ndmp->type_def.cipsov4 = doip; + + rc = netlbl_domhsh_remove_default(&audit_info); + if (rc != 0) + printk("%s:%d remove rc = %d\n", __func__, __LINE__, rc); + + rc = netlbl_domhsh_add_default(ndmp, &audit_info); + if (rc != 0) + printk("%s:%d add rc = %d\n", __func__, __LINE__, rc); +} + +/** + * smk_read_cipso - read() for /smack/cipso + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + * + * label level[/cat[,cat]] + */ +static ssize_t smk_read_cipso(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct smack_known *skp; + struct smack_cipso *scp; + ssize_t bytes; + char sep; + char *result; + char *cp; + char *cbp; + int realbytes = 0; + int cat = -1; + int i; + unsigned char m; + + smk_cipso_doi(); + + if (smk_cipso_written == 0) + return 0; + + result = kzalloc(smk_cipso_written, GFP_KERNEL); + if (result == NULL) + return -ENOMEM; + cp = result; + + for (skp = smack_known; skp != NULL; skp = skp->smk_next) { + if (skp->smk_cipso == NULL) + continue; + scp = skp->smk_cipso; + cp += sprintf(cp, "%-8s %3d", + (char *)&skp->smk_known, scp->smk_level); + cat = 1; + sep = '/'; + cbp = (char *)&scp->smk_catset; + for (i = 0; i < sizeof(scp->smk_catset); i++) { + for (m = 0x80; m != 0; m >>= 1) { + if ((m & cbp[i]) != 0) { + cp += sprintf(cp, "%c%d", sep, cat); + sep = ','; + } + cat++; + } + } + *cp++ = '\n'; + } + *cp++ = '\0'; + realbytes = strlen(result); + + bytes = simple_read_from_buffer(buf, count, ppos, result, realbytes); + + kfree(result); + + return bytes; +} + +/** + * smk_write_cipso - write() for /smack/cipso + * @filp: 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_cipso(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct smack_known *skp; + struct smack_cipso *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; + /* + * 80 characters per line ought to be enough. + */ + if (count > SMACK_LIST_MAX * 80) + return -ENOMEM; + + data = kzalloc(count + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) { + kfree(data); + return -EFAULT; + } + + *(data + count) = '\0'; + smk_cipso_written += count; + + for (eolp = strchr(data, '\n'), linep = data; + eolp != NULL && rc >= 0; + linep = eolp + 1, eolp = strchr(linep, '\n')) { + + if (eolp == linep) + continue; + *eolp = '\0'; + + mapcatset = 0; + mapsmack = smk_import(linep, 0); + if (*mapsmack == SMK_INVALID) + continue; + skp = container_of(mapsmack, struct smack_known, smk_known); + + 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; + if (cat > SMACK_CIPSO_MAXCAT) { + i = 0; + break; + } + smack_catset_bit(cat, &mapcatset); + + cp = strchr(cp, ','); + if (cp != NULL) + cp = smk_digit(cp); + } while (cp != NULL); + } + + if (i != 1) + continue; + + + if (skp->smk_cipso == NULL) { + scp = kzalloc(sizeof(struct smack_cipso), GFP_KERNEL); + if (scp == NULL) { + rc = -ENOMEM; + break; + } + } else + scp = NULL; + + spin_lock_bh(&skp->smk_cipsolock); + if (skp->smk_cipso == NULL) { + skp->smk_cipso = scp; + scp = NULL; + } + skp->smk_cipso->smk_level = maplevel; + skp->smk_cipso->smk_catset = mapcatset; + spin_unlock_bh(&skp->smk_cipsolock); + + /* + * The only way this could be true is for there + * to have been two attempts to update the cipso + * list at the same time. One of the two will have + * won cleanly, but there remains cleanup to do. + */ + if (scp != NULL) { + printk(KERN_WARNING "%s: CIPSO collision for \"%s\"\n", + __func__, (char *)&skp->smk_known); + kfree(scp); + } + /* + * Add this to ensure that there are + * enough bytes for the regurgitation + */ + smk_cipso_written += sizeof(smack_t); + } + + kfree(data); + return rc; +} + +static const struct file_operations smk_cipso_ops = { + .read = smk_read_cipso, + .write = smk_write_cipso, +}; + +/** + * smk_read_doi - read() for /smack/doi + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_doi(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char temp[80]; + ssize_t rc; + + if (*ppos != 0) + return 0; + + sprintf(temp, "%d", smk_cipso_doi_value); + rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); + + return rc; +} + +/** + * smk_write_doi - write() for /smack/doi + * @filp: 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_doi(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char temp[80]; + int i; + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + + if (count > sizeof(temp)) + return -EINVAL; + + if (copy_from_user(temp, buf, count) != 0) + return -EFAULT; + + if (sscanf(temp, "%d", &i) != 1) + return -EINVAL; + + smk_cipso_doi_value = i; + + return count; +} + +static const struct file_operations smk_doi_ops = { + .read = smk_read_doi, + .write = smk_write_doi, +}; + +/** + * smk_read_direct - read() for /smack/direct + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_direct(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char temp[80]; + ssize_t rc; + + if (*ppos != 0) + return 0; + + sprintf(temp, "%d", smack_cipso_direct); + rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); + + return rc; +} + +/** + * smk_write_direct - write() for /smack/direct + * @filp: 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_direct(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char temp[80]; + int i; + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + + if (count > sizeof(temp)) + return -EINVAL; + + if (copy_from_user(temp, buf, count) != 0) + return -EFAULT; + + if (sscanf(temp, "%d", &i) != 1) + return -EINVAL; + + smack_cipso_direct = i; + + return count; +} + +static const struct file_operations smk_direct_ops = { + .read = smk_read_direct, + .write = smk_write_direct, +}; + +/** + * smk_read_ambient - read() for /smack/ambient + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +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, + strlen((char *)smack_net_ambient) + 1); + + return rc; +} + +/** + * smk_write_ambient - write() for /smack/ambient + * @filp: 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_ambient(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char in[sizeof(smack_t)]; + smack_t *smack; + + if (!capable(CAP_MAC_OVERRIDE)) + return -EPERM; + + if (count >= sizeof(smack_t)) + return -EINVAL; + + if (copy_from_user(in, buf, count) != 0) + return -EFAULT; + + smack = smk_import(in, count); + if (*smack == SMK_INVALID) + return -EINVAL; + /* + * Better check to be sure this is OK. + */ + smack_net_ambient = smack; + + return count; +} + +static const struct file_operations smk_ambient_ops = { + .read = smk_read_ambient, + .write = smk_write_ambient, +}; + +struct option_names { + int o_number; + char *o_name; + char *o_alias; +}; + +static struct option_names netlbl_choices[] = { + { 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" }, +}; + +/** + * smk_read_nltype - read() for /smack/nltype + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +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 < ARRAY_SIZE(netlbl_choices); 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; +} + +/** + * smk_write_nltype - write() for /smack/nltype + * @filp: 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_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 < ARRAY_SIZE(netlbl_choices); 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 const 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; + +/** + * smackfs_follow_link - follow a smackfs symlink + * @dentry: name cache entry + * @nd: name entry + * + * A symlink on smackfs has unusual semantics. + * + * The Smack value of the task is appended to the link string. + * Thus, if a task labeled "Gentoo" does chdir("/smack/tmp") + * it will use "/moldy/Gentoo". + * + * The expected usage is the /tmp is a symlink to /smack/tmp + * which is itself a symlink to /moldy. /moldy should have a + * directory for each label in use to accomodate the value + * appended on the redirection. + * + * An interesting addition would be a file system that automatically + * creates directories as needed, at the appropriate label. + */ +static void *smackfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + smack_t *sp = current->security; + 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", __func__, __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); + /* + * Unlike the readlink below, hang on to the memory allocated + * because nd_set_link passes it along. + */ + return NULL; +} + +/** + * smackfs_readlink - read a smackfs symlink + * @dentry: name cache entry + * @buffer: where the result goes + * @buflen: buffer size + * + * Returns 0 on success, an error code otherwise + * + * Supports the same semantics as the follow code. + */ +static int smackfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) +{ + smack_t *csp = current->security; + 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", __func__, __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; +} + +/** + * smackfs_put_link - free a followed component + * @dentry: unused + * @nd: name entry + * @ptr: unused + * + * free the buffer used in following the link. + */ +static void smackfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr) +{ + kfree(nd_get_link(nd)); +} + +static struct inode_operations smackfs_symlink_inode_operations = { + .readlink = smackfs_readlink, + .follow_link = smackfs_follow_link, + .put_link = smackfs_put_link, +}; + +/** + * smk_add_symlink - follow a smackfs symlink + * @name: the name of the synlink + * @target: where it points to + * + * Add a smackfs symlink + */ +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", __func__, __LINE__); + return; + } + + inode = new_inode(smk_sb); + if (inode == NULL) { + printk("%s:%d link inode failed\n", __func__, __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; +} + +/** + * smk_read_links - read() for /smack/links + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +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) + cp += sprintf(cp, "%s %s\n", slp->sl_name, slp->sl_target); + + bytes = simple_read_from_buffer(buf,count,ppos,result,strlen(result)); + + kfree(result); + + return bytes; +} + +/** + * smk_write_links - write() for /smack/links + * @filp: 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 + * + * This might be better done using "real" symlink creation. + */ +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; + /* + * 80 characters per line ought to be enough. + */ + if (count > SMACK_LIST_MAX * 80) + return -ENOMEM; + + data = kzalloc(count + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) { + kfree(data); + return -EFAULT; + } + + data[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", + __func__, __LINE__); + break; + } + smk_add_symlink(name, target); + printk("%s:%d add %s -> %s\n", + __func__, __LINE__, name, target); + } + + kfree(data); + return rc; +} + +static const struct file_operations smk_links_ops = { + .read = smk_read_links, + .write = smk_write_links, +}; + +/** + * smk_fill_super - fill the /smackfs superblock + * @sb: the empty superblock + * @data: unused + * @silent: unused + * + * Fill in the well known entries for /smack and set up for + * symlinks + * + * Returns 0 on success, an error code on failure + */ +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_LINKS] = {"links", &smk_links_ops, S_IRUGO|S_IWUSR}, + [SMK_CIPSO] = {"cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR}, + [SMK_DOI] = {"doi", &smk_doi_ops, S_IRUGO|S_IWUSR}, + [SMK_DIRECT] = {"direct", &smk_direct_ops, S_IRUGO|S_IWUSR}, + [SMK_AMBIENT] = {"ambient", &smk_ambient_ops,S_IRUGO|S_IWUSR}, + [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", + __func__, rc); + return rc; + } + + root_inode = sb->s_root->d_inode; + root_inode->i_security = new_inode_smack(&smack_known_floor.smk_known); + + /* + * Create a directory for /smack/tmp + */ + smk_add_symlink("tmp", SMK_TMPPATH_ROOT); + + return 0; +} + +/** + * smk_get_sb - get the smackfs superblock + * @fs_type: passed along without comment + * @flags: passed along without comment + * @dev_name: passed along without comment + * @data: passed along without comment + * @mnt: passed along without comment + * + * Just passes everything along. + * + * Returns what the lower level code does. + */ +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; + +/** + * init_smk_fs - get the smackfs superblock + * + * register the smackfs + * + * Returns 0 unless the registration fails. + */ +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.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/smack/smack.h linux-2.6.23-rc4-mm1-audit/security/smack/smack.h --- linux-2.6.23-rc4-mm1-base/security/smack/smack.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23-rc4-mm1-audit/security/smack/smack.h 2007-09-02 22:22:34.000000000 -0700 @@ -0,0 +1,232 @@ +/* + * 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 +#include + +/* + * A smack_t contains an 8 byte string, 7 characters + * and a terminating NULL. Yes, it's short and 12.5% + * wasted. + * + * There is likely to be experimentation with alternative + * representations, perhaps a u32 so that it's the same + * as what's used in Other LSM MAC schemes, or an array + * of 20 chars so that the names aren't so tightly constrained. + */ +typedef u64 smack_t; + +struct superblock_smack { + smack_t *smk_root; + smack_t *smk_floor; + smack_t *smk_hat; + smack_t *smk_default; + int smk_initialized; +}; + +/* + * Socket smack data + */ +struct socket_smack { + 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 */ +}; + +/* + * Inode smack data + */ +struct inode_smack { + smack_t *smk_inode; /* label of the fso */ + struct mutex smk_lock; /* initialization lock */ + int smk_flags; /* smack inode flags */ +}; + +#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */ + +/* + * A label access rule. + */ +struct smack_rule { + smack_t *smk_subject; + smack_t *smk_object; + int smk_access; +}; + +/* + * An entry in the table of permitted label accesses. + */ +struct smk_list_entry { + struct smk_list_entry *smk_next; + struct smack_rule smk_rule; +}; + +/* + * An entry in the table mapping smack values to + * CIPSO level/category-set values. + */ +struct smack_cipso { + int smk_level; + smack_t smk_catset; +}; + +/* + * This is the repository for labels seen so that it is + * not necessary to keep allocating tiny chuncks of memory + * and so that they can be shared. + * + * Labels are never modified in place. Anytime a label + * is imported (e.g. xattrset on a file) the list is checked + * for it and it is added if it doesn't exist. The address + * is passed out in either case. Entries are added, but + * never deleted. + * + * Since labels are hanging around anyway it doesn't + * hurt to maintain a secid for those awkward situations + * where kernel components that ought to use LSM independent + * interfaces don't. The secid should go away when all of + * these components have been repaired. + * + * If there is a cipso value associated with the label it + * gets stored here, too. This will most likely be rare as + * the cipso direct mapping in used internally. + */ +struct smack_known { + struct smack_known *smk_next; + smack_t smk_known; + u32 smk_secid; + struct smack_cipso *smk_cipso; + spinlock_t smk_cipsolock; +}; + +/* + * Mount options + */ +#define SMK_FSDEFAULT "smackfsdef=" +#define SMK_FSFLOOR "smackfsfloor=" +#define SMK_FSHAT "smackfshat=" +#define SMK_FSROOT "smackfsroot=" + +/* + * xattr names + */ +#define XATTR_SMACK_SUFFIX "SMACK64" +#define XATTR_SMACK_IPIN "SMACK64IPIN" +#define XATTR_SMACK_IPOUT "SMACK64IPOUT" +#define XATTR_SMACK_PACKET "SMACK64PACKET" +#define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX + +/* + * smackfs macic number + */ +#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ + +/* + * A limit on the number of entries in the lists + * makes some of the list administration easier. + */ +#define SMACK_LIST_MAX 10000 + +/* + * CIPSO defaults. + */ +#define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */ +#define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */ +#define SMACK_CIPSO_MAXCAT 63 /* Bigger gets harder */ + +/* + * 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_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 + +/* + * These functions are in smack_lsm.c + */ +struct inode_smack *new_inode_smack(smack_t *); + +/* + * These functions are in smack_access.c + */ +int smk_access(smack_t *, smack_t *, int); +int smk_curacc(smack_t *, u32); +int smack_to_cipso(const smack_t *, struct smack_cipso *); +void smack_from_cipso(u32, smack_t *, smack_t *); +smack_t *smack_from_secid(const u32); +smack_t *smk_import(const char *, int); + +/* + * Stricly for CIPSO level manipulation. + * Set the category bit number in a smack_t. + */ +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); +} + +/* + * Present a pointer to the smack label in an inode blob. + */ +static inline smack_t *smk_of_inode(const struct inode *isp) +{ + struct inode_smack *sip = isp->i_security; + return sip->smk_inode; +} + +/* + * Present the secid associated with a smack value. + */ +static inline u32 smack_to_secid(const smack_t *smack) +{ + struct smack_known *kp; + + kp = container_of(smack, struct smack_known, smk_known); + return kp->smk_secid; +} + +#endif /* _SECURITY_SMACK_H */ diff -uprN -X linux-2.6.23-rc4-mm1-base/Documentation/dontdiff linux-2.6.23-rc4-mm1-base/security/smack/smack_lsm.c linux-2.6.23-rc4-mm1-audit/security/smack/smack_lsm.c --- linux-2.6.23-rc4-mm1-base/security/smack/smack_lsm.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23-rc4-mm1-audit/security/smack/smack_lsm.c 2007-09-02 22:22:34.000000000 -0700 @@ -0,0 +1,2630 @@ +/* + * 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 +#include + +#include "smack.h" + +/* + * 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 +#define TMPFS_MAGIC 0x01021994 + + +/* + * These are in smack_access.c + */ +extern struct smack_known smack_known_unset; +extern struct smack_known smack_known_huh; +extern struct smack_known smack_known_hat; +extern struct smack_known smack_known_star; +extern struct smack_known smack_known_floor; +extern struct smack_known smack_known_invalid; + +/* + * These are maintained in smackfs.c + */ +extern smack_t *smack_net_ambient; +extern int smack_net_nltype; +extern int smack_cipso_direct; + +/** + * smk_fetch - Fetch the smack label from a file. + * @ip: a pointer to the inode + * @dp: a pointer to the dentry + * + * Returns a pointer to the master list entry for the Smack label + * or NULL if there was no label to fetch. + */ +static smack_t *smk_fetch(struct inode *ip, struct dentry *dp) +{ + int rc; + char in[sizeof(smack_t)]; + smack_t *result; + + if (ip->i_op->getxattr == NULL) + return NULL; + + rc = ip->i_op->getxattr(dp, XATTR_NAME_SMACK, in, sizeof(smack_t)); + if (rc < 0) + return NULL; + + result = smk_import(in, rc); + if (*result == SMK_INVALID) + return NULL; + + return result; +} + +/** + * new_inode_smack - allocate an inode security blob + * @smack: a pointer to the Smack label to use in the blob + * + * Returns the new blob or NULL if there's no memory available + */ +struct inode_smack *new_inode_smack(smack_t *smack) +{ + struct inode_smack *isp; + + isp = kzalloc(sizeof(struct inode_smack), GFP_KERNEL); + if (isp == NULL) + return NULL; + + isp->smk_inode = smack; + isp->smk_flags = 0; + mutex_init(&isp->smk_lock); + + return isp; +} + +/* + * LSM hooks. + * We he, that is fun! + */ + +/** + * smack_ptrace - Smack approval on ptrace + * @ptp: parent task pointer + * @ctp: child task pointer + * + * Returns 0 if access is OK, an error code otherwise + * + * Do the capability checks, and require read and write. + */ +static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp) +{ + int rc; + + rc = cap_ptrace(ptp, ctp); + if (rc != 0) + return rc; + + rc = smk_access(ptp->security, ctp->security, MAY_READWRITE); + if (rc != 0 && __capable(ptp, CAP_MAC_OVERRIDE)) + return 0; + + return rc; +} + +/** + * 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 type) +{ + int rc; + smack_t *sp = current->security; + + rc = cap_syslog(type); + if (rc != 0) + return rc; + + if (__capable(current, CAP_MAC_OVERRIDE)) + return 0; + + if (*sp != SMK_FLOOR) + rc = -EACCES; + + return rc; +} + +/** + * smack_task_alloc_security - "allocate" a task blob + * @tsk: the task in need of a blob + * + * Smack isn't using copies of blobs. Everyone + * points to an immutible list. No alloc required. + * No data copy required. + * + * Always returns 0 + */ +static int smack_task_alloc_security(struct task_struct *tsk) +{ + tsk->security = current->security; + + return 0; +} + +/** + * smack_task_free_security - "free" a task blob + * @task: the task with the blob + * + * Smack isn't using copies of blobs. Everyone + * points to an immutible list. The blobs never go away. + * There is no leak here. + */ +static void smack_task_free_security(struct task_struct *task) +{ + task->security = NULL; +} + +/** + * smack_task_setpgid - Smack check on setting pgid + * @p: the task object + * @pgid: unused + * + * Return 0 if write access is permitted + */ +static int smack_task_setpgid(struct task_struct *p, pid_t pgid) +{ + return smk_curacc(p->security, MAY_WRITE); +} + +/** + * smack_task_setnice - Smack check on setting nice + * @p: the task object + * @nice: unused + * + * Return 0 if write access is permitted + */ +static int smack_task_setnice(struct task_struct *p, int nice) +{ + return smk_curacc(p->security, MAY_WRITE); +} + +/** + * smack_task_setioprio - Smack check on setting ioprio + * @p: the task object + * @ioprio: unused + * + * Return 0 if write access is permitted + */ +static int smack_task_setioprio(struct task_struct *p, int ioprio) +{ + return smk_curacc(p->security, MAY_WRITE); +} + +/** + * smack_task_getioprio - Smack check on reading ioprio + * @p: the task object + * + * Return 0 if read access is permitted + */ +static int smack_task_getioprio(struct task_struct *p) +{ + return smk_curacc(p->security, MAY_READ); +} + +/** + * smack_task_setscheduler - Smack check on setting scheduler + * @p: the task object + * @policy: unused + * @lp: unused + * + * Return 0 if read access is permitted + */ +static int smack_task_setscheduler(struct task_struct *p, int policy, + struct sched_param *lp) +{ + return smk_curacc(p->security, MAY_WRITE); +} + +/** + * smack_task_getscheduler - Smack check on reading scheduler + * @p: the task object + * + * Return 0 if read access is permitted + */ +static int smack_task_getscheduler(struct task_struct *p) +{ + return smk_curacc(p->security, MAY_READ); +} + +/** + * smack_task_movememory - Smack check on moving memory + * @p: the task object + * + * Return 0 if write access is permitted + */ +static int smack_task_movememory(struct task_struct *p) +{ + return smk_curacc(p->security, MAY_WRITE); +} + +/** + * smack_task_kill - Samck check on signal delivery + * @p: the task object + * @info: unused + * @sig: unused + * @secid: identifies the smack to use in lieu of current's + * + * Return 0 if write access is permitted + * + * The secid behavior is an artifact of an SELinux hack + * in the USB code. Someday it may go away. + */ +static int smack_task_kill(struct task_struct *p, struct siginfo *info, + int sig, u32 secid) +{ + /* + * Sending a signal requires that the sender + * can write the receiver. + */ + if (secid == 0) + return smk_curacc(p->security, MAY_WRITE); + /* + * If the secid isn't 0 we're dealing with some USB IO + * specific behavior. This is not clean. For one thing + * we can't take privilege into account. + */ + return smk_access(smack_from_secid(secid), p->security, MAY_WRITE); +} + +/* + * Superblock Hooks. + */ + +/** + * smack_sb_alloc_security - allocate a superblock blob + * @sb: the superblock getting the blob + * + * Returns 0 on success or -ENOMEM on error. + */ +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 = &smack_known_floor.smk_known; + sbsp->smk_default = &smack_known_floor.smk_known; + sbsp->smk_floor = &smack_known_floor.smk_known; + sbsp->smk_hat = &smack_known_hat.smk_known; + sbsp->smk_initialized = 0; + + sb->s_security = sbsp; + + return 0; +} + +/** + * smack_sb_free_security - free a superblock blob + * @sb: the superblock getting the blob + * + */ +static void smack_sb_free_security(struct super_block *sb) +{ + kfree(sb->s_security); + sb->s_security = NULL; +} + +/** + * smack_sb_copy_data - copy mount options data for processing + * @type: file system type + * @orig: where to start + * @smackopts + * + * Returns 0 on success or -ENOMEM on error. + * + * Copy the Smack specific mount options out of the mount + * options list. + */ +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; +} + +/** + * smack_sb_kern_mount - Smack specific mount processing + * @sb: the file system superblock + * @data: the smack mount options + * + * Returns 0 on success, an error code on failure + */ +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; + struct inode_smack *isp; + 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; + + 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_import(op, 0); + } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) { + op += strlen(SMK_FSFLOOR); + if (strlen(op) <= SMK_MAXLEN) + sp->smk_floor = smk_import(op, 0); + } else if (strncmp(op,SMK_FSDEFAULT,strlen(SMK_FSDEFAULT))==0) { + op += strlen(SMK_FSDEFAULT); + if (strlen(op) <= SMK_MAXLEN) + sp->smk_default = smk_import(op, 0); + } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) { + op += strlen(SMK_FSROOT); + if (strlen(op) <= SMK_MAXLEN) + sp->smk_root = smk_import(op, 0); + } + } + + /* + * Initialize the root inode. + */ + isp = inode->i_security; + if (isp == NULL) + inode->i_security = new_inode_smack(sp->smk_root); + else + isp->smk_inode = sp->smk_root; + + return 0; +} + +/** + * smack_sb_statfs - Smack check on statfs + * @dentry: identifies the file system in question + * + * Returns 0 if current can read the floor of the filesystem, + * and error code otherwise + */ +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); +} + +/** + * smack_sb_mount - Smack check for mounting + * @dev_name: unused + * @nd: 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(char *dev_name, struct nameidata *nd, + char *type, unsigned long flags, void *data) +{ + struct superblock_smack *sbp; + + 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; + + return smk_curacc(sbp->smk_floor, MAY_WRITE); +} + +/** + * 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; + + sbp = mnt->mnt_sb->s_security; + + return smk_curacc(sbp->smk_floor, MAY_WRITE); +} + +/* + * Inode hooks + */ + +/** + * smack_inode_alloc_security - allocate an inode blob + * @inode - the inode in need of a blob + * + * Returns 0 if it gets a blob, -ENOMEM otherwise + */ +static int smack_inode_alloc_security(struct inode *inode) +{ + inode->i_security = new_inode_smack(current->security); + if (inode->i_security == NULL) + return -ENOMEM; + return 0; +} + +/** + * smack_inode_free_security - free an inode blob + * @inode - the inode with a blob + * + * Clears the blob pointer in inode + */ +static void smack_inode_free_security(struct inode *inode) +{ + kfree(inode->i_security); + inode->i_security = NULL; +} + +/** + * smack_inode_init_security - copy out the smack from an inode + * @inode: the inode + * @dir: unused + * @name: where to put the attribute name + * @value: where to put the attribute value + * @len: where to put the length of the attribute + * + * Returns 0 if it all works out, -ENOMEM if there's no memory + */ +static int smack_inode_init_security(struct inode *inode, struct inode *dir, + char **name, void **value, size_t *len) +{ + smack_t *isp = smk_of_inode(inode); + + if (name) { + *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL); + if (*name == NULL) + return -ENOMEM; + } + + if (value) { + *value = kstrdup((char *)isp, GFP_KERNEL); + if (*value == NULL) + return -ENOMEM; + } + + if (len) + *len = strlen((char *)isp) + 1; + + return 0; +} + +/** + * smack_inode_link - Smack check on link + * @old_dentry: unused + * @dir: the directory with the entry to change + * @new_dentry: unused + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + return smk_curacc(smk_of_inode(dir), MAY_WRITE); +} + +/** + * smack_inode_symlink - Smack check on symlink + * @dir: the directory to add the entry to + * @dentry: unused + * @name: unused + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_symlink(struct inode *dir, struct dentry *dentry, + const char *name) +{ + return smk_curacc(smk_of_inode(dir), MAY_WRITE); +} + +/** + * smack_inode_mknod - Smack check on mknod + * @dir: the directory to add the entry to + * @dentry: unused + * @mode: unused + * @dev: unused + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_mknod(struct inode *dir, struct dentry *dentry, + int mode, dev_t dev) +{ + return smk_curacc(smk_of_inode(dir), MAY_WRITE); +} + +/** + * smack_inode_rename - Smack check on rename + * @old_inode: the old directory + * @old_dentry: unused + * @new_inode: the new directory + * @new_dentry: unused + * + * Read and write access is required on both the old and + * new directories. + * + * Returns 0 if access is permitted, an error code otherwise + */ +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(smk_of_inode(old_inode), MAY_READWRITE); + if (rc == 0) + rc = smk_curacc(smk_of_inode(new_inode), MAY_READWRITE); + return rc; +} + +/** + * smack_inode_readlink - Smack check on readlink + * @dentry: the symlink + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_readlink(struct dentry *dentry) +{ + return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ); +} + +/** + * smack_inode_follow_link - Smack check on following a symlink + * @dentry: the symlink + * @nameidata: unused + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_follow_link(struct dentry *dentry, + struct nameidata *nameidata) +{ + return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ); +} + +/** + * smack_inode_permission - Smack version of permission() + * @inode: the inode in question + * @mask: the access requested + * @nd: unused + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_permission(struct inode *inode, int mask, + struct nameidata *nd) +{ + /* + * No permission to check. Existence test. Yup, it's there. + */ + if (mask == 0) + return 0; + + return smk_curacc(smk_of_inode(inode), mask); +} + +/** + * smack_inode_setattr - Smack check for setting attributes + * @dentry: the object + * @iattr: unused + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) +{ + return smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE); +} + +/** + * smack_inode_getattr - Smack check for getting attributes + * @mnt: unused + * @dentry: the object + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) +{ + return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ); +} + +/** + * smack_inode_setxattr - Smack check for setting xattrs + * @dentry: the object + * @name: passed to the cap call + * @value: passed to the cap call + * @size: passed to the cap call + * @flags: passed to the cap call + * + * Check with cap_inode_setxattr to see if the capability + * scheme approves, then check the Smack access + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_setxattr(struct dentry *dentry, char *name, + void *value, size_t size, int flags) +{ + int rc; + + if (strcmp(name, XATTR_NAME_SMACK) == 0 && + !__capable(current, CAP_MAC_OVERRIDE)) + return -EPERM; + + rc = cap_inode_setxattr(dentry, name, value, size, flags); + if (rc == 0) + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE); + + return rc; +} + +/** + * smack_inode_post_setxattr - Apply the Smack update approved above + * @dentry: object + * @name: attribute name + * @value: attribute value + * @size: attribute size + * @flags: unused + * + * Set the pointer in the inode blob to the entry found + * in the master label list. + */ +static void smack_inode_post_setxattr(struct dentry *dentry, char *name, + void *value, size_t size, int flags) +{ + struct inode_smack *isp; + + /* + * Not SMACK + */ + if (strcmp(name, XATTR_NAME_SMACK)) + return; + + if (size >= sizeof(smack_t)) + return; + + isp = dentry->d_inode->i_security; + + /* + * No locking is done here. This is a pointer + * assignment. + */ + isp->smk_inode = smk_import(value, size); + + return; +} + +/* + * smack_inode_getxattr - Smack check on getxattr + * @dentry: the object + * @name: unused + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_getxattr(struct dentry *dentry, char *name) +{ + return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ); +} + +/* + * smack_inode_listxattr - Smack check on listxattr + * @dentry: the object + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_listxattr(struct dentry *dentry) +{ + return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ); +} + +/* + * smack_inode_removexattr - Smack check on removexattr + * @dentry: the object + * @name: name of the attribute + * + * Removing the Smack attribute requires CAP_MAC_OVERRIDE + * + * Returns 0 if access is permitted, an error code otherwise + */ +static int smack_inode_removexattr(struct dentry *dentry, char *name) +{ + int rc; + + if (strcmp(name, XATTR_NAME_SMACK) == 0 && + !__capable(current, CAP_MAC_OVERRIDE)) + return -EPERM; + + rc = cap_inode_removexattr(dentry, name); + if (rc == 0) + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE); + + return rc; +} + +/** + * smack_inode_getsecurity - get smack xattrs + * @inode: the object + * @name: attribute name + * @buffer: where to put the result + * @size: size of the buffer + * @err: unused + * + * Returns the size of the attribute or an error code + */ +static int smack_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err) +{ + struct socket_smack *ssp; + struct socket *sock; + struct super_block *sbp; + struct inode *ip = (struct inode *)inode; + 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) == 0) { + isp = smk_of_inode(inode); + *bsp = *isp; + return strlen((char *)isp) + 1; + } + + /* + * The rest of the Smack xattrs are only on sockets. + */ + sbp = ip->i_sb; + if (sbp->s_magic != SOCKFS_MAGIC) + return -EOPNOTSUPP; + + sock = SOCKET_I(ip); + if (sock == NULL) + return -EOPNOTSUPP; + + ssp = sock->sk->sk_security; + + if (strcmp(name, XATTR_SMACK_PACKET) == 0) + *bsp = ssp->smk_packet; + else if (strcmp(name, XATTR_SMACK_IPIN) == 0) + *bsp = *ssp->smk_in; + else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) + *bsp = *ssp->smk_out; + else + return -EOPNOTSUPP; + + return strlen((char *)bsp) + 1; +} + +/** + * smack_inode_setsecurity - set smack xattrs + * @inode: the object + * @name: attribute name + * @value: attribute value + * @size: size of the attribute + * @flags: unused + * + * Sets the named attribute in the appropriate blob + * + * Returns 0 on success, or an error code + */ +static int smack_inode_setsecurity(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + smack_t *sp; + struct inode_smack *nsp = (struct inode_smack *)inode->i_security; + struct socket_smack *ssp; + struct socket *sock; + + if (value == NULL || size > sizeof(smack_t)) + return -EACCES; + + sp = smk_import(value, size); + if (*sp == SMK_INVALID) + return -EINVAL; + + if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { + mutex_lock(&nsp->smk_lock); + nsp->smk_inode = sp; + mutex_unlock(&nsp->smk_lock); + return 0; + } + /* + * The rest of the Smack xattrs are only on sockets. + */ + if (inode->i_sb->s_magic != SOCKFS_MAGIC) + return -EOPNOTSUPP; + + sock = SOCKET_I(inode); + if (sock == NULL) + return -EOPNOTSUPP; + + ssp = sock->sk->sk_security; + + if (strcmp(name, XATTR_SMACK_PACKET) == 0) + ssp->smk_packet = *sp; + else if (strcmp(name, XATTR_SMACK_IPIN) == 0) + ssp->smk_in = sp; + else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) + ssp->smk_out = sp; + else + return -EOPNOTSUPP; + return 0; +} + +/** + * smack_inode_listsecurity - list the Smack attributes + * @inode: the object + * @buffer: where they go + * @buffer_size: size of buffer + * + * Returns 0 on success, -EINVAL otherwise + */ +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; +} + +/** + * smack_d_instantiate - Make sure the blob is correct on an inode + * @opt_dentry: unused + * @inode: the object + * + * Set the inode's security blob if it hasn't been done already. + */ +static void smack_d_instantiate (struct dentry *opt_dentry, struct inode *inode) +{ + struct super_block *sbp; + struct superblock_smack *sbsp; + struct inode_smack *isp; + smack_t *csp = current->security; + smack_t *fetched; + smack_t *final; + struct dentry *dp; + + if (inode == NULL) + return; + + if (inode->i_security == NULL) + inode->i_security = + new_inode_smack(&smack_known_unset.smk_known); + + isp = inode->i_security; + + mutex_lock(&isp->smk_lock); + /* + * If the inode is already instantiated + * take the quick way out + */ + if (isp->smk_flags & SMK_INODE_INSTANT) + goto unlockandout; + + 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. + */ + final = 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. + */ + final = &smack_known_star.smk_known; + break; + case PIPEFS_MAGIC: + /* + * Casey says pipes are easy (?) + */ + final = &smack_known_star.smk_known; + break; + case DEVPTS_SUPER_MAGIC: + /* + * devpts seems content with the label of the task. + * Programs that change smack have to treat the + * pty with respect. + */ + final = csp; + break; + case SOCKFS_MAGIC: + /* + * Casey says sockets get the smack of the task. + */ + final = csp; + break; + case PROC_SUPER_MAGIC: + /* + * Casey says procfs appears not to care. + * The superblock default suffices. + */ + break; + case TMPFS_MAGIC: + /* + * Device labels should come from the filesystem, + * but watch out, because they're volitile, + * getting recreated on every reboot. + */ + final = &smack_known_star.smk_known; + /* + * No break. + * + * If a smack value has been set we want to use it, + * but since tmpfs isn't giving us the opportunity + * to set mount options simulate setting the + * superblock default. + */ + default: + /* + * This isn't an understood special case. + * Get the value from the xattr. + * + * 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) + break; + /* + * Get the dentry for xattr. + */ + if (opt_dentry == NULL) { + dp = d_find_alias(inode); + if (dp == NULL) + break; + } else { + dp = dget(opt_dentry); + if (dp == NULL) + break; + } + + fetched = smk_fetch(inode, dp); + if (fetched != NULL) + final = fetched; + + dput(dp); + break; + } + + if (final == NULL) + isp->smk_inode = csp; + else + isp->smk_inode = final; + + isp->smk_flags |= SMK_INODE_INSTANT; + +unlockandout: + mutex_unlock(&isp->smk_lock); + return; +} + +/* + * File Hooks + */ + +/** + * smack_file_alloc_security - assign a file security blob + * @file: the object + * + * The security blob for a file is a pointer to the master + * label list, so no allocation is done. + * + * Returns 0 + */ +static int smack_file_alloc_security(struct file *file) +{ + file->f_security = current->security; + return 0; +} + +/** + * smack_file_free_security - clear a file security blob + * @file: the object + * + * The security blob for a file is a pointer to the master + * label list, so no memory is freed. + */ +static void smack_file_free_security(struct file *file) +{ + file->f_security = NULL; +} + +/** + * smack_file_permission - Smack check on file operations + * @file: unused + * @mask: unused + * + * Returns 0 + * + * 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. + * + * I'll say no for now. Smack does not do the frequent + * label changing that SELinux does. + */ +static int smack_file_permission(struct file *file, int mask) +{ + return 0; +} + +/** + * smack_file_ioctl - Smack check on ioctls + * @file: the object + * @cmd: what to do + * @arg: unused + * + * Relies heavily on the correct use of the ioctl command conventions. + * + * Returns 0 if allowed, error code otherwise + */ +static int smack_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc = 0; + + if (_IOC_DIR(cmd) & _IOC_WRITE) + rc = smk_curacc(file->f_security, MAY_WRITE); + + if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ)) + rc = smk_curacc(file->f_security, MAY_READ); + + return rc; +} + +/** + * smack_file_lock - Smack check on file locking + * @file: the object + * @cmd unused + * + * Returns 0 if current has write access, error code otherwise + */ +static int smack_file_lock(struct file *file, unsigned int cmd) +{ + return smk_curacc(file->f_security, MAY_WRITE); +} + +/** + * smack_file_fcntl - Smack check on fcntl + * @file: the object + * @cmd: what action to check + * @arg: unused + * + * Returns 0 if current has access, error code otherwise + */ +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; +} + +/** + * smack_file_send_sigiotask - Smack on sigio + * @tsk: The target task + * @fown: the object the signal come from + * @signum: unused + * + * Allow a privileged task to get signals even if it shouldn't + * + * Returns 0 if a subject with the object's smack could + * write to the task, an error code otherwise. + */ +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; +} + +/** + * smack_file_receive - Smack file receive check + * @file: the object + * + * Returns 0 if current has access, error code otherwise + */ +static int smack_file_receive(struct file *file) +{ + int may = 0; + + /* + * This code relies on bitmasks. + */ + if (file->f_mode & FMODE_READ) + may = MAY_READ; + if (file->f_mode & FMODE_WRITE) + may |= MAY_WRITE; + + return smk_curacc(file->f_security, may); +} + +/* + * Socket hooks. + */ + +/** + * smack_sk_alloc_security - Allocate a socket blob + * @sk: the socket + * @family: unused + * @priority: memory allocation priority + * + * Assign Smack pointers to current except for smk_packet which + * is not a pointer but the real thing. + * + * Returns 0 on success, -ENOMEM is there's no memory + */ +static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t priority) +{ + smack_t *csp = current->security; + struct socket_smack *ssp; + + ssp = kzalloc(sizeof(struct socket_smack), priority); + if (ssp == NULL) + return -ENOMEM; + + ssp->smk_in = csp; + ssp->smk_out = csp; + ssp->smk_packet = SMK_INVALID; + + sk->sk_security = ssp; + + return 0; +} + +/** + * smack_sk_free_security - Free a socket blob + * @sk: the socket + * + * Clears the blob pointer + */ +static void smack_sk_free_security(struct sock *sk) +{ + kfree(sk->sk_security); + sk->sk_security = NULL; +} + +/** + * smack_set_catset - convert a capset to netlabel mls categories + * @catset: the Smack categories + * @sap: where to put the netlabel categories + * + * Allocates and fills mls_cat + */ +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 == 0) + 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); + } +} + +/** + * smack_to_secattr - fill a secattr from a smack value + * @smack: the smack value + * @nlsp: where the result goes + * + * 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, struct netlbl_lsm_secattr *nlsp) +{ + struct smack_cipso cipso; + int rc; + + switch (smack_net_nltype) { + case NETLBL_NLTYPE_CIPSOV4: + nlsp->domain = NULL; + nlsp->flags = NETLBL_SECATTR_DOMAIN; + nlsp->flags |= NETLBL_SECATTR_MLS_LVL; + + rc = smack_to_cipso(smack, &cipso); + if (rc == 0) { + nlsp->mls_lvl = cipso.smk_level; + smack_set_catset(cipso.smk_catset, nlsp); + } else { + nlsp->mls_lvl = smack_cipso_direct; + smack_set_catset(*smack, nlsp); + } + break; + default: + break; + } +} + +/** + * smack_netlabel - Set the secattr on a socket + * @sk: the socket + * + * Convert the outbound smack value (smk_out) to a + * secattr and attach it to the socket. + * + * Returns 0 on success or an error code + */ +static int smack_netlabel(struct sock *sk) +{ + struct socket_smack *ssp = sk->sk_security; + struct netlbl_lsm_secattr secattr; + int rc; + + netlbl_secattr_init(&secattr); + smack_to_secattr(ssp->smk_out, &secattr); + if (secattr.flags != NETLBL_SECATTR_NONE) + rc = netlbl_sock_setattr(sk, &secattr); + else + rc = -EINVAL; + + netlbl_secattr_destroy(&secattr); + return rc; +} + +/** + * smack_socket_post_create - finish socket setup + * @sock: the socket + * @family: protocol family + * @type: unused + * @protocol: unused + * @kern: indicates kern vs task socket + * + * Sets the security blob on the socket's inode. + * Sets the secattr value for outgoing packets. + * + * Returns 0 on success, and error code otherwise + */ +static int smack_socket_post_create(struct socket *sock, int family, + int type, int protocol, int kern) +{ + struct inode_smack *isp; + + isp = SOCK_INODE(sock)->i_security; + + if (isp == NULL) { + if (kern) + isp = new_inode_smack(&smack_known_floor.smk_known); + else + isp = new_inode_smack(current->security); + SOCK_INODE(sock)->i_security = isp; + } + + if (family != PF_INET) + return 0; + + /* + * Set the outbound netlbl. + */ + return smack_netlabel(sock->sk); +} + +/** + * smack_inode_create - Smack check on inode creation + * @dir: containing directory object + * @dentry: unused + * @mode: unused + * + * Returns 0 if current can write the containing directory, + * error code otherwise + */ +static int smack_inode_create(struct inode *dir, struct dentry *dentry, + int mode) +{ + return smk_curacc(smk_of_inode(dir), MAY_WRITE); +} + +/** + * smack_inode_unlink - Smack check on inode deletion + * @dir: containing directory object + * @dentry: file to unlink + * + * Returns 0 if current can write the containing directory + * and the object, error code otherwise + */ +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(smk_of_inode(ip), MAY_WRITE); + if (rc == 0) + /* + * You also need write access to the containing directory + */ + rc = smk_curacc(smk_of_inode(dir), MAY_WRITE); + + return rc; +} + +/** + * smack_inode_mkdir - Smack check on directory creation + * @dir: containing directory object + * @dentry: unused + * @mode: unused + * + * Returns 0 if current can write the containing