xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/netpgp.c (revision f2af7cc18768e2ca30fbc8508a6ee20d421e72de)
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 */
88 static int
89 conffile(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 */
123 static char    *
124 userid_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 */
138 static void
139 resultp(__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 */
178 static int
179 size_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 */
215 static int
216 findvar(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 */
226 static void *
227 readkeyring(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 */
254 static int
255 readsshkeys(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 */
320 static void
321 set_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 */
336 static int
337 grabdate(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 */
360 static uint64_t
361 get_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 */
392 static int64_t
393 get_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 */
407 static const __ops_key_t *
408 resolve_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 */
426 static int
427 appendkey(__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 */
449 static unsigned
450 isarmoured(__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 */
473 static void
474 p(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 */
487 static void
488 pobj(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 */
537 static char *
538 ptimestr(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 */
551 static void
552 format_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 */
629 int
630 netpgp_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 */
752 int
753 netpgp_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 */
782 int
783 netpgp_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 */
793 int
794 netpgp_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 
813 DEFINE_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 */
820 int
821 netpgp_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 */
870 int
871 netpgp_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 */
912 int
913 netpgp_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 */
944 int
945 netpgp_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 */
958 char *
959 netpgp_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 */
980 char *
981 netpgp_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 */
996 int
997 netpgp_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 */
1014 int
1015 netpgp_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 */
1084 int
1085 netpgp_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 */
1119 int
1120 netpgp_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 */
1141 int
1142 netpgp_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 */
1221 int
1222 netpgp_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 */
1257 int
1258 netpgp_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 */
1330 int
1331 netpgp_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 */
1383 int
1384 netpgp_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 */
1424 int
1425 netpgp_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 */
1454 int
1455 netpgp_set_debug(const char *f)
1456 {
1457 	return __ops_set_debug_level(f);
1458 }
1459 
1460 /* get the debugging level per filename */
1461 int
1462 netpgp_get_debug(const char *f)
1463 {
1464 	return __ops_get_debug_level(f);
1465 }
1466 
1467 /* return the version for the library */
1468 const char *
1469 netpgp_get_info(const char *type)
1470 {
1471 	return __ops_get_info(type);
1472 }
1473 
1474 /* list all the packets in a file */
1475 int
1476 netpgp_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 */
1523 int
1524 netpgp_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 */
1555 int
1556 netpgp_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) */
1572 char *
1573 netpgp_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 */
1581 int
1582 netpgp_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" */
1598 int
1599 netpgp_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 */
1629 int
1630 netpgp_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' */
1638 int
1639 netpgp_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