| 1 | |
| 2 |
|
| 3 |
|
| 4 | #include <linux/module.h>
|
| 5 | #include <linux/device.h>
|
| 6 | #include <linux/ndctl.h>
|
| 7 | #include <linux/slab.h>
|
| 8 | #include <linux/io.h>
|
| 9 | #include <linux/mm.h>
|
| 10 | #include <linux/cred.h>
|
| 11 | #include <linux/key.h>
|
| 12 | #include <linux/key-type.h>
|
| 13 | #include <keys/user-type.h>
|
| 14 | #include <keys/encrypted-type.h>
|
| 15 | #include "nd-core.h"
|
| 16 | #include "nd.h"
|
| 17 |
|
| 18 | #define NVDIMM_BASE_KEY 0
|
| 19 | #define NVDIMM_NEW_KEY 1
|
| 20 |
|
| 21 | static bool key_revalidate = true;
|
| 22 | module_param(key_revalidate, bool, 0444);
|
| 23 | MODULE_PARM_DESC(key_revalidate, "Require key validation at init.");
|
| 24 |
|
| 25 | static const char zero_key[NVDIMM_PASSPHRASE_LEN];
|
| 26 |
|
| 27 | static void *key_data(struct key *key)
|
| 28 | {
|
| 29 | struct encrypted_key_payload *epayload = dereference_key_locked(key);
|
| 30 |
|
| 31 | lockdep_assert_held_read(&key->sem);
|
| 32 |
|
| 33 | return epayload->decrypted_data;
|
| 34 | }
|
| 35 |
|
| 36 | static void nvdimm_put_key(struct key *key)
|
| 37 | {
|
| 38 | if (!key)
|
| 39 | return;
|
| 40 |
|
| 41 | up_read(&key->sem);
|
| 42 | key_put(key);
|
| 43 | }
|
| 44 |
|
| 45 | |
| 46 | * Retrieve kernel key for DIMM and request from user space if
|
| 47 | * necessary. Returns a key held for read and must be put by
|
| 48 | * nvdimm_put_key() before the usage goes out of scope.
|
| 49 |
|
| 50 | static struct key *nvdimm_request_key(struct nvdimm *nvdimm)
|
| 51 | {
|
| 52 | struct key *key = NULL;
|
| 53 | static const char NVDIMM_PREFIX[] = "nvdimm:";
|
| 54 | char desc[NVDIMM_KEY_DESC_LEN + sizeof(NVDIMM_PREFIX)];
|
| 55 | struct device *dev = &nvdimm->dev;
|
| 56 |
|
| 57 | sprintf(desc, "%s%s", NVDIMM_PREFIX, nvdimm->dimm_id);
|
| 58 | key = request_key(&key_type_encrypted, desc, "");
|
| 59 | if (IS_ERR(key)) {
|
| 60 | if (PTR_ERR(key) == -ENOKEY)
|
| 61 | dev_dbg(dev, "request_key() found no key\n");
|
| 62 | else
|
| 63 | dev_dbg(dev, "request_key() upcall failed\n");
|
| 64 | key = NULL;
|
| 65 | } else {
|
| 66 | struct encrypted_key_payload *epayload;
|
| 67 |
|
| 68 | down_read(&key->sem);
|
| 69 | epayload = dereference_key_locked(key);
|
| 70 | if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) {
|
| 71 | up_read(&key->sem);
|
| 72 | key_put(key);
|
| 73 | key = NULL;
|
| 74 | }
|
| 75 | }
|
| 76 |
|
| 77 | return key;
|
| 78 | }
|
| 79 |
|
| 80 | static const void *nvdimm_get_key_payload(struct nvdimm *nvdimm,
|
| 81 | struct key **key)
|
| 82 | {
|
| 83 | *key = nvdimm_request_key(nvdimm);
|
| 84 | if (!*key)
|
| 85 | return zero_key;
|
| 86 |
|
| 87 | return key_data(*key);
|
| 88 | }
|
| 89 |
|
| 90 | static struct key *nvdimm_lookup_user_key(struct nvdimm *nvdimm,
|
| 91 | key_serial_t id, int subclass)
|
| 92 | {
|
| 93 | key_ref_t keyref;
|
| 94 | struct key *key;
|
| 95 | struct encrypted_key_payload *epayload;
|
| 96 | struct device *dev = &nvdimm->dev;
|
| 97 |
|
| 98 | keyref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
|
| 99 | if (IS_ERR(keyref))
|
| 100 | return NULL;
|
| 101 |
|
| 102 | key = key_ref_to_ptr(keyref);
|
| 103 | if (key->type != &key_type_encrypted) {
|
| 104 | key_put(key);
|
| 105 | return NULL;
|
| 106 | }
|
| 107 |
|
| 108 | dev_dbg(dev, "%s: key found: %#x\n", __func__, key_serial(key));
|
| 109 |
|
| 110 | down_read_nested(&key->sem, subclass);
|
| 111 | epayload = dereference_key_locked(key);
|
| 112 | if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) {
|
| 113 | up_read(&key->sem);
|
| 114 | key_put(key);
|
| 115 | key = NULL;
|
| 116 | }
|
| 117 | return key;
|
| 118 | }
|
| 119 |
|
| 120 | static const void *nvdimm_get_user_key_payload(struct nvdimm *nvdimm,
|
| 121 | key_serial_t id, int subclass, struct key **key)
|
| 122 | {
|
| 123 | *key = NULL;
|
| 124 | if (id == 0) {
|
| 125 | if (subclass == NVDIMM_BASE_KEY)
|
| 126 | return zero_key;
|
| 127 | else
|
| 128 | return NULL;
|
| 129 | }
|
| 130 |
|
| 131 | *key = nvdimm_lookup_user_key(nvdimm, id, subclass);
|
| 132 | if (!*key)
|
| 133 | return NULL;
|
| 134 |
|
| 135 | return key_data(*key);
|
| 136 | }
|
| 137 |
|
| 138 |
|
| 139 | static int nvdimm_key_revalidate(struct nvdimm *nvdimm)
|
| 140 | {
|
| 141 | struct key *key;
|
| 142 | int rc;
|
| 143 | const void *data;
|
| 144 |
|
| 145 | if (!nvdimm->sec.ops->change_key)
|
| 146 | return -EOPNOTSUPP;
|
| 147 |
|
| 148 | data = nvdimm_get_key_payload(nvdimm, &key);
|
| 149 |
|
| 150 | |
| 151 | * Send the same key to the hardware as new and old key to
|
| 152 | * verify that the key is good.
|
| 153 |
|
| 154 | rc = nvdimm->sec.ops->change_key(nvdimm, data, data, NVDIMM_USER);
|
| 155 | if (rc < 0) {
|
| 156 | nvdimm_put_key(key);
|
| 157 | return rc;
|
| 158 | }
|
| 159 |
|
| 160 | nvdimm_put_key(key);
|
| 161 | nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
|
| 162 | return 0;
|
| 163 | }
|
| 164 |
|
| 165 | static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
|
| 166 | {
|
| 167 | struct device *dev = &nvdimm->dev;
|
| 168 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
| 169 | struct key *key;
|
| 170 | const void *data;
|
| 171 | int rc;
|
| 172 |
|
| 173 |
|
| 174 | lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
|
| 175 |
|
| 176 | if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock
|
| 177 | || !nvdimm->sec.flags)
|
| 178 | return -EIO;
|
| 179 |
|
| 180 |
|
| 181 | if (IS_ENABLED(CONFIG_NVDIMM_SECURITY_TEST))
|
| 182 | nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
|
| 183 |
|
| 184 |
|
| 185 | if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
|
| 186 | return 0;
|
| 187 |
|
| 188 | if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
|
| 189 | dev_dbg(dev, "Security operation in progress.\n");
|
| 190 | return -EBUSY;
|
| 191 | }
|
| 192 |
|
| 193 | |
| 194 | * If the pre-OS has unlocked the DIMM, attempt to send the key
|
| 195 | * from request_key() to the hardware for verification. Failure
|
| 196 | * to revalidate the key against the hardware results in a
|
| 197 | * freeze of the security configuration. I.e. if the OS does not
|
| 198 | * have the key, security is being managed pre-OS.
|
| 199 |
|
| 200 | if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) {
|
| 201 | if (!key_revalidate)
|
| 202 | return 0;
|
| 203 |
|
| 204 | return nvdimm_key_revalidate(nvdimm);
|
| 205 | } else
|
| 206 | data = nvdimm_get_key_payload(nvdimm, &key);
|
| 207 |
|
| 208 | rc = nvdimm->sec.ops->unlock(nvdimm, data);
|
| 209 | dev_dbg(dev, "key: %d unlock: %s\n", key_serial(key),
|
| 210 | rc == 0 ? "success" : "fail");
|
| 211 | if (rc == 0)
|
| 212 | set_bit(NDD_INCOHERENT, &nvdimm->flags);
|
| 213 |
|
| 214 | nvdimm_put_key(key);
|
| 215 | nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
|
| 216 | return rc;
|
| 217 | }
|
| 218 |
|
| 219 | int nvdimm_security_unlock(struct device *dev)
|
| 220 | {
|
| 221 | struct nvdimm *nvdimm = to_nvdimm(dev);
|
| 222 |
|
| 223 | guard(nvdimm_bus)(dev);
|
| 224 | return __nvdimm_security_unlock(nvdimm);
|
| 225 | }
|
| 226 |
|
| 227 | static int check_security_state(struct nvdimm *nvdimm)
|
| 228 | {
|
| 229 | struct device *dev = &nvdimm->dev;
|
| 230 |
|
| 231 | if (test_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags)) {
|
| 232 | dev_dbg(dev, "Incorrect security state: %#lx\n",
|
| 233 | nvdimm->sec.flags);
|
| 234 | return -EIO;
|
| 235 | }
|
| 236 |
|
| 237 | if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
|
| 238 | dev_dbg(dev, "Security operation in progress.\n");
|
| 239 | return -EBUSY;
|
| 240 | }
|
| 241 |
|
| 242 | return 0;
|
| 243 | }
|
| 244 |
|
| 245 | static int security_disable(struct nvdimm *nvdimm, unsigned int keyid,
|
| 246 | enum nvdimm_passphrase_type pass_type)
|
| 247 | {
|
| 248 | struct device *dev = &nvdimm->dev;
|
| 249 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
| 250 | struct key *key;
|
| 251 | int rc;
|
| 252 | const void *data;
|
| 253 |
|
| 254 |
|
| 255 | lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
|
| 256 |
|
| 257 | if (!nvdimm->sec.ops || !nvdimm->sec.flags)
|
| 258 | return -EOPNOTSUPP;
|
| 259 |
|
| 260 | if (pass_type == NVDIMM_USER && !nvdimm->sec.ops->disable)
|
| 261 | return -EOPNOTSUPP;
|
| 262 |
|
| 263 | if (pass_type == NVDIMM_MASTER && !nvdimm->sec.ops->disable_master)
|
| 264 | return -EOPNOTSUPP;
|
| 265 |
|
| 266 | rc = check_security_state(nvdimm);
|
| 267 | if (rc)
|
| 268 | return rc;
|
| 269 |
|
| 270 | data = nvdimm_get_user_key_payload(nvdimm, keyid,
|
| 271 | NVDIMM_BASE_KEY, &key);
|
| 272 | if (!data)
|
| 273 | return -ENOKEY;
|
| 274 |
|
| 275 | if (pass_type == NVDIMM_MASTER) {
|
| 276 | rc = nvdimm->sec.ops->disable_master(nvdimm, data);
|
| 277 | dev_dbg(dev, "key: %d disable_master: %s\n", key_serial(key),
|
| 278 | rc == 0 ? "success" : "fail");
|
| 279 | } else {
|
| 280 | rc = nvdimm->sec.ops->disable(nvdimm, data);
|
| 281 | dev_dbg(dev, "key: %d disable: %s\n", key_serial(key),
|
| 282 | rc == 0 ? "success" : "fail");
|
| 283 | }
|
| 284 |
|
| 285 | nvdimm_put_key(key);
|
| 286 | if (pass_type == NVDIMM_MASTER)
|
| 287 | nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
|
| 288 | else
|
| 289 | nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
|
| 290 | return rc;
|
| 291 | }
|
| 292 |
|
| 293 | static int security_update(struct nvdimm *nvdimm, unsigned int keyid,
|
| 294 | unsigned int new_keyid,
|
| 295 | enum nvdimm_passphrase_type pass_type)
|
| 296 | {
|
| 297 | struct device *dev = &nvdimm->dev;
|
| 298 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
| 299 | struct key *key, *newkey;
|
| 300 | int rc;
|
| 301 | const void *data, *newdata;
|
| 302 |
|
| 303 |
|
| 304 | lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
|
| 305 |
|
| 306 | if (!nvdimm->sec.ops || !nvdimm->sec.ops->change_key
|
| 307 | || !nvdimm->sec.flags)
|
| 308 | return -EOPNOTSUPP;
|
| 309 |
|
| 310 | rc = check_security_state(nvdimm);
|
| 311 | if (rc)
|
| 312 | return rc;
|
| 313 |
|
| 314 | data = nvdimm_get_user_key_payload(nvdimm, keyid,
|
| 315 | NVDIMM_BASE_KEY, &key);
|
| 316 | if (!data)
|
| 317 | return -ENOKEY;
|
| 318 |
|
| 319 | newdata = nvdimm_get_user_key_payload(nvdimm, new_keyid,
|
| 320 | NVDIMM_NEW_KEY, &newkey);
|
| 321 | if (!newdata) {
|
| 322 | nvdimm_put_key(key);
|
| 323 | return -ENOKEY;
|
| 324 | }
|
| 325 |
|
| 326 | rc = nvdimm->sec.ops->change_key(nvdimm, data, newdata, pass_type);
|
| 327 | dev_dbg(dev, "key: %d %d update%s: %s\n",
|
| 328 | key_serial(key), key_serial(newkey),
|
| 329 | pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
|
| 330 | rc == 0 ? "success" : "fail");
|
| 331 |
|
| 332 | nvdimm_put_key(newkey);
|
| 333 | nvdimm_put_key(key);
|
| 334 | if (pass_type == NVDIMM_MASTER)
|
| 335 | nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm,
|
| 336 | NVDIMM_MASTER);
|
| 337 | else
|
| 338 | nvdimm->sec.flags = nvdimm_security_flags(nvdimm,
|
| 339 | NVDIMM_USER);
|
| 340 | return rc;
|
| 341 | }
|
| 342 |
|
| 343 | static int security_erase(struct nvdimm *nvdimm, unsigned int keyid,
|
| 344 | enum nvdimm_passphrase_type pass_type)
|
| 345 | {
|
| 346 | struct device *dev = &nvdimm->dev;
|
| 347 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
| 348 | struct key *key = NULL;
|
| 349 | int rc;
|
| 350 | const void *data;
|
| 351 |
|
| 352 |
|
| 353 | lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
|
| 354 |
|
| 355 | if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase
|
| 356 | || !nvdimm->sec.flags)
|
| 357 | return -EOPNOTSUPP;
|
| 358 |
|
| 359 | rc = check_security_state(nvdimm);
|
| 360 | if (rc)
|
| 361 | return rc;
|
| 362 |
|
| 363 | if (!test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.ext_flags)
|
| 364 | && pass_type == NVDIMM_MASTER) {
|
| 365 | dev_dbg(dev,
|
| 366 | "Attempt to secure erase in wrong master state.\n");
|
| 367 | return -EOPNOTSUPP;
|
| 368 | }
|
| 369 |
|
| 370 | data = nvdimm_get_user_key_payload(nvdimm, keyid,
|
| 371 | NVDIMM_BASE_KEY, &key);
|
| 372 | if (!data)
|
| 373 | return -ENOKEY;
|
| 374 |
|
| 375 | rc = nvdimm->sec.ops->erase(nvdimm, data, pass_type);
|
| 376 | if (rc == 0)
|
| 377 | set_bit(NDD_INCOHERENT, &nvdimm->flags);
|
| 378 | dev_dbg(dev, "key: %d erase%s: %s\n", key_serial(key),
|
| 379 | pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
|
| 380 | rc == 0 ? "success" : "fail");
|
| 381 |
|
| 382 | nvdimm_put_key(key);
|
| 383 | nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
|
| 384 | return rc;
|
| 385 | }
|
| 386 |
|
| 387 | static int security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
|
| 388 | {
|
| 389 | struct device *dev = &nvdimm->dev;
|
| 390 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
| 391 | struct key *key = NULL;
|
| 392 | int rc;
|
| 393 | const void *data;
|
| 394 |
|
| 395 |
|
| 396 | lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
|
| 397 |
|
| 398 | if (!nvdimm->sec.ops || !nvdimm->sec.ops->overwrite
|
| 399 | || !nvdimm->sec.flags)
|
| 400 | return -EOPNOTSUPP;
|
| 401 |
|
| 402 | rc = check_security_state(nvdimm);
|
| 403 | if (rc)
|
| 404 | return rc;
|
| 405 |
|
| 406 | data = nvdimm_get_user_key_payload(nvdimm, keyid,
|
| 407 | NVDIMM_BASE_KEY, &key);
|
| 408 | if (!data)
|
| 409 | return -ENOKEY;
|
| 410 |
|
| 411 | rc = nvdimm->sec.ops->overwrite(nvdimm, data);
|
| 412 | if (rc == 0)
|
| 413 | set_bit(NDD_INCOHERENT, &nvdimm->flags);
|
| 414 | dev_dbg(dev, "key: %d overwrite submission: %s\n", key_serial(key),
|
| 415 | rc == 0 ? "success" : "fail");
|
| 416 |
|
| 417 | nvdimm_put_key(key);
|
| 418 | if (rc == 0) {
|
| 419 | set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
|
| 420 | set_bit(NDD_WORK_PENDING, &nvdimm->flags);
|
| 421 | set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags);
|
| 422 | |
| 423 | * Make sure we don't lose device while doing overwrite
|
| 424 | * query.
|
| 425 |
|
| 426 | get_device(dev);
|
| 427 | queue_delayed_work(system_percpu_wq, &nvdimm->dwork, 0);
|
| 428 | }
|
| 429 |
|
| 430 | return rc;
|
| 431 | }
|
| 432 |
|
| 433 | static void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
|
| 434 | {
|
| 435 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev);
|
| 436 | int rc;
|
| 437 | unsigned int tmo;
|
| 438 |
|
| 439 |
|
| 440 | lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
|
| 441 |
|
| 442 | |
| 443 | * Abort and release device if we no longer have the overwrite
|
| 444 | * flag set. It means the work has been canceled.
|
| 445 |
|
| 446 | if (!test_bit(NDD_WORK_PENDING, &nvdimm->flags))
|
| 447 | return;
|
| 448 |
|
| 449 | tmo = nvdimm->sec.overwrite_tmo;
|
| 450 |
|
| 451 | if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite
|
| 452 | || !nvdimm->sec.flags)
|
| 453 | return;
|
| 454 |
|
| 455 | rc = nvdimm->sec.ops->query_overwrite(nvdimm);
|
| 456 | if (rc == -EBUSY) {
|
| 457 |
|
| 458 |
|
| 459 | tmo += 10;
|
| 460 | queue_delayed_work(system_percpu_wq, &nvdimm->dwork, tmo * HZ);
|
| 461 | nvdimm->sec.overwrite_tmo = min(15U * 60U, tmo);
|
| 462 | return;
|
| 463 | }
|
| 464 |
|
| 465 | if (rc < 0)
|
| 466 | dev_dbg(&nvdimm->dev, "overwrite failed\n");
|
| 467 | else
|
| 468 | dev_dbg(&nvdimm->dev, "overwrite completed\n");
|
| 469 |
|
| 470 | |
| 471 | * Mark the overwrite work done and update dimm security flags,
|
| 472 | * then send a sysfs event notification to wake up userspace
|
| 473 | * poll threads to picked up the changed state.
|
| 474 |
|
| 475 | nvdimm->sec.overwrite_tmo = 0;
|
| 476 | clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
|
| 477 | clear_bit(NDD_WORK_PENDING, &nvdimm->flags);
|
| 478 | nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
|
| 479 | nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
|
| 480 | if (nvdimm->sec.overwrite_state)
|
| 481 | sysfs_notify_dirent(nvdimm->sec.overwrite_state);
|
| 482 | put_device(&nvdimm->dev);
|
| 483 | }
|
| 484 |
|
| 485 | void nvdimm_security_overwrite_query(struct work_struct *work)
|
| 486 | {
|
| 487 | struct nvdimm *nvdimm =
|
| 488 | container_of(work, typeof(*nvdimm), dwork.work);
|
| 489 |
|
| 490 | guard(nvdimm_bus)(&nvdimm->dev);
|
| 491 | __nvdimm_security_overwrite_query(nvdimm);
|
| 492 | }
|
| 493 |
|
| 494 | #define OPS \
|
| 495 | C( OP_FREEZE, "freeze", 1), \
|
| 496 | C( OP_DISABLE, "disable", 2), \
|
| 497 | C( OP_DISABLE_MASTER, "disable_master", 2), \
|
| 498 | C( OP_UPDATE, "update", 3), \
|
| 499 | C( OP_ERASE, "erase", 2), \
|
| 500 | C( OP_OVERWRITE, "overwrite", 2), \
|
| 501 | C( OP_MASTER_UPDATE, "master_update", 3), \
|
| 502 | C( OP_MASTER_ERASE, "master_erase", 2)
|
| 503 | #undef C
|
| 504 | #define C(a, b, c) a
|
| 505 | enum nvdimmsec_op_ids { OPS };
|
| 506 | #undef C
|
| 507 | #define C(a, b, c) { b, c }
|
| 508 | static struct {
|
| 509 | const char *name;
|
| 510 | int args;
|
| 511 | } ops[] = { OPS };
|
| 512 | #undef C
|
| 513 |
|
| 514 | #define SEC_CMD_SIZE 32
|
| 515 | #define KEY_ID_SIZE 10
|
| 516 |
|
| 517 | ssize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len)
|
| 518 | {
|
| 519 | struct nvdimm *nvdimm = to_nvdimm(dev);
|
| 520 | ssize_t rc;
|
| 521 | char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1],
|
| 522 | nkeystr[KEY_ID_SIZE+1];
|
| 523 | unsigned int key, newkey;
|
| 524 | int i;
|
| 525 |
|
| 526 | rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
|
| 527 | " %"__stringify(KEY_ID_SIZE)"s"
|
| 528 | " %"__stringify(KEY_ID_SIZE)"s",
|
| 529 | cmd, keystr, nkeystr);
|
| 530 | if (rc < 1)
|
| 531 | return -EINVAL;
|
| 532 | for (i = 0; i < ARRAY_SIZE(ops); i++)
|
| 533 | if (sysfs_streq(cmd, ops[i].name))
|
| 534 | break;
|
| 535 | if (i >= ARRAY_SIZE(ops))
|
| 536 | return -EINVAL;
|
| 537 | if (ops[i].args > 1)
|
| 538 | rc = kstrtouint(keystr, 0, &key);
|
| 539 | if (rc >= 0 && ops[i].args > 2)
|
| 540 | rc = kstrtouint(nkeystr, 0, &newkey);
|
| 541 | if (rc < 0)
|
| 542 | return rc;
|
| 543 |
|
| 544 | if (i == OP_FREEZE) {
|
| 545 | dev_dbg(dev, "freeze\n");
|
| 546 | rc = nvdimm_security_freeze(nvdimm);
|
| 547 | } else if (i == OP_DISABLE) {
|
| 548 | dev_dbg(dev, "disable %u\n", key);
|
| 549 | rc = security_disable(nvdimm, key, NVDIMM_USER);
|
| 550 | } else if (i == OP_DISABLE_MASTER) {
|
| 551 | dev_dbg(dev, "disable_master %u\n", key);
|
| 552 | rc = security_disable(nvdimm, key, NVDIMM_MASTER);
|
| 553 | } else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) {
|
| 554 | dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey);
|
| 555 | rc = security_update(nvdimm, key, newkey, i == OP_UPDATE
|
| 556 | ? NVDIMM_USER : NVDIMM_MASTER);
|
| 557 | } else if (i == OP_ERASE || i == OP_MASTER_ERASE) {
|
| 558 | dev_dbg(dev, "%s %u\n", ops[i].name, key);
|
| 559 | if (atomic_read(&nvdimm->busy)) {
|
| 560 | dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
|
| 561 | return -EBUSY;
|
| 562 | }
|
| 563 | rc = security_erase(nvdimm, key, i == OP_ERASE
|
| 564 | ? NVDIMM_USER : NVDIMM_MASTER);
|
| 565 | } else if (i == OP_OVERWRITE) {
|
| 566 | dev_dbg(dev, "overwrite %u\n", key);
|
| 567 | if (atomic_read(&nvdimm->busy)) {
|
| 568 | dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
|
| 569 | return -EBUSY;
|
| 570 | }
|
| 571 | rc = security_overwrite(nvdimm, key);
|
| 572 | } else
|
| 573 | return -EINVAL;
|
| 574 |
|
| 575 | if (rc == 0)
|
| 576 | rc = len;
|
| 577 | return rc;
|
| 578 | }
|