netpgp.c revision 1.32
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.32 2009/12/07 16:17:17 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
86/* read any gpg config file */
87static int
88conffile(netpgp_t *netpgp, char *homedir, char *userid, size_t length)
89{
90	regmatch_t	 matchv[10];
91	regex_t		 keyre;
92	char		 buf[BUFSIZ];
93	FILE		*fp;
94
95	__OPS_USED(netpgp);
96	(void) snprintf(buf, sizeof(buf), "%s/gpg.conf", homedir);
97	if ((fp = fopen(buf, "r")) == NULL) {
98		return 0;
99	}
100	(void) memset(&keyre, 0x0, sizeof(keyre));
101	(void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)",
102		REG_EXTENDED);
103	while (fgets(buf, sizeof(buf), fp) != NULL) {
104		if (regexec(&keyre, buf, 10, matchv, 0) == 0) {
105			(void) memcpy(userid, &buf[(int)matchv[1].rm_so],
106				MIN((unsigned)(matchv[1].rm_eo -
107						matchv[1].rm_so), length));
108			(void) fprintf(stderr,
109				"netpgp: default key set to \"%.*s\"\n",
110				(int)(matchv[1].rm_eo - matchv[1].rm_so),
111				&buf[(int)matchv[1].rm_so]);
112		}
113	}
114	(void) fclose(fp);
115	return 1;
116}
117
118/* small function to pretty print an 8-character raw userid */
119static char    *
120userid_to_id(const unsigned char *userid, char *id)
121{
122	static const char *hexes = "0123456789abcdef";
123	int		   i;
124
125	for (i = 0; i < 8 ; i++) {
126		id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4];
127		id[(i * 2) + 1] = hexes[userid[i] & 0xf];
128	}
129	id[8 * 2] = 0x0;
130	return id;
131}
132
133/* print out the successful signature information */
134static void
135resultp(__ops_io_t *io,
136	const char *f,
137	__ops_validation_t *res,
138	__ops_keyring_t *ring)
139{
140	const __ops_key_t	*pubkey;
141	unsigned		 from;
142	unsigned		 i;
143	char			 id[MAX_ID_LENGTH + 1];
144
145	for (i = 0; i < res->validc; i++) {
146		(void) fprintf(io->res,
147			"Good signature for %s made %susing %s key %s\n",
148			f,
149			ctime(&res->valid_sigs[i].birthtime),
150			__ops_show_pka(res->valid_sigs[i].key_alg),
151			userid_to_id(res->valid_sigs[i].signer_id, id));
152		from = 0;
153		pubkey = __ops_getkeybyid(io, ring,
154			(const unsigned char *) res->valid_sigs[i].signer_id,
155			&from);
156		__ops_print_keydata(io, pubkey, "pub", &pubkey->key.pubkey);
157	}
158}
159
160/* check there's enough space in the arrays */
161static int
162size_arrays(netpgp_t *netpgp, unsigned needed)
163{
164	char	**temp;
165
166	if (netpgp->size == 0) {
167		/* only get here first time around */
168		netpgp->size = needed;
169		if ((netpgp->name = calloc(sizeof(char *), needed)) == NULL) {
170			(void) fprintf(stderr, "size_arrays: bad alloc\n");
171			return 0;
172		}
173		if ((netpgp->value = calloc(sizeof(char *), needed)) == NULL) {
174			free(netpgp->name);
175			(void) fprintf(stderr, "size_arrays: bad alloc\n");
176			return 0;
177		}
178	} else if (netpgp->c == netpgp->size) {
179		/* only uses 'needed' when filled array */
180		netpgp->size += needed;
181		temp = realloc(netpgp->name, sizeof(char *) * needed);
182		if (temp == NULL) {
183			(void) fprintf(stderr, "size_arrays: bad alloc\n");
184			return 0;
185		}
186		netpgp->name = temp;
187		temp = realloc(netpgp->value, sizeof(char *) * needed);
188		if (temp == NULL) {
189			(void) fprintf(stderr, "size_arrays: bad alloc\n");
190			return 0;
191		}
192		netpgp->value = temp;
193	}
194	return 1;
195}
196
197/* find the name in the array */
198static int
199findvar(netpgp_t *netpgp, const char *name)
200{
201	unsigned	i;
202
203	for (i = 0 ; i < netpgp->c && strcmp(netpgp->name[i], name) != 0; i++) {
204	}
205	return (i == netpgp->c) ? -1 : (int)i;
206}
207
208/* read a keyring and return it */
209static void *
210readkeyring(netpgp_t *netpgp, const char *name)
211{
212	__ops_keyring_t	*keyring;
213	const unsigned	 noarmor = 0;
214	char		 f[MAXPATHLEN];
215	char		*filename;
216	char		*homedir;
217
218	homedir = netpgp_getvar(netpgp, "homedir");
219	if ((filename = netpgp_getvar(netpgp, name)) == NULL) {
220		(void) snprintf(f, sizeof(f), "%s/%s.gpg", homedir, name);
221		filename = f;
222	}
223	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
224		(void) fprintf(stderr, "readkeyring: bad alloc\n");
225		return NULL;
226	}
227	if (!__ops_keyring_fileread(keyring, noarmor, filename)) {
228		free(keyring);
229		(void) fprintf(stderr, "Can't read %s %s\n", name, filename);
230		return NULL;
231	}
232	netpgp_setvar(netpgp, name, filename);
233	return keyring;
234}
235
236/* read keys from ssh key files */
237static int
238readsshkeys(netpgp_t *netpgp, const char *pubname, const char *secname)
239{
240	__ops_keyring_t	*pubring;
241	__ops_keyring_t	*secring;
242	char		 f[MAXPATHLEN];
243	char		*filename;
244	char		*etcdir;
245
246	__OPS_USED(secname);
247	etcdir = netpgp_getvar(netpgp, "sshkeydir");
248	if ((filename = netpgp_getvar(netpgp, pubname)) == NULL) {
249		(void) snprintf(f, sizeof(f), "%s/ssh_host_rsa_key.pub", etcdir);
250		filename = f;
251	}
252	if ((pubring = calloc(1, sizeof(*pubring))) == NULL) {
253		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
254		return 0;
255	}
256	if (!__ops_ssh2_readkeys(netpgp->io, pubring, NULL, filename, NULL)) {
257		free(pubring);
258		(void) fprintf(stderr, "readsshkeys: can't read %s\n", filename);
259		return 0;
260	}
261	netpgp->pubring = pubring;
262	netpgp_setvar(netpgp, pubname, filename);
263	if ((filename = netpgp_getvar(netpgp, secname)) == NULL) {
264		(void) snprintf(f, sizeof(f), "%s/ssh_host_rsa_key", etcdir);
265		filename = f;
266	}
267	if ((secring = calloc(1, sizeof(*secring))) == NULL) {
268		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
269		return 0;
270	}
271	if (__ops_ssh2_readkeys(netpgp->io, pubring, secring, NULL, filename)) {
272		netpgp->secring = secring;
273		netpgp_setvar(netpgp, secname, filename);
274	} else {
275		(void) fprintf(stderr, "readsshkeys: can't read sec %s (%d)\n", filename, errno);
276	}
277	return 1;
278}
279
280/***************************************************************************/
281/* exported functions start here */
282/***************************************************************************/
283
284/* initialise a netpgp_t structure */
285int
286netpgp_init(netpgp_t *netpgp)
287{
288	__ops_io_t	*io;
289	char		 id[MAX_ID_LENGTH];
290	char		*homedir;
291	char		*userid;
292	char		*stream;
293	char		*passfd;
294	char		*results;
295	int		 coredumps;
296
297#ifdef HAVE_SYS_RESOURCE_H
298	struct rlimit	limit;
299
300	coredumps = netpgp_getvar(netpgp, "coredumps") != NULL;
301	if (!coredumps) {
302		(void) memset(&limit, 0x0, sizeof(limit));
303		if (setrlimit(RLIMIT_CORE, &limit) != 0) {
304			(void) fprintf(stderr,
305			"netpgp_init: warning - can't turn off core dumps\n");
306			coredumps = 1;
307		}
308	}
309#else
310	coredumps = 1;
311#endif
312	if ((io = calloc(1, sizeof(*io))) == NULL) {
313		(void) fprintf(stderr, "netpgp_init: bad alloc\n");
314		return 0;
315	}
316	io->outs = stdout;
317	if ((stream = netpgp_getvar(netpgp, "stdout")) != NULL &&
318	    strcmp(stream, "stderr") == 0) {
319		io->outs = stderr;
320	}
321	io->errs = stderr;
322	if ((stream = netpgp_getvar(netpgp, "stderr")) != NULL &&
323	    strcmp(stream, "stdout") == 0) {
324		io->errs = stdout;
325	}
326	if ((results = netpgp_getvar(netpgp, "results")) == NULL) {
327		io->res = io->errs;
328	} else if ((io->res = fopen(results, "w")) == NULL) {
329		(void) fprintf(io->errs, "Can't open results %s for writing\n",
330			results);
331		return 0;
332	}
333	netpgp->io = io;
334	if (coredumps) {
335		(void) fprintf(io->errs,
336			"netpgp: warning: core dumps enabled\n");
337	}
338	if ((homedir = netpgp_getvar(netpgp, "homedir")) == NULL) {
339		(void) fprintf(io->errs, "netpgp: bad homedir\n");
340		return 0;
341	}
342	/* read from either gpg files or ssh keys */
343	if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
344		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
345			(void) memset(id, 0x0, sizeof(id));
346			(void) conffile(netpgp, homedir, id, sizeof(id));
347			if (id[0] != 0x0) {
348				netpgp_setvar(netpgp, "userid", userid = id);
349			}
350		}
351		if (userid == NULL) {
352			if (netpgp_getvar(netpgp, "need userid") != NULL) {
353				(void) fprintf(io->errs, "Cannot find user id\n");
354				return 0;
355			}
356		} else {
357			(void) netpgp_setvar(netpgp, "userid", userid);
358		}
359		if ((netpgp->pubring = readkeyring(netpgp, "pubring")) == NULL) {
360			(void) fprintf(io->errs, "Can't read pub keyring\n");
361			return 0;
362		}
363		if ((netpgp->secring = readkeyring(netpgp, "secring")) == NULL) {
364			(void) fprintf(io->errs, "Can't read sec keyring\n");
365			return 0;
366		}
367	} else {
368		if (!readsshkeys(netpgp, "ssh pub key", "ssh sec file")) {
369			(void) fprintf(io->errs, "Can't read ssh pub key\n");
370			return 0;
371		}
372		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
373			/* set ssh uid to first one in ring */
374			__ops_keyring_t	*pubring;
375			unsigned char	*src;
376			int		 i;
377			int		 n;
378
379			pubring = netpgp->pubring;
380			(void) memset(id, 0x0, sizeof(id));
381			src = pubring->keys[0].key_id;
382			for (i = 0, n = 0 ; i < OPS_KEY_ID_SIZE ; i += 2) {
383				n += snprintf(&id[n], sizeof(id) - n, "%02x%02x", src[i], src[i + 1]);
384			}
385			id[n] = 0x0;
386			netpgp_setvar(netpgp, "userid", userid = id);
387		}
388		if (userid == NULL) {
389			if (netpgp_getvar(netpgp, "need userid") != NULL) {
390				(void) fprintf(io->errs, "Cannot find user id\n");
391				return 0;
392			}
393		} else {
394			(void) netpgp_setvar(netpgp, "userid", userid);
395		}
396	}
397	if ((passfd = netpgp_getvar(netpgp, "pass-fd")) != NULL &&
398	    (netpgp->passfp = fdopen(atoi(passfd), "r")) == NULL) {
399		(void) fprintf(io->errs, "Can't open fd %s for reading\n",
400			passfd);
401		return 0;
402	}
403	return 1;
404}
405
406/* finish off with the netpgp_t struct */
407int
408netpgp_end(netpgp_t *netpgp)
409{
410	unsigned	i;
411
412	for (i = 0 ; i < netpgp->c ; i++) {
413		if (netpgp->name[i] != NULL) {
414			free(netpgp->name[i]);
415		}
416		if (netpgp->value[i] != NULL) {
417			free(netpgp->value[i]);
418		}
419	}
420	if (netpgp->name != NULL) {
421		free(netpgp->name);
422	}
423	if (netpgp->value != NULL) {
424		free(netpgp->value);
425	}
426	if (netpgp->pubring != NULL) {
427		__ops_keyring_free(netpgp->pubring);
428	}
429	if (netpgp->secring != NULL) {
430		__ops_keyring_free(netpgp->secring);
431	}
432	free(netpgp->io);
433	return 1;
434}
435
436/* list the keys in a keyring */
437int
438netpgp_list_keys(netpgp_t *netpgp)
439{
440	return __ops_keyring_list(netpgp->io, netpgp->pubring);
441}
442
443/* find and list some keys in a keyring */
444int
445netpgp_match_list_keys(netpgp_t *netpgp, char *name)
446{
447	const __ops_key_t	*key;
448	unsigned		 found;
449	unsigned		 k;
450	char			*data;
451
452	found = k = 0;
453	do {
454		if ((key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring, name, &k)) != NULL) {
455			__ops_sprint_keydata(key, &data, "pub", &key->key.pubkey);
456			printf("%s\n", data);
457			free(data);
458			found += 1;
459			k += 1;
460		}
461	} while (key != NULL);
462	printf("Found %u key%s\n", found, (found == 1) ? "" : "s");
463	return (found > 0);
464}
465
466/* find a key in a keyring */
467int
468netpgp_find_key(netpgp_t *netpgp, char *id)
469{
470	__ops_io_t	*io;
471
472	io = netpgp->io;
473	if (id == NULL) {
474		(void) fprintf(io->errs, "NULL id to search for\n");
475		return 0;
476	}
477	return __ops_getkeybyname(netpgp->io, netpgp->pubring, id) != NULL;
478}
479
480/* get a key in a keyring */
481char *
482netpgp_get_key(netpgp_t *netpgp, const char *id)
483{
484	const __ops_key_t	*key;
485	__ops_io_t		*io;
486	char			*newkey;
487
488	io = netpgp->io;
489	if (id == NULL) {
490		(void) fprintf(io->errs, "NULL id to search for\n");
491		return NULL;
492	}
493	if ((key = __ops_getkeybyname(netpgp->io, netpgp->pubring, id)) == NULL) {
494		(void) fprintf(io->errs, "Can't find key '%s'\n", id);
495		return NULL;
496	}
497	return (__ops_sprint_keydata(key, &newkey, "pub", &key->key.pubkey) > 0) ? newkey : NULL;
498}
499
500/* export a given key */
501int
502netpgp_export_key(netpgp_t *netpgp, char *userid)
503{
504	const __ops_key_t	*keypair;
505	__ops_io_t		*io;
506
507	io = netpgp->io;
508	if (userid == NULL) {
509		userid = netpgp_getvar(netpgp, "userid");
510	}
511	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
512	if (keypair == NULL) {
513		(void) fprintf(io->errs,
514			"Cannot find own key \"%s\" in keyring\n", userid);
515		return 0;
516	}
517	return __ops_export_key(keypair, NULL);
518}
519
520/* import a key into our keyring */
521int
522netpgp_import_key(netpgp_t *netpgp, char *f)
523{
524	const unsigned	noarmor = 0;
525	const unsigned	armor = 1;
526	__ops_io_t	*io;
527	int		done;
528
529	io = netpgp->io;
530	if ((done = __ops_keyring_fileread(netpgp->pubring, noarmor, f)) == 0) {
531		done = __ops_keyring_fileread(netpgp->pubring, armor, f);
532	}
533	if (!done) {
534		(void) fprintf(io->errs, "Cannot import key from file %s\n",
535				f);
536		return 0;
537	}
538	return __ops_keyring_list(io, netpgp->pubring);
539}
540
541/* generate a new key */
542int
543netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
544{
545	__ops_key_t		*keypair;
546	__ops_userid_t		 uid;
547	__ops_output_t		*create;
548	const unsigned		 noarmor = 0;
549	__ops_io_t		*io;
550	char			*ringfile;
551	int             	 fd;
552
553	(void) memset(&uid, 0x0, sizeof(uid));
554	io = netpgp->io;
555	/* generate a new key for 'id' */
556	uid.userid = (unsigned char *) id;
557	keypair = __ops_rsa_new_selfsign_key(numbits, 65537UL, &uid);
558	if (keypair == NULL) {
559		(void) fprintf(io->errs, "Cannot generate key\n");
560		return 0;
561	}
562	/* write public key, and try to re-read it */
563	ringfile = netpgp_getvar(netpgp, "pubring");
564	fd = __ops_setup_file_append(&create, ringfile);
565	if (!__ops_write_xfer_pubkey(create, keypair, noarmor)) {
566		(void) fprintf(io->errs, "Cannot write pubkey\n");
567		return 0;
568	}
569	__ops_teardown_file_write(create, fd);
570	__ops_keyring_free(netpgp->pubring);
571	if (!__ops_keyring_fileread(netpgp->pubring, noarmor, ringfile)) {
572		(void) fprintf(io->errs, "Cannot read pubring %s\n", ringfile);
573		return 0;
574	}
575	/* write secret key, and try to re-read it */
576	ringfile = netpgp_getvar(netpgp, "sec ring file");
577	fd = __ops_setup_file_append(&create, ringfile);
578	if (!__ops_write_xfer_seckey(create, keypair, NULL, 0, noarmor)) {
579		(void) fprintf(io->errs, "Cannot write seckey\n");
580		return 0;
581	}
582	__ops_teardown_file_write(create, fd);
583	__ops_keyring_free(netpgp->secring);
584	if (!__ops_keyring_fileread(netpgp->secring, noarmor, ringfile)) {
585		(void) fprintf(io->errs, "Can't read secring %s\n", ringfile);
586		return 0;
587	}
588	__ops_keydata_free(keypair);
589	return 1;
590}
591
592/* encrypt a file */
593int
594netpgp_encrypt_file(netpgp_t *netpgp,
595			const char *userid,
596			const char *f,
597			char *out,
598			int armored)
599{
600	const __ops_key_t	*keypair;
601	const unsigned		 overwrite = 1;
602	const char		*suffix;
603	__ops_io_t		*io;
604	char			 outname[MAXPATHLEN];
605
606	io = netpgp->io;
607	if (f == NULL) {
608		(void) fprintf(io->errs,
609			"netpgp_encrypt_file: no filename specified\n");
610		return 0;
611	}
612	if (userid == NULL) {
613		userid = netpgp_getvar(netpgp, "userid");
614	}
615	suffix = (armored) ? ".asc" : ".gpg";
616	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
617	if (keypair == NULL) {
618		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
619					userid);
620		return 0;
621	}
622	if (out == NULL) {
623		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
624		out = outname;
625	}
626	return (int)__ops_encrypt_file(io, f, out, keypair, (unsigned)armored,
627					overwrite);
628}
629
630/* decrypt a file */
631int
632netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored)
633{
634	const unsigned	overwrite = 1;
635	__ops_io_t		*io;
636
637	io = netpgp->io;
638	if (f == NULL) {
639		(void) fprintf(io->errs,
640			"netpgp_decrypt_file: no filename specified\n");
641		return 0;
642	}
643	return __ops_decrypt_file(netpgp->io, f, out, netpgp->secring,
644				(unsigned)armored, overwrite, netpgp->passfp,
645				get_passphrase_cb);
646}
647
648/* sign a file */
649int
650netpgp_sign_file(netpgp_t *netpgp,
651		const char *userid,
652		const char *f,
653		char *out,
654		int armored,
655		int cleartext,
656		int detached)
657{
658	const __ops_key_t	*keypair;
659	__ops_seckey_t		*seckey;
660	const unsigned		 overwrite = 1;
661	__ops_io_t		*io;
662	char			*hashalg;
663	int			 ret;
664
665	io = netpgp->io;
666	if (f == NULL) {
667		(void) fprintf(io->errs,
668			"netpgp_sign_file: no filename specified\n");
669		return 0;
670	}
671	if (userid == NULL) {
672		userid = netpgp_getvar(netpgp, "userid");
673	}
674	/* get key with which to sign */
675	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
676	if (keypair == NULL) {
677		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
678				userid);
679		return 0;
680	}
681	ret = 1;
682	do {
683		/* print out the user id */
684		__ops_print_keydata(io, keypair, "pub", &keypair->key.pubkey);
685		if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
686			/* now decrypt key */
687			seckey = __ops_decrypt_seckey(keypair);
688			if (seckey == NULL) {
689				(void) fprintf(io->errs, "Bad passphrase\n");
690			}
691		} else {
692			__ops_keyring_t	*secring;
693
694			secring = netpgp->secring;
695			seckey = &secring->keys[0].key.seckey;
696		}
697	} while (seckey == NULL);
698	/* sign file */
699	hashalg = netpgp_getvar(netpgp, "hash");
700	if (detached) {
701		ret = __ops_sign_detached(io, f, out, seckey, hashalg);
702	} else {
703		ret = __ops_sign_file(io, f, out, seckey, hashalg,
704				(unsigned)armored, (unsigned)cleartext, overwrite);
705	}
706	__ops_forget(seckey, sizeof(*seckey));
707	return ret;
708}
709
710/* verify a file */
711int
712netpgp_verify_file(netpgp_t *netpgp, const char *in, const char *out, int armored)
713{
714	__ops_validation_t	 result;
715	__ops_io_t		*io;
716
717	(void) memset(&result, 0x0, sizeof(result));
718	io = netpgp->io;
719	if (in == NULL) {
720		(void) fprintf(io->errs,
721			"netpgp_verify_file: no filename specified\n");
722		return 0;
723	}
724	if (__ops_validate_file(io, &result, in, out, armored,
725						netpgp->pubring)) {
726		resultp(io, in, &result, netpgp->pubring);
727		return 1;
728	}
729	if (result.validc + result.invalidc + result.unknownc == 0) {
730		(void) fprintf(io->errs,
731		"\"%s\": No signatures found - is this a signed file?\n",
732			in);
733	} else {
734		(void) fprintf(io->errs,
735"\"%s\": verification failure: %u invalid signatures, %u unknown signatures\n",
736			in, result.invalidc, result.unknownc);
737	}
738	return 0;
739}
740
741/* sign some memory */
742int
743netpgp_sign_memory(netpgp_t *netpgp,
744		const char *userid,
745		char *mem,
746		size_t size,
747		char *out,
748		size_t outsize,
749		const unsigned armored,
750		const unsigned cleartext)
751{
752	const __ops_key_t	*keypair;
753	__ops_seckey_t		*seckey;
754	__ops_memory_t		*signedmem;
755	__ops_io_t		*io;
756	char			*hashalg;
757	int			 ret;
758
759	io = netpgp->io;
760	if (mem == NULL) {
761		(void) fprintf(io->errs,
762			"netpgp_sign_memory: no memory to sign\n");
763		return 0;
764	}
765	if (userid == NULL) {
766		userid = netpgp_getvar(netpgp, "userid");
767	}
768	/* get key with which to sign */
769	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
770	if (keypair == NULL) {
771		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
772				userid);
773		return 0;
774	}
775	ret = 1;
776	do {
777		/* print out the user id */
778		__ops_print_keydata(io, keypair, "pub", &keypair->key.pubkey);
779		/* now decrypt key */
780		seckey = __ops_decrypt_seckey(keypair);
781		if (seckey == NULL) {
782			(void) fprintf(io->errs, "Bad passphrase\n");
783		}
784	} while (seckey == NULL);
785	/* sign file */
786	hashalg = netpgp_getvar(netpgp, "hash");
787	signedmem = __ops_sign_buf(io, mem, size, seckey, hashalg,
788						armored, cleartext);
789	if (signedmem) {
790		size_t	m;
791
792		m = MIN(__ops_mem_len(signedmem), outsize);
793		(void) memcpy(out, __ops_mem_data(signedmem), m);
794		__ops_memory_free(signedmem);
795	}
796	__ops_forget(seckey, sizeof(*seckey));
797	return ret;
798}
799
800/* verify memory */
801int
802netpgp_verify_memory(netpgp_t *netpgp, const void *in, const size_t size, const int armored)
803{
804	__ops_validation_t	 result;
805	__ops_memory_t		*signedmem;
806	__ops_io_t		*io;
807	int			 ret;
808
809	(void) memset(&result, 0x0, sizeof(result));
810	io = netpgp->io;
811	if (in == NULL) {
812		(void) fprintf(io->errs,
813			"netpgp_verify_memory: no memory to verify\n");
814		return 0;
815	}
816	signedmem = __ops_memory_new();
817	__ops_memory_add(signedmem, in, size);
818	ret = __ops_validate_mem(io, &result, signedmem, armored,
819						netpgp->pubring);
820	__ops_memory_free(signedmem);
821	if (ret) {
822		resultp(io, in, &result, netpgp->pubring);
823		return 1;
824	}
825	if (result.validc + result.invalidc + result.unknownc == 0) {
826		(void) fprintf(io->errs,
827		"No signatures found - is this memory signed?\n");
828	} else {
829		(void) fprintf(io->errs,
830"memory verification failure: %u invalid signatures, %u unknown signatures\n",
831			result.invalidc, result.unknownc);
832	}
833	return 0;
834}
835
836/* wrappers for the ops_debug_level functions we added to openpgpsdk */
837
838/* set the debugging level per filename */
839int
840netpgp_set_debug(const char *f)
841{
842	return __ops_set_debug_level(f);
843}
844
845/* get the debugging level per filename */
846int
847netpgp_get_debug(const char *f)
848{
849	return __ops_get_debug_level(f);
850}
851
852/* return the version for the library */
853const char *
854netpgp_get_info(const char *type)
855{
856	return __ops_get_info(type);
857}
858
859/* list all the packets in a file */
860int
861netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname)
862{
863	__ops_keyring_t	*keyring;
864	const unsigned	 noarmor = 0;
865	__ops_io_t	*io;
866	char		 ringname[MAXPATHLEN];
867	char		*homedir;
868	int		 ret;
869
870	io = netpgp->io;
871	if (f == NULL) {
872		(void) fprintf(io->errs, "No file containing packets\n");
873		return 0;
874	}
875	homedir = netpgp_getvar(netpgp, "homedir");
876	if (pubringname == NULL) {
877		(void) snprintf(ringname, sizeof(ringname),
878				"%s/pubring.gpg", homedir);
879		pubringname = ringname;
880	}
881	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
882		(void) fprintf(io->errs, "netpgp_list_packets: bad alloc\n");
883		return 0;
884	}
885	if (!__ops_keyring_fileread(keyring, noarmor, pubringname)) {
886		free(keyring);
887		(void) fprintf(io->errs, "Cannot read pub keyring %s\n",
888			pubringname);
889		return 0;
890	}
891	netpgp->pubring = keyring;
892	netpgp_setvar(netpgp, "pubring", pubringname);
893	ret = __ops_list_packets(io, f, (unsigned)armour, keyring,
894					netpgp->passfp,
895					get_passphrase_cb);
896	free(keyring);
897	return ret;
898}
899
900/* set a variable */
901int
902netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value)
903{
904	int	i;
905
906	if ((i = findvar(netpgp, name)) < 0) {
907		/* add the element to the array */
908		if (size_arrays(netpgp, netpgp->size + 15)) {
909			netpgp->name[i = netpgp->c++] = strdup(name);
910		}
911	} else {
912		/* replace the element in the array */
913		if (netpgp->value[i]) {
914			free(netpgp->value[i]);
915			netpgp->value[i] = NULL;
916		}
917	}
918	/* sanity checks for range of values */
919	if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) {
920		if (__ops_str_to_hash_alg(value) == OPS_HASH_UNKNOWN) {
921			return 0;
922		}
923	}
924	netpgp->value[i] = strdup(value);
925	return 1;
926}
927
928/* get a variable's value (NULL if not set) */
929char *
930netpgp_getvar(netpgp_t *netpgp, const char *name)
931{
932	int	i;
933
934	return ((i = findvar(netpgp, name)) < 0) ? NULL : netpgp->value[i];
935}
936