1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * NVMe over Fabrics DH-HMAC-CHAP authentication. 4 * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. 5 * All rights reserved. 6 */ 7 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 8 #include <linux/module.h> 9 #include <linux/init.h> 10 #include <linux/slab.h> 11 #include <linux/err.h> 12 #include <crypto/hash.h> 13 #include <linux/crc32.h> 14 #include <linux/base64.h> 15 #include <linux/ctype.h> 16 #include <linux/random.h> 17 #include <linux/nvme-auth.h> 18 #include <asm/unaligned.h> 19 20 #include "nvmet.h" 21 22 int nvmet_auth_set_key(struct nvmet_host *host, const char *secret, 23 bool set_ctrl) 24 { 25 unsigned char key_hash; 26 char *dhchap_secret; 27 28 if (sscanf(secret, "DHHC-1:%hhd:%*s", &key_hash) != 1) 29 return -EINVAL; 30 if (key_hash > 3) { 31 pr_warn("Invalid DH-HMAC-CHAP hash id %d\n", 32 key_hash); 33 return -EINVAL; 34 } 35 if (key_hash > 0) { 36 /* Validate selected hash algorithm */ 37 const char *hmac = nvme_auth_hmac_name(key_hash); 38 39 if (!crypto_has_shash(hmac, 0, 0)) { 40 pr_err("DH-HMAC-CHAP hash %s unsupported\n", hmac); 41 return -ENOTSUPP; 42 } 43 } 44 dhchap_secret = kstrdup(secret, GFP_KERNEL); 45 if (!dhchap_secret) 46 return -ENOMEM; 47 down_write(&nvmet_config_sem); 48 if (set_ctrl) { 49 kfree(host->dhchap_ctrl_secret); 50 host->dhchap_ctrl_secret = strim(dhchap_secret); 51 host->dhchap_ctrl_key_hash = key_hash; 52 } else { 53 kfree(host->dhchap_secret); 54 host->dhchap_secret = strim(dhchap_secret); 55 host->dhchap_key_hash = key_hash; 56 } 57 up_write(&nvmet_config_sem); 58 return 0; 59 } 60 61 int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id) 62 { 63 const char *dhgroup_kpp; 64 int ret = 0; 65 66 pr_debug("%s: ctrl %d selecting dhgroup %d\n", 67 __func__, ctrl->cntlid, dhgroup_id); 68 69 if (ctrl->dh_tfm) { 70 if (ctrl->dh_gid == dhgroup_id) { 71 pr_debug("%s: ctrl %d reuse existing DH group %d\n", 72 __func__, ctrl->cntlid, dhgroup_id); 73 return 0; 74 } 75 crypto_free_kpp(ctrl->dh_tfm); 76 ctrl->dh_tfm = NULL; 77 ctrl->dh_gid = 0; 78 } 79 80 if (dhgroup_id == NVME_AUTH_DHGROUP_NULL) 81 return 0; 82 83 dhgroup_kpp = nvme_auth_dhgroup_kpp(dhgroup_id); 84 if (!dhgroup_kpp) { 85 pr_debug("%s: ctrl %d invalid DH group %d\n", 86 __func__, ctrl->cntlid, dhgroup_id); 87 return -EINVAL; 88 } 89 ctrl->dh_tfm = crypto_alloc_kpp(dhgroup_kpp, 0, 0); 90 if (IS_ERR(ctrl->dh_tfm)) { 91 pr_debug("%s: ctrl %d failed to setup DH group %d, err %ld\n", 92 __func__, ctrl->cntlid, dhgroup_id, 93 PTR_ERR(ctrl->dh_tfm)); 94 ret = PTR_ERR(ctrl->dh_tfm); 95 ctrl->dh_tfm = NULL; 96 ctrl->dh_gid = 0; 97 } else { 98 ctrl->dh_gid = dhgroup_id; 99 pr_debug("%s: ctrl %d setup DH group %d\n", 100 __func__, ctrl->cntlid, ctrl->dh_gid); 101 ret = nvme_auth_gen_privkey(ctrl->dh_tfm, ctrl->dh_gid); 102 if (ret < 0) { 103 pr_debug("%s: ctrl %d failed to generate private key, err %d\n", 104 __func__, ctrl->cntlid, ret); 105 kfree_sensitive(ctrl->dh_key); 106 return ret; 107 } 108 ctrl->dh_keysize = crypto_kpp_maxsize(ctrl->dh_tfm); 109 kfree_sensitive(ctrl->dh_key); 110 ctrl->dh_key = kzalloc(ctrl->dh_keysize, GFP_KERNEL); 111 if (!ctrl->dh_key) { 112 pr_warn("ctrl %d failed to allocate public key\n", 113 ctrl->cntlid); 114 return -ENOMEM; 115 } 116 ret = nvme_auth_gen_pubkey(ctrl->dh_tfm, ctrl->dh_key, 117 ctrl->dh_keysize); 118 if (ret < 0) { 119 pr_warn("ctrl %d failed to generate public key\n", 120 ctrl->cntlid); 121 kfree(ctrl->dh_key); 122 ctrl->dh_key = NULL; 123 } 124 } 125 126 return ret; 127 } 128 129 u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl) 130 { 131 int ret = 0; 132 struct nvmet_host_link *p; 133 struct nvmet_host *host = NULL; 134 135 down_read(&nvmet_config_sem); 136 if (nvmet_is_disc_subsys(ctrl->subsys)) 137 goto out_unlock; 138 139 if (ctrl->subsys->allow_any_host) 140 goto out_unlock; 141 142 list_for_each_entry(p, &ctrl->subsys->hosts, entry) { 143 pr_debug("check %s\n", nvmet_host_name(p->host)); 144 if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn)) 145 continue; 146 host = p->host; 147 break; 148 } 149 if (!host) { 150 pr_debug("host %s not found\n", ctrl->hostnqn); 151 ret = NVME_AUTH_DHCHAP_FAILURE_FAILED; 152 goto out_unlock; 153 } 154 155 ret = nvmet_setup_dhgroup(ctrl, host->dhchap_dhgroup_id); 156 if (ret < 0) { 157 pr_warn("Failed to setup DH group"); 158 ret = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; 159 goto out_unlock; 160 } 161 162 if (!host->dhchap_secret) { 163 pr_debug("No authentication provided\n"); 164 goto out_unlock; 165 } 166 167 if (host->dhchap_hash_id == ctrl->shash_id) { 168 pr_debug("Re-use existing hash ID %d\n", 169 ctrl->shash_id); 170 } else { 171 ctrl->shash_id = host->dhchap_hash_id; 172 } 173 174 /* Skip the 'DHHC-1:XX:' prefix */ 175 nvme_auth_free_key(ctrl->host_key); 176 ctrl->host_key = nvme_auth_extract_key(host->dhchap_secret + 10, 177 host->dhchap_key_hash); 178 if (IS_ERR(ctrl->host_key)) { 179 ret = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; 180 ctrl->host_key = NULL; 181 goto out_free_hash; 182 } 183 pr_debug("%s: using hash %s key %*ph\n", __func__, 184 ctrl->host_key->hash > 0 ? 185 nvme_auth_hmac_name(ctrl->host_key->hash) : "none", 186 (int)ctrl->host_key->len, ctrl->host_key->key); 187 188 nvme_auth_free_key(ctrl->ctrl_key); 189 if (!host->dhchap_ctrl_secret) { 190 ctrl->ctrl_key = NULL; 191 goto out_unlock; 192 } 193 194 ctrl->ctrl_key = nvme_auth_extract_key(host->dhchap_ctrl_secret + 10, 195 host->dhchap_ctrl_key_hash); 196 if (IS_ERR(ctrl->ctrl_key)) { 197 ret = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE; 198 ctrl->ctrl_key = NULL; 199 goto out_free_hash; 200 } 201 pr_debug("%s: using ctrl hash %s key %*ph\n", __func__, 202 ctrl->ctrl_key->hash > 0 ? 203 nvme_auth_hmac_name(ctrl->ctrl_key->hash) : "none", 204 (int)ctrl->ctrl_key->len, ctrl->ctrl_key->key); 205 206 out_free_hash: 207 if (ret) { 208 if (ctrl->host_key) { 209 nvme_auth_free_key(ctrl->host_key); 210 ctrl->host_key = NULL; 211 } 212 ctrl->shash_id = 0; 213 } 214 out_unlock: 215 up_read(&nvmet_config_sem); 216 217 return ret; 218 } 219 220 void nvmet_auth_sq_free(struct nvmet_sq *sq) 221 { 222 cancel_delayed_work(&sq->auth_expired_work); 223 kfree(sq->dhchap_c1); 224 sq->dhchap_c1 = NULL; 225 kfree(sq->dhchap_c2); 226 sq->dhchap_c2 = NULL; 227 kfree(sq->dhchap_skey); 228 sq->dhchap_skey = NULL; 229 } 230 231 void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) 232 { 233 ctrl->shash_id = 0; 234 235 if (ctrl->dh_tfm) { 236 crypto_free_kpp(ctrl->dh_tfm); 237 ctrl->dh_tfm = NULL; 238 ctrl->dh_gid = 0; 239 } 240 kfree_sensitive(ctrl->dh_key); 241 ctrl->dh_key = NULL; 242 243 if (ctrl->host_key) { 244 nvme_auth_free_key(ctrl->host_key); 245 ctrl->host_key = NULL; 246 } 247 if (ctrl->ctrl_key) { 248 nvme_auth_free_key(ctrl->ctrl_key); 249 ctrl->ctrl_key = NULL; 250 } 251 } 252 253 bool nvmet_check_auth_status(struct nvmet_req *req) 254 { 255 if (req->sq->ctrl->host_key && 256 !req->sq->authenticated) 257 return false; 258 return true; 259 } 260 261 int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, 262 unsigned int shash_len) 263 { 264 struct crypto_shash *shash_tfm; 265 struct shash_desc *shash; 266 struct nvmet_ctrl *ctrl = req->sq->ctrl; 267 const char *hash_name; 268 u8 *challenge = req->sq->dhchap_c1; 269 struct nvme_dhchap_key *transformed_key; 270 u8 buf[4]; 271 int ret; 272 273 hash_name = nvme_auth_hmac_name(ctrl->shash_id); 274 if (!hash_name) { 275 pr_warn("Hash ID %d invalid\n", ctrl->shash_id); 276 return -EINVAL; 277 } 278 279 shash_tfm = crypto_alloc_shash(hash_name, 0, 0); 280 if (IS_ERR(shash_tfm)) { 281 pr_err("failed to allocate shash %s\n", hash_name); 282 return PTR_ERR(shash_tfm); 283 } 284 285 if (shash_len != crypto_shash_digestsize(shash_tfm)) { 286 pr_err("%s: hash len mismatch (len %d digest %d)\n", 287 __func__, shash_len, 288 crypto_shash_digestsize(shash_tfm)); 289 ret = -EINVAL; 290 goto out_free_tfm; 291 } 292 293 transformed_key = nvme_auth_transform_key(ctrl->host_key, 294 ctrl->hostnqn); 295 if (IS_ERR(transformed_key)) { 296 ret = PTR_ERR(transformed_key); 297 goto out_free_tfm; 298 } 299 300 ret = crypto_shash_setkey(shash_tfm, transformed_key->key, 301 transformed_key->len); 302 if (ret) 303 goto out_free_response; 304 305 if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) { 306 challenge = kmalloc(shash_len, GFP_KERNEL); 307 if (!challenge) { 308 ret = -ENOMEM; 309 goto out_free_response; 310 } 311 ret = nvme_auth_augmented_challenge(ctrl->shash_id, 312 req->sq->dhchap_skey, 313 req->sq->dhchap_skey_len, 314 req->sq->dhchap_c1, 315 challenge, shash_len); 316 if (ret) 317 goto out_free_challenge; 318 } 319 320 pr_debug("ctrl %d qid %d host response seq %u transaction %d\n", 321 ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, 322 req->sq->dhchap_tid); 323 324 shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm), 325 GFP_KERNEL); 326 if (!shash) { 327 ret = -ENOMEM; 328 goto out_free_challenge; 329 } 330 shash->tfm = shash_tfm; 331 ret = crypto_shash_init(shash); 332 if (ret) 333 goto out; 334 ret = crypto_shash_update(shash, challenge, shash_len); 335 if (ret) 336 goto out; 337 put_unaligned_le32(req->sq->dhchap_s1, buf); 338 ret = crypto_shash_update(shash, buf, 4); 339 if (ret) 340 goto out; 341 put_unaligned_le16(req->sq->dhchap_tid, buf); 342 ret = crypto_shash_update(shash, buf, 2); 343 if (ret) 344 goto out; 345 memset(buf, 0, 4); 346 ret = crypto_shash_update(shash, buf, 1); 347 if (ret) 348 goto out; 349 ret = crypto_shash_update(shash, "HostHost", 8); 350 if (ret) 351 goto out; 352 ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn)); 353 if (ret) 354 goto out; 355 ret = crypto_shash_update(shash, buf, 1); 356 if (ret) 357 goto out; 358 ret = crypto_shash_update(shash, ctrl->subsysnqn, 359 strlen(ctrl->subsysnqn)); 360 if (ret) 361 goto out; 362 ret = crypto_shash_final(shash, response); 363 out: 364 kfree(shash); 365 out_free_challenge: 366 if (challenge != req->sq->dhchap_c1) 367 kfree(challenge); 368 out_free_response: 369 nvme_auth_free_key(transformed_key); 370 out_free_tfm: 371 crypto_free_shash(shash_tfm); 372 return ret; 373 } 374 375 int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response, 376 unsigned int shash_len) 377 { 378 struct crypto_shash *shash_tfm; 379 struct shash_desc *shash; 380 struct nvmet_ctrl *ctrl = req->sq->ctrl; 381 const char *hash_name; 382 u8 *challenge = req->sq->dhchap_c2; 383 struct nvme_dhchap_key *transformed_key; 384 u8 buf[4]; 385 int ret; 386 387 hash_name = nvme_auth_hmac_name(ctrl->shash_id); 388 if (!hash_name) { 389 pr_warn("Hash ID %d invalid\n", ctrl->shash_id); 390 return -EINVAL; 391 } 392 393 shash_tfm = crypto_alloc_shash(hash_name, 0, 0); 394 if (IS_ERR(shash_tfm)) { 395 pr_err("failed to allocate shash %s\n", hash_name); 396 return PTR_ERR(shash_tfm); 397 } 398 399 if (shash_len != crypto_shash_digestsize(shash_tfm)) { 400 pr_debug("%s: hash len mismatch (len %d digest %d)\n", 401 __func__, shash_len, 402 crypto_shash_digestsize(shash_tfm)); 403 ret = -EINVAL; 404 goto out_free_tfm; 405 } 406 407 transformed_key = nvme_auth_transform_key(ctrl->ctrl_key, 408 ctrl->subsysnqn); 409 if (IS_ERR(transformed_key)) { 410 ret = PTR_ERR(transformed_key); 411 goto out_free_tfm; 412 } 413 414 ret = crypto_shash_setkey(shash_tfm, transformed_key->key, 415 transformed_key->len); 416 if (ret) 417 goto out_free_response; 418 419 if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) { 420 challenge = kmalloc(shash_len, GFP_KERNEL); 421 if (!challenge) { 422 ret = -ENOMEM; 423 goto out_free_response; 424 } 425 ret = nvme_auth_augmented_challenge(ctrl->shash_id, 426 req->sq->dhchap_skey, 427 req->sq->dhchap_skey_len, 428 req->sq->dhchap_c2, 429 challenge, shash_len); 430 if (ret) 431 goto out_free_challenge; 432 } 433 434 shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm), 435 GFP_KERNEL); 436 if (!shash) { 437 ret = -ENOMEM; 438 goto out_free_challenge; 439 } 440 shash->tfm = shash_tfm; 441 442 ret = crypto_shash_init(shash); 443 if (ret) 444 goto out; 445 ret = crypto_shash_update(shash, challenge, shash_len); 446 if (ret) 447 goto out; 448 put_unaligned_le32(req->sq->dhchap_s2, buf); 449 ret = crypto_shash_update(shash, buf, 4); 450 if (ret) 451 goto out; 452 put_unaligned_le16(req->sq->dhchap_tid, buf); 453 ret = crypto_shash_update(shash, buf, 2); 454 if (ret) 455 goto out; 456 memset(buf, 0, 4); 457 ret = crypto_shash_update(shash, buf, 1); 458 if (ret) 459 goto out; 460 ret = crypto_shash_update(shash, "Controller", 10); 461 if (ret) 462 goto out; 463 ret = crypto_shash_update(shash, ctrl->subsysnqn, 464 strlen(ctrl->subsysnqn)); 465 if (ret) 466 goto out; 467 ret = crypto_shash_update(shash, buf, 1); 468 if (ret) 469 goto out; 470 ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn)); 471 if (ret) 472 goto out; 473 ret = crypto_shash_final(shash, response); 474 out: 475 kfree(shash); 476 out_free_challenge: 477 if (challenge != req->sq->dhchap_c2) 478 kfree(challenge); 479 out_free_response: 480 nvme_auth_free_key(transformed_key); 481 out_free_tfm: 482 crypto_free_shash(shash_tfm); 483 return ret; 484 } 485 486 int nvmet_auth_ctrl_exponential(struct nvmet_req *req, 487 u8 *buf, int buf_size) 488 { 489 struct nvmet_ctrl *ctrl = req->sq->ctrl; 490 int ret = 0; 491 492 if (!ctrl->dh_key) { 493 pr_warn("ctrl %d no DH public key!\n", ctrl->cntlid); 494 return -ENOKEY; 495 } 496 if (buf_size != ctrl->dh_keysize) { 497 pr_warn("ctrl %d DH public key size mismatch, need %zu is %d\n", 498 ctrl->cntlid, ctrl->dh_keysize, buf_size); 499 ret = -EINVAL; 500 } else { 501 memcpy(buf, ctrl->dh_key, buf_size); 502 pr_debug("%s: ctrl %d public key %*ph\n", __func__, 503 ctrl->cntlid, (int)buf_size, buf); 504 } 505 506 return ret; 507 } 508 509 int nvmet_auth_ctrl_sesskey(struct nvmet_req *req, 510 u8 *pkey, int pkey_size) 511 { 512 struct nvmet_ctrl *ctrl = req->sq->ctrl; 513 int ret; 514 515 req->sq->dhchap_skey_len = ctrl->dh_keysize; 516 req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL); 517 if (!req->sq->dhchap_skey) 518 return -ENOMEM; 519 ret = nvme_auth_gen_shared_secret(ctrl->dh_tfm, 520 pkey, pkey_size, 521 req->sq->dhchap_skey, 522 req->sq->dhchap_skey_len); 523 if (ret) 524 pr_debug("failed to compute shared secret, err %d\n", ret); 525 else 526 pr_debug("%s: shared secret %*ph\n", __func__, 527 (int)req->sq->dhchap_skey_len, 528 req->sq->dhchap_skey); 529 530 return ret; 531 } 532