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