netpgp.c revision 1.38
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.38 2010/02/11 17:46:09 agc Exp $");
38#endif
39
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <sys/param.h>
43#include <sys/mman.h>
44
45#ifdef HAVE_SYS_RESOURCE_H
46#include <sys/resource.h>
47#endif
48
49#ifdef HAVE_FCNTL_H
50#include <fcntl.h>
51#endif
52
53#include <errno.h>
54#include <regex.h>
55#include <stdarg.h>
56#include <stdlib.h>
57#include <string.h>
58#include <time.h>
59
60#ifdef HAVE_UNISTD_H
61#include <unistd.h>
62#endif
63
64#include <errno.h>
65
66#ifdef HAVE_LIMITS_H
67#include <limits.h>
68#endif
69
70#include <netpgp.h>
71
72#include "packet.h"
73#include "packet-parse.h"
74#include "keyring.h"
75#include "errors.h"
76#include "packet-show.h"
77#include "create.h"
78#include "netpgpsdk.h"
79#include "memory.h"
80#include "validate.h"
81#include "readerwriter.h"
82#include "netpgpdefs.h"
83#include "crypto.h"
84#include "ops-ssh.h"
85#include "defs.h"
86
87/* read any gpg config file */
88static int
89conffile(netpgp_t *netpgp, char *homedir, char *userid, size_t length)
90{
91	regmatch_t	 matchv[10];
92	regex_t		 keyre;
93	char		 buf[BUFSIZ];
94	FILE		*fp;
95
96	__OPS_USED(netpgp);
97	(void) snprintf(buf, sizeof(buf), "%s/gpg.conf", homedir);
98	if ((fp = fopen(buf, "r")) == NULL) {
99		return 0;
100	}
101	(void) memset(&keyre, 0x0, sizeof(keyre));
102	(void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)",
103		REG_EXTENDED);
104	while (fgets(buf, 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	return 1;
119}
120
121/* small function to pretty print an 8-character raw userid */
122static char    *
123userid_to_id(const unsigned char *userid, char *id)
124{
125	static const char *hexes = "0123456789abcdef";
126	int		   i;
127
128	for (i = 0; i < 8 ; i++) {
129		id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4];
130		id[(i * 2) + 1] = hexes[userid[i] & 0xf];
131	}
132	id[8 * 2] = 0x0;
133	return id;
134}
135
136/* print out the successful signature information */
137static void
138resultp(__ops_io_t *io,
139	const char *f,
140	__ops_validation_t *res,
141	__ops_keyring_t *ring)
142{
143	const __ops_key_t	*pubkey;
144	unsigned		 from;
145	unsigned		 i;
146	time_t			 t;
147	char			 id[MAX_ID_LENGTH + 1];
148
149	for (i = 0; i < res->validc; i++) {
150		(void) fprintf(io->res,
151			"Good signature for %s made %s",
152			(f) ? f : "<stdin>",
153			ctime(&res->valid_sigs[i].birthtime));
154		if (res->duration > 0) {
155			t = res->birthtime + res->duration;
156			(void) fprintf(io->res, "Valid until %s", ctime(&t));
157		}
158		(void) fprintf(io->res,
159			"using %s key %s\n",
160			__ops_show_pka(res->valid_sigs[i].key_alg),
161			userid_to_id(res->valid_sigs[i].signer_id, id));
162		from = 0;
163		pubkey = __ops_getkeybyid(io, ring,
164			(const unsigned char *) res->valid_sigs[i].signer_id,
165			&from);
166		__ops_print_keydata(io, pubkey, "pub", &pubkey->key.pubkey);
167	}
168}
169
170/* check there's enough space in the arrays */
171static int
172size_arrays(netpgp_t *netpgp, unsigned needed)
173{
174	char	**temp;
175
176	if (netpgp->size == 0) {
177		/* only get here first time around */
178		netpgp->size = needed;
179		if ((netpgp->name = calloc(sizeof(char *), needed)) == NULL) {
180			(void) fprintf(stderr, "size_arrays: bad alloc\n");
181			return 0;
182		}
183		if ((netpgp->value = calloc(sizeof(char *), needed)) == NULL) {
184			free(netpgp->name);
185			(void) fprintf(stderr, "size_arrays: bad alloc\n");
186			return 0;
187		}
188	} else if (netpgp->c == netpgp->size) {
189		/* only uses 'needed' when filled array */
190		netpgp->size += needed;
191		temp = realloc(netpgp->name, sizeof(char *) * needed);
192		if (temp == NULL) {
193			(void) fprintf(stderr, "size_arrays: bad alloc\n");
194			return 0;
195		}
196		netpgp->name = temp;
197		temp = realloc(netpgp->value, sizeof(char *) * needed);
198		if (temp == NULL) {
199			(void) fprintf(stderr, "size_arrays: bad alloc\n");
200			return 0;
201		}
202		netpgp->value = temp;
203	}
204	return 1;
205}
206
207/* find the name in the array */
208static int
209findvar(netpgp_t *netpgp, const char *name)
210{
211	unsigned	i;
212
213	for (i = 0 ; i < netpgp->c && strcmp(netpgp->name[i], name) != 0; i++) {
214	}
215	return (i == netpgp->c) ? -1 : (int)i;
216}
217
218/* read a keyring and return it */
219static void *
220readkeyring(netpgp_t *netpgp, const char *name)
221{
222	__ops_keyring_t	*keyring;
223	const unsigned	 noarmor = 0;
224	char		 f[MAXPATHLEN];
225	char		*filename;
226	char		*homedir;
227
228	homedir = netpgp_getvar(netpgp, "homedir");
229	if ((filename = netpgp_getvar(netpgp, name)) == NULL) {
230		(void) snprintf(f, sizeof(f), "%s/%s.gpg", homedir, name);
231		filename = f;
232	}
233	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
234		(void) fprintf(stderr, "readkeyring: bad alloc\n");
235		return NULL;
236	}
237	if (!__ops_keyring_fileread(keyring, noarmor, filename)) {
238		free(keyring);
239		(void) fprintf(stderr, "Can't read %s %s\n", name, filename);
240		return NULL;
241	}
242	netpgp_setvar(netpgp, name, filename);
243	return keyring;
244}
245
246/* read keys from ssh key files */
247static int
248readsshkeys(netpgp_t *netpgp, char *homedir)
249{
250	__ops_keyring_t	*pubring;
251	__ops_keyring_t	*secring;
252	char		 f[MAXPATHLEN];
253	char		*filename;
254
255	if ((filename = netpgp_getvar(netpgp, "sshkeyfile")) == NULL) {
256		(void) snprintf(f, sizeof(f), "%s/.ssh/is_rsa.pub", homedir);
257		filename = f;
258	}
259	if ((pubring = calloc(1, sizeof(*pubring))) == NULL) {
260		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
261		return 0;
262	}
263	if (!__ops_ssh2_readkeys(netpgp->io, pubring, NULL, filename, NULL)) {
264		free(pubring);
265		(void) fprintf(stderr, "readsshkeys: can't read %s\n",
266				filename);
267		return 0;
268	}
269	if (netpgp->pubring == NULL) {
270		netpgp->pubring = pubring;
271	} else {
272		__ops_append_keyring(netpgp->pubring, pubring);
273	}
274	netpgp_setvar(netpgp, "sshpubfile", filename);
275	/* try to take the ".pub" off the end */
276	if (filename == f) {
277		f[strlen(f) - 4] = 0x0;
278	} else {
279		(void) snprintf(f, sizeof(f), "%.*s",
280				(int)strlen(filename) - 4, filename);
281		filename = f;
282	}
283	if ((secring = calloc(1, sizeof(*secring))) == NULL) {
284		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
285		return 0;
286	}
287	if (__ops_ssh2_readkeys(netpgp->io, pubring, secring, NULL, filename)) {
288		netpgp->secring = secring;
289		netpgp_setvar(netpgp, "sshsecfile", filename);
290	} else {
291		(void) fprintf(stderr, "readsshkeys: can't read sec %s (%d)\n",
292				filename, errno);
293	}
294	return 1;
295}
296
297/* set ssh uid to first one in ring */
298static void
299set_ssh_userid(__ops_keyring_t *pubring, char *id, size_t len, int last)
300{
301	unsigned char	*src;
302	int		 i;
303	int		 n;
304
305	(void) memset(id, 0x0, len);
306	src = pubring->keys[(last) ? pubring->keyc - 1 : 0].key_id;
307	for (i = 0, n = 0 ; i < OPS_KEY_ID_SIZE ; i += 2) {
308		n += snprintf(&id[n], len - n, "%02x%02x", src[i], src[i + 1]);
309	}
310	id[n] = 0x0;
311}
312
313/* get expiration in seconds */
314static uint64_t
315get_duration(char *s)
316{
317	struct tm	 tm;
318	uint64_t	 now;
319	char		*mult;
320
321	if (s == NULL) {
322		return 0;
323	}
324	now = strtoull(s, NULL, 10);
325	if ((mult = strchr("hdwmy", s[strlen(s) - 1])) != NULL) {
326		switch(*mult) {
327		case 'h':
328			return now * 60 * 60;
329		case 'd':
330			return now * 60 * 60 * 24;
331		case 'w':
332			return now * 60 * 60 * 24 * 7;
333		case 'm':
334			return now * 60 * 60 * 24 * 31;
335		case 'y':
336			return now * 60 * 60 * 24 * 365;
337		}
338	}
339	if (strptime(s, "%Y-%m-%d", &tm) != NULL) {
340		return mktime(&tm);
341	}
342	if (strptime(s, "%Y/%m/%d", &tm) != NULL) {
343		return mktime(&tm);
344	}
345	return (uint64_t)strtoll(s, NULL, 10);
346}
347
348/* get birthtime in seconds */
349static int64_t
350get_birthtime(char *s)
351{
352	struct tm	 tm;
353
354	if (s == NULL) {
355		return time(NULL);
356	}
357	if (strptime(s, "%Y-%m-%d", &tm) != NULL) {
358		return mktime(&tm);
359	}
360	if (strptime(s, "%Y/%m/%d", &tm) != NULL) {
361		return mktime(&tm);
362	}
363	return (uint64_t)strtoll(s, NULL, 10);
364}
365
366/***************************************************************************/
367/* exported functions start here */
368/***************************************************************************/
369
370/* initialise a netpgp_t structure */
371int
372netpgp_init(netpgp_t *netpgp)
373{
374	__ops_io_t	*io;
375	char		 id[MAX_ID_LENGTH];
376	char		*homedir;
377	char		*userid;
378	char		*stream;
379	char		*passfd;
380	char		*results;
381	int		 coredumps;
382	int		 last;
383
384#ifdef HAVE_SYS_RESOURCE_H
385	struct rlimit	limit;
386
387	coredumps = netpgp_getvar(netpgp, "coredumps") != NULL;
388	if (!coredumps) {
389		(void) memset(&limit, 0x0, sizeof(limit));
390		if (setrlimit(RLIMIT_CORE, &limit) != 0) {
391			(void) fprintf(stderr,
392			"netpgp: warning - can't turn off core dumps\n");
393			coredumps = 1;
394		}
395	}
396#else
397	coredumps = 1;
398#endif
399	if ((io = calloc(1, sizeof(*io))) == NULL) {
400		(void) fprintf(stderr, "netpgp_init: bad alloc\n");
401		return 0;
402	}
403	io->outs = stdout;
404	if ((stream = netpgp_getvar(netpgp, "stdout")) != NULL &&
405	    strcmp(stream, "stderr") == 0) {
406		io->outs = stderr;
407	}
408	io->errs = stderr;
409	if ((stream = netpgp_getvar(netpgp, "stderr")) != NULL &&
410	    strcmp(stream, "stdout") == 0) {
411		io->errs = stdout;
412	}
413	if ((results = netpgp_getvar(netpgp, "results")) == NULL) {
414		io->res = io->errs;
415	} else if ((io->res = fopen(results, "w")) == NULL) {
416		(void) fprintf(io->errs, "Can't open results %s for writing\n",
417			results);
418		return 0;
419	}
420	netpgp->io = io;
421	if ((passfd = netpgp_getvar(netpgp, "pass-fd")) != NULL &&
422	    (netpgp->passfp = fdopen(atoi(passfd), "r")) == NULL) {
423		(void) fprintf(io->errs, "Can't open fd %s for reading\n",
424			passfd);
425		return 0;
426	}
427	if (coredumps) {
428		(void) fprintf(io->errs,
429			"netpgp: warning: core dumps enabled\n");
430	}
431	if ((homedir = netpgp_getvar(netpgp, "homedir")) == NULL) {
432		(void) fprintf(io->errs, "netpgp: bad homedir\n");
433		return 0;
434	}
435	/* read from either gpg files or ssh keys */
436	if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
437		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
438			(void) memset(id, 0x0, sizeof(id));
439			(void) conffile(netpgp, homedir, id, sizeof(id));
440			if (id[0] != 0x0) {
441				netpgp_setvar(netpgp, "userid", userid = id);
442			}
443		}
444		if (userid == NULL) {
445			if (netpgp_getvar(netpgp, "need userid") != NULL) {
446				(void) fprintf(io->errs,
447						"Cannot find user id\n");
448				return 0;
449			}
450		} else {
451			(void) netpgp_setvar(netpgp, "userid", userid);
452		}
453		netpgp->pubring = readkeyring(netpgp, "pubring");
454		if (netpgp->pubring == NULL) {
455			(void) fprintf(io->errs, "Can't read pub keyring\n");
456			return 0;
457		}
458		netpgp->secring = readkeyring(netpgp, "secring");
459		if (netpgp->secring == NULL) {
460			(void) fprintf(io->errs, "Can't read sec keyring\n");
461			return 0;
462		}
463	} else {
464		last = (netpgp->pubring != NULL);
465		if (!readsshkeys(netpgp, homedir)) {
466			(void) fprintf(io->errs, "Can't read ssh pub key\n");
467			return 0;
468		}
469		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
470			set_ssh_userid(netpgp->pubring, id, sizeof(id), last);
471			netpgp_setvar(netpgp, "userid", userid = id);
472		}
473		if (userid == NULL) {
474			if (netpgp_getvar(netpgp, "need userid") != NULL) {
475				(void) fprintf(io->errs,
476						"Cannot find user id\n");
477				return 0;
478			}
479		} else {
480			(void) netpgp_setvar(netpgp, "userid", userid);
481		}
482	}
483	return 1;
484}
485
486/* finish off with the netpgp_t struct */
487int
488netpgp_end(netpgp_t *netpgp)
489{
490	unsigned	i;
491
492	for (i = 0 ; i < netpgp->c ; i++) {
493		if (netpgp->name[i] != NULL) {
494			free(netpgp->name[i]);
495		}
496		if (netpgp->value[i] != NULL) {
497			free(netpgp->value[i]);
498		}
499	}
500	if (netpgp->name != NULL) {
501		free(netpgp->name);
502	}
503	if (netpgp->value != NULL) {
504		free(netpgp->value);
505	}
506	if (netpgp->pubring != NULL) {
507		__ops_keyring_free(netpgp->pubring);
508	}
509	if (netpgp->secring != NULL) {
510		__ops_keyring_free(netpgp->secring);
511	}
512	free(netpgp->io);
513	return 1;
514}
515
516/* list the keys in a keyring */
517int
518netpgp_list_keys(netpgp_t *netpgp)
519{
520	return __ops_keyring_list(netpgp->io, netpgp->pubring);
521}
522
523DEFINE_ARRAY(strings_t, char *);
524
525#ifndef HKP_VERSION
526#define HKP_VERSION	1
527#endif
528
529/* find and list some keys in a keyring */
530int
531netpgp_match_keys(netpgp_t *netpgp, char *name, const char *fmt, void *vp)
532{
533	const __ops_key_t	*key;
534	unsigned		 k;
535	strings_t		 pubs;
536	FILE			*fp = (FILE *)vp;
537
538	(void) memset(&pubs, 0x0, sizeof(pubs));
539	k = 0;
540	do {
541		key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring,
542						name, &k);
543		if (key != NULL) {
544			ALLOC(char *, pubs.v, pubs.size, pubs.c, 10, 10,
545					"netpgp_match_keys", return 0);
546			if (strcmp(fmt, "mr") == 0) {
547				__ops_hkp_sprint_keydata(
548						key, &pubs.v[pubs.c],
549						&key->key.pubkey);
550			} else {
551				__ops_sprint_keydata(
552						key, &pubs.v[pubs.c],
553						"pub",
554						&key->key.pubkey);
555			}
556			pubs.c += 1;
557			k += 1;
558		}
559	} while (key != NULL);
560	if (strcmp(fmt, "mr") == 0) {
561		(void) fprintf(fp, "info:%d:%d\n", HKP_VERSION, pubs.c);
562	} else {
563		(void) fprintf(fp, "%d keys found\n", pubs.c);
564	}
565	for (k = 0 ; k < pubs.c ; k++) {
566		(void) fprintf(fp, "%s", pubs.v[k]);
567		free(pubs.v[k]);
568	}
569	free(pubs.v);
570	return pubs.c;
571}
572
573/* find and list some public keys in a keyring */
574int
575netpgp_match_pubkeys(netpgp_t *netpgp, char *name, void *vp)
576{
577	const __ops_key_t	*key;
578	unsigned		 k;
579	strings_t		 pubs;
580	FILE			*fp = (FILE *)vp;
581
582	(void) memset(&pubs, 0x0, sizeof(pubs));
583	do {
584		key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring,
585						name, &k);
586		if (key != NULL) {
587			char	out[1024 * 64];
588
589			ALLOC(char *, pubs.v, pubs.size, pubs.c, 10, 10,
590					"netpgp_match_pubkeys", return 0);
591			(void) __ops_sprint_pubkey(key, out, sizeof(out));
592			pubs.v[pubs.c++] = strdup(out);
593			k += 1;
594		}
595	} while (key != NULL);
596	(void) fprintf(fp, "info:%d:%d\n", HKP_VERSION, pubs.c);
597	for (k = 0 ; k < pubs.c ; k++) {
598		(void) fprintf(fp, "%s", pubs.v[k]);
599		free(pubs.v[k]);
600	}
601	free(pubs.v);
602	return pubs.c;
603}
604
605/* find a key in a keyring */
606int
607netpgp_find_key(netpgp_t *netpgp, char *id)
608{
609	__ops_io_t	*io;
610
611	io = netpgp->io;
612	if (id == NULL) {
613		(void) fprintf(io->errs, "NULL id to search for\n");
614		return 0;
615	}
616	return __ops_getkeybyname(netpgp->io, netpgp->pubring, id) != NULL;
617}
618
619/* get a key in a keyring */
620char *
621netpgp_get_key(netpgp_t *netpgp, const char *id)
622{
623	const __ops_key_t	*key;
624	__ops_io_t		*io;
625	char			*newkey;
626
627	io = netpgp->io;
628	if (id == NULL) {
629		/* just get the default userid */
630		id = netpgp_getvar(netpgp, "userid");
631	}
632	key = __ops_getkeybyname(netpgp->io, netpgp->pubring, id);
633	if (key == NULL) {
634		(void) fprintf(io->errs, "Can't find key '%s'\n", id);
635		return NULL;
636	}
637	return (__ops_sprint_keydata(key, &newkey, "pub",
638				&key->key.pubkey) > 0) ? newkey : NULL;
639}
640
641/* export a given key */
642int
643netpgp_export_key(netpgp_t *netpgp, char *userid)
644{
645	const __ops_key_t	*keypair;
646	__ops_io_t		*io;
647
648	io = netpgp->io;
649	if (userid == NULL) {
650		userid = netpgp_getvar(netpgp, "userid");
651	}
652	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
653	if (keypair == NULL) {
654		(void) fprintf(io->errs,
655			"Cannot find own key \"%s\" in keyring\n", userid);
656		return 0;
657	}
658	return __ops_export_key(keypair, NULL);
659}
660
661/* import a key into our keyring */
662int
663netpgp_import_key(netpgp_t *netpgp, char *f)
664{
665	const unsigned	noarmor = 0;
666	const unsigned	armor = 1;
667	__ops_io_t	*io;
668	int		done;
669
670	io = netpgp->io;
671	if ((done = __ops_keyring_fileread(netpgp->pubring, noarmor, f)) == 0) {
672		done = __ops_keyring_fileread(netpgp->pubring, armor, f);
673	}
674	if (!done) {
675		(void) fprintf(io->errs, "Cannot import key from file %s\n",
676				f);
677		return 0;
678	}
679	return __ops_keyring_list(io, netpgp->pubring);
680}
681
682/* generate a new key */
683int
684netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
685{
686	__ops_key_t		*keypair;
687	__ops_userid_t		 uid;
688	__ops_output_t		*create;
689	const unsigned		 noarmor = 0;
690	__ops_io_t		*io;
691	char			*ringfile;
692	int             	 fd;
693
694	(void) memset(&uid, 0x0, sizeof(uid));
695	io = netpgp->io;
696	/* generate a new key for 'id' */
697	uid.userid = (unsigned char *) id;
698	keypair = __ops_rsa_new_selfsign_key(numbits, 65537UL, &uid);
699	if (keypair == NULL) {
700		(void) fprintf(io->errs, "Cannot generate key\n");
701		return 0;
702	}
703	/* write public key, and try to re-read it */
704	ringfile = netpgp_getvar(netpgp, "pubring");
705	fd = __ops_setup_file_append(&create, ringfile);
706	if (!__ops_write_xfer_pubkey(create, keypair, noarmor)) {
707		(void) fprintf(io->errs, "Cannot write pubkey\n");
708		return 0;
709	}
710	__ops_teardown_file_write(create, fd);
711	__ops_keyring_free(netpgp->pubring);
712	if (!__ops_keyring_fileread(netpgp->pubring, noarmor, ringfile)) {
713		(void) fprintf(io->errs, "Cannot read pubring %s\n", ringfile);
714		return 0;
715	}
716	/* write secret key, and try to re-read it */
717	ringfile = netpgp_getvar(netpgp, "sec ring file");
718	fd = __ops_setup_file_append(&create, ringfile);
719	if (!__ops_write_xfer_seckey(create, keypair, NULL, 0, noarmor)) {
720		(void) fprintf(io->errs, "Cannot write seckey\n");
721		return 0;
722	}
723	__ops_teardown_file_write(create, fd);
724	__ops_keyring_free(netpgp->secring);
725	if (!__ops_keyring_fileread(netpgp->secring, noarmor, ringfile)) {
726		(void) fprintf(io->errs, "Can't read secring %s\n", ringfile);
727		return 0;
728	}
729	__ops_keydata_free(keypair);
730	return 1;
731}
732
733/* encrypt a file */
734int
735netpgp_encrypt_file(netpgp_t *netpgp,
736			const char *userid,
737			const char *f,
738			char *out,
739			int armored)
740{
741	const __ops_key_t	*keypair;
742	const unsigned		 overwrite = 1;
743	const char		*suffix;
744	__ops_io_t		*io;
745	char			 outname[MAXPATHLEN];
746
747	io = netpgp->io;
748	if (f == NULL) {
749		(void) fprintf(io->errs,
750			"netpgp_encrypt_file: no filename specified\n");
751		return 0;
752	}
753	if (userid == NULL) {
754		userid = netpgp_getvar(netpgp, "userid");
755	}
756	suffix = (armored) ? ".asc" : ".gpg";
757	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
758	if (keypair == NULL) {
759		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
760					userid);
761		return 0;
762	}
763	if (out == NULL) {
764		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
765		out = outname;
766	}
767	return (int)__ops_encrypt_file(io, f, out, keypair, (unsigned)armored,
768					overwrite);
769}
770
771#define ARMOR_HEAD	"-----BEGIN PGP MESSAGE-----\r\n"
772
773/* decrypt a file */
774int
775netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored)
776{
777	const unsigned	 overwrite = 1;
778	__ops_io_t	*io;
779	unsigned	 realarmour;
780	FILE		*fp;
781	char		 buf[BUFSIZ];
782
783	io = netpgp->io;
784	if (f == NULL) {
785		(void) fprintf(io->errs,
786			"netpgp_decrypt_file: no filename specified\n");
787		return 0;
788	}
789	realarmour = (unsigned)armored;
790	if ((fp = fopen(f, "r")) == NULL) {
791		(void) fprintf(io->errs,
792			"netpgp_decrypt_file: can't open '%s'\n", f);
793		return 0;
794	}
795	if (fgets(buf, sizeof(buf), fp) == NULL) {
796		realarmour = 0;
797	} else {
798		realarmour = (strcmp(buf, ARMOR_HEAD) == 0);
799	}
800	(void) fclose(fp);
801	return __ops_decrypt_file(netpgp->io, f, out, netpgp->secring,
802				(unsigned)realarmour, overwrite,
803				netpgp->passfp, get_passphrase_cb);
804}
805
806/* sign a file */
807int
808netpgp_sign_file(netpgp_t *netpgp,
809		const char *userid,
810		const char *f,
811		char *out,
812		int armored,
813		int cleartext,
814		int detached)
815{
816	const __ops_key_t	*keypair;
817	const __ops_key_t	*pubkey;
818	__ops_seckey_t		*seckey;
819	const unsigned		 overwrite = 1;
820	__ops_io_t		*io;
821	char			*hashalg;
822	int			 ret;
823
824	io = netpgp->io;
825	if (f == NULL) {
826		(void) fprintf(io->errs,
827			"netpgp_sign_file: no filename specified\n");
828		return 0;
829	}
830	if (userid == NULL) {
831		userid = netpgp_getvar(netpgp, "userid");
832	}
833	/* get key with which to sign */
834	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
835	if (keypair == NULL) {
836		(void) fprintf(io->errs, "Userid '%s' not found in secring\n",
837				userid);
838		return 0;
839	}
840	ret = 1;
841	do {
842		if (netpgp->passfp == NULL) {
843			/* print out the user id */
844			pubkey = __ops_getkeybyname(io, netpgp->pubring, userid);
845			if (pubkey == NULL) {
846				(void) fprintf(io->errs,
847					"netpgp: warning - using pubkey from secring\n");
848				__ops_print_keydata(io, keypair, "pub",
849					&keypair->key.seckey.pubkey);
850			} else {
851				__ops_print_keydata(io, pubkey, "pub", &pubkey->key.pubkey);
852			}
853		}
854		if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
855			/* now decrypt key */
856			seckey = __ops_decrypt_seckey(keypair, netpgp->passfp);
857			if (seckey == NULL) {
858				(void) fprintf(io->errs, "Bad passphrase\n");
859			}
860		} else {
861			__ops_keyring_t	*secring;
862
863			secring = netpgp->secring;
864			seckey = &secring->keys[0].key.seckey;
865		}
866	} while (seckey == NULL);
867	/* sign file */
868	hashalg = netpgp_getvar(netpgp, "hash");
869	if (detached) {
870		ret = __ops_sign_detached(io, f, out, seckey, hashalg,
871				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
872				get_duration(netpgp_getvar(netpgp, "duration")));
873	} else {
874		ret = __ops_sign_file(io, f, out, seckey, hashalg,
875				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
876				get_duration(netpgp_getvar(netpgp, "duration")),
877				(unsigned)armored, (unsigned)cleartext,
878				overwrite);
879	}
880	__ops_forget(seckey, sizeof(*seckey));
881	return ret;
882}
883
884/* verify a file */
885int
886netpgp_verify_file(netpgp_t *netpgp, const char *in, const char *out, int armored)
887{
888	__ops_validation_t	 result;
889	__ops_io_t		*io;
890
891	(void) memset(&result, 0x0, sizeof(result));
892	io = netpgp->io;
893	if (in == NULL) {
894		(void) fprintf(io->errs,
895			"netpgp_verify_file: no filename specified\n");
896		return 0;
897	}
898	if (__ops_validate_file(io, &result, in, out, armored,
899						netpgp->pubring)) {
900		resultp(io, in, &result, netpgp->pubring);
901		return 1;
902	}
903	if (result.validc + result.invalidc + result.unknownc == 0) {
904		(void) fprintf(io->errs,
905		"\"%s\": No signatures found - is this a signed file?\n",
906			in);
907	} else if (result.invalidc == 0 && result.unknownc == 0) {
908		(void) fprintf(io->errs,
909			"\"%s\": file verification failure: invalid signature time\n", in);
910	} else {
911		(void) fprintf(io->errs,
912"\"%s\": verification failure: %u invalid signatures, %u unknown signatures\n",
913			in, result.invalidc, result.unknownc);
914	}
915	return 0;
916}
917
918/* sign some memory */
919int
920netpgp_sign_memory(netpgp_t *netpgp,
921		const char *userid,
922		char *mem,
923		size_t size,
924		char *out,
925		size_t outsize,
926		const unsigned armored,
927		const unsigned cleartext)
928{
929	const __ops_key_t	*keypair;
930	const __ops_key_t	*pubkey;
931	__ops_seckey_t		*seckey;
932	__ops_memory_t		*signedmem;
933	__ops_io_t		*io;
934	char			*hashalg;
935	int			 ret;
936
937	io = netpgp->io;
938	if (mem == NULL) {
939		(void) fprintf(io->errs,
940			"netpgp_sign_memory: no memory to sign\n");
941		return 0;
942	}
943	if (userid == NULL) {
944		userid = netpgp_getvar(netpgp, "userid");
945	}
946	/* get key with which to sign */
947	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
948	if (keypair == NULL) {
949		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
950				userid);
951		return 0;
952	}
953	ret = 1;
954	do {
955		if (netpgp->passfp == NULL) {
956			/* print out the user id */
957			pubkey = __ops_getkeybyname(io, netpgp->pubring, userid);
958			if (pubkey == NULL) {
959				(void) fprintf(io->errs,
960					"netpgp: warning - using pubkey from secring\n");
961				__ops_print_keydata(io, keypair, "pub",
962					&keypair->key.seckey.pubkey);
963			} else {
964				__ops_print_keydata(io, pubkey, "pub", &pubkey->key.pubkey);
965			}
966		}
967		/* now decrypt key */
968		seckey = __ops_decrypt_seckey(keypair, netpgp->passfp);
969		if (seckey == NULL) {
970			(void) fprintf(io->errs, "Bad passphrase\n");
971		}
972	} while (seckey == NULL);
973	/* sign file */
974	(void) memset(out, 0x0, outsize);
975	hashalg = netpgp_getvar(netpgp, "hash");
976	signedmem = __ops_sign_buf(io, mem, size, seckey,
977				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
978				get_duration(netpgp_getvar(netpgp, "duration")),
979				hashalg, armored, cleartext);
980	if (signedmem) {
981		size_t	m;
982
983		m = MIN(__ops_mem_len(signedmem), outsize);
984		(void) memcpy(out, __ops_mem_data(signedmem), m);
985		__ops_memory_free(signedmem);
986		ret = (int)m;
987	} else {
988		ret = 0;
989	}
990	__ops_forget(seckey, sizeof(*seckey));
991	return ret;
992}
993
994/* verify memory */
995int
996netpgp_verify_memory(netpgp_t *netpgp, const void *in, const size_t size,
997			void *out, size_t outsize, const int armored)
998{
999	__ops_validation_t	 result;
1000	__ops_memory_t		*signedmem;
1001	__ops_memory_t		*cat;
1002	__ops_io_t		*io;
1003	size_t			 m;
1004	int			 ret;
1005
1006	(void) memset(&result, 0x0, sizeof(result));
1007	io = netpgp->io;
1008	if (in == NULL) {
1009		(void) fprintf(io->errs,
1010			"netpgp_verify_memory: no memory to verify\n");
1011		return 0;
1012	}
1013	signedmem = __ops_memory_new();
1014	__ops_memory_add(signedmem, in, size);
1015	ret = __ops_validate_mem(io, &result, signedmem,
1016				(out) ? &cat : NULL,
1017				armored, netpgp->pubring);
1018	__ops_memory_free(signedmem);
1019	if (ret) {
1020		resultp(io, "<stdin>", &result, netpgp->pubring);
1021		if (out) {
1022			m = MIN(__ops_mem_len(cat), outsize);
1023			(void) memcpy(out, __ops_mem_data(cat), m);
1024			__ops_memory_free(cat);
1025		} else {
1026			m = 1;
1027		}
1028		return (int)m;
1029	}
1030	if (result.validc + result.invalidc + result.unknownc == 0) {
1031		(void) fprintf(io->errs,
1032		"No signatures found - is this memory signed?\n");
1033	} else if (result.invalidc == 0 && result.unknownc == 0) {
1034		(void) fprintf(io->errs,
1035			"memory verification failure: invalid signature time\n");
1036	} else {
1037		(void) fprintf(io->errs,
1038"memory verification failure: %u invalid signatures, %u unknown signatures\n",
1039			result.invalidc, result.unknownc);
1040	}
1041	return 0;
1042}
1043
1044/* encrypt some memory */
1045int
1046netpgp_encrypt_memory(netpgp_t *netpgp,
1047			const char *userid,
1048			void *in,
1049			const size_t insize,
1050			char *out,
1051			size_t outsize,
1052			int armored)
1053{
1054	const __ops_key_t	*keypair;
1055	__ops_memory_t		*enc;
1056	__ops_io_t		*io;
1057	size_t			 m;
1058
1059	io = netpgp->io;
1060	if (in == NULL) {
1061		(void) fprintf(io->errs,
1062			"netpgp_encrypt_buf: no memory to encrypt\n");
1063		return 0;
1064	}
1065	if (userid == NULL) {
1066		userid = netpgp_getvar(netpgp, "userid");
1067	}
1068	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
1069	if (keypair == NULL) {
1070		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
1071					userid);
1072		return 0;
1073	}
1074	if (in == out) {
1075		(void) fprintf(io->errs,
1076			"netpgp_encrypt_buf: input and output bufs need to be different\n");
1077		return 0;
1078	}
1079	if (outsize < insize) {
1080		(void) fprintf(io->errs,
1081			"netpgp_encrypt_buf: input size is larger than output size\n");
1082		return 0;
1083	}
1084	enc = __ops_encrypt_buf(io, in, insize, keypair, (unsigned)armored);
1085	m = MIN(__ops_mem_len(enc), outsize);
1086	(void) memcpy(out, __ops_mem_data(enc), m);
1087	__ops_memory_free(enc);
1088	return (int)m;
1089}
1090
1091/* decrypt a chunk of memory */
1092int
1093netpgp_decrypt_memory(netpgp_t *netpgp, const void *input, const size_t insize,
1094			char *out, size_t outsize, const int armored)
1095{
1096	__ops_memory_t	*mem;
1097	__ops_io_t	*io;
1098	unsigned	 realarmour;
1099	size_t		 m;
1100
1101	io = netpgp->io;
1102	realarmour = (unsigned) armored;
1103	if (input == NULL) {
1104		(void) fprintf(io->errs,
1105			"netpgp_decrypt_memory: no memory\n");
1106		return 0;
1107	}
1108	realarmour = (strncmp(input, ARMOR_HEAD, sizeof(ARMOR_HEAD) - 1) == 0);
1109	mem = __ops_decrypt_buf(netpgp->io, input, insize, netpgp->secring,
1110				realarmour, netpgp->passfp,
1111				get_passphrase_cb);
1112	m = MIN(__ops_mem_len(mem), outsize);
1113	(void) memcpy(out, __ops_mem_data(mem), m);
1114	__ops_memory_free(mem);
1115	return (int)m;
1116}
1117
1118/* wrappers for the ops_debug_level functions we added to openpgpsdk */
1119
1120/* set the debugging level per filename */
1121int
1122netpgp_set_debug(const char *f)
1123{
1124	return __ops_set_debug_level(f);
1125}
1126
1127/* get the debugging level per filename */
1128int
1129netpgp_get_debug(const char *f)
1130{
1131	return __ops_get_debug_level(f);
1132}
1133
1134/* return the version for the library */
1135const char *
1136netpgp_get_info(const char *type)
1137{
1138	return __ops_get_info(type);
1139}
1140
1141/* list all the packets in a file */
1142int
1143netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname)
1144{
1145	__ops_keyring_t	*keyring;
1146	const unsigned	 noarmor = 0;
1147	struct stat	 st;
1148	__ops_io_t	*io;
1149	char		 ringname[MAXPATHLEN];
1150	char		*homedir;
1151	int		 ret;
1152
1153	io = netpgp->io;
1154	if (f == NULL) {
1155		(void) fprintf(io->errs, "No file containing packets\n");
1156		return 0;
1157	}
1158	if (stat(f, &st) < 0) {
1159		(void) fprintf(io->errs, "No such file '%s'\n", f);
1160		return 0;
1161	}
1162	homedir = netpgp_getvar(netpgp, "homedir");
1163	if (pubringname == NULL) {
1164		(void) snprintf(ringname, sizeof(ringname),
1165				"%s/pubring.gpg", homedir);
1166		pubringname = ringname;
1167	}
1168	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
1169		(void) fprintf(io->errs, "netpgp_list_packets: bad alloc\n");
1170		return 0;
1171	}
1172	if (!__ops_keyring_fileread(keyring, noarmor, pubringname)) {
1173		free(keyring);
1174		(void) fprintf(io->errs, "Cannot read pub keyring %s\n",
1175			pubringname);
1176		return 0;
1177	}
1178	netpgp->pubring = keyring;
1179	netpgp_setvar(netpgp, "pubring", pubringname);
1180	ret = __ops_list_packets(io, f, (unsigned)armour, keyring,
1181					netpgp->passfp,
1182					get_passphrase_cb);
1183	free(keyring);
1184	return ret;
1185}
1186
1187/* set a variable */
1188int
1189netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value)
1190{
1191	int	i;
1192
1193	if ((i = findvar(netpgp, name)) < 0) {
1194		/* add the element to the array */
1195		if (size_arrays(netpgp, netpgp->size + 15)) {
1196			netpgp->name[i = netpgp->c++] = strdup(name);
1197		}
1198	} else {
1199		/* replace the element in the array */
1200		if (netpgp->value[i]) {
1201			free(netpgp->value[i]);
1202			netpgp->value[i] = NULL;
1203		}
1204	}
1205	/* sanity checks for range of values */
1206	if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) {
1207		if (__ops_str_to_hash_alg(value) == OPS_HASH_UNKNOWN) {
1208			return 0;
1209		}
1210	}
1211	netpgp->value[i] = strdup(value);
1212	return 1;
1213}
1214
1215/* get a variable's value (NULL if not set) */
1216char *
1217netpgp_getvar(netpgp_t *netpgp, const char *name)
1218{
1219	int	i;
1220
1221	return ((i = findvar(netpgp, name)) < 0) ? NULL : netpgp->value[i];
1222}
1223
1224/* increment a value */
1225int
1226netpgp_incvar(netpgp_t *netpgp, const char *name, const int delta)
1227{
1228	char	*cp;
1229	char	 num[16];
1230	int	 val;
1231
1232	val = 0;
1233	if ((cp = netpgp_getvar(netpgp, name)) != NULL) {
1234		val = atoi(cp);
1235	}
1236	(void) snprintf(num, sizeof(num), "%d", val + delta);
1237	netpgp_setvar(netpgp, name, num);
1238	return 1;
1239}
1240
1241/* set the home directory value to "home/subdir" */
1242int
1243netpgp_set_homedir(netpgp_t *netpgp, char *home, const char *subdir, const int quiet)
1244{
1245	struct stat	st;
1246	char		d[MAXPATHLEN];
1247
1248	if (home == NULL) {
1249		if (!quiet) {
1250			(void) fprintf(stderr, "NULL HOME directory\n");
1251		}
1252		return 0;
1253	}
1254	(void) snprintf(d, sizeof(d), "%s%s", home, (subdir) ? subdir : "");
1255	if (stat(d, &st) == 0) {
1256		if ((st.st_mode & S_IFMT) == S_IFDIR) {
1257			netpgp_setvar(netpgp, "homedir", d);
1258			return 1;
1259		}
1260		(void) fprintf(stderr, "netpgp: homedir \"%s\" is not a dir\n",
1261					d);
1262		return 0;
1263	}
1264	if (!quiet) {
1265		(void) fprintf(stderr,
1266			"netpgp: warning homedir \"%s\" not found\n", d);
1267	}
1268	return 1;
1269}
1270
1271/* validate all sigs in the pub keyring */
1272int
1273netpgp_validate_sigs(netpgp_t *netpgp)
1274{
1275	__ops_validation_t	result;
1276
1277	return (int)__ops_validate_all_sigs(&result, netpgp->pubring, NULL);
1278}
1279
1280#if 0
1281#include "sshkey.h"
1282
1283int
1284netpgp_pgpkey_to_sshkey(netpgp_t *netpgp, char *name, SSHKey *sshkey)
1285{
1286	const __ops_key_t	*pgpkey;
1287	unsigned		 k;
1288
1289	k = 0;
1290	pgpkey = __ops_getnextkeybyname(netpgp->io, netpgp->pubring, name, &k);
1291	if (pgpkey == NULL) {
1292		pgpkey = __ops_getkeybyname(io, netpgp->pubring, userid);
1293	}
1294	if (pgpkey == NULL) {
1295		(void) fprintf(stderr, "No key matching '%s'\n", name);
1296		return 0;
1297	}
1298	switch(pgpkey->key.pubkey.alg) {
1299	case OPS_PKA_RSA:
1300		sshkey->type = KEY_RSA;
1301		sshkey->rsa = calloc(1, sizeof(*sshkey->rsa);
1302		if (sshkey->rsa == NULL) {
1303			(void) fprintf(stderr, "RSA memory problems\n");
1304			return 0;
1305		}
1306		sshkey->rsa->n = pgpkey->key.pubkey.key.rsa.n;
1307		sshkey->rsa->e = pgpkey->key.pubkey.key.rsa.e;
1308		sshkey->rsa->d = pgpkey->key.seckey.key.rsa.d;
1309		sshkey->rsa->p = pgpkey->key.seckey.key.rsa.p;
1310		sshkey->rsa->q = pgpkey->key.seckey.key.rsa.q;
1311		sshkey->rsa->iqmp = pgpkey->key.seckey.key.rsa.u;
1312		break;
1313	case OPS_PKA_DSA:
1314		sshkey->type = KEY_DSA;
1315		sshkey->dsa = calloc(1, sizeof(*sshkey->dsa);
1316		if (sshkey->dsa == NULL) {
1317			(void) fprintf(stderr, "DSA memory problems\n");
1318			return 0;
1319		}
1320		sshkey->rsa->n = pgpkey->key.pubkey.key.rsa.n;
1321		key->dsa->p = pgpkey->key.pubkey.key.dsa.p;
1322		key->dsa->q = pgpkey->key.pubkey.key.dsa.q;
1323		key->dsa->g = pgpkey->key.pubkey.key.dsa.g;
1324		key->dsa->pub_key = pgpkey->key.pubkey.key.dsa.y;
1325		key->dsa->priv_key = pgpkey->key.seckey.key.dsa.x;
1326		break;
1327	default:
1328		(void) fprintf(stderr, "weird type\n");
1329		return 0;
1330	}
1331	return 1;
1332}
1333#endif
1334