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