xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/netpgp.c (revision 36a7970f3ca969e60c2740bc8a6eebfb895840c0)
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.15 2009/05/21 00:33:31 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_OPENSSL_CAST_H
50 #include <openssl/cast.h>
51 #endif
52 
53 #ifdef HAVE_FCNTL_H
54 #include <fcntl.h>
55 #endif
56 
57 #include <regex.h>
58 #include <stdarg.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <time.h>
62 
63 #ifdef HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif
66 
67 #include <errno.h>
68 
69 #ifdef HAVE_LIMITS_H
70 #include <limits.h>
71 #endif
72 
73 #include <netpgp.h>
74 
75 #include "packet.h"
76 #include "packet-parse.h"
77 #include "keyring.h"
78 #include "errors.h"
79 #include "packet-show.h"
80 #include "create.h"
81 #include "netpgpsdk.h"
82 #include "memory.h"
83 #include "validate.h"
84 #include "readerwriter.h"
85 #include "netpgpdefs.h"
86 #include "crypto.h"
87 
88 enum {
89 	MAX_ID_LENGTH		= 128,
90 	MAX_PASSPHRASE_LENGTH	= 256
91 };
92 
93 /* read any gpg config file */
94 static int
95 conffile(netpgp_t *netpgp, char *homedir, char *userid, size_t length)
96 {
97 	regmatch_t	 matchv[10];
98 	regex_t		 keyre;
99 	char		 buf[BUFSIZ];
100 	FILE		*fp;
101 
102 	__OPS_USED(netpgp);
103 	(void) snprintf(buf, sizeof(buf), "%s/.gnupg/gpg.conf", homedir);
104 	if ((fp = fopen(buf, "r")) == NULL) {
105 		return 0;
106 	}
107 	(void) memset(&keyre, 0x0, sizeof(keyre));
108 	(void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)",
109 		REG_EXTENDED);
110 	while (fgets(buf, sizeof(buf), fp) != NULL) {
111 		if (regexec(&keyre, buf, 10, matchv, 0) == 0) {
112 			(void) memcpy(userid, &buf[(int)matchv[1].rm_so],
113 				MIN((unsigned)(matchv[1].rm_eo -
114 						matchv[1].rm_so), length));
115 			(void) fprintf(stderr,
116 				"netpgp: default key set to \"%.*s\"\n",
117 				(int)(matchv[1].rm_eo - matchv[1].rm_so),
118 				&buf[(int)matchv[1].rm_so]);
119 		}
120 	}
121 	(void) fclose(fp);
122 	return 1;
123 }
124 
125 /* wrapper to get a pass phrase from the user */
126 static void
127 get_pass_phrase(char *phrase, size_t size)
128 {
129 	char           *p;
130 
131 	while ((p = getpass("netpgp passphrase: ")) == NULL) {
132 	}
133 	(void) snprintf(phrase, size, "%s", p);
134 }
135 
136 /* small function to pretty print an 8-character raw userid */
137 static char    *
138 userid_to_id(const unsigned char *userid, char *id)
139 {
140 	static const char *hexes = "0123456789ABCDEF";
141 	int		   i;
142 
143 	for (i = 0; i < 8 ; i++) {
144 		id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4];
145 		id[(i * 2) + 1] = hexes[userid[i] & 0xf];
146 	}
147 	id[8 * 2] = 0x0;
148 	return id;
149 }
150 
151 /* print out the successful signature information */
152 static void
153 psuccess(FILE *fp, char *f, __ops_validation_t *res, __ops_keyring_t *pubring)
154 {
155 	const __ops_keydata_t	*pubkey;
156 	unsigned		 i;
157 	char			 id[MAX_ID_LENGTH + 1];
158 
159 	for (i = 0; i < res->validc; i++) {
160 		(void) fprintf(fp,
161 			"Good signature for %s made %susing %s key %s\n",
162 			f,
163 			ctime(&res->valid_sigs[i].birthtime),
164 			__ops_show_pka(res->valid_sigs[i].key_alg),
165 			userid_to_id(res->valid_sigs[i].signer_id, id));
166 		pubkey = __ops_keyring_find_key_by_id(pubring,
167 			(const unsigned char *) res->valid_sigs[i].signer_id);
168 		__ops_print_pubkeydata(fp, pubkey);
169 	}
170 }
171 
172 /***************************************************************************/
173 /* exported functions start here */
174 /***************************************************************************/
175 
176 /* initialise a netpgp_t structure */
177 int
178 netpgp_init(netpgp_t *netpgp, char *userid, char *fpubring, char *fsecring)
179 {
180 	__ops_keyring_t	*keyring;
181 	char		*homedir;
182 	char		 ringname[MAXPATHLEN];
183 	char		 id[MAX_ID_LENGTH];
184 
185 #ifdef HAVE_SYS_RESOURCE_H
186 	struct rlimit	limit;
187 
188 	(void) memset(&limit, 0x0, sizeof(limit));
189 	if (setrlimit(RLIMIT_CORE, &limit) != 0) {
190 		(void) fprintf(stderr,
191 			"netpgp_init: warning - can't turn off core dumps\n");
192 	}
193 #else
194 	(void) fprintf(stderr,
195 		"netpgp_init: warning - no way of switching off core dumps\n");
196 #endif
197 	homedir = getenv("HOME");
198 	if (userid == NULL) {
199 		(void) memset(id, 0x0, sizeof(id));
200 		conffile(netpgp, homedir, id, sizeof(id));
201 		if (id[0] != 0x0) {
202 			userid = id;
203 		}
204 	}
205 	if (userid == NULL) {
206 		(void) fprintf(stderr, "Cannot find user id\n");
207 		return 0;
208 	}
209 	(void) netpgp_setvar(netpgp, "userid", id);
210 	if (fpubring == NULL) {
211 		(void) snprintf(ringname, sizeof(ringname),
212 			"%s/.gnupg/pubring.gpg", homedir);
213 		fpubring = ringname;
214 	}
215 	keyring = calloc(1, sizeof(*keyring));
216 	if (!__ops_keyring_fileread(keyring, 0, fpubring)) {
217 		(void) fprintf(stderr, "Can't read pub keyring %s\n", fpubring);
218 		return 0;
219 	}
220 	netpgp->pubring = keyring;
221 	netpgp->pubringfile = strdup(fpubring);
222 	if (fsecring == NULL) {
223 		(void) snprintf(ringname, sizeof(ringname),
224 				"%s/.gnupg/secring.gpg", homedir);
225 		fsecring = ringname;
226 	}
227 	keyring = calloc(1, sizeof(*keyring));
228 	if (!__ops_keyring_fileread(keyring, 0, fsecring)) {
229 		(void) fprintf(stderr, "Can't read sec keyring %s\n", fsecring);
230 		return 0;
231 	}
232 	netpgp->secring = keyring;
233 	netpgp->secringfile = strdup(fsecring);
234 	netpgp->userid = strdup(userid);
235 	return 1;
236 }
237 
238 /* finish off with the netpgp_t struct */
239 int
240 netpgp_end(netpgp_t *netpgp)
241 {
242 	if (netpgp->pubring != NULL) {
243 		__ops_keyring_free(netpgp->pubring);
244 	}
245 	if (netpgp->pubringfile != NULL) {
246 		(void) free(netpgp->pubringfile);
247 	}
248 	if (netpgp->secring != NULL) {
249 		__ops_keyring_free(netpgp->secring);
250 	}
251 	if (netpgp->secringfile != NULL) {
252 		(void) free(netpgp->secringfile);
253 	}
254 	(void) free(netpgp->userid);
255 	return 1;
256 }
257 
258 /* list the keys in a keyring */
259 int
260 netpgp_list_keys(netpgp_t *netpgp)
261 {
262 	__ops_keyring_list(netpgp->pubring);
263 	return 1;
264 }
265 
266 /* find a key in a keyring */
267 int
268 netpgp_find_key(netpgp_t *netpgp, char *id)
269 {
270 	if (id == NULL) {
271 		(void) fprintf(stderr, "NULL id to search for\n");
272 		return 0;
273 	}
274 	return __ops_find_key_by_userid(netpgp->pubring, id) != NULL;
275 }
276 
277 /* export a given key */
278 int
279 netpgp_export_key(netpgp_t *netpgp, char *userid)
280 {
281 	const __ops_keydata_t	*keypair;
282 
283 	if (userid == NULL) {
284 		userid = netpgp->userid;
285 	}
286 	keypair = __ops_find_key_by_userid(netpgp->pubring, userid);
287 	if (keypair == NULL) {
288 		(void) fprintf(stderr,
289 			"Cannot find own key \"%s\" in keyring\n", userid);
290 		return 0;
291 	}
292 	__ops_export_key(keypair, NULL);
293 	return 1;
294 }
295 
296 /* import a key into our keyring */
297 int
298 netpgp_import_key(netpgp_t *netpgp, char *f)
299 {
300 	int	done;
301 
302 	if ((done = __ops_keyring_fileread(netpgp->pubring, 0, f)) == 0) {
303 		done = __ops_keyring_fileread(netpgp->pubring, 1, f);
304 	}
305 	if (!done) {
306 		(void) fprintf(stderr, "Cannot import key from file %s\n", f);
307 		return 0;
308 	}
309 	__ops_keyring_list(netpgp->pubring);
310 	return 1;
311 }
312 
313 /* generate a new key */
314 int
315 netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
316 {
317 	__ops_keydata_t		*keypair;
318 	__ops_userid_t		 uid;
319 	__ops_output_t		*create;
320 	int             	 fd;
321 
322 	(void) memset(&uid, 0x0, sizeof(uid));
323 	uid.userid = (unsigned char *) id;
324 	keypair = __ops_rsa_new_selfsign_key(numbits, 65537UL, &uid);
325 	if (keypair == NULL) {
326 		(void) fprintf(stderr, "Cannot generate key\n");
327 		return 0;
328 	}
329 	/* write public key */
330 	fd = __ops_setup_file_append(&create, netpgp->pubringfile);
331 	__ops_write_xfer_pubkey(create, keypair, 0);
332 	__ops_teardown_file_write(create, fd);
333 	__ops_keyring_free(netpgp->pubring);
334 	if (!__ops_keyring_fileread(netpgp->pubring, 0, netpgp->pubringfile)) {
335 		(void) fprintf(stderr, "Cannot read pubring %s\n",
336 				netpgp->pubringfile);
337 		return 0;
338 	}
339 	/* write secret key */
340 	fd = __ops_setup_file_append(&create, netpgp->secringfile);
341 	__ops_write_xfer_seckey(create, keypair, NULL, 0, 0);
342 	__ops_teardown_file_write(create, fd);
343 	__ops_keyring_free(netpgp->secring);
344 	if (!__ops_keyring_fileread(netpgp->secring, 0, netpgp->secringfile)) {
345 		(void) fprintf(stderr, "Can't read secring %s\n",
346 				netpgp->secringfile);
347 		return 0;
348 	}
349 	__ops_keydata_free(keypair);
350 	return 1;
351 }
352 
353 /* encrypt a file */
354 int
355 netpgp_encrypt_file(netpgp_t *netpgp, char *userid, char *f, char *out, int armored)
356 {
357 	const __ops_keydata_t	*keypair;
358 	const char		*suffix;
359 	char			 outname[MAXPATHLEN];
360 
361 	if (userid == NULL) {
362 		userid = netpgp->userid;
363 	}
364 	suffix = (armored) ? ".asc" : ".gpg";
365 	keypair = __ops_find_key_by_userid(netpgp->pubring, userid);
366 	if (keypair == NULL) {
367 		(void) fprintf(stderr, "Userid '%s' not found in keyring\n",
368 					userid);
369 		return 0;
370 	}
371 	if (out == NULL) {
372 		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
373 		out = outname;
374 	}
375 	return (int)__ops_encrypt_file(f, out, keypair, (unsigned)armored, 1U);
376 }
377 
378 /* decrypt a file */
379 int
380 netpgp_decrypt_file(netpgp_t *netpgp, char *f, char *out, int armored)
381 {
382 	return __ops_decrypt_file(f, out, netpgp->secring, (unsigned)armored,
383 		1U, get_passphrase_cb);
384 }
385 
386 /* sign a file */
387 int
388 netpgp_sign_file(netpgp_t *netpgp, char *userid, char *f, char *out,
389 		int armored, int cleartext, int detached)
390 {
391 	const __ops_keydata_t	*keypair;
392 	__ops_seckey_t		*seckey;
393 	char			*hashalg;
394 	char			 passphrase[MAX_PASSPHRASE_LENGTH];
395 
396 	if (userid == NULL) {
397 		userid = netpgp->userid;
398 	}
399 	/* get key with which to sign */
400 	keypair = __ops_find_key_by_userid(netpgp->secring, userid);
401 	if (keypair == NULL) {
402 		(void) fprintf(stderr, "Userid '%s' not found in keyring\n",
403 				userid);
404 		return 0;
405 	}
406 	do {
407 		/* print out the user id */
408 		__ops_print_pubkeydata(stderr, keypair);
409 		/* get the passphrase */
410 		get_pass_phrase(passphrase, sizeof(passphrase));
411 		/* now decrypt key */
412 		seckey = __ops_decrypt_seckey(keypair, passphrase);
413 		if (seckey == NULL) {
414 			(void) fprintf(stderr, "Bad passphrase\n");
415 		}
416 	} while (seckey == NULL);
417 	/* sign file */
418 	hashalg = netpgp_getvar(netpgp, "hash");
419 	if (cleartext) {
420 		__ops_sign_file_as_cleartext(f, out, seckey, hashalg, 1U);
421 	} else if (detached) {
422 		__ops_sign_detached(f, out, seckey, hashalg);
423 	} else {
424 		__ops_sign_file(f, out, seckey, hashalg, (unsigned)armored, 1);
425 	}
426 	(void) memset(passphrase, 0x0, sizeof(passphrase));
427 	__ops_seckey_forget(seckey);
428 	return 1;
429 }
430 
431 /* verify a file */
432 int
433 netpgp_verify_file(netpgp_t *netpgp, char *in, const char *out, int armored)
434 {
435 	__ops_validation_t	result;
436 
437 	(void) memset(&result, 0x0, sizeof(result));
438 	if (__ops_validate_file(&result, in, out, armored, netpgp->pubring)) {
439 		psuccess(stderr, in, &result, netpgp->pubring);
440 		return 1;
441 	}
442 	if (result.validc + result.invalidc + result.unknownc == 0) {
443 		(void) fprintf(stderr,
444 		"\"%s\": No signatures found - is this a signed file?\n",
445 			in);
446 	} else {
447 		(void) fprintf(stderr,
448 "\"%s\": verification failure: %d invalid signatures, %d unknown signatures\n",
449 			in, result.invalidc, result.unknownc);
450 	}
451 	return 0;
452 }
453 
454 /* wrappers for the ops_debug_level functions we added to openpgpsdk */
455 
456 /* set the debugging level per filename */
457 int
458 netpgp_set_debug(const char *f)
459 {
460 	return __ops_set_debug_level(f);
461 }
462 
463 /* get the debugging level per filename */
464 int
465 netpgp_get_debug(const char *f)
466 {
467 	return __ops_get_debug_level(f);
468 }
469 
470 /* return the version for the library */
471 const char *
472 netpgp_get_info(const char *type)
473 {
474 	return __ops_get_info(type);
475 }
476 
477 int
478 netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname)
479 {
480 	__ops_keyring_t	*keyring;
481 	char		 ringname[MAXPATHLEN];
482 	char		*homedir;
483 
484 	homedir = getenv("HOME");
485 	if (pubringname == NULL) {
486 		(void) snprintf(ringname, sizeof(ringname),
487 			"%s/.gnupg/pubring.gpg", homedir);
488 		pubringname = ringname;
489 	}
490 	keyring = calloc(1, sizeof(*keyring));
491 	if (!__ops_keyring_fileread(keyring, 0, pubringname)) {
492 		(void) fprintf(stderr, "Cannot read pub keyring %s\n",
493 			pubringname);
494 		return 0;
495 	}
496 	netpgp->pubring = keyring;
497 	netpgp->pubringfile = strdup(pubringname);
498 	__ops_list_packets(f, (unsigned)armour, keyring, get_passphrase_cb);
499 	return 1;
500 }
501 
502 /* set a variable */
503 int
504 netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value)
505 {
506 	if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) {
507 		if (netpgp->hashalg) {
508 			(void) free(netpgp->hashalg);
509 			netpgp->hashalg = NULL;
510 		}
511 		if (__ops_str_to_hash_alg(value) == OPS_HASH_UNKNOWN) {
512 			return 0;
513 		}
514 		netpgp->hashalg = strdup(value);
515 		return 1;
516 	}
517 	if (strcmp(name, "userid") == 0 || strcmp(name, "user") == 0) {
518 		if (netpgp->userid) {
519 			(void) free(netpgp->userid);
520 			netpgp->userid = NULL;
521 		}
522 		netpgp->userid = strdup(value);
523 		return 1;
524 	}
525 	if (strcmp(name, "verbose") == 0) {
526 		if (netpgp->verbose) {
527 			(void) free(netpgp->verbose);
528 			netpgp->verbose = NULL;
529 		}
530 		netpgp->verbose = strdup(value);
531 		return 1;
532 	}
533 	return 0;
534 }
535 
536 /* get a variable's value (NULL if not set) */
537 char *
538 netpgp_getvar(netpgp_t *netpgp, const char *name)
539 {
540 	if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) {
541 		return netpgp->hashalg;
542 	}
543 	if (strcmp(name, "userid") == 0 || strcmp(name, "user") == 0) {
544 		return netpgp->userid;
545 	}
546 	if (strcmp(name, "verbose") == 0) {
547 		return netpgp->verbose;
548 	}
549 	return NULL;
550 }
551