netpgp.c revision 1.33
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.33 2009/12/14 23:29:56 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_FCNTL_H
50#include <fcntl.h>
51#endif
52
53#include <errno.h>
54#include <regex.h>
55#include <stdarg.h>
56#include <stdlib.h>
57#include <string.h>
58#include <time.h>
59
60#ifdef HAVE_UNISTD_H
61#include <unistd.h>
62#endif
63
64#include <errno.h>
65
66#ifdef HAVE_LIMITS_H
67#include <limits.h>
68#endif
69
70#include <netpgp.h>
71
72#include "packet.h"
73#include "packet-parse.h"
74#include "keyring.h"
75#include "errors.h"
76#include "packet-show.h"
77#include "create.h"
78#include "netpgpsdk.h"
79#include "memory.h"
80#include "validate.h"
81#include "readerwriter.h"
82#include "netpgpdefs.h"
83#include "crypto.h"
84#include "ops-ssh.h"
85
86/* read any gpg config file */
87static int
88conffile(netpgp_t *netpgp, char *homedir, char *userid, size_t length)
89{
90	regmatch_t	 matchv[10];
91	regex_t		 keyre;
92	char		 buf[BUFSIZ];
93	FILE		*fp;
94
95	__OPS_USED(netpgp);
96	(void) snprintf(buf, sizeof(buf), "%s/gpg.conf", homedir);
97	if ((fp = fopen(buf, "r")) == NULL) {
98		return 0;
99	}
100	(void) memset(&keyre, 0x0, sizeof(keyre));
101	(void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)",
102		REG_EXTENDED);
103	while (fgets(buf, sizeof(buf), fp) != NULL) {
104		if (regexec(&keyre, buf, 10, matchv, 0) == 0) {
105			(void) memcpy(userid, &buf[(int)matchv[1].rm_so],
106				MIN((unsigned)(matchv[1].rm_eo -
107						matchv[1].rm_so), length));
108			(void) fprintf(stderr,
109				"netpgp: default key set to \"%.*s\"\n",
110				(int)(matchv[1].rm_eo - matchv[1].rm_so),
111				&buf[(int)matchv[1].rm_so]);
112		}
113	}
114	(void) fclose(fp);
115	return 1;
116}
117
118/* small function to pretty print an 8-character raw userid */
119static char    *
120userid_to_id(const unsigned char *userid, char *id)
121{
122	static const char *hexes = "0123456789abcdef";
123	int		   i;
124
125	for (i = 0; i < 8 ; i++) {
126		id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4];
127		id[(i * 2) + 1] = hexes[userid[i] & 0xf];
128	}
129	id[8 * 2] = 0x0;
130	return id;
131}
132
133/* print out the successful signature information */
134static void
135resultp(__ops_io_t *io,
136	const char *f,
137	__ops_validation_t *res,
138	__ops_keyring_t *ring)
139{
140	const __ops_key_t	*pubkey;
141	unsigned		 from;
142	unsigned		 i;
143	char			 id[MAX_ID_LENGTH + 1];
144
145	for (i = 0; i < res->validc; i++) {
146		(void) fprintf(io->res,
147			"Good signature for %s made %susing %s key %s\n",
148			f,
149			ctime(&res->valid_sigs[i].birthtime),
150			__ops_show_pka(res->valid_sigs[i].key_alg),
151			userid_to_id(res->valid_sigs[i].signer_id, id));
152		from = 0;
153		pubkey = __ops_getkeybyid(io, ring,
154			(const unsigned char *) res->valid_sigs[i].signer_id,
155			&from);
156		__ops_print_keydata(io, pubkey, "pub", &pubkey->key.pubkey);
157	}
158}
159
160/* check there's enough space in the arrays */
161static int
162size_arrays(netpgp_t *netpgp, unsigned needed)
163{
164	char	**temp;
165
166	if (netpgp->size == 0) {
167		/* only get here first time around */
168		netpgp->size = needed;
169		if ((netpgp->name = calloc(sizeof(char *), needed)) == NULL) {
170			(void) fprintf(stderr, "size_arrays: bad alloc\n");
171			return 0;
172		}
173		if ((netpgp->value = calloc(sizeof(char *), needed)) == NULL) {
174			free(netpgp->name);
175			(void) fprintf(stderr, "size_arrays: bad alloc\n");
176			return 0;
177		}
178	} else if (netpgp->c == netpgp->size) {
179		/* only uses 'needed' when filled array */
180		netpgp->size += needed;
181		temp = realloc(netpgp->name, sizeof(char *) * needed);
182		if (temp == NULL) {
183			(void) fprintf(stderr, "size_arrays: bad alloc\n");
184			return 0;
185		}
186		netpgp->name = temp;
187		temp = realloc(netpgp->value, sizeof(char *) * needed);
188		if (temp == NULL) {
189			(void) fprintf(stderr, "size_arrays: bad alloc\n");
190			return 0;
191		}
192		netpgp->value = temp;
193	}
194	return 1;
195}
196
197/* find the name in the array */
198static int
199findvar(netpgp_t *netpgp, const char *name)
200{
201	unsigned	i;
202
203	for (i = 0 ; i < netpgp->c && strcmp(netpgp->name[i], name) != 0; i++) {
204	}
205	return (i == netpgp->c) ? -1 : (int)i;
206}
207
208/* read a keyring and return it */
209static void *
210readkeyring(netpgp_t *netpgp, const char *name)
211{
212	__ops_keyring_t	*keyring;
213	const unsigned	 noarmor = 0;
214	char		 f[MAXPATHLEN];
215	char		*filename;
216	char		*homedir;
217
218	homedir = netpgp_getvar(netpgp, "homedir");
219	if ((filename = netpgp_getvar(netpgp, name)) == NULL) {
220		(void) snprintf(f, sizeof(f), "%s/%s.gpg", homedir, name);
221		filename = f;
222	}
223	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
224		(void) fprintf(stderr, "readkeyring: bad alloc\n");
225		return NULL;
226	}
227	if (!__ops_keyring_fileread(keyring, noarmor, filename)) {
228		free(keyring);
229		(void) fprintf(stderr, "Can't read %s %s\n", name, filename);
230		return NULL;
231	}
232	netpgp_setvar(netpgp, name, filename);
233	return keyring;
234}
235
236/* read keys from ssh key files */
237static int
238readsshkeys(netpgp_t *netpgp, char *homedir)
239{
240	__ops_keyring_t	*pubring;
241	__ops_keyring_t	*secring;
242	char		 f[MAXPATHLEN];
243	char		*filename;
244
245	if ((filename = netpgp_getvar(netpgp, "sshkeyfile")) == NULL) {
246		(void) snprintf(f, sizeof(f), "%s/.ssh/is_rsa.pub", homedir);
247		filename = f;
248	}
249	if ((pubring = calloc(1, sizeof(*pubring))) == NULL) {
250		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
251		return 0;
252	}
253	if (!__ops_ssh2_readkeys(netpgp->io, pubring, NULL, filename, NULL)) {
254		free(pubring);
255		(void) fprintf(stderr, "readsshkeys: can't read %s\n",
256				filename);
257		return 0;
258	}
259	netpgp->pubring = pubring;
260	netpgp_setvar(netpgp, "sshpubfile", filename);
261	/* try to take the ".pub" off the end */
262	if (filename == f) {
263		f[strlen(f) - 4] = 0x0;
264	} else {
265		(void) snprintf(f, sizeof(f), "%.*s",
266				(int)strlen(filename) - 4, filename);
267		filename = f;
268	}
269	if ((secring = calloc(1, sizeof(*secring))) == NULL) {
270		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
271		return 0;
272	}
273	if (__ops_ssh2_readkeys(netpgp->io, pubring, secring, NULL, filename)) {
274		netpgp->secring = secring;
275		netpgp_setvar(netpgp, "sshsecfile", filename);
276	} else {
277		(void) fprintf(stderr, "readsshkeys: can't read sec %s (%d)\n",
278				filename, errno);
279	}
280	return 1;
281}
282
283/* set ssh uid to first one in ring */
284static void
285set_ssh_userid(__ops_keyring_t *pubring, char *id, size_t len)
286{
287	unsigned char	*src;
288	int		 i;
289	int		 n;
290
291	(void) memset(id, 0x0, len);
292	src = pubring->keys[0].key_id;
293	for (i = 0, n = 0 ; i < OPS_KEY_ID_SIZE ; i += 2) {
294		n += snprintf(&id[n], len - n, "%02x%02x", src[i], src[i + 1]);
295	}
296	id[n] = 0x0;
297}
298
299/***************************************************************************/
300/* exported functions start here */
301/***************************************************************************/
302
303/* initialise a netpgp_t structure */
304int
305netpgp_init(netpgp_t *netpgp)
306{
307	__ops_io_t	*io;
308	char		 id[MAX_ID_LENGTH];
309	char		*homedir;
310	char		*userid;
311	char		*stream;
312	char		*passfd;
313	char		*results;
314	int		 coredumps;
315
316#ifdef HAVE_SYS_RESOURCE_H
317	struct rlimit	limit;
318
319	coredumps = netpgp_getvar(netpgp, "coredumps") != NULL;
320	if (!coredumps) {
321		(void) memset(&limit, 0x0, sizeof(limit));
322		if (setrlimit(RLIMIT_CORE, &limit) != 0) {
323			(void) fprintf(stderr,
324			"netpgp_init: warning - can't turn off core dumps\n");
325			coredumps = 1;
326		}
327	}
328#else
329	coredumps = 1;
330#endif
331	if ((io = calloc(1, sizeof(*io))) == NULL) {
332		(void) fprintf(stderr, "netpgp_init: bad alloc\n");
333		return 0;
334	}
335	io->outs = stdout;
336	if ((stream = netpgp_getvar(netpgp, "stdout")) != NULL &&
337	    strcmp(stream, "stderr") == 0) {
338		io->outs = stderr;
339	}
340	io->errs = stderr;
341	if ((stream = netpgp_getvar(netpgp, "stderr")) != NULL &&
342	    strcmp(stream, "stdout") == 0) {
343		io->errs = stdout;
344	}
345	if ((results = netpgp_getvar(netpgp, "results")) == NULL) {
346		io->res = io->errs;
347	} else if ((io->res = fopen(results, "w")) == NULL) {
348		(void) fprintf(io->errs, "Can't open results %s for writing\n",
349			results);
350		return 0;
351	}
352	netpgp->io = io;
353	if (coredumps) {
354		(void) fprintf(io->errs,
355			"netpgp: warning: core dumps enabled\n");
356	}
357	if ((homedir = netpgp_getvar(netpgp, "homedir")) == NULL) {
358		(void) fprintf(io->errs, "netpgp: bad homedir\n");
359		return 0;
360	}
361	/* read from either gpg files or ssh keys */
362	if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
363		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
364			(void) memset(id, 0x0, sizeof(id));
365			(void) conffile(netpgp, homedir, id, sizeof(id));
366			if (id[0] != 0x0) {
367				netpgp_setvar(netpgp, "userid", userid = id);
368			}
369		}
370		if (userid == NULL) {
371			if (netpgp_getvar(netpgp, "need userid") != NULL) {
372				(void) fprintf(io->errs,
373						"Cannot find user id\n");
374				return 0;
375			}
376		} else {
377			(void) netpgp_setvar(netpgp, "userid", userid);
378		}
379		netpgp->pubring = readkeyring(netpgp, "pubring");
380		if (netpgp->pubring == NULL) {
381			(void) fprintf(io->errs, "Can't read pub keyring\n");
382			return 0;
383		}
384		netpgp->secring = readkeyring(netpgp, "secring");
385		if (netpgp->secring == NULL) {
386			(void) fprintf(io->errs, "Can't read sec keyring\n");
387			return 0;
388		}
389	} else {
390		if (!readsshkeys(netpgp, homedir)) {
391			(void) fprintf(io->errs, "Can't read ssh pub key\n");
392			return 0;
393		}
394		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
395			set_ssh_userid(netpgp->pubring, id, sizeof(id));
396			netpgp_setvar(netpgp, "userid", userid = id);
397		}
398		if (userid == NULL) {
399			if (netpgp_getvar(netpgp, "need userid") != NULL) {
400				(void) fprintf(io->errs,
401						"Cannot find user id\n");
402				return 0;
403			}
404		} else {
405			(void) netpgp_setvar(netpgp, "userid", userid);
406		}
407	}
408	if ((passfd = netpgp_getvar(netpgp, "pass-fd")) != NULL &&
409	    (netpgp->passfp = fdopen(atoi(passfd), "r")) == NULL) {
410		(void) fprintf(io->errs, "Can't open fd %s for reading\n",
411			passfd);
412		return 0;
413	}
414	return 1;
415}
416
417/* finish off with the netpgp_t struct */
418int
419netpgp_end(netpgp_t *netpgp)
420{
421	unsigned	i;
422
423	for (i = 0 ; i < netpgp->c ; i++) {
424		if (netpgp->name[i] != NULL) {
425			free(netpgp->name[i]);
426		}
427		if (netpgp->value[i] != NULL) {
428			free(netpgp->value[i]);
429		}
430	}
431	if (netpgp->name != NULL) {
432		free(netpgp->name);
433	}
434	if (netpgp->value != NULL) {
435		free(netpgp->value);
436	}
437	if (netpgp->pubring != NULL) {
438		__ops_keyring_free(netpgp->pubring);
439	}
440	if (netpgp->secring != NULL) {
441		__ops_keyring_free(netpgp->secring);
442	}
443	free(netpgp->io);
444	return 1;
445}
446
447/* list the keys in a keyring */
448int
449netpgp_list_keys(netpgp_t *netpgp)
450{
451	return __ops_keyring_list(netpgp->io, netpgp->pubring);
452}
453
454/* find and list some keys in a keyring */
455int
456netpgp_match_list_keys(netpgp_t *netpgp, char *name)
457{
458	const __ops_key_t	*key;
459	unsigned		 found;
460	unsigned		 k;
461	char			*data;
462
463	found = k = 0;
464	do {
465		key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring,
466						name, &k);
467		if (key != NULL) {
468			__ops_sprint_keydata(key, &data, "pub",
469						&key->key.pubkey);
470			printf("%s\n", data);
471			free(data);
472			found += 1;
473			k += 1;
474		}
475	} while (key != NULL);
476	printf("Found %u key%s\n", found, (found == 1) ? "" : "s");
477	return (found > 0);
478}
479
480/* find a key in a keyring */
481int
482netpgp_find_key(netpgp_t *netpgp, char *id)
483{
484	__ops_io_t	*io;
485
486	io = netpgp->io;
487	if (id == NULL) {
488		(void) fprintf(io->errs, "NULL id to search for\n");
489		return 0;
490	}
491	return __ops_getkeybyname(netpgp->io, netpgp->pubring, id) != NULL;
492}
493
494/* get a key in a keyring */
495char *
496netpgp_get_key(netpgp_t *netpgp, const char *id)
497{
498	const __ops_key_t	*key;
499	__ops_io_t		*io;
500	char			*newkey;
501
502	io = netpgp->io;
503	if (id == NULL) {
504		(void) fprintf(io->errs, "NULL id to search for\n");
505		return NULL;
506	}
507	key = __ops_getkeybyname(netpgp->io, netpgp->pubring, id);
508	if (key == NULL) {
509		(void) fprintf(io->errs, "Can't find key '%s'\n", id);
510		return NULL;
511	}
512	return (__ops_sprint_keydata(key, &newkey, "pub",
513				&key->key.pubkey) > 0) ? newkey : NULL;
514}
515
516/* export a given key */
517int
518netpgp_export_key(netpgp_t *netpgp, char *userid)
519{
520	const __ops_key_t	*keypair;
521	__ops_io_t		*io;
522
523	io = netpgp->io;
524	if (userid == NULL) {
525		userid = netpgp_getvar(netpgp, "userid");
526	}
527	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
528	if (keypair == NULL) {
529		(void) fprintf(io->errs,
530			"Cannot find own key \"%s\" in keyring\n", userid);
531		return 0;
532	}
533	return __ops_export_key(keypair, NULL);
534}
535
536/* import a key into our keyring */
537int
538netpgp_import_key(netpgp_t *netpgp, char *f)
539{
540	const unsigned	noarmor = 0;
541	const unsigned	armor = 1;
542	__ops_io_t	*io;
543	int		done;
544
545	io = netpgp->io;
546	if ((done = __ops_keyring_fileread(netpgp->pubring, noarmor, f)) == 0) {
547		done = __ops_keyring_fileread(netpgp->pubring, armor, f);
548	}
549	if (!done) {
550		(void) fprintf(io->errs, "Cannot import key from file %s\n",
551				f);
552		return 0;
553	}
554	return __ops_keyring_list(io, netpgp->pubring);
555}
556
557/* generate a new key */
558int
559netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
560{
561	__ops_key_t		*keypair;
562	__ops_userid_t		 uid;
563	__ops_output_t		*create;
564	const unsigned		 noarmor = 0;
565	__ops_io_t		*io;
566	char			*ringfile;
567	int             	 fd;
568
569	(void) memset(&uid, 0x0, sizeof(uid));
570	io = netpgp->io;
571	/* generate a new key for 'id' */
572	uid.userid = (unsigned char *) id;
573	keypair = __ops_rsa_new_selfsign_key(numbits, 65537UL, &uid);
574	if (keypair == NULL) {
575		(void) fprintf(io->errs, "Cannot generate key\n");
576		return 0;
577	}
578	/* write public key, and try to re-read it */
579	ringfile = netpgp_getvar(netpgp, "pubring");
580	fd = __ops_setup_file_append(&create, ringfile);
581	if (!__ops_write_xfer_pubkey(create, keypair, noarmor)) {
582		(void) fprintf(io->errs, "Cannot write pubkey\n");
583		return 0;
584	}
585	__ops_teardown_file_write(create, fd);
586	__ops_keyring_free(netpgp->pubring);
587	if (!__ops_keyring_fileread(netpgp->pubring, noarmor, ringfile)) {
588		(void) fprintf(io->errs, "Cannot read pubring %s\n", ringfile);
589		return 0;
590	}
591	/* write secret key, and try to re-read it */
592	ringfile = netpgp_getvar(netpgp, "sec ring file");
593	fd = __ops_setup_file_append(&create, ringfile);
594	if (!__ops_write_xfer_seckey(create, keypair, NULL, 0, noarmor)) {
595		(void) fprintf(io->errs, "Cannot write seckey\n");
596		return 0;
597	}
598	__ops_teardown_file_write(create, fd);
599	__ops_keyring_free(netpgp->secring);
600	if (!__ops_keyring_fileread(netpgp->secring, noarmor, ringfile)) {
601		(void) fprintf(io->errs, "Can't read secring %s\n", ringfile);
602		return 0;
603	}
604	__ops_keydata_free(keypair);
605	return 1;
606}
607
608/* encrypt a file */
609int
610netpgp_encrypt_file(netpgp_t *netpgp,
611			const char *userid,
612			const char *f,
613			char *out,
614			int armored)
615{
616	const __ops_key_t	*keypair;
617	const unsigned		 overwrite = 1;
618	const char		*suffix;
619	__ops_io_t		*io;
620	char			 outname[MAXPATHLEN];
621
622	io = netpgp->io;
623	if (f == NULL) {
624		(void) fprintf(io->errs,
625			"netpgp_encrypt_file: no filename specified\n");
626		return 0;
627	}
628	if (userid == NULL) {
629		userid = netpgp_getvar(netpgp, "userid");
630	}
631	suffix = (armored) ? ".asc" : ".gpg";
632	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
633	if (keypair == NULL) {
634		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
635					userid);
636		return 0;
637	}
638	if (out == NULL) {
639		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
640		out = outname;
641	}
642	return (int)__ops_encrypt_file(io, f, out, keypair, (unsigned)armored,
643					overwrite);
644}
645
646/* decrypt a file */
647int
648netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored)
649{
650	const unsigned	overwrite = 1;
651	__ops_io_t		*io;
652
653	io = netpgp->io;
654	if (f == NULL) {
655		(void) fprintf(io->errs,
656			"netpgp_decrypt_file: no filename specified\n");
657		return 0;
658	}
659	return __ops_decrypt_file(netpgp->io, f, out, netpgp->secring,
660				(unsigned)armored, overwrite, netpgp->passfp,
661				get_passphrase_cb);
662}
663
664/* sign a file */
665int
666netpgp_sign_file(netpgp_t *netpgp,
667		const char *userid,
668		const char *f,
669		char *out,
670		int armored,
671		int cleartext,
672		int detached)
673{
674	const __ops_key_t	*keypair;
675	__ops_seckey_t		*seckey;
676	const unsigned		 overwrite = 1;
677	__ops_io_t		*io;
678	char			*hashalg;
679	int			 ret;
680
681	io = netpgp->io;
682	if (f == NULL) {
683		(void) fprintf(io->errs,
684			"netpgp_sign_file: no filename specified\n");
685		return 0;
686	}
687	if (userid == NULL) {
688		userid = netpgp_getvar(netpgp, "userid");
689	}
690	/* get key with which to sign */
691	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
692	if (keypair == NULL) {
693		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
694				userid);
695		return 0;
696	}
697	ret = 1;
698	do {
699		/* print out the user id */
700		__ops_print_keydata(io, keypair, "pub", &keypair->key.pubkey);
701		if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
702			/* now decrypt key */
703			seckey = __ops_decrypt_seckey(keypair);
704			if (seckey == NULL) {
705				(void) fprintf(io->errs, "Bad passphrase\n");
706			}
707		} else {
708			__ops_keyring_t	*secring;
709
710			secring = netpgp->secring;
711			seckey = &secring->keys[0].key.seckey;
712		}
713	} while (seckey == NULL);
714	/* sign file */
715	hashalg = netpgp_getvar(netpgp, "hash");
716	if (detached) {
717		ret = __ops_sign_detached(io, f, out, seckey, hashalg);
718	} else {
719		ret = __ops_sign_file(io, f, out, seckey, hashalg,
720				(unsigned)armored, (unsigned)cleartext,
721				overwrite);
722	}
723	__ops_forget(seckey, sizeof(*seckey));
724	return ret;
725}
726
727/* verify a file */
728int
729netpgp_verify_file(netpgp_t *netpgp, const char *in, const char *out, int armored)
730{
731	__ops_validation_t	 result;
732	__ops_io_t		*io;
733
734	(void) memset(&result, 0x0, sizeof(result));
735	io = netpgp->io;
736	if (in == NULL) {
737		(void) fprintf(io->errs,
738			"netpgp_verify_file: no filename specified\n");
739		return 0;
740	}
741	if (__ops_validate_file(io, &result, in, out, armored,
742						netpgp->pubring)) {
743		resultp(io, in, &result, netpgp->pubring);
744		return 1;
745	}
746	if (result.validc + result.invalidc + result.unknownc == 0) {
747		(void) fprintf(io->errs,
748		"\"%s\": No signatures found - is this a signed file?\n",
749			in);
750	} else {
751		(void) fprintf(io->errs,
752"\"%s\": verification failure: %u invalid signatures, %u unknown signatures\n",
753			in, result.invalidc, result.unknownc);
754	}
755	return 0;
756}
757
758/* sign some memory */
759int
760netpgp_sign_memory(netpgp_t *netpgp,
761		const char *userid,
762		char *mem,
763		size_t size,
764		char *out,
765		size_t outsize,
766		const unsigned armored,
767		const unsigned cleartext)
768{
769	const __ops_key_t	*keypair;
770	__ops_seckey_t		*seckey;
771	__ops_memory_t		*signedmem;
772	__ops_io_t		*io;
773	char			*hashalg;
774	int			 ret;
775
776	io = netpgp->io;
777	if (mem == NULL) {
778		(void) fprintf(io->errs,
779			"netpgp_sign_memory: no memory to sign\n");
780		return 0;
781	}
782	if (userid == NULL) {
783		userid = netpgp_getvar(netpgp, "userid");
784	}
785	/* get key with which to sign */
786	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
787	if (keypair == NULL) {
788		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
789				userid);
790		return 0;
791	}
792	ret = 1;
793	do {
794		/* print out the user id */
795		__ops_print_keydata(io, keypair, "pub", &keypair->key.pubkey);
796		/* now decrypt key */
797		seckey = __ops_decrypt_seckey(keypair);
798		if (seckey == NULL) {
799			(void) fprintf(io->errs, "Bad passphrase\n");
800		}
801	} while (seckey == NULL);
802	/* sign file */
803	hashalg = netpgp_getvar(netpgp, "hash");
804	signedmem = __ops_sign_buf(io, mem, size, seckey, hashalg,
805						armored, cleartext);
806	if (signedmem) {
807		size_t	m;
808
809		m = MIN(__ops_mem_len(signedmem), outsize);
810		(void) memcpy(out, __ops_mem_data(signedmem), m);
811		__ops_memory_free(signedmem);
812	}
813	__ops_forget(seckey, sizeof(*seckey));
814	return ret;
815}
816
817/* verify memory */
818int
819netpgp_verify_memory(netpgp_t *netpgp, const void *in, const size_t size,
820			const int armored)
821{
822	__ops_validation_t	 result;
823	__ops_memory_t		*signedmem;
824	__ops_io_t		*io;
825	int			 ret;
826
827	(void) memset(&result, 0x0, sizeof(result));
828	io = netpgp->io;
829	if (in == NULL) {
830		(void) fprintf(io->errs,
831			"netpgp_verify_memory: no memory to verify\n");
832		return 0;
833	}
834	signedmem = __ops_memory_new();
835	__ops_memory_add(signedmem, in, size);
836	ret = __ops_validate_mem(io, &result, signedmem, armored,
837						netpgp->pubring);
838	__ops_memory_free(signedmem);
839	if (ret) {
840		resultp(io, in, &result, netpgp->pubring);
841		return 1;
842	}
843	if (result.validc + result.invalidc + result.unknownc == 0) {
844		(void) fprintf(io->errs,
845		"No signatures found - is this memory signed?\n");
846	} else {
847		(void) fprintf(io->errs,
848"memory verification failure: %u invalid signatures, %u unknown signatures\n",
849			result.invalidc, result.unknownc);
850	}
851	return 0;
852}
853
854/* wrappers for the ops_debug_level functions we added to openpgpsdk */
855
856/* set the debugging level per filename */
857int
858netpgp_set_debug(const char *f)
859{
860	return __ops_set_debug_level(f);
861}
862
863/* get the debugging level per filename */
864int
865netpgp_get_debug(const char *f)
866{
867	return __ops_get_debug_level(f);
868}
869
870/* return the version for the library */
871const char *
872netpgp_get_info(const char *type)
873{
874	return __ops_get_info(type);
875}
876
877/* list all the packets in a file */
878int
879netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname)
880{
881	__ops_keyring_t	*keyring;
882	const unsigned	 noarmor = 0;
883	__ops_io_t	*io;
884	char		 ringname[MAXPATHLEN];
885	char		*homedir;
886	int		 ret;
887
888	io = netpgp->io;
889	if (f == NULL) {
890		(void) fprintf(io->errs, "No file containing packets\n");
891		return 0;
892	}
893	homedir = netpgp_getvar(netpgp, "homedir");
894	if (pubringname == NULL) {
895		(void) snprintf(ringname, sizeof(ringname),
896				"%s/pubring.gpg", homedir);
897		pubringname = ringname;
898	}
899	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
900		(void) fprintf(io->errs, "netpgp_list_packets: bad alloc\n");
901		return 0;
902	}
903	if (!__ops_keyring_fileread(keyring, noarmor, pubringname)) {
904		free(keyring);
905		(void) fprintf(io->errs, "Cannot read pub keyring %s\n",
906			pubringname);
907		return 0;
908	}
909	netpgp->pubring = keyring;
910	netpgp_setvar(netpgp, "pubring", pubringname);
911	ret = __ops_list_packets(io, f, (unsigned)armour, keyring,
912					netpgp->passfp,
913					get_passphrase_cb);
914	free(keyring);
915	return ret;
916}
917
918/* set a variable */
919int
920netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value)
921{
922	int	i;
923
924	if ((i = findvar(netpgp, name)) < 0) {
925		/* add the element to the array */
926		if (size_arrays(netpgp, netpgp->size + 15)) {
927			netpgp->name[i = netpgp->c++] = strdup(name);
928		}
929	} else {
930		/* replace the element in the array */
931		if (netpgp->value[i]) {
932			free(netpgp->value[i]);
933			netpgp->value[i] = NULL;
934		}
935	}
936	/* sanity checks for range of values */
937	if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) {
938		if (__ops_str_to_hash_alg(value) == OPS_HASH_UNKNOWN) {
939			return 0;
940		}
941	}
942	netpgp->value[i] = strdup(value);
943	return 1;
944}
945
946/* get a variable's value (NULL if not set) */
947char *
948netpgp_getvar(netpgp_t *netpgp, const char *name)
949{
950	int	i;
951
952	return ((i = findvar(netpgp, name)) < 0) ? NULL : netpgp->value[i];
953}
954
955/* increment a value */
956int
957netpgp_incvar(netpgp_t *netpgp, const char *name, const int delta)
958{
959	char	*cp;
960	char	 num[16];
961	int	 val;
962
963	val = 0;
964	if ((cp = netpgp_getvar(netpgp, name)) != NULL) {
965		val = atoi(cp);
966	}
967	(void) snprintf(num, sizeof(num), "%d", val + delta);
968	netpgp_setvar(netpgp, name, num);
969	return 1;
970}
971
972/* set the home directory value to "home/subdir" */
973int
974netpgp_set_homedir(netpgp_t *netpgp, char *home, const char *subdir, const int quiet)
975{
976	struct stat	st;
977	char		d[MAXPATHLEN];
978
979	if (home == NULL) {
980		if (!quiet) {
981			(void) fprintf(stderr, "NULL HOME directory\n");
982		}
983		return 0;
984	}
985	(void) snprintf(d, sizeof(d), "%s%s", home, (subdir) ? subdir : "");
986	if (stat(d, &st) == 0) {
987		if ((st.st_mode & S_IFMT) == S_IFDIR) {
988			netpgp_setvar(netpgp, "homedir", d);
989			return 1;
990		}
991		(void) fprintf(stderr, "netpgp: homedir \"%s\" is not a dir\n",
992					d);
993		return 0;
994	}
995	if (!quiet) {
996		(void) fprintf(stderr,
997			"netpgp: warning homedir \"%s\" not found\n", d);
998	}
999	return 1;
1000}
1001