netpgp.c revision 1.73
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.73 2010/09/02 07:31:16 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#include "defs.h"
86
87/* read any gpg config file */
88static int
89conffile(netpgp_t *netpgp, char *homedir, char *userid, size_t length)
90{
91	regmatch_t	 matchv[10];
92	regex_t		 keyre;
93	char		 buf[BUFSIZ];
94	FILE		*fp;
95
96	__OPS_USED(netpgp);
97	(void) snprintf(buf, sizeof(buf), "%s/gpg.conf", homedir);
98	if ((fp = fopen(buf, "r")) == NULL) {
99		return 0;
100	}
101	(void) memset(&keyre, 0x0, sizeof(keyre));
102	(void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)",
103		REG_EXTENDED);
104	while (fgets(buf, (int)sizeof(buf), fp) != NULL) {
105		if (regexec(&keyre, buf, 10, matchv, 0) == 0) {
106			(void) memcpy(userid, &buf[(int)matchv[1].rm_so],
107				MIN((unsigned)(matchv[1].rm_eo -
108						matchv[1].rm_so), length));
109			if (netpgp->passfp == NULL) {
110				(void) fprintf(stderr,
111				"netpgp: default key set to \"%.*s\"\n",
112				(int)(matchv[1].rm_eo - matchv[1].rm_so),
113				&buf[(int)matchv[1].rm_so]);
114			}
115		}
116	}
117	(void) fclose(fp);
118	regfree(&keyre);
119	return 1;
120}
121
122/* small function to pretty print an 8-character raw userid */
123static char    *
124userid_to_id(const uint8_t *userid, char *id)
125{
126	static const char *hexes = "0123456789abcdef";
127	int		   i;
128
129	for (i = 0; i < 8 ; i++) {
130		id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4];
131		id[(i * 2) + 1] = hexes[userid[i] & 0xf];
132	}
133	id[8 * 2] = 0x0;
134	return id;
135}
136
137/* print out the successful signature information */
138static void
139resultp(__ops_io_t *io,
140	const char *f,
141	__ops_validation_t *res,
142	__ops_keyring_t *ring)
143{
144	const __ops_key_t	*key;
145	__ops_pubkey_t		*sigkey;
146	unsigned		 from;
147	unsigned		 i;
148	time_t			 t;
149	char			 id[MAX_ID_LENGTH + 1];
150
151	for (i = 0; i < res->validc; i++) {
152		(void) fprintf(io->res,
153			"Good signature for %s made %s",
154			(f) ? f : "<stdin>",
155			ctime(&res->valid_sigs[i].birthtime));
156		if (res->duration > 0) {
157			t = res->birthtime + res->duration;
158			(void) fprintf(io->res, "Valid until %s", ctime(&t));
159		}
160		(void) fprintf(io->res,
161			"using %s key %s\n",
162			__ops_show_pka(res->valid_sigs[i].key_alg),
163			userid_to_id(res->valid_sigs[i].signer_id, id));
164		from = 0;
165		key = __ops_getkeybyid(io, ring,
166			(const uint8_t *) res->valid_sigs[i].signer_id,
167			&from, &sigkey);
168		if (sigkey == &key->enckey) {
169			(void) fprintf(io->res,
170				"WARNING: signature for %s made with encryption key\n",
171				(f) ? f : "<stdin>");
172		}
173		__ops_print_keydata(io, ring, key, "signature ", &key->key.pubkey, 0);
174	}
175}
176
177/* check there's enough space in the arrays */
178static int
179size_arrays(netpgp_t *netpgp, unsigned needed)
180{
181	char	**temp;
182
183	if (netpgp->size == 0) {
184		/* only get here first time around */
185		netpgp->size = needed;
186		if ((netpgp->name = calloc(sizeof(char *), needed)) == NULL) {
187			(void) fprintf(stderr, "size_arrays: bad alloc\n");
188			return 0;
189		}
190		if ((netpgp->value = calloc(sizeof(char *), needed)) == NULL) {
191			free(netpgp->name);
192			(void) fprintf(stderr, "size_arrays: bad alloc\n");
193			return 0;
194		}
195	} else if (netpgp->c == netpgp->size) {
196		/* only uses 'needed' when filled array */
197		netpgp->size += needed;
198		temp = realloc(netpgp->name, sizeof(char *) * needed);
199		if (temp == NULL) {
200			(void) fprintf(stderr, "size_arrays: bad alloc\n");
201			return 0;
202		}
203		netpgp->name = temp;
204		temp = realloc(netpgp->value, sizeof(char *) * needed);
205		if (temp == NULL) {
206			(void) fprintf(stderr, "size_arrays: bad alloc\n");
207			return 0;
208		}
209		netpgp->value = temp;
210	}
211	return 1;
212}
213
214/* find the name in the array */
215static int
216findvar(netpgp_t *netpgp, const char *name)
217{
218	unsigned	i;
219
220	for (i = 0 ; i < netpgp->c && strcmp(netpgp->name[i], name) != 0; i++) {
221	}
222	return (i == netpgp->c) ? -1 : (int)i;
223}
224
225/* read a keyring and return it */
226static void *
227readkeyring(netpgp_t *netpgp, const char *name)
228{
229	__ops_keyring_t	*keyring;
230	const unsigned	 noarmor = 0;
231	char		 f[MAXPATHLEN];
232	char		*filename;
233	char		*homedir;
234
235	homedir = netpgp_getvar(netpgp, "homedir");
236	if ((filename = netpgp_getvar(netpgp, name)) == NULL) {
237		(void) snprintf(f, sizeof(f), "%s/%s.gpg", homedir, name);
238		filename = f;
239	}
240	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
241		(void) fprintf(stderr, "readkeyring: bad alloc\n");
242		return NULL;
243	}
244	if (!__ops_keyring_fileread(keyring, noarmor, filename)) {
245		free(keyring);
246		(void) fprintf(stderr, "Can't read %s %s\n", name, filename);
247		return NULL;
248	}
249	netpgp_setvar(netpgp, name, filename);
250	return keyring;
251}
252
253/* read keys from ssh key files */
254static int
255readsshkeys(netpgp_t *netpgp, char *homedir, const char *needseckey)
256{
257	__ops_keyring_t	*pubring;
258	__ops_keyring_t	*secring;
259	unsigned	 hashtype;
260	char		*hash;
261	char		 f[MAXPATHLEN];
262	char		*filename;
263
264	if ((filename = netpgp_getvar(netpgp, "sshkeyfile")) == NULL) {
265		(void) snprintf(f, sizeof(f), "%s/id_rsa.pub", homedir);
266		filename = f;
267	}
268	if ((pubring = calloc(1, sizeof(*pubring))) == NULL) {
269		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
270		return 0;
271	}
272	/* openssh2 keys use md5 by default */
273	hashtype = OPS_HASH_MD5;
274	if ((hash = netpgp_getvar(netpgp, "hash")) != NULL) {
275		/* openssh 2 hasn't really caught up to anything else yet */
276		if (netpgp_strcasecmp(hash, "md5") == 0) {
277			hashtype = OPS_HASH_MD5;
278		} else if (netpgp_strcasecmp(hash, "sha1") == 0) {
279			hashtype = OPS_HASH_SHA1;
280		} else if (netpgp_strcasecmp(hash, "sha256") == 0) {
281			hashtype = OPS_HASH_SHA256;
282		}
283	}
284	if (!__ops_ssh2_readkeys(netpgp->io, pubring, NULL, filename, NULL, hashtype)) {
285		free(pubring);
286		(void) fprintf(stderr, "readsshkeys: can't read %s\n",
287				filename);
288		return 0;
289	}
290	if (netpgp->pubring == NULL) {
291		netpgp->pubring = pubring;
292	} else {
293		__ops_append_keyring(netpgp->pubring, pubring);
294	}
295	if (needseckey) {
296		netpgp_setvar(netpgp, "sshpubfile", filename);
297		/* try to take the ".pub" off the end */
298		if (filename == f) {
299			f[strlen(f) - 4] = 0x0;
300		} else {
301			(void) snprintf(f, sizeof(f), "%.*s",
302					(int)strlen(filename) - 4, filename);
303			filename = f;
304		}
305		if ((secring = calloc(1, sizeof(*secring))) == NULL) {
306			(void) fprintf(stderr, "readsshkeys: bad alloc\n");
307			return 0;
308		}
309		if (!__ops_ssh2_readkeys(netpgp->io, pubring, secring, NULL, filename, hashtype)) {
310			(void) fprintf(stderr, "readsshkeys: can't read sec %s\n", filename);
311			return 0;
312		}
313		netpgp->secring = secring;
314		netpgp_setvar(netpgp, "sshsecfile", filename);
315	}
316	return 1;
317}
318
319/* set ssh uid to first one in pubring */
320static void
321set_first_pubring(__ops_keyring_t *pubring, char *id, size_t len, int last)
322{
323	uint8_t	*src;
324	int	 i;
325	int	 n;
326
327	(void) memset(id, 0x0, len);
328	src = pubring->keys[(last) ? pubring->keyc - 1 : 0].sigid;
329	for (i = 0, n = 0 ; i < OPS_KEY_ID_SIZE ; i += 2) {
330		n += snprintf(&id[n], len - n, "%02x%02x", src[i], src[i + 1]);
331	}
332	id[n] = 0x0;
333}
334
335/* find the time - in a specific %Y-%m-%d format - using a regexp */
336static int
337grabdate(char *s, int64_t *t)
338{
339	static regex_t	r;
340	static int	compiled;
341	regmatch_t	matches[10];
342	struct tm	tm;
343
344	if (!compiled) {
345		compiled = 1;
346		(void) regcomp(&r, "([0-9][0-9][0-9][0-9])[-/]([0-9][0-9])[-/]([0-9][0-9])", REG_EXTENDED);
347	}
348	if (regexec(&r, s, 10, matches, 0) == 0) {
349		(void) memset(&tm, 0x0, sizeof(tm));
350		tm.tm_year = (int)strtol(&s[(int)matches[1].rm_so], NULL, 10);
351		tm.tm_mon = (int)strtol(&s[(int)matches[2].rm_so], NULL, 10) - 1;
352		tm.tm_mday = (int)strtol(&s[(int)matches[3].rm_so], NULL, 10);
353		*t = mktime(&tm);
354		return 1;
355	}
356	return 0;
357}
358
359/* get expiration in seconds */
360static uint64_t
361get_duration(char *s)
362{
363	uint64_t	 now;
364	int64_t	 	 t;
365	char		*mult;
366
367	if (s == NULL) {
368		return 0;
369	}
370	now = (uint64_t)strtoull(s, NULL, 10);
371	if ((mult = strchr("hdwmy", s[strlen(s) - 1])) != NULL) {
372		switch(*mult) {
373		case 'h':
374			return now * 60 * 60;
375		case 'd':
376			return now * 60 * 60 * 24;
377		case 'w':
378			return now * 60 * 60 * 24 * 7;
379		case 'm':
380			return now * 60 * 60 * 24 * 31;
381		case 'y':
382			return now * 60 * 60 * 24 * 365;
383		}
384	}
385	if (grabdate(s, &t)) {
386		return t;
387	}
388	return (uint64_t)strtoll(s, NULL, 10);
389}
390
391/* get birthtime in seconds */
392static int64_t
393get_birthtime(char *s)
394{
395	int64_t	t;
396
397	if (s == NULL) {
398		return time(NULL);
399	}
400	if (grabdate(s, &t)) {
401		return t;
402	}
403	return (uint64_t)strtoll(s, NULL, 10);
404}
405
406/* resolve the userid */
407static const __ops_key_t *
408resolve_userid(netpgp_t *netpgp, const __ops_keyring_t *keyring, const char *userid)
409{
410	const __ops_key_t	*key;
411	__ops_io_t		*io;
412
413	if (userid == NULL) {
414		userid = netpgp_getvar(netpgp, "userid");
415	} else if (userid[0] == '0' && userid[1] == 'x') {
416		userid += 2;
417	}
418	io = netpgp->io;
419	if ((key = __ops_getkeybyname(io, keyring, userid)) == NULL) {
420		(void) fprintf(io->errs, "Can't find key '%s'\n", userid);
421	}
422	return key;
423}
424
425/* append a key to a keyring */
426static int
427appendkey(__ops_io_t *io, __ops_key_t *key, char *ringfile)
428{
429	__ops_output_t	*create;
430	const unsigned	 noarmor = 0;
431	int		 fd;
432
433	if ((fd = __ops_setup_file_append(&create, ringfile)) < 0) {
434		fd = __ops_setup_file_write(&create, ringfile, 0);
435	}
436	if (fd < 0) {
437		(void) fprintf(io->errs, "can't open pubring '%s'\n", ringfile);
438		return 0;
439	}
440	if (!__ops_write_xfer_pubkey(create, key, noarmor)) {
441		(void) fprintf(io->errs, "Cannot write pubkey\n");
442		return 0;
443	}
444	__ops_teardown_file_write(create, fd);
445	return 1;
446}
447
448/* return 1 if the file contains ascii-armoured text */
449static unsigned
450isarmoured(__ops_io_t *io, const char *f, const void *memory, const char *text)
451{
452	unsigned	 armoured;
453	FILE		*fp;
454	char	 	 buf[BUFSIZ];
455
456	armoured = 0;
457	if (f) {
458		if ((fp = fopen(f, "r")) == NULL) {
459			(void) fprintf(io->errs, "isarmoured: can't open '%s'\n", f);
460			return 0;
461		}
462		if (fgets(buf, (int)sizeof(buf), fp) != NULL) {
463			armoured = (strncmp(buf, text, strlen(text)) == 0);
464		}
465		(void) fclose(fp);
466	} else {
467		armoured = (strncmp(memory, text, strlen(text)) == 0);
468	}
469	return armoured;
470}
471
472/* vararg print function */
473static void
474p(FILE *fp, const char *s, ...)
475{
476	va_list	args;
477
478	va_start(args, s);
479	while (s != NULL) {
480		(void) fprintf(fp, "%s", s);
481		s = va_arg(args, char *);
482	}
483	va_end(args);
484}
485
486/* print a JSON object to the FILE stream */
487static void
488pobj(FILE *fp, mj_t *obj, int depth)
489{
490	unsigned	i;
491
492	if (obj == NULL) {
493		(void) fprintf(stderr, "No object found\n");
494		return;
495	}
496	for (i = 0 ; i < (unsigned)depth ; i++) {
497		p(fp, " ", NULL);
498	}
499	switch(obj->type) {
500	case MJ_NULL:
501	case MJ_FALSE:
502	case MJ_TRUE:
503		p(fp, (obj->type == MJ_NULL) ? "null" : (obj->type == MJ_FALSE) ? "false" : "true", NULL);
504		break;
505	case MJ_NUMBER:
506		p(fp, obj->value.s, NULL);
507		break;
508	case MJ_STRING:
509		(void) fprintf(fp, "%.*s", (int)(obj->c), obj->value.s);
510		break;
511	case MJ_ARRAY:
512		for (i = 0 ; i < obj->c ; i++) {
513			pobj(fp, &obj->value.v[i], depth + 1);
514			if (i < obj->c - 1) {
515				(void) fprintf(fp, ", ");
516			}
517		}
518		(void) fprintf(fp, "\n");
519		break;
520	case MJ_OBJECT:
521		for (i = 0 ; i < obj->c ; i += 2) {
522			pobj(fp, &obj->value.v[i], depth + 1);
523			p(fp, ": ", NULL);
524			pobj(fp, &obj->value.v[i + 1], 0);
525			if (i < obj->c - 1) {
526				p(fp, ", ", NULL);
527			}
528		}
529		p(fp, "\n", NULL);
530		break;
531	default:
532		break;
533	}
534}
535
536/* return the time as a string */
537static char *
538ptimestr(char *dest, size_t size, time_t t)
539{
540	struct tm      *tm;
541
542	tm = gmtime(&t);
543	(void) snprintf(dest, size, "%04d-%02d-%02d",
544		tm->tm_year + 1900,
545		tm->tm_mon + 1,
546		tm->tm_mday);
547	return dest;
548}
549
550/* format a JSON object */
551static void
552format_json_key(FILE *fp, mj_t *obj, const int psigs)
553{
554	int64_t	 birthtime;
555	int64_t	 duration;
556	time_t	 now;
557	char	 tbuf[32];
558	char	*s;
559	mj_t	*sub;
560	int	 i;
561
562	if (__ops_get_debug_level(__FILE__)) {
563		mj_asprint(&s, obj);
564		(void) fprintf(stderr, "formatobj: json is '%s'\n", s);
565		free(s);
566	}
567	if (obj->c == 2 && obj->value.v[1].type == MJ_STRING &&
568	    strcmp(obj->value.v[1].value.s, "[REVOKED]") == 0) {
569		/* whole key has been rovoked - just return */
570		return;
571	}
572	pobj(fp, &obj->value.v[mj_object_find(obj, "header", 0, 2) + 1], 0);
573	p(fp, " ", NULL);
574	pobj(fp, &obj->value.v[mj_object_find(obj, "key bits", 0, 2) + 1], 0);
575	p(fp, "/", NULL);
576	pobj(fp, &obj->value.v[mj_object_find(obj, "pka", 0, 2) + 1], 0);
577	p(fp, " ", NULL);
578	pobj(fp, &obj->value.v[mj_object_find(obj, "key id", 0, 2) + 1], 0);
579	birthtime = strtoll(obj->value.v[mj_object_find(obj, "birthtime", 0, 2) + 1].value.s, NULL, 10);
580	p(fp, " ", ptimestr(tbuf, sizeof(tbuf), birthtime), NULL);
581	duration = strtoll(obj->value.v[mj_object_find(obj, "duration", 0, 2) + 1].value.s, NULL, 10);
582	if (duration > 0) {
583		now = time(NULL);
584		p(fp, " ", (birthtime + duration < now) ? "[EXPIRED " : "[EXPIRES ",
585			ptimestr(tbuf, sizeof(tbuf), birthtime + duration), "]", NULL);
586	}
587	p(fp, "\n", "Key fingerprint: ", NULL);
588	pobj(fp, &obj->value.v[mj_object_find(obj, "fingerprint", 0, 2) + 1], 0);
589	p(fp, "\n", NULL);
590	/* go to field after \"duration\" */
591	for (i = mj_object_find(obj, "duration", 0, 2) + 2; i < mj_arraycount(obj) ; i += 2) {
592		if (strcmp(obj->value.v[i].value.s, "uid") == 0) {
593			sub = &obj->value.v[i + 1];
594			p(fp, "uid", NULL);
595			pobj(fp, &sub->value.v[0], (psigs) ? 4 : 14); /* human name */
596			pobj(fp, &sub->value.v[1], 1); /* any revocation */
597			p(fp, "\n", NULL);
598		} else if (strcmp(obj->value.v[i].value.s, "encryption") == 0) {
599			sub = &obj->value.v[i + 1];
600			p(fp, "encryption", NULL);
601			pobj(fp, &sub->value.v[0], 1);	/* size */
602			p(fp, "/", NULL);
603			pobj(fp, &sub->value.v[1], 0); /* alg */
604			p(fp, " ", NULL);
605			pobj(fp, &sub->value.v[2], 0); /* id */
606			p(fp, " ", ptimestr(tbuf, sizeof(tbuf), strtoll(sub->value.v[3].value.s, NULL, 10)),
607				"\n", NULL);
608		} else if (strcmp(obj->value.v[i].value.s, "sig") == 0) {
609			sub = &obj->value.v[i + 1];
610			p(fp, "sig", NULL);
611			pobj(fp, &sub->value.v[0], 8);	/* size */
612			p(fp, "  ", ptimestr(tbuf, sizeof(tbuf), strtoll(sub->value.v[1].value.s, NULL, 10)),
613				" ", NULL); /* time */
614			pobj(fp, &sub->value.v[2], 0); /* human name */
615			p(fp, "\n", NULL);
616		} else {
617			fprintf(stderr, "weird '%s'\n", obj->value.v[i].value.s);
618			pobj(fp, &obj->value.v[i], 0); /* human name */
619		}
620	}
621	p(fp, "\n", NULL);
622}
623
624/***************************************************************************/
625/* exported functions start here */
626/***************************************************************************/
627
628/* initialise a netpgp_t structure */
629int
630netpgp_init(netpgp_t *netpgp)
631{
632	__ops_io_t	*io;
633	char		 id[MAX_ID_LENGTH];
634	char		*homedir;
635	char		*userid;
636	char		*stream;
637	char		*passfd;
638	char		*results;
639	int		 coredumps;
640	int		 last;
641
642#ifdef HAVE_SYS_RESOURCE_H
643	struct rlimit	limit;
644
645	coredumps = netpgp_getvar(netpgp, "coredumps") != NULL;
646	if (!coredumps) {
647		(void) memset(&limit, 0x0, sizeof(limit));
648		if (setrlimit(RLIMIT_CORE, &limit) != 0) {
649			(void) fprintf(stderr,
650			"netpgp: warning - can't turn off core dumps\n");
651			coredumps = 1;
652		}
653	}
654#else
655	coredumps = 1;
656#endif
657	if ((io = calloc(1, sizeof(*io))) == NULL) {
658		(void) fprintf(stderr, "netpgp_init: bad alloc\n");
659		return 0;
660	}
661	io->outs = stdout;
662	if ((stream = netpgp_getvar(netpgp, "outs")) != NULL &&
663	    strcmp(stream, "<stderr>") == 0) {
664		io->outs = stderr;
665	}
666	io->errs = stderr;
667	if ((stream = netpgp_getvar(netpgp, "errs")) != NULL &&
668	    strcmp(stream, "<stdout>") == 0) {
669		io->errs = stdout;
670	}
671	if ((results = netpgp_getvar(netpgp, "res")) == NULL) {
672		io->res = io->errs;
673	} else if (strcmp(results, "<stdout>") == 0) {
674		io->res = stdout;
675	} else if (strcmp(results, "<stderr>") == 0) {
676		io->res = stderr;
677	} else {
678		if ((io->res = fopen(results, "w")) == NULL) {
679			(void) fprintf(io->errs, "Can't open results %s for writing\n",
680				results);
681			free(io);
682			return 0;
683		}
684	}
685	netpgp->io = io;
686	if ((passfd = netpgp_getvar(netpgp, "pass-fd")) != NULL &&
687	    (netpgp->passfp = fdopen(atoi(passfd), "r")) == NULL) {
688		(void) fprintf(io->errs, "Can't open fd %s for reading\n",
689			passfd);
690		return 0;
691	}
692	if (coredumps) {
693		(void) fprintf(io->errs,
694			"netpgp: warning: core dumps enabled\n");
695	}
696	if ((homedir = netpgp_getvar(netpgp, "homedir")) == NULL) {
697		(void) fprintf(io->errs, "netpgp: bad homedir\n");
698		return 0;
699	}
700	/* read from either gpg files or ssh keys */
701	if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
702		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
703			(void) memset(id, 0x0, sizeof(id));
704			(void) conffile(netpgp, homedir, id, sizeof(id));
705			if (id[0] != 0x0) {
706				netpgp_setvar(netpgp, "userid", userid = id);
707			}
708		}
709		if (userid == NULL) {
710			if (netpgp_getvar(netpgp, "need userid") != NULL) {
711				(void) fprintf(io->errs,
712						"Cannot find user id\n");
713				return 0;
714			}
715		} else {
716			(void) netpgp_setvar(netpgp, "userid", userid);
717		}
718		netpgp->pubring = readkeyring(netpgp, "pubring");
719		if (netpgp->pubring == NULL) {
720			(void) fprintf(io->errs, "Can't read pub keyring\n");
721			return 0;
722		}
723		netpgp->secring = readkeyring(netpgp, "secring");
724		if (netpgp->secring == NULL) {
725			(void) fprintf(io->errs, "Can't read sec keyring\n");
726			return 0;
727		}
728	} else {
729		last = (netpgp->pubring != NULL);
730		if (!readsshkeys(netpgp, homedir, netpgp_getvar(netpgp, "need seckey"))) {
731			(void) fprintf(io->errs, "Can't read ssh keys\n");
732			return 0;
733		}
734		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
735			set_first_pubring(netpgp->pubring, id, sizeof(id), last);
736			netpgp_setvar(netpgp, "userid", userid = id);
737		}
738		if (userid == NULL) {
739			if (netpgp_getvar(netpgp, "need userid") != NULL) {
740				(void) fprintf(io->errs,
741						"Cannot find user id\n");
742				return 0;
743			}
744		} else {
745			(void) netpgp_setvar(netpgp, "userid", userid);
746		}
747	}
748	return 1;
749}
750
751/* finish off with the netpgp_t struct */
752int
753netpgp_end(netpgp_t *netpgp)
754{
755	unsigned	i;
756
757	for (i = 0 ; i < netpgp->c ; i++) {
758		if (netpgp->name[i] != NULL) {
759			free(netpgp->name[i]);
760		}
761		if (netpgp->value[i] != NULL) {
762			free(netpgp->value[i]);
763		}
764	}
765	if (netpgp->name != NULL) {
766		free(netpgp->name);
767	}
768	if (netpgp->value != NULL) {
769		free(netpgp->value);
770	}
771	if (netpgp->pubring != NULL) {
772		__ops_keyring_free(netpgp->pubring);
773	}
774	if (netpgp->secring != NULL) {
775		__ops_keyring_free(netpgp->secring);
776	}
777	free(netpgp->io);
778	return 1;
779}
780
781/* list the keys in a keyring */
782int
783netpgp_list_keys(netpgp_t *netpgp, const int psigs)
784{
785	if (netpgp->pubring == NULL) {
786		(void) fprintf(stderr, "No keyring\n");
787		return 0;
788	}
789	return __ops_keyring_list(netpgp->io, netpgp->pubring, psigs);
790}
791
792/* list the keys in a keyring, returning a JSON string */
793int
794netpgp_list_keys_json(netpgp_t *netpgp, char **json, const int psigs)
795{
796	mj_t	obj;
797	int	ret;
798
799	if (netpgp->pubring == NULL) {
800		(void) fprintf(stderr, "No keyring\n");
801		return 0;
802	}
803	(void) memset(&obj, 0x0, sizeof(obj));
804	if (!__ops_keyring_json(netpgp->io, netpgp->pubring, &obj, psigs)) {
805		(void) fprintf(stderr, "No keys in keyring\n");
806		return 0;
807	}
808	ret = mj_asprint(json, &obj);
809	mj_delete(&obj);
810	return ret;
811}
812
813DEFINE_ARRAY(strings_t, char *);
814
815#ifndef HKP_VERSION
816#define HKP_VERSION	1
817#endif
818
819/* find and list some keys in a keyring */
820int
821netpgp_match_keys(netpgp_t *netpgp, char *name, const char *fmt, void *vp, const int psigs)
822{
823	const __ops_key_t	*key;
824	unsigned		 k;
825	strings_t		 pubs;
826	FILE			*fp = (FILE *)vp;
827
828	if (name[0] == '0' && name[1] == 'x') {
829		name += 2;
830	}
831	(void) memset(&pubs, 0x0, sizeof(pubs));
832	k = 0;
833	do {
834		key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring,
835						name, &k);
836		if (key != NULL) {
837			ALLOC(char *, pubs.v, pubs.size, pubs.c, 10, 10,
838					"netpgp_match_keys", return 0);
839			if (strcmp(fmt, "mr") == 0) {
840				__ops_hkp_sprint_keydata(netpgp->io, netpgp->pubring,
841						key, &pubs.v[pubs.c],
842						&key->key.pubkey, psigs);
843			} else {
844				__ops_sprint_keydata(netpgp->io, netpgp->pubring,
845						key, &pubs.v[pubs.c],
846						"signature ",
847						&key->key.pubkey, psigs);
848			}
849			if (pubs.v[pubs.c] != NULL) {
850				pubs.c += 1;
851			}
852			k += 1;
853		}
854	} while (key != NULL);
855	if (strcmp(fmt, "mr") == 0) {
856		(void) fprintf(fp, "info:%d:%d\n", HKP_VERSION, pubs.c);
857	} else {
858		(void) fprintf(fp, "%d key%s found\n", pubs.c,
859			(pubs.c == 1) ? "" : "s");
860	}
861	for (k = 0 ; k < pubs.c ; k++) {
862		(void) fprintf(fp, "%s%s", pubs.v[k], (k < pubs.c - 1) ? "\n" : "");
863		free(pubs.v[k]);
864	}
865	free(pubs.v);
866	return pubs.c;
867}
868
869/* find and list some keys in a keyring - return JSON string */
870int
871netpgp_match_keys_json(netpgp_t *netpgp, char **json, char *name, const char *fmt, const int psigs)
872{
873	const __ops_key_t	*key;
874	unsigned		 k;
875	mj_t			 id_array;
876	int			 ret;
877
878	if (name[0] == '0' && name[1] == 'x') {
879		name += 2;
880	}
881	(void) memset(&id_array, 0x0, sizeof(id_array));
882	k = 0;
883	*json = NULL;
884	mj_create(&id_array, "array");
885	do {
886		key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring,
887						name, &k);
888		if (key != NULL) {
889			if (strcmp(fmt, "mr") == 0) {
890#if 0
891				__ops_hkp_sprint_keydata(netpgp->io, netpgp->pubring,
892						key, &pubs.v[pubs.c],
893						&key->key.pubkey, psigs);
894#endif
895			} else {
896				ALLOC(mj_t, id_array.value.v, id_array.size,
897					id_array.c, 10, 10, "netpgp_match_keys_json", return 0);
898				__ops_sprint_mj(netpgp->io, netpgp->pubring,
899						key, &id_array.value.v[id_array.c++],
900						"signature ",
901						&key->key.pubkey, psigs);
902			}
903			k += 1;
904		}
905	} while (key != NULL);
906	ret = mj_asprint(json, &id_array);
907	mj_delete(&id_array);
908	return ret;
909}
910
911/* find and list some public keys in a keyring */
912int
913netpgp_match_pubkeys(netpgp_t *netpgp, char *name, void *vp)
914{
915	const __ops_key_t	*key;
916	unsigned		 k;
917	strings_t		 pubs;
918	FILE			*fp = (FILE *)vp;
919
920	(void) memset(&pubs, 0x0, sizeof(pubs));
921	do {
922		key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring,
923						name, &k);
924		if (key != NULL) {
925			char	out[1024 * 64];
926
927			ALLOC(char *, pubs.v, pubs.size, pubs.c, 10, 10,
928					"netpgp_match_pubkeys", return 0);
929			(void) __ops_sprint_pubkey(key, out, sizeof(out));
930			pubs.v[pubs.c++] = netpgp_strdup(out);
931			k += 1;
932		}
933	} while (key != NULL);
934	(void) fprintf(fp, "info:%d:%d\n", HKP_VERSION, pubs.c);
935	for (k = 0 ; k < pubs.c ; k++) {
936		(void) fprintf(fp, "%s", pubs.v[k]);
937		free(pubs.v[k]);
938	}
939	free(pubs.v);
940	return pubs.c;
941}
942
943/* find a key in a keyring */
944int
945netpgp_find_key(netpgp_t *netpgp, char *id)
946{
947	__ops_io_t	*io;
948
949	io = netpgp->io;
950	if (id == NULL) {
951		(void) fprintf(io->errs, "NULL id to search for\n");
952		return 0;
953	}
954	return __ops_getkeybyname(netpgp->io, netpgp->pubring, id) != NULL;
955}
956
957/* get a key in a keyring */
958char *
959netpgp_get_key(netpgp_t *netpgp, const char *name, const char *fmt)
960{
961	const __ops_key_t	*key;
962	char			*newkey;
963
964	if ((key = resolve_userid(netpgp, netpgp->pubring, name)) == NULL) {
965		return NULL;
966	}
967	if (strcmp(fmt, "mr") == 0) {
968		return (__ops_hkp_sprint_keydata(netpgp->io, netpgp->pubring,
969				key, &newkey,
970				&key->key.pubkey,
971				netpgp_getvar(netpgp, "subkey sigs") != NULL) > 0) ? newkey : NULL;
972	}
973	return (__ops_sprint_keydata(netpgp->io, netpgp->pubring,
974				key, &newkey, "signature",
975				&key->key.pubkey,
976				netpgp_getvar(netpgp, "subkey sigs") != NULL) > 0) ? newkey : NULL;
977}
978
979/* export a given key */
980char *
981netpgp_export_key(netpgp_t *netpgp, char *name)
982{
983	const __ops_key_t	*key;
984	__ops_io_t		*io;
985
986	io = netpgp->io;
987	if ((key = resolve_userid(netpgp, netpgp->pubring, name)) == NULL) {
988		return NULL;
989	}
990	return __ops_export_key(io, key, NULL);
991}
992
993#define IMPORT_ARMOR_HEAD	"-----BEGIN PGP PUBLIC KEY BLOCK-----"
994
995/* import a key into our keyring */
996int
997netpgp_import_key(netpgp_t *netpgp, char *f)
998{
999	__ops_io_t	*io;
1000	unsigned	 realarmor;
1001	int		 done;
1002
1003	io = netpgp->io;
1004	realarmor = isarmoured(io, f, NULL, IMPORT_ARMOR_HEAD);
1005	done = __ops_keyring_fileread(netpgp->pubring, realarmor, f);
1006	if (!done) {
1007		(void) fprintf(io->errs, "Cannot import key from file %s\n", f);
1008		return 0;
1009	}
1010	return __ops_keyring_list(io, netpgp->pubring, 0);
1011}
1012
1013/* generate a new key */
1014int
1015netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
1016{
1017	__ops_output_t		*create;
1018	const unsigned		 noarmor = 0;
1019	__ops_key_t		*key;
1020	__ops_io_t		*io;
1021	uint8_t			*uid;
1022	char			 newid[1024];
1023	char			 filename[MAXPATHLEN];
1024	char			 dir[MAXPATHLEN];
1025	char			*cp;
1026	char			*ringfile;
1027	int             	 fd;
1028
1029	uid = NULL;
1030	io = netpgp->io;
1031	/* generate a new key */
1032	if (id) {
1033		(void) snprintf(newid, sizeof(newid), "%s", id);
1034	} else {
1035		(void) snprintf(newid, sizeof(newid), "RSA %d-bit key <%s@localhost>", numbits, getenv("LOGNAME"));
1036	}
1037	uid = (uint8_t *)newid;
1038	key = __ops_rsa_new_selfsign_key(numbits, 65537UL, uid, netpgp_getvar(netpgp, "hash"));
1039	if (key == NULL) {
1040		(void) fprintf(io->errs, "Cannot generate key\n");
1041		return 0;
1042	}
1043	cp = NULL;
1044	__ops_sprint_keydata(netpgp->io, NULL, key, &cp, "signature ", &key->key.seckey.pubkey, 0);
1045	(void) fprintf(stdout, "%s", cp);
1046	/* write public key */
1047	(void) snprintf(dir, sizeof(dir), "%s/%.16s", netpgp_getvar(netpgp, "homedir"), &cp[31]);
1048	if (mkdir(dir, 0700) < 0) {
1049		(void) fprintf(io->errs, "can't mkdir '%s'\n", dir);
1050		return 0;
1051	}
1052	(void) fprintf(io->errs, "netpgp: generated keys in directory %s\n", dir);
1053	(void) snprintf(ringfile = filename, sizeof(filename), "%s/pubring.gpg", dir);
1054	if (!appendkey(io, key, ringfile)) {
1055		(void) fprintf(io->errs, "Cannot write pubkey to '%s'\n", ringfile);
1056		return 0;
1057	}
1058	if (netpgp->pubring != NULL) {
1059		__ops_keyring_free(netpgp->pubring);
1060	}
1061	/* write secret key */
1062	(void) snprintf(ringfile = filename, sizeof(filename), "%s/secring.gpg", dir);
1063	if ((fd = __ops_setup_file_append(&create, ringfile)) < 0) {
1064		fd = __ops_setup_file_write(&create, ringfile, 0);
1065	}
1066	if (fd < 0) {
1067		(void) fprintf(io->errs, "can't append secring '%s'\n", ringfile);
1068		return 0;
1069	}
1070	if (!__ops_write_xfer_seckey(create, key, NULL, 0, noarmor)) {
1071		(void) fprintf(io->errs, "Cannot write seckey\n");
1072		return 0;
1073	}
1074	__ops_teardown_file_write(create, fd);
1075	if (netpgp->secring != NULL) {
1076		__ops_keyring_free(netpgp->secring);
1077	}
1078	__ops_keydata_free(key);
1079	free(cp);
1080	return 1;
1081}
1082
1083/* encrypt a file */
1084int
1085netpgp_encrypt_file(netpgp_t *netpgp,
1086			const char *userid,
1087			const char *f,
1088			char *out,
1089			int armored)
1090{
1091	const __ops_key_t	*key;
1092	const unsigned		 overwrite = 1;
1093	const char		*suffix;
1094	__ops_io_t		*io;
1095	char			 outname[MAXPATHLEN];
1096
1097	io = netpgp->io;
1098	if (f == NULL) {
1099		(void) fprintf(io->errs,
1100			"netpgp_encrypt_file: no filename specified\n");
1101		return 0;
1102	}
1103	suffix = (armored) ? ".asc" : ".gpg";
1104	/* get key with which to sign */
1105	if ((key = resolve_userid(netpgp, netpgp->pubring, userid)) == NULL) {
1106		return 0;
1107	}
1108	if (out == NULL) {
1109		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
1110		out = outname;
1111	}
1112	return (int)__ops_encrypt_file(io, f, out, key, (unsigned)armored,
1113					overwrite);
1114}
1115
1116#define ARMOR_HEAD	"-----BEGIN PGP MESSAGE-----"
1117
1118/* decrypt a file */
1119int
1120netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored)
1121{
1122	const unsigned	 overwrite = 1;
1123	__ops_io_t	*io;
1124	unsigned	 realarmor;
1125
1126	__OPS_USED(armored);
1127	io = netpgp->io;
1128	if (f == NULL) {
1129		(void) fprintf(io->errs,
1130			"netpgp_decrypt_file: no filename specified\n");
1131		return 0;
1132	}
1133	realarmor = isarmoured(io, f, NULL, ARMOR_HEAD);
1134	return __ops_decrypt_file(netpgp->io, f, out, netpgp->secring,
1135				netpgp->pubring,
1136				realarmor, overwrite,
1137				netpgp->passfp, get_passphrase_cb);
1138}
1139
1140/* sign a file */
1141int
1142netpgp_sign_file(netpgp_t *netpgp,
1143		const char *userid,
1144		const char *f,
1145		char *out,
1146		int armored,
1147		int cleartext,
1148		int detached)
1149{
1150	const __ops_key_t	*keypair;
1151	const __ops_key_t	*pubkey;
1152	__ops_seckey_t		*seckey;
1153	const unsigned		 overwrite = 1;
1154	__ops_io_t		*io;
1155	const char		*hashalg;
1156	int			 ret;
1157
1158	io = netpgp->io;
1159	if (f == NULL) {
1160		(void) fprintf(io->errs,
1161			"netpgp_sign_file: no filename specified\n");
1162		return 0;
1163	}
1164	/* get key with which to sign */
1165	if ((keypair = resolve_userid(netpgp, netpgp->secring, userid)) == NULL) {
1166		return 0;
1167	}
1168	ret = 1;
1169	do {
1170		if (netpgp->passfp == NULL) {
1171			/* print out the user id */
1172			pubkey = __ops_getkeybyname(io, netpgp->pubring, userid);
1173			if (pubkey == NULL) {
1174				(void) fprintf(io->errs,
1175					"netpgp: warning - using pubkey from secring\n");
1176				__ops_print_keydata(io, netpgp->pubring, keypair, "signature ",
1177					&keypair->key.seckey.pubkey, 0);
1178			} else {
1179				__ops_print_keydata(io, netpgp->pubring, pubkey, "signature ",
1180					&pubkey->key.pubkey, 0);
1181			}
1182		}
1183		if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
1184			/* now decrypt key */
1185			seckey = __ops_decrypt_seckey(keypair, netpgp->passfp);
1186			if (seckey == NULL) {
1187				(void) fprintf(io->errs, "Bad passphrase\n");
1188			}
1189		} else {
1190			__ops_keyring_t	*secring;
1191
1192			secring = netpgp->secring;
1193			seckey = &secring->keys[0].key.seckey;
1194		}
1195	} while (seckey == NULL);
1196	/* sign file */
1197	hashalg = netpgp_getvar(netpgp, "hash");
1198	if (seckey->pubkey.alg == OPS_PKA_DSA) {
1199		hashalg = "sha1";
1200	}
1201	if (detached) {
1202		ret = __ops_sign_detached(io, f, out, seckey, hashalg,
1203				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
1204				get_duration(netpgp_getvar(netpgp, "duration")),
1205				(unsigned)armored,
1206				overwrite);
1207	} else {
1208		ret = __ops_sign_file(io, f, out, seckey, hashalg,
1209				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
1210				get_duration(netpgp_getvar(netpgp, "duration")),
1211				(unsigned)armored, (unsigned)cleartext,
1212				overwrite);
1213	}
1214	__ops_forget(seckey, (unsigned)sizeof(*seckey));
1215	return ret;
1216}
1217
1218#define ARMOR_SIG_HEAD	"-----BEGIN PGP SIGNATURE-----\r\n"
1219
1220/* verify a file */
1221int
1222netpgp_verify_file(netpgp_t *netpgp, const char *in, const char *out, int armored)
1223{
1224	__ops_validation_t	 result;
1225	__ops_io_t		*io;
1226	unsigned		 realarmor;
1227
1228	__OPS_USED(armored);
1229	(void) memset(&result, 0x0, sizeof(result));
1230	io = netpgp->io;
1231	if (in == NULL) {
1232		(void) fprintf(io->errs,
1233			"netpgp_verify_file: no filename specified\n");
1234		return 0;
1235	}
1236	realarmor = isarmoured(io, in, NULL, ARMOR_SIG_HEAD);
1237	if (__ops_validate_file(io, &result, in, out, (const int)realarmor, netpgp->pubring)) {
1238		resultp(io, in, &result, netpgp->pubring);
1239		return 1;
1240	}
1241	if (result.validc + result.invalidc + result.unknownc == 0) {
1242		(void) fprintf(io->errs,
1243		"\"%s\": No signatures found - is this a signed file?\n",
1244			in);
1245	} else if (result.invalidc == 0 && result.unknownc == 0) {
1246		(void) fprintf(io->errs,
1247			"\"%s\": file verification failure: invalid signature time\n", in);
1248	} else {
1249		(void) fprintf(io->errs,
1250"\"%s\": verification failure: %u invalid signatures, %u unknown signatures\n",
1251			in, result.invalidc, result.unknownc);
1252	}
1253	return 0;
1254}
1255
1256/* sign some memory */
1257int
1258netpgp_sign_memory(netpgp_t *netpgp,
1259		const char *userid,
1260		char *mem,
1261		size_t size,
1262		char *out,
1263		size_t outsize,
1264		const unsigned armored,
1265		const unsigned cleartext)
1266{
1267	const __ops_key_t	*keypair;
1268	const __ops_key_t	*pubkey;
1269	__ops_seckey_t		*seckey;
1270	__ops_memory_t		*signedmem;
1271	__ops_io_t		*io;
1272	const char		*hashalg;
1273	int			 ret;
1274
1275	io = netpgp->io;
1276	if (mem == NULL) {
1277		(void) fprintf(io->errs,
1278			"netpgp_sign_memory: no memory to sign\n");
1279		return 0;
1280	}
1281	if ((keypair = resolve_userid(netpgp, netpgp->secring, userid)) == NULL) {
1282		return 0;
1283	}
1284	ret = 1;
1285	do {
1286		if (netpgp->passfp == NULL) {
1287			/* print out the user id */
1288			pubkey = __ops_getkeybyname(io, netpgp->pubring, userid);
1289			if (pubkey == NULL) {
1290				(void) fprintf(io->errs,
1291					"netpgp: warning - using pubkey from secring\n");
1292				__ops_print_keydata(io, netpgp->pubring, keypair, "signature ",
1293					&keypair->key.seckey.pubkey, 0);
1294			} else {
1295				__ops_print_keydata(io, netpgp->pubring, pubkey, "signature ",
1296					&pubkey->key.pubkey, 0);
1297			}
1298		}
1299		/* now decrypt key */
1300		seckey = __ops_decrypt_seckey(keypair, netpgp->passfp);
1301		if (seckey == NULL) {
1302			(void) fprintf(io->errs, "Bad passphrase\n");
1303		}
1304	} while (seckey == NULL);
1305	/* sign file */
1306	(void) memset(out, 0x0, outsize);
1307	hashalg = netpgp_getvar(netpgp, "hash");
1308	if (seckey->pubkey.alg == OPS_PKA_DSA) {
1309		hashalg = "sha1";
1310	}
1311	signedmem = __ops_sign_buf(io, mem, size, seckey,
1312				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
1313				get_duration(netpgp_getvar(netpgp, "duration")),
1314				hashalg, armored, cleartext);
1315	if (signedmem) {
1316		size_t	m;
1317
1318		m = MIN(__ops_mem_len(signedmem), outsize);
1319		(void) memcpy(out, __ops_mem_data(signedmem), m);
1320		__ops_memory_free(signedmem);
1321		ret = (int)m;
1322	} else {
1323		ret = 0;
1324	}
1325	__ops_forget(seckey, (unsigned)sizeof(*seckey));
1326	return ret;
1327}
1328
1329/* verify memory */
1330int
1331netpgp_verify_memory(netpgp_t *netpgp, const void *in, const size_t size,
1332			void *out, size_t outsize, const int armored)
1333{
1334	__ops_validation_t	 result;
1335	__ops_memory_t		*signedmem;
1336	__ops_memory_t		*cat;
1337	__ops_io_t		*io;
1338	size_t			 m;
1339	int			 ret;
1340
1341	(void) memset(&result, 0x0, sizeof(result));
1342	io = netpgp->io;
1343	if (in == NULL) {
1344		(void) fprintf(io->errs,
1345			"netpgp_verify_memory: no memory to verify\n");
1346		return 0;
1347	}
1348	signedmem = __ops_memory_new();
1349	__ops_memory_add(signedmem, in, size);
1350	if (out) {
1351		cat = __ops_memory_new();
1352	}
1353	ret = __ops_validate_mem(io, &result, signedmem,
1354				(out) ? &cat : NULL,
1355				armored, netpgp->pubring);
1356	__ops_memory_free(signedmem);
1357	if (ret) {
1358		resultp(io, "<stdin>", &result, netpgp->pubring);
1359		if (out) {
1360			m = MIN(__ops_mem_len(cat), outsize);
1361			(void) memcpy(out, __ops_mem_data(cat), m);
1362			__ops_memory_free(cat);
1363		} else {
1364			m = 1;
1365		}
1366		return (int)m;
1367	}
1368	if (result.validc + result.invalidc + result.unknownc == 0) {
1369		(void) fprintf(io->errs,
1370		"No signatures found - is this memory signed?\n");
1371	} else if (result.invalidc == 0 && result.unknownc == 0) {
1372		(void) fprintf(io->errs,
1373			"memory verification failure: invalid signature time\n");
1374	} else {
1375		(void) fprintf(io->errs,
1376"memory verification failure: %u invalid signatures, %u unknown signatures\n",
1377			result.invalidc, result.unknownc);
1378	}
1379	return 0;
1380}
1381
1382/* encrypt some memory */
1383int
1384netpgp_encrypt_memory(netpgp_t *netpgp,
1385			const char *userid,
1386			void *in,
1387			const size_t insize,
1388			char *out,
1389			size_t outsize,
1390			int armored)
1391{
1392	const __ops_key_t	*keypair;
1393	__ops_memory_t		*enc;
1394	__ops_io_t		*io;
1395	size_t			 m;
1396
1397	io = netpgp->io;
1398	if (in == NULL) {
1399		(void) fprintf(io->errs,
1400			"netpgp_encrypt_buf: no memory to encrypt\n");
1401		return 0;
1402	}
1403	if ((keypair = resolve_userid(netpgp, netpgp->pubring, userid)) == NULL) {
1404		return 0;
1405	}
1406	if (in == out) {
1407		(void) fprintf(io->errs,
1408			"netpgp_encrypt_buf: input and output bufs need to be different\n");
1409		return 0;
1410	}
1411	if (outsize < insize) {
1412		(void) fprintf(io->errs,
1413			"netpgp_encrypt_buf: input size is larger than output size\n");
1414		return 0;
1415	}
1416	enc = __ops_encrypt_buf(io, in, insize, keypair, (unsigned)armored);
1417	m = MIN(__ops_mem_len(enc), outsize);
1418	(void) memcpy(out, __ops_mem_data(enc), m);
1419	__ops_memory_free(enc);
1420	return (int)m;
1421}
1422
1423/* decrypt a chunk of memory */
1424int
1425netpgp_decrypt_memory(netpgp_t *netpgp, const void *input, const size_t insize,
1426			char *out, size_t outsize, const int armored)
1427{
1428	__ops_memory_t	*mem;
1429	__ops_io_t	*io;
1430	unsigned	 realarmour;
1431	size_t		 m;
1432
1433	__OPS_USED(armored);
1434	io = netpgp->io;
1435	if (input == NULL) {
1436		(void) fprintf(io->errs,
1437			"netpgp_decrypt_memory: no memory\n");
1438		return 0;
1439	}
1440	realarmour = isarmoured(io, NULL, input, ARMOR_HEAD);
1441	mem = __ops_decrypt_buf(netpgp->io, input, insize, netpgp->secring,
1442				netpgp->pubring,
1443				realarmour, netpgp->passfp,
1444				get_passphrase_cb);
1445	m = MIN(__ops_mem_len(mem), outsize);
1446	(void) memcpy(out, __ops_mem_data(mem), m);
1447	__ops_memory_free(mem);
1448	return (int)m;
1449}
1450
1451/* wrappers for the ops_debug_level functions we added to openpgpsdk */
1452
1453/* set the debugging level per filename */
1454int
1455netpgp_set_debug(const char *f)
1456{
1457	return __ops_set_debug_level(f);
1458}
1459
1460/* get the debugging level per filename */
1461int
1462netpgp_get_debug(const char *f)
1463{
1464	return __ops_get_debug_level(f);
1465}
1466
1467/* return the version for the library */
1468const char *
1469netpgp_get_info(const char *type)
1470{
1471	return __ops_get_info(type);
1472}
1473
1474/* list all the packets in a file */
1475int
1476netpgp_list_packets(netpgp_t *netpgp, char *f, int armor, char *pubringname)
1477{
1478	__ops_keyring_t	*keyring;
1479	const unsigned	 noarmor = 0;
1480	struct stat	 st;
1481	__ops_io_t	*io;
1482	char		 ringname[MAXPATHLEN];
1483	char		*homedir;
1484	int		 ret;
1485
1486	io = netpgp->io;
1487	if (f == NULL) {
1488		(void) fprintf(io->errs, "No file containing packets\n");
1489		return 0;
1490	}
1491	if (stat(f, &st) < 0) {
1492		(void) fprintf(io->errs, "No such file '%s'\n", f);
1493		return 0;
1494	}
1495	homedir = netpgp_getvar(netpgp, "homedir");
1496	if (pubringname == NULL) {
1497		(void) snprintf(ringname, sizeof(ringname),
1498				"%s/pubring.gpg", homedir);
1499		pubringname = ringname;
1500	}
1501	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
1502		(void) fprintf(io->errs, "netpgp_list_packets: bad alloc\n");
1503		return 0;
1504	}
1505	if (!__ops_keyring_fileread(keyring, noarmor, pubringname)) {
1506		free(keyring);
1507		(void) fprintf(io->errs, "Cannot read pub keyring %s\n",
1508			pubringname);
1509		return 0;
1510	}
1511	netpgp->pubring = keyring;
1512	netpgp_setvar(netpgp, "pubring", pubringname);
1513	ret = __ops_list_packets(io, f, (unsigned)armor,
1514					netpgp->secring,
1515					netpgp->pubring,
1516					netpgp->passfp,
1517					get_passphrase_cb);
1518	free(keyring);
1519	return ret;
1520}
1521
1522/* set a variable */
1523int
1524netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value)
1525{
1526	char	*newval;
1527	int	 i;
1528
1529	/* protect against the case where 'value' is netpgp->value[i] */
1530	newval = netpgp_strdup(value);
1531	if ((i = findvar(netpgp, name)) < 0) {
1532		/* add the element to the array */
1533		if (size_arrays(netpgp, netpgp->size + 15)) {
1534			netpgp->name[i = netpgp->c++] = netpgp_strdup(name);
1535		}
1536	} else {
1537		/* replace the element in the array */
1538		if (netpgp->value[i]) {
1539			free(netpgp->value[i]);
1540			netpgp->value[i] = NULL;
1541		}
1542	}
1543	/* sanity checks for range of values */
1544	if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) {
1545		if (__ops_str_to_hash_alg(newval) == OPS_HASH_UNKNOWN) {
1546			free(newval);
1547			return 0;
1548		}
1549	}
1550	netpgp->value[i] = newval;
1551	return 1;
1552}
1553
1554/* unset a variable */
1555int
1556netpgp_unsetvar(netpgp_t *netpgp, const char *name)
1557{
1558	int	i;
1559
1560	if ((i = findvar(netpgp, name)) >= 0) {
1561		if (netpgp->value[i]) {
1562			free(netpgp->value[i]);
1563			netpgp->value[i] = NULL;
1564		}
1565		netpgp->value[i] = NULL;
1566		return 1;
1567	}
1568	return 0;
1569}
1570
1571/* get a variable's value (NULL if not set) */
1572char *
1573netpgp_getvar(netpgp_t *netpgp, const char *name)
1574{
1575	int	i;
1576
1577	return ((i = findvar(netpgp, name)) < 0) ? NULL : netpgp->value[i];
1578}
1579
1580/* increment a value */
1581int
1582netpgp_incvar(netpgp_t *netpgp, const char *name, const int delta)
1583{
1584	char	*cp;
1585	char	 num[16];
1586	int	 val;
1587
1588	val = 0;
1589	if ((cp = netpgp_getvar(netpgp, name)) != NULL) {
1590		val = atoi(cp);
1591	}
1592	(void) snprintf(num, sizeof(num), "%d", val + delta);
1593	netpgp_setvar(netpgp, name, num);
1594	return 1;
1595}
1596
1597/* set the home directory value to "home/subdir" */
1598int
1599netpgp_set_homedir(netpgp_t *netpgp, char *home, const char *subdir, const int quiet)
1600{
1601	struct stat	st;
1602	char		d[MAXPATHLEN];
1603
1604	if (home == NULL) {
1605		if (!quiet) {
1606			(void) fprintf(stderr, "NULL HOME directory\n");
1607		}
1608		return 0;
1609	}
1610	(void) snprintf(d, sizeof(d), "%s%s", home, (subdir) ? subdir : "");
1611	if (stat(d, &st) == 0) {
1612		if ((st.st_mode & S_IFMT) == S_IFDIR) {
1613			netpgp_setvar(netpgp, "homedir", d);
1614			return 1;
1615		}
1616		(void) fprintf(stderr, "netpgp: homedir \"%s\" is not a dir\n",
1617					d);
1618		return 0;
1619	}
1620	if (!quiet) {
1621		(void) fprintf(stderr,
1622			"netpgp: warning homedir \"%s\" not found\n", d);
1623	}
1624	netpgp_setvar(netpgp, "homedir", d);
1625	return 1;
1626}
1627
1628/* validate all sigs in the pub keyring */
1629int
1630netpgp_validate_sigs(netpgp_t *netpgp)
1631{
1632	__ops_validation_t	result;
1633
1634	return (int)__ops_validate_all_sigs(&result, netpgp->pubring, NULL);
1635}
1636
1637/* print the json out on 'fp' */
1638int
1639netpgp_format_json(void *vp, const char *json, const int psigs)
1640{
1641	mj_t	 ids;
1642	FILE	*fp;
1643	int	 from;
1644	int	 idc;
1645	int	 tok;
1646	int	 to;
1647	int	 i;
1648
1649	if ((fp = (FILE *)vp) == NULL || json == NULL) {
1650		return 0;
1651	}
1652	/* ids is an array of strings, each containing 1 entry */
1653	(void) memset(&ids, 0x0, sizeof(ids));
1654	from = to = tok = 0;
1655	/* convert from string into an mj structure */
1656	(void) mj_parse(&ids, json, &from, &to, &tok);
1657	if ((idc = mj_arraycount(&ids)) == 1 && strchr(json, '{') == NULL) {
1658		idc = 0;
1659	}
1660	(void) fprintf(fp, "%d key%s found\n", idc, (idc == 1) ? "" : "s");
1661	for (i = 0 ; i < idc ; i++) {
1662		format_json_key(fp, &ids.value.v[i], psigs);
1663	}
1664	/* clean up */
1665	mj_delete(&ids);
1666	return idc;
1667}
1668