netpgp.c revision 1.23
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.23 2009/06/10 16:36:23 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 88enum { 89 MAX_ID_LENGTH = 128, 90 MAX_PASSPHRASE_LENGTH = 256 91}; 92 93/* read any gpg config file */ 94static int 95conffile(netpgp_t *netpgp, char *homedir, char *userid, size_t length) 96{ 97 regmatch_t matchv[10]; 98 regex_t keyre; 99 char buf[BUFSIZ]; 100 FILE *fp; 101 102 __OPS_USED(netpgp); 103 (void) snprintf(buf, sizeof(buf), "%s/gpg.conf", homedir); 104 if ((fp = fopen(buf, "r")) == NULL) { 105 (void) fprintf(stderr, "conffile: can't open '%s'\n", buf); 106 return 0; 107 } 108 (void) memset(&keyre, 0x0, sizeof(keyre)); 109 (void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)", 110 REG_EXTENDED); 111 while (fgets(buf, sizeof(buf), fp) != NULL) { 112 if (regexec(&keyre, buf, 10, matchv, 0) == 0) { 113 (void) memcpy(userid, &buf[(int)matchv[1].rm_so], 114 MIN((unsigned)(matchv[1].rm_eo - 115 matchv[1].rm_so), length)); 116 (void) fprintf(stderr, 117 "netpgp: default key set to \"%.*s\"\n", 118 (int)(matchv[1].rm_eo - matchv[1].rm_so), 119 &buf[(int)matchv[1].rm_so]); 120 } 121 } 122 (void) fclose(fp); 123 return 1; 124} 125 126/* small function to pretty print an 8-character raw userid */ 127static char * 128userid_to_id(const unsigned char *userid, char *id) 129{ 130 static const char *hexes = "0123456789abcdef"; 131 int i; 132 133 for (i = 0; i < 8 ; i++) { 134 id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4]; 135 id[(i * 2) + 1] = hexes[userid[i] & 0xf]; 136 } 137 id[8 * 2] = 0x0; 138 return id; 139} 140 141/* print out the successful signature information */ 142static void 143resultp(__ops_io_t *io, 144 const char *f, 145 __ops_validation_t *res, 146 __ops_keyring_t *ring) 147{ 148 const __ops_key_t *pubkey; 149 unsigned i; 150 char id[MAX_ID_LENGTH + 1]; 151 152 for (i = 0; i < res->validc; i++) { 153 (void) fprintf(io->errs, 154 "Good signature for %s made %susing %s key %s\n", 155 f, 156 ctime(&res->valid_sigs[i].birthtime), 157 __ops_show_pka(res->valid_sigs[i].key_alg), 158 userid_to_id(res->valid_sigs[i].signer_id, id)); 159 pubkey = __ops_getkeybyid(io, ring, 160 (const unsigned char *) res->valid_sigs[i].signer_id); 161 __ops_print_pubkeydata(io, pubkey); 162 } 163} 164 165/* check there's enough space in the arrays */ 166static void 167size_arrays(netpgp_t *netpgp, unsigned needed) 168{ 169 if (netpgp->size == 0) { 170 /* only get here first time around */ 171 netpgp->size = needed; 172 netpgp->name = calloc(sizeof(char *), needed); 173 netpgp->value = calloc(sizeof(char *), needed); 174 } else if (netpgp->c == netpgp->size) { 175 /* only uses 'needed' when filled array */ 176 netpgp->size += needed; 177 netpgp->name = realloc(netpgp->name, sizeof(char *) * needed); 178 netpgp->value = realloc(netpgp->value, sizeof(char *) * needed); 179 } 180} 181 182/* find the name in the array */ 183static int 184findvar(netpgp_t *netpgp, const char *name) 185{ 186 unsigned i; 187 188 for (i = 0 ; i < netpgp->c && strcmp(netpgp->name[i], name) != 0; i++) { 189 } 190 return (i == netpgp->c) ? -1 : (int)i; 191} 192 193/* read a keyring and return it */ 194static void * 195readkeyring(netpgp_t *netpgp, const char *name) 196{ 197 __ops_keyring_t *keyring; 198 const unsigned noarmor = 0; 199 char f[MAXPATHLEN]; 200 char *filename; 201 char *homedir; 202 203 homedir = netpgp_getvar(netpgp, "homedir"); 204 if ((filename = netpgp_getvar(netpgp, name)) == NULL) { 205 (void) snprintf(f, sizeof(f), "%s/%s.gpg", homedir, name); 206 filename = f; 207 } 208 keyring = calloc(1, sizeof(*keyring)); 209 if (!__ops_keyring_fileread(keyring, noarmor, filename)) { 210 (void) fprintf(stderr, "Can't read %s %s\n", name, filename); 211 return NULL; 212 } 213 netpgp_setvar(netpgp, name, filename); 214 return keyring; 215} 216 217/***************************************************************************/ 218/* exported functions start here */ 219/***************************************************************************/ 220 221/* initialise a netpgp_t structure */ 222int 223netpgp_init(netpgp_t *netpgp) 224{ 225 __ops_io_t *io; 226 char id[MAX_ID_LENGTH]; 227 char *homedir; 228 char *userid; 229 char *stream; 230 char *passfd; 231 char *results; 232 int coredumps; 233 234#ifdef HAVE_SYS_RESOURCE_H 235 struct rlimit limit; 236 237 coredumps = netpgp_getvar(netpgp, "coredumps") != NULL; 238 if (!coredumps) { 239 (void) memset(&limit, 0x0, sizeof(limit)); 240 if (setrlimit(RLIMIT_CORE, &limit) != 0) { 241 (void) fprintf(stderr, 242 "netpgp_init: warning - can't turn off core dumps\n"); 243 coredumps = 1; 244 } 245 } 246#else 247 coredumps = 1; 248#endif 249 io = calloc(1, sizeof(*io)); 250 io->outs = stdout; 251 if ((stream = netpgp_getvar(netpgp, "stdout")) != NULL && 252 strcmp(stream, "stderr") == 0) { 253 io->outs = stderr; 254 } 255 io->errs = stderr; 256 if ((stream = netpgp_getvar(netpgp, "stderr")) != NULL && 257 strcmp(stream, "stdout") == 0) { 258 io->errs = stdout; 259 } 260 io->errs = stderr; 261 netpgp->io = io; 262 if (coredumps) { 263 (void) fprintf(io->errs, 264 "netpgp: warning: core dumps enabled\n"); 265 } 266 if ((homedir = netpgp_getvar(netpgp, "homedir")) == NULL) { 267 (void) fprintf(io->errs, "netpgp: bad homedir\n"); 268 return 0; 269 } 270 if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) { 271 (void) memset(id, 0x0, sizeof(id)); 272 (void) conffile(netpgp, homedir, id, sizeof(id)); 273 if (id[0] != 0x0) { 274 netpgp_setvar(netpgp, "userid", userid = id); 275 } 276 } 277 if (userid == NULL) { 278 if (netpgp_getvar(netpgp, "userid checks") == NULL) { 279 (void) fprintf(io->errs, "Cannot find user id\n"); 280 return 0; 281 } 282 (void) fprintf(io->errs, "Skipping user id check\n"); 283 } else { 284 (void) netpgp_setvar(netpgp, "userid", id); 285 } 286 if ((netpgp->pubring = readkeyring(netpgp, "pubring")) == NULL) { 287 (void) fprintf(io->errs, "Can't read pub keyring\n"); 288 return 0; 289 } 290 if ((netpgp->secring = readkeyring(netpgp, "secring")) == NULL) { 291 (void) fprintf(io->errs, "Can't read sec keyring\n"); 292 return 0; 293 } 294 if ((passfd = netpgp_getvar(netpgp, "pass-fd")) != NULL && 295 (netpgp->passfp = fdopen(atoi(passfd), "r")) == NULL) { 296 (void) fprintf(io->errs, "Can't open fd %s for reading\n", 297 passfd); 298 return 0; 299 } 300 if ((results = netpgp_getvar(netpgp, "results")) == NULL) { 301 io->res = io->errs; 302 } else if ((io->res = fopen(results, "w")) == NULL) { 303 (void) fprintf(io->errs, "Can't open results %s for writing\n", 304 results); 305 return 0; 306 } 307 return 1; 308} 309 310/* finish off with the netpgp_t struct */ 311int 312netpgp_end(netpgp_t *netpgp) 313{ 314 unsigned i; 315 316 for (i = 0 ; i < netpgp->c ; i++) { 317 if (netpgp->name[i] != NULL) { 318 (void) free(netpgp->name[i]); 319 } 320 if (netpgp->value[i] != NULL) { 321 (void) free(netpgp->value[i]); 322 } 323 } 324 if (netpgp->name != NULL) { 325 (void) free(netpgp->name); 326 } 327 if (netpgp->value != NULL) { 328 (void) free(netpgp->value); 329 } 330 if (netpgp->pubring != NULL) { 331 __ops_keyring_free(netpgp->pubring); 332 } 333 if (netpgp->secring != NULL) { 334 __ops_keyring_free(netpgp->secring); 335 } 336 (void) free(netpgp->io); 337 return 1; 338} 339 340/* list the keys in a keyring */ 341int 342netpgp_list_keys(netpgp_t *netpgp) 343{ 344 return __ops_keyring_list(netpgp->io, netpgp->pubring); 345} 346 347/* find a key in a keyring */ 348int 349netpgp_find_key(netpgp_t *netpgp, char *id) 350{ 351 __ops_io_t *io; 352 353 io = netpgp->io; 354 if (id == NULL) { 355 (void) fprintf(io->errs, "NULL id to search for\n"); 356 return 0; 357 } 358 return __ops_getkeybyname(netpgp->io, netpgp->pubring, id) != NULL; 359} 360 361/* export a given key */ 362int 363netpgp_export_key(netpgp_t *netpgp, char *userid) 364{ 365 const __ops_key_t *keypair; 366 __ops_io_t *io; 367 368 io = netpgp->io; 369 if (userid == NULL) { 370 userid = netpgp_getvar(netpgp, "userid"); 371 } 372 keypair = __ops_getkeybyname(io, netpgp->pubring, userid); 373 if (keypair == NULL) { 374 (void) fprintf(io->errs, 375 "Cannot find own key \"%s\" in keyring\n", userid); 376 return 0; 377 } 378 return __ops_export_key(keypair, NULL); 379} 380 381/* import a key into our keyring */ 382int 383netpgp_import_key(netpgp_t *netpgp, char *f) 384{ 385 const unsigned noarmor = 0; 386 const unsigned armor = 1; 387 __ops_io_t *io; 388 int done; 389 390 io = netpgp->io; 391 if ((done = __ops_keyring_fileread(netpgp->pubring, noarmor, f)) == 0) { 392 done = __ops_keyring_fileread(netpgp->pubring, armor, f); 393 } 394 if (!done) { 395 (void) fprintf(io->errs, "Cannot import key from file %s\n", 396 f); 397 return 0; 398 } 399 return __ops_keyring_list(io, netpgp->pubring); 400} 401 402/* generate a new key */ 403int 404netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits) 405{ 406 __ops_key_t *keypair; 407 __ops_userid_t uid; 408 __ops_output_t *create; 409 const unsigned noarmor = 0; 410 __ops_io_t *io; 411 char *ringfile; 412 int fd; 413 414 (void) memset(&uid, 0x0, sizeof(uid)); 415 io = netpgp->io; 416 /* generate a new key for 'id' */ 417 uid.userid = (unsigned char *) id; 418 keypair = __ops_rsa_new_selfsign_key(numbits, 65537UL, &uid); 419 if (keypair == NULL) { 420 (void) fprintf(io->errs, "Cannot generate key\n"); 421 return 0; 422 } 423 /* write public key, and try to re-read it */ 424 ringfile = netpgp_getvar(netpgp, "pubring"); 425 fd = __ops_setup_file_append(&create, ringfile); 426 if (!__ops_write_xfer_pubkey(create, keypair, noarmor)) { 427 (void) fprintf(io->errs, "Cannot write pubkey\n"); 428 return 0; 429 } 430 __ops_teardown_file_write(create, fd); 431 __ops_keyring_free(netpgp->pubring); 432 if (!__ops_keyring_fileread(netpgp->pubring, noarmor, ringfile)) { 433 (void) fprintf(io->errs, "Cannot read pubring %s\n", ringfile); 434 return 0; 435 } 436 /* write secret key, and try to re-read it */ 437 ringfile = netpgp_getvar(netpgp, "sec ring file"); 438 fd = __ops_setup_file_append(&create, ringfile); 439 if (!__ops_write_xfer_seckey(create, keypair, NULL, 0, noarmor)) { 440 (void) fprintf(io->errs, "Cannot write seckey\n"); 441 return 0; 442 } 443 __ops_teardown_file_write(create, fd); 444 __ops_keyring_free(netpgp->secring); 445 if (!__ops_keyring_fileread(netpgp->secring, noarmor, ringfile)) { 446 (void) fprintf(io->errs, "Can't read secring %s\n", ringfile); 447 return 0; 448 } 449 __ops_keydata_free(keypair); 450 return 1; 451} 452 453/* encrypt a file */ 454int 455netpgp_encrypt_file(netpgp_t *netpgp, 456 const char *userid, 457 const char *f, 458 char *out, 459 int armored) 460{ 461 const __ops_key_t *keypair; 462 const unsigned overwrite = 1; 463 const char *suffix; 464 __ops_io_t *io; 465 char outname[MAXPATHLEN]; 466 467 io = netpgp->io; 468 if (userid == NULL) { 469 userid = netpgp_getvar(netpgp, "userid"); 470 } 471 suffix = (armored) ? ".asc" : ".gpg"; 472 keypair = __ops_getkeybyname(io, netpgp->pubring, userid); 473 if (keypair == NULL) { 474 (void) fprintf(io->errs, "Userid '%s' not found in keyring\n", 475 userid); 476 return 0; 477 } 478 if (out == NULL) { 479 (void) snprintf(outname, sizeof(outname), "%s%s", f, suffix); 480 out = outname; 481 } 482 return (int)__ops_encrypt_file(io, f, out, keypair, (unsigned)armored, 483 overwrite); 484} 485 486/* decrypt a file */ 487int 488netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored) 489{ 490 const unsigned overwrite = 1; 491 492 return __ops_decrypt_file(netpgp->io, f, out, netpgp->secring, 493 (unsigned)armored, overwrite, netpgp->passfp, 494 get_passphrase_cb); 495} 496 497/* sign a file */ 498int 499netpgp_sign_file(netpgp_t *netpgp, 500 const char *userid, 501 const char *f, 502 char *out, 503 int armored, 504 int cleartext, 505 int detached) 506{ 507 const __ops_key_t *keypair; 508 __ops_seckey_t *seckey; 509 const unsigned overwrite = 1; 510 __ops_io_t *io; 511 char *hashalg; 512 char pass[MAX_PASSPHRASE_LENGTH]; 513 int ret; 514 515 io = netpgp->io; 516 if (userid == NULL) { 517 userid = netpgp_getvar(netpgp, "userid"); 518 } 519 /* get key with which to sign */ 520 keypair = __ops_getkeybyname(io, netpgp->secring, userid); 521 if (keypair == NULL) { 522 (void) fprintf(io->errs, "Userid '%s' not found in keyring\n", 523 userid); 524 return 0; 525 } 526 ret = 1; 527 do { 528 /* print out the user id */ 529 __ops_print_pubkeydata(io, keypair); 530 /* get the passphrase */ 531 if (!__ops_getpassphrase(netpgp->passfp, pass, sizeof(pass))) { 532 (void) fprintf(io->errs, "Can't get passphrase\n"); 533 return 0; 534 } 535 /* now decrypt key */ 536 seckey = __ops_decrypt_seckey(keypair, pass); 537 if (seckey == NULL) { 538 (void) fprintf(io->errs, "Bad passphrase\n"); 539 } 540 __ops_forget(pass, sizeof(pass)); 541 } while (seckey == NULL); 542 /* sign file */ 543 hashalg = netpgp_getvar(netpgp, "hash"); 544 if (cleartext) { 545 ret = __ops_sign_file_as_cleartext(io, f, out, seckey, 546 hashalg, overwrite); 547 } else if (detached) { 548 ret = __ops_sign_detached(io, f, out, seckey, hashalg); 549 } else { 550 ret = __ops_sign_file(io, f, out, seckey, hashalg, 551 (unsigned)armored, overwrite); 552 } 553 __ops_forget(seckey, sizeof(*seckey)); 554 return ret; 555} 556 557/* verify a file */ 558int 559netpgp_verify_file(netpgp_t *netpgp, const char *in, const char *out, int armored) 560{ 561 __ops_validation_t result; 562 __ops_io_t *io; 563 564 (void) memset(&result, 0x0, sizeof(result)); 565 io = netpgp->io; 566 if (__ops_validate_file(io, &result, in, out, armored, 567 netpgp->pubring)) { 568 resultp(io, in, &result, netpgp->pubring); 569 return 1; 570 } 571 if (result.validc + result.invalidc + result.unknownc == 0) { 572 (void) fprintf(io->errs, 573 "\"%s\": No signatures found - is this a signed file?\n", 574 in); 575 } else { 576 (void) fprintf(io->errs, 577"\"%s\": verification failure: %d invalid signatures, %d unknown signatures\n", 578 in, result.invalidc, result.unknownc); 579 } 580 return 0; 581} 582 583/* wrappers for the ops_debug_level functions we added to openpgpsdk */ 584 585/* set the debugging level per filename */ 586int 587netpgp_set_debug(const char *f) 588{ 589 return __ops_set_debug_level(f); 590} 591 592/* get the debugging level per filename */ 593int 594netpgp_get_debug(const char *f) 595{ 596 return __ops_get_debug_level(f); 597} 598 599/* return the version for the library */ 600const char * 601netpgp_get_info(const char *type) 602{ 603 return __ops_get_info(type); 604} 605 606/* list all the packets in a file */ 607int 608netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname) 609{ 610 __ops_keyring_t *keyring; 611 const unsigned noarmor = 0; 612 __ops_io_t *io; 613 char ringname[MAXPATHLEN]; 614 char *homedir; 615 616 io = netpgp->io; 617 if (f == NULL) { 618 (void) fprintf(io->errs, "No file containing packets\n"); 619 return 0; 620 } 621 homedir = netpgp_getvar(netpgp, "homedir"); 622 if (pubringname == NULL) { 623 (void) snprintf(ringname, sizeof(ringname), 624 "%s/pubring.gpg", homedir); 625 pubringname = ringname; 626 } 627 keyring = calloc(1, sizeof(*keyring)); 628 if (!__ops_keyring_fileread(keyring, noarmor, pubringname)) { 629 (void) fprintf(io->errs, "Cannot read pub keyring %s\n", 630 pubringname); 631 return 0; 632 } 633 netpgp->pubring = keyring; 634 netpgp_setvar(netpgp, "pubring", pubringname); 635 return __ops_list_packets(io, f, (unsigned)armour, keyring, 636 netpgp->passfp, 637 get_passphrase_cb); 638} 639 640/* set a variable */ 641int 642netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value) 643{ 644 int i; 645 646 if ((i = findvar(netpgp, name)) < 0) { 647 /* add the element to the array */ 648 size_arrays(netpgp, netpgp->size + 15); 649 netpgp->name[i = netpgp->c++] = strdup(name); 650 } else { 651 /* replace the element in the array */ 652 if (netpgp->value[i]) { 653 (void) free(netpgp->value[i]); 654 netpgp->value[i] = NULL; 655 } 656 } 657 /* sanity checks for range of values */ 658 if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) { 659 if (__ops_str_to_hash_alg(value) == OPS_HASH_UNKNOWN) { 660 return 0; 661 } 662 } 663 netpgp->value[i] = strdup(value); 664 return 1; 665} 666 667/* get a variable's value (NULL if not set) */ 668char * 669netpgp_getvar(netpgp_t *netpgp, const char *name) 670{ 671 int i; 672 673 return ((i = findvar(netpgp, name)) < 0) ? NULL : netpgp->value[i]; 674} 675