1 /* $Id: acctproc.c,v 1.20 2019/06/17 15:20:10 tb Exp $ */ 2 /* 3 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/stat.h> 19 20 #include <err.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include <openssl/pem.h> 27 #include <openssl/evp.h> 28 #include <openssl/rsa.h> 29 #include <openssl/rand.h> 30 #include <openssl/err.h> 31 32 #include "extern.h" 33 #include "key.h" 34 35 /* 36 * Converts a BIGNUM to the form used in JWK. 37 * This is essentially a base64-encoded big-endian binary string 38 * representation of the number. 39 */ 40 static char * 41 bn2string(const BIGNUM *bn) 42 { 43 int len; 44 char *buf, *bbuf; 45 46 /* Extract big-endian representation of BIGNUM. */ 47 48 len = BN_num_bytes(bn); 49 if ((buf = malloc(len)) == NULL) { 50 warn("malloc"); 51 return NULL; 52 } else if (len != BN_bn2bin(bn, (unsigned char *)buf)) { 53 warnx("BN_bn2bin"); 54 free(buf); 55 return NULL; 56 } 57 58 /* Convert to base64url. */ 59 60 if ((bbuf = base64buf_url(buf, len)) == NULL) { 61 warnx("base64buf_url"); 62 free(buf); 63 return NULL; 64 } 65 66 free(buf); 67 return bbuf; 68 } 69 70 /* 71 * Extract the relevant RSA components from the key and create the JSON 72 * thumbprint from them. 73 */ 74 static char * 75 op_thumb_rsa(EVP_PKEY *pkey) 76 { 77 char *exp = NULL, *mod = NULL, *json = NULL; 78 RSA *r; 79 80 if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL) 81 warnx("EVP_PKEY_get0_RSA"); 82 else if ((mod = bn2string(r->n)) == NULL) 83 warnx("bn2string"); 84 else if ((exp = bn2string(r->e)) == NULL) 85 warnx("bn2string"); 86 else if ((json = json_fmt_thumb_rsa(exp, mod)) == NULL) 87 warnx("json_fmt_thumb_rsa"); 88 89 free(exp); 90 free(mod); 91 return json; 92 } 93 94 /* 95 * Extract the relevant EC components from the key and create the JSON 96 * thumbprint from them. 97 */ 98 static char * 99 op_thumb_ec(EVP_PKEY *pkey) 100 { 101 BIGNUM *X = NULL, *Y = NULL; 102 EC_KEY *ec = NULL; 103 char *x = NULL, *y = NULL; 104 char *json = NULL; 105 106 if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) 107 warnx("EVP_PKEY_get0_EC_KEY"); 108 else if ((X = BN_new()) == NULL) 109 warnx("BN_new"); 110 else if ((Y = BN_new()) == NULL) 111 warnx("BN_new"); 112 else if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec), 113 EC_KEY_get0_public_key(ec), X, Y, NULL)) 114 warnx("EC_POINT_get_affine_coordinates_GFp"); 115 else if ((x = bn2string(X)) == NULL) 116 warnx("bn2string"); 117 else if ((y = bn2string(Y)) == NULL) 118 warnx("bn2string"); 119 else if ((json = json_fmt_thumb_ec(x, y)) == NULL) 120 warnx("json_fmt_thumb_rsa"); 121 122 BN_free(X); 123 BN_free(Y); 124 free(x); 125 free(y); 126 return json; 127 } 128 129 /* 130 * The thumbprint operation is used for the challenge sequence. 131 */ 132 static int 133 op_thumbprint(int fd, EVP_PKEY *pkey) 134 { 135 char *thumb = NULL, *dig64 = NULL; 136 EVP_MD_CTX *ctx = NULL; 137 unsigned char *dig = NULL; 138 unsigned int digsz; 139 int rc = 0; 140 141 /* Construct the thumbprint input itself. */ 142 143 switch (EVP_PKEY_type(pkey->type)) { 144 case EVP_PKEY_RSA: 145 if ((thumb = op_thumb_rsa(pkey)) != NULL) 146 break; 147 goto out; 148 case EVP_PKEY_EC: 149 if ((thumb = op_thumb_ec(pkey)) != NULL) 150 break; 151 goto out; 152 default: 153 warnx("EVP_PKEY_type: unknown key type"); 154 goto out; 155 } 156 157 /* 158 * Compute the SHA256 digest of the thumbprint then 159 * base64-encode the digest itself. 160 * If the reader is closed when we write, ignore it (we'll pick 161 * it up in the read loop). 162 */ 163 164 if ((dig = malloc(EVP_MAX_MD_SIZE)) == NULL) { 165 warn("malloc"); 166 goto out; 167 } else if ((ctx = EVP_MD_CTX_new()) == NULL) { 168 warnx("EVP_MD_CTX_new"); 169 goto out; 170 } else if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) { 171 warnx("EVP_SignInit_ex"); 172 goto out; 173 } else if (!EVP_DigestUpdate(ctx, thumb, strlen(thumb))) { 174 warnx("EVP_SignUpdate"); 175 goto out; 176 } else if (!EVP_DigestFinal_ex(ctx, dig, &digsz)) { 177 warnx("EVP_SignFinal"); 178 goto out; 179 } else if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) { 180 warnx("base64buf_url"); 181 goto out; 182 } else if (writestr(fd, COMM_THUMB, dig64) < 0) 183 goto out; 184 185 rc = 1; 186 out: 187 EVP_MD_CTX_free(ctx); 188 free(thumb); 189 free(dig); 190 free(dig64); 191 return rc; 192 } 193 194 static int 195 op_sign_rsa(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url) 196 { 197 char *exp = NULL, *mod = NULL; 198 int rc = 0; 199 RSA *r; 200 201 *prot = NULL; 202 203 /* 204 * First, extract relevant portions of our private key. 205 * Finally, format the header combined with the nonce. 206 */ 207 208 if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL) 209 warnx("EVP_PKEY_get0_RSA"); 210 else if ((mod = bn2string(r->n)) == NULL) 211 warnx("bn2string"); 212 else if ((exp = bn2string(r->e)) == NULL) 213 warnx("bn2string"); 214 else if ((*prot = json_fmt_protected_rsa(exp, mod, nonce, url)) == NULL) 215 warnx("json_fmt_protected_rsa"); 216 else 217 rc = 1; 218 219 free(exp); 220 free(mod); 221 return rc; 222 } 223 224 static int 225 op_sign_ec(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url) 226 { 227 BIGNUM *X = NULL, *Y = NULL; 228 EC_KEY *ec = NULL; 229 char *x = NULL, *y = NULL; 230 int rc = 0; 231 232 *prot = NULL; 233 234 if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) 235 warnx("EVP_PKEY_get0_EC_KEY"); 236 else if ((X = BN_new()) == NULL) 237 warnx("BN_new"); 238 else if ((Y = BN_new()) == NULL) 239 warnx("BN_new"); 240 else if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec), 241 EC_KEY_get0_public_key(ec), X, Y, NULL)) 242 warnx("EC_POINT_get_affine_coordinates_GFp"); 243 else if ((x = bn2string(X)) == NULL) 244 warnx("bn2string"); 245 else if ((y = bn2string(Y)) == NULL) 246 warnx("bn2string"); 247 else if ((*prot = json_fmt_protected_ec(x, y, nonce, url)) == NULL) 248 warnx("json_fmt_protected_ec"); 249 else 250 rc = 1; 251 252 BN_free(X); 253 BN_free(Y); 254 free(x); 255 free(y); 256 return rc; 257 } 258 259 /* 260 * Operation to sign a message with the account key. 261 * This requires the sender ("fd") to provide the payload and a nonce. 262 */ 263 static int 264 op_sign(int fd, EVP_PKEY *pkey, enum acctop op) 265 { 266 EVP_MD_CTX *ctx = NULL; 267 const EVP_MD *evp_md = NULL; 268 EC_KEY *ec; 269 ECDSA_SIG *ec_sig = NULL; 270 const BIGNUM *ec_sig_r = NULL, *ec_sig_s = NULL; 271 int cc, rc = 0; 272 unsigned int digsz, bufsz, degree, bn_len, r_len, s_len; 273 char *nonce = NULL, *pay = NULL, *pay64 = NULL; 274 char *prot = NULL, *prot64 = NULL; 275 char *sign = NULL, *dig64 = NULL, *fin = NULL; 276 char *url = NULL, *kid = NULL, *alg = NULL; 277 unsigned char *dig = NULL, *buf = NULL; 278 const unsigned char *digp; 279 280 /* Read our payload and nonce from the requestor. */ 281 282 if ((pay = readstr(fd, COMM_PAY)) == NULL) 283 goto out; 284 else if ((nonce = readstr(fd, COMM_NONCE)) == NULL) 285 goto out; 286 else if ((url = readstr(fd, COMM_URL)) == NULL) 287 goto out; 288 289 if (op == ACCT_KID_SIGN) 290 if ((kid = readstr(fd, COMM_KID)) == NULL) 291 goto out; 292 293 /* Base64-encode the payload. */ 294 295 if ((pay64 = base64buf_url(pay, strlen(pay))) == NULL) { 296 warnx("base64buf_url"); 297 goto out; 298 } 299 300 switch (EVP_PKEY_type(pkey->type)) { 301 case EVP_PKEY_RSA: 302 alg = "RS256"; 303 evp_md = EVP_sha256(); 304 break; 305 case EVP_PKEY_EC: 306 alg = "ES384"; 307 evp_md = EVP_sha384(); 308 break; 309 default: 310 warnx("unknown account key type"); 311 goto out; 312 } 313 314 if (op == ACCT_KID_SIGN) { 315 if ((prot = json_fmt_protected_kid(alg, kid, nonce, url)) == 316 NULL) { 317 warnx("json_fmt_protected_kid"); 318 goto out; 319 } 320 } else { 321 switch (EVP_PKEY_type(pkey->type)) { 322 case EVP_PKEY_RSA: 323 if (!op_sign_rsa(&prot, pkey, nonce, url)) 324 goto out; 325 break; 326 case EVP_PKEY_EC: 327 if (!op_sign_ec(&prot, pkey, nonce, url)) 328 goto out; 329 break; 330 default: 331 warnx("EVP_PKEY_type"); 332 goto out; 333 } 334 } 335 336 /* The header combined with the nonce, base64. */ 337 338 if ((prot64 = base64buf_url(prot, strlen(prot))) == NULL) { 339 warnx("base64buf_url"); 340 goto out; 341 } 342 343 /* Now the signature material. */ 344 345 cc = asprintf(&sign, "%s.%s", prot64, pay64); 346 if (cc == -1) { 347 warn("asprintf"); 348 sign = NULL; 349 goto out; 350 } 351 352 if ((dig = malloc(EVP_PKEY_size(pkey))) == NULL) { 353 warn("malloc"); 354 goto out; 355 } 356 357 /* 358 * Here we go: using our RSA key as merged into the envelope, 359 * sign a SHA256 digest of our message. 360 */ 361 362 if ((ctx = EVP_MD_CTX_new()) == NULL) { 363 warnx("EVP_MD_CTX_new"); 364 goto out; 365 } else if (!EVP_SignInit_ex(ctx, evp_md, NULL)) { 366 warnx("EVP_SignInit_ex"); 367 goto out; 368 } else if (!EVP_SignUpdate(ctx, sign, strlen(sign))) { 369 warnx("EVP_SignUpdate"); 370 goto out; 371 } else if (!EVP_SignFinal(ctx, dig, &digsz, pkey)) { 372 warnx("EVP_SignFinal"); 373 goto out; 374 } 375 376 switch (EVP_PKEY_type(pkey->type)) { 377 case EVP_PKEY_RSA: 378 if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) { 379 warnx("base64buf_url"); 380 goto out; 381 } 382 break; 383 case EVP_PKEY_EC: 384 if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) { 385 warnx("EVP_PKEY_get0_EC_KEY"); 386 goto out; 387 } 388 degree = EC_GROUP_get_degree(EC_KEY_get0_group(ec)); 389 bn_len = (degree + 7) / 8; 390 391 digp = dig; /* d2i_ECDSA_SIG advances digp */ 392 if ((ec_sig = d2i_ECDSA_SIG(NULL, &digp, digsz)) == NULL) { 393 warnx("d2i_ECDSA_SIG"); 394 goto out; 395 } 396 397 ECDSA_SIG_get0(ec_sig, &ec_sig_r, &ec_sig_s); 398 399 r_len = BN_num_bytes(ec_sig_r); 400 s_len = BN_num_bytes(ec_sig_s); 401 402 if((r_len > bn_len) || (s_len > bn_len)) { 403 warnx("ECDSA_SIG_get0"); 404 goto out; 405 } 406 407 bufsz = 2 * bn_len; 408 if ((buf = calloc(1, bufsz)) == NULL) { 409 warnx("calloc"); 410 goto out; 411 } 412 413 /* put r and s in with leading zeros if any */ 414 BN_bn2bin(ec_sig_r, buf + bn_len - r_len); 415 BN_bn2bin(ec_sig_s, buf + bufsz - s_len); 416 417 if ((dig64 = base64buf_url((char *)buf, bufsz)) == NULL) { 418 warnx("base64buf_url"); 419 goto out; 420 } 421 422 break; 423 default: 424 warnx("EVP_PKEY_type"); 425 goto out; 426 } 427 428 /* 429 * Write back in the correct JSON format. 430 * If the reader is closed, just ignore it (we'll pick it up 431 * when we next enter the read loop). 432 */ 433 434 if ((fin = json_fmt_signed(prot64, pay64, dig64)) == NULL) { 435 warnx("json_fmt_signed"); 436 goto out; 437 } else if (writestr(fd, COMM_REQ, fin) < 0) 438 goto out; 439 440 rc = 1; 441 out: 442 EVP_MD_CTX_free(ctx); 443 free(pay); 444 free(sign); 445 free(pay64); 446 free(url); 447 free(nonce); 448 free(kid); 449 free(prot); 450 free(prot64); 451 free(dig); 452 free(dig64); 453 free(fin); 454 free(buf); 455 return rc; 456 } 457 458 int 459 acctproc(int netsock, const char *acctkey, enum keytype keytype) 460 { 461 FILE *f = NULL; 462 EVP_PKEY *pkey = NULL; 463 long lval; 464 enum acctop op; 465 int rc = 0, cc, newacct = 0; 466 mode_t prev; 467 468 /* 469 * First, open our private key file read-only or write-only if 470 * we're creating from scratch. 471 * Set our umask to be maximally restrictive. 472 */ 473 474 prev = umask((S_IWUSR | S_IXUSR) | S_IRWXG | S_IRWXO); 475 if ((f = fopen(acctkey, "r")) == NULL && errno == ENOENT) { 476 f = fopen(acctkey, "wx"); 477 newacct = 1; 478 } 479 umask(prev); 480 481 if (f == NULL) { 482 warn("%s", acctkey); 483 goto out; 484 } 485 486 /* File-system, user, and sandbox jailing. */ 487 488 ERR_load_crypto_strings(); 489 490 if (pledge("stdio", NULL) == -1) { 491 warn("pledge"); 492 goto out; 493 } 494 495 if (newacct) { 496 switch (keytype) { 497 case KT_ECDSA: 498 if ((pkey = ec_key_create(f, acctkey)) == NULL) 499 goto out; 500 dodbg("%s: generated ECDSA account key", acctkey); 501 break; 502 case KT_RSA: 503 if ((pkey = rsa_key_create(f, acctkey)) == NULL) 504 goto out; 505 dodbg("%s: generated RSA account key", acctkey); 506 break; 507 } 508 } else { 509 if ((pkey = key_load(f, acctkey)) == NULL) 510 goto out; 511 /* XXX check if account key type equals configured key type */ 512 doddbg("%s: loaded account key", acctkey); 513 } 514 515 fclose(f); 516 f = NULL; 517 518 /* Notify the netproc that we've started up. */ 519 520 if ((cc = writeop(netsock, COMM_ACCT_STAT, ACCT_READY)) == 0) 521 rc = 1; 522 if (cc <= 0) 523 goto out; 524 525 /* 526 * Now we wait for requests from the network-facing process. 527 * It might ask us for our thumbprint, for example, or for us to 528 * sign a message. 529 */ 530 531 for (;;) { 532 op = ACCT__MAX; 533 if ((lval = readop(netsock, COMM_ACCT)) == 0) 534 op = ACCT_STOP; 535 else if (lval == ACCT_SIGN || lval == ACCT_KID_SIGN || 536 lval == ACCT_THUMBPRINT) 537 op = lval; 538 539 if (ACCT__MAX == op) { 540 warnx("unknown operation from netproc"); 541 goto out; 542 } else if (ACCT_STOP == op) 543 break; 544 545 switch (op) { 546 case ACCT_SIGN: 547 case ACCT_KID_SIGN: 548 if (op_sign(netsock, pkey, op)) 549 break; 550 warnx("op_sign"); 551 goto out; 552 case ACCT_THUMBPRINT: 553 if (op_thumbprint(netsock, pkey)) 554 break; 555 warnx("op_thumbprint"); 556 goto out; 557 default: 558 abort(); 559 } 560 } 561 562 rc = 1; 563 out: 564 close(netsock); 565 if (f != NULL) 566 fclose(f); 567 EVP_PKEY_free(pkey); 568 ERR_print_errors_fp(stderr); 569 ERR_free_strings(); 570 return rc; 571 } 572