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.15 2009/05/21 00:33:31 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 enum { 89 MAX_ID_LENGTH = 128, 90 MAX_PASSPHRASE_LENGTH = 256 91 }; 92 93 /* read any gpg config file */ 94 static int 95 conffile(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/.gnupg/gpg.conf", homedir); 104 if ((fp = fopen(buf, "r")) == NULL) { 105 return 0; 106 } 107 (void) memset(&keyre, 0x0, sizeof(keyre)); 108 (void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)", 109 REG_EXTENDED); 110 while (fgets(buf, sizeof(buf), fp) != NULL) { 111 if (regexec(&keyre, buf, 10, matchv, 0) == 0) { 112 (void) memcpy(userid, &buf[(int)matchv[1].rm_so], 113 MIN((unsigned)(matchv[1].rm_eo - 114 matchv[1].rm_so), length)); 115 (void) fprintf(stderr, 116 "netpgp: default key set to \"%.*s\"\n", 117 (int)(matchv[1].rm_eo - matchv[1].rm_so), 118 &buf[(int)matchv[1].rm_so]); 119 } 120 } 121 (void) fclose(fp); 122 return 1; 123 } 124 125 /* wrapper to get a pass phrase from the user */ 126 static void 127 get_pass_phrase(char *phrase, size_t size) 128 { 129 char *p; 130 131 while ((p = getpass("netpgp passphrase: ")) == NULL) { 132 } 133 (void) snprintf(phrase, size, "%s", p); 134 } 135 136 /* small function to pretty print an 8-character raw userid */ 137 static char * 138 userid_to_id(const unsigned char *userid, char *id) 139 { 140 static const char *hexes = "0123456789ABCDEF"; 141 int i; 142 143 for (i = 0; i < 8 ; i++) { 144 id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4]; 145 id[(i * 2) + 1] = hexes[userid[i] & 0xf]; 146 } 147 id[8 * 2] = 0x0; 148 return id; 149 } 150 151 /* print out the successful signature information */ 152 static void 153 psuccess(FILE *fp, char *f, __ops_validation_t *res, __ops_keyring_t *pubring) 154 { 155 const __ops_keydata_t *pubkey; 156 unsigned i; 157 char id[MAX_ID_LENGTH + 1]; 158 159 for (i = 0; i < res->validc; i++) { 160 (void) fprintf(fp, 161 "Good signature for %s made %susing %s key %s\n", 162 f, 163 ctime(&res->valid_sigs[i].birthtime), 164 __ops_show_pka(res->valid_sigs[i].key_alg), 165 userid_to_id(res->valid_sigs[i].signer_id, id)); 166 pubkey = __ops_keyring_find_key_by_id(pubring, 167 (const unsigned char *) res->valid_sigs[i].signer_id); 168 __ops_print_pubkeydata(fp, pubkey); 169 } 170 } 171 172 /***************************************************************************/ 173 /* exported functions start here */ 174 /***************************************************************************/ 175 176 /* initialise a netpgp_t structure */ 177 int 178 netpgp_init(netpgp_t *netpgp, char *userid, char *fpubring, char *fsecring) 179 { 180 __ops_keyring_t *keyring; 181 char *homedir; 182 char ringname[MAXPATHLEN]; 183 char id[MAX_ID_LENGTH]; 184 185 #ifdef HAVE_SYS_RESOURCE_H 186 struct rlimit limit; 187 188 (void) memset(&limit, 0x0, sizeof(limit)); 189 if (setrlimit(RLIMIT_CORE, &limit) != 0) { 190 (void) fprintf(stderr, 191 "netpgp_init: warning - can't turn off core dumps\n"); 192 } 193 #else 194 (void) fprintf(stderr, 195 "netpgp_init: warning - no way of switching off core dumps\n"); 196 #endif 197 homedir = getenv("HOME"); 198 if (userid == NULL) { 199 (void) memset(id, 0x0, sizeof(id)); 200 conffile(netpgp, homedir, id, sizeof(id)); 201 if (id[0] != 0x0) { 202 userid = id; 203 } 204 } 205 if (userid == NULL) { 206 (void) fprintf(stderr, "Cannot find user id\n"); 207 return 0; 208 } 209 (void) netpgp_setvar(netpgp, "userid", id); 210 if (fpubring == NULL) { 211 (void) snprintf(ringname, sizeof(ringname), 212 "%s/.gnupg/pubring.gpg", homedir); 213 fpubring = ringname; 214 } 215 keyring = calloc(1, sizeof(*keyring)); 216 if (!__ops_keyring_fileread(keyring, 0, fpubring)) { 217 (void) fprintf(stderr, "Can't read pub keyring %s\n", fpubring); 218 return 0; 219 } 220 netpgp->pubring = keyring; 221 netpgp->pubringfile = strdup(fpubring); 222 if (fsecring == NULL) { 223 (void) snprintf(ringname, sizeof(ringname), 224 "%s/.gnupg/secring.gpg", homedir); 225 fsecring = ringname; 226 } 227 keyring = calloc(1, sizeof(*keyring)); 228 if (!__ops_keyring_fileread(keyring, 0, fsecring)) { 229 (void) fprintf(stderr, "Can't read sec keyring %s\n", fsecring); 230 return 0; 231 } 232 netpgp->secring = keyring; 233 netpgp->secringfile = strdup(fsecring); 234 netpgp->userid = strdup(userid); 235 return 1; 236 } 237 238 /* finish off with the netpgp_t struct */ 239 int 240 netpgp_end(netpgp_t *netpgp) 241 { 242 if (netpgp->pubring != NULL) { 243 __ops_keyring_free(netpgp->pubring); 244 } 245 if (netpgp->pubringfile != NULL) { 246 (void) free(netpgp->pubringfile); 247 } 248 if (netpgp->secring != NULL) { 249 __ops_keyring_free(netpgp->secring); 250 } 251 if (netpgp->secringfile != NULL) { 252 (void) free(netpgp->secringfile); 253 } 254 (void) free(netpgp->userid); 255 return 1; 256 } 257 258 /* list the keys in a keyring */ 259 int 260 netpgp_list_keys(netpgp_t *netpgp) 261 { 262 __ops_keyring_list(netpgp->pubring); 263 return 1; 264 } 265 266 /* find a key in a keyring */ 267 int 268 netpgp_find_key(netpgp_t *netpgp, char *id) 269 { 270 if (id == NULL) { 271 (void) fprintf(stderr, "NULL id to search for\n"); 272 return 0; 273 } 274 return __ops_find_key_by_userid(netpgp->pubring, id) != NULL; 275 } 276 277 /* export a given key */ 278 int 279 netpgp_export_key(netpgp_t *netpgp, char *userid) 280 { 281 const __ops_keydata_t *keypair; 282 283 if (userid == NULL) { 284 userid = netpgp->userid; 285 } 286 keypair = __ops_find_key_by_userid(netpgp->pubring, userid); 287 if (keypair == NULL) { 288 (void) fprintf(stderr, 289 "Cannot find own key \"%s\" in keyring\n", userid); 290 return 0; 291 } 292 __ops_export_key(keypair, NULL); 293 return 1; 294 } 295 296 /* import a key into our keyring */ 297 int 298 netpgp_import_key(netpgp_t *netpgp, char *f) 299 { 300 int done; 301 302 if ((done = __ops_keyring_fileread(netpgp->pubring, 0, f)) == 0) { 303 done = __ops_keyring_fileread(netpgp->pubring, 1, f); 304 } 305 if (!done) { 306 (void) fprintf(stderr, "Cannot import key from file %s\n", f); 307 return 0; 308 } 309 __ops_keyring_list(netpgp->pubring); 310 return 1; 311 } 312 313 /* generate a new key */ 314 int 315 netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits) 316 { 317 __ops_keydata_t *keypair; 318 __ops_userid_t uid; 319 __ops_output_t *create; 320 int fd; 321 322 (void) memset(&uid, 0x0, sizeof(uid)); 323 uid.userid = (unsigned char *) id; 324 keypair = __ops_rsa_new_selfsign_key(numbits, 65537UL, &uid); 325 if (keypair == NULL) { 326 (void) fprintf(stderr, "Cannot generate key\n"); 327 return 0; 328 } 329 /* write public key */ 330 fd = __ops_setup_file_append(&create, netpgp->pubringfile); 331 __ops_write_xfer_pubkey(create, keypair, 0); 332 __ops_teardown_file_write(create, fd); 333 __ops_keyring_free(netpgp->pubring); 334 if (!__ops_keyring_fileread(netpgp->pubring, 0, netpgp->pubringfile)) { 335 (void) fprintf(stderr, "Cannot read pubring %s\n", 336 netpgp->pubringfile); 337 return 0; 338 } 339 /* write secret key */ 340 fd = __ops_setup_file_append(&create, netpgp->secringfile); 341 __ops_write_xfer_seckey(create, keypair, NULL, 0, 0); 342 __ops_teardown_file_write(create, fd); 343 __ops_keyring_free(netpgp->secring); 344 if (!__ops_keyring_fileread(netpgp->secring, 0, netpgp->secringfile)) { 345 (void) fprintf(stderr, "Can't read secring %s\n", 346 netpgp->secringfile); 347 return 0; 348 } 349 __ops_keydata_free(keypair); 350 return 1; 351 } 352 353 /* encrypt a file */ 354 int 355 netpgp_encrypt_file(netpgp_t *netpgp, char *userid, char *f, char *out, int armored) 356 { 357 const __ops_keydata_t *keypair; 358 const char *suffix; 359 char outname[MAXPATHLEN]; 360 361 if (userid == NULL) { 362 userid = netpgp->userid; 363 } 364 suffix = (armored) ? ".asc" : ".gpg"; 365 keypair = __ops_find_key_by_userid(netpgp->pubring, userid); 366 if (keypair == NULL) { 367 (void) fprintf(stderr, "Userid '%s' not found in keyring\n", 368 userid); 369 return 0; 370 } 371 if (out == NULL) { 372 (void) snprintf(outname, sizeof(outname), "%s%s", f, suffix); 373 out = outname; 374 } 375 return (int)__ops_encrypt_file(f, out, keypair, (unsigned)armored, 1U); 376 } 377 378 /* decrypt a file */ 379 int 380 netpgp_decrypt_file(netpgp_t *netpgp, char *f, char *out, int armored) 381 { 382 return __ops_decrypt_file(f, out, netpgp->secring, (unsigned)armored, 383 1U, get_passphrase_cb); 384 } 385 386 /* sign a file */ 387 int 388 netpgp_sign_file(netpgp_t *netpgp, char *userid, char *f, char *out, 389 int armored, int cleartext, int detached) 390 { 391 const __ops_keydata_t *keypair; 392 __ops_seckey_t *seckey; 393 char *hashalg; 394 char passphrase[MAX_PASSPHRASE_LENGTH]; 395 396 if (userid == NULL) { 397 userid = netpgp->userid; 398 } 399 /* get key with which to sign */ 400 keypair = __ops_find_key_by_userid(netpgp->secring, userid); 401 if (keypair == NULL) { 402 (void) fprintf(stderr, "Userid '%s' not found in keyring\n", 403 userid); 404 return 0; 405 } 406 do { 407 /* print out the user id */ 408 __ops_print_pubkeydata(stderr, keypair); 409 /* get the passphrase */ 410 get_pass_phrase(passphrase, sizeof(passphrase)); 411 /* now decrypt key */ 412 seckey = __ops_decrypt_seckey(keypair, passphrase); 413 if (seckey == NULL) { 414 (void) fprintf(stderr, "Bad passphrase\n"); 415 } 416 } while (seckey == NULL); 417 /* sign file */ 418 hashalg = netpgp_getvar(netpgp, "hash"); 419 if (cleartext) { 420 __ops_sign_file_as_cleartext(f, out, seckey, hashalg, 1U); 421 } else if (detached) { 422 __ops_sign_detached(f, out, seckey, hashalg); 423 } else { 424 __ops_sign_file(f, out, seckey, hashalg, (unsigned)armored, 1); 425 } 426 (void) memset(passphrase, 0x0, sizeof(passphrase)); 427 __ops_seckey_forget(seckey); 428 return 1; 429 } 430 431 /* verify a file */ 432 int 433 netpgp_verify_file(netpgp_t *netpgp, char *in, const char *out, int armored) 434 { 435 __ops_validation_t result; 436 437 (void) memset(&result, 0x0, sizeof(result)); 438 if (__ops_validate_file(&result, in, out, armored, netpgp->pubring)) { 439 psuccess(stderr, in, &result, netpgp->pubring); 440 return 1; 441 } 442 if (result.validc + result.invalidc + result.unknownc == 0) { 443 (void) fprintf(stderr, 444 "\"%s\": No signatures found - is this a signed file?\n", 445 in); 446 } else { 447 (void) fprintf(stderr, 448 "\"%s\": verification failure: %d invalid signatures, %d unknown signatures\n", 449 in, result.invalidc, result.unknownc); 450 } 451 return 0; 452 } 453 454 /* wrappers for the ops_debug_level functions we added to openpgpsdk */ 455 456 /* set the debugging level per filename */ 457 int 458 netpgp_set_debug(const char *f) 459 { 460 return __ops_set_debug_level(f); 461 } 462 463 /* get the debugging level per filename */ 464 int 465 netpgp_get_debug(const char *f) 466 { 467 return __ops_get_debug_level(f); 468 } 469 470 /* return the version for the library */ 471 const char * 472 netpgp_get_info(const char *type) 473 { 474 return __ops_get_info(type); 475 } 476 477 int 478 netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname) 479 { 480 __ops_keyring_t *keyring; 481 char ringname[MAXPATHLEN]; 482 char *homedir; 483 484 homedir = getenv("HOME"); 485 if (pubringname == NULL) { 486 (void) snprintf(ringname, sizeof(ringname), 487 "%s/.gnupg/pubring.gpg", homedir); 488 pubringname = ringname; 489 } 490 keyring = calloc(1, sizeof(*keyring)); 491 if (!__ops_keyring_fileread(keyring, 0, pubringname)) { 492 (void) fprintf(stderr, "Cannot read pub keyring %s\n", 493 pubringname); 494 return 0; 495 } 496 netpgp->pubring = keyring; 497 netpgp->pubringfile = strdup(pubringname); 498 __ops_list_packets(f, (unsigned)armour, keyring, get_passphrase_cb); 499 return 1; 500 } 501 502 /* set a variable */ 503 int 504 netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value) 505 { 506 if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) { 507 if (netpgp->hashalg) { 508 (void) free(netpgp->hashalg); 509 netpgp->hashalg = NULL; 510 } 511 if (__ops_str_to_hash_alg(value) == OPS_HASH_UNKNOWN) { 512 return 0; 513 } 514 netpgp->hashalg = strdup(value); 515 return 1; 516 } 517 if (strcmp(name, "userid") == 0 || strcmp(name, "user") == 0) { 518 if (netpgp->userid) { 519 (void) free(netpgp->userid); 520 netpgp->userid = NULL; 521 } 522 netpgp->userid = strdup(value); 523 return 1; 524 } 525 if (strcmp(name, "verbose") == 0) { 526 if (netpgp->verbose) { 527 (void) free(netpgp->verbose); 528 netpgp->verbose = NULL; 529 } 530 netpgp->verbose = strdup(value); 531 return 1; 532 } 533 return 0; 534 } 535 536 /* get a variable's value (NULL if not set) */ 537 char * 538 netpgp_getvar(netpgp_t *netpgp, const char *name) 539 { 540 if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) { 541 return netpgp->hashalg; 542 } 543 if (strcmp(name, "userid") == 0 || strcmp(name, "user") == 0) { 544 return netpgp->userid; 545 } 546 if (strcmp(name, "verbose") == 0) { 547 return netpgp->verbose; 548 } 549 return NULL; 550 } 551