diff options
Diffstat (limited to 'security/keys/user_defined.c')
-rw-r--r-- | security/keys/user_defined.c | 87 |
1 files changed, 63 insertions, 24 deletions
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 8d65b3a28129..e446acba73d3 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c | |||
@@ -42,12 +42,21 @@ struct key_type key_type_user = { | |||
42 | .read = user_read, | 42 | .read = user_read, |
43 | }; | 43 | }; |
44 | 44 | ||
45 | struct user_key_payload { | ||
46 | struct rcu_head rcu; /* RCU destructor */ | ||
47 | unsigned short datalen; /* length of this data */ | ||
48 | char data[0]; /* actual data */ | ||
49 | }; | ||
50 | |||
51 | EXPORT_SYMBOL_GPL(key_type_user); | ||
52 | |||
45 | /*****************************************************************************/ | 53 | /*****************************************************************************/ |
46 | /* | 54 | /* |
47 | * instantiate a user defined key | 55 | * instantiate a user defined key |
48 | */ | 56 | */ |
49 | static int user_instantiate(struct key *key, const void *data, size_t datalen) | 57 | static int user_instantiate(struct key *key, const void *data, size_t datalen) |
50 | { | 58 | { |
59 | struct user_key_payload *upayload; | ||
51 | int ret; | 60 | int ret; |
52 | 61 | ||
53 | ret = -EINVAL; | 62 | ret = -EINVAL; |
@@ -58,13 +67,15 @@ static int user_instantiate(struct key *key, const void *data, size_t datalen) | |||
58 | if (ret < 0) | 67 | if (ret < 0) |
59 | goto error; | 68 | goto error; |
60 | 69 | ||
61 | /* attach the data */ | ||
62 | ret = -ENOMEM; | 70 | ret = -ENOMEM; |
63 | key->payload.data = kmalloc(datalen, GFP_KERNEL); | 71 | upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); |
64 | if (!key->payload.data) | 72 | if (!upayload) |
65 | goto error; | 73 | goto error; |
66 | 74 | ||
67 | memcpy(key->payload.data, data, datalen); | 75 | /* attach the data */ |
76 | upayload->datalen = datalen; | ||
77 | memcpy(upayload->data, data, datalen); | ||
78 | rcu_assign_pointer(key->payload.data, upayload); | ||
68 | ret = 0; | 79 | ret = 0; |
69 | 80 | ||
70 | error: | 81 | error: |
@@ -75,18 +86,25 @@ static int user_instantiate(struct key *key, const void *data, size_t datalen) | |||
75 | /*****************************************************************************/ | 86 | /*****************************************************************************/ |
76 | /* | 87 | /* |
77 | * duplicate a user defined key | 88 | * duplicate a user defined key |
89 | * - both keys' semaphores are locked against further modification | ||
90 | * - the new key cannot yet be accessed | ||
78 | */ | 91 | */ |
79 | static int user_duplicate(struct key *key, const struct key *source) | 92 | static int user_duplicate(struct key *key, const struct key *source) |
80 | { | 93 | { |
94 | struct user_key_payload *upayload, *spayload; | ||
81 | int ret; | 95 | int ret; |
82 | 96 | ||
83 | /* just copy the payload */ | 97 | /* just copy the payload */ |
84 | ret = -ENOMEM; | 98 | ret = -ENOMEM; |
85 | key->payload.data = kmalloc(source->datalen, GFP_KERNEL); | 99 | upayload = kmalloc(sizeof(*upayload) + source->datalen, GFP_KERNEL); |
100 | if (upayload) { | ||
101 | spayload = rcu_dereference(source->payload.data); | ||
102 | BUG_ON(source->datalen != spayload->datalen); | ||
103 | |||
104 | upayload->datalen = key->datalen = spayload->datalen; | ||
105 | memcpy(upayload->data, spayload->data, key->datalen); | ||
86 | 106 | ||
87 | if (key->payload.data) { | 107 | key->payload.data = upayload; |
88 | key->datalen = source->datalen; | ||
89 | memcpy(key->payload.data, source->payload.data, source->datalen); | ||
90 | ret = 0; | 108 | ret = 0; |
91 | } | 109 | } |
92 | 110 | ||
@@ -96,40 +114,54 @@ static int user_duplicate(struct key *key, const struct key *source) | |||
96 | 114 | ||
97 | /*****************************************************************************/ | 115 | /*****************************************************************************/ |
98 | /* | 116 | /* |
117 | * dispose of the old data from an updated user defined key | ||
118 | */ | ||
119 | static void user_update_rcu_disposal(struct rcu_head *rcu) | ||
120 | { | ||
121 | struct user_key_payload *upayload; | ||
122 | |||
123 | upayload = container_of(rcu, struct user_key_payload, rcu); | ||
124 | |||
125 | kfree(upayload); | ||
126 | |||
127 | } /* end user_update_rcu_disposal() */ | ||
128 | |||
129 | /*****************************************************************************/ | ||
130 | /* | ||
99 | * update a user defined key | 131 | * update a user defined key |
132 | * - the key's semaphore is write-locked | ||
100 | */ | 133 | */ |
101 | static int user_update(struct key *key, const void *data, size_t datalen) | 134 | static int user_update(struct key *key, const void *data, size_t datalen) |
102 | { | 135 | { |
103 | void *new, *zap; | 136 | struct user_key_payload *upayload, *zap; |
104 | int ret; | 137 | int ret; |
105 | 138 | ||
106 | ret = -EINVAL; | 139 | ret = -EINVAL; |
107 | if (datalen <= 0 || datalen > 32767 || !data) | 140 | if (datalen <= 0 || datalen > 32767 || !data) |
108 | goto error; | 141 | goto error; |
109 | 142 | ||
110 | /* copy the data */ | 143 | /* construct a replacement payload */ |
111 | ret = -ENOMEM; | 144 | ret = -ENOMEM; |
112 | new = kmalloc(datalen, GFP_KERNEL); | 145 | upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); |
113 | if (!new) | 146 | if (!upayload) |
114 | goto error; | 147 | goto error; |
115 | 148 | ||
116 | memcpy(new, data, datalen); | 149 | upayload->datalen = datalen; |
150 | memcpy(upayload->data, data, datalen); | ||
117 | 151 | ||
118 | /* check the quota and attach the new data */ | 152 | /* check the quota and attach the new data */ |
119 | zap = new; | 153 | zap = upayload; |
120 | write_lock(&key->lock); | ||
121 | 154 | ||
122 | ret = key_payload_reserve(key, datalen); | 155 | ret = key_payload_reserve(key, datalen); |
123 | 156 | ||
124 | if (ret == 0) { | 157 | if (ret == 0) { |
125 | /* attach the new data, displacing the old */ | 158 | /* attach the new data, displacing the old */ |
126 | zap = key->payload.data; | 159 | zap = key->payload.data; |
127 | key->payload.data = new; | 160 | rcu_assign_pointer(key->payload.data, upayload); |
128 | key->expiry = 0; | 161 | key->expiry = 0; |
129 | } | 162 | } |
130 | 163 | ||
131 | write_unlock(&key->lock); | 164 | call_rcu(&zap->rcu, user_update_rcu_disposal); |
132 | kfree(zap); | ||
133 | 165 | ||
134 | error: | 166 | error: |
135 | return ret; | 167 | return ret; |
@@ -152,13 +184,15 @@ static int user_match(const struct key *key, const void *description) | |||
152 | */ | 184 | */ |
153 | static void user_destroy(struct key *key) | 185 | static void user_destroy(struct key *key) |
154 | { | 186 | { |
155 | kfree(key->payload.data); | 187 | struct user_key_payload *upayload = key->payload.data; |
188 | |||
189 | kfree(upayload); | ||
156 | 190 | ||
157 | } /* end user_destroy() */ | 191 | } /* end user_destroy() */ |
158 | 192 | ||
159 | /*****************************************************************************/ | 193 | /*****************************************************************************/ |
160 | /* | 194 | /* |
161 | * describe the user | 195 | * describe the user key |
162 | */ | 196 | */ |
163 | static void user_describe(const struct key *key, struct seq_file *m) | 197 | static void user_describe(const struct key *key, struct seq_file *m) |
164 | { | 198 | { |
@@ -171,18 +205,23 @@ static void user_describe(const struct key *key, struct seq_file *m) | |||
171 | /*****************************************************************************/ | 205 | /*****************************************************************************/ |
172 | /* | 206 | /* |
173 | * read the key data | 207 | * read the key data |
208 | * - the key's semaphore is read-locked | ||
174 | */ | 209 | */ |
175 | static long user_read(const struct key *key, | 210 | static long user_read(const struct key *key, |
176 | char __user *buffer, size_t buflen) | 211 | char __user *buffer, size_t buflen) |
177 | { | 212 | { |
178 | long ret = key->datalen; | 213 | struct user_key_payload *upayload; |
214 | long ret; | ||
215 | |||
216 | upayload = rcu_dereference(key->payload.data); | ||
217 | ret = upayload->datalen; | ||
179 | 218 | ||
180 | /* we can return the data as is */ | 219 | /* we can return the data as is */ |
181 | if (buffer && buflen > 0) { | 220 | if (buffer && buflen > 0) { |
182 | if (buflen > key->datalen) | 221 | if (buflen > upayload->datalen) |
183 | buflen = key->datalen; | 222 | buflen = upayload->datalen; |
184 | 223 | ||
185 | if (copy_to_user(buffer, key->payload.data, buflen) != 0) | 224 | if (copy_to_user(buffer, upayload->data, buflen) != 0) |
186 | ret = -EFAULT; | 225 | ret = -EFAULT; |
187 | } | 226 | } |
188 | 227 | ||