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