summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig68
-rw-r--r--security/apparmor/domain.c35
-rw-r--r--security/apparmor/path.c2
-rw-r--r--security/apparmor/policy.c4
-rw-r--r--security/commoncap.c13
-rw-r--r--security/integrity/ima/ima_main.c4
-rw-r--r--security/keys/Kconfig71
-rw-r--r--security/keys/Makefile12
-rw-r--r--security/keys/compat.c3
-rw-r--r--security/keys/gc.c94
-rw-r--r--security/keys/internal.h15
-rw-r--r--security/keys/key.c25
-rw-r--r--security/keys/keyctl.c34
-rw-r--r--security/keys/keyring.c167
-rw-r--r--security/keys/permission.c43
-rw-r--r--security/keys/proc.c3
-rw-r--r--security/keys/process_keys.c2
-rw-r--r--security/selinux/hooks.c10
-rw-r--r--security/smack/smack.h57
-rw-r--r--security/smack/smack_access.c233
-rw-r--r--security/smack/smack_lsm.c282
-rw-r--r--security/smack/smackfs.c1005
-rw-r--r--security/tomoyo/common.c26
-rw-r--r--security/tomoyo/common.h1
-rw-r--r--security/yama/yama_lsm.c63
25 files changed, 1486 insertions, 786 deletions
diff --git a/security/Kconfig b/security/Kconfig
index ccc61f8006b..e9c6ac724fe 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -4,73 +4,7 @@
menu "Security options"
-config KEYS
- bool "Enable access key retention support"
- help
- This option provides support for retaining authentication tokens and
- access keys in the kernel.
-
- It also includes provision of methods by which such keys might be
- associated with a process so that network filesystems, encryption
- support and the like can find them.
-
- Furthermore, a special type of key is available that acts as keyring:
- a searchable sequence of keys. Each process is equipped with access
- to five standard keyrings: UID-specific, GID-specific, session,
- process and thread.
-
- If you are unsure as to whether this is required, answer N.
-
-config TRUSTED_KEYS
- tristate "TRUSTED KEYS"
- depends on KEYS && TCG_TPM
- select CRYPTO
- select CRYPTO_HMAC
- select CRYPTO_SHA1
- help
- This option provides support for creating, sealing, and unsealing
- keys in the kernel. Trusted keys are random number symmetric keys,
- generated and RSA-sealed by the TPM. The TPM only unseals the keys,
- if the boot PCRs and other criteria match. Userspace will only ever
- see encrypted blobs.
-
- If you are unsure as to whether this is required, answer N.
-
-config ENCRYPTED_KEYS
- tristate "ENCRYPTED KEYS"
- depends on KEYS
- select CRYPTO
- select CRYPTO_HMAC
- select CRYPTO_AES
- select CRYPTO_CBC
- select CRYPTO_SHA256
- select CRYPTO_RNG
- help
- This option provides support for create/encrypting/decrypting keys
- in the kernel. Encrypted keys are kernel generated random numbers,
- which are encrypted/decrypted with a 'master' symmetric key. The
- 'master' key can be either a trusted-key or user-key type.
- Userspace only ever sees/stores encrypted blobs.
-
- If you are unsure as to whether this is required, answer N.
-
-config KEYS_DEBUG_PROC_KEYS
- bool "Enable the /proc/keys file by which keys may be viewed"
- depends on KEYS
- help
- This option turns on support for the /proc/keys file - through which
- can be listed all the keys on the system that are viewable by the
- reading process.
-
- The only keys included in the list are those that grant View
- permission to the reading process whether or not it possesses them.
- Note that LSM security checks are still performed, and may further
- filter out keys that the current process is not authorised to view.
-
- Only key attributes are listed here; key payloads are not included in
- the resulting table.
-
- If you are unsure as to whether this is required, answer N.
+source security/keys/Kconfig
config SECURITY_DMESG_RESTRICT
bool "Restrict unprivileged access to the kernel syslog"
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 6327685c101..b81ea10a17a 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -394,6 +394,11 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
new_profile = find_attach(ns, &ns->base.profiles, name);
if (!new_profile)
goto cleanup;
+ /*
+ * NOTE: Domain transitions from unconfined are allowed
+ * even when no_new_privs is set because this aways results
+ * in a further reduction of permissions.
+ */
goto apply;
}
@@ -455,6 +460,16 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
/* fail exec */
error = -EACCES;
+ /*
+ * Policy has specified a domain transition, if no_new_privs then
+ * fail the exec.
+ */
+ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) {
+ aa_put_profile(new_profile);
+ error = -EPERM;
+ goto cleanup;
+ }
+
if (!new_profile)
goto audit;
@@ -609,6 +624,14 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
const char *target = NULL, *info = NULL;
int error = 0;
+ /*
+ * Fail explicitly requested domain transitions if no_new_privs.
+ * There is no exception for unconfined as change_hat is not
+ * available.
+ */
+ if (current->no_new_privs)
+ return -EPERM;
+
/* released below */
cred = get_current_cred();
cxt = cred->security;
@@ -750,6 +773,18 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
cxt = cred->security;
profile = aa_cred_profile(cred);
+ /*
+ * Fail explicitly requested domain transitions if no_new_privs
+ * and not unconfined.
+ * Domain transitions from unconfined are allowed even when
+ * no_new_privs is set because this aways results in a reduction
+ * of permissions.
+ */
+ if (current->no_new_privs && !unconfined(profile)) {
+ put_cred(cred);
+ return -EPERM;
+ }
+
if (ns_name) {
/* released below */
ns = aa_find_namespace(profile->ns, ns_name);
diff --git a/security/apparmor/path.c b/security/apparmor/path.c
index 2daeea4f926..e91ffee8016 100644
--- a/security/apparmor/path.c
+++ b/security/apparmor/path.c
@@ -94,6 +94,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
* be returned.
*/
if (!res || IS_ERR(res)) {
+ if (PTR_ERR(res) == -ENAMETOOLONG)
+ return -ENAMETOOLONG;
connected = 0;
res = dentry_path_raw(path->dentry, buf, buflen);
if (IS_ERR(res)) {
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 421681c7c34..cf5fd220309 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -903,6 +903,10 @@ struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname)
profile = aa_get_profile(__lookup_profile(&ns->base, hname));
read_unlock(&ns->lock);
+ /* the unconfined profile is not in the regular profile list */
+ if (!profile && strcmp(hname, "unconfined") == 0)
+ profile = aa_get_profile(ns->unconfined);
+
/* refcount released by caller */
return profile;
}
diff --git a/security/commoncap.c b/security/commoncap.c
index 0cf4b53480a..f80d1160939 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -29,6 +29,7 @@
#include <linux/securebits.h>
#include <linux/user_namespace.h>
#include <linux/binfmts.h>
+#include <linux/personality.h>
/*
* If a non-root user executes a setuid-root binary in
@@ -505,15 +506,23 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
}
skip:
+ /* if we have fs caps, clear dangerous personality flags */
+ if (!cap_issubset(new->cap_permitted, old->cap_permitted))
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
+
+
/* Don't let someone trace a set[ug]id/setpcap binary with the revised
- * credentials unless they have the appropriate permit
+ * credentials unless they have the appropriate permit.
+ *
+ * In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
*/
if ((new->euid != old->uid ||
new->egid != old->gid ||
!cap_issubset(new->cap_permitted, old->cap_permitted)) &&
bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
/* downgrade; they get no more than they had, and maybe less */
- if (!capable(CAP_SETUID)) {
+ if (!capable(CAP_SETUID) ||
+ (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) {
new->euid = new->uid;
new->egid = new->gid;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 1eff5cb001e..b17be79b9cf 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -194,7 +194,9 @@ int ima_bprm_check(struct linux_binprm *bprm)
{
int rc;
- rc = process_measurement(bprm->file, bprm->filename,
+ rc = process_measurement(bprm->file,
+ (strcmp(bprm->filename, bprm->interp) == 0) ?
+ bprm->filename : bprm->interp,
MAY_EXEC, BPRM_CHECK);
return 0;
}
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
new file mode 100644
index 00000000000..a90d6d300db
--- /dev/null
+++ b/security/keys/Kconfig
@@ -0,0 +1,71 @@
+#
+# Key management configuration
+#
+
+config KEYS
+ bool "Enable access key retention support"
+ help
+ This option provides support for retaining authentication tokens and
+ access keys in the kernel.
+
+ It also includes provision of methods by which such keys might be
+ associated with a process so that network filesystems, encryption
+ support and the like can find them.
+
+ Furthermore, a special type of key is available that acts as keyring:
+ a searchable sequence of keys. Each process is equipped with access
+ to five standard keyrings: UID-specific, GID-specific, session,
+ process and thread.
+
+ If you are unsure as to whether this is required, answer N.
+
+config TRUSTED_KEYS
+ tristate "TRUSTED KEYS"
+ depends on KEYS && TCG_TPM
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_SHA1
+ help
+ This option provides support for creating, sealing, and unsealing
+ keys in the kernel. Trusted keys are random number symmetric keys,
+ generated and RSA-sealed by the TPM. The TPM only unseals the keys,
+ if the boot PCRs and other criteria match. Userspace will only ever
+ see encrypted blobs.
+
+ If you are unsure as to whether this is required, answer N.
+
+config ENCRYPTED_KEYS
+ tristate "ENCRYPTED KEYS"
+ depends on KEYS
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_AES
+ select CRYPTO_CBC
+ select CRYPTO_SHA256
+ select CRYPTO_RNG
+ help
+ This option provides support for create/encrypting/decrypting keys
+ in the kernel. Encrypted keys are kernel generated random numbers,
+ which are encrypted/decrypted with a 'master' symmetric key. The
+ 'master' key can be either a trusted-key or user-key type.
+ Userspace only ever sees/stores encrypted blobs.
+
+ If you are unsure as to whether this is required, answer N.
+
+config KEYS_DEBUG_PROC_KEYS
+ bool "Enable the /proc/keys file by which keys may be viewed"
+ depends on KEYS
+ help
+ This option turns on support for the /proc/keys file - through which
+ can be listed all the keys on the system that are viewable by the
+ reading process.
+
+ The only keys included in the list are those that grant View
+ permission to the reading process whether or not it possesses them.
+ Note that LSM security checks are still performed, and may further
+ filter out keys that the current process is not authorised to view.
+
+ Only key attributes are listed here; key payloads are not included in
+ the resulting table.
+
+ If you are unsure as to whether this is required, answer N.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index a56f1ffdc64..504aaa00838 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -2,6 +2,9 @@
# Makefile for key management
#
+#
+# Core
+#
obj-y := \
gc.o \
key.o \
@@ -12,9 +15,12 @@ obj-y := \
request_key.o \
request_key_auth.o \
user_defined.o
-
-obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
+
+#
+# Key types
+#
+obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
diff --git a/security/keys/compat.c b/security/keys/compat.c
index 4c48e13448f..fab4f8dda6c 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -135,6 +135,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
return compat_keyctl_instantiate_key_iov(
arg2, compat_ptr(arg3), arg4, arg5);
+ case KEYCTL_INVALIDATE:
+ return keyctl_invalidate_key(arg2);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/gc.c b/security/keys/gc.c
index a42b45531aa..61ab7c82ebb 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -72,6 +72,15 @@ void key_schedule_gc(time_t gc_at)
}
/*
+ * Schedule a dead links collection run.
+ */
+void key_schedule_gc_links(void)
+{
+ set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags);
+ queue_work(system_nrt_wq, &key_gc_work);
+}
+
+/*
* Some key's cleanup time was met after it expired, so we need to get the
* reaper to go through a cycle finding expired keys.
*/
@@ -79,8 +88,7 @@ static void key_gc_timer_func(unsigned long data)
{
kenter("");
key_gc_next_run = LONG_MAX;
- set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags);
- queue_work(system_nrt_wq, &key_gc_work);
+ key_schedule_gc_links();
}
/*
@@ -131,12 +139,12 @@ void key_gc_keytype(struct key_type *ktype)
static void key_gc_keyring(struct key *keyring, time_t limit)
{
struct keyring_list *klist;
- struct key *key;
int loop;
kenter("%x", key_serial(keyring));
- if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED)))
goto dont_gc;
/* scan the keyring looking for dead keys */
@@ -148,9 +156,8 @@ static void key_gc_keyring(struct key *keyring, time_t limit)
loop = klist->nkeys;
smp_rmb();
for (loop--; loop >= 0; loop--) {
- key = klist->keys[loop];
- if (test_bit(KEY_FLAG_DEAD, &key->flags) ||
- (key->expiry > 0 && key->expiry <= limit))
+ struct key *key = rcu_dereference(klist->keys[loop]);
+ if (key_is_dead(key, limit))
goto do_gc;
}
@@ -168,38 +175,45 @@ do_gc:
}
/*
- * Garbage collect an unreferenced, detached key
+ * Garbage collect a list of unreferenced, detached keys
*/
-static noinline void key_gc_unused_key(struct key *key)
+static noinline void key_gc_unused_keys(struct list_head *keys)
{
- key_check(key);
-
- security_key_free(key);
-
- /* deal with the user's key tracking and quota */
- if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
- spin_lock(&key->user->lock);
- key->user->qnkeys--;
- key->user->qnbytes -= key->quotalen;
- spin_unlock(&key->user->lock);
- }
+ while (!list_empty(keys)) {
+ struct key *key =
+ list_entry(keys->next, struct key, graveyard_link);
+ list_del(&key->graveyard_link);
+
+ kdebug("- %u", key->serial);
+ key_check(key);
+
+ security_key_free(key);
+
+ /* deal with the user's key tracking and quota */
+ if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+ spin_lock(&key->user->lock);
+ key->user->qnkeys--;
+ key->user->qnbytes -= key->quotalen;
+ spin_unlock(&key->user->lock);
+ }
- atomic_dec(&key->user->nkeys);
- if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
- atomic_dec(&key->user->nikeys);
+ atomic_dec(&key->user->nkeys);
+ if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+ atomic_dec(&key->user->nikeys);
- key_user_put(key->user);
+ key_user_put(key->user);
- /* now throw away the key memory */
- if (key->type->destroy)
- key->type->destroy(key);
+ /* now throw away the key memory */
+ if (key->type->destroy)
+ key->type->destroy(key);
- kfree(key->description);
+ kfree(key->description);
#ifdef KEY_DEBUGGING
- key->magic = KEY_DEBUG_MAGIC_X;
+ key->magic = KEY_DEBUG_MAGIC_X;
#endif
- kmem_cache_free(key_jar, key);
+ kmem_cache_free(key_jar, key);
+ }
}
/*
@@ -211,6 +225,7 @@ static noinline void key_gc_unused_key(struct key *key)
*/
static void key_garbage_collector(struct work_struct *work)
{
+ static LIST_HEAD(graveyard);
static u8 gc_state; /* Internal persistent state */
#define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */
#define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */
@@ -316,15 +331,22 @@ maybe_resched:
key_schedule_gc(new_timer);
}
- if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) {
- /* Make sure everyone revalidates their keys if we marked a
- * bunch as being dead and make sure all keyring ex-payloads
- * are destroyed.
+ if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2) ||
+ !list_empty(&graveyard)) {
+ /* Make sure that all pending keyring payload destructions are
+ * fulfilled and that people aren't now looking at dead or
+ * dying keys that they don't have a reference upon or a link
+ * to.
*/
- kdebug("dead sync");
+ kdebug("gc sync");
synchronize_rcu();
}
+ if (!list_empty(&graveyard)) {
+ kdebug("gc keys");
+ key_gc_unused_keys(&graveyard);
+ }
+
if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 |
KEY_GC_REAPING_DEAD_2))) {
if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) {
@@ -359,7 +381,7 @@ found_unreferenced_key:
rb_erase(&key->serial_node, &key_serial_tree);
spin_unlock(&key_serial_lock);
- key_gc_unused_key(key);
+ list_add_tail(&key->graveyard_link, &graveyard);
gc_state |= KEY_GC_REAP_AGAIN;
goto maybe_resched;
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 65647f82558..f711b094ed4 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -152,7 +152,8 @@ extern long join_session_keyring(const char *name);
extern struct work_struct key_gc_work;
extern unsigned key_gc_delay;
extern void keyring_gc(struct key *keyring, time_t limit);
-extern void key_schedule_gc(time_t expiry_at);
+extern void key_schedule_gc(time_t gc_at);
+extern void key_schedule_gc_links(void);
extern void key_gc_keytype(struct key_type *ktype);
extern int key_task_permission(const key_ref_t key_ref,
@@ -197,6 +198,17 @@ extern struct key *request_key_auth_new(struct key *target,
extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
/*
+ * Determine whether a key is dead.
+ */
+static inline bool key_is_dead(struct key *key, time_t limit)
+{
+ return
+ key->flags & ((1 << KEY_FLAG_DEAD) |
+ (1 << KEY_FLAG_INVALIDATED)) ||
+ (key->expiry > 0 && key->expiry <= limit);
+}
+
+/*
* keyctl() functions
*/
extern long keyctl_get_keyring_ID(key_serial_t, int);
@@ -225,6 +237,7 @@ extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t);
extern long keyctl_instantiate_key_iov(key_serial_t,
const struct iovec __user *,
unsigned, key_serial_t);
+extern long keyctl_invalidate_key(key_serial_t);
extern long keyctl_instantiate_key_common(key_serial_t,
const struct iovec __user *,
diff --git a/security/keys/key.c b/security/keys/key.c
index 06783cffb3a..c9bf66ac36e 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -955,6 +955,28 @@ void key_revoke(struct key *key)
EXPORT_SYMBOL(key_revoke);
/**
+ * key_invalidate - Invalidate a key.
+ * @key: The key to be invalidated.
+ *
+ * Mark a key as being invalidated and have it cleaned up immediately. The key
+ * is ignored by all searches and other operations from this point.
+ */
+void key_invalidate(struct key *key)
+{
+ kenter("%d", key_serial(key));
+
+ key_check(key);
+
+ if (!test_bit(KEY_FLAG_INVALIDATED, &key->flags)) {
+ down_write_nested(&key->sem, 1);
+ if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags))
+ key_schedule_gc_links();
+ up_write(&key->sem);
+ }
+}
+EXPORT_SYMBOL(key_invalidate);
+
+/**
* register_key_type - Register a type of key.
* @ktype: The new key type.
*
@@ -980,6 +1002,8 @@ int register_key_type(struct key_type *ktype)
/* store the type */
list_add(&ktype->link, &key_types_list);
+
+ pr_notice("Key type %s registered\n", ktype->name);
ret = 0;
out:
@@ -1002,6 +1026,7 @@ void unregister_key_type(struct key_type *ktype)
list_del_init(&ktype->link);
downgrade_write(&key_types_sem);
key_gc_keytype(ktype);
+ pr_notice("Key type %s unregistered\n", ktype->name);
up_read(&key_types_sem);
}
EXPORT_SYMBOL(unregister_key_type);
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index fb767c6cd99..ddb3e05bc5f 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -375,6 +375,37 @@ error:
}
/*
+ * Invalidate a key.
+ *
+ * The key must be grant the caller Invalidate permission for this to work.
+ * The key and any links to the key will be automatically garbage collected
+ * immediately.
+ *
+ * If successful, 0 is returned.
+ */
+long keyctl_invalidate_key(key_serial_t id)
+{
+ key_ref_t key_ref;
+ long ret;
+
+ kenter("%d", id);
+
+ key_ref = lookup_user_key(id, 0, KEY_SEARCH);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error;
+ }
+
+ key_invalidate(key_ref_to_ptr(key_ref));
+ ret = 0;
+
+ key_ref_put(key_ref);
+error:
+ kleave(" = %ld", ret);
+ return ret;
+}
+
+/*
* Clear the specified keyring, creating an empty process keyring if one of the
* special keyring IDs is used.
*
@@ -1622,6 +1653,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
(unsigned) arg4,
(key_serial_t) arg5);
+ case KEYCTL_INVALIDATE:
+ return keyctl_invalidate_key((key_serial_t) arg2);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index d605f75292e..7445875f681 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -25,6 +25,15 @@
(keyring)->payload.subscriptions, \
rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
+#define rcu_deref_link_locked(klist, index, keyring) \
+ (rcu_dereference_protected( \
+ (klist)->keys[index], \
+ rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
+
+#define MAX_KEYRING_LINKS \
+ min_t(size_t, USHRT_MAX - 1, \
+ ((PAGE_SIZE - sizeof(struct keyring_list)) / sizeof(struct key *)))
+
#define KEY_LINK_FIXQUOTA 1UL
/*
@@ -138,6 +147,11 @@ static int keyring_match(const struct key *keyring, const void *description)
/*
* Clean up a keyring when it is destroyed. Unpublish its name if it had one
* and dispose of its data.
+ *
+ * The garbage collector detects the final key_put(), removes the keyring from
+ * the serial number tree and then does RCU synchronisation before coming here,
+ * so we shouldn't need to worry about code poking around here with the RCU
+ * readlock held by this time.
*/
static void keyring_destroy(struct key *keyring)
{
@@ -154,11 +168,10 @@ static void keyring_destroy(struct key *keyring)
write_unlock(&keyring_name_lock);
}
- klist = rcu_dereference_check(keyring->payload.subscriptions,
- atomic_read(&keyring->usage) == 0);
+ klist = rcu_access_pointer(keyring->payload.subscriptions);
if (klist) {
for (loop = klist->nkeys - 1; loop >= 0; loop--)
- key_put(klist->keys[loop]);
+ key_put(rcu_access_pointer(klist->keys[loop]));
kfree(klist);
}
}
@@ -214,7 +227,8 @@ static long keyring_read(const struct key *keyring,
ret = -EFAULT;
for (loop = 0; loop < klist->nkeys; loop++) {
- key = klist->keys[loop];
+ key = rcu_deref_link_locked(klist, loop,
+ keyring);
tmp = sizeof(key_serial_t);
if (tmp > buflen)
@@ -309,6 +323,8 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
bool no_state_check)
{
struct {
+ /* Need a separate keylist pointer for RCU purposes */
+ struct key *keyring;
struct keyring_list *keylist;
int kix;
} stack[KEYRING_SEARCH_MAX_DEPTH];
@@ -366,13 +382,17 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
/* otherwise, the top keyring must not be revoked, expired, or
* negatively instantiated if we are to search it */
key_ref = ERR_PTR(-EAGAIN);
- if (kflags & ((1 << KEY_FLAG_REVOKED) | (1 << KEY_FLAG_NEGATIVE)) ||
+ if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED) |
+ (1 << KEY_FLAG_NEGATIVE)) ||
(keyring->expiry && now.tv_sec >= keyring->expiry))
goto error_2;
/* start processing a new keyring */
descend:
- if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ kflags = keyring->flags;
+ if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED)))
goto not_this_keyring;
keylist = rcu_dereference(keyring->payload.subscriptions);
@@ -383,16 +403,17 @@ descend:
nkeys = keylist->nkeys;
smp_rmb();
for (kix = 0; kix < nkeys; kix++) {
- key = keylist->keys[kix];
+ key = rcu_dereference(keylist->keys[kix]);
kflags = key->flags;
/* ignore keys not of this type */
if (key->type != type)
continue;
- /* skip revoked keys and expired keys */
+ /* skip invalidated, revoked and expired keys */
if (!no_state_check) {
- if (kflags & (1 << KEY_FLAG_REVOKED))
+ if (kflags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED)))
continue;
if (key->expiry && now.tv_sec >= key->expiry)
@@ -426,7 +447,7 @@ ascend:
nkeys = keylist->nkeys;
smp_rmb();
for (; kix < nkeys; kix++) {
- key = keylist->keys[kix];
+ key = rcu_dereference(keylist->keys[kix]);
if (key->type != &key_type_keyring)
continue;
@@ -441,6 +462,7 @@ ascend:
continue;
/* stack the current position */
+ stack[sp].keyring = keyring;
stack[sp].keylist = keylist;
stack[sp].kix = kix;
sp++;
@@ -456,6 +478,7 @@ not_this_keyring:
if (sp > 0) {
/* resume the processing of a keyring higher up in the tree */
sp--;
+ keyring = stack[sp].keyring;
keylist = stack[sp].keylist;
kix = stack[sp].kix + 1;
goto ascend;
@@ -467,6 +490,10 @@ not_this_keyring:
/* we found a viable match */
found:
atomic_inc(&key->usage);
+ key->last_used_at = now.tv_sec;
+ keyring->last_used_at = now.tv_sec;
+ while (sp > 0)
+ stack[--sp].keyring->last_used_at = now.tv_sec;
key_check(key);
key_ref = make_key_ref(key, possessed);
error_2:
@@ -531,14 +558,14 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
nkeys = klist->nkeys;
smp_rmb();
for (loop = 0; loop < nkeys ; loop++) {
- key = klist->keys[loop];
-
+ key = rcu_dereference(klist->keys[loop]);
if (key->type == ktype &&
(!key->type->match ||
key->type->match(key, description)) &&
key_permission(make_key_ref(key, possessed),
perm) == 0 &&
- !test_bit(KEY_FLAG_REVOKED, &key->flags)
+ !(key->flags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED)))
)
goto found;
}
@@ -549,6 +576,8 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
found:
atomic_inc(&key->usage);
+ keyring->last_used_at = key->last_used_at =
+ current_kernel_time().tv_sec;
rcu_read_unlock();
return make_key_ref(key, possessed);
}
@@ -602,6 +631,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
* (ie. it has a zero usage count) */
if (!atomic_inc_not_zero(&keyring->usage))
continue;
+ keyring->last_used_at = current_kernel_time().tv_sec;
goto out;
}
}
@@ -654,7 +684,7 @@ ascend:
nkeys = keylist->nkeys;
smp_rmb();
for (; kix < nkeys; kix++) {
- key = keylist->keys[kix];
+ key = rcu_dereference(keylist->keys[kix]);
if (key == A)
goto cycle_detected;
@@ -711,7 +741,7 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
container_of(rcu, struct keyring_list, rcu);
if (klist->delkey != USHRT_MAX)
- key_put(klist->keys[klist->delkey]);
+ key_put(rcu_access_pointer(klist->keys[klist->delkey]));
kfree(klist);
}
@@ -725,8 +755,9 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
struct keyring_list *klist, *nklist;
unsigned long prealloc;
unsigned max;
+ time_t lowest_lru;
size_t size;
- int loop, ret;
+ int loop, lru, ret;
kenter("%d,%s,%s,", key_serial(keyring), type->name, description);
@@ -747,31 +778,39 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
klist = rcu_dereference_locked_keyring(keyring);
/* see if there's a matching key we can displace */
+ lru = -1;
if (klist && klist->nkeys > 0) {
+ lowest_lru = TIME_T_MAX;
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
- if (klist->keys[loop]->type == type &&
- strcmp(klist->keys[loop]->description,
- description) == 0
- ) {
- /* found a match - we'll replace this one with
- * the new key */
- size = sizeof(struct key *) * klist->maxkeys;
- size += sizeof(*klist);
- BUG_ON(size > PAGE_SIZE);
-
- ret = -ENOMEM;
- nklist = kmemdup(klist, size, GFP_KERNEL);
- if (!nklist)
- goto error_sem;
-
- /* note replacement slot */
- klist->delkey = nklist->delkey = loop;
- prealloc = (unsigned long)nklist;
+ struct key *key = rcu_deref_link_locked(klist, loop,
+ keyring);
+ if (key->type == type &&
+ strcmp(key->description, description) == 0) {
+ /* Found a match - we'll replace the link with
+ * one to the new key. We record the slot
+ * position.
+ */
+ klist->delkey = loop;
+ prealloc = 0;
goto done;
}
+ if (key->last_used_at < lowest_lru) {
+ lowest_lru = key->last_used_at;
+ lru = loop;
+ }
}
}
+ /* If the keyring is full then do an LRU discard */
+ if (klist &&
+ klist->nkeys == klist->maxkeys &&
+ klist->maxkeys >= MAX_KEYRING_LINKS) {
+ kdebug("LRU discard %d\n", lru);
+ klist->delkey = lru;
+ prealloc = 0;
+ goto done;
+ }
+
/* check that we aren't going to overrun the user's quota */
ret = key_payload_reserve(keyring,
keyring->datalen + KEYQUOTA_LINK_BYTES);
@@ -780,20 +819,19 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
if (klist && klist->nkeys < klist->maxkeys) {
/* there's sufficient slack space to append directly */
- nklist = NULL;
+ klist->delkey = klist->nkeys;
prealloc = KEY_LINK_FIXQUOTA;
} else {
/* grow the key list */
max = 4;
- if (klist)
+ if (klist) {
max += klist->maxkeys;
+ if (max > MAX_KEYRING_LINKS)
+ max = MAX_KEYRING_LINKS;
+ BUG_ON(max <= klist->maxkeys);
+ }
- ret = -ENFILE;
- if (max > USHRT_MAX - 1)
- goto error_quota;
size = sizeof(*klist) + sizeof(struct key *) * max;
- if (size > PAGE_SIZE)
- goto error_quota;
ret = -ENOMEM;
nklist = kmalloc(size, GFP_KERNEL);
@@ -813,10 +851,10 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
}
/* add the key into the new space */
- nklist->keys[nklist->delkey] = NULL;
+ RCU_INIT_POINTER(nklist->keys[nklist->delkey], NULL);
+ prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA;
}
- prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA;
done:
*_prealloc = prealloc;
kleave(" = 0");
@@ -862,6 +900,7 @@ void __key_link(struct key *keyring, struct key *key,
unsigned long *_prealloc)
{
struct keyring_list *klist, *nklist;
+ struct key *discard;
nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA);
*_prealloc = 0;
@@ -871,14 +910,16 @@ void __key_link(struct key *keyring, struct key *key,
klist = rcu_dereference_locked_keyring(keyring);
atomic_inc(&key->usage);
+ keyring->last_used_at = key->last_used_at =
+ current_kernel_time().tv_sec;
/* there's a matching key we can displace or an empty slot in a newly
* allocated list we can fill */
if (nklist) {
- kdebug("replace %hu/%hu/%hu",
+ kdebug("reissue %hu/%hu/%hu",
nklist->delkey, nklist->nkeys, nklist->maxkeys);
- nklist->keys[nklist->delkey] = key;
+ RCU_INIT_POINTER(nklist->keys[nklist->delkey], key);
rcu_assign_pointer(keyring->payload.subscriptions, nklist);
@@ -889,9 +930,23 @@ void __key_link(struct key *keyring, struct key *key,
klist->delkey, klist->nkeys, klist->maxkeys);
call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
}
+ } else if (klist->delkey < klist->nkeys) {
+ kdebug("replace %hu/%hu/%hu",
+ klist->delkey, klist->nkeys, klist->maxkeys);
+
+ discard = rcu_dereference_protected(
+ klist->keys[klist->delkey],
+ rwsem_is_locked(&keyring->sem));
+ rcu_assign_pointer(klist->keys[klist->delkey], key);
+ /* The garbage collector will take care of RCU
+ * synchronisation */
+ key_put(discard);
} else {
/* there's sufficient slack space to append directly */
- klist->keys[klist->nkeys] = key;
+ kdebug("append %hu/%hu/%hu",
+ klist->delkey, klist->nkeys, klist->maxkeys);
+
+ RCU_INIT_POINTER(klist->keys[klist->delkey], key);
smp_wmb();
klist->nkeys++;
}
@@ -998,7 +1053,7 @@ int key_unlink(struct key *keyring, struct key *key)
if (klist) {
/* search the keyring for the key */
for (loop = 0; loop < klist->nkeys; loop++)
- if (klist->keys[loop] == key)
+ if (rcu_access_pointer(klist->keys[loop]) == key)
goto key_is_present;
}
@@ -1061,7 +1116,7 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
klist = container_of(rcu, struct keyring_list, rcu);
for (loop = klist->nkeys - 1; loop >= 0; loop--)
- key_put(klist->keys[loop]);
+ key_put(rcu_access_pointer(klist->keys[loop]));
kfree(klist);
}
@@ -1128,15 +1183,6 @@ static void keyring_revoke(struct key *keyring)
}
/*
- * Determine whether a key is dead.
- */
-static bool key_is_dead(struct key *key, time_t limit)
-{
- return test_bit(KEY_FLAG_DEAD, &key->flags) ||
- (key->expiry > 0 && key->expiry <= limit);
-}
-
-/*
* Collect garbage from the contents of a keyring, replacing the old list with
* a new one with the pointers all shuffled down.
*
@@ -1161,7 +1207,8 @@ void keyring_gc(struct key *keyring, time_t limit)
/* work out how many subscriptions we're keeping */
keep = 0;
for (loop = klist->nkeys - 1; loop >= 0; loop--)
- if (!key_is_dead(klist->keys[loop], limit))
+ if (!key_is_dead(rcu_deref_link_locked(klist, loop, keyring),
+ limit))
keep++;
if (keep == klist->nkeys)
@@ -1182,11 +1229,11 @@ void keyring_gc(struct key *keyring, time_t limit)
*/
keep = 0;
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
- key = klist->keys[loop];
+ key = rcu_deref_link_locked(klist, loop, keyring);
if (!key_is_dead(key, limit)) {
if (keep >= max)
goto discard_new;
- new->keys[keep++] = key_get(key);
+ RCU_INIT_POINTER(new->keys[keep++], key_get(key));
}
}
new->nkeys = keep;
diff --git a/security/keys/permission.c b/security/keys/permission.c
index c35b5229e3c..57d96363d7f 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -87,32 +87,29 @@ EXPORT_SYMBOL(key_task_permission);
* key_validate - Validate a key.
* @key: The key to be validated.
*
- * Check that a key is valid, returning 0 if the key is okay, -EKEYREVOKED if
- * the key's type has been removed or if the key has been revoked or
- * -EKEYEXPIRED if the key has expired.
+ * Check that a key is valid, returning 0 if the key is okay, -ENOKEY if the
+ * key is invalidated, -EKEYREVOKED if the key's type has been removed or if
+ * the key has been revoked or -EKEYEXPIRED if the key has expired.
*/
-int key_validate(struct key *key)
+int key_validate(const struct key *key)
{
- struct timespec now;
- int ret = 0;
-
- if (key) {
- /* check it's still accessible */
- ret = -EKEYREVOKED;
- if (test_bit(KEY_FLAG_REVOKED, &key->flags) ||
- test_bit(KEY_FLAG_DEAD, &key->flags))
- goto error;
-
- /* check it hasn't expired */
- ret = 0;
- if (key->expiry) {
- now = current_kernel_time();
- if (now.tv_sec >= key->expiry)
- ret = -EKEYEXPIRED;
- }
+ unsigned long flags = key->flags;
+
+ if (flags & (1 << KEY_FLAG_INVALIDATED))
+ return -ENOKEY;
+
+ /* check it's still accessible */
+ if (flags & ((1 << KEY_FLAG_REVOKED) |
+ (1 << KEY_FLAG_DEAD)))
+ return -EKEYREVOKED;
+
+ /* check it hasn't expired */
+ if (key->expiry) {
+ struct timespec now = current_kernel_time();
+ if (now.tv_sec >= key->expiry)
+ return -EKEYEXPIRED;
}
-error:
- return ret;
+ return 0;
}
EXPORT_SYMBOL(key_validate);
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 49bbc97943a..30d1ddfd9ce 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -242,7 +242,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
#define showflag(KEY, LETTER, FLAG) \
(test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
- seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
+ seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
key->serial,
showflag(key, 'I', KEY_FLAG_INSTANTIATED),
showflag(key, 'R', KEY_FLAG_REVOKED),
@@ -250,6 +250,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
showflag(key, 'N', KEY_FLAG_NEGATIVE),
+ showflag(key, 'i', KEY_FLAG_INVALIDATED),
atomic_read(&key->usage),
xbuf,
key->perm,
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index be7ecb2018d..e137fcd7042 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -732,6 +732,8 @@ try_again:
if (ret < 0)
goto invalid_key;
+ key->last_used_at = current_kernel_time().tv_sec;
+
error:
put_cred(cred);
return key_ref;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e94349b85bf..fa2341b6833 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1986,6 +1986,13 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
new_tsec->sid = old_tsec->exec_sid;
/* Reset exec SID on execve. */
new_tsec->exec_sid = 0;
+
+ /*
+ * Minimize confusion: if no_new_privs and a transition is
+ * explicitly requested, then fail the exec.
+ */
+ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
+ return -EPERM;
} else {
/* Check for a default transition on this program. */
rc = security_transition_sid(old_tsec->sid, isec->sid,
@@ -1998,7 +2005,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = bprm->file->f_path;
- if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
+ if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) ||
+ (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS))
new_tsec->sid = old_tsec->sid;
if (new_tsec->sid == old_tsec->sid) {
diff --git a/security/smack/smack.h b/security/smack/smack.h
index b61e75f224d..cc361b8f3d1 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -23,13 +23,19 @@
#include <linux/lsm_audit.h>
/*
+ * Smack labels were limited to 23 characters for a long time.
+ */
+#define SMK_LABELLEN 24
+#define SMK_LONGLABEL 256
+
+/*
+ * Maximum number of bytes for the levels in a CIPSO IP option.
* Why 23? CIPSO is constrained to 30, so a 32 byte buffer is
* bigger than can be used, and 24 is the next lower multiple
* of 8, and there are too many issues if there isn't space set
* aside for the terminating null byte.
*/
-#define SMK_MAXLEN 23
-#define SMK_LABELLEN (SMK_MAXLEN+1)
+#define SMK_CIPSOLEN 24
struct superblock_smack {
char *smk_root;
@@ -66,6 +72,7 @@ struct task_smack {
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
#define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */
+#define SMK_INODE_CHANGED 0x04 /* smack was transmuted */
/*
* A label access rule.
@@ -78,15 +85,6 @@ struct smack_rule {
};
/*
- * An entry in the table mapping smack values to
- * CIPSO level/category-set values.
- */
-struct smack_cipso {
- int smk_level;
- char smk_catset[SMK_LABELLEN];
-};
-
-/*
* An entry in the table identifying hosts.
*/
struct smk_netlbladdr {
@@ -113,22 +111,19 @@ struct smk_netlbladdr {
* 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.
+ * The cipso value associated with the label gets stored here, too.
*
* Keep the access rules for this subject label here so that
* the entire set of rules does not need to be examined every
* time.
*/
struct smack_known {
- struct list_head list;
- char smk_known[SMK_LABELLEN];
- u32 smk_secid;
- struct smack_cipso *smk_cipso;
- spinlock_t smk_cipsolock; /* for changing cipso map */
- struct list_head smk_rules; /* access rules */
- struct mutex smk_rules_lock; /* lock for the rules */
+ struct list_head list;
+ char *smk_known;
+ u32 smk_secid;
+ struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */
+ struct list_head smk_rules; /* access rules */
+ struct mutex smk_rules_lock; /* lock for rules */
};
/*
@@ -165,6 +160,7 @@ struct smack_known {
#define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */
#define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */
#define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */
+#define SMACK_CIPSO_MAPPED_DEFAULT 251 /* Also arbitrary */
#define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */
#define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */
#define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */
@@ -215,10 +211,9 @@ struct inode_smack *new_inode_smack(char *);
int smk_access_entry(char *, char *, struct list_head *);
int smk_access(char *, char *, int, struct smk_audit_info *);
int smk_curacc(char *, u32, struct smk_audit_info *);
-int smack_to_cipso(const char *, struct smack_cipso *);
-char *smack_from_cipso(u32, char *);
char *smack_from_secid(const u32);
-void smk_parse_smack(const char *string, int len, char *smack);
+char *smk_parse_smack(const char *string, int len);
+int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
char *smk_import(const char *, int);
struct smack_known *smk_import_entry(const char *, int);
struct smack_known *smk_find_entry(const char *);
@@ -228,6 +223,7 @@ u32 smack_to_secid(const char *);
* Shared data.
*/
extern int smack_cipso_direct;
+extern int smack_cipso_mapped;
extern char *smack_net_ambient;
extern char *smack_onlycap;
extern const char *smack_cipso_option;
@@ -239,24 +235,13 @@ extern struct smack_known smack_known_invalid;
extern struct smack_known smack_known_star;
extern struct smack_known smack_known_web;
+extern struct mutex smack_known_lock;
extern struct list_head smack_known_list;
extern struct list_head smk_netlbladdr_list;
extern struct security_operations smack_ops;
/*
- * Stricly for CIPSO level manipulation.
- * Set the category bit number in a smack label sized buffer.
- */
-static inline void smack_catset_bit(int cat, char *catsetp)
-{
- if (cat > SMK_LABELLEN * 8)
- return;
-
- catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8);
-}
-
-/*
* Is the directory transmuting?
*/
static inline int smk_inode_transmutable(const struct inode *isp)
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index c8115f7308f..9f3705e9271 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -19,37 +19,31 @@
struct smack_known smack_known_huh = {
.smk_known = "?",
.smk_secid = 2,
- .smk_cipso = NULL,
};
struct smack_known smack_known_hat = {
.smk_known = "^",
.smk_secid = 3,
- .smk_cipso = NULL,
};
struct smack_known smack_known_star = {
.smk_known = "*",
.smk_secid = 4,
- .smk_cipso = NULL,
};
struct smack_known smack_known_floor = {
.smk_known = "_",
.smk_secid = 5,
- .smk_cipso = NULL,
};
struct smack_known smack_known_invalid = {
.smk_known = "",
.smk_secid = 6,
- .smk_cipso = NULL,
};
struct smack_known smack_known_web = {
.smk_known = "@",
.smk_secid = 7,
- .smk_cipso = NULL,
};
LIST_HEAD(smack_known_list);
@@ -331,7 +325,7 @@ void smack_log(char *subject_label, char *object_label, int request,
}
#endif
-static DEFINE_MUTEX(smack_known_lock);
+DEFINE_MUTEX(smack_known_lock);
/**
* smk_find_entry - find a label on the list, return the list entry
@@ -345,7 +339,7 @@ struct smack_known *smk_find_entry(const char *string)
struct smack_known *skp;
list_for_each_entry_rcu(skp, &smack_known_list, list) {
- if (strncmp(skp->smk_known, string, SMK_MAXLEN) == 0)
+ if (strcmp(skp->smk_known, string) == 0)
return skp;
}
@@ -356,27 +350,76 @@ struct smack_known *smk_find_entry(const char *string)
* smk_parse_smack - parse smack label from a text string
* @string: a text string that might contain a Smack label
* @len: the maximum size, or zero if it is NULL terminated.
- * @smack: parsed smack label, or NULL if parse error
+ *
+ * Returns a pointer to the clean label, or NULL
*/
-void smk_parse_smack(const char *string, int len, char *smack)
+char *smk_parse_smack(const char *string, int len)
{
- int found;
+ char *smack;
int i;
- if (len <= 0 || len > SMK_MAXLEN)
- len = SMK_MAXLEN;
-
- for (i = 0, found = 0; i < SMK_LABELLEN; i++) {
- if (found)
- smack[i] = '\0';
- else if (i >= len || string[i] > '~' || string[i] <= ' ' ||
- string[i] == '/' || string[i] == '"' ||
- string[i] == '\\' || string[i] == '\'') {
- smack[i] = '\0';
- found = 1;
- } else
- smack[i] = string[i];
+ if (len <= 0)
+ len = strlen(string) + 1;
+
+ /*
+ * Reserve a leading '-' as an indicator that
+ * this isn't a label, but an option to interfaces
+ * including /smack/cipso and /smack/cipso2
+ */
+ if (string[0] == '-')
+ return NULL;
+
+ for (i = 0; i < len; i++)
+ if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
+ string[i] == '"' || string[i] == '\\' || string[i] == '\'')
+ break;
+
+ if (i == 0 || i >= SMK_LONGLABEL)
+ return NULL;
+
+ smack = kzalloc(i + 1, GFP_KERNEL);
+ if (smack != NULL) {
+ strncpy(smack, string, i + 1);
+ smack[i] = '\0';
}
+ return smack;
+}
+
+/**
+ * smk_netlbl_mls - convert a catset to netlabel mls categories
+ * @catset: the Smack categories
+ * @sap: where to put the netlabel categories
+ *
+ * Allocates and fills attr.mls
+ * Returns 0 on success, error code on failure.
+ */
+int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
+ int len)
+{
+ unsigned char *cp;
+ unsigned char m;
+ int cat;
+ int rc;
+ int byte;
+
+ sap->flags |= NETLBL_SECATTR_MLS_CAT;
+ sap->attr.mls.lvl = level;
+ sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
+ sap->attr.mls.cat->startbit = 0;
+
+ for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++)
+ for (m = 0x80; m != 0; m >>= 1, cat++) {
+ if ((m & *cp) == 0)
+ continue;
+ rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat,
+ cat, GFP_ATOMIC);
+ if (rc < 0) {
+ netlbl_secattr_catmap_free(sap->attr.mls.cat);
+ return rc;
+ }
+ }
+
+ return 0;
}
/**
@@ -390,33 +433,59 @@ void smk_parse_smack(const char *string, int len, char *smack)
struct smack_known *smk_import_entry(const char *string, int len)
{
struct smack_known *skp;
- char smack[SMK_LABELLEN];
+ char *smack;
+ int slen;
+ int rc;
- smk_parse_smack(string, len, smack);
- if (smack[0] == '\0')
+ smack = smk_parse_smack(string, len);
+ if (smack == NULL)
return NULL;
mutex_lock(&smack_known_lock);
skp = smk_find_entry(smack);
+ if (skp != NULL)
+ goto freeout;
- if (skp == NULL) {
- skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
- if (skp != NULL) {
- strncpy(skp->smk_known, smack, SMK_MAXLEN);
- skp->smk_secid = smack_next_secid++;
- skp->smk_cipso = NULL;
- INIT_LIST_HEAD(&skp->smk_rules);
- spin_lock_init(&skp->smk_cipsolock);
- mutex_init(&skp->smk_rules_lock);
- /*
- * Make sure that the entry is actually
- * filled before putting it on the list.
- */
- list_add_rcu(&skp->list, &smack_known_list);
- }
- }
+ skp = kzalloc(sizeof(*skp), GFP_KERNEL);
+ if (skp == NULL)
+ goto freeout;
+ skp->smk_known = smack;
+ skp->smk_secid = smack_next_secid++;
+ skp->smk_netlabel.domain = skp->smk_known;
+ skp->smk_netlabel.flags =
+ NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
+ /*
+ * If direct labeling works use it.
+ * Otherwise use mapped labeling.
+ */
+ slen = strlen(smack);
+ if (slen < SMK_CIPSOLEN)
+ rc = smk_netlbl_mls(smack_cipso_direct, skp->smk_known,
+ &skp->smk_netlabel, slen);
+ else
+ rc = smk_netlbl_mls(smack_cipso_mapped, (char *)&skp->smk_secid,
+ &skp->smk_netlabel, sizeof(skp->smk_secid));
+
+ if (rc >= 0) {
+ INIT_LIST_HEAD(&skp->smk_rules);
+ mutex_init(&skp->smk_rules_lock);
+ /*
+ * Make sure that the entry is actually
+ * filled before putting it on the list.
+ */
+ list_add_rcu(&skp->list, &smack_known_list);
+ goto unlockout;
+ }
+ /*
+ * smk_netlbl_mls failed.
+ */
+ kfree(skp);
+ skp = NULL;
+freeout:
+ kfree(smack);
+unlockout:
mutex_unlock(&smack_known_lock);
return skp;
@@ -479,79 +548,9 @@ char *smack_from_secid(const u32 secid)
*/
u32 smack_to_secid(const char *smack)
{
- struct smack_known *skp;
+ struct smack_known *skp = smk_find_entry(smack);
- rcu_read_lock();
- list_for_each_entry_rcu(skp, &smack_known_list, list) {
- if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
- rcu_read_unlock();
- return skp->smk_secid;
- }
- }
- rcu_read_unlock();
- return 0;
-}
-
-/**
- * smack_from_cipso - find the Smack label associated with a CIPSO option
- * @level: Bell & LaPadula level from the network
- * @cp: Bell & LaPadula categories from the network
- *
- * This is a simple lookup in the label table.
- *
- * Return the matching label from the label list or NULL.
- */
-char *smack_from_cipso(u32 level, char *cp)
-{
- struct smack_known *kp;
- char *final = NULL;
-
- rcu_read_lock();
- list_for_each_entry(kp, &smack_known_list, list) {
- if (kp->smk_cipso == NULL)
- continue;
-
- spin_lock_bh(&kp->smk_cipsolock);
-
- if (kp->smk_cipso->smk_level == level &&
- memcmp(kp->smk_cipso->smk_catset, cp, SMK_LABELLEN) == 0)
- final = kp->smk_known;
-
- spin_unlock_bh(&kp->smk_cipsolock);
-
- if (final != NULL)
- break;
- }
- rcu_read_unlock();
-
- return final;
-}
-
-/**
- * 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 char *smack, struct smack_cipso *cp)
-{
- struct smack_known *kp;
- int found = 0;
-
- rcu_read_lock();
- list_for_each_entry_rcu(kp, &smack_known_list, list) {
- if (kp->smk_known == smack ||
- strcmp(kp->smk_known, smack) == 0) {
- found = 1;
- break;
- }
- }
- rcu_read_unlock();
-
- if (found == 0 || kp->smk_cipso == NULL)
- return -ENOENT;
-
- memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso));
- return 0;
+ if (skp == NULL)
+ return 0;
+ return skp->smk_secid;
}
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 8ef0199ebca..d583c054580 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -30,7 +30,6 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/pipe_fs_i.h>
-#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <linux/audit.h>
#include <linux/magic.h>
@@ -57,16 +56,23 @@
static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp)
{
int rc;
- char in[SMK_LABELLEN];
+ char *buffer;
+ char *result = NULL;
if (ip->i_op->getxattr == NULL)
return NULL;
- rc = ip->i_op->getxattr(dp, name, in, SMK_LABELLEN);
- if (rc < 0)
+ buffer = kzalloc(SMK_LONGLABEL, GFP_KERNEL);
+ if (buffer == NULL)
return NULL;
- return smk_import(in, rc);
+ rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL);
+ if (rc > 0)
+ result = smk_import(buffer, rc);
+
+ kfree(buffer);
+
+ return result;
}
/**
@@ -79,7 +85,7 @@ struct inode_smack *new_inode_smack(char *smack)
{
struct inode_smack *isp;
- isp = kzalloc(sizeof(struct inode_smack), GFP_KERNEL);
+ isp = kzalloc(sizeof(struct inode_smack), GFP_NOFS);
if (isp == NULL)
return NULL;
@@ -556,13 +562,14 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
void **value, size_t *len)
{
struct smack_known *skp;
+ struct inode_smack *issp = inode->i_security;
char *csp = smk_of_current();
char *isp = smk_of_inode(inode);
char *dsp = smk_of_inode(dir);
int may;
if (name) {
- *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL);
+ *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_NOFS);
if (*name == NULL)
return -ENOMEM;
}
@@ -577,12 +584,15 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
* If the access rule allows transmutation and
* the directory requests transmutation then
* by all means transmute.
+ * Mark the inode as changed.
*/
if (may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
- smk_inode_transmutable(dir))
+ smk_inode_transmutable(dir)) {
isp = dsp;
+ issp->smk_flags |= SMK_INODE_CHANGED;
+ }
- *value = kstrdup(isp, GFP_KERNEL);
+ *value = kstrdup(isp, GFP_NOFS);
if (*value == NULL)
return -ENOMEM;
}
@@ -821,7 +831,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
* check label validity here so import wont fail on
* post_setxattr
*/
- if (size == 0 || size >= SMK_LABELLEN ||
+ if (size == 0 || size >= SMK_LONGLABEL ||
smk_import(value, size) == NULL)
rc = -EINVAL;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
@@ -1820,65 +1830,6 @@ static char *smack_host_label(struct sockaddr_in *sip)
}
/**
- * smack_set_catset - convert a capset to netlabel mls categories
- * @catset: the Smack categories
- * @sap: where to put the netlabel categories
- *
- * Allocates and fills attr.mls.cat
- */
-static void smack_set_catset(char *catset, struct netlbl_lsm_secattr *sap)
-{
- unsigned char *cp;
- unsigned char m;
- int cat;
- int rc;
- int byte;
-
- if (!catset)
- return;
-
- sap->flags |= NETLBL_SECATTR_MLS_CAT;
- sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
- sap->attr.mls.cat->startbit = 0;
-
- for (cat = 1, cp = catset, byte = 0; byte < SMK_LABELLEN; cp++, byte++)
- for (m = 0x80; m != 0; m >>= 1, cat++) {
- if ((m & *cp) == 0)
- continue;
- rc = netlbl_secattr_catmap_setbit(sap->attr.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.
- * Apologies to the TSIG group in general and GW in particular.
- */
-static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
-{
- struct smack_cipso cipso;
- int rc;
-
- nlsp->domain = smack;
- nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
-
- rc = smack_to_cipso(smack, &cipso);
- if (rc == 0) {
- nlsp->attr.mls.lvl = cipso.smk_level;
- smack_set_catset(cipso.smk_catset, nlsp);
- } else {
- nlsp->attr.mls.lvl = smack_cipso_direct;
- smack_set_catset(smack, nlsp);
- }
-}
-
-/**
* smack_netlabel - Set the secattr on a socket
* @sk: the socket
* @labeled: socket label scheme
@@ -1890,8 +1841,8 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
*/
static int smack_netlabel(struct sock *sk, int labeled)
{
+ struct smack_known *skp;
struct socket_smack *ssp = sk->sk_security;
- struct netlbl_lsm_secattr secattr;
int rc = 0;
/*
@@ -1909,10 +1860,8 @@ static int smack_netlabel(struct sock *sk, int labeled)
labeled == SMACK_UNLABELED_SOCKET)
netlbl_sock_delattr(sk);
else {
- netlbl_secattr_init(&secattr);
- smack_to_secattr(ssp->smk_out, &secattr);
- rc = netlbl_sock_setattr(sk, sk->sk_family, &secattr);
- netlbl_secattr_destroy(&secattr);
+ skp = smk_find_entry(ssp->smk_out);
+ rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel);
}
bh_unlock_sock(sk);
@@ -1939,18 +1888,19 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
char *hostsp;
struct socket_smack *ssp = sk->sk_security;
struct smk_audit_info ad;
- struct lsm_network_audit net;
rcu_read_lock();
hostsp = smack_host_label(sap);
if (hostsp != NULL) {
- sk_lbl = SMACK_UNLABELED_SOCKET;
#ifdef CONFIG_AUDIT
+ struct lsm_network_audit net;
+
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
ad.a.u.net->family = sap->sin_family;
ad.a.u.net->dport = sap->sin_port;
ad.a.u.net->v4info.daddr = sap->sin_addr.s_addr;
#endif
+ sk_lbl = SMACK_UNLABELED_SOCKET;
rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE, &ad);
} else {
sk_lbl = SMACK_CIPSO_SOCKET;
@@ -1984,7 +1934,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
struct socket *sock;
int rc = 0;
- if (value == NULL || size > SMK_LABELLEN || size == 0)
+ if (value == NULL || size > SMK_LONGLABEL || size == 0)
return -EACCES;
sp = smk_import(value, size);
@@ -2551,6 +2501,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
char *final;
char trattr[TRANS_TRUE_SIZE];
int transflag = 0;
+ int rc;
struct dentry *dp;
if (inode == NULL)
@@ -2669,17 +2620,38 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
dp = dget(opt_dentry);
fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp);
- if (fetched != NULL) {
+ if (fetched != NULL)
final = fetched;
- if (S_ISDIR(inode->i_mode)) {
- trattr[0] = '\0';
- inode->i_op->getxattr(dp,
+
+ /*
+ * Transmuting directory
+ */
+ if (S_ISDIR(inode->i_mode)) {
+ /*
+ * If this is a new directory and the label was
+ * transmuted when the inode was initialized
+ * set the transmute attribute on the directory
+ * and mark the inode.
+ *
+ * If there is a transmute attribute on the
+ * directory mark the inode.
+ */
+ if (isp->smk_flags & SMK_INODE_CHANGED) {
+ isp->smk_flags &= ~SMK_INODE_CHANGED;
+ rc = inode->i_op->setxattr(dp,
XATTR_NAME_SMACKTRANSMUTE,
- trattr, TRANS_TRUE_SIZE);
- if (strncmp(trattr, TRANS_TRUE,
- TRANS_TRUE_SIZE) == 0)
- transflag = SMK_INODE_TRANSMUTE;
+ TRANS_TRUE, TRANS_TRUE_SIZE,
+ 0);
+ } else {
+ rc = inode->i_op->getxattr(dp,
+ XATTR_NAME_SMACKTRANSMUTE, trattr,
+ TRANS_TRUE_SIZE);
+ if (rc >= 0 && strncmp(trattr, TRANS_TRUE,
+ TRANS_TRUE_SIZE) != 0)
+ rc = -EINVAL;
}
+ if (rc >= 0)
+ transflag = SMK_INODE_TRANSMUTE;
}
isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp);
@@ -2758,7 +2730,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
- if (value == NULL || size == 0 || size >= SMK_LABELLEN)
+ if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
return -EINVAL;
if (strcmp(name, "current") != 0)
@@ -2809,11 +2781,14 @@ static int smack_unix_stream_connect(struct sock *sock,
struct socket_smack *osp = other->sk_security;
struct socket_smack *nsp = newsk->sk_security;
struct smk_audit_info ad;
- struct lsm_network_audit net;
int rc = 0;
+#ifdef CONFIG_AUDIT
+ struct lsm_network_audit net;
+
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
smk_ad_setfield_u_net_sk(&ad, other);
+#endif
if (!capable(CAP_MAC_OVERRIDE))
rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
@@ -2842,11 +2817,14 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
struct socket_smack *ssp = sock->sk->sk_security;
struct socket_smack *osp = other->sk->sk_security;
struct smk_audit_info ad;
- struct lsm_network_audit net;
int rc = 0;
+#ifdef CONFIG_AUDIT
+ struct lsm_network_audit net;
+
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
smk_ad_setfield_u_net_sk(&ad, other->sk);
+#endif
if (!capable(CAP_MAC_OVERRIDE))
rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
@@ -2888,10 +2866,9 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
static char *smack_from_secattr(struct netlbl_lsm_secattr *sap,
struct socket_smack *ssp)
{
- struct smack_known *skp;
- char smack[SMK_LABELLEN];
+ struct smack_known *kp;
char *sp;
- int pcat;
+ int found = 0;
if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {
/*
@@ -2899,59 +2876,27 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap,
* If there are flags but no level netlabel isn't
* behaving the way we expect it to.
*
- * Get the categories, if any
+ * Look it up in the label table
* Without guidance regarding the smack value
* for the packet fall back on the network
* ambient value.
*/
- memset(smack, '\0', SMK_LABELLEN);
- if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
- for (pcat = -1;;) {
- pcat = netlbl_secattr_catmap_walk(
- sap->attr.mls.cat, pcat + 1);
- if (pcat < 0)
- break;
- smack_catset_bit(pcat, smack);
- }
- /*
- * If it is CIPSO using smack direct mapping
- * we are already done. WeeHee.
- */
- if (sap->attr.mls.lvl == smack_cipso_direct) {
- /*
- * The label sent is usually on the label list.
- *
- * If it is not we may still want to allow the
- * delivery.
- *
- * If the recipient is accepting all packets
- * because it is using the star ("*") label
- * for SMACK64IPIN provide the web ("@") label
- * so that a directed response will succeed.
- * This is not very correct from a MAC point
- * of view, but gets around the problem that
- * locking prevents adding the newly discovered
- * label to the list.
- * The case where the recipient is not using
- * the star label should obviously fail.
- * The easy way to do this is to provide the
- * star label as the subject label.
- */
- skp = smk_find_entry(smack);
- if (skp != NULL)
- return skp->smk_known;
- if (ssp != NULL &&
- ssp->smk_in == smack_known_star.smk_known)
- return smack_known_web.smk_known;
- return smack_known_star.smk_known;
+ rcu_read_lock();
+ list_for_each_entry(kp, &smack_known_list, list) {
+ if (sap->attr.mls.lvl != kp->smk_netlabel.attr.mls.lvl)
+ continue;
+ if (memcmp(sap->attr.mls.cat,
+ kp->smk_netlabel.attr.mls.cat,
+ SMK_CIPSOLEN) != 0)
+ continue;
+ found = 1;
+ break;
}
- /*
- * Look it up in the supplied table if it is not
- * a direct mapping.
- */
- sp = smack_from_cipso(sap->attr.mls.lvl, smack);
- if (sp != NULL)
- return sp;
+ rcu_read_unlock();
+
+ if (found)
+ return kp->smk_known;
+
if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known)
return smack_known_web.smk_known;
return smack_known_star.smk_known;
@@ -2993,7 +2938,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
char *csp;
int rc;
struct smk_audit_info ad;
+#ifdef CONFIG_AUDIT
struct lsm_network_audit net;
+#endif
if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
return 0;
@@ -3149,14 +3096,18 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
u16 family = sk->sk_family;
+ struct smack_known *skp;
struct socket_smack *ssp = sk->sk_security;
struct netlbl_lsm_secattr secattr;
struct sockaddr_in addr;
struct iphdr *hdr;
char *sp;
+ char *hsp;
int rc;
struct smk_audit_info ad;
+#ifdef CONFIG_AUDIT
struct lsm_network_audit net;
+#endif
/* handle mapped IPv4 packets arriving via IPv6 sockets */
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
@@ -3198,16 +3149,14 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
hdr = ip_hdr(skb);
addr.sin_addr.s_addr = hdr->saddr;
rcu_read_lock();
- if (smack_host_label(&addr) == NULL) {
- rcu_read_unlock();
- netlbl_secattr_init(&secattr);
- smack_to_secattr(sp, &secattr);
- rc = netlbl_req_setattr(req, &secattr);
- netlbl_secattr_destroy(&secattr);
- } else {
- rcu_read_unlock();
+ hsp = smack_host_label(&addr);
+ rcu_read_unlock();
+
+ if (hsp == NULL) {
+ skp = smk_find_entry(sp);
+ rc = netlbl_req_setattr(req, &skp->smk_netlabel);
+ } else
netlbl_req_delattr(req);
- }
return rc;
}
@@ -3389,7 +3338,7 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule,
char *rule = vrule;
if (!rule) {
- audit_log(actx, GFP_KERNEL, AUDIT_SELINUX_ERR,
+ audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"Smack: missing rule\n");
return -ENOENT;
}
@@ -3629,8 +3578,29 @@ struct security_operations smack_ops = {
};
-static __init void init_smack_know_list(void)
+static __init void init_smack_known_list(void)
{
+ /*
+ * Initialize rule list locks
+ */
+ mutex_init(&smack_known_huh.smk_rules_lock);
+ mutex_init(&smack_known_hat.smk_rules_lock);
+ mutex_init(&smack_known_floor.smk_rules_lock);
+ mutex_init(&smack_known_star.smk_rules_lock);
+ mutex_init(&smack_known_invalid.smk_rules_lock);
+ mutex_init(&smack_known_web.smk_rules_lock);
+ /*
+ * Initialize rule lists
+ */
+ INIT_LIST_HEAD(&smack_known_huh.smk_rules);
+ INIT_LIST_HEAD(&smack_known_hat.smk_rules);
+ INIT_LIST_HEAD(&smack_known_star.smk_rules);
+ INIT_LIST_HEAD(&smack_known_floor.smk_rules);
+ INIT_LIST_HEAD(&smack_known_invalid.smk_rules);
+ INIT_LIST_HEAD(&smack_known_web.smk_rules);
+ /*
+ * Create the known labels list
+ */
list_add(&smack_known_huh.list, &smack_known_list);
list_add(&smack_known_hat.list, &smack_known_list);
list_add(&smack_known_star.list, &smack_known_list);
@@ -3665,16 +3635,8 @@ static __init int smack_init(void)
cred = (struct cred *) current->cred;
cred->security = tsp;
- /* initialize the smack_know_list */
- init_smack_know_list();
- /*
- * Initialize locks
- */
- spin_lock_init(&smack_known_huh.smk_cipsolock);
- spin_lock_init(&smack_known_hat.smk_cipsolock);
- spin_lock_init(&smack_known_star.smk_cipsolock);
- spin_lock_init(&smack_known_floor.smk_cipsolock);
- spin_lock_init(&smack_known_invalid.smk_cipsolock);
+ /* initialize the smack_known_list */
+ init_smack_known_list();
/*
* Register with LSM
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 5c32f36ff70..1810c9a4ed4 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -22,7 +22,6 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <net/net_namespace.h>
-#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <linux/seq_file.h>
#include <linux/ctype.h>
@@ -45,6 +44,11 @@ enum smk_inos {
SMK_LOGGING = 10, /* logging */
SMK_LOAD_SELF = 11, /* task specific rules */
SMK_ACCESSES = 12, /* access policy */
+ SMK_MAPPED = 13, /* CIPSO level indicating mapped label */
+ SMK_LOAD2 = 14, /* load policy with long labels */
+ SMK_LOAD_SELF2 = 15, /* load task specific rules with long labels */
+ SMK_ACCESS2 = 16, /* make an access check with long labels */
+ SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */
};
/*
@@ -60,7 +64,7 @@ static DEFINE_MUTEX(smk_netlbladdr_lock);
* If it isn't somehow marked, use this.
* It can be reset via smackfs/ambient
*/
-char *smack_net_ambient = smack_known_floor.smk_known;
+char *smack_net_ambient;
/*
* This is the level in a CIPSO header that indicates a
@@ -70,6 +74,13 @@ char *smack_net_ambient = smack_known_floor.smk_known;
int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
/*
+ * This is the level in a CIPSO header that indicates a
+ * secid is contained directly in the category set.
+ * It can be reset via smackfs/mapped
+ */
+int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
+
+/*
* Unless a process is running with this label even
* having CAP_MAC_OVERRIDE isn't enough to grant
* privilege to violate MAC policy. If no label is
@@ -89,7 +100,7 @@ LIST_HEAD(smk_netlbladdr_list);
/*
* Rule lists are maintained for each label.
- * This master list is just for reading /smack/load.
+ * This master list is just for reading /smack/load and /smack/load2.
*/
struct smack_master_list {
struct list_head list;
@@ -125,6 +136,18 @@ const char *smack_cipso_option = SMACK_CIPSO_OPTION;
#define SMK_OLOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN)
#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
+/*
+ * Stricly for CIPSO level manipulation.
+ * Set the category bit number in a smack label sized buffer.
+ */
+static inline void smack_catset_bit(unsigned int cat, char *catsetp)
+{
+ if (cat == 0 || cat > (SMK_CIPSOLEN * 8))
+ return;
+
+ catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8);
+}
+
/**
* smk_netlabel_audit_set - fill a netlbl_audit struct
* @nap: structure to fill
@@ -137,12 +160,10 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap)
}
/*
- * Values for parsing single label host rules
+ * Value for parsing single label host rules
* "1.2.3.4 X"
- * "192.168.138.129/32 abcdefghijklmnopqrstuvw"
*/
#define SMK_NETLBLADDRMIN 9
-#define SMK_NETLBLADDRMAX 42
/**
* smk_set_access - add a rule to the rule list
@@ -188,33 +209,47 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
}
/**
- * smk_parse_rule - parse Smack rule from load string
- * @data: string to be parsed whose size is SMK_LOADLEN
+ * smk_fill_rule - Fill Smack rule from strings
+ * @subject: subject label string
+ * @object: object label string
+ * @access: access string
* @rule: Smack rule
* @import: if non-zero, import labels
+ *
+ * Returns 0 on success, -1 on failure
*/
-static int smk_parse_rule(const char *data, struct smack_rule *rule, int import)
+static int smk_fill_rule(const char *subject, const char *object,
+ const char *access, struct smack_rule *rule,
+ int import)
{
- char smack[SMK_LABELLEN];
+ int rc = -1;
+ int done;
+ const char *cp;
struct smack_known *skp;
if (import) {
- rule->smk_subject = smk_import(data, 0);
+ rule->smk_subject = smk_import(subject, 0);
if (rule->smk_subject == NULL)
return -1;
- rule->smk_object = smk_import(data + SMK_LABELLEN, 0);
+ rule->smk_object = smk_import(object, 0);
if (rule->smk_object == NULL)
return -1;
} else {
- smk_parse_smack(data, 0, smack);
- skp = smk_find_entry(smack);
+ cp = smk_parse_smack(subject, 0);
+ if (cp == NULL)
+ return -1;
+ skp = smk_find_entry(cp);
+ kfree(cp);
if (skp == NULL)
return -1;
rule->smk_subject = skp->smk_known;
- smk_parse_smack(data + SMK_LABELLEN, 0, smack);
- skp = smk_find_entry(smack);
+ cp = smk_parse_smack(object, 0);
+ if (cp == NULL)
+ return -1;
+ skp = smk_find_entry(cp);
+ kfree(cp);
if (skp == NULL)
return -1;
rule->smk_object = skp->smk_known;
@@ -222,90 +257,127 @@ static int smk_parse_rule(const char *data, struct smack_rule *rule, int import)
rule->smk_access = 0;
- switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
- case '-':
- break;
- case 'r':
- case 'R':
- rule->smk_access |= MAY_READ;
- break;
- default:
- return -1;
+ for (cp = access, done = 0; *cp && !done; cp++) {
+ switch (*cp) {
+ case '-':
+ break;
+ case 'r':
+ case 'R':
+ rule->smk_access |= MAY_READ;
+ break;
+ case 'w':
+ case 'W':
+ rule->smk_access |= MAY_WRITE;
+ break;
+ case 'x':
+ case 'X':
+ rule->smk_access |= MAY_EXEC;
+ break;
+ case 'a':
+ case 'A':
+ rule->smk_access |= MAY_APPEND;
+ break;
+ case 't':
+ case 'T':
+ rule->smk_access |= MAY_TRANSMUTE;
+ break;
+ default:
+ done = 1;
+ break;
+ }
}
+ rc = 0;
- switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
- case '-':
- break;
- case 'w':
- case 'W':
- rule->smk_access |= MAY_WRITE;
- break;
- default:
- return -1;
- }
+ return rc;
+}
- switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
- case '-':
- break;
- case 'x':
- case 'X':
- rule->smk_access |= MAY_EXEC;
- break;
- default:
- return -1;
- }
+/**
+ * smk_parse_rule - parse Smack rule from load string
+ * @data: string to be parsed whose size is SMK_LOADLEN
+ * @rule: Smack rule
+ * @import: if non-zero, import labels
+ *
+ * Returns 0 on success, -1 on errors.
+ */
+static int smk_parse_rule(const char *data, struct smack_rule *rule, int import)
+{
+ int rc;
- switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
- case '-':
- break;
- case 'a':
- case 'A':
- rule->smk_access |= MAY_APPEND;
- break;
- default:
- return -1;
- }
+ rc = smk_fill_rule(data, data + SMK_LABELLEN,
+ data + SMK_LABELLEN + SMK_LABELLEN, rule, import);
+ return rc;
+}
- switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
- case '-':
- break;
- case 't':
- case 'T':
- rule->smk_access |= MAY_TRANSMUTE;
- break;
- default:
- return -1;
- }
+/**
+ * smk_parse_long_rule - parse Smack rule from rule string
+ * @data: string to be parsed, null terminated
+ * @rule: Smack rule
+ * @import: if non-zero, import labels
+ *
+ * Returns 0 on success, -1 on failure
+ */
+static int smk_parse_long_rule(const char *data, struct smack_rule *rule,
+ int import)
+{
+ char *subject;
+ char *object;
+ char *access;
+ int datalen;
+ int rc = -1;
- return 0;
+ /*
+ * This is probably inefficient, but safe.
+ */
+ datalen = strlen(data);
+ subject = kzalloc(datalen, GFP_KERNEL);
+ if (subject == NULL)
+ return -1;
+ object = kzalloc(datalen, GFP_KERNEL);
+ if (object == NULL)
+ goto free_out_s;
+ access = kzalloc(datalen, GFP_KERNEL);
+ if (access == NULL)
+ goto free_out_o;
+
+ if (sscanf(data, "%s %s %s", subject, object, access) == 3)
+ rc = smk_fill_rule(subject, object, access, rule, import);
+
+ kfree(access);
+free_out_o:
+ kfree(object);
+free_out_s:
+ kfree(subject);
+ return rc;
}
+#define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */
+#define SMK_LONG_FMT 1 /* Variable long label format */
/**
- * smk_write_load_list - write() for any /smack/load
+ * smk_write_rules_list - write() for any /smack rule file
* @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start - must be 0
* @rule_list: the list of rules to write to
* @rule_lock: lock for the rule list
+ * @format: /smack/load or /smack/load2 format.
*
* Get one smack access rule from above.
- * The format is exactly:
- * char subject[SMK_LABELLEN]
- * char object[SMK_LABELLEN]
- * char access[SMK_ACCESSLEN]
- *
- * writes must be SMK_LABELLEN+SMK_LABELLEN+SMK_ACCESSLEN bytes.
+ * The format for SMK_LONG_FMT is:
+ * "subject<whitespace>object<whitespace>access[<whitespace>...]"
+ * The format for SMK_FIXED24_FMT is exactly:
+ * "subject object rwxat"
*/
-static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos,
- struct list_head *rule_list,
- struct mutex *rule_lock)
+static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos,
+ struct list_head *rule_list,
+ struct mutex *rule_lock, int format)
{
struct smack_master_list *smlp;
struct smack_known *skp;
struct smack_rule *rule;
char *data;
+ int datalen;
int rc = -EINVAL;
int load = 0;
@@ -315,13 +387,18 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
*/
if (*ppos != 0)
return -EINVAL;
- /*
- * Minor hack for backward compatibility
- */
- if (count < (SMK_OLOADLEN) || count > SMK_LOADLEN)
- return -EINVAL;
- data = kzalloc(SMK_LOADLEN, GFP_KERNEL);
+ if (format == SMK_FIXED24_FMT) {
+ /*
+ * Minor hack for backward compatibility
+ */
+ if (count != SMK_OLOADLEN && count != SMK_LOADLEN)
+ return -EINVAL;
+ datalen = SMK_LOADLEN;
+ } else
+ datalen = count + 1;
+
+ data = kzalloc(datalen, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -330,20 +407,29 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
goto out;
}
- /*
- * More on the minor hack for backward compatibility
- */
- if (count == (SMK_OLOADLEN))
- data[SMK_OLOADLEN] = '-';
-
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (rule == NULL) {
rc = -ENOMEM;
goto out;
}
- if (smk_parse_rule(data, rule, 1))
- goto out_free_rule;
+ if (format == SMK_LONG_FMT) {
+ /*
+ * Be sure the data string is terminated.
+ */
+ data[count] = '\0';
+ if (smk_parse_long_rule(data, rule, 1))
+ goto out_free_rule;
+ } else {
+ /*
+ * More on the minor hack for backward compatibility
+ */
+ if (count == (SMK_OLOADLEN))
+ data[SMK_OLOADLEN] = '-';
+ if (smk_parse_rule(data, rule, 1))
+ goto out_free_rule;
+ }
+
if (rule_list == NULL) {
load = 1;
@@ -354,18 +440,20 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
rc = count;
/*
- * If this is "load" as opposed to "load-self" and a new rule
+ * If this is a global as opposed to self and a new rule
* it needs to get added for reporting.
* smk_set_access returns true if there was already a rule
* for the subject/object pair, and false if it was new.
*/
- if (load && !smk_set_access(rule, rule_list, rule_lock)) {
- smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
- if (smlp != NULL) {
- smlp->smk_rule = rule;
- list_add_rcu(&smlp->list, &smack_rule_list);
- } else
- rc = -ENOMEM;
+ if (!smk_set_access(rule, rule_list, rule_lock)) {
+ if (load) {
+ smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
+ if (smlp != NULL) {
+ smlp->smk_rule = rule;
+ list_add_rcu(&smlp->list, &smack_rule_list);
+ } else
+ rc = -ENOMEM;
+ }
goto out;
}
@@ -421,29 +509,18 @@ static void smk_seq_stop(struct seq_file *s, void *v)
/* No-op */
}
-/*
- * Seq_file read operations for /smack/load
- */
-
-static void *load_seq_start(struct seq_file *s, loff_t *pos)
-{
- return smk_seq_start(s, pos, &smack_rule_list);
-}
-
-static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
+static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
{
- return smk_seq_next(s, v, pos, &smack_rule_list);
-}
-
-static int load_seq_show(struct seq_file *s, void *v)
-{
- struct list_head *list = v;
- struct smack_master_list *smlp =
- list_entry(list, struct smack_master_list, list);
- struct smack_rule *srp = smlp->smk_rule;
+ /*
+ * Don't show any rules with label names too long for
+ * interface file (/smack/load or /smack/load2)
+ * because you should expect to be able to write
+ * anything you read back.
+ */
+ if (strlen(srp->smk_subject) >= max || strlen(srp->smk_object) >= max)
+ return;
- seq_printf(s, "%s %s", (char *)srp->smk_subject,
- (char *)srp->smk_object);
+ seq_printf(s, "%s %s", srp->smk_subject, srp->smk_object);
seq_putc(s, ' ');
@@ -461,13 +538,36 @@ static int load_seq_show(struct seq_file *s, void *v)
seq_putc(s, '-');
seq_putc(s, '\n');
+}
+
+/*
+ * Seq_file read operations for /smack/load
+ */
+
+static void *load2_seq_start(struct seq_file *s, loff_t *pos)
+{
+ return smk_seq_start(s, pos, &smack_rule_list);
+}
+
+static void *load2_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ return smk_seq_next(s, v, pos, &smack_rule_list);
+}
+
+static int load_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smack_master_list *smlp =
+ list_entry(list, struct smack_master_list, list);
+
+ smk_rule_show(s, smlp->smk_rule, SMK_LABELLEN);
return 0;
}
static const struct seq_operations load_seq_ops = {
- .start = load_seq_start,
- .next = load_seq_next,
+ .start = load2_seq_start,
+ .next = load2_seq_next,
.show = load_seq_show,
.stop = smk_seq_stop,
};
@@ -504,7 +604,8 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
- return smk_write_load_list(file, buf, count, ppos, NULL, NULL);
+ return smk_write_rules_list(file, buf, count, ppos, NULL, NULL,
+ SMK_FIXED24_FMT);
}
static const struct file_operations smk_load_ops = {
@@ -574,6 +675,8 @@ static void smk_unlbl_ambient(char *oldambient)
printk(KERN_WARNING "%s:%d remove rc = %d\n",
__func__, __LINE__, rc);
}
+ if (smack_net_ambient == NULL)
+ smack_net_ambient = smack_known_floor.smk_known;
rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET,
NULL, NULL, &nai);
@@ -605,27 +708,28 @@ static int cipso_seq_show(struct seq_file *s, void *v)
struct list_head *list = v;
struct smack_known *skp =
list_entry(list, struct smack_known, list);
- struct smack_cipso *scp = skp->smk_cipso;
- char *cbp;
+ struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
char sep = '/';
- int cat = 1;
int i;
- unsigned char m;
- if (scp == NULL)
+ /*
+ * Don't show a label that could not have been set using
+ * /smack/cipso. This is in support of the notion that
+ * anything read from /smack/cipso ought to be writeable
+ * to /smack/cipso.
+ *
+ * /smack/cipso2 should be used instead.
+ */
+ if (strlen(skp->smk_known) >= SMK_LABELLEN)
return 0;
- seq_printf(s, "%s %3d", (char *)&skp->smk_known, scp->smk_level);
+ seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl);
- cbp = scp->smk_catset;
- for (i = 0; i < SMK_LABELLEN; i++)
- for (m = 0x80; m != 0; m >>= 1) {
- if (m & cbp[i]) {
- seq_printf(s, "%c%d", sep, cat);
- sep = ',';
- }
- cat++;
- }
+ for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0;
+ i = netlbl_secattr_catmap_walk(cmp, i + 1)) {
+ seq_printf(s, "%c%d", sep, i);
+ sep = ',';
+ }
seq_putc(s, '\n');
@@ -653,23 +757,24 @@ static int smk_open_cipso(struct inode *inode, struct file *file)
}
/**
- * smk_write_cipso - write() for /smack/cipso
+ * smk_set_cipso - do the work for write() for cipso and cipso2
* @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
+ * @format: /smack/cipso or /smack/cipso2
*
* Accepts only one cipso rule per write call.
* 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)
+static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos, int format)
{
struct smack_known *skp;
- struct smack_cipso *scp = NULL;
- char mapcatset[SMK_LABELLEN];
+ struct netlbl_lsm_secattr ncats;
+ char mapcatset[SMK_CIPSOLEN];
int maplevel;
- int cat;
+ unsigned int cat;
int catlen;
ssize_t rc = -EINVAL;
char *data = NULL;
@@ -686,7 +791,8 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf,
return -EPERM;
if (*ppos != 0)
return -EINVAL;
- if (count < SMK_CIPSOMIN || count > SMK_CIPSOMAX)
+ if (format == SMK_FIXED24_FMT &&
+ (count < SMK_CIPSOMIN || count > SMK_CIPSOMAX))
return -EINVAL;
data = kzalloc(count + 1, GFP_KERNEL);
@@ -698,11 +804,6 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf,
goto unlockedout;
}
- /* labels cannot begin with a '-' */
- if (data[0] == '-') {
- rc = -EINVAL;
- goto unlockedout;
- }
data[count] = '\0';
rule = data;
/*
@@ -715,7 +816,11 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf,
if (skp == NULL)
goto out;
- rule += SMK_LABELLEN;
+ if (format == SMK_FIXED24_FMT)
+ rule += SMK_LABELLEN;
+ else
+ rule += strlen(skp->smk_known);
+
ret = sscanf(rule, "%d", &maplevel);
if (ret != 1 || maplevel > SMACK_CIPSO_MAXLEVEL)
goto out;
@@ -725,41 +830,29 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf,
if (ret != 1 || catlen > SMACK_CIPSO_MAXCATNUM)
goto out;
- if (count != (SMK_CIPSOMIN + catlen * SMK_DIGITLEN))
+ if (format == SMK_FIXED24_FMT &&
+ count != (SMK_CIPSOMIN + catlen * SMK_DIGITLEN))
goto out;
memset(mapcatset, 0, sizeof(mapcatset));
for (i = 0; i < catlen; i++) {
rule += SMK_DIGITLEN;
- ret = sscanf(rule, "%d", &cat);
+ ret = sscanf(rule, "%u", &cat);
if (ret != 1 || cat > SMACK_CIPSO_MAXCATVAL)
goto out;
smack_catset_bit(cat, mapcatset);
}
- if (skp->smk_cipso == NULL) {
- scp = kzalloc(sizeof(struct smack_cipso), GFP_KERNEL);
- if (scp == NULL) {
- rc = -ENOMEM;
- goto out;
- }
+ rc = smk_netlbl_mls(maplevel, mapcatset, &ncats, SMK_CIPSOLEN);
+ if (rc >= 0) {
+ netlbl_secattr_catmap_free(skp->smk_netlabel.attr.mls.cat);
+ skp->smk_netlabel.attr.mls.cat = ncats.attr.mls.cat;
+ skp->smk_netlabel.attr.mls.lvl = ncats.attr.mls.lvl;
+ rc = count;
}
- spin_lock_bh(&skp->smk_cipsolock);
-
- if (scp == NULL)
- scp = skp->smk_cipso;
- else
- skp->smk_cipso = scp;
-
- scp->smk_level = maplevel;
- memcpy(scp->smk_catset, mapcatset, sizeof(mapcatset));
-
- spin_unlock_bh(&skp->smk_cipsolock);
-
- rc = count;
out:
mutex_unlock(&smack_cipso_lock);
unlockedout:
@@ -767,6 +860,22 @@ unlockedout:
return rc;
}
+/**
+ * smk_write_cipso - write() for /smack/cipso
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Accepts only one cipso rule per write call.
+ * 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)
+{
+ return smk_set_cipso(file, buf, count, ppos, SMK_FIXED24_FMT);
+}
+
static const struct file_operations smk_cipso_ops = {
.open = smk_open_cipso,
.read = seq_read,
@@ -776,6 +885,80 @@ static const struct file_operations smk_cipso_ops = {
};
/*
+ * Seq_file read operations for /smack/cipso2
+ */
+
+/*
+ * Print cipso labels in format:
+ * label level[/cat[,cat]]
+ */
+static int cipso2_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smack_known *skp =
+ list_entry(list, struct smack_known, list);
+ struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
+ char sep = '/';
+ int i;
+
+ seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl);
+
+ for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0;
+ i = netlbl_secattr_catmap_walk(cmp, i + 1)) {
+ seq_printf(s, "%c%d", sep, i);
+ sep = ',';
+ }
+
+ seq_putc(s, '\n');
+
+ return 0;
+}
+
+static const struct seq_operations cipso2_seq_ops = {
+ .start = cipso_seq_start,
+ .next = cipso_seq_next,
+ .show = cipso2_seq_show,
+ .stop = smk_seq_stop,
+};
+
+/**
+ * smk_open_cipso2 - open() for /smack/cipso2
+ * @inode: inode structure representing file
+ * @file: "cipso2" file pointer
+ *
+ * Connect our cipso_seq_* operations with /smack/cipso2
+ * file_operations
+ */
+static int smk_open_cipso2(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &cipso2_seq_ops);
+}
+
+/**
+ * smk_write_cipso2 - write() for /smack/cipso2
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Accepts only one cipso rule per write call.
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_cipso2(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return smk_set_cipso(file, buf, count, ppos, SMK_LONG_FMT);
+}
+
+static const struct file_operations smk_cipso2_ops = {
+ .open = smk_open_cipso2,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_cipso2,
+ .release = seq_release,
+};
+
+/*
* Seq_file read operations for /smack/netlabel
*/
@@ -887,9 +1070,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
{
struct smk_netlbladdr *skp;
struct sockaddr_in newname;
- char smack[SMK_LABELLEN];
+ char *smack;
char *sp;
- char data[SMK_NETLBLADDRMAX + 1];
+ char *data;
char *host = (char *)&newname.sin_addr.s_addr;
int rc;
struct netlbl_audit audit_info;
@@ -911,10 +1094,23 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
return -EPERM;
if (*ppos != 0)
return -EINVAL;
- if (count < SMK_NETLBLADDRMIN || count > SMK_NETLBLADDRMAX)
+ if (count < SMK_NETLBLADDRMIN)
return -EINVAL;
- if (copy_from_user(data, buf, count) != 0)
- return -EFAULT;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ rc = -EFAULT;
+ goto free_data_out;
+ }
+
+ smack = kzalloc(count + 1, GFP_KERNEL);
+ if (smack == NULL) {
+ rc = -ENOMEM;
+ goto free_data_out;
+ }
data[count] = '\0';
@@ -923,24 +1119,34 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
if (rc != 6) {
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
&host[0], &host[1], &host[2], &host[3], smack);
- if (rc != 5)
- return -EINVAL;
+ if (rc != 5) {
+ rc = -EINVAL;
+ goto free_out;
+ }
m = BEBITS;
}
- if (m > BEBITS)
- return -EINVAL;
+ if (m > BEBITS) {
+ rc = -EINVAL;
+ goto free_out;
+ }
- /* if smack begins with '-', its an option, don't import it */
+ /*
+ * If smack begins with '-', it is an option, don't import it
+ */
if (smack[0] != '-') {
sp = smk_import(smack, 0);
- if (sp == NULL)
- return -EINVAL;
+ if (sp == NULL) {
+ rc = -EINVAL;
+ goto free_out;
+ }
} else {
/* check known options */
if (strcmp(smack, smack_cipso_option) == 0)
sp = (char *)smack_cipso_option;
- else
- return -EINVAL;
+ else {
+ rc = -EINVAL;
+ goto free_out;
+ }
}
for (temp_mask = 0; m > 0; m--) {
@@ -1006,6 +1212,11 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
mutex_unlock(&smk_netlbladdr_lock);
+free_out:
+ kfree(smack);
+free_data_out:
+ kfree(data);
+
return rc;
}
@@ -1119,6 +1330,7 @@ static ssize_t smk_read_direct(struct file *filp, char __user *buf,
static ssize_t smk_write_direct(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
+ struct smack_known *skp;
char temp[80];
int i;
@@ -1136,7 +1348,20 @@ static ssize_t smk_write_direct(struct file *file, const char __user *buf,
if (sscanf(temp, "%d", &i) != 1)
return -EINVAL;
- smack_cipso_direct = i;
+ /*
+ * Don't do anything if the value hasn't actually changed.
+ * If it is changing reset the level on entries that were
+ * set up to be direct when they were created.
+ */
+ if (smack_cipso_direct != i) {
+ mutex_lock(&smack_known_lock);
+ list_for_each_entry_rcu(skp, &smack_known_list, list)
+ if (skp->smk_netlabel.attr.mls.lvl ==
+ smack_cipso_direct)
+ skp->smk_netlabel.attr.mls.lvl = i;
+ smack_cipso_direct = i;
+ mutex_unlock(&smack_known_lock);
+ }
return count;
}
@@ -1148,6 +1373,84 @@ static const struct file_operations smk_direct_ops = {
};
/**
+ * smk_read_mapped - read() for /smack/mapped
+ * @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_mapped(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_mapped);
+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+ return rc;
+}
+
+/**
+ * smk_write_mapped - write() for /smack/mapped
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_mapped(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct smack_known *skp;
+ char temp[80];
+ int i;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (count >= sizeof(temp) || count == 0)
+ return -EINVAL;
+
+ if (copy_from_user(temp, buf, count) != 0)
+ return -EFAULT;
+
+ temp[count] = '\0';
+
+ if (sscanf(temp, "%d", &i) != 1)
+ return -EINVAL;
+
+ /*
+ * Don't do anything if the value hasn't actually changed.
+ * If it is changing reset the level on entries that were
+ * set up to be mapped when they were created.
+ */
+ if (smack_cipso_mapped != i) {
+ mutex_lock(&smack_known_lock);
+ list_for_each_entry_rcu(skp, &smack_known_list, list)
+ if (skp->smk_netlabel.attr.mls.lvl ==
+ smack_cipso_mapped)
+ skp->smk_netlabel.attr.mls.lvl = i;
+ smack_cipso_mapped = i;
+ mutex_unlock(&smack_known_lock);
+ }
+
+ return count;
+}
+
+static const struct file_operations smk_mapped_ops = {
+ .read = smk_read_mapped,
+ .write = smk_write_mapped,
+ .llseek = default_llseek,
+};
+
+/**
* smk_read_ambient - read() for /smack/ambient
* @filp: file pointer, not actually used
* @buf: where to put the result
@@ -1195,22 +1498,28 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf,
static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char in[SMK_LABELLEN];
char *oldambient;
- char *smack;
+ char *smack = NULL;
+ char *data;
+ int rc = count;
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
- if (count >= SMK_LABELLEN)
- return -EINVAL;
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
- if (copy_from_user(in, buf, count) != 0)
- return -EFAULT;
+ if (copy_from_user(data, buf, count) != 0) {
+ rc = -EFAULT;
+ goto out;
+ }
- smack = smk_import(in, count);
- if (smack == NULL)
- return -EINVAL;
+ smack = smk_import(data, count);
+ if (smack == NULL) {
+ rc = -EINVAL;
+ goto out;
+ }
mutex_lock(&smack_ambient_lock);
@@ -1220,7 +1529,9 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
mutex_unlock(&smack_ambient_lock);
- return count;
+out:
+ kfree(data);
+ return rc;
}
static const struct file_operations smk_ambient_ops = {
@@ -1271,8 +1582,9 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char in[SMK_LABELLEN];
+ char *data;
char *sp = smk_of_task(current->cred->security);
+ int rc = count;
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
@@ -1285,11 +1597,9 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
if (smack_onlycap != NULL && smack_onlycap != sp)
return -EPERM;
- if (count >= SMK_LABELLEN)
- return -EINVAL;
-
- if (copy_from_user(in, buf, count) != 0)
- return -EFAULT;
+ data = kzalloc(count, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
/*
* Should the null string be passed in unset the onlycap value.
@@ -1297,10 +1607,17 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
* smk_import only expects to return NULL for errors. It
* is usually the case that a nullstring or "\n" would be
* bad to pass to smk_import but in fact this is useful here.
+ *
+ * smk_import will also reject a label beginning with '-',
+ * so "-usecapabilities" will also work.
*/
- smack_onlycap = smk_import(in, count);
+ if (copy_from_user(data, buf, count) != 0)
+ rc = -EFAULT;
+ else
+ smack_onlycap = smk_import(data, count);
- return count;
+ kfree(data);
+ return rc;
}
static const struct file_operations smk_onlycap_ops = {
@@ -1398,25 +1715,7 @@ static int load_self_seq_show(struct seq_file *s, void *v)
struct smack_rule *srp =
list_entry(list, struct smack_rule, list);
- seq_printf(s, "%s %s", (char *)srp->smk_subject,
- (char *)srp->smk_object);
-
- seq_putc(s, ' ');
-
- if (srp->smk_access & MAY_READ)
- seq_putc(s, 'r');
- if (srp->smk_access & MAY_WRITE)
- seq_putc(s, 'w');
- if (srp->smk_access & MAY_EXEC)
- seq_putc(s, 'x');
- if (srp->smk_access & MAY_APPEND)
- seq_putc(s, 'a');
- if (srp->smk_access & MAY_TRANSMUTE)
- seq_putc(s, 't');
- if (srp->smk_access == 0)
- seq_putc(s, '-');
-
- seq_putc(s, '\n');
+ smk_rule_show(s, srp, SMK_LABELLEN);
return 0;
}
@@ -1430,7 +1729,7 @@ static const struct seq_operations load_self_seq_ops = {
/**
- * smk_open_load_self - open() for /smack/load-self
+ * smk_open_load_self - open() for /smack/load-self2
* @inode: inode structure representing file
* @file: "load" file pointer
*
@@ -1454,8 +1753,8 @@ static ssize_t smk_write_load_self(struct file *file, const char __user *buf,
{
struct task_smack *tsp = current_security();
- return smk_write_load_list(file, buf, count, ppos, &tsp->smk_rules,
- &tsp->smk_rules_lock);
+ return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
+ &tsp->smk_rules_lock, SMK_FIXED24_FMT);
}
static const struct file_operations smk_load_self_ops = {
@@ -1467,24 +1766,42 @@ static const struct file_operations smk_load_self_ops = {
};
/**
- * smk_write_access - handle access check transaction
+ * smk_user_access - handle access check transaction
* @file: file pointer
* @buf: data from user space
* @count: bytes sent
* @ppos: where to start - must be 0
*/
-static ssize_t smk_write_access(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+static ssize_t smk_user_access(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos, int format)
{
struct smack_rule rule;
char *data;
+ char *cod;
int res;
data = simple_transaction_get(file, buf, count);
if (IS_ERR(data))
return PTR_ERR(data);
- if (count < SMK_LOADLEN || smk_parse_rule(data, &rule, 0))
+ if (format == SMK_FIXED24_FMT) {
+ if (count < SMK_LOADLEN)
+ return -EINVAL;
+ res = smk_parse_rule(data, &rule, 0);
+ } else {
+ /*
+ * Copy the data to make sure the string is terminated.
+ */
+ cod = kzalloc(count + 1, GFP_KERNEL);
+ if (cod == NULL)
+ return -ENOMEM;
+ memcpy(cod, data, count);
+ cod[count] = '\0';
+ res = smk_parse_long_rule(cod, &rule, 0);
+ kfree(cod);
+ }
+
+ if (res)
return -EINVAL;
res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access,
@@ -1493,7 +1810,23 @@ static ssize_t smk_write_access(struct file *file, const char __user *buf,
data[1] = '\0';
simple_transaction_set(file, 2);
- return SMK_LOADLEN;
+
+ if (format == SMK_FIXED24_FMT)
+ return SMK_LOADLEN;
+ return count;
+}
+
+/**
+ * smk_write_access - handle access check transaction
+ * @file: file pointer
+ * @buf: data from user space
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ */
+static ssize_t smk_write_access(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return smk_user_access(file, buf, count, ppos, SMK_FIXED24_FMT);
}
static const struct file_operations smk_access_ops = {
@@ -1503,6 +1836,163 @@ static const struct file_operations smk_access_ops = {
.llseek = generic_file_llseek,
};
+
+/*
+ * Seq_file read operations for /smack/load2
+ */
+
+static int load2_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smack_master_list *smlp =
+ list_entry(list, struct smack_master_list, list);
+
+ smk_rule_show(s, smlp->smk_rule, SMK_LONGLABEL);
+
+ return 0;
+}
+
+static const struct seq_operations load2_seq_ops = {
+ .start = load2_seq_start,
+ .next = load2_seq_next,
+ .show = load2_seq_show,
+ .stop = smk_seq_stop,
+};
+
+/**
+ * smk_open_load2 - open() for /smack/load2
+ * @inode: inode structure representing file
+ * @file: "load2" file pointer
+ *
+ * For reading, use load2_seq_* seq_file reading operations.
+ */
+static int smk_open_load2(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &load2_seq_ops);
+}
+
+/**
+ * smk_write_load2 - write() for /smack/load2
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ *
+ */
+static ssize_t smk_write_load2(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ /*
+ * Must have privilege.
+ */
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ return smk_write_rules_list(file, buf, count, ppos, NULL, NULL,
+ SMK_LONG_FMT);
+}
+
+static const struct file_operations smk_load2_ops = {
+ .open = smk_open_load2,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_load2,
+ .release = seq_release,
+};
+
+/*
+ * Seq_file read operations for /smack/load-self2
+ */
+
+static void *load_self2_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct task_smack *tsp = current_security();
+
+ return smk_seq_start(s, pos, &tsp->smk_rules);
+}
+
+static void *load_self2_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct task_smack *tsp = current_security();
+
+ return smk_seq_next(s, v, pos, &tsp->smk_rules);
+}
+
+static int load_self2_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smack_rule *srp =
+ list_entry(list, struct smack_rule, list);
+
+ smk_rule_show(s, srp, SMK_LONGLABEL);
+
+ return 0;
+}
+
+static const struct seq_operations load_self2_seq_ops = {
+ .start = load_self2_seq_start,
+ .next = load_self2_seq_next,
+ .show = load_self2_seq_show,
+ .stop = smk_seq_stop,
+};
+
+/**
+ * smk_open_load_self2 - open() for /smack/load-self2
+ * @inode: inode structure representing file
+ * @file: "load" file pointer
+ *
+ * For reading, use load_seq_* seq_file reading operations.
+ */
+static int smk_open_load_self2(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &load_self2_seq_ops);
+}
+
+/**
+ * smk_write_load_self2 - write() for /smack/load-self2
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ *
+ */
+static ssize_t smk_write_load_self2(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_smack *tsp = current_security();
+
+ return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
+ &tsp->smk_rules_lock, SMK_LONG_FMT);
+}
+
+static const struct file_operations smk_load_self2_ops = {
+ .open = smk_open_load_self2,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_load_self2,
+ .release = seq_release,
+};
+
+/**
+ * smk_write_access2 - handle access check transaction
+ * @file: file pointer
+ * @buf: data from user space
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ */
+static ssize_t smk_write_access2(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return smk_user_access(file, buf, count, ppos, SMK_LONG_FMT);
+}
+
+static const struct file_operations smk_access2_ops = {
+ .write = smk_write_access2,
+ .read = simple_transaction_read,
+ .release = simple_transaction_release,
+ .llseek = generic_file_llseek,
+};
+
/**
* smk_fill_super - fill the /smackfs superblock
* @sb: the empty superblock
@@ -1539,6 +2029,16 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
"load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
[SMK_ACCESSES] = {
"access", &smk_access_ops, S_IRUGO|S_IWUGO},
+ [SMK_MAPPED] = {
+ "mapped", &smk_mapped_ops, S_IRUGO|S_IWUSR},
+ [SMK_LOAD2] = {
+ "load2", &smk_load2_ops, S_IRUGO|S_IWUSR},
+ [SMK_LOAD_SELF2] = {
+ "load-self2", &smk_load_self2_ops, S_IRUGO|S_IWUGO},
+ [SMK_ACCESS2] = {
+ "access2", &smk_access2_ops, S_IRUGO|S_IWUGO},
+ [SMK_CIPSO2] = {
+ "cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR},
/* last one */
{""}
};
@@ -1581,6 +2081,15 @@ static struct file_system_type smk_fs_type = {
static struct vfsmount *smackfs_mount;
+static int __init smk_preset_netlabel(struct smack_known *skp)
+{
+ skp->smk_netlabel.domain = skp->smk_known;
+ skp->smk_netlabel.flags =
+ NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
+ return smk_netlbl_mls(smack_cipso_direct, skp->smk_known,
+ &skp->smk_netlabel, strlen(skp->smk_known));
+}
+
/**
* init_smk_fs - get the smackfs superblock
*
@@ -1597,6 +2106,7 @@ static struct vfsmount *smackfs_mount;
static int __init init_smk_fs(void)
{
int err;
+ int rc;
if (!security_module_enable(&smack_ops))
return 0;
@@ -1614,19 +2124,24 @@ static int __init init_smk_fs(void)
smk_cipso_doi();
smk_unlbl_ambient(NULL);
- mutex_init(&smack_known_floor.smk_rules_lock);
- mutex_init(&smack_known_hat.smk_rules_lock);
- mutex_init(&smack_known_huh.smk_rules_lock);
- mutex_init(&smack_known_invalid.smk_rules_lock);
- mutex_init(&smack_known_star.smk_rules_lock);
- mutex_init(&smack_known_web.smk_rules_lock);
-
- INIT_LIST_HEAD(&smack_known_floor.smk_rules);
- INIT_LIST_HEAD(&smack_known_hat.smk_rules);
- INIT_LIST_HEAD(&smack_known_huh.smk_rules);
- INIT_LIST_HEAD(&smack_known_invalid.smk_rules);
- INIT_LIST_HEAD(&smack_known_star.smk_rules);
- INIT_LIST_HEAD(&smack_known_web.smk_rules);
+ rc = smk_preset_netlabel(&smack_known_floor);
+ if (err == 0 && rc < 0)
+ err = rc;
+ rc = smk_preset_netlabel(&smack_known_hat);
+ if (err == 0 && rc < 0)
+ err = rc;
+ rc = smk_preset_netlabel(&smack_known_huh);
+ if (err == 0 && rc < 0)
+ err = rc;
+ rc = smk_preset_netlabel(&smack_known_invalid);
+ if (err == 0 && rc < 0)
+ err = rc;
+ rc = smk_preset_netlabel(&smack_known_star);
+ if (err == 0 && rc < 0)
+ err = rc;
+ rc = smk_preset_netlabel(&smack_known_web);
+ if (err == 0 && rc < 0)
+ err = rc;
return err;
}
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index 8656b16eef7..2e0f12c6293 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -850,14 +850,9 @@ static int tomoyo_update_manager_entry(const char *manager,
policy_list[TOMOYO_ID_MANAGER],
};
int error = is_delete ? -ENOENT : -ENOMEM;
- if (tomoyo_domain_def(manager)) {
- if (!tomoyo_correct_domain(manager))
- return -EINVAL;
- e.is_domain = true;
- } else {
- if (!tomoyo_correct_path(manager))
- return -EINVAL;
- }
+ if (!tomoyo_correct_domain(manager) &&
+ !tomoyo_correct_word(manager))
+ return -EINVAL;
e.manager = tomoyo_get_name(manager);
if (e.manager) {
error = tomoyo_update_policy(&e.head, sizeof(e), &param,
@@ -932,23 +927,14 @@ static bool tomoyo_manager(void)
return true;
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
return false;
- list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
- policy_list[TOMOYO_ID_MANAGER], head.list) {
- if (!ptr->head.is_deleted && ptr->is_domain
- && !tomoyo_pathcmp(domainname, ptr->manager)) {
- found = true;
- break;
- }
- }
- if (found)
- return true;
exe = tomoyo_get_exe();
if (!exe)
return false;
list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
policy_list[TOMOYO_ID_MANAGER], head.list) {
- if (!ptr->head.is_deleted && !ptr->is_domain
- && !strcmp(exe, ptr->manager->name)) {
+ if (!ptr->head.is_deleted &&
+ (!tomoyo_pathcmp(domainname, ptr->manager) ||
+ !strcmp(exe, ptr->manager->name))) {
found = true;
break;
}
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 30fd9836970..75e4dc1c02a 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -860,7 +860,6 @@ struct tomoyo_aggregator {
/* Structure for policy manager. */
struct tomoyo_manager {
struct tomoyo_acl_head head;
- bool is_domain; /* True if manager is a domainname. */
/* A path to program or a domainname. */
const struct tomoyo_path_info *manager;
};
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 573723843a0..83554ee8a58 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -18,7 +18,12 @@
#include <linux/prctl.h>
#include <linux/ratelimit.h>
-static int ptrace_scope = 1;
+#define YAMA_SCOPE_DISABLED 0
+#define YAMA_SCOPE_RELATIONAL 1
+#define YAMA_SCOPE_CAPABILITY 2
+#define YAMA_SCOPE_NO_ATTACH 3
+
+static int ptrace_scope = YAMA_SCOPE_RELATIONAL;
/* describe a ptrace relationship for potential exception */
struct ptrace_relation {
@@ -251,17 +256,32 @@ static int yama_ptrace_access_check(struct task_struct *child,
return rc;
/* require ptrace target be a child of ptracer on attach */
- if (mode == PTRACE_MODE_ATTACH &&
- ptrace_scope &&
- !task_is_descendant(current, child) &&
- !ptracer_exception_found(current, child) &&
- !capable(CAP_SYS_PTRACE))
- rc = -EPERM;
+ if (mode == PTRACE_MODE_ATTACH) {
+ switch (ptrace_scope) {
+ case YAMA_SCOPE_DISABLED:
+ /* No additional restrictions. */
+ break;
+ case YAMA_SCOPE_RELATIONAL:
+ if (!task_is_descendant(current, child) &&
+ !ptracer_exception_found(current, child) &&
+ !ns_capable(task_user_ns(child), CAP_SYS_PTRACE))
+ rc = -EPERM;
+ break;
+ case YAMA_SCOPE_CAPABILITY:
+ if (!ns_capable(task_user_ns(child), CAP_SYS_PTRACE))
+ rc = -EPERM;
+ break;
+ case YAMA_SCOPE_NO_ATTACH:
+ default:
+ rc = -EPERM;
+ break;
+ }
+ }
if (rc) {
char name[sizeof(current->comm)];
- printk_ratelimited(KERN_NOTICE "ptrace of non-child"
- " pid %d was attempted by: %s (pid %d)\n",
+ printk_ratelimited(KERN_NOTICE
+ "ptrace of pid %d was attempted by: %s (pid %d)\n",
child->pid,
get_task_comm(name, current),
current->pid);
@@ -279,8 +299,27 @@ static struct security_operations yama_ops = {
};
#ifdef CONFIG_SYSCTL
+static int yama_dointvec_minmax(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int rc;
+
+ if (write && !capable(CAP_SYS_PTRACE))
+ return -EPERM;
+
+ rc = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+ if (rc)
+ return rc;
+
+ /* Lock the max value if it ever gets set. */
+ if (write && *(int *)table->data == *(int *)table->extra2)
+ table->extra1 = table->extra2;
+
+ return rc;
+}
+
static int zero;
-static int one = 1;
+static int max_scope = YAMA_SCOPE_NO_ATTACH;
struct ctl_path yama_sysctl_path[] = {
{ .procname = "kernel", },
@@ -294,9 +333,9 @@ static struct ctl_table yama_sysctl_table[] = {
.data = &ptrace_scope,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec_minmax,
+ .proc_handler = yama_dointvec_minmax,
.extra1 = &zero,
- .extra2 = &one,
+ .extra2 = &max_scope,
},
{ }
};