X-Git-Url: http://git.onelab.eu/?p=linux-2.6.git;a=blobdiff_plain;f=security%2Fkeys%2Fprocess_keys.c;fp=security%2Fkeys%2Fprocess_keys.c;h=217a0bef3c82b341db5c07d414b7385a8f4a00fb;hp=2eb0e471cd40817a4ae548188dd82be73cbaca71;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hpb=cee37fe97739d85991964371c1f3a745c00dd236 diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 2eb0e471c..217a0bef3 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -1,6 +1,6 @@ /* process_keys.c: management of a process's keyrings * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -16,11 +16,12 @@ #include #include #include +#include #include #include "internal.h" /* session keyring create vs join semaphore */ -static DECLARE_MUTEX(key_session_sem); +static DEFINE_MUTEX(key_session_mutex); /* the root user's tracking struct */ struct key_user root_key_user = { @@ -38,10 +39,9 @@ struct key root_user_keyring = { .serial = 2, .type = &key_type_keyring, .user = &root_key_user, - .lock = RW_LOCK_UNLOCKED, .sem = __RWSEM_INITIALIZER(root_user_keyring.sem), - .perm = KEY_USR_ALL, - .flags = KEY_FLAG_INSTANTIATED, + .perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL, + .flags = 1 << KEY_FLAG_INSTANTIATED, .description = "_uid.0", #ifdef KEY_DEBUGGING .magic = KEY_DEBUG_MAGIC, @@ -54,10 +54,9 @@ struct key root_session_keyring = { .serial = 1, .type = &key_type_keyring, .user = &root_key_user, - .lock = RW_LOCK_UNLOCKED, .sem = __RWSEM_INITIALIZER(root_session_keyring.sem), - .perm = KEY_USR_ALL, - .flags = KEY_FLAG_INSTANTIATED, + .perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL, + .flags = 1 << KEY_FLAG_INSTANTIATED, .description = "_uid_ses.0", #ifdef KEY_DEBUGGING .magic = KEY_DEBUG_MAGIC, @@ -100,7 +99,7 @@ int alloc_uid_keyring(struct user_struct *user) user->session_keyring = session_keyring; ret = 0; - error: +error: return ret; } /* end alloc_uid_keyring() */ @@ -158,7 +157,7 @@ int install_thread_keyring(struct task_struct *tsk) ret = 0; key_put(old); - error: +error: return ret; } /* end install_thread_keyring() */ @@ -167,13 +166,14 @@ int install_thread_keyring(struct task_struct *tsk) /* * make sure a process keyring is installed */ -static int install_process_keyring(struct task_struct *tsk) +int install_process_keyring(struct task_struct *tsk) { - unsigned long flags; struct key *keyring; char buf[20]; int ret; + might_sleep(); + if (!tsk->signal->process_keyring) { sprintf(buf, "_pid.%u", tsk->tgid); @@ -183,19 +183,19 @@ static int install_process_keyring(struct task_struct *tsk) goto error; } - /* attach or swap keyrings */ - spin_lock_irqsave(&tsk->sighand->siglock, flags); + /* attach keyring */ + spin_lock_irq(&tsk->sighand->siglock); if (!tsk->signal->process_keyring) { tsk->signal->process_keyring = keyring; keyring = NULL; } - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + spin_unlock_irq(&tsk->sighand->siglock); key_put(keyring); } ret = 0; - error: +error: return ret; } /* end install_process_keyring() */ @@ -208,36 +208,37 @@ static int install_process_keyring(struct task_struct *tsk) static int install_session_keyring(struct task_struct *tsk, struct key *keyring) { - unsigned long flags; struct key *old; char buf[20]; - int ret; + + might_sleep(); /* create an empty session keyring */ if (!keyring) { sprintf(buf, "_ses.%u", tsk->tgid); keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); - if (IS_ERR(keyring)) { - ret = PTR_ERR(keyring); - goto error; - } + if (IS_ERR(keyring)) + return PTR_ERR(keyring); } else { atomic_inc(&keyring->usage); } /* install the keyring */ - spin_lock_irqsave(&tsk->sighand->siglock, flags); + spin_lock_irq(&tsk->sighand->siglock); old = tsk->signal->session_keyring; - tsk->signal->session_keyring = keyring; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + rcu_assign_pointer(tsk->signal->session_keyring, keyring); + spin_unlock_irq(&tsk->sighand->siglock); - ret = 0; + /* we're using RCU on the pointer, but there's no point synchronising + * on it if it didn't previously point to anything */ + if (old) { + synchronize_rcu(); + key_put(old); + } - key_put(old); - error: - return ret; + return 0; } /* end install_session_keyring() */ @@ -247,8 +248,6 @@ static int install_session_keyring(struct task_struct *tsk, */ int copy_thread_group_keys(struct task_struct *tsk) { - unsigned long flags; - key_check(current->thread_group->session_keyring); key_check(current->thread_group->process_keyring); @@ -256,10 +255,10 @@ int copy_thread_group_keys(struct task_struct *tsk) tsk->signal->process_keyring = NULL; /* same session keyring */ - spin_lock_irqsave(¤t->sighand->siglock, flags); + rcu_read_lock(); tsk->signal->session_keyring = - key_get(current->signal->session_keyring); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); + key_get(rcu_dereference(current->signal->session_keyring)); + rcu_read_unlock(); return 0; @@ -272,9 +271,14 @@ int copy_thread_group_keys(struct task_struct *tsk) int copy_keys(unsigned long clone_flags, struct task_struct *tsk) { key_check(tsk->thread_keyring); + key_check(tsk->request_key_auth); /* no thread keyring yet */ tsk->thread_keyring = NULL; + + /* copy the request_key() authorisation for this thread */ + key_get(tsk->request_key_auth); + return 0; } /* end copy_keys() */ @@ -292,11 +296,12 @@ void exit_thread_group_keys(struct signal_struct *tg) /*****************************************************************************/ /* - * dispose of keys upon thread exit + * dispose of per-thread keys upon thread exit */ void exit_keys(struct task_struct *tsk) { key_put(tsk->thread_keyring); + key_put(tsk->request_key_auth); } /* end exit_keys() */ @@ -306,7 +311,6 @@ void exit_keys(struct task_struct *tsk) */ int exec_keys(struct task_struct *tsk) { - unsigned long flags; struct key *old; /* newly exec'd tasks don't get a thread keyring */ @@ -318,10 +322,10 @@ int exec_keys(struct task_struct *tsk) key_put(old); /* discard the process keyring from a newly exec'd task */ - spin_lock_irqsave(&tsk->sighand->siglock, flags); + spin_lock_irq(&tsk->sighand->siglock); old = tsk->signal->process_keyring; tsk->signal->process_keyring = NULL; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + spin_unlock_irq(&tsk->sighand->siglock); key_put(old); @@ -349,9 +353,7 @@ void key_fsuid_changed(struct task_struct *tsk) /* update the ownership of the thread keyring */ if (tsk->thread_keyring) { down_write(&tsk->thread_keyring->sem); - write_lock(&tsk->thread_keyring->lock); tsk->thread_keyring->uid = tsk->fsuid; - write_unlock(&tsk->thread_keyring->lock); up_write(&tsk->thread_keyring->sem); } @@ -366,9 +368,7 @@ void key_fsgid_changed(struct task_struct *tsk) /* update the ownership of the thread keyring */ if (tsk->thread_keyring) { down_write(&tsk->thread_keyring->sem); - write_lock(&tsk->thread_keyring->lock); tsk->thread_keyring->gid = tsk->fsgid; - write_unlock(&tsk->thread_keyring->lock); up_write(&tsk->thread_keyring->sem); } @@ -382,13 +382,13 @@ void key_fsgid_changed(struct task_struct *tsk) * - we return -EAGAIN if we didn't find any matching key * - we return -ENOKEY if we found only negative matching keys */ -struct key *search_process_keyrings_aux(struct key_type *type, - const void *description, - key_match_func_t match) +key_ref_t search_process_keyrings(struct key_type *type, + const void *description, + key_match_func_t match, + struct task_struct *context) { - struct task_struct *tsk = current; - unsigned long flags; - struct key *key, *ret, *err, *tmp; + struct request_key_auth *rka; + key_ref_t key_ref, ret, err; /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were * searchable, but we failed to find a key or we found a negative key; @@ -397,97 +397,145 @@ struct key *search_process_keyrings_aux(struct key_type *type, * * in terms of priority: success > -ENOKEY > -EAGAIN > other error */ - key = NULL; + key_ref = NULL; ret = NULL; err = ERR_PTR(-EAGAIN); /* search the thread keyring first */ - if (tsk->thread_keyring) { - key = keyring_search_aux(tsk->thread_keyring, type, - description, match); - if (!IS_ERR(key)) + if (context->thread_keyring) { + key_ref = keyring_search_aux( + make_key_ref(context->thread_keyring, 1), + context, type, description, match); + if (!IS_ERR(key_ref)) goto found; - switch (PTR_ERR(key)) { + switch (PTR_ERR(key_ref)) { case -EAGAIN: /* no key */ if (ret) break; case -ENOKEY: /* negative key */ - ret = key; + ret = key_ref; break; default: - err = key; + err = key_ref; break; } } /* search the process keyring second */ - if (tsk->signal->process_keyring) { - key = keyring_search_aux(tsk->signal->process_keyring, - type, description, match); - if (!IS_ERR(key)) + if (context->signal->process_keyring) { + key_ref = keyring_search_aux( + make_key_ref(context->signal->process_keyring, 1), + context, type, description, match); + if (!IS_ERR(key_ref)) + goto found; + + switch (PTR_ERR(key_ref)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key_ref; + break; + default: + err = key_ref; + break; + } + } + + /* search the session keyring */ + if (context->signal->session_keyring) { + rcu_read_lock(); + key_ref = keyring_search_aux( + make_key_ref(rcu_dereference( + context->signal->session_keyring), + 1), + context, type, description, match); + rcu_read_unlock(); + + if (!IS_ERR(key_ref)) goto found; - switch (PTR_ERR(key)) { + switch (PTR_ERR(key_ref)) { case -EAGAIN: /* no key */ if (ret) break; case -ENOKEY: /* negative key */ - ret = key; + ret = key_ref; break; default: - err = key; + err = key_ref; break; } } + /* or search the user-session keyring */ + else { + key_ref = keyring_search_aux( + make_key_ref(context->user->session_keyring, 1), + context, type, description, match); + if (!IS_ERR(key_ref)) + goto found; - /* search the session keyring last */ - spin_lock_irqsave(&tsk->sighand->siglock, flags); + switch (PTR_ERR(key_ref)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key_ref; + break; + default: + err = key_ref; + break; + } + } - tmp = tsk->signal->session_keyring; - if (!tmp) - tmp = tsk->user->session_keyring; - atomic_inc(&tmp->usage); + /* if this process has an instantiation authorisation key, then we also + * search the keyrings of the process mentioned there + * - we don't permit access to request_key auth keys via this method + */ + if (context->request_key_auth && + context == current && + type != &key_type_request_key_auth && + key_validate(context->request_key_auth) == 0 + ) { + rka = context->request_key_auth->payload.data; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + key_ref = search_process_keyrings(type, description, match, + rka->context); - key = keyring_search_aux(tmp, type, description, match); - key_put(tmp); - if (!IS_ERR(key)) - goto found; + if (!IS_ERR(key_ref)) + goto found; - switch (PTR_ERR(key)) { - case -EAGAIN: /* no key */ - if (ret) + switch (PTR_ERR(key_ref)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key_ref; break; - case -ENOKEY: /* negative key */ - ret = key; - break; - default: - err = key; - break; + default: + err = key_ref; + break; + } } /* no key - decide on the error we're going to go for */ - key = ret ? ret : err; + key_ref = ret ? ret : err; - found: - return key; +found: + return key_ref; -} /* end search_process_keyrings_aux() */ +} /* end search_process_keyrings() */ /*****************************************************************************/ /* - * search the process keyrings for the first matching key - * - we return -EAGAIN if we didn't find any matching key - * - we return -ENOKEY if we found only negative matching keys + * see if the key we're looking at is the target key */ -struct key *search_process_keyrings(struct key_type *type, - const char *description) +static int lookup_user_key_possessed(const struct key *key, const void *target) { - return search_process_keyrings_aux(type, description, type->match); + return key == target; -} /* end search_process_keyrings() */ +} /* end lookup_user_key_possessed() */ /*****************************************************************************/ /* @@ -495,73 +543,80 @@ struct key *search_process_keyrings(struct key_type *type, * - don't create special keyrings unless so requested * - partially constructed keys aren't found unless requested */ -struct key *lookup_user_key(key_serial_t id, int create, int partial, - key_perm_t perm) +key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id, + int create, int partial, key_perm_t perm) { - struct task_struct *tsk = current; - unsigned long flags; + key_ref_t key_ref, skey_ref; struct key *key; int ret; - key = ERR_PTR(-ENOKEY); + if (!context) + context = current; + + key_ref = ERR_PTR(-ENOKEY); switch (id) { case KEY_SPEC_THREAD_KEYRING: - if (!tsk->thread_keyring) { + if (!context->thread_keyring) { if (!create) goto error; - ret = install_thread_keyring(tsk); + ret = install_thread_keyring(context); if (ret < 0) { key = ERR_PTR(ret); goto error; } } - key = tsk->thread_keyring; + key = context->thread_keyring; atomic_inc(&key->usage); + key_ref = make_key_ref(key, 1); break; case KEY_SPEC_PROCESS_KEYRING: - if (!tsk->signal->process_keyring) { + if (!context->signal->process_keyring) { if (!create) goto error; - ret = install_process_keyring(tsk); + ret = install_process_keyring(context); if (ret < 0) { key = ERR_PTR(ret); goto error; } } - key = tsk->signal->process_keyring; + key = context->signal->process_keyring; atomic_inc(&key->usage); + key_ref = make_key_ref(key, 1); break; case KEY_SPEC_SESSION_KEYRING: - if (!tsk->signal->session_keyring) { + if (!context->signal->session_keyring) { /* always install a session keyring upon access if one * doesn't exist yet */ ret = install_session_keyring( - tsk, tsk->user->session_keyring); + context, context->user->session_keyring); if (ret < 0) goto error; } - spin_lock_irqsave(&tsk->sighand->siglock, flags); - key = tsk->signal->session_keyring; + rcu_read_lock(); + key = rcu_dereference(context->signal->session_keyring); atomic_inc(&key->usage); - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + rcu_read_unlock(); + key_ref = make_key_ref(key, 1); break; case KEY_SPEC_USER_KEYRING: - key = tsk->user->uid_keyring; + key = context->user->uid_keyring; atomic_inc(&key->usage); + key_ref = make_key_ref(key, 1); break; case KEY_SPEC_USER_SESSION_KEYRING: - key = tsk->user->session_keyring; + key = context->user->session_keyring; atomic_inc(&key->usage); + key_ref = make_key_ref(key, 1); break; case KEY_SPEC_GROUP_KEYRING: @@ -569,18 +624,42 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, key = ERR_PTR(-EINVAL); goto error; + case KEY_SPEC_REQKEY_AUTH_KEY: + key = context->request_key_auth; + if (!key) + goto error; + + atomic_inc(&key->usage); + key_ref = make_key_ref(key, 1); + break; + default: - key = ERR_PTR(-EINVAL); + key_ref = ERR_PTR(-EINVAL); if (id < 1) goto error; key = key_lookup(id); - if (IS_ERR(key)) + if (IS_ERR(key)) { + key_ref = ERR_PTR(PTR_ERR(key)); goto error; + } + + key_ref = make_key_ref(key, 0); + + /* check to see if we possess the key */ + skey_ref = search_process_keyrings(key->type, key, + lookup_user_key_possessed, + current); + + if (!IS_ERR(skey_ref)) { + key_put(key); + key_ref = skey_ref; + } + break; } - /* check the status and permissions */ + /* check the status */ if (perm) { ret = key_validate(key); if (ret < 0) @@ -588,19 +667,20 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, } ret = -EIO; - if (!partial && !(key->flags & KEY_FLAG_INSTANTIATED)) + if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) goto invalid_key; - ret = -EACCES; - if (!key_permission(key, perm)) + /* check the permissions */ + ret = key_task_permission(key_ref, context, perm); + if (ret < 0) goto invalid_key; - error: - return key; +error: + return key_ref; - invalid_key: - key_put(key); - key = ERR_PTR(ret); +invalid_key: + key_ref_put(key_ref); + key_ref = ERR_PTR(ret); goto error; } /* end lookup_user_key() */ @@ -615,7 +695,6 @@ struct key *lookup_user_key(key_serial_t id, int create, int partial, long join_session_keyring(const char *name) { struct task_struct *tsk = current; - unsigned long flags; struct key *keyring; long ret; @@ -625,14 +704,14 @@ long join_session_keyring(const char *name) if (ret < 0) goto error; - spin_lock_irqsave(&tsk->sighand->siglock, flags); - ret = tsk->signal->session_keyring->serial; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + rcu_read_lock(); + ret = rcu_dereference(tsk->signal->session_keyring)->serial; + rcu_read_unlock(); goto error; } /* allow the user to join or create a named keyring */ - down(&key_session_sem); + mutex_lock(&key_session_mutex); /* look for an existing keyring of this name */ keyring = find_keyring_by_name(name, 0); @@ -641,7 +720,7 @@ long join_session_keyring(const char *name) keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); - goto error; + goto error2; } } else if (IS_ERR(keyring)) { @@ -657,9 +736,9 @@ long join_session_keyring(const char *name) ret = keyring->serial; key_put(keyring); - error2: - up(&key_session_sem); - error: +error2: + mutex_unlock(&key_session_mutex); +error: return ret; } /* end join_session_keyring() */