1 /* $Id: acctproc.c,v 1.12 2018/07/28 15:25:23 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/rsa.h> 28 #include <openssl/rand.h> 29 #include <openssl/err.h> 30 31 #include "extern.h" 32 #include "rsa.h" 33 34 /* 35 * Converts a BIGNUM to the form used in JWK. 36 * This is essentially a base64-encoded big-endian binary string 37 * representation of the number. 38 */ 39 static char * 40 bn2string(const BIGNUM *bn) 41 { 42 int len; 43 char *buf, *bbuf; 44 45 /* Extract big-endian representation of BIGNUM. */ 46 47 len = BN_num_bytes(bn); 48 if ((buf = malloc(len)) == NULL) { 49 warn("malloc"); 50 return NULL; 51 } else if (len != BN_bn2bin(bn, (unsigned char *)buf)) { 52 warnx("BN_bn2bin"); 53 free(buf); 54 return NULL; 55 } 56 57 /* Convert to base64url. */ 58 59 if ((bbuf = base64buf_url(buf, len)) == NULL) { 60 warnx("base64buf_url"); 61 free(buf); 62 return NULL; 63 } 64 65 free(buf); 66 return bbuf; 67 } 68 69 /* 70 * Extract the relevant RSA components from the key and create the JSON 71 * thumbprint from them. 72 */ 73 static char * 74 op_thumb_rsa(EVP_PKEY *pkey) 75 { 76 char *exp = NULL, *mod = NULL, *json = NULL; 77 RSA *r; 78 79 if ((r = EVP_PKEY_get1_RSA(pkey)) == NULL) 80 warnx("EVP_PKEY_get1_RSA"); 81 else if ((mod = bn2string(r->n)) == NULL) 82 warnx("bn2string"); 83 else if ((exp = bn2string(r->e)) == NULL) 84 warnx("bn2string"); 85 else if ((json = json_fmt_thumb_rsa(exp, mod)) == NULL) 86 warnx("json_fmt_thumb_rsa"); 87 88 free(exp); 89 free(mod); 90 return json; 91 } 92 93 /* 94 * The thumbprint operation is used for the challenge sequence. 95 */ 96 static int 97 op_thumbprint(int fd, EVP_PKEY *pkey) 98 { 99 char *thumb = NULL, *dig64 = NULL; 100 EVP_MD_CTX *ctx = NULL; 101 unsigned char *dig = NULL; 102 unsigned int digsz; 103 int rc = 0; 104 105 /* Construct the thumbprint input itself. */ 106 107 switch (EVP_PKEY_type(pkey->type)) { 108 case EVP_PKEY_RSA: 109 if ((thumb = op_thumb_rsa(pkey)) != NULL) 110 break; 111 goto out; 112 default: 113 warnx("EVP_PKEY_type: unknown key type"); 114 goto out; 115 } 116 117 /* 118 * Compute the SHA256 digest of the thumbprint then 119 * base64-encode the digest itself. 120 * If the reader is closed when we write, ignore it (we'll pick 121 * it up in the read loop). 122 */ 123 124 if ((dig = malloc(EVP_MAX_MD_SIZE)) == NULL) { 125 warn("malloc"); 126 goto out; 127 } else if ((ctx = EVP_MD_CTX_create()) == NULL) { 128 warnx("EVP_MD_CTX_create"); 129 goto out; 130 } else if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) { 131 warnx("EVP_SignInit_ex"); 132 goto out; 133 } else if (!EVP_DigestUpdate(ctx, thumb, strlen(thumb))) { 134 warnx("EVP_SignUpdate"); 135 goto out; 136 } else if (!EVP_DigestFinal_ex(ctx, dig, &digsz)) { 137 warnx("EVP_SignFinal"); 138 goto out; 139 } else if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) { 140 warnx("base64buf_url"); 141 goto out; 142 } else if (writestr(fd, COMM_THUMB, dig64) < 0) 143 goto out; 144 145 rc = 1; 146 out: 147 if (ctx != NULL) 148 EVP_MD_CTX_destroy(ctx); 149 150 free(thumb); 151 free(dig); 152 free(dig64); 153 return rc; 154 } 155 156 static int 157 op_sign_rsa(char **head, char **prot, EVP_PKEY *pkey, const char *nonce) 158 { 159 char *exp = NULL, *mod = NULL; 160 int rc = 0; 161 RSA *r; 162 163 *head = NULL; 164 *prot = NULL; 165 166 /* 167 * First, extract relevant portions of our private key. 168 * Then construct the public header. 169 * Finally, format the header combined with the nonce. 170 */ 171 172 if ((r = EVP_PKEY_get1_RSA(pkey)) == NULL) 173 warnx("EVP_PKEY_get1_RSA"); 174 else if ((mod = bn2string(r->n)) == NULL) 175 warnx("bn2string"); 176 else if ((exp = bn2string(r->e)) == NULL) 177 warnx("bn2string"); 178 else if ((*head = json_fmt_header_rsa(exp, mod)) == NULL) 179 warnx("json_fmt_header_rsa"); 180 else if ((*prot = json_fmt_protected_rsa(exp, mod, nonce)) == NULL) 181 warnx("json_fmt_protected_rsa"); 182 else 183 rc = 1; 184 185 free(exp); 186 free(mod); 187 return rc; 188 } 189 190 /* 191 * Operation to sign a message with the account key. 192 * This requires the sender ("fd") to provide the payload and a nonce. 193 */ 194 static int 195 op_sign(int fd, EVP_PKEY *pkey) 196 { 197 char *nonce = NULL, *pay = NULL, *pay64 = NULL; 198 char *prot = NULL, *prot64 = NULL, *head = NULL; 199 char *sign = NULL, *dig64 = NULL, *fin = NULL; 200 unsigned char *dig = NULL; 201 EVP_MD_CTX *ctx = NULL; 202 int cc, rc = 0; 203 unsigned int digsz; 204 205 /* Read our payload and nonce from the requestor. */ 206 207 if ((pay = readstr(fd, COMM_PAY)) == NULL) 208 goto out; 209 else if ((nonce = readstr(fd, COMM_NONCE)) == NULL) 210 goto out; 211 212 /* Base64-encode the payload. */ 213 214 if ((pay64 = base64buf_url(pay, strlen(pay))) == NULL) { 215 warnx("base64buf_url"); 216 goto out; 217 } 218 219 switch (EVP_PKEY_type(pkey->type)) { 220 case EVP_PKEY_RSA: 221 if (!op_sign_rsa(&head, &prot, pkey, nonce)) 222 goto out; 223 break; 224 default: 225 warnx("EVP_PKEY_type"); 226 goto out; 227 } 228 229 /* The header combined with the nonce, base64. */ 230 231 if ((prot64 = base64buf_url(prot, strlen(prot))) == NULL) { 232 warnx("base64buf_url"); 233 goto out; 234 } 235 236 /* Now the signature material. */ 237 238 cc = asprintf(&sign, "%s.%s", prot64, pay64); 239 if (cc == -1) { 240 warn("asprintf"); 241 sign = NULL; 242 goto out; 243 } 244 245 if ((dig = malloc(EVP_PKEY_size(pkey))) == NULL) { 246 warn("malloc"); 247 goto out; 248 } 249 250 /* 251 * Here we go: using our RSA key as merged into the envelope, 252 * sign a SHA256 digest of our message. 253 */ 254 255 if ((ctx = EVP_MD_CTX_create()) == NULL) { 256 warnx("EVP_MD_CTX_create"); 257 goto out; 258 } else if (!EVP_SignInit_ex(ctx, EVP_sha256(), NULL)) { 259 warnx("EVP_SignInit_ex"); 260 goto out; 261 } else if (!EVP_SignUpdate(ctx, sign, strlen(sign))) { 262 warnx("EVP_SignUpdate"); 263 goto out; 264 } else if (!EVP_SignFinal(ctx, dig, &digsz, pkey)) { 265 warnx("EVP_SignFinal"); 266 goto out; 267 } else if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) { 268 warnx("base64buf_url"); 269 goto out; 270 } 271 272 /* 273 * Write back in the correct JSON format. 274 * If the reader is closed, just ignore it (we'll pick it up 275 * when we next enter the read loop). 276 */ 277 278 if ((fin = json_fmt_signed(head, prot64, pay64, dig64)) == NULL) { 279 warnx("json_fmt_signed"); 280 goto out; 281 } else if (writestr(fd, COMM_REQ, fin) < 0) 282 goto out; 283 284 rc = 1; 285 out: 286 if (ctx != NULL) 287 EVP_MD_CTX_destroy(ctx); 288 289 free(pay); 290 free(sign); 291 free(pay64); 292 free(nonce); 293 free(head); 294 free(prot); 295 free(prot64); 296 free(dig); 297 free(dig64); 298 free(fin); 299 return rc; 300 } 301 302 int 303 acctproc(int netsock, const char *acctkey, int newacct) 304 { 305 FILE *f = NULL; 306 EVP_PKEY *pkey = NULL; 307 long lval; 308 enum acctop op; 309 int rc = 0, cc; 310 mode_t prev; 311 312 /* 313 * First, open our private key file read-only or write-only if 314 * we're creating from scratch. 315 * Set our umask to be maximally restrictive. 316 */ 317 318 prev = umask((S_IWUSR | S_IXUSR) | S_IRWXG | S_IRWXO); 319 f = fopen(acctkey, newacct ? "wx" : "r"); 320 umask(prev); 321 322 if (f == NULL) { 323 warn("%s", acctkey); 324 goto out; 325 } 326 327 /* File-system, user, and sandbox jailing. */ 328 329 ERR_load_crypto_strings(); 330 331 if (pledge("stdio", NULL) == -1) { 332 warn("pledge"); 333 goto out; 334 } 335 336 if (newacct) { 337 if ((pkey = rsa_key_create(f, acctkey)) == NULL) 338 goto out; 339 dodbg("%s: generated RSA account key", acctkey); 340 } else { 341 if ((pkey = rsa_key_load(f, acctkey)) == NULL) 342 goto out; 343 doddbg("%s: loaded RSA account key", acctkey); 344 } 345 346 fclose(f); 347 f = NULL; 348 349 /* Notify the netproc that we've started up. */ 350 351 if ((cc = writeop(netsock, COMM_ACCT_STAT, ACCT_READY)) == 0) 352 rc = 1; 353 if (cc <= 0) 354 goto out; 355 356 /* 357 * Now we wait for requests from the network-facing process. 358 * It might ask us for our thumbprint, for example, or for us to 359 * sign a message. 360 */ 361 362 for (;;) { 363 op = ACCT__MAX; 364 if ((lval = readop(netsock, COMM_ACCT)) == 0) 365 op = ACCT_STOP; 366 else if (lval == ACCT_SIGN || lval == ACCT_THUMBPRINT) 367 op = lval; 368 369 if (ACCT__MAX == op) { 370 warnx("unknown operation from netproc"); 371 goto out; 372 } else if (ACCT_STOP == op) 373 break; 374 375 switch (op) { 376 case ACCT_SIGN: 377 if (op_sign(netsock, pkey)) 378 break; 379 warnx("op_sign"); 380 goto out; 381 case ACCT_THUMBPRINT: 382 if (op_thumbprint(netsock, pkey)) 383 break; 384 warnx("op_thumbprint"); 385 goto out; 386 default: 387 abort(); 388 } 389 } 390 391 rc = 1; 392 out: 393 close(netsock); 394 if (f != NULL) 395 fclose(f); 396 EVP_PKEY_free(pkey); 397 ERR_print_errors_fp(stderr); 398 ERR_free_strings(); 399 return rc; 400 } 401