David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 1 | /* Task credentials management |
| 2 | * |
| 3 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. |
| 4 | * Written by David Howells (dhowells@redhat.com) |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public Licence |
| 8 | * as published by the Free Software Foundation; either version |
| 9 | * 2 of the Licence, or (at your option) any later version. |
| 10 | */ |
| 11 | #include <linux/module.h> |
| 12 | #include <linux/cred.h> |
| 13 | #include <linux/sched.h> |
| 14 | #include <linux/key.h> |
| 15 | #include <linux/keyctl.h> |
| 16 | #include <linux/init_task.h> |
| 17 | #include <linux/security.h> |
| 18 | |
| 19 | /* |
David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame^] | 20 | * The common credentials for the initial task's thread group |
| 21 | */ |
| 22 | #ifdef CONFIG_KEYS |
| 23 | static struct thread_group_cred init_tgcred = { |
| 24 | .usage = ATOMIC_INIT(2), |
| 25 | .tgid = 0, |
| 26 | .lock = SPIN_LOCK_UNLOCKED, |
| 27 | }; |
| 28 | #endif |
| 29 | |
| 30 | /* |
David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 31 | * The initial credentials for the initial task |
| 32 | */ |
| 33 | struct cred init_cred = { |
| 34 | .usage = ATOMIC_INIT(3), |
| 35 | .securebits = SECUREBITS_DEFAULT, |
| 36 | .cap_inheritable = CAP_INIT_INH_SET, |
| 37 | .cap_permitted = CAP_FULL_SET, |
| 38 | .cap_effective = CAP_INIT_EFF_SET, |
| 39 | .cap_bset = CAP_INIT_BSET, |
| 40 | .user = INIT_USER, |
| 41 | .group_info = &init_groups, |
David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame^] | 42 | #ifdef CONFIG_KEYS |
| 43 | .tgcred = &init_tgcred, |
| 44 | #endif |
David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 45 | }; |
| 46 | |
| 47 | /* |
David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame^] | 48 | * Dispose of the shared task group credentials |
| 49 | */ |
| 50 | #ifdef CONFIG_KEYS |
| 51 | static void release_tgcred_rcu(struct rcu_head *rcu) |
| 52 | { |
| 53 | struct thread_group_cred *tgcred = |
| 54 | container_of(rcu, struct thread_group_cred, rcu); |
| 55 | |
| 56 | BUG_ON(atomic_read(&tgcred->usage) != 0); |
| 57 | |
| 58 | key_put(tgcred->session_keyring); |
| 59 | key_put(tgcred->process_keyring); |
| 60 | kfree(tgcred); |
| 61 | } |
| 62 | #endif |
| 63 | |
| 64 | /* |
| 65 | * Release a set of thread group credentials. |
| 66 | */ |
| 67 | static void release_tgcred(struct cred *cred) |
| 68 | { |
| 69 | #ifdef CONFIG_KEYS |
| 70 | struct thread_group_cred *tgcred = cred->tgcred; |
| 71 | |
| 72 | if (atomic_dec_and_test(&tgcred->usage)) |
| 73 | call_rcu(&tgcred->rcu, release_tgcred_rcu); |
| 74 | #endif |
| 75 | } |
| 76 | |
| 77 | /* |
David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 78 | * The RCU callback to actually dispose of a set of credentials |
| 79 | */ |
| 80 | static void put_cred_rcu(struct rcu_head *rcu) |
| 81 | { |
| 82 | struct cred *cred = container_of(rcu, struct cred, rcu); |
| 83 | |
| 84 | BUG_ON(atomic_read(&cred->usage) != 0); |
| 85 | |
| 86 | key_put(cred->thread_keyring); |
| 87 | key_put(cred->request_key_auth); |
David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame^] | 88 | release_tgcred(cred); |
David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 89 | put_group_info(cred->group_info); |
| 90 | free_uid(cred->user); |
| 91 | security_cred_free(cred); |
| 92 | kfree(cred); |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * __put_cred - Destroy a set of credentials |
| 97 | * @sec: The record to release |
| 98 | * |
| 99 | * Destroy a set of credentials on which no references remain. |
| 100 | */ |
| 101 | void __put_cred(struct cred *cred) |
| 102 | { |
| 103 | call_rcu(&cred->rcu, put_cred_rcu); |
| 104 | } |
| 105 | EXPORT_SYMBOL(__put_cred); |
| 106 | |
| 107 | /* |
| 108 | * Copy credentials for the new process created by fork() |
| 109 | */ |
| 110 | int copy_creds(struct task_struct *p, unsigned long clone_flags) |
| 111 | { |
| 112 | struct cred *pcred; |
| 113 | int ret; |
| 114 | |
| 115 | pcred = kmemdup(p->cred, sizeof(*p->cred), GFP_KERNEL); |
| 116 | if (!pcred) |
| 117 | return -ENOMEM; |
| 118 | |
David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame^] | 119 | #ifdef CONFIG_KEYS |
| 120 | if (clone_flags & CLONE_THREAD) { |
| 121 | atomic_inc(&pcred->tgcred->usage); |
| 122 | } else { |
| 123 | pcred->tgcred = kmalloc(sizeof(struct cred), GFP_KERNEL); |
| 124 | if (!pcred->tgcred) { |
| 125 | kfree(pcred); |
| 126 | return -ENOMEM; |
| 127 | } |
| 128 | atomic_set(&pcred->tgcred->usage, 1); |
| 129 | spin_lock_init(&pcred->tgcred->lock); |
| 130 | pcred->tgcred->process_keyring = NULL; |
| 131 | pcred->tgcred->session_keyring = |
| 132 | key_get(p->cred->tgcred->session_keyring); |
| 133 | } |
| 134 | #endif |
| 135 | |
David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 136 | #ifdef CONFIG_SECURITY |
| 137 | pcred->security = NULL; |
| 138 | #endif |
| 139 | |
| 140 | ret = security_cred_alloc(pcred); |
| 141 | if (ret < 0) { |
David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame^] | 142 | release_tgcred(pcred); |
David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 143 | kfree(pcred); |
| 144 | return ret; |
| 145 | } |
| 146 | |
| 147 | atomic_set(&pcred->usage, 1); |
| 148 | get_group_info(pcred->group_info); |
| 149 | get_uid(pcred->user); |
| 150 | key_get(pcred->thread_keyring); |
| 151 | key_get(pcred->request_key_auth); |
| 152 | |
| 153 | atomic_inc(&pcred->user->processes); |
| 154 | |
| 155 | /* RCU assignment is unneeded here as no-one can have accessed this |
| 156 | * pointer yet, barring us */ |
| 157 | p->cred = pcred; |
| 158 | return 0; |
| 159 | } |