xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/netpgp.c (revision 3816d47b2c42fcd6e549e3407f842a5b1a1d23ad)
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.36 2009/12/22 06:55:03 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 */
87 static int
88 conffile(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 */
119 static char    *
120 userid_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 */
134 static void
135 resultp(__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) ? f : "<stdin>",
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 */
161 static int
162 size_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 */
198 static int
199 findvar(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 */
209 static void *
210 readkeyring(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 */
237 static int
238 readsshkeys(netpgp_t *netpgp, char *homedir)
239 {
240 	__ops_keyring_t	*pubring;
241 	__ops_keyring_t	*secring;
242 	char		 f[MAXPATHLEN];
243 	char		*filename;
244 
245 	if ((filename = netpgp_getvar(netpgp, "sshkeyfile")) == NULL) {
246 		(void) snprintf(f, sizeof(f), "%s/.ssh/is_rsa.pub", homedir);
247 		filename = f;
248 	}
249 	if ((pubring = calloc(1, sizeof(*pubring))) == NULL) {
250 		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
251 		return 0;
252 	}
253 	if (!__ops_ssh2_readkeys(netpgp->io, pubring, NULL, filename, NULL)) {
254 		free(pubring);
255 		(void) fprintf(stderr, "readsshkeys: can't read %s\n",
256 				filename);
257 		return 0;
258 	}
259 	netpgp->pubring = pubring;
260 	netpgp_setvar(netpgp, "sshpubfile", filename);
261 	/* try to take the ".pub" off the end */
262 	if (filename == f) {
263 		f[strlen(f) - 4] = 0x0;
264 	} else {
265 		(void) snprintf(f, sizeof(f), "%.*s",
266 				(int)strlen(filename) - 4, filename);
267 		filename = f;
268 	}
269 	if ((secring = calloc(1, sizeof(*secring))) == NULL) {
270 		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
271 		return 0;
272 	}
273 	if (__ops_ssh2_readkeys(netpgp->io, pubring, secring, NULL, filename)) {
274 		netpgp->secring = secring;
275 		netpgp_setvar(netpgp, "sshsecfile", filename);
276 	} else {
277 		(void) fprintf(stderr, "readsshkeys: can't read sec %s (%d)\n",
278 				filename, errno);
279 	}
280 	return 1;
281 }
282 
283 /* set ssh uid to first one in ring */
284 static void
285 set_ssh_userid(__ops_keyring_t *pubring, char *id, size_t len)
286 {
287 	unsigned char	*src;
288 	int		 i;
289 	int		 n;
290 
291 	(void) memset(id, 0x0, len);
292 	src = pubring->keys[0].key_id;
293 	for (i = 0, n = 0 ; i < OPS_KEY_ID_SIZE ; i += 2) {
294 		n += snprintf(&id[n], len - n, "%02x%02x", src[i], src[i + 1]);
295 	}
296 	id[n] = 0x0;
297 }
298 
299 /***************************************************************************/
300 /* exported functions start here */
301 /***************************************************************************/
302 
303 /* initialise a netpgp_t structure */
304 int
305 netpgp_init(netpgp_t *netpgp)
306 {
307 	__ops_io_t	*io;
308 	char		 id[MAX_ID_LENGTH];
309 	char		*homedir;
310 	char		*userid;
311 	char		*stream;
312 	char		*passfd;
313 	char		*results;
314 	int		 coredumps;
315 
316 #ifdef HAVE_SYS_RESOURCE_H
317 	struct rlimit	limit;
318 
319 	coredumps = netpgp_getvar(netpgp, "coredumps") != NULL;
320 	if (!coredumps) {
321 		(void) memset(&limit, 0x0, sizeof(limit));
322 		if (setrlimit(RLIMIT_CORE, &limit) != 0) {
323 			(void) fprintf(stderr,
324 			"netpgp_init: warning - can't turn off core dumps\n");
325 			coredumps = 1;
326 		}
327 	}
328 #else
329 	coredumps = 1;
330 #endif
331 	if ((io = calloc(1, sizeof(*io))) == NULL) {
332 		(void) fprintf(stderr, "netpgp_init: bad alloc\n");
333 		return 0;
334 	}
335 	io->outs = stdout;
336 	if ((stream = netpgp_getvar(netpgp, "stdout")) != NULL &&
337 	    strcmp(stream, "stderr") == 0) {
338 		io->outs = stderr;
339 	}
340 	io->errs = stderr;
341 	if ((stream = netpgp_getvar(netpgp, "stderr")) != NULL &&
342 	    strcmp(stream, "stdout") == 0) {
343 		io->errs = stdout;
344 	}
345 	if ((results = netpgp_getvar(netpgp, "results")) == NULL) {
346 		io->res = io->errs;
347 	} else if ((io->res = fopen(results, "w")) == NULL) {
348 		(void) fprintf(io->errs, "Can't open results %s for writing\n",
349 			results);
350 		return 0;
351 	}
352 	netpgp->io = io;
353 	if (coredumps) {
354 		(void) fprintf(io->errs,
355 			"netpgp: warning: core dumps enabled\n");
356 	}
357 	if ((homedir = netpgp_getvar(netpgp, "homedir")) == NULL) {
358 		(void) fprintf(io->errs, "netpgp: bad homedir\n");
359 		return 0;
360 	}
361 	/* read from either gpg files or ssh keys */
362 	if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
363 		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
364 			(void) memset(id, 0x0, sizeof(id));
365 			(void) conffile(netpgp, homedir, id, sizeof(id));
366 			if (id[0] != 0x0) {
367 				netpgp_setvar(netpgp, "userid", userid = id);
368 			}
369 		}
370 		if (userid == NULL) {
371 			if (netpgp_getvar(netpgp, "need userid") != NULL) {
372 				(void) fprintf(io->errs,
373 						"Cannot find user id\n");
374 				return 0;
375 			}
376 		} else {
377 			(void) netpgp_setvar(netpgp, "userid", userid);
378 		}
379 		netpgp->pubring = readkeyring(netpgp, "pubring");
380 		if (netpgp->pubring == NULL) {
381 			(void) fprintf(io->errs, "Can't read pub keyring\n");
382 			return 0;
383 		}
384 		netpgp->secring = readkeyring(netpgp, "secring");
385 		if (netpgp->secring == NULL) {
386 			(void) fprintf(io->errs, "Can't read sec keyring\n");
387 			return 0;
388 		}
389 	} else {
390 		if (!readsshkeys(netpgp, homedir)) {
391 			(void) fprintf(io->errs, "Can't read ssh pub key\n");
392 			return 0;
393 		}
394 		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
395 			set_ssh_userid(netpgp->pubring, id, sizeof(id));
396 			netpgp_setvar(netpgp, "userid", userid = id);
397 		}
398 		if (userid == NULL) {
399 			if (netpgp_getvar(netpgp, "need userid") != NULL) {
400 				(void) fprintf(io->errs,
401 						"Cannot find user id\n");
402 				return 0;
403 			}
404 		} else {
405 			(void) netpgp_setvar(netpgp, "userid", userid);
406 		}
407 	}
408 	if ((passfd = netpgp_getvar(netpgp, "pass-fd")) != NULL &&
409 	    (netpgp->passfp = fdopen(atoi(passfd), "r")) == NULL) {
410 		(void) fprintf(io->errs, "Can't open fd %s for reading\n",
411 			passfd);
412 		return 0;
413 	}
414 	return 1;
415 }
416 
417 /* finish off with the netpgp_t struct */
418 int
419 netpgp_end(netpgp_t *netpgp)
420 {
421 	unsigned	i;
422 
423 	for (i = 0 ; i < netpgp->c ; i++) {
424 		if (netpgp->name[i] != NULL) {
425 			free(netpgp->name[i]);
426 		}
427 		if (netpgp->value[i] != NULL) {
428 			free(netpgp->value[i]);
429 		}
430 	}
431 	if (netpgp->name != NULL) {
432 		free(netpgp->name);
433 	}
434 	if (netpgp->value != NULL) {
435 		free(netpgp->value);
436 	}
437 	if (netpgp->pubring != NULL) {
438 		__ops_keyring_free(netpgp->pubring);
439 	}
440 	if (netpgp->secring != NULL) {
441 		__ops_keyring_free(netpgp->secring);
442 	}
443 	free(netpgp->io);
444 	return 1;
445 }
446 
447 /* list the keys in a keyring */
448 int
449 netpgp_list_keys(netpgp_t *netpgp)
450 {
451 	return __ops_keyring_list(netpgp->io, netpgp->pubring);
452 }
453 
454 /* find and list some keys in a keyring */
455 int
456 netpgp_match_list_keys(netpgp_t *netpgp, char *name)
457 {
458 	const __ops_key_t	*key;
459 	unsigned		 found;
460 	unsigned		 k;
461 	char			*data;
462 
463 	found = k = 0;
464 	do {
465 		key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring,
466 						name, &k);
467 		if (key != NULL) {
468 			__ops_sprint_keydata(key, &data, "pub",
469 						&key->key.pubkey);
470 			printf("%s\n", data);
471 			free(data);
472 			found += 1;
473 			k += 1;
474 		}
475 	} while (key != NULL);
476 	printf("Found %u key%s\n", found, (found == 1) ? "" : "s");
477 	return (found > 0);
478 }
479 
480 /* find a key in a keyring */
481 int
482 netpgp_find_key(netpgp_t *netpgp, char *id)
483 {
484 	__ops_io_t	*io;
485 
486 	io = netpgp->io;
487 	if (id == NULL) {
488 		(void) fprintf(io->errs, "NULL id to search for\n");
489 		return 0;
490 	}
491 	return __ops_getkeybyname(netpgp->io, netpgp->pubring, id) != NULL;
492 }
493 
494 /* get a key in a keyring */
495 char *
496 netpgp_get_key(netpgp_t *netpgp, const char *id)
497 {
498 	const __ops_key_t	*key;
499 	__ops_io_t		*io;
500 	char			*newkey;
501 
502 	io = netpgp->io;
503 	if (id == NULL) {
504 		(void) fprintf(io->errs, "NULL id to search for\n");
505 		return NULL;
506 	}
507 	key = __ops_getkeybyname(netpgp->io, netpgp->pubring, id);
508 	if (key == NULL) {
509 		(void) fprintf(io->errs, "Can't find key '%s'\n", id);
510 		return NULL;
511 	}
512 	return (__ops_sprint_keydata(key, &newkey, "pub",
513 				&key->key.pubkey) > 0) ? newkey : NULL;
514 }
515 
516 /* export a given key */
517 int
518 netpgp_export_key(netpgp_t *netpgp, char *userid)
519 {
520 	const __ops_key_t	*keypair;
521 	__ops_io_t		*io;
522 
523 	io = netpgp->io;
524 	if (userid == NULL) {
525 		userid = netpgp_getvar(netpgp, "userid");
526 	}
527 	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
528 	if (keypair == NULL) {
529 		(void) fprintf(io->errs,
530 			"Cannot find own key \"%s\" in keyring\n", userid);
531 		return 0;
532 	}
533 	return __ops_export_key(keypair, NULL);
534 }
535 
536 /* import a key into our keyring */
537 int
538 netpgp_import_key(netpgp_t *netpgp, char *f)
539 {
540 	const unsigned	noarmor = 0;
541 	const unsigned	armor = 1;
542 	__ops_io_t	*io;
543 	int		done;
544 
545 	io = netpgp->io;
546 	if ((done = __ops_keyring_fileread(netpgp->pubring, noarmor, f)) == 0) {
547 		done = __ops_keyring_fileread(netpgp->pubring, armor, f);
548 	}
549 	if (!done) {
550 		(void) fprintf(io->errs, "Cannot import key from file %s\n",
551 				f);
552 		return 0;
553 	}
554 	return __ops_keyring_list(io, netpgp->pubring);
555 }
556 
557 /* generate a new key */
558 int
559 netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
560 {
561 	__ops_key_t		*keypair;
562 	__ops_userid_t		 uid;
563 	__ops_output_t		*create;
564 	const unsigned		 noarmor = 0;
565 	__ops_io_t		*io;
566 	char			*ringfile;
567 	int             	 fd;
568 
569 	(void) memset(&uid, 0x0, sizeof(uid));
570 	io = netpgp->io;
571 	/* generate a new key for 'id' */
572 	uid.userid = (unsigned char *) id;
573 	keypair = __ops_rsa_new_selfsign_key(numbits, 65537UL, &uid);
574 	if (keypair == NULL) {
575 		(void) fprintf(io->errs, "Cannot generate key\n");
576 		return 0;
577 	}
578 	/* write public key, and try to re-read it */
579 	ringfile = netpgp_getvar(netpgp, "pubring");
580 	fd = __ops_setup_file_append(&create, ringfile);
581 	if (!__ops_write_xfer_pubkey(create, keypair, noarmor)) {
582 		(void) fprintf(io->errs, "Cannot write pubkey\n");
583 		return 0;
584 	}
585 	__ops_teardown_file_write(create, fd);
586 	__ops_keyring_free(netpgp->pubring);
587 	if (!__ops_keyring_fileread(netpgp->pubring, noarmor, ringfile)) {
588 		(void) fprintf(io->errs, "Cannot read pubring %s\n", ringfile);
589 		return 0;
590 	}
591 	/* write secret key, and try to re-read it */
592 	ringfile = netpgp_getvar(netpgp, "sec ring file");
593 	fd = __ops_setup_file_append(&create, ringfile);
594 	if (!__ops_write_xfer_seckey(create, keypair, NULL, 0, noarmor)) {
595 		(void) fprintf(io->errs, "Cannot write seckey\n");
596 		return 0;
597 	}
598 	__ops_teardown_file_write(create, fd);
599 	__ops_keyring_free(netpgp->secring);
600 	if (!__ops_keyring_fileread(netpgp->secring, noarmor, ringfile)) {
601 		(void) fprintf(io->errs, "Can't read secring %s\n", ringfile);
602 		return 0;
603 	}
604 	__ops_keydata_free(keypair);
605 	return 1;
606 }
607 
608 /* encrypt a file */
609 int
610 netpgp_encrypt_file(netpgp_t *netpgp,
611 			const char *userid,
612 			const char *f,
613 			char *out,
614 			int armored)
615 {
616 	const __ops_key_t	*keypair;
617 	const unsigned		 overwrite = 1;
618 	const char		*suffix;
619 	__ops_io_t		*io;
620 	char			 outname[MAXPATHLEN];
621 
622 	io = netpgp->io;
623 	if (f == NULL) {
624 		(void) fprintf(io->errs,
625 			"netpgp_encrypt_file: no filename specified\n");
626 		return 0;
627 	}
628 	if (userid == NULL) {
629 		userid = netpgp_getvar(netpgp, "userid");
630 	}
631 	suffix = (armored) ? ".asc" : ".gpg";
632 	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
633 	if (keypair == NULL) {
634 		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
635 					userid);
636 		return 0;
637 	}
638 	if (out == NULL) {
639 		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
640 		out = outname;
641 	}
642 	return (int)__ops_encrypt_file(io, f, out, keypair, (unsigned)armored,
643 					overwrite);
644 }
645 
646 #define ARMOR_HEAD	"-----BEGIN PGP MESSAGE-----\r\n"
647 
648 /* decrypt a file */
649 int
650 netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored)
651 {
652 	const unsigned	 overwrite = 1;
653 	__ops_io_t	*io;
654 	unsigned	 realarmour;
655 	FILE		*fp;
656 	char		 buf[BUFSIZ];
657 
658 	io = netpgp->io;
659 	if (f == NULL) {
660 		(void) fprintf(io->errs,
661 			"netpgp_decrypt_file: no filename specified\n");
662 		return 0;
663 	}
664 	realarmour = (unsigned)armored;
665 	if ((fp = fopen(f, "r")) == NULL) {
666 		(void) fprintf(io->errs,
667 			"netpgp_decrypt_file: can't open '%s'\n", f);
668 		return 0;
669 	}
670 	if (fgets(buf, sizeof(buf), fp) == NULL) {
671 		realarmour = 0;
672 	} else {
673 		realarmour = (strcmp(buf, ARMOR_HEAD) == 0);
674 	}
675 	(void) fclose(fp);
676 	return __ops_decrypt_file(netpgp->io, f, out, netpgp->secring,
677 				(unsigned)realarmour, overwrite,
678 				netpgp->passfp, get_passphrase_cb);
679 }
680 
681 /* sign a file */
682 int
683 netpgp_sign_file(netpgp_t *netpgp,
684 		const char *userid,
685 		const char *f,
686 		char *out,
687 		int armored,
688 		int cleartext,
689 		int detached)
690 {
691 	const __ops_key_t	*keypair;
692 	__ops_seckey_t		*seckey;
693 	const unsigned		 overwrite = 1;
694 	__ops_io_t		*io;
695 	char			*hashalg;
696 	int			 ret;
697 
698 	io = netpgp->io;
699 	if (f == NULL) {
700 		(void) fprintf(io->errs,
701 			"netpgp_sign_file: no filename specified\n");
702 		return 0;
703 	}
704 	if (userid == NULL) {
705 		userid = netpgp_getvar(netpgp, "userid");
706 	}
707 	/* get key with which to sign */
708 	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
709 	if (keypair == NULL) {
710 		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
711 				userid);
712 		return 0;
713 	}
714 	ret = 1;
715 	do {
716 		/* print out the user id */
717 		__ops_print_keydata(io, keypair, "pub", &keypair->key.pubkey);
718 		if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
719 			/* now decrypt key */
720 			seckey = __ops_decrypt_seckey(keypair);
721 			if (seckey == NULL) {
722 				(void) fprintf(io->errs, "Bad passphrase\n");
723 			}
724 		} else {
725 			__ops_keyring_t	*secring;
726 
727 			secring = netpgp->secring;
728 			seckey = &secring->keys[0].key.seckey;
729 		}
730 	} while (seckey == NULL);
731 	/* sign file */
732 	hashalg = netpgp_getvar(netpgp, "hash");
733 	if (detached) {
734 		ret = __ops_sign_detached(io, f, out, seckey, hashalg);
735 	} else {
736 		ret = __ops_sign_file(io, f, out, seckey, hashalg,
737 				(unsigned)armored, (unsigned)cleartext,
738 				overwrite);
739 	}
740 	__ops_forget(seckey, sizeof(*seckey));
741 	return ret;
742 }
743 
744 /* verify a file */
745 int
746 netpgp_verify_file(netpgp_t *netpgp, const char *in, const char *out, int armored)
747 {
748 	__ops_validation_t	 result;
749 	__ops_io_t		*io;
750 
751 	(void) memset(&result, 0x0, sizeof(result));
752 	io = netpgp->io;
753 	if (in == NULL) {
754 		(void) fprintf(io->errs,
755 			"netpgp_verify_file: no filename specified\n");
756 		return 0;
757 	}
758 	if (__ops_validate_file(io, &result, in, out, armored,
759 						netpgp->pubring)) {
760 		resultp(io, in, &result, netpgp->pubring);
761 		return 1;
762 	}
763 	if (result.validc + result.invalidc + result.unknownc == 0) {
764 		(void) fprintf(io->errs,
765 		"\"%s\": No signatures found - is this a signed file?\n",
766 			in);
767 	} else {
768 		(void) fprintf(io->errs,
769 "\"%s\": verification failure: %u invalid signatures, %u unknown signatures\n",
770 			in, result.invalidc, result.unknownc);
771 	}
772 	return 0;
773 }
774 
775 /* sign some memory */
776 int
777 netpgp_sign_memory(netpgp_t *netpgp,
778 		const char *userid,
779 		char *mem,
780 		size_t size,
781 		char *out,
782 		size_t outsize,
783 		const unsigned armored,
784 		const unsigned cleartext)
785 {
786 	const __ops_key_t	*keypair;
787 	__ops_seckey_t		*seckey;
788 	__ops_memory_t		*signedmem;
789 	__ops_io_t		*io;
790 	char			*hashalg;
791 	int			 ret;
792 
793 	io = netpgp->io;
794 	if (mem == NULL) {
795 		(void) fprintf(io->errs,
796 			"netpgp_sign_memory: no memory to sign\n");
797 		return 0;
798 	}
799 	if (userid == NULL) {
800 		userid = netpgp_getvar(netpgp, "userid");
801 	}
802 	/* get key with which to sign */
803 	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
804 	if (keypair == NULL) {
805 		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
806 				userid);
807 		return 0;
808 	}
809 	ret = 1;
810 	do {
811 		/* print out the user id */
812 		__ops_print_keydata(io, keypair, "pub", &keypair->key.pubkey);
813 		/* now decrypt key */
814 		seckey = __ops_decrypt_seckey(keypair);
815 		if (seckey == NULL) {
816 			(void) fprintf(io->errs, "Bad passphrase\n");
817 		}
818 	} while (seckey == NULL);
819 	/* sign file */
820 	hashalg = netpgp_getvar(netpgp, "hash");
821 	signedmem = __ops_sign_buf(io, mem, size, seckey, hashalg,
822 						armored, cleartext);
823 	if (signedmem) {
824 		size_t	m;
825 
826 		m = MIN(__ops_mem_len(signedmem), outsize);
827 		(void) memcpy(out, __ops_mem_data(signedmem), m);
828 		__ops_memory_free(signedmem);
829 		ret = (int)m;
830 	} else {
831 		ret = 0;
832 	}
833 	__ops_forget(seckey, sizeof(*seckey));
834 	return ret;
835 }
836 
837 /* verify memory */
838 int
839 netpgp_verify_memory(netpgp_t *netpgp, const void *in, const size_t size,
840 			void *out, size_t outsize, const int armored)
841 {
842 	__ops_validation_t	 result;
843 	__ops_memory_t		*signedmem;
844 	__ops_memory_t		*cat;
845 	__ops_io_t		*io;
846 	size_t			 m;
847 	int			 ret;
848 
849 	(void) memset(&result, 0x0, sizeof(result));
850 	io = netpgp->io;
851 	if (in == NULL) {
852 		(void) fprintf(io->errs,
853 			"netpgp_verify_memory: no memory to verify\n");
854 		return 0;
855 	}
856 	signedmem = __ops_memory_new();
857 	__ops_memory_add(signedmem, in, size);
858 	ret = __ops_validate_mem(io, &result, signedmem,
859 				(out) ? &cat : NULL,
860 				armored, netpgp->pubring);
861 	__ops_memory_free(signedmem);
862 	if (ret) {
863 		resultp(io, "<stdin>", &result, netpgp->pubring);
864 		if (out) {
865 			m = MIN(__ops_mem_len(cat), outsize);
866 			(void) memcpy(out, __ops_mem_data(cat), m);
867 			__ops_memory_free(cat);
868 		} else {
869 			m = 1;
870 		}
871 		return (int)m;
872 	}
873 	if (result.validc + result.invalidc + result.unknownc == 0) {
874 		(void) fprintf(io->errs,
875 		"No signatures found - is this memory signed?\n");
876 	} else {
877 		(void) fprintf(io->errs,
878 "memory verification failure: %u invalid signatures, %u unknown signatures\n",
879 			result.invalidc, result.unknownc);
880 	}
881 	return 0;
882 }
883 
884 /* encrypt some memory */
885 int
886 netpgp_encrypt_memory(netpgp_t *netpgp,
887 			const char *userid,
888 			void *in,
889 			const size_t insize,
890 			char *out,
891 			size_t outsize,
892 			int armored)
893 {
894 	const __ops_key_t	*keypair;
895 	__ops_memory_t		*enc;
896 	__ops_io_t		*io;
897 	size_t			 m;
898 
899 	io = netpgp->io;
900 	if (in == NULL) {
901 		(void) fprintf(io->errs,
902 			"netpgp_encrypt_buf: no memory to encrypt\n");
903 		return 0;
904 	}
905 	if (userid == NULL) {
906 		userid = netpgp_getvar(netpgp, "userid");
907 	}
908 	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
909 	if (keypair == NULL) {
910 		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
911 					userid);
912 		return 0;
913 	}
914 	if (in == out) {
915 		(void) fprintf(io->errs,
916 			"netpgp_encrypt_buf: input and output bufs need to be different\n");
917 		return 0;
918 	}
919 	if (outsize < insize) {
920 		(void) fprintf(io->errs,
921 			"netpgp_encrypt_buf: input size is larger than output size\n");
922 		return 0;
923 	}
924 	enc = __ops_encrypt_buf(io, in, insize, keypair, (unsigned)armored);
925 	m = MIN(__ops_mem_len(enc), outsize);
926 	(void) memcpy(out, __ops_mem_data(enc), m);
927 	__ops_memory_free(enc);
928 	return (int)m;
929 }
930 
931 /* decrypt a chunk of memory */
932 int
933 netpgp_decrypt_memory(netpgp_t *netpgp, const void *input, const size_t insize,
934 			char *out, size_t outsize, const int armored)
935 {
936 	__ops_memory_t	*mem;
937 	__ops_io_t	*io;
938 	unsigned	 realarmour;
939 	size_t		 m;
940 
941 	io = netpgp->io;
942 	realarmour = (unsigned) armored;
943 	if (input == NULL) {
944 		(void) fprintf(io->errs,
945 			"netpgp_decrypt_memory: no memory\n");
946 		return 0;
947 	}
948 	realarmour = (strncmp(input, ARMOR_HEAD, sizeof(ARMOR_HEAD) - 1) == 0);
949 	mem = __ops_decrypt_buf(netpgp->io, input, insize, netpgp->secring,
950 				realarmour, netpgp->passfp,
951 				get_passphrase_cb);
952 	m = MIN(__ops_mem_len(mem), outsize);
953 	(void) memcpy(out, __ops_mem_data(mem), m);
954 	__ops_memory_free(mem);
955 	return (int)m;
956 }
957 
958 /* wrappers for the ops_debug_level functions we added to openpgpsdk */
959 
960 /* set the debugging level per filename */
961 int
962 netpgp_set_debug(const char *f)
963 {
964 	return __ops_set_debug_level(f);
965 }
966 
967 /* get the debugging level per filename */
968 int
969 netpgp_get_debug(const char *f)
970 {
971 	return __ops_get_debug_level(f);
972 }
973 
974 /* return the version for the library */
975 const char *
976 netpgp_get_info(const char *type)
977 {
978 	return __ops_get_info(type);
979 }
980 
981 /* list all the packets in a file */
982 int
983 netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname)
984 {
985 	__ops_keyring_t	*keyring;
986 	const unsigned	 noarmor = 0;
987 	__ops_io_t	*io;
988 	char		 ringname[MAXPATHLEN];
989 	char		*homedir;
990 	int		 ret;
991 
992 	io = netpgp->io;
993 	if (f == NULL) {
994 		(void) fprintf(io->errs, "No file containing packets\n");
995 		return 0;
996 	}
997 	homedir = netpgp_getvar(netpgp, "homedir");
998 	if (pubringname == NULL) {
999 		(void) snprintf(ringname, sizeof(ringname),
1000 				"%s/pubring.gpg", homedir);
1001 		pubringname = ringname;
1002 	}
1003 	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
1004 		(void) fprintf(io->errs, "netpgp_list_packets: bad alloc\n");
1005 		return 0;
1006 	}
1007 	if (!__ops_keyring_fileread(keyring, noarmor, pubringname)) {
1008 		free(keyring);
1009 		(void) fprintf(io->errs, "Cannot read pub keyring %s\n",
1010 			pubringname);
1011 		return 0;
1012 	}
1013 	netpgp->pubring = keyring;
1014 	netpgp_setvar(netpgp, "pubring", pubringname);
1015 	ret = __ops_list_packets(io, f, (unsigned)armour, keyring,
1016 					netpgp->passfp,
1017 					get_passphrase_cb);
1018 	free(keyring);
1019 	return ret;
1020 }
1021 
1022 /* set a variable */
1023 int
1024 netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value)
1025 {
1026 	int	i;
1027 
1028 	if ((i = findvar(netpgp, name)) < 0) {
1029 		/* add the element to the array */
1030 		if (size_arrays(netpgp, netpgp->size + 15)) {
1031 			netpgp->name[i = netpgp->c++] = strdup(name);
1032 		}
1033 	} else {
1034 		/* replace the element in the array */
1035 		if (netpgp->value[i]) {
1036 			free(netpgp->value[i]);
1037 			netpgp->value[i] = NULL;
1038 		}
1039 	}
1040 	/* sanity checks for range of values */
1041 	if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) {
1042 		if (__ops_str_to_hash_alg(value) == OPS_HASH_UNKNOWN) {
1043 			return 0;
1044 		}
1045 	}
1046 	netpgp->value[i] = strdup(value);
1047 	return 1;
1048 }
1049 
1050 /* get a variable's value (NULL if not set) */
1051 char *
1052 netpgp_getvar(netpgp_t *netpgp, const char *name)
1053 {
1054 	int	i;
1055 
1056 	return ((i = findvar(netpgp, name)) < 0) ? NULL : netpgp->value[i];
1057 }
1058 
1059 /* increment a value */
1060 int
1061 netpgp_incvar(netpgp_t *netpgp, const char *name, const int delta)
1062 {
1063 	char	*cp;
1064 	char	 num[16];
1065 	int	 val;
1066 
1067 	val = 0;
1068 	if ((cp = netpgp_getvar(netpgp, name)) != NULL) {
1069 		val = atoi(cp);
1070 	}
1071 	(void) snprintf(num, sizeof(num), "%d", val + delta);
1072 	netpgp_setvar(netpgp, name, num);
1073 	return 1;
1074 }
1075 
1076 /* set the home directory value to "home/subdir" */
1077 int
1078 netpgp_set_homedir(netpgp_t *netpgp, char *home, const char *subdir, const int quiet)
1079 {
1080 	struct stat	st;
1081 	char		d[MAXPATHLEN];
1082 
1083 	if (home == NULL) {
1084 		if (!quiet) {
1085 			(void) fprintf(stderr, "NULL HOME directory\n");
1086 		}
1087 		return 0;
1088 	}
1089 	(void) snprintf(d, sizeof(d), "%s%s", home, (subdir) ? subdir : "");
1090 	if (stat(d, &st) == 0) {
1091 		if ((st.st_mode & S_IFMT) == S_IFDIR) {
1092 			netpgp_setvar(netpgp, "homedir", d);
1093 			return 1;
1094 		}
1095 		(void) fprintf(stderr, "netpgp: homedir \"%s\" is not a dir\n",
1096 					d);
1097 		return 0;
1098 	}
1099 	if (!quiet) {
1100 		(void) fprintf(stderr,
1101 			"netpgp: warning homedir \"%s\" not found\n", d);
1102 	}
1103 	return 1;
1104 }
1105 
1106