xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/netpgp.c (revision c8da0e5fefd3800856b306200a18b2315c7fbb9f)
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 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/param.h>
34 #include <sys/mman.h>
35 
36 #ifdef HAVE_SYS_RESOURCE_H
37 #include <sys/resource.h>
38 #endif
39 
40 #ifdef HAVE_OPENSSL_CAST_H
41 #include <openssl/cast.h>
42 #endif
43 
44 #include <netpgp.h>
45 
46 #include "packet.h"
47 #include "packet-parse.h"
48 #include "keyring.h"
49 #include "errors.h"
50 #include "packet-show.h"
51 #include "create.h"
52 #include "netpgpsdk.h"
53 #include "memory.h"
54 #include "validate.h"
55 #include "readerwriter.h"
56 #include "netpgpdefs.h"
57 #include "parse_local.h"
58 
59 #ifdef HAVE_FCNTL_H
60 #include <fcntl.h>
61 #endif
62 
63 #include <regex.h>
64 #include <stdarg.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <time.h>
68 
69 #ifdef HAVE_UNISTD_H
70 #include <unistd.h>
71 #endif
72 
73 #include <errno.h>
74 
75 #ifdef HAVE_LIMITS_H
76 #include <limits.h>
77 #endif
78 
79 enum {
80 	MAX_ID_LENGTH		= 128,
81 	MAX_PASSPHRASE_LENGTH	= 256
82 };
83 
84 /* read any gpg config file */
85 static int
86 conffile(char *homedir, char *userid, size_t length, int verbose)
87 {
88 	regmatch_t	 matchv[10];
89 	regex_t		 keyre;
90 	char		 buf[BUFSIZ];
91 	FILE		*fp;
92 
93 	(void) snprintf(buf, sizeof(buf), "%s/.gnupg/gpg.conf", homedir);
94 	if ((fp = fopen(buf, "r")) == NULL) {
95 		return 0;
96 	}
97 	(void) memset(&keyre, 0x0, sizeof(keyre));
98 	(void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)",
99 		REG_EXTENDED);
100 	while (fgets(buf, sizeof(buf), fp) != NULL) {
101 		if (regexec(&keyre, buf, 10, matchv, 0) == 0) {
102 			(void) memcpy(userid, &buf[(int)matchv[1].rm_so],
103 				MIN((unsigned)(matchv[1].rm_eo -
104 						matchv[1].rm_so), length));
105 			if (verbose) {
106 				printf("setting default key to \"%.*s\"\n",
107 				(int)(matchv[1].rm_eo - matchv[1].rm_so),
108 				&buf[(int)matchv[1].rm_so]);
109 			}
110 		}
111 	}
112 	(void) fclose(fp);
113 	return 1;
114 }
115 
116 /* wrapper to get a pass phrase from the user */
117 static void
118 get_pass_phrase(char *phrase, size_t size)
119 {
120 	char           *p;
121 
122 	while ((p = getpass("netpgp passphrase: ")) == NULL) {
123 	}
124 	(void) snprintf(phrase, size, "%s", p);
125 }
126 
127 /* small function to pretty print an 8-character raw userid */
128 static char    *
129 userid_to_id(const unsigned char *userid, char *id)
130 {
131 	static const char *hexes = "0123456789ABCDEF";
132 	int		   i;
133 
134 	for (i = 0; i < 8 ; i++) {
135 		id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4];
136 		id[(i * 2) + 1] = hexes[userid[i] & 0xf];
137 	}
138 	id[8 * 2] = 0x0;
139 	return id;
140 }
141 
142 /* print out the successful signature information */
143 static void
144 psuccess(FILE *fp, char *f, __ops_validation_t *res, __ops_keyring_t *pubring)
145 {
146 	const __ops_keydata_t	*pubkey;
147 	unsigned		 i;
148 	char			 id[MAX_ID_LENGTH + 1];
149 
150 	for (i = 0; i < res->validc; i++) {
151 		(void) fprintf(fp,
152 			"Good signature for %s made %susing %s key %s\n",
153 			f,
154 			ctime(&res->valid_sigs[i].birthtime),
155 			__ops_show_pka(res->valid_sigs[i].key_alg),
156 			userid_to_id(res->valid_sigs[i].signer_id, id));
157 		pubkey = __ops_keyring_find_key_by_id(pubring,
158 			(const unsigned char *) res->valid_sigs[i].signer_id);
159 		__ops_print_pubkeydata(pubkey);
160 	}
161 }
162 
163 /* sign a file, and put the signature in a separate file */
164 static int
165 sign_detached(char *f, char *sigfile, __ops_seckey_t *seckey,
166 		const char *hashstr)
167 {
168 	__ops_create_sig_t	*sig;
169 	__ops_hash_alg_t	 alg;
170 	__ops_createinfo_t	*info;
171 	unsigned char	 	 keyid[OPS_KEY_ID_SIZE];
172 	unsigned char		*mmapped;
173 	struct stat		 st;
174 	time_t			 t;
175 	char			 fname[MAXPATHLEN];
176 	int			 fd;
177 
178 	/* find out which hash algorithm to use */
179 	alg = __ops_str_to_hash_alg(hashstr);
180 	if (alg == OPS_HASH_UNKNOWN) {
181 		(void) fprintf(stderr,"Unknown hash algorithm: %s\n", hashstr);
182 		return 0;
183 	}
184 
185 	/* create a new signature */
186 	sig = __ops_create_sig_new();
187 	__ops_start_sig(sig, seckey, alg, OPS_SIG_BINARY);
188 
189 	/* read the contents of 'f' */
190 	fd = open(f, O_RDONLY);
191 	if (fd < 0) {
192 		(void) fprintf(stderr, "can't open file \"%s\" to sign\n",
193 			f);
194 		return 0;
195 	}
196 	/* attempt to mmap(2) the file - if that fails, fall back to
197 	 * standard read(2) */
198 	mmapped = MAP_FAILED;
199 	if (fstat(fd, &st) == 0) {
200 		mmapped = mmap(NULL, (size_t)st.st_size, PROT_READ,
201 					MAP_FILE | MAP_PRIVATE, fd, 0);
202 	}
203 	if (mmapped == MAP_FAILED) {
204 		for (;;) {
205 			unsigned char	buf[8192];
206 			int 		n;
207 
208 			if ((n = read(fd, buf, sizeof(buf))) == 0) {
209 				break;
210 			}
211 			if (n < 0) {
212 				(void) fprintf(stderr, "short read \"%s\"\n",
213 						f);
214 				(void) close(fd);
215 				return 0;
216 			}
217 			__ops_sig_add_data(sig, buf, (unsigned)n);
218 		}
219 	} else {
220 		__ops_sig_add_data(sig, mmapped, (unsigned)st.st_size);
221 		(void) munmap(mmapped, (unsigned)st.st_size);
222 	}
223 	(void) close(fd);
224 
225 	/* calculate the signature */
226 	t = time(NULL);
227 	__ops_sig_add_birthtime(sig, t);
228 	__ops_keyid(keyid, OPS_KEY_ID_SIZE, OPS_KEY_ID_SIZE,
229 		&seckey->pubkey);
230 	__ops_sig_add_issuer_key_id(sig, keyid);
231 	__ops_sig_hashed_subpackets_end(sig);
232 
233 	/* write the signature to the detached file */
234 	if (sigfile == NULL) {
235 		(void) snprintf(fname, sizeof(fname), "%s.sig", f);
236 		sigfile = fname;
237 	}
238 	fd = open(sigfile, O_CREAT|O_TRUNC|O_WRONLY, 0666);
239 	if (fd < 0) {
240 		(void) fprintf(stderr, "can't write signature to \"%s\"\n",
241 				sigfile);
242 		return 0;
243 	}
244 
245 	info = __ops_createinfo_new();
246 	__ops_writer_set_fd(info, fd);
247 	__ops_write_sig(sig, &seckey->pubkey, seckey, info);
248 	__ops_seckey_free(seckey);
249 	(void) close(fd);
250 
251 	return 1;
252 }
253 
254 /***************************************************************************/
255 /* exported functions start here */
256 /***************************************************************************/
257 
258 /* initialise a netpgp_t structure */
259 int
260 netpgp_init(netpgp_t *netpgp, char *userid, char *pubring, char *secring)
261 {
262 	__ops_keyring_t	*keyring;
263 	char		*homedir;
264 	char		 ringname[MAXPATHLEN];
265 	char		 id[MAX_ID_LENGTH];
266 
267 #ifdef HAVE_SYS_RESOURCE_H
268 	struct rlimit	limit;
269 
270 	(void) memset(&limit, 0x0, sizeof(limit));
271 	if (setrlimit(RLIMIT_CORE, &limit) != 0) {
272 		(void) fprintf(stderr,
273 			"netpgp_init: warning - can't turn off core dumps\n");
274 	}
275 #else
276 	(void) fprintf(stderr,
277 		"netpgp_init: warning - no way of switching off core dumps\n");
278 #endif
279 	(void) memset(netpgp, 0x0, sizeof(*netpgp));
280 	homedir = getenv("HOME");
281 	if (userid == NULL) {
282 		(void) memset(id, 0x0, sizeof(id));
283 		conffile(homedir, id, sizeof(id), 1);
284 		if (id[0] != 0x0) {
285 			userid = id;
286 		}
287 	}
288 	if (userid == NULL) {
289 		(void) fprintf(stderr, "Cannot find user id\n");
290 		return 0;
291 	}
292 	if (pubring == NULL) {
293 		(void) snprintf(ringname, sizeof(ringname),
294 			"%s/.gnupg/pubring.gpg", homedir);
295 		pubring = ringname;
296 	}
297 	keyring = calloc(1, sizeof(*keyring));
298 	if (!__ops_keyring_fileread(keyring, false, pubring)) {
299 		(void) fprintf(stderr, "Cannot read pub keyring %s\n", pubring);
300 		return 0;
301 	}
302 	netpgp->pubring = keyring;
303 	netpgp->pubringfile = strdup(pubring);
304 	if (secring == NULL) {
305 		(void) snprintf(ringname, sizeof(ringname),
306 				"%s/.gnupg/secring.gpg", homedir);
307 		secring = ringname;
308 	}
309 	keyring = calloc(1, sizeof(*keyring));
310 	if (!__ops_keyring_fileread(keyring, false, secring)) {
311 		(void) fprintf(stderr, "Cannot read sec keyring %s\n", secring);
312 		return 0;
313 	}
314 	netpgp->secring = keyring;
315 	netpgp->secringfile = strdup(secring);
316 	netpgp->userid = strdup(userid);
317 	return 1;
318 }
319 
320 /* finish off with the netpgp_t struct */
321 int
322 netpgp_end(netpgp_t *netpgp)
323 {
324 	if (netpgp->pubring != NULL) {
325 		__ops_keyring_free(netpgp->pubring);
326 	}
327 	if (netpgp->pubringfile != NULL) {
328 		(void) free(netpgp->pubringfile);
329 	}
330 	if (netpgp->secring != NULL) {
331 		__ops_keyring_free(netpgp->secring);
332 	}
333 	if (netpgp->secringfile != NULL) {
334 		(void) free(netpgp->secringfile);
335 	}
336 	(void) free(netpgp->userid);
337 	return 1;
338 }
339 
340 /* list the keys in a keyring */
341 int
342 netpgp_list_keys(netpgp_t *netpgp)
343 {
344 	__ops_keyring_list(netpgp->pubring);
345 	return 1;
346 }
347 
348 /* find a key in a keyring */
349 int
350 netpgp_find_key(netpgp_t *netpgp, char *id)
351 {
352 	if (id == NULL) {
353 		(void) fprintf(stderr, "NULL id to search for\n");
354 		return 0;
355 	}
356 	return __ops_keyring_find_key_by_userid(netpgp->pubring, id) != NULL;
357 }
358 
359 /* export a given key */
360 int
361 netpgp_export_key(netpgp_t *netpgp, char *userid)
362 {
363 	const __ops_keydata_t	*keypair;
364 
365 	if (userid == NULL) {
366 		userid = netpgp->userid;
367 	}
368 	keypair = __ops_keyring_find_key_by_userid(netpgp->pubring, userid);
369 	if (keypair == NULL) {
370 		(void) fprintf(stderr,
371 			"Cannot find own key \"%s\" in keyring\n", userid);
372 		return 0;
373 	}
374 	__ops_export_key(keypair, NULL);
375 	return 1;
376 }
377 
378 /* import a key into our keyring */
379 int
380 netpgp_import_key(netpgp_t *netpgp, char *f)
381 {
382 	int	done;
383 
384 	if ((done = __ops_keyring_fileread(netpgp->pubring, false, f)) == 0) {
385 		done = __ops_keyring_fileread(netpgp->pubring, true, f);
386 	}
387 	if (!done) {
388 		(void) fprintf(stderr, "Cannot import key from file %s\n", f);
389 		return 0;
390 	}
391 	__ops_keyring_list(netpgp->pubring);
392 	return 1;
393 }
394 
395 /* generate a new key */
396 int
397 netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
398 {
399 	__ops_createinfo_t	*create;
400 	__ops_keydata_t		*keypair;
401 	__ops_user_id_t		 uid;
402 	int             	 fd;
403 
404 	(void) memset(&uid, 0x0, sizeof(uid));
405 	uid.user_id = (unsigned char *) id;
406 	keypair = __ops_rsa_create_selfsigned_keypair(numbits,
407 				(const unsigned long)65537, &uid);
408 	if (keypair == NULL) {
409 		(void) fprintf(stderr, "Cannot generate key\n");
410 		return 0;
411 	}
412 	/* write public key */
413 	fd = __ops_setup_file_append(&create, netpgp->pubringfile);
414 	__ops_write_transferable_pubkey(keypair, false, create);
415 	__ops_teardown_file_write(create, fd);
416 	__ops_keyring_free(netpgp->pubring);
417 	if (!__ops_keyring_fileread(netpgp->pubring, false,
418 				netpgp->pubringfile)) {
419 		(void) fprintf(stderr, "Cannot re-read keyring %s\n",
420 				netpgp->pubringfile);
421 		return 0;
422 	}
423 	/* write secret key */
424 	fd = __ops_setup_file_append(&create, netpgp->secringfile);
425 	__ops_write_transferable_seckey(keypair, NULL, 0, false, create);
426 	__ops_teardown_file_write(create, fd);
427 	__ops_keyring_free(netpgp->secring);
428 	if (!__ops_keyring_fileread(netpgp->secring, false,
429 				netpgp->secringfile)) {
430 		fprintf(stderr, "Cannot re-read keyring %s\n",
431 				netpgp->secringfile);
432 		return 0;
433 	}
434 	__ops_keydata_free(keypair);
435 	return 1;
436 }
437 
438 /* encrypt a file */
439 int
440 netpgp_encrypt_file(netpgp_t *netpgp, char *userid, char *f, char *out, int armored)
441 {
442 	const __ops_keydata_t	*keypair;
443 	const char		*suffix;
444 	char			 outname[MAXPATHLEN];
445 
446 	if (userid == NULL) {
447 		userid = netpgp->userid;
448 	}
449 	suffix = (armored) ? ".asc" : ".gpg";
450 	keypair = __ops_keyring_find_key_by_userid(netpgp->pubring, userid);
451 	if (keypair == NULL) {
452 		(void) fprintf(stderr, "Userid '%s' not found in keyring\n",
453 					userid);
454 		return 0;
455 	}
456 	if (out == NULL) {
457 		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
458 		out = outname;
459 	}
460 	__ops_encrypt_file(f, out, keypair, armored, true);
461 	return 1;
462 }
463 
464 /* decrypt a file */
465 int
466 netpgp_decrypt_file(netpgp_t *netpgp, char *f, char *out, int armored)
467 {
468 	return __ops_decrypt_file(f, out, netpgp->secring, armored, true,
469 		get_passphrase_cb) == true;
470 }
471 
472 /* sign a file */
473 int
474 netpgp_sign_file(netpgp_t *netpgp, char *userid, char *f, char *out,
475 		int armored, int cleartext, int detached)
476 {
477 	const __ops_keydata_t	*keypair;
478 	__ops_seckey_t		*seckey;
479 	char			 passphrase[MAX_PASSPHRASE_LENGTH];
480 
481 	if (userid == NULL) {
482 		userid = netpgp->userid;
483 	}
484 	/* get key with which to sign */
485 	keypair = __ops_keyring_find_key_by_userid(netpgp->secring, userid);
486 	if (keypair == NULL) {
487 		(void) fprintf(stderr, "Userid '%s' not found in keyring\n",
488 				userid);
489 		return 0;
490 	}
491 	do {
492 		/* print out the user id */
493 		__ops_print_pubkeydata(keypair);
494 		/* get the passphrase */
495 		get_pass_phrase(passphrase, sizeof(passphrase));
496 		/* now decrypt key */
497 		seckey = __ops_decrypt_seckey(keypair, passphrase);
498 		if (seckey == NULL) {
499 			(void) fprintf(stderr, "Bad passphrase\n");
500 		}
501 	} while (seckey == NULL);
502 	/* sign file */
503 	if (cleartext) {
504 		__ops_sign_file_as_cleartext(f, out, seckey, "SHA256", true);
505 	} else if (detached) {
506 		sign_detached(f, out, seckey, "SHA256");
507 	} else {
508 		__ops_sign_file(f, out, seckey, "SHA256", armored, true);
509 	}
510 	(void) memset(passphrase, 0x0, sizeof(passphrase));
511 	return 1;
512 }
513 
514 /* verify a file */
515 int
516 netpgp_verify_file(netpgp_t *netpgp, char *infile, const char *outfile,
517 			int armored)
518 {
519 	__ops_validation_t	result;
520 
521 	(void) memset(&result, 0x0, sizeof(result));
522 	if (__ops_validate_file(&result, infile, outfile, armored,
523 				netpgp->pubring)) {
524 		psuccess(stderr, infile, &result, netpgp->pubring);
525 		return 1;
526 	}
527 	if (result.validc + result.invalidc + result.unknownc == 0) {
528 		(void) fprintf(stderr,
529 		"\"%s\": No signatures found - is this a signed file?\n",
530 			infile);
531 		return 0;
532 	}
533 	(void) fprintf(stderr,
534 "\"%s\": verification failure: %d invalid signatures, %d unknown signatures\n",
535 		infile, result.invalidc, result.unknownc);
536 	return 0;
537 }
538 
539 /* wrappers for the ops_debug_level functions we added to openpgpsdk */
540 
541 /* set the debugging level per filename */
542 int
543 netpgp_set_debug(const char *f)
544 {
545 	return __ops_set_debug_level(f);
546 }
547 
548 /* get the debugging level per filename */
549 int
550 netpgp_get_debug(const char *f)
551 {
552 	return __ops_get_debug_level(f);
553 }
554 
555 /* return the version for the library */
556 const char *
557 netpgp_get_info(const char *type)
558 {
559 	return __ops_get_info(type);
560 }
561 
562 int
563 netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname)
564 {
565 	__ops_keyring_t	*keyring;
566 	char		 ringname[MAXPATHLEN];
567 	char		*homedir;
568 
569 	homedir = getenv("HOME");
570 	if (pubringname == NULL) {
571 		(void) snprintf(ringname, sizeof(ringname),
572 			"%s/.gnupg/pubring.gpg", homedir);
573 		pubringname = ringname;
574 	}
575 	keyring = calloc(1, sizeof(*keyring));
576 	if (!__ops_keyring_fileread(keyring, false, pubringname)) {
577 		(void) fprintf(stderr, "Cannot read pub keyring %s\n",
578 			pubringname);
579 		return 0;
580 	}
581 	netpgp->pubring = keyring;
582 	netpgp->pubringfile = strdup(pubringname);
583 	__ops_list_packets(f, armour, keyring, get_passphrase_cb);
584 	return 1;
585 }
586