xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/netpgp.c (revision 4817a0b0b8fe9612e8ebe21a9bf2d97b95038a97)
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.87 2010/12/01 22:14:52 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 "ssh2pgp.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 	__PGP_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(pgp_io_t *io,
140 	const char *f,
141 	pgp_validation_t *res,
142 	pgp_keyring_t *ring)
143 {
144 	const pgp_key_t	*key;
145 	pgp_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 			pgp_show_pka(res->valid_sigs[i].key_alg),
163 			userid_to_id(res->valid_sigs[i].signer_id, id));
164 		from = 0;
165 		key = pgp_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 		pgp_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 	pgp_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 (!pgp_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 	pgp_keyring_t	*pubring;
258 	pgp_keyring_t	*secring;
259 	struct stat	 st;
260 	unsigned	 hashtype;
261 	char		*hash;
262 	char		 f[MAXPATHLEN];
263 	char		*filename;
264 
265 	if ((filename = netpgp_getvar(netpgp, "sshkeyfile")) == NULL) {
266 		/* set reasonable default for RSA key */
267 		(void) snprintf(f, sizeof(f), "%s/id_rsa.pub", homedir);
268 		filename = f;
269 	} else if (strcmp(&filename[strlen(filename) - 4], ".pub") != 0) {
270 		/* got ssh keys, check for pub file name */
271 		(void) snprintf(f, sizeof(f), "%s.pub", filename);
272 		filename = f;
273 	}
274 	/* check the pub file exists */
275 	if (stat(filename, &st) != 0) {
276 		(void) fprintf(stderr, "readsshkeys: bad pubkey filename '%s'\n", filename);
277 		return 0;
278 	}
279 	if ((pubring = calloc(1, sizeof(*pubring))) == NULL) {
280 		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
281 		return 0;
282 	}
283 	/* openssh2 keys use md5 by default */
284 	hashtype = PGP_HASH_MD5;
285 	if ((hash = netpgp_getvar(netpgp, "hash")) != NULL) {
286 		/* openssh 2 hasn't really caught up to anything else yet */
287 		if (netpgp_strcasecmp(hash, "md5") == 0) {
288 			hashtype = PGP_HASH_MD5;
289 		} else if (netpgp_strcasecmp(hash, "sha1") == 0) {
290 			hashtype = PGP_HASH_SHA1;
291 		} else if (netpgp_strcasecmp(hash, "sha256") == 0) {
292 			hashtype = PGP_HASH_SHA256;
293 		}
294 	}
295 	if (!pgp_ssh2_readkeys(netpgp->io, pubring, NULL, filename, NULL, hashtype)) {
296 		free(pubring);
297 		(void) fprintf(stderr, "readsshkeys: can't read %s\n",
298 				filename);
299 		return 0;
300 	}
301 	if (netpgp->pubring == NULL) {
302 		netpgp->pubring = pubring;
303 	} else {
304 		pgp_append_keyring(netpgp->pubring, pubring);
305 	}
306 	if (needseckey) {
307 		netpgp_setvar(netpgp, "sshpubfile", filename);
308 		/* try to take the ".pub" off the end */
309 		if (filename == f) {
310 			f[strlen(f) - 4] = 0x0;
311 		} else {
312 			(void) snprintf(f, sizeof(f), "%.*s",
313 					(int)strlen(filename) - 4, filename);
314 			filename = f;
315 		}
316 		if ((secring = calloc(1, sizeof(*secring))) == NULL) {
317 			(void) fprintf(stderr, "readsshkeys: bad alloc\n");
318 			return 0;
319 		}
320 		if (!pgp_ssh2_readkeys(netpgp->io, pubring, secring, NULL, filename, hashtype)) {
321 			(void) fprintf(stderr, "readsshkeys: can't read sec %s\n", filename);
322 			return 0;
323 		}
324 		netpgp->secring = secring;
325 		netpgp_setvar(netpgp, "sshsecfile", filename);
326 	}
327 	return 1;
328 }
329 
330 /* set ssh uid to first one in pubring */
331 static void
332 set_first_pubring(pgp_keyring_t *pubring, char *id, size_t len, int last)
333 {
334 	uint8_t	*src;
335 	int	 i;
336 	int	 n;
337 
338 	(void) memset(id, 0x0, len);
339 	src = pubring->keys[(last) ? pubring->keyc - 1 : 0].sigid;
340 	for (i = 0, n = 0 ; i < PGP_KEY_ID_SIZE ; i += 2) {
341 		n += snprintf(&id[n], len - n, "%02x%02x", src[i], src[i + 1]);
342 	}
343 	id[n] = 0x0;
344 }
345 
346 /* find the time - in a specific %Y-%m-%d format - using a regexp */
347 static int
348 grabdate(char *s, int64_t *t)
349 {
350 	static regex_t	r;
351 	static int	compiled;
352 	regmatch_t	matches[10];
353 	struct tm	tm;
354 
355 	if (!compiled) {
356 		compiled = 1;
357 		(void) regcomp(&r, "([0-9][0-9][0-9][0-9])[-/]([0-9][0-9])[-/]([0-9][0-9])", REG_EXTENDED);
358 	}
359 	if (regexec(&r, s, 10, matches, 0) == 0) {
360 		(void) memset(&tm, 0x0, sizeof(tm));
361 		tm.tm_year = (int)strtol(&s[(int)matches[1].rm_so], NULL, 10);
362 		tm.tm_mon = (int)strtol(&s[(int)matches[2].rm_so], NULL, 10) - 1;
363 		tm.tm_mday = (int)strtol(&s[(int)matches[3].rm_so], NULL, 10);
364 		*t = mktime(&tm);
365 		return 1;
366 	}
367 	return 0;
368 }
369 
370 /* get expiration in seconds */
371 static uint64_t
372 get_duration(char *s)
373 {
374 	uint64_t	 now;
375 	int64_t	 	 t;
376 	char		*mult;
377 
378 	if (s == NULL) {
379 		return 0;
380 	}
381 	now = (uint64_t)strtoull(s, NULL, 10);
382 	if ((mult = strchr("hdwmy", s[strlen(s) - 1])) != NULL) {
383 		switch(*mult) {
384 		case 'h':
385 			return now * 60 * 60;
386 		case 'd':
387 			return now * 60 * 60 * 24;
388 		case 'w':
389 			return now * 60 * 60 * 24 * 7;
390 		case 'm':
391 			return now * 60 * 60 * 24 * 31;
392 		case 'y':
393 			return now * 60 * 60 * 24 * 365;
394 		}
395 	}
396 	if (grabdate(s, &t)) {
397 		return t;
398 	}
399 	return (uint64_t)strtoll(s, NULL, 10);
400 }
401 
402 /* get birthtime in seconds */
403 static int64_t
404 get_birthtime(char *s)
405 {
406 	int64_t	t;
407 
408 	if (s == NULL) {
409 		return time(NULL);
410 	}
411 	if (grabdate(s, &t)) {
412 		return t;
413 	}
414 	return (uint64_t)strtoll(s, NULL, 10);
415 }
416 
417 /* resolve the userid */
418 static const pgp_key_t *
419 resolve_userid(netpgp_t *netpgp, const pgp_keyring_t *keyring, const char *userid)
420 {
421 	const pgp_key_t	*key;
422 	pgp_io_t		*io;
423 
424 	if (userid == NULL) {
425 		userid = netpgp_getvar(netpgp, "userid");
426 		if (userid == NULL)
427 			return NULL;
428 	} else if (userid[0] == '0' && userid[1] == 'x') {
429 		userid += 2;
430 	}
431 	io = netpgp->io;
432 	if ((key = pgp_getkeybyname(io, keyring, userid)) == NULL) {
433 		(void) fprintf(io->errs, "Can't find key '%s'\n", userid);
434 	}
435 	return key;
436 }
437 
438 /* append a key to a keyring */
439 static int
440 appendkey(pgp_io_t *io, pgp_key_t *key, char *ringfile)
441 {
442 	pgp_output_t	*create;
443 	const unsigned	 noarmor = 0;
444 	int		 fd;
445 
446 	if ((fd = pgp_setup_file_append(&create, ringfile)) < 0) {
447 		fd = pgp_setup_file_write(&create, ringfile, 0);
448 	}
449 	if (fd < 0) {
450 		(void) fprintf(io->errs, "can't open pubring '%s'\n", ringfile);
451 		return 0;
452 	}
453 	if (!pgp_write_xfer_pubkey(create, key, noarmor)) {
454 		(void) fprintf(io->errs, "Cannot write pubkey\n");
455 		return 0;
456 	}
457 	pgp_teardown_file_write(create, fd);
458 	return 1;
459 }
460 
461 /* return 1 if the file contains ascii-armoured text */
462 static unsigned
463 isarmoured(pgp_io_t *io, const char *f, const void *memory, const char *text)
464 {
465 	regmatch_t	 matches[10];
466 	unsigned	 armoured;
467 	regex_t		 r;
468 	FILE		*fp;
469 	char	 	 buf[BUFSIZ];
470 
471 	armoured = 0;
472 	(void) regcomp(&r, text, REG_EXTENDED);
473 	if (f) {
474 		if ((fp = fopen(f, "r")) == NULL) {
475 			(void) fprintf(io->errs, "isarmoured: can't open '%s'\n", f);
476 			regfree(&r);
477 			return 0;
478 		}
479 		if (fgets(buf, (int)sizeof(buf), fp) != NULL) {
480 			if (regexec(&r, buf, 10, matches, 0) == 0) {
481 				armoured = 1;
482 			}
483 		}
484 		(void) fclose(fp);
485 	} else {
486 		if (regexec(&r, memory, 10, matches, 0) == 0) {
487 			armoured = 1;
488 		}
489 	}
490 	regfree(&r);
491 	return armoured;
492 }
493 
494 /* vararg print function */
495 static void
496 p(FILE *fp, const char *s, ...)
497 {
498 	va_list	args;
499 
500 	va_start(args, s);
501 	while (s != NULL) {
502 		(void) fprintf(fp, "%s", s);
503 		s = va_arg(args, char *);
504 	}
505 	va_end(args);
506 }
507 
508 /* print a JSON object to the FILE stream */
509 static void
510 pobj(FILE *fp, mj_t *obj, int depth)
511 {
512 	unsigned	i;
513 
514 	if (obj == NULL) {
515 		(void) fprintf(stderr, "No object found\n");
516 		return;
517 	}
518 	for (i = 0 ; i < (unsigned)depth ; i++) {
519 		p(fp, " ", NULL);
520 	}
521 	switch(obj->type) {
522 	case MJ_NULL:
523 	case MJ_FALSE:
524 	case MJ_TRUE:
525 		p(fp, (obj->type == MJ_NULL) ? "null" : (obj->type == MJ_FALSE) ? "false" : "true", NULL);
526 		break;
527 	case MJ_NUMBER:
528 		p(fp, obj->value.s, NULL);
529 		break;
530 	case MJ_STRING:
531 		(void) fprintf(fp, "%.*s", (int)(obj->c), obj->value.s);
532 		break;
533 	case MJ_ARRAY:
534 		for (i = 0 ; i < obj->c ; i++) {
535 			pobj(fp, &obj->value.v[i], depth + 1);
536 			if (i < obj->c - 1) {
537 				(void) fprintf(fp, ", ");
538 			}
539 		}
540 		(void) fprintf(fp, "\n");
541 		break;
542 	case MJ_OBJECT:
543 		for (i = 0 ; i < obj->c ; i += 2) {
544 			pobj(fp, &obj->value.v[i], depth + 1);
545 			p(fp, ": ", NULL);
546 			pobj(fp, &obj->value.v[i + 1], 0);
547 			if (i < obj->c - 1) {
548 				p(fp, ", ", NULL);
549 			}
550 		}
551 		p(fp, "\n", NULL);
552 		break;
553 	default:
554 		break;
555 	}
556 }
557 
558 /* return the time as a string */
559 static char *
560 ptimestr(char *dest, size_t size, time_t t)
561 {
562 	struct tm      *tm;
563 
564 	tm = gmtime(&t);
565 	(void) snprintf(dest, size, "%04d-%02d-%02d",
566 		tm->tm_year + 1900,
567 		tm->tm_mon + 1,
568 		tm->tm_mday);
569 	return dest;
570 }
571 
572 /* format a JSON object */
573 static void
574 format_json_key(FILE *fp, mj_t *obj, const int psigs)
575 {
576 	int64_t	 birthtime;
577 	int64_t	 duration;
578 	time_t	 now;
579 	char	 tbuf[32];
580 	char	*s;
581 	mj_t	*sub;
582 	int	 i;
583 
584 	if (pgp_get_debug_level(__FILE__)) {
585 		mj_asprint(&s, obj);
586 		(void) fprintf(stderr, "formatobj: json is '%s'\n", s);
587 		free(s);
588 	}
589 	if (obj->c == 2 && obj->value.v[1].type == MJ_STRING &&
590 	    strcmp(obj->value.v[1].value.s, "[REVOKED]") == 0) {
591 		/* whole key has been rovoked - just return */
592 		return;
593 	}
594 	pobj(fp, &obj->value.v[mj_object_find(obj, "header", 0, 2) + 1], 0);
595 	p(fp, " ", NULL);
596 	pobj(fp, &obj->value.v[mj_object_find(obj, "key bits", 0, 2) + 1], 0);
597 	p(fp, "/", NULL);
598 	pobj(fp, &obj->value.v[mj_object_find(obj, "pka", 0, 2) + 1], 0);
599 	p(fp, " ", NULL);
600 	pobj(fp, &obj->value.v[mj_object_find(obj, "key id", 0, 2) + 1], 0);
601 	birthtime = strtoll(obj->value.v[mj_object_find(obj, "birthtime", 0, 2) + 1].value.s, NULL, 10);
602 	p(fp, " ", ptimestr(tbuf, sizeof(tbuf), birthtime), NULL);
603 	duration = strtoll(obj->value.v[mj_object_find(obj, "duration", 0, 2) + 1].value.s, NULL, 10);
604 	if (duration > 0) {
605 		now = time(NULL);
606 		p(fp, " ", (birthtime + duration < now) ? "[EXPIRED " : "[EXPIRES ",
607 			ptimestr(tbuf, sizeof(tbuf), birthtime + duration), "]", NULL);
608 	}
609 	p(fp, "\n", "Key fingerprint: ", NULL);
610 	pobj(fp, &obj->value.v[mj_object_find(obj, "fingerprint", 0, 2) + 1], 0);
611 	p(fp, "\n", NULL);
612 	/* go to field after \"duration\" */
613 	for (i = mj_object_find(obj, "duration", 0, 2) + 2; i < mj_arraycount(obj) ; i += 2) {
614 		if (strcmp(obj->value.v[i].value.s, "uid") == 0) {
615 			sub = &obj->value.v[i + 1];
616 			p(fp, "uid", NULL);
617 			pobj(fp, &sub->value.v[0], (psigs) ? 4 : 14); /* human name */
618 			pobj(fp, &sub->value.v[1], 1); /* any revocation */
619 			p(fp, "\n", NULL);
620 		} else if (strcmp(obj->value.v[i].value.s, "encryption") == 0) {
621 			sub = &obj->value.v[i + 1];
622 			p(fp, "encryption", NULL);
623 			pobj(fp, &sub->value.v[0], 1);	/* size */
624 			p(fp, "/", NULL);
625 			pobj(fp, &sub->value.v[1], 0); /* alg */
626 			p(fp, " ", NULL);
627 			pobj(fp, &sub->value.v[2], 0); /* id */
628 			p(fp, " ", ptimestr(tbuf, sizeof(tbuf), strtoll(sub->value.v[3].value.s, NULL, 10)),
629 				"\n", NULL);
630 		} else if (strcmp(obj->value.v[i].value.s, "sig") == 0) {
631 			sub = &obj->value.v[i + 1];
632 			p(fp, "sig", NULL);
633 			pobj(fp, &sub->value.v[0], 8);	/* size */
634 			p(fp, "  ", ptimestr(tbuf, sizeof(tbuf), strtoll(sub->value.v[1].value.s, NULL, 10)),
635 				" ", NULL); /* time */
636 			pobj(fp, &sub->value.v[2], 0); /* human name */
637 			p(fp, "\n", NULL);
638 		} else {
639 			fprintf(stderr, "weird '%s'\n", obj->value.v[i].value.s);
640 			pobj(fp, &obj->value.v[i], 0); /* human name */
641 		}
642 	}
643 	p(fp, "\n", NULL);
644 }
645 
646 /* save a pgp pubkey to a temp file */
647 static int
648 savepubkey(char *res, char *f, size_t size)
649 {
650 	size_t	len;
651 	int	cc;
652 	int	wc;
653 	int	fd;
654 
655 	(void) snprintf(f, size, "/tmp/pgp2ssh.XXXXXXX");
656 	if ((fd = mkstemp(f)) < 0) {
657 		(void) fprintf(stderr, "can't create temp file '%s'\n", f);
658 		return 0;
659 	}
660 	len = strlen(res);
661 	for (cc = 0 ; (wc = write(fd, &res[cc], len - cc)) > 0 ; cc += wc) {
662 	}
663 	(void) close(fd);
664 	return 1;
665 }
666 
667 /* format a uint32_t */
668 static int
669 formatu32(uint8_t *buffer, uint32_t value)
670 {
671 	buffer[0] = (uint8_t)(value >> 24) & 0xff;
672 	buffer[1] = (uint8_t)(value >> 16) & 0xff;
673 	buffer[2] = (uint8_t)(value >> 8) & 0xff;
674 	buffer[3] = (uint8_t)value & 0xff;
675 	return sizeof(uint32_t);
676 }
677 
678 /* format a string as (len, string) */
679 static int
680 formatstring(char *buffer, const uint8_t *s, size_t len)
681 {
682 	int	cc;
683 
684 	cc = formatu32((uint8_t *)buffer, len);
685 	(void) memcpy(&buffer[cc], s, len);
686 	return cc + len;
687 }
688 
689 /* format a bignum, checking for "interesting" high bit values */
690 static int
691 formatbignum(char *buffer, BIGNUM *bn)
692 {
693 	size_t	 len;
694 	uint8_t	*cp;
695 	int	 cc;
696 
697 	len = (size_t) BN_num_bytes(bn);
698 	if ((cp = calloc(1, len + 1)) == NULL) {
699 		(void) fprintf(stderr, "calloc failure in formatbignum\n");
700 		return 0;
701 	}
702 	(void) BN_bn2bin(bn, cp + 1);
703 	cp[0] = 0x0;
704 	cc = (cp[1] & 0x80) ? formatstring(buffer, cp, len + 1) : formatstring(buffer, &cp[1], len);
705 	free(cp);
706 	return cc;
707 }
708 
709 #define MAX_PASSPHRASE_ATTEMPTS	3
710 #define INFINITE_ATTEMPTS	-1
711 
712 /* get the passphrase from the user */
713 static int
714 find_passphrase(FILE *passfp, const char *id, char *passphrase, size_t size, int attempts)
715 {
716 	char	 prompt[BUFSIZ];
717 	char	 buf[128];
718 	char	*cp;
719 	int	 cc;
720 	int	 i;
721 
722 	if (passfp) {
723 		if (fgets(passphrase, (int)size, passfp) == NULL) {
724 			return 0;
725 		}
726 		return strlen(passphrase);
727 	}
728 	for (i = 0 ; i < attempts ; i++) {
729 		(void) snprintf(prompt, sizeof(prompt), "Enter passphrase for %.16s: ", id);
730 		if ((cp = getpass(prompt)) == NULL) {
731 			break;
732 		}
733 		cc = snprintf(buf, sizeof(buf), "%s", cp);
734 		(void) snprintf(prompt, sizeof(prompt), "Repeat passphrase for %.16s: ", id);
735 		if ((cp = getpass(prompt)) == NULL) {
736 			break;
737 		}
738 		cc = snprintf(passphrase, size, "%s", cp);
739 		if (strcmp(buf, passphrase) == 0) {
740 			(void) memset(buf, 0x0, sizeof(buf));
741 			return cc;
742 		}
743 	}
744 	(void) memset(buf, 0x0, sizeof(buf));
745 	(void) memset(passphrase, 0x0, size);
746 	return 0;
747 }
748 
749 /***************************************************************************/
750 /* exported functions start here */
751 /***************************************************************************/
752 
753 /* initialise a netpgp_t structure */
754 int
755 netpgp_init(netpgp_t *netpgp)
756 {
757 	pgp_io_t	*io;
758 	time_t		 t;
759 	char		 id[MAX_ID_LENGTH];
760 	char		*homedir;
761 	char		*userid;
762 	char		*stream;
763 	char		*passfd;
764 	char		*results;
765 	int		 coredumps;
766 	int		 last;
767 
768 #ifdef HAVE_SYS_RESOURCE_H
769 	struct rlimit	limit;
770 
771 	coredumps = netpgp_getvar(netpgp, "coredumps") != NULL;
772 	if (!coredumps) {
773 		(void) memset(&limit, 0x0, sizeof(limit));
774 		if (setrlimit(RLIMIT_CORE, &limit) != 0) {
775 			(void) fprintf(stderr,
776 			"netpgp: warning - can't turn off core dumps\n");
777 			coredumps = 1;
778 		}
779 	}
780 #else
781 	coredumps = 1;
782 #endif
783 	if ((io = calloc(1, sizeof(*io))) == NULL) {
784 		(void) fprintf(stderr, "netpgp_init: bad alloc\n");
785 		return 0;
786 	}
787 	io->outs = stdout;
788 	if ((stream = netpgp_getvar(netpgp, "outs")) != NULL &&
789 	    strcmp(stream, "<stderr>") == 0) {
790 		io->outs = stderr;
791 	}
792 	io->errs = stderr;
793 	if ((stream = netpgp_getvar(netpgp, "errs")) != NULL &&
794 	    strcmp(stream, "<stdout>") == 0) {
795 		io->errs = stdout;
796 	}
797 	if ((results = netpgp_getvar(netpgp, "res")) == NULL) {
798 		io->res = io->errs;
799 	} else if (strcmp(results, "<stdout>") == 0) {
800 		io->res = stdout;
801 	} else if (strcmp(results, "<stderr>") == 0) {
802 		io->res = stderr;
803 	} else {
804 		if ((io->res = fopen(results, "w")) == NULL) {
805 			(void) fprintf(io->errs, "Can't open results %s for writing\n",
806 				results);
807 			free(io);
808 			return 0;
809 		}
810 	}
811 	netpgp->io = io;
812 	if ((passfd = netpgp_getvar(netpgp, "pass-fd")) != NULL &&
813 	    (netpgp->passfp = fdopen(atoi(passfd), "r")) == NULL) {
814 		(void) fprintf(io->errs, "Can't open fd %s for reading\n",
815 			passfd);
816 		return 0;
817 	}
818 	if (coredumps) {
819 		(void) fprintf(io->errs,
820 			"netpgp: warning: core dumps enabled\n");
821 	}
822 	if ((homedir = netpgp_getvar(netpgp, "homedir")) == NULL) {
823 		(void) fprintf(io->errs, "netpgp: bad homedir\n");
824 		return 0;
825 	}
826 	/* read from either gpg files or ssh keys */
827 	if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
828 		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
829 			(void) memset(id, 0x0, sizeof(id));
830 			(void) conffile(netpgp, homedir, id, sizeof(id));
831 			if (id[0] != 0x0) {
832 				netpgp_setvar(netpgp, "userid", userid = id);
833 			}
834 		}
835 		if (userid == NULL) {
836 			if (netpgp_getvar(netpgp, "need userid") != NULL) {
837 				(void) fprintf(io->errs,
838 						"Cannot find user id\n");
839 				return 0;
840 			}
841 		} else {
842 			(void) netpgp_setvar(netpgp, "userid", userid);
843 		}
844 		netpgp->pubring = readkeyring(netpgp, "pubring");
845 		if (netpgp->pubring == NULL) {
846 			(void) fprintf(io->errs, "Can't read pub keyring\n");
847 			return 0;
848 		}
849 		netpgp->secring = readkeyring(netpgp, "secring");
850 		if (netpgp->secring == NULL) {
851 			(void) fprintf(io->errs, "Can't read sec keyring\n");
852 			return 0;
853 		}
854 	} else {
855 		last = (netpgp->pubring != NULL);
856 		if (!readsshkeys(netpgp, homedir, netpgp_getvar(netpgp, "need seckey"))) {
857 			(void) fprintf(io->errs, "Can't read ssh keys\n");
858 			return 0;
859 		}
860 		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
861 			set_first_pubring(netpgp->pubring, id, sizeof(id), last);
862 			netpgp_setvar(netpgp, "userid", userid = id);
863 		}
864 		if (userid == NULL) {
865 			if (netpgp_getvar(netpgp, "need userid") != NULL) {
866 				(void) fprintf(io->errs,
867 						"Cannot find user id\n");
868 				return 0;
869 			}
870 		} else {
871 			(void) netpgp_setvar(netpgp, "userid", userid);
872 		}
873 	}
874 	t = time(NULL);
875 	netpgp_setvar(netpgp, "initialised", ctime(&t));
876 	return 1;
877 }
878 
879 /* finish off with the netpgp_t struct */
880 int
881 netpgp_end(netpgp_t *netpgp)
882 {
883 	unsigned	i;
884 
885 	for (i = 0 ; i < netpgp->c ; i++) {
886 		if (netpgp->name[i] != NULL) {
887 			free(netpgp->name[i]);
888 		}
889 		if (netpgp->value[i] != NULL) {
890 			free(netpgp->value[i]);
891 		}
892 	}
893 	if (netpgp->name != NULL) {
894 		free(netpgp->name);
895 	}
896 	if (netpgp->value != NULL) {
897 		free(netpgp->value);
898 	}
899 	if (netpgp->pubring != NULL) {
900 		pgp_keyring_free(netpgp->pubring);
901 	}
902 	if (netpgp->secring != NULL) {
903 		pgp_keyring_free(netpgp->secring);
904 	}
905 	free(netpgp->io);
906 	return 1;
907 }
908 
909 /* list the keys in a keyring */
910 int
911 netpgp_list_keys(netpgp_t *netpgp, const int psigs)
912 {
913 	if (netpgp->pubring == NULL) {
914 		(void) fprintf(stderr, "No keyring\n");
915 		return 0;
916 	}
917 	return pgp_keyring_list(netpgp->io, netpgp->pubring, psigs);
918 }
919 
920 /* list the keys in a keyring, returning a JSON string */
921 int
922 netpgp_list_keys_json(netpgp_t *netpgp, char **json, const int psigs)
923 {
924 	mj_t	obj;
925 	int	ret;
926 
927 	if (netpgp->pubring == NULL) {
928 		(void) fprintf(stderr, "No keyring\n");
929 		return 0;
930 	}
931 	(void) memset(&obj, 0x0, sizeof(obj));
932 	if (!pgp_keyring_json(netpgp->io, netpgp->pubring, &obj, psigs)) {
933 		(void) fprintf(stderr, "No keys in keyring\n");
934 		return 0;
935 	}
936 	ret = mj_asprint(json, &obj);
937 	mj_delete(&obj);
938 	return ret;
939 }
940 
941 DEFINE_ARRAY(strings_t, char *);
942 
943 #ifndef HKP_VERSION
944 #define HKP_VERSION	1
945 #endif
946 
947 /* find and list some keys in a keyring */
948 int
949 netpgp_match_keys(netpgp_t *netpgp, char *name, const char *fmt, void *vp, const int psigs)
950 {
951 	const pgp_key_t	*key;
952 	unsigned		 k;
953 	strings_t		 pubs;
954 	FILE			*fp = (FILE *)vp;
955 
956 	if (name[0] == '0' && name[1] == 'x') {
957 		name += 2;
958 	}
959 	(void) memset(&pubs, 0x0, sizeof(pubs));
960 	k = 0;
961 	do {
962 		key = pgp_getnextkeybyname(netpgp->io, netpgp->pubring,
963 						name, &k);
964 		if (key != NULL) {
965 			ALLOC(char *, pubs.v, pubs.size, pubs.c, 10, 10,
966 					"netpgp_match_keys", return 0);
967 			if (strcmp(fmt, "mr") == 0) {
968 				pgp_hkp_sprint_keydata(netpgp->io, netpgp->pubring,
969 						key, &pubs.v[pubs.c],
970 						&key->key.pubkey, psigs);
971 			} else {
972 				pgp_sprint_keydata(netpgp->io, netpgp->pubring,
973 						key, &pubs.v[pubs.c],
974 						"signature ",
975 						&key->key.pubkey, psigs);
976 			}
977 			if (pubs.v[pubs.c] != NULL) {
978 				pubs.c += 1;
979 			}
980 			k += 1;
981 		}
982 	} while (key != NULL);
983 	if (strcmp(fmt, "mr") == 0) {
984 		(void) fprintf(fp, "info:%d:%d\n", HKP_VERSION, pubs.c);
985 	} else {
986 		(void) fprintf(fp, "%d key%s found\n", pubs.c,
987 			(pubs.c == 1) ? "" : "s");
988 	}
989 	for (k = 0 ; k < pubs.c ; k++) {
990 		(void) fprintf(fp, "%s%s", pubs.v[k], (k < pubs.c - 1) ? "\n" : "");
991 		free(pubs.v[k]);
992 	}
993 	free(pubs.v);
994 	return pubs.c;
995 }
996 
997 /* find and list some keys in a keyring - return JSON string */
998 int
999 netpgp_match_keys_json(netpgp_t *netpgp, char **json, char *name, const char *fmt, const int psigs)
1000 {
1001 	const pgp_key_t	*key;
1002 	unsigned		 k;
1003 	mj_t			 id_array;
1004 	int			 ret;
1005 
1006 	if (name[0] == '0' && name[1] == 'x') {
1007 		name += 2;
1008 	}
1009 	(void) memset(&id_array, 0x0, sizeof(id_array));
1010 	k = 0;
1011 	*json = NULL;
1012 	mj_create(&id_array, "array");
1013 	do {
1014 		key = pgp_getnextkeybyname(netpgp->io, netpgp->pubring,
1015 						name, &k);
1016 		if (key != NULL) {
1017 			if (strcmp(fmt, "mr") == 0) {
1018 #if 0
1019 				pgp_hkp_sprint_keydata(netpgp->io, netpgp->pubring,
1020 						key, &pubs.v[pubs.c],
1021 						&key->key.pubkey, psigs);
1022 #endif
1023 			} else {
1024 				ALLOC(mj_t, id_array.value.v, id_array.size,
1025 					id_array.c, 10, 10, "netpgp_match_keys_json", return 0);
1026 				pgp_sprint_mj(netpgp->io, netpgp->pubring,
1027 						key, &id_array.value.v[id_array.c++],
1028 						"signature ",
1029 						&key->key.pubkey, psigs);
1030 			}
1031 			k += 1;
1032 		}
1033 	} while (key != NULL);
1034 	ret = mj_asprint(json, &id_array);
1035 	mj_delete(&id_array);
1036 	return ret;
1037 }
1038 
1039 /* find and list some public keys in a keyring */
1040 int
1041 netpgp_match_pubkeys(netpgp_t *netpgp, char *name, void *vp)
1042 {
1043 	const pgp_key_t	*key;
1044 	unsigned		 k;
1045 	strings_t		 pubs;
1046 	FILE			*fp = (FILE *)vp;
1047 
1048 	(void) memset(&pubs, 0x0, sizeof(pubs));
1049 	do {
1050 		key = pgp_getnextkeybyname(netpgp->io, netpgp->pubring,
1051 						name, &k);
1052 		if (key != NULL) {
1053 			char	out[1024 * 64];
1054 
1055 			ALLOC(char *, pubs.v, pubs.size, pubs.c, 10, 10,
1056 					"netpgp_match_pubkeys", return 0);
1057 			(void) pgp_sprint_pubkey(key, out, sizeof(out));
1058 			pubs.v[pubs.c++] = netpgp_strdup(out);
1059 			k += 1;
1060 		}
1061 	} while (key != NULL);
1062 	(void) fprintf(fp, "info:%d:%d\n", HKP_VERSION, pubs.c);
1063 	for (k = 0 ; k < pubs.c ; k++) {
1064 		(void) fprintf(fp, "%s", pubs.v[k]);
1065 		free(pubs.v[k]);
1066 	}
1067 	free(pubs.v);
1068 	return pubs.c;
1069 }
1070 
1071 /* find a key in a keyring */
1072 int
1073 netpgp_find_key(netpgp_t *netpgp, char *id)
1074 {
1075 	pgp_io_t	*io;
1076 
1077 	io = netpgp->io;
1078 	if (id == NULL) {
1079 		(void) fprintf(io->errs, "NULL id to search for\n");
1080 		return 0;
1081 	}
1082 	return pgp_getkeybyname(netpgp->io, netpgp->pubring, id) != NULL;
1083 }
1084 
1085 /* get a key in a keyring */
1086 char *
1087 netpgp_get_key(netpgp_t *netpgp, const char *name, const char *fmt)
1088 {
1089 	const pgp_key_t	*key;
1090 	char			*newkey;
1091 
1092 	if ((key = resolve_userid(netpgp, netpgp->pubring, name)) == NULL) {
1093 		return NULL;
1094 	}
1095 	if (strcmp(fmt, "mr") == 0) {
1096 		return (pgp_hkp_sprint_keydata(netpgp->io, netpgp->pubring,
1097 				key, &newkey,
1098 				&key->key.pubkey,
1099 				netpgp_getvar(netpgp, "subkey sigs") != NULL) > 0) ? newkey : NULL;
1100 	}
1101 	return (pgp_sprint_keydata(netpgp->io, netpgp->pubring,
1102 				key, &newkey, "signature",
1103 				&key->key.pubkey,
1104 				netpgp_getvar(netpgp, "subkey sigs") != NULL) > 0) ? newkey : NULL;
1105 }
1106 
1107 /* export a given key */
1108 char *
1109 netpgp_export_key(netpgp_t *netpgp, char *name)
1110 {
1111 	const pgp_key_t	*key;
1112 	pgp_io_t		*io;
1113 
1114 	io = netpgp->io;
1115 	if ((key = resolve_userid(netpgp, netpgp->pubring, name)) == NULL) {
1116 		return NULL;
1117 	}
1118 	return pgp_export_key(io, key, NULL);
1119 }
1120 
1121 #define IMPORT_ARMOR_HEAD	"-----BEGIN PGP PUBLIC KEY BLOCK-----"
1122 
1123 /* import a key into our keyring */
1124 int
1125 netpgp_import_key(netpgp_t *netpgp, char *f)
1126 {
1127 	pgp_io_t	*io;
1128 	unsigned	 realarmor;
1129 	int		 done;
1130 
1131 	io = netpgp->io;
1132 	realarmor = isarmoured(io, f, NULL, IMPORT_ARMOR_HEAD);
1133 	done = pgp_keyring_fileread(netpgp->pubring, realarmor, f);
1134 	if (!done) {
1135 		(void) fprintf(io->errs, "Cannot import key from file %s\n", f);
1136 		return 0;
1137 	}
1138 	return pgp_keyring_list(io, netpgp->pubring, 0);
1139 }
1140 
1141 #define ID_OFFSET	38
1142 
1143 /* generate a new key */
1144 int
1145 netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
1146 {
1147 	pgp_output_t		*create;
1148 	const unsigned		 noarmor = 0;
1149 	pgp_key_t		*key;
1150 	pgp_io_t		*io;
1151 	uint8_t			*uid;
1152 	char			 passphrase[128];
1153 	char			 newid[1024];
1154 	char			 filename[MAXPATHLEN];
1155 	char			 dir[MAXPATHLEN];
1156 	char			*cp;
1157 	char			*ringfile;
1158 	char			*numtries;
1159 	int             	 attempts;
1160 	int             	 passc;
1161 	int             	 fd;
1162 	int             	 cc;
1163 
1164 	uid = NULL;
1165 	io = netpgp->io;
1166 	/* generate a new key */
1167 	if (id) {
1168 		(void) snprintf(newid, sizeof(newid), "%s", id);
1169 	} else {
1170 		(void) snprintf(newid, sizeof(newid),
1171 			"RSA %d-bit key <%s@localhost>", numbits, getenv("LOGNAME"));
1172 	}
1173 	uid = (uint8_t *)newid;
1174 	key = pgp_rsa_new_selfsign_key(numbits, 65537UL, uid,
1175 			netpgp_getvar(netpgp, "hash"),
1176 			netpgp_getvar(netpgp, "cipher"));
1177 	if (key == NULL) {
1178 		(void) fprintf(io->errs, "Cannot generate key\n");
1179 		return 0;
1180 	}
1181 	cp = NULL;
1182 	pgp_sprint_keydata(netpgp->io, NULL, key, &cp, "signature ", &key->key.seckey.pubkey, 0);
1183 	(void) fprintf(stdout, "%s", cp);
1184 	/* write public key */
1185 	cc = snprintf(dir, sizeof(dir), "%s/%.16s", netpgp_getvar(netpgp, "homedir"), &cp[ID_OFFSET]);
1186 	netpgp_setvar(netpgp, "generated userid", &dir[cc - 16]);
1187 	if (mkdir(dir, 0700) < 0) {
1188 		(void) fprintf(io->errs, "can't mkdir '%s'\n", dir);
1189 		return 0;
1190 	}
1191 	(void) fprintf(io->errs, "netpgp: generated keys in directory %s\n", dir);
1192 	(void) snprintf(ringfile = filename, sizeof(filename), "%s/pubring.gpg", dir);
1193 	if (!appendkey(io, key, ringfile)) {
1194 		(void) fprintf(io->errs, "Cannot write pubkey to '%s'\n", ringfile);
1195 		return 0;
1196 	}
1197 	if (netpgp->pubring != NULL) {
1198 		pgp_keyring_free(netpgp->pubring);
1199 	}
1200 	/* write secret key */
1201 	(void) snprintf(ringfile = filename, sizeof(filename), "%s/secring.gpg", dir);
1202 	if ((fd = pgp_setup_file_append(&create, ringfile)) < 0) {
1203 		fd = pgp_setup_file_write(&create, ringfile, 0);
1204 	}
1205 	if (fd < 0) {
1206 		(void) fprintf(io->errs, "can't append secring '%s'\n", ringfile);
1207 		return 0;
1208 	}
1209 	/* get the passphrase */
1210 	if ((numtries = netpgp_getvar(netpgp, "numtries")) == NULL ||
1211 	    (attempts = atoi(numtries)) <= 0) {
1212 		attempts = MAX_PASSPHRASE_ATTEMPTS;
1213 	} else if (strcmp(numtries, "unlimited") == 0) {
1214 		attempts = INFINITE_ATTEMPTS;
1215 	}
1216 	passc = find_passphrase(netpgp->passfp, &cp[ID_OFFSET], passphrase, sizeof(passphrase), attempts);
1217 	if (!pgp_write_xfer_seckey(create, key, (uint8_t *)passphrase, (const unsigned)passc, noarmor)) {
1218 		(void) fprintf(io->errs, "Cannot write seckey\n");
1219 		return 0;
1220 	}
1221 	pgp_teardown_file_write(create, fd);
1222 	if (netpgp->secring != NULL) {
1223 		pgp_keyring_free(netpgp->secring);
1224 	}
1225 	pgp_keydata_free(key);
1226 	free(cp);
1227 	return 1;
1228 }
1229 
1230 /* encrypt a file */
1231 int
1232 netpgp_encrypt_file(netpgp_t *netpgp,
1233 			const char *userid,
1234 			const char *f,
1235 			char *out,
1236 			int armored)
1237 {
1238 	const pgp_key_t	*key;
1239 	const unsigned		 overwrite = 1;
1240 	const char		*suffix;
1241 	pgp_io_t		*io;
1242 	char			 outname[MAXPATHLEN];
1243 
1244 	io = netpgp->io;
1245 	if (f == NULL) {
1246 		(void) fprintf(io->errs,
1247 			"netpgp_encrypt_file: no filename specified\n");
1248 		return 0;
1249 	}
1250 	suffix = (armored) ? ".asc" : ".gpg";
1251 	/* get key with which to sign */
1252 	if ((key = resolve_userid(netpgp, netpgp->pubring, userid)) == NULL) {
1253 		return 0;
1254 	}
1255 	if (out == NULL) {
1256 		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
1257 		out = outname;
1258 	}
1259 	return (int)pgp_encrypt_file(io, f, out, key, (unsigned)armored,
1260 				overwrite, netpgp_getvar(netpgp, "cipher"));
1261 }
1262 
1263 #define ARMOR_HEAD	"-----BEGIN PGP MESSAGE-----"
1264 
1265 /* decrypt a file */
1266 int
1267 netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored)
1268 {
1269 	const unsigned	 overwrite = 1;
1270 	pgp_io_t	*io;
1271 	unsigned	 realarmor;
1272 	unsigned	 sshkeys;
1273 	char		*numtries;
1274 	int            	 attempts;
1275 
1276 	__PGP_USED(armored);
1277 	io = netpgp->io;
1278 	if (f == NULL) {
1279 		(void) fprintf(io->errs,
1280 			"netpgp_decrypt_file: no filename specified\n");
1281 		return 0;
1282 	}
1283 	realarmor = isarmoured(io, f, NULL, ARMOR_HEAD);
1284 	sshkeys = (unsigned)(netpgp_getvar(netpgp, "ssh keys") != NULL);
1285 	if ((numtries = netpgp_getvar(netpgp, "numtries")) == NULL ||
1286 	    (attempts = atoi(numtries)) <= 0) {
1287 		attempts = MAX_PASSPHRASE_ATTEMPTS;
1288 	} else if (strcmp(numtries, "unlimited") == 0) {
1289 		attempts = INFINITE_ATTEMPTS;
1290 	}
1291 	return pgp_decrypt_file(netpgp->io, f, out, netpgp->secring,
1292 				netpgp->pubring,
1293 				realarmor, overwrite, sshkeys,
1294 				netpgp->passfp, attempts, get_passphrase_cb);
1295 }
1296 
1297 /* sign a file */
1298 int
1299 netpgp_sign_file(netpgp_t *netpgp,
1300 		const char *userid,
1301 		const char *f,
1302 		char *out,
1303 		int armored,
1304 		int cleartext,
1305 		int detached)
1306 {
1307 	const pgp_key_t		*keypair;
1308 	const pgp_key_t		*pubkey;
1309 	const unsigned		 overwrite = 1;
1310 	pgp_seckey_t		*seckey;
1311 	const char		*hashalg;
1312 	pgp_io_t		*io;
1313 	char			*numtries;
1314 	int			 attempts;
1315 	int			 ret;
1316 	int			 i;
1317 
1318 	io = netpgp->io;
1319 	if (f == NULL) {
1320 		(void) fprintf(io->errs,
1321 			"netpgp_sign_file: no filename specified\n");
1322 		return 0;
1323 	}
1324 	/* get key with which to sign */
1325 	if ((keypair = resolve_userid(netpgp, netpgp->secring, userid)) == NULL) {
1326 		return 0;
1327 	}
1328 	ret = 1;
1329 	if ((numtries = netpgp_getvar(netpgp, "numtries")) == NULL ||
1330 	    (attempts = atoi(numtries)) <= 0) {
1331 		attempts = MAX_PASSPHRASE_ATTEMPTS;
1332 	} else if (strcmp(numtries, "unlimited") == 0) {
1333 		attempts = INFINITE_ATTEMPTS;
1334 	}
1335 	for (i = 0, seckey = NULL ; !seckey && (i < attempts || attempts == INFINITE_ATTEMPTS) ; i++) {
1336 		if (netpgp->passfp == NULL) {
1337 			/* print out the user id */
1338 			pubkey = pgp_getkeybyname(io, netpgp->pubring, userid);
1339 			if (pubkey == NULL) {
1340 				(void) fprintf(io->errs,
1341 					"netpgp: warning - using pubkey from secring\n");
1342 				pgp_print_keydata(io, netpgp->pubring, keypair, "signature ",
1343 					&keypair->key.seckey.pubkey, 0);
1344 			} else {
1345 				pgp_print_keydata(io, netpgp->pubring, pubkey, "signature ",
1346 					&pubkey->key.pubkey, 0);
1347 			}
1348 		}
1349 		if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
1350 			/* now decrypt key */
1351 			seckey = pgp_decrypt_seckey(keypair, netpgp->passfp);
1352 			if (seckey == NULL) {
1353 				(void) fprintf(io->errs, "Bad passphrase\n");
1354 			}
1355 		} else {
1356 			pgp_keyring_t	*secring;
1357 
1358 			secring = netpgp->secring;
1359 			seckey = &secring->keys[0].key.seckey;
1360 		}
1361 	}
1362 	if (seckey == NULL) {
1363 		(void) fprintf(io->errs, "Bad passphrase\n");
1364 		return 0;
1365 	}
1366 	/* sign file */
1367 	hashalg = netpgp_getvar(netpgp, "hash");
1368 	if (seckey->pubkey.alg == PGP_PKA_DSA) {
1369 		hashalg = "sha1";
1370 	}
1371 	if (detached) {
1372 		ret = pgp_sign_detached(io, f, out, seckey, hashalg,
1373 				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
1374 				get_duration(netpgp_getvar(netpgp, "duration")),
1375 				(unsigned)armored,
1376 				overwrite);
1377 	} else {
1378 		ret = pgp_sign_file(io, f, out, seckey, hashalg,
1379 				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
1380 				get_duration(netpgp_getvar(netpgp, "duration")),
1381 				(unsigned)armored, (unsigned)cleartext,
1382 				overwrite);
1383 	}
1384 	pgp_forget(seckey, (unsigned)sizeof(*seckey));
1385 	return ret;
1386 }
1387 
1388 #define ARMOR_SIG_HEAD	"-----BEGIN PGP (SIGNATURE|SIGNED MESSAGE)-----"
1389 
1390 /* verify a file */
1391 int
1392 netpgp_verify_file(netpgp_t *netpgp, const char *in, const char *out, int armored)
1393 {
1394 	pgp_validation_t	 result;
1395 	pgp_io_t		*io;
1396 	unsigned		 realarmor;
1397 
1398 	__PGP_USED(armored);
1399 	(void) memset(&result, 0x0, sizeof(result));
1400 	io = netpgp->io;
1401 	if (in == NULL) {
1402 		(void) fprintf(io->errs,
1403 			"netpgp_verify_file: no filename specified\n");
1404 		return 0;
1405 	}
1406 	realarmor = isarmoured(io, in, NULL, ARMOR_SIG_HEAD);
1407 	if (pgp_validate_file(io, &result, in, out, (const int)realarmor, netpgp->pubring)) {
1408 		resultp(io, in, &result, netpgp->pubring);
1409 		return 1;
1410 	}
1411 	if (result.validc + result.invalidc + result.unknownc == 0) {
1412 		(void) fprintf(io->errs,
1413 		"\"%s\": No signatures found - is this a signed file?\n",
1414 			in);
1415 	} else if (result.invalidc == 0 && result.unknownc == 0) {
1416 		(void) fprintf(io->errs,
1417 			"\"%s\": file verification failure: invalid signature time\n", in);
1418 	} else {
1419 		(void) fprintf(io->errs,
1420 "\"%s\": verification failure: %u invalid signatures, %u unknown signatures\n",
1421 			in, result.invalidc, result.unknownc);
1422 	}
1423 	return 0;
1424 }
1425 
1426 /* sign some memory */
1427 int
1428 netpgp_sign_memory(netpgp_t *netpgp,
1429 		const char *userid,
1430 		char *mem,
1431 		size_t size,
1432 		char *out,
1433 		size_t outsize,
1434 		const unsigned armored,
1435 		const unsigned cleartext)
1436 {
1437 	const pgp_key_t		*keypair;
1438 	const pgp_key_t		*pubkey;
1439 	pgp_seckey_t		*seckey;
1440 	pgp_memory_t		*signedmem;
1441 	const char		*hashalg;
1442 	pgp_io_t		*io;
1443 	char 			*numtries;
1444 	int			 attempts;
1445 	int			 ret;
1446 	int			 i;
1447 
1448 	io = netpgp->io;
1449 	if (mem == NULL) {
1450 		(void) fprintf(io->errs,
1451 			"netpgp_sign_memory: no memory to sign\n");
1452 		return 0;
1453 	}
1454 	if ((keypair = resolve_userid(netpgp, netpgp->secring, userid)) == NULL) {
1455 		return 0;
1456 	}
1457 	ret = 1;
1458 	if ((numtries = netpgp_getvar(netpgp, "numtries")) == NULL ||
1459 	    (attempts = atoi(numtries)) <= 0) {
1460 		attempts = MAX_PASSPHRASE_ATTEMPTS;
1461 	} else if (strcmp(numtries, "unlimited") == 0) {
1462 		attempts = INFINITE_ATTEMPTS;
1463 	}
1464 	for (i = 0, seckey = NULL ; !seckey && (i < attempts || attempts == INFINITE_ATTEMPTS) ; i++) {
1465 		if (netpgp->passfp == NULL) {
1466 			/* print out the user id */
1467 			pubkey = pgp_getkeybyname(io, netpgp->pubring, userid);
1468 			if (pubkey == NULL) {
1469 				(void) fprintf(io->errs,
1470 					"netpgp: warning - using pubkey from secring\n");
1471 				pgp_print_keydata(io, netpgp->pubring, keypair, "signature ",
1472 					&keypair->key.seckey.pubkey, 0);
1473 			} else {
1474 				pgp_print_keydata(io, netpgp->pubring, pubkey, "signature ",
1475 					&pubkey->key.pubkey, 0);
1476 			}
1477 		}
1478 		/* now decrypt key */
1479 		seckey = pgp_decrypt_seckey(keypair, netpgp->passfp);
1480 		if (seckey == NULL) {
1481 			(void) fprintf(io->errs, "Bad passphrase\n");
1482 		}
1483 	}
1484 	if (seckey == NULL) {
1485 		(void) fprintf(io->errs, "Bad passphrase\n");
1486 		return 0;
1487 	}
1488 	/* sign file */
1489 	(void) memset(out, 0x0, outsize);
1490 	hashalg = netpgp_getvar(netpgp, "hash");
1491 	if (seckey->pubkey.alg == PGP_PKA_DSA) {
1492 		hashalg = "sha1";
1493 	}
1494 	signedmem = pgp_sign_buf(io, mem, size, seckey,
1495 				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
1496 				get_duration(netpgp_getvar(netpgp, "duration")),
1497 				hashalg, armored, cleartext);
1498 	if (signedmem) {
1499 		size_t	m;
1500 
1501 		m = MIN(pgp_mem_len(signedmem), outsize);
1502 		(void) memcpy(out, pgp_mem_data(signedmem), m);
1503 		pgp_memory_free(signedmem);
1504 		ret = (int)m;
1505 	} else {
1506 		ret = 0;
1507 	}
1508 	pgp_forget(seckey, (unsigned)sizeof(*seckey));
1509 	return ret;
1510 }
1511 
1512 /* verify memory */
1513 int
1514 netpgp_verify_memory(netpgp_t *netpgp, const void *in, const size_t size,
1515 			void *out, size_t outsize, const int armored)
1516 {
1517 	pgp_validation_t	 result;
1518 	pgp_memory_t		*signedmem;
1519 	pgp_memory_t		*cat;
1520 	pgp_io_t		*io;
1521 	size_t			 m;
1522 	int			 ret;
1523 
1524 	(void) memset(&result, 0x0, sizeof(result));
1525 	io = netpgp->io;
1526 	if (in == NULL) {
1527 		(void) fprintf(io->errs,
1528 			"netpgp_verify_memory: no memory to verify\n");
1529 		return 0;
1530 	}
1531 	signedmem = pgp_memory_new();
1532 	pgp_memory_add(signedmem, in, size);
1533 	if (out) {
1534 		cat = pgp_memory_new();
1535 	}
1536 	ret = pgp_validate_mem(io, &result, signedmem,
1537 				(out) ? &cat : NULL,
1538 				armored, netpgp->pubring);
1539 	pgp_memory_free(signedmem);
1540 	if (ret) {
1541 		resultp(io, "<stdin>", &result, netpgp->pubring);
1542 		if (out) {
1543 			m = MIN(pgp_mem_len(cat), outsize);
1544 			(void) memcpy(out, pgp_mem_data(cat), m);
1545 			pgp_memory_free(cat);
1546 		} else {
1547 			m = 1;
1548 		}
1549 		return (int)m;
1550 	}
1551 	if (result.validc + result.invalidc + result.unknownc == 0) {
1552 		(void) fprintf(io->errs,
1553 		"No signatures found - is this memory signed?\n");
1554 	} else if (result.invalidc == 0 && result.unknownc == 0) {
1555 		(void) fprintf(io->errs,
1556 			"memory verification failure: invalid signature time\n");
1557 	} else {
1558 		(void) fprintf(io->errs,
1559 "memory verification failure: %u invalid signatures, %u unknown signatures\n",
1560 			result.invalidc, result.unknownc);
1561 	}
1562 	return 0;
1563 }
1564 
1565 /* encrypt some memory */
1566 int
1567 netpgp_encrypt_memory(netpgp_t *netpgp,
1568 			const char *userid,
1569 			void *in,
1570 			const size_t insize,
1571 			char *out,
1572 			size_t outsize,
1573 			int armored)
1574 {
1575 	const pgp_key_t	*keypair;
1576 	pgp_memory_t		*enc;
1577 	pgp_io_t		*io;
1578 	size_t			 m;
1579 
1580 	io = netpgp->io;
1581 	if (in == NULL) {
1582 		(void) fprintf(io->errs,
1583 			"netpgp_encrypt_buf: no memory to encrypt\n");
1584 		return 0;
1585 	}
1586 	if ((keypair = resolve_userid(netpgp, netpgp->pubring, userid)) == NULL) {
1587 		return 0;
1588 	}
1589 	if (in == out) {
1590 		(void) fprintf(io->errs,
1591 			"netpgp_encrypt_buf: input and output bufs need to be different\n");
1592 		return 0;
1593 	}
1594 	if (outsize < insize) {
1595 		(void) fprintf(io->errs,
1596 			"netpgp_encrypt_buf: input size is larger than output size\n");
1597 		return 0;
1598 	}
1599 	enc = pgp_encrypt_buf(io, in, insize, keypair, (unsigned)armored,
1600 				netpgp_getvar(netpgp, "cipher"));
1601 	m = MIN(pgp_mem_len(enc), outsize);
1602 	(void) memcpy(out, pgp_mem_data(enc), m);
1603 	pgp_memory_free(enc);
1604 	return (int)m;
1605 }
1606 
1607 /* decrypt a chunk of memory */
1608 int
1609 netpgp_decrypt_memory(netpgp_t *netpgp, const void *input, const size_t insize,
1610 			char *out, size_t outsize, const int armored)
1611 {
1612 	pgp_memory_t	*mem;
1613 	pgp_io_t	*io;
1614 	unsigned	 realarmour;
1615 	unsigned	 sshkeys;
1616 	size_t		 m;
1617 	char		*numtries;
1618 	int            	 attempts;
1619 
1620 	__PGP_USED(armored);
1621 	io = netpgp->io;
1622 	if (input == NULL) {
1623 		(void) fprintf(io->errs,
1624 			"netpgp_decrypt_memory: no memory\n");
1625 		return 0;
1626 	}
1627 	realarmour = isarmoured(io, NULL, input, ARMOR_HEAD);
1628 	sshkeys = (unsigned)(netpgp_getvar(netpgp, "ssh keys") != NULL);
1629 	if ((numtries = netpgp_getvar(netpgp, "numtries")) == NULL ||
1630 	    (attempts = atoi(numtries)) <= 0) {
1631 		attempts = MAX_PASSPHRASE_ATTEMPTS;
1632 	} else if (strcmp(numtries, "unlimited") == 0) {
1633 		attempts = INFINITE_ATTEMPTS;
1634 	}
1635 	mem = pgp_decrypt_buf(netpgp->io, input, insize, netpgp->secring,
1636 				netpgp->pubring,
1637 				realarmour, sshkeys,
1638 				netpgp->passfp,
1639 				attempts,
1640 				get_passphrase_cb);
1641 	if (mem == NULL) {
1642 		return -1;
1643 	}
1644 	m = MIN(pgp_mem_len(mem), outsize);
1645 	(void) memcpy(out, pgp_mem_data(mem), m);
1646 	pgp_memory_free(mem);
1647 	return (int)m;
1648 }
1649 
1650 /* wrappers for the ops_debug_level functions we added to openpgpsdk */
1651 
1652 /* set the debugging level per filename */
1653 int
1654 netpgp_set_debug(const char *f)
1655 {
1656 	return pgp_set_debug_level(f);
1657 }
1658 
1659 /* get the debugging level per filename */
1660 int
1661 netpgp_get_debug(const char *f)
1662 {
1663 	return pgp_get_debug_level(f);
1664 }
1665 
1666 /* return the version for the library */
1667 const char *
1668 netpgp_get_info(const char *type)
1669 {
1670 	return pgp_get_info(type);
1671 }
1672 
1673 /* list all the packets in a file */
1674 int
1675 netpgp_list_packets(netpgp_t *netpgp, char *f, int armor, char *pubringname)
1676 {
1677 	pgp_keyring_t	*keyring;
1678 	const unsigned	 noarmor = 0;
1679 	struct stat	 st;
1680 	pgp_io_t	*io;
1681 	char		 ringname[MAXPATHLEN];
1682 	char		*homedir;
1683 	int		 ret;
1684 
1685 	io = netpgp->io;
1686 	if (f == NULL) {
1687 		(void) fprintf(io->errs, "No file containing packets\n");
1688 		return 0;
1689 	}
1690 	if (stat(f, &st) < 0) {
1691 		(void) fprintf(io->errs, "No such file '%s'\n", f);
1692 		return 0;
1693 	}
1694 	homedir = netpgp_getvar(netpgp, "homedir");
1695 	if (pubringname == NULL) {
1696 		(void) snprintf(ringname, sizeof(ringname),
1697 				"%s/pubring.gpg", homedir);
1698 		pubringname = ringname;
1699 	}
1700 	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
1701 		(void) fprintf(io->errs, "netpgp_list_packets: bad alloc\n");
1702 		return 0;
1703 	}
1704 	if (!pgp_keyring_fileread(keyring, noarmor, pubringname)) {
1705 		free(keyring);
1706 		(void) fprintf(io->errs, "Cannot read pub keyring %s\n",
1707 			pubringname);
1708 		return 0;
1709 	}
1710 	netpgp->pubring = keyring;
1711 	netpgp_setvar(netpgp, "pubring", pubringname);
1712 	ret = pgp_list_packets(io, f, (unsigned)armor,
1713 					netpgp->secring,
1714 					netpgp->pubring,
1715 					netpgp->passfp,
1716 					get_passphrase_cb);
1717 	free(keyring);
1718 	return ret;
1719 }
1720 
1721 /* set a variable */
1722 int
1723 netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value)
1724 {
1725 	char	*newval;
1726 	int	 i;
1727 
1728 	/* protect against the case where 'value' is netpgp->value[i] */
1729 	newval = netpgp_strdup(value);
1730 	if ((i = findvar(netpgp, name)) < 0) {
1731 		/* add the element to the array */
1732 		if (size_arrays(netpgp, netpgp->size + 15)) {
1733 			netpgp->name[i = netpgp->c++] = netpgp_strdup(name);
1734 		}
1735 	} else {
1736 		/* replace the element in the array */
1737 		if (netpgp->value[i]) {
1738 			free(netpgp->value[i]);
1739 			netpgp->value[i] = NULL;
1740 		}
1741 	}
1742 	/* sanity checks for range of values */
1743 	if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) {
1744 		if (pgp_str_to_hash_alg(newval) == PGP_HASH_UNKNOWN) {
1745 			free(newval);
1746 			return 0;
1747 		}
1748 	}
1749 	netpgp->value[i] = newval;
1750 	return 1;
1751 }
1752 
1753 /* unset a variable */
1754 int
1755 netpgp_unsetvar(netpgp_t *netpgp, const char *name)
1756 {
1757 	int	i;
1758 
1759 	if ((i = findvar(netpgp, name)) >= 0) {
1760 		if (netpgp->value[i]) {
1761 			free(netpgp->value[i]);
1762 			netpgp->value[i] = NULL;
1763 		}
1764 		netpgp->value[i] = NULL;
1765 		return 1;
1766 	}
1767 	return 0;
1768 }
1769 
1770 /* get a variable's value (NULL if not set) */
1771 char *
1772 netpgp_getvar(netpgp_t *netpgp, const char *name)
1773 {
1774 	int	i;
1775 
1776 	return ((i = findvar(netpgp, name)) < 0) ? NULL : netpgp->value[i];
1777 }
1778 
1779 /* increment a value */
1780 int
1781 netpgp_incvar(netpgp_t *netpgp, const char *name, const int delta)
1782 {
1783 	char	*cp;
1784 	char	 num[16];
1785 	int	 val;
1786 
1787 	val = 0;
1788 	if ((cp = netpgp_getvar(netpgp, name)) != NULL) {
1789 		val = atoi(cp);
1790 	}
1791 	(void) snprintf(num, sizeof(num), "%d", val + delta);
1792 	netpgp_setvar(netpgp, name, num);
1793 	return 1;
1794 }
1795 
1796 /* set the home directory value to "home/subdir" */
1797 int
1798 netpgp_set_homedir(netpgp_t *netpgp, char *home, const char *subdir, const int quiet)
1799 {
1800 	struct stat	st;
1801 	char		d[MAXPATHLEN];
1802 
1803 	if (home == NULL) {
1804 		if (!quiet) {
1805 			(void) fprintf(stderr, "NULL HOME directory\n");
1806 		}
1807 		return 0;
1808 	}
1809 	(void) snprintf(d, sizeof(d), "%s%s", home, (subdir) ? subdir : "");
1810 	if (stat(d, &st) == 0) {
1811 		if ((st.st_mode & S_IFMT) == S_IFDIR) {
1812 			netpgp_setvar(netpgp, "homedir", d);
1813 			return 1;
1814 		}
1815 		(void) fprintf(stderr, "netpgp: homedir \"%s\" is not a dir\n",
1816 					d);
1817 		return 0;
1818 	}
1819 	if (!quiet) {
1820 		(void) fprintf(stderr,
1821 			"netpgp: warning homedir \"%s\" not found\n", d);
1822 	}
1823 	netpgp_setvar(netpgp, "homedir", d);
1824 	return 1;
1825 }
1826 
1827 /* validate all sigs in the pub keyring */
1828 int
1829 netpgp_validate_sigs(netpgp_t *netpgp)
1830 {
1831 	pgp_validation_t	result;
1832 
1833 	return (int)pgp_validate_all_sigs(&result, netpgp->pubring, NULL);
1834 }
1835 
1836 /* print the json out on 'fp' */
1837 int
1838 netpgp_format_json(void *vp, const char *json, const int psigs)
1839 {
1840 	mj_t	 ids;
1841 	FILE	*fp;
1842 	int	 from;
1843 	int	 idc;
1844 	int	 tok;
1845 	int	 to;
1846 	int	 i;
1847 
1848 	if ((fp = (FILE *)vp) == NULL || json == NULL) {
1849 		return 0;
1850 	}
1851 	/* ids is an array of strings, each containing 1 entry */
1852 	(void) memset(&ids, 0x0, sizeof(ids));
1853 	from = to = tok = 0;
1854 	/* convert from string into an mj structure */
1855 	(void) mj_parse(&ids, json, &from, &to, &tok);
1856 	if ((idc = mj_arraycount(&ids)) == 1 && strchr(json, '{') == NULL) {
1857 		idc = 0;
1858 	}
1859 	(void) fprintf(fp, "%d key%s found\n", idc, (idc == 1) ? "" : "s");
1860 	for (i = 0 ; i < idc ; i++) {
1861 		format_json_key(fp, &ids.value.v[i], psigs);
1862 	}
1863 	/* clean up */
1864 	mj_delete(&ids);
1865 	return idc;
1866 }
1867 
1868 /* find a key in keyring, and write it in ssh format */
1869 int
1870 netpgp_write_sshkey(netpgp_t *netpgp, char *s, const char *userid, char *out, size_t size)
1871 {
1872 	const pgp_key_t	*key;
1873 	pgp_keyring_t		*keyring;
1874 	pgp_io_t		*io;
1875 	unsigned		 k;
1876 	size_t			 cc;
1877 	char			 f[MAXPATHLEN];
1878 
1879 	if ((io = calloc(1, sizeof(pgp_io_t))) == NULL) {
1880 		(void) fprintf(stderr, "netpgp_save_sshpub: bad alloc 1\n");
1881 		return 0;
1882 	}
1883 	io->outs = stdout;
1884 	io->errs = stderr;
1885 	io->res = stderr;
1886 	netpgp->io = io;
1887 	/* write new to temp file */
1888 	savepubkey(s, f, sizeof(f));
1889 	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
1890 		(void) fprintf(stderr, "netpgp_save_sshpub: bad alloc 2\n");
1891 		return 0;
1892 	}
1893 	if (!pgp_keyring_fileread(netpgp->pubring = keyring, 1, f)) {
1894 		(void) fprintf(stderr, "can't import key\n");
1895 		return 0;
1896 	}
1897 	/* get rsa key */
1898 	k = 0;
1899 	key = pgp_getnextkeybyname(netpgp->io, netpgp->pubring, userid, &k);
1900 	if (key == NULL) {
1901 		(void) fprintf(stderr, "no key found for '%s'\n", userid);
1902 		return 0;
1903 	}
1904 	if (key->key.pubkey.alg != PGP_PKA_RSA) {
1905 		/* we're not interested in supporting DSA either :-) */
1906 		(void) fprintf(stderr, "key not RSA '%s'\n", userid);
1907 		return 0;
1908 	}
1909 	/* XXX - check trust sigs */
1910 	/* XXX - check expiry */
1911 	/* XXX - check start */
1912 	/* XXX - check not weak key */
1913 	/* get rsa e and n */
1914 	(void) memset(out, 0x0, size);
1915 	cc = formatstring((char *)out, (const uint8_t *)"ssh-rsa", 7);
1916 	cc += formatbignum((char *)&out[cc], key->key.pubkey.key.rsa.e);
1917 	cc += formatbignum((char *)&out[cc], key->key.pubkey.key.rsa.n);
1918 	free(io);
1919 	free(keyring);
1920 	return cc;
1921 }
1922