1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
|
/*
* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* Per-File-Key (PFK).
*
* PFK exposes API's for loading and removing keys from encryption hw
* and also API to determine whether 2 adjacent blocks can be agregated by
* Block Layer in one request to encryption hw.
*
* Please note, the only API that uses EXPORT_SYMBOL() is pfk_remove_key,
* this is intentionally, as it is the only API that is intended to be used
* by any kernel module, including dynamically loaded ones.
*/
/* Uncomment the line below to enable debug messages */
/* #define DEBUG 1 */
#define pr_fmt(fmt) "pfk [%s]: " fmt, __func__
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/printk.h>
#include <linux/bio.h>
#include <linux/security.h>
#include <crypto/ice.h>
#include <linux/pfk.h>
#include "pfk_kc.h"
#include "objsec.h"
#include "pfk_ice.h"
static DEFINE_MUTEX(pfk_lock);
static bool pfk_ready;
/* might be replaced by a table when more than one cipher is supported */
#define PFK_SUPPORTED_CIPHER "aes_xts"
#define PFK_SUPPORTED_KEY_SIZE 32
#define PFK_SUPPORTED_SALT_SIZE 32
static int pfk_inode_alloc_security(struct inode *inode)
{
struct inode_security_struct *i_sec = NULL;
if (inode == NULL)
return -EINVAL;
i_sec = kzalloc(sizeof(*i_sec), GFP_KERNEL);
if (i_sec == NULL)
return -ENOMEM;
inode->i_security = i_sec;
return 0;
}
static void pfk_inode_free_security(struct inode *inode)
{
if (inode == NULL)
return;
kzfree(inode->i_security);
}
static struct security_operations pfk_security_ops = {
.name = "pfk",
.inode_alloc_security = pfk_inode_alloc_security,
.inode_free_security = pfk_inode_free_security,
.allow_merge_bio = pfk_allow_merge_bio,
};
static int __init pfk_lsm_init(void)
{
int ret;
/* Check if PFK is the chosen lsm via security_module_enable() */
if (security_module_enable(&pfk_security_ops)) {
/* replace null callbacks with empty callbacks */
security_fixup_ops(&pfk_security_ops);
ret = register_security(&pfk_security_ops);
if (ret != 0) {
pr_err("pfk lsm registeration failed, ret=%d.\n", ret);
return ret;
}
pr_debug("pfk is the chosen lsm, registered successfully !\n");
} else {
pr_debug("pfk is not the chosen lsm.\n");
if (!selinux_is_enabled()) {
pr_err("se linux is not enabled.\n");
return -ENODEV;
}
}
return 0;
}
/**
* pfk_is_ready() - driver is initialized and ready.
*
* Return: true if the driver is ready.
*/
inline bool pfk_is_ready(void)
{
return pfk_ready;
}
EXPORT_SYMBOL(pfk_is_ready);
static int get_key_from_bio(const struct bio *bio, bool *is_pfe,
const unsigned char **key_ret, size_t *key_size_ret,
const unsigned char **salt_ret, size_t *salt_size_ret,
enum ice_cryto_algo_mode *algo_mode_ret,
enum ice_crpto_key_size *key_size_type_ret)
{
const struct bio_crypt_ctx *ctx = &bio->bi_crypt_ctx;
if (!(ctx->bc_flags & BC_ENCRYPT_FL)) {
*is_pfe = false;
return -ENOTSUPP;
}
if (!(ctx->bc_flags & BC_AES_256_XTS_FL)) {
pr_err("pfk: unsupported encryption algorithm (bc_flags=0x%x)\n",
ctx->bc_flags);
return -ENOTSUPP;
}
if (ctx->bc_key_size !=
PFK_SUPPORTED_KEY_SIZE + PFK_SUPPORTED_SALT_SIZE) {
pr_err("pfk: unsupported key size: %u\n", ctx->bc_key_size);
return -ENOTSUPP;
}
*key_ret = &ctx->bc_key[0];
*key_size_ret = PFK_SUPPORTED_KEY_SIZE;
*salt_ret = &ctx->bc_key[PFK_SUPPORTED_KEY_SIZE];
*salt_size_ret = PFK_SUPPORTED_SALT_SIZE;
*algo_mode_ret = ICE_CRYPTO_ALGO_MODE_AES_XTS;
BUILD_BUG_ON(PFK_SUPPORTED_KEY_SIZE != 256 / 8);
*key_size_type_ret = ICE_CRYPTO_KEY_SIZE_256;
return 0;
}
/**
* pfk_load_key_start() - loads PFE encryption key to the ICE
* Can also be invoked from non
* PFE context, than it is not
* relevant and is_pfe flag is
* set to true
* @bio: Pointer to the BIO structure
* @ice_setting: Pointer to ice setting structure that will be filled with
* ice configuration values, including the index to which the key was loaded
* @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked
* from PFE context
*
* Returns the index where the key is stored in encryption hw and additional
* information that will be used later for configuration of the encryption hw.
*
* Must be followed by pfk_load_key_end when key is no longer used by ice
*
*/
int pfk_load_key_start(const struct bio *bio,
struct ice_crypto_setting *ice_setting, bool *is_pfe,
bool async)
{
int ret = 0;
const unsigned char *key = NULL;
const unsigned char *salt = NULL;
size_t key_size = 0;
size_t salt_size = 0;
enum ice_cryto_algo_mode algo_mode = 0;
enum ice_crpto_key_size key_size_type = 0;
u32 key_index = 0;
if (!is_pfe) {
pr_err("is_pfe is NULL\n");
return -EINVAL;
}
/* only a few errors below can indicate that
* this function was not invoked within PFE context,
* otherwise we will consider it PFE
*/
*is_pfe = true;
if (!pfk_is_ready())
return -ENODEV;
if (!ice_setting) {
pr_err("ice setting is NULL\n");
return -EINVAL;
}
ret = get_key_from_bio(bio, is_pfe, &key, &key_size, &salt, &salt_size,
&algo_mode, &key_size_type);
if (ret)
return ret;
#if 0 /* debug */
printk(KERN_WARNING "%s: Loading key w/ key[0:1] == [%.2x%.2x] and "
"salt[0:1] == [%.2x%.2x] into key_index [%d]\n", __func__,
key[0], key[1], salt[0], salt[1], key_index);
#endif
ret = pfk_kc_load_key_start(key, key_size, salt, salt_size, &key_index,
async);
if (ret) {
if (ret != -EBUSY && ret != -EAGAIN) {
pr_err("start: could not load key into pfk key cache, "
"error %d\n", ret);
}
return ret;
}
ice_setting->key_size = key_size_type;
ice_setting->algo_mode = algo_mode;
/* hardcoded for now */
ice_setting->key_mode = ICE_CRYPTO_USE_LUT_SW_KEY;
ice_setting->key_index = key_index;
return 0;
}
/**
* pfk_load_key_end() - marks the PFE key as no longer used by ICE
* Can also be invoked from non
* PFE context, than it is not
* relevant and is_pfe flag is
* set to true
* @bio: Pointer to the BIO structure
* @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked
* from PFE context
*/
int pfk_load_key_end(const struct bio *bio, bool *is_pfe)
{
int ret = 0;
const unsigned char *key = NULL;
const unsigned char *salt = NULL;
size_t key_size = 0;
size_t salt_size = 0;
enum ice_cryto_algo_mode algo_mode = 0;
enum ice_crpto_key_size key_size_type = 0;
if (!is_pfe) {
pr_err("is_pfe is NULL\n");
return -EINVAL;
}
/* only a few errors below can indicate that
* this function was not invoked within PFE context,
* otherwise we will consider it PFE
*/
*is_pfe = true;
if (!pfk_is_ready())
return -ENODEV;
ret = get_key_from_bio(bio, is_pfe, &key, &key_size, &salt, &salt_size,
&algo_mode, &key_size_type);
if (ret)
return ret;
pfk_kc_load_key_end(key, key_size, salt, salt_size);
return 0;
}
/**
* pfk_remove_key() - removes key from hw
* @key: pointer to the key
* @key_size: key size
*
* Will be used by external clients to remove a particular key for security
* reasons.
* The only API that can be used by dynamically loaded modules,
* see explanations above at the beginning of this file.
* The key is removed securely (by memsetting the previous value)
*/
int pfk_remove_key(const unsigned char *key, size_t key_size)
{
int ret = 0;
if (!pfk_is_ready())
return -ENODEV;
if (!key)
return -EINVAL;
ret = pfk_kc_remove_key(key, key_size);
return ret;
}
EXPORT_SYMBOL(pfk_remove_key);
/**
* pfk_allow_merge_bio() - Check if 2 BIOs can be merged.
* @bio1: Pointer to first BIO structure.
* @bio2: Pointer to second BIO structure.
*
* Prevent merging of BIOs from encrypted and non-encrypted
* files, or files encrypted with different key.
* This API is called by the file system block layer.
*
* Return: true if the BIOs allowed to be merged, false
* otherwise.
*/
bool pfk_allow_merge_bio(struct bio *bio1, struct bio *bio2)
{
/* if there is no pfk, don't disallow merging blocks */
if (!pfk_is_ready())
return true;
/* cannot merge uninitialized BIO */
if (!bio1 || !bio2)
return false;
/* Allow other components to make the decision. */
return true;
}
static void __exit pfk_exit(void)
{
pfk_ready = false;
pfk_kc_deinit();
}
static int __init pfk_init(void)
{
int ret = 0;
ret = pfk_kc_init();
if (ret != 0) {
pr_err("could init pfk key cache, error %d\n", ret);
goto fail;
}
ret = pfk_lsm_init();
if (ret != 0) {
pr_debug("neither pfk nor se-linux sec modules are enabled\n");
pr_debug("not an error, just don't enable pfk\n");
pfk_kc_deinit();
return 0;
}
pfk_ready = true;
pr_info("Driver initialized successfully\n");
return 0;
fail:
pr_err("Failed to init driver\n");
return -ENODEV;
}
module_init(pfk_init);
module_exit(pfk_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Per-File-Key driver");
|