aboutsummaryrefslogtreecommitdiffstats
path: root/security/keys/keyctl.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2008-11-13 18:39:23 -0500
committerJames Morris <jmorris@namei.org>2008-11-13 18:39:23 -0500
commitd84f4f992cbd76e8f39c488cf0c5d123843923b1 (patch)
treefc4a0349c42995715b93d0f7a3c78e9ea9b3f36e /security/keys/keyctl.c
parent745ca2475a6ac596e3d8d37c2759c0fbe2586227 (diff)
CRED: Inaugurate COW credentials
Inaugurate copy-on-write credentials management. This uses RCU to manage the credentials pointer in the task_struct with respect to accesses by other tasks. A process may only modify its own credentials, and so does not need locking to access or modify its own credentials. A mutex (cred_replace_mutex) is added to the task_struct to control the effect of PTRACE_ATTACHED on credential calculations, particularly with respect to execve(). With this patch, the contents of an active credentials struct may not be changed directly; rather a new set of credentials must be prepared, modified and committed using something like the following sequence of events: struct cred *new = prepare_creds(); int ret = blah(new); if (ret < 0) { abort_creds(new); return ret; } return commit_creds(new); There are some exceptions to this rule: the keyrings pointed to by the active credentials may be instantiated - keyrings violate the COW rule as managing COW keyrings is tricky, given that it is possible for a task to directly alter the keys in a keyring in use by another task. To help enforce this, various pointers to sets of credentials, such as those in the task_struct, are declared const. The purpose of this is compile-time discouragement of altering credentials through those pointers. Once a set of credentials has been made public through one of these pointers, it may not be modified, except under special circumstances: (1) Its reference count may incremented and decremented. (2) The keyrings to which it points may be modified, but not replaced. The only safe way to modify anything else is to create a replacement and commit using the functions described in Documentation/credentials.txt (which will be added by a later patch). This patch and the preceding patches have been tested with the LTP SELinux testsuite. This patch makes several logical sets of alteration: (1) execve(). This now prepares and commits credentials in various places in the security code rather than altering the current creds directly. (2) Temporary credential overrides. do_coredump() and sys_faccessat() now prepare their own credentials and temporarily override the ones currently on the acting thread, whilst preventing interference from other threads by holding cred_replace_mutex on the thread being dumped. This will be replaced in a future patch by something that hands down the credentials directly to the functions being called, rather than altering the task's objective credentials. (3) LSM interface. A number of functions have been changed, added or removed: (*) security_capset_check(), ->capset_check() (*) security_capset_set(), ->capset_set() Removed in favour of security_capset(). (*) security_capset(), ->capset() New. This is passed a pointer to the new creds, a pointer to the old creds and the proposed capability sets. It should fill in the new creds or return an error. All pointers, barring the pointer to the new creds, are now const. (*) security_bprm_apply_creds(), ->bprm_apply_creds() Changed; now returns a value, which will cause the process to be killed if it's an error. (*) security_task_alloc(), ->task_alloc_security() Removed in favour of security_prepare_creds(). (*) security_cred_free(), ->cred_free() New. Free security data attached to cred->security. (*) security_prepare_creds(), ->cred_prepare() New. Duplicate any security data attached to cred->security. (*) security_commit_creds(), ->cred_commit() New. Apply any security effects for the upcoming installation of new security by commit_creds(). (*) security_task_post_setuid(), ->task_post_setuid() Removed in favour of security_task_fix_setuid(). (*) security_task_fix_setuid(), ->task_fix_setuid() Fix up the proposed new credentials for setuid(). This is used by cap_set_fix_setuid() to implicitly adjust capabilities in line with setuid() changes. Changes are made to the new credentials, rather than the task itself as in security_task_post_setuid(). (*) security_task_reparent_to_init(), ->task_reparent_to_init() Removed. Instead the task being reparented to init is referred directly to init's credentials. NOTE! This results in the loss of some state: SELinux's osid no longer records the sid of the thread that forked it. (*) security_key_alloc(), ->key_alloc() (*) security_key_permission(), ->key_permission() Changed. These now take cred pointers rather than task pointers to refer to the security context. (4) sys_capset(). This has been simplified and uses less locking. The LSM functions it calls have been merged. (5) reparent_to_kthreadd(). This gives the current thread the same credentials as init by simply using commit_thread() to point that way. (6) __sigqueue_alloc() and switch_uid() __sigqueue_alloc() can't stop the target task from changing its creds beneath it, so this function gets a reference to the currently applicable user_struct which it then passes into the sigqueue struct it returns if successful. switch_uid() is now called from commit_creds(), and possibly should be folded into that. commit_creds() should take care of protecting __sigqueue_alloc(). (7) [sg]et[ug]id() and co and [sg]et_current_groups. The set functions now all use prepare_creds(), commit_creds() and abort_creds() to build and check a new set of credentials before applying it. security_task_set[ug]id() is called inside the prepared section. This guarantees that nothing else will affect the creds until we've finished. The calling of set_dumpable() has been moved into commit_creds(). Much of the functionality of set_user() has been moved into commit_creds(). The get functions all simply access the data directly. (8) security_task_prctl() and cap_task_prctl(). security_task_prctl() has been modified to return -ENOSYS if it doesn't want to handle a function, or otherwise return the return value directly rather than through an argument. Additionally, cap_task_prctl() now prepares a new set of credentials, even if it doesn't end up using it. (9) Keyrings. A number of changes have been made to the keyrings code: (a) switch_uid_keyring(), copy_keys(), exit_keys() and suid_keys() have all been dropped and built in to the credentials functions directly. They may want separating out again later. (b) key_alloc() and search_process_keyrings() now take a cred pointer rather than a task pointer to specify the security context. (c) copy_creds() gives a new thread within the same thread group a new thread keyring if its parent had one, otherwise it discards the thread keyring. (d) The authorisation key now points directly to the credentials to extend the search into rather pointing to the task that carries them. (e) Installing thread, process or session keyrings causes a new set of credentials to be created, even though it's not strictly necessary for process or session keyrings (they're shared). (10) Usermode helper. The usermode helper code now carries a cred struct pointer in its subprocess_info struct instead of a new session keyring pointer. This set of credentials is derived from init_cred and installed on the new process after it has been cloned. call_usermodehelper_setup() allocates the new credentials and call_usermodehelper_freeinfo() discards them if they haven't been used. A special cred function (prepare_usermodeinfo_creds()) is provided specifically for call_usermodehelper_setup() to call. call_usermodehelper_setkeys() adjusts the credentials to sport the supplied keyring as the new session keyring. (11) SELinux. SELinux has a number of changes, in addition to those to support the LSM interface changes mentioned above: (a) selinux_setprocattr() no longer does its check for whether the current ptracer can access processes with the new SID inside the lock that covers getting the ptracer's SID. Whilst this lock ensures that the check is done with the ptracer pinned, the result is only valid until the lock is released, so there's no point doing it inside the lock. (12) is_single_threaded(). This function has been extracted from selinux_setprocattr() and put into a file of its own in the lib/ directory as join_session_keyring() now wants to use it too. The code in SELinux just checked to see whether a task shared mm_structs with other tasks (CLONE_VM), but that isn't good enough. We really want to know if they're part of the same thread group (CLONE_THREAD). (13) nfsd. The NFS server daemon now has to use the COW credentials to set the credentials it is going to use. It really needs to pass the credentials down to the functions it calls, but it can't do that until other patches in this series have been applied. Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: James Morris <jmorris@namei.org> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/keys/keyctl.c')
-rw-r--r--security/keys/keyctl.c95
1 files changed, 67 insertions, 28 deletions
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 8833b447adef..7c72baa02f2e 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -866,6 +866,23 @@ static long get_instantiation_keyring(key_serial_t ringid,
866 return -ENOKEY; 866 return -ENOKEY;
867} 867}
868 868
869/*
870 * change the request_key authorisation key on the current process
871 */
872static int keyctl_change_reqkey_auth(struct key *key)
873{
874 struct cred *new;
875
876 new = prepare_creds();
877 if (!new)
878 return -ENOMEM;
879
880 key_put(new->request_key_auth);
881 new->request_key_auth = key_get(key);
882
883 return commit_creds(new);
884}
885
869/*****************************************************************************/ 886/*****************************************************************************/
870/* 887/*
871 * instantiate the key with the specified payload, and, if one is given, link 888 * instantiate the key with the specified payload, and, if one is given, link
@@ -876,12 +893,15 @@ long keyctl_instantiate_key(key_serial_t id,
876 size_t plen, 893 size_t plen,
877 key_serial_t ringid) 894 key_serial_t ringid)
878{ 895{
896 const struct cred *cred = current_cred();
879 struct request_key_auth *rka; 897 struct request_key_auth *rka;
880 struct key *instkey, *dest_keyring; 898 struct key *instkey, *dest_keyring;
881 void *payload; 899 void *payload;
882 long ret; 900 long ret;
883 bool vm = false; 901 bool vm = false;
884 902
903 kenter("%d,,%zu,%d", id, plen, ringid);
904
885 ret = -EINVAL; 905 ret = -EINVAL;
886 if (plen > 1024 * 1024 - 1) 906 if (plen > 1024 * 1024 - 1)
887 goto error; 907 goto error;
@@ -889,7 +909,7 @@ long keyctl_instantiate_key(key_serial_t id,
889 /* the appropriate instantiation authorisation key must have been 909 /* the appropriate instantiation authorisation key must have been
890 * assumed before calling this */ 910 * assumed before calling this */
891 ret = -EPERM; 911 ret = -EPERM;
892 instkey = current->cred->request_key_auth; 912 instkey = cred->request_key_auth;
893 if (!instkey) 913 if (!instkey)
894 goto error; 914 goto error;
895 915
@@ -931,10 +951,8 @@ long keyctl_instantiate_key(key_serial_t id,
931 951
932 /* discard the assumed authority if it's just been disabled by 952 /* discard the assumed authority if it's just been disabled by
933 * instantiation of the key */ 953 * instantiation of the key */
934 if (ret == 0) { 954 if (ret == 0)
935 key_put(current->cred->request_key_auth); 955 keyctl_change_reqkey_auth(NULL);
936 current->cred->request_key_auth = NULL;
937 }
938 956
939error2: 957error2:
940 if (!vm) 958 if (!vm)
@@ -953,14 +971,17 @@ error:
953 */ 971 */
954long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) 972long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
955{ 973{
974 const struct cred *cred = current_cred();
956 struct request_key_auth *rka; 975 struct request_key_auth *rka;
957 struct key *instkey, *dest_keyring; 976 struct key *instkey, *dest_keyring;
958 long ret; 977 long ret;
959 978
979 kenter("%d,%u,%d", id, timeout, ringid);
980
960 /* the appropriate instantiation authorisation key must have been 981 /* the appropriate instantiation authorisation key must have been
961 * assumed before calling this */ 982 * assumed before calling this */
962 ret = -EPERM; 983 ret = -EPERM;
963 instkey = current->cred->request_key_auth; 984 instkey = cred->request_key_auth;
964 if (!instkey) 985 if (!instkey)
965 goto error; 986 goto error;
966 987
@@ -982,10 +1003,8 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
982 1003
983 /* discard the assumed authority if it's just been disabled by 1004 /* discard the assumed authority if it's just been disabled by
984 * instantiation of the key */ 1005 * instantiation of the key */
985 if (ret == 0) { 1006 if (ret == 0)
986 key_put(current->cred->request_key_auth); 1007 keyctl_change_reqkey_auth(NULL);
987 current->cred->request_key_auth = NULL;
988 }
989 1008
990error: 1009error:
991 return ret; 1010 return ret;
@@ -999,36 +1018,56 @@ error:
999 */ 1018 */
1000long keyctl_set_reqkey_keyring(int reqkey_defl) 1019long keyctl_set_reqkey_keyring(int reqkey_defl)
1001{ 1020{
1002 struct cred *cred = current->cred; 1021 struct cred *new;
1003 int ret; 1022 int ret, old_setting;
1023
1024 old_setting = current_cred_xxx(jit_keyring);
1025
1026 if (reqkey_defl == KEY_REQKEY_DEFL_NO_CHANGE)
1027 return old_setting;
1028
1029 new = prepare_creds();
1030 if (!new)
1031 return -ENOMEM;
1004 1032
1005 switch (reqkey_defl) { 1033 switch (reqkey_defl) {
1006 case KEY_REQKEY_DEFL_THREAD_KEYRING: 1034 case KEY_REQKEY_DEFL_THREAD_KEYRING:
1007 ret = install_thread_keyring(); 1035 ret = install_thread_keyring_to_cred(new);
1008 if (ret < 0) 1036 if (ret < 0)
1009 return ret; 1037 goto error;
1010 goto set; 1038 goto set;
1011 1039
1012 case KEY_REQKEY_DEFL_PROCESS_KEYRING: 1040 case KEY_REQKEY_DEFL_PROCESS_KEYRING:
1013 ret = install_process_keyring(); 1041 ret = install_process_keyring_to_cred(new);
1014 if (ret < 0) 1042 if (ret < 0) {
1015 return ret; 1043 if (ret != -EEXIST)
1044 goto error;
1045 ret = 0;
1046 }
1047 goto set;
1016 1048
1017 case KEY_REQKEY_DEFL_DEFAULT: 1049 case KEY_REQKEY_DEFL_DEFAULT:
1018 case KEY_REQKEY_DEFL_SESSION_KEYRING: 1050 case KEY_REQKEY_DEFL_SESSION_KEYRING:
1019 case KEY_REQKEY_DEFL_USER_KEYRING: 1051 case KEY_REQKEY_DEFL_USER_KEYRING:
1020 case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: 1052 case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
1021 set: 1053 case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
1022 cred->jit_keyring = reqkey_defl; 1054 goto set;
1023 1055
1024 case KEY_REQKEY_DEFL_NO_CHANGE: 1056 case KEY_REQKEY_DEFL_NO_CHANGE:
1025 return cred->jit_keyring;
1026
1027 case KEY_REQKEY_DEFL_GROUP_KEYRING: 1057 case KEY_REQKEY_DEFL_GROUP_KEYRING:
1028 default: 1058 default:
1029 return -EINVAL; 1059 ret = -EINVAL;
1060 goto error;
1030 } 1061 }
1031 1062
1063set:
1064 new->jit_keyring = reqkey_defl;
1065 commit_creds(new);
1066 return old_setting;
1067error:
1068 abort_creds(new);
1069 return -EINVAL;
1070
1032} /* end keyctl_set_reqkey_keyring() */ 1071} /* end keyctl_set_reqkey_keyring() */
1033 1072
1034/*****************************************************************************/ 1073/*****************************************************************************/
@@ -1087,9 +1126,7 @@ long keyctl_assume_authority(key_serial_t id)
1087 1126
1088 /* we divest ourselves of authority if given an ID of 0 */ 1127 /* we divest ourselves of authority if given an ID of 0 */
1089 if (id == 0) { 1128 if (id == 0) {
1090 key_put(current->cred->request_key_auth); 1129 ret = keyctl_change_reqkey_auth(NULL);
1091 current->cred->request_key_auth = NULL;
1092 ret = 0;
1093 goto error; 1130 goto error;
1094 } 1131 }
1095 1132
@@ -1104,10 +1141,12 @@ long keyctl_assume_authority(key_serial_t id)
1104 goto error; 1141 goto error;
1105 } 1142 }
1106 1143
1107 key_put(current->cred->request_key_auth); 1144 ret = keyctl_change_reqkey_auth(authkey);
1108 current->cred->request_key_auth = authkey; 1145 if (ret < 0)
1109 ret = authkey->serial; 1146 goto error;
1147 key_put(authkey);
1110 1148
1149 ret = authkey->serial;
1111error: 1150error:
1112 return ret; 1151 return ret;
1113 1152