1 /*- 2 * Copyright (c) 2009 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Alistair Crooks (agc@NetBSD.org) 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 #include "config.h" 30 31 #ifdef HAVE_SYS_CDEFS_H 32 #include <sys/cdefs.h> 33 #endif 34 35 #if defined(__NetBSD__) 36 __COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved."); 37 __RCSID("$NetBSD: netpgp.c,v 1.26 2009/06/13 05:25:08 agc Exp $"); 38 #endif 39 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <sys/param.h> 43 #include <sys/mman.h> 44 45 #ifdef HAVE_SYS_RESOURCE_H 46 #include <sys/resource.h> 47 #endif 48 49 #ifdef HAVE_OPENSSL_CAST_H 50 #include <openssl/cast.h> 51 #endif 52 53 #ifdef HAVE_FCNTL_H 54 #include <fcntl.h> 55 #endif 56 57 #include <regex.h> 58 #include <stdarg.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <time.h> 62 63 #ifdef HAVE_UNISTD_H 64 #include <unistd.h> 65 #endif 66 67 #include <errno.h> 68 69 #ifdef HAVE_LIMITS_H 70 #include <limits.h> 71 #endif 72 73 #include <netpgp.h> 74 75 #include "packet.h" 76 #include "packet-parse.h" 77 #include "keyring.h" 78 #include "errors.h" 79 #include "packet-show.h" 80 #include "create.h" 81 #include "netpgpsdk.h" 82 #include "memory.h" 83 #include "validate.h" 84 #include "readerwriter.h" 85 #include "netpgpdefs.h" 86 #include "crypto.h" 87 88 /* read any gpg config file */ 89 static int 90 conffile(netpgp_t *netpgp, char *homedir, char *userid, size_t length) 91 { 92 regmatch_t matchv[10]; 93 regex_t keyre; 94 char buf[BUFSIZ]; 95 FILE *fp; 96 97 __OPS_USED(netpgp); 98 (void) snprintf(buf, sizeof(buf), "%s/gpg.conf", homedir); 99 if ((fp = fopen(buf, "r")) == NULL) { 100 return 0; 101 } 102 (void) memset(&keyre, 0x0, sizeof(keyre)); 103 (void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)", 104 REG_EXTENDED); 105 while (fgets(buf, sizeof(buf), fp) != NULL) { 106 if (regexec(&keyre, buf, 10, matchv, 0) == 0) { 107 (void) memcpy(userid, &buf[(int)matchv[1].rm_so], 108 MIN((unsigned)(matchv[1].rm_eo - 109 matchv[1].rm_so), length)); 110 (void) fprintf(stderr, 111 "netpgp: default key set to \"%.*s\"\n", 112 (int)(matchv[1].rm_eo - matchv[1].rm_so), 113 &buf[(int)matchv[1].rm_so]); 114 } 115 } 116 (void) fclose(fp); 117 return 1; 118 } 119 120 /* small function to pretty print an 8-character raw userid */ 121 static char * 122 userid_to_id(const unsigned char *userid, char *id) 123 { 124 static const char *hexes = "0123456789abcdef"; 125 int i; 126 127 for (i = 0; i < 8 ; i++) { 128 id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4]; 129 id[(i * 2) + 1] = hexes[userid[i] & 0xf]; 130 } 131 id[8 * 2] = 0x0; 132 return id; 133 } 134 135 /* print out the successful signature information */ 136 static void 137 resultp(__ops_io_t *io, 138 const char *f, 139 __ops_validation_t *res, 140 __ops_keyring_t *ring) 141 { 142 const __ops_key_t *pubkey; 143 unsigned i; 144 char id[MAX_ID_LENGTH + 1]; 145 146 for (i = 0; i < res->validc; i++) { 147 (void) fprintf(io->res, 148 "Good signature for %s made %susing %s key %s\n", 149 f, 150 ctime(&res->valid_sigs[i].birthtime), 151 __ops_show_pka(res->valid_sigs[i].key_alg), 152 userid_to_id(res->valid_sigs[i].signer_id, id)); 153 pubkey = __ops_getkeybyid(io, ring, 154 (const unsigned char *) res->valid_sigs[i].signer_id); 155 __ops_print_pubkeydata(io, pubkey); 156 } 157 } 158 159 /* check there's enough space in the arrays */ 160 static void 161 size_arrays(netpgp_t *netpgp, unsigned needed) 162 { 163 if (netpgp->size == 0) { 164 /* only get here first time around */ 165 netpgp->size = needed; 166 netpgp->name = calloc(sizeof(char *), needed); 167 netpgp->value = calloc(sizeof(char *), needed); 168 } else if (netpgp->c == netpgp->size) { 169 /* only uses 'needed' when filled array */ 170 netpgp->size += needed; 171 netpgp->name = realloc(netpgp->name, sizeof(char *) * needed); 172 netpgp->value = realloc(netpgp->value, sizeof(char *) * needed); 173 } 174 } 175 176 /* find the name in the array */ 177 static int 178 findvar(netpgp_t *netpgp, const char *name) 179 { 180 unsigned i; 181 182 for (i = 0 ; i < netpgp->c && strcmp(netpgp->name[i], name) != 0; i++) { 183 } 184 return (i == netpgp->c) ? -1 : (int)i; 185 } 186 187 /* read a keyring and return it */ 188 static void * 189 readkeyring(netpgp_t *netpgp, const char *name) 190 { 191 __ops_keyring_t *keyring; 192 const unsigned noarmor = 0; 193 char f[MAXPATHLEN]; 194 char *filename; 195 char *homedir; 196 197 homedir = netpgp_getvar(netpgp, "homedir"); 198 if ((filename = netpgp_getvar(netpgp, name)) == NULL) { 199 (void) snprintf(f, sizeof(f), "%s/%s.gpg", homedir, name); 200 filename = f; 201 } 202 keyring = calloc(1, sizeof(*keyring)); 203 if (!__ops_keyring_fileread(keyring, noarmor, filename)) { 204 (void) fprintf(stderr, "Can't read %s %s\n", name, filename); 205 return NULL; 206 } 207 netpgp_setvar(netpgp, name, filename); 208 return keyring; 209 } 210 211 /***************************************************************************/ 212 /* exported functions start here */ 213 /***************************************************************************/ 214 215 /* initialise a netpgp_t structure */ 216 int 217 netpgp_init(netpgp_t *netpgp) 218 { 219 __ops_io_t *io; 220 char id[MAX_ID_LENGTH]; 221 char *homedir; 222 char *userid; 223 char *stream; 224 char *passfd; 225 char *results; 226 int coredumps; 227 228 #ifdef HAVE_SYS_RESOURCE_H 229 struct rlimit limit; 230 231 coredumps = netpgp_getvar(netpgp, "coredumps") != NULL; 232 if (!coredumps) { 233 (void) memset(&limit, 0x0, sizeof(limit)); 234 if (setrlimit(RLIMIT_CORE, &limit) != 0) { 235 (void) fprintf(stderr, 236 "netpgp_init: warning - can't turn off core dumps\n"); 237 coredumps = 1; 238 } 239 } 240 #else 241 coredumps = 1; 242 #endif 243 io = calloc(1, sizeof(*io)); 244 io->outs = stdout; 245 if ((stream = netpgp_getvar(netpgp, "stdout")) != NULL && 246 strcmp(stream, "stderr") == 0) { 247 io->outs = stderr; 248 } 249 io->errs = stderr; 250 if ((stream = netpgp_getvar(netpgp, "stderr")) != NULL && 251 strcmp(stream, "stdout") == 0) { 252 io->errs = stdout; 253 } 254 io->errs = stderr; 255 netpgp->io = io; 256 if (coredumps) { 257 (void) fprintf(io->errs, 258 "netpgp: warning: core dumps enabled\n"); 259 } 260 if ((homedir = netpgp_getvar(netpgp, "homedir")) == NULL) { 261 (void) fprintf(io->errs, "netpgp: bad homedir\n"); 262 return 0; 263 } 264 if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) { 265 (void) memset(id, 0x0, sizeof(id)); 266 (void) conffile(netpgp, homedir, id, sizeof(id)); 267 if (id[0] != 0x0) { 268 netpgp_setvar(netpgp, "userid", userid = id); 269 } 270 } 271 if (userid == NULL) { 272 if (netpgp_getvar(netpgp, "need userid") != NULL) { 273 (void) fprintf(io->errs, "Cannot find user id\n"); 274 return 0; 275 } 276 } else { 277 (void) netpgp_setvar(netpgp, "userid", userid); 278 } 279 if ((netpgp->pubring = readkeyring(netpgp, "pubring")) == NULL) { 280 (void) fprintf(io->errs, "Can't read pub keyring\n"); 281 return 0; 282 } 283 if ((netpgp->secring = readkeyring(netpgp, "secring")) == NULL) { 284 (void) fprintf(io->errs, "Can't read sec keyring\n"); 285 return 0; 286 } 287 if ((passfd = netpgp_getvar(netpgp, "pass-fd")) != NULL && 288 (netpgp->passfp = fdopen(atoi(passfd), "r")) == NULL) { 289 (void) fprintf(io->errs, "Can't open fd %s for reading\n", 290 passfd); 291 return 0; 292 } 293 if ((results = netpgp_getvar(netpgp, "results")) == NULL) { 294 io->res = io->errs; 295 } else if ((io->res = fopen(results, "w")) == NULL) { 296 (void) fprintf(io->errs, "Can't open results %s for writing\n", 297 results); 298 return 0; 299 } 300 return 1; 301 } 302 303 /* finish off with the netpgp_t struct */ 304 int 305 netpgp_end(netpgp_t *netpgp) 306 { 307 unsigned i; 308 309 for (i = 0 ; i < netpgp->c ; i++) { 310 if (netpgp->name[i] != NULL) { 311 (void) free(netpgp->name[i]); 312 } 313 if (netpgp->value[i] != NULL) { 314 (void) free(netpgp->value[i]); 315 } 316 } 317 if (netpgp->name != NULL) { 318 (void) free(netpgp->name); 319 } 320 if (netpgp->value != NULL) { 321 (void) free(netpgp->value); 322 } 323 if (netpgp->pubring != NULL) { 324 __ops_keyring_free(netpgp->pubring); 325 } 326 if (netpgp->secring != NULL) { 327 __ops_keyring_free(netpgp->secring); 328 } 329 (void) free(netpgp->io); 330 return 1; 331 } 332 333 /* list the keys in a keyring */ 334 int 335 netpgp_list_keys(netpgp_t *netpgp) 336 { 337 return __ops_keyring_list(netpgp->io, netpgp->pubring); 338 } 339 340 /* find a key in a keyring */ 341 int 342 netpgp_find_key(netpgp_t *netpgp, char *id) 343 { 344 __ops_io_t *io; 345 346 io = netpgp->io; 347 if (id == NULL) { 348 (void) fprintf(io->errs, "NULL id to search for\n"); 349 return 0; 350 } 351 return __ops_getkeybyname(netpgp->io, netpgp->pubring, id) != NULL; 352 } 353 354 /* export a given key */ 355 int 356 netpgp_export_key(netpgp_t *netpgp, char *userid) 357 { 358 const __ops_key_t *keypair; 359 __ops_io_t *io; 360 361 io = netpgp->io; 362 if (userid == NULL) { 363 userid = netpgp_getvar(netpgp, "userid"); 364 } 365 keypair = __ops_getkeybyname(io, netpgp->pubring, userid); 366 if (keypair == NULL) { 367 (void) fprintf(io->errs, 368 "Cannot find own key \"%s\" in keyring\n", userid); 369 return 0; 370 } 371 return __ops_export_key(keypair, NULL); 372 } 373 374 /* import a key into our keyring */ 375 int 376 netpgp_import_key(netpgp_t *netpgp, char *f) 377 { 378 const unsigned noarmor = 0; 379 const unsigned armor = 1; 380 __ops_io_t *io; 381 int done; 382 383 io = netpgp->io; 384 if ((done = __ops_keyring_fileread(netpgp->pubring, noarmor, f)) == 0) { 385 done = __ops_keyring_fileread(netpgp->pubring, armor, f); 386 } 387 if (!done) { 388 (void) fprintf(io->errs, "Cannot import key from file %s\n", 389 f); 390 return 0; 391 } 392 return __ops_keyring_list(io, netpgp->pubring); 393 } 394 395 /* generate a new key */ 396 int 397 netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits) 398 { 399 __ops_key_t *keypair; 400 __ops_userid_t uid; 401 __ops_output_t *create; 402 const unsigned noarmor = 0; 403 __ops_io_t *io; 404 char *ringfile; 405 int fd; 406 407 (void) memset(&uid, 0x0, sizeof(uid)); 408 io = netpgp->io; 409 /* generate a new key for 'id' */ 410 uid.userid = (unsigned char *) id; 411 keypair = __ops_rsa_new_selfsign_key(numbits, 65537UL, &uid); 412 if (keypair == NULL) { 413 (void) fprintf(io->errs, "Cannot generate key\n"); 414 return 0; 415 } 416 /* write public key, and try to re-read it */ 417 ringfile = netpgp_getvar(netpgp, "pubring"); 418 fd = __ops_setup_file_append(&create, ringfile); 419 if (!__ops_write_xfer_pubkey(create, keypair, noarmor)) { 420 (void) fprintf(io->errs, "Cannot write pubkey\n"); 421 return 0; 422 } 423 __ops_teardown_file_write(create, fd); 424 __ops_keyring_free(netpgp->pubring); 425 if (!__ops_keyring_fileread(netpgp->pubring, noarmor, ringfile)) { 426 (void) fprintf(io->errs, "Cannot read pubring %s\n", ringfile); 427 return 0; 428 } 429 /* write secret key, and try to re-read it */ 430 ringfile = netpgp_getvar(netpgp, "sec ring file"); 431 fd = __ops_setup_file_append(&create, ringfile); 432 if (!__ops_write_xfer_seckey(create, keypair, NULL, 0, noarmor)) { 433 (void) fprintf(io->errs, "Cannot write seckey\n"); 434 return 0; 435 } 436 __ops_teardown_file_write(create, fd); 437 __ops_keyring_free(netpgp->secring); 438 if (!__ops_keyring_fileread(netpgp->secring, noarmor, ringfile)) { 439 (void) fprintf(io->errs, "Can't read secring %s\n", ringfile); 440 return 0; 441 } 442 __ops_keydata_free(keypair); 443 return 1; 444 } 445 446 /* encrypt a file */ 447 int 448 netpgp_encrypt_file(netpgp_t *netpgp, 449 const char *userid, 450 const char *f, 451 char *out, 452 int armored) 453 { 454 const __ops_key_t *keypair; 455 const unsigned overwrite = 1; 456 const char *suffix; 457 __ops_io_t *io; 458 char outname[MAXPATHLEN]; 459 460 io = netpgp->io; 461 if (userid == NULL) { 462 userid = netpgp_getvar(netpgp, "userid"); 463 } 464 suffix = (armored) ? ".asc" : ".gpg"; 465 keypair = __ops_getkeybyname(io, netpgp->pubring, userid); 466 if (keypair == NULL) { 467 (void) fprintf(io->errs, "Userid '%s' not found in keyring\n", 468 userid); 469 return 0; 470 } 471 if (out == NULL) { 472 (void) snprintf(outname, sizeof(outname), "%s%s", f, suffix); 473 out = outname; 474 } 475 return (int)__ops_encrypt_file(io, f, out, keypair, (unsigned)armored, 476 overwrite); 477 } 478 479 /* decrypt a file */ 480 int 481 netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored) 482 { 483 const unsigned overwrite = 1; 484 485 return __ops_decrypt_file(netpgp->io, f, out, netpgp->secring, 486 (unsigned)armored, overwrite, netpgp->passfp, 487 get_passphrase_cb); 488 } 489 490 /* sign a file */ 491 int 492 netpgp_sign_file(netpgp_t *netpgp, 493 const char *userid, 494 const char *f, 495 char *out, 496 int armored, 497 int cleartext, 498 int detached) 499 { 500 const __ops_key_t *keypair; 501 __ops_seckey_t *seckey; 502 const unsigned overwrite = 1; 503 __ops_io_t *io; 504 char *hashalg; 505 int ret; 506 507 io = netpgp->io; 508 if (userid == NULL) { 509 userid = netpgp_getvar(netpgp, "userid"); 510 } 511 /* get key with which to sign */ 512 keypair = __ops_getkeybyname(io, netpgp->secring, userid); 513 if (keypair == NULL) { 514 (void) fprintf(io->errs, "Userid '%s' not found in keyring\n", 515 userid); 516 return 0; 517 } 518 ret = 1; 519 do { 520 /* print out the user id */ 521 __ops_print_pubkeydata(io, keypair); 522 /* now decrypt key */ 523 seckey = __ops_decrypt_seckey(keypair); 524 if (seckey == NULL) { 525 (void) fprintf(io->errs, "Bad passphrase\n"); 526 } 527 } while (seckey == NULL); 528 /* sign file */ 529 hashalg = netpgp_getvar(netpgp, "hash"); 530 if (cleartext) { 531 ret = __ops_sign_file_as_cleartext(io, f, out, seckey, 532 hashalg, overwrite); 533 } else if (detached) { 534 ret = __ops_sign_detached(io, f, out, seckey, hashalg); 535 } else { 536 ret = __ops_sign_file(io, f, out, seckey, hashalg, 537 (unsigned)armored, overwrite); 538 } 539 __ops_forget(seckey, sizeof(*seckey)); 540 return ret; 541 } 542 543 /* verify a file */ 544 int 545 netpgp_verify_file(netpgp_t *netpgp, const char *in, const char *out, int armored) 546 { 547 __ops_validation_t result; 548 __ops_io_t *io; 549 550 (void) memset(&result, 0x0, sizeof(result)); 551 io = netpgp->io; 552 if (__ops_validate_file(io, &result, in, out, armored, 553 netpgp->pubring)) { 554 resultp(io, in, &result, netpgp->pubring); 555 return 1; 556 } 557 if (result.validc + result.invalidc + result.unknownc == 0) { 558 (void) fprintf(io->errs, 559 "\"%s\": No signatures found - is this a signed file?\n", 560 in); 561 } else { 562 (void) fprintf(io->errs, 563 "\"%s\": verification failure: %d invalid signatures, %d unknown signatures\n", 564 in, result.invalidc, result.unknownc); 565 } 566 return 0; 567 } 568 569 /* wrappers for the ops_debug_level functions we added to openpgpsdk */ 570 571 /* set the debugging level per filename */ 572 int 573 netpgp_set_debug(const char *f) 574 { 575 return __ops_set_debug_level(f); 576 } 577 578 /* get the debugging level per filename */ 579 int 580 netpgp_get_debug(const char *f) 581 { 582 return __ops_get_debug_level(f); 583 } 584 585 /* return the version for the library */ 586 const char * 587 netpgp_get_info(const char *type) 588 { 589 return __ops_get_info(type); 590 } 591 592 /* list all the packets in a file */ 593 int 594 netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname) 595 { 596 __ops_keyring_t *keyring; 597 const unsigned noarmor = 0; 598 __ops_io_t *io; 599 char ringname[MAXPATHLEN]; 600 char *homedir; 601 602 io = netpgp->io; 603 if (f == NULL) { 604 (void) fprintf(io->errs, "No file containing packets\n"); 605 return 0; 606 } 607 homedir = netpgp_getvar(netpgp, "homedir"); 608 if (pubringname == NULL) { 609 (void) snprintf(ringname, sizeof(ringname), 610 "%s/pubring.gpg", homedir); 611 pubringname = ringname; 612 } 613 keyring = calloc(1, sizeof(*keyring)); 614 if (!__ops_keyring_fileread(keyring, noarmor, pubringname)) { 615 (void) fprintf(io->errs, "Cannot read pub keyring %s\n", 616 pubringname); 617 return 0; 618 } 619 netpgp->pubring = keyring; 620 netpgp_setvar(netpgp, "pubring", pubringname); 621 return __ops_list_packets(io, f, (unsigned)armour, keyring, 622 netpgp->passfp, 623 get_passphrase_cb); 624 } 625 626 /* set a variable */ 627 int 628 netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value) 629 { 630 int i; 631 632 if ((i = findvar(netpgp, name)) < 0) { 633 /* add the element to the array */ 634 size_arrays(netpgp, netpgp->size + 15); 635 netpgp->name[i = netpgp->c++] = strdup(name); 636 } else { 637 /* replace the element in the array */ 638 if (netpgp->value[i]) { 639 (void) free(netpgp->value[i]); 640 netpgp->value[i] = NULL; 641 } 642 } 643 /* sanity checks for range of values */ 644 if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) { 645 if (__ops_str_to_hash_alg(value) == OPS_HASH_UNKNOWN) { 646 return 0; 647 } 648 } 649 netpgp->value[i] = strdup(value); 650 return 1; 651 } 652 653 /* get a variable's value (NULL if not set) */ 654 char * 655 netpgp_getvar(netpgp_t *netpgp, const char *name) 656 { 657 int i; 658 659 return ((i = findvar(netpgp, name)) < 0) ? NULL : netpgp->value[i]; 660 } 661