netpgp.c revision 1.4
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#include <sys/types.h>
32#include <sys/param.h>
33
34#ifdef HAVE_OPENSSL_CAST_H
35#include <openssl/cast.h>
36#endif
37
38#include <netpgp.h>
39
40#include "packet.h"
41#include "packet-parse.h"
42#include "keyring.h"
43#include "errors.h"
44#include "packet-show.h"
45#include "create.h"
46#include "netpgpsdk.h"
47#include "memory.h"
48#include "validate.h"
49#include "readerwriter.h"
50#include "netpgpdefs.h"
51#include "parse_local.h"
52
53#ifdef HAVE_ASSERT_H
54#include <assert.h>
55#endif
56
57#include <regex.h>
58#include <stdarg.h>
59#include <stdlib.h>
60#include <string.h>
61
62#ifdef HAVE_UNISTD_H
63#include <unistd.h>
64#endif
65
66#include <errno.h>
67
68#ifdef HAVE_LIMITS_H
69#include <limits.h>
70#endif
71
72enum {
73	MAX_ID_LENGTH		= 128,
74	MAX_PASSPHRASE_LENGTH	= 256
75};
76
77/* read any gpg config file */
78static int
79conffile(char *homedir, char *userid, size_t length, int verbose)
80{
81	regmatch_t	 matchv[10];
82	regex_t		 r;
83	char		 buf[BUFSIZ];
84	FILE		*fp;
85
86	(void) snprintf(buf, sizeof(buf), "%s/.gnupg/gpg.conf", homedir);
87	if ((fp = fopen(buf, "r")) == NULL) {
88		return 0;
89	}
90	(void) regcomp(&r, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)",
91		REG_EXTENDED);
92	while (fgets(buf, sizeof(buf), fp) != NULL) {
93		if (regexec(&r, buf, 10, matchv, 0) == 0) {
94			(void) memcpy(userid, &buf[(int)matchv[1].rm_so],
95				MIN((unsigned)(matchv[1].rm_eo - matchv[1].rm_so), length));
96			if (verbose) {
97				printf("setting default key to \"%.*s\"\n",
98					(int)(matchv[1].rm_eo - matchv[1].rm_so),
99					&buf[(int)matchv[1].rm_so]);
100			}
101		}
102	}
103	(void) fclose(fp);
104	return 1;
105}
106
107/* wrapper to get a pass phrase from the user */
108static void
109get_pass_phrase(char *phrase, size_t size)
110{
111	char           *p;
112
113	while ((p = getpass("netpgp passphrase: ")) == NULL) {
114	}
115	(void) snprintf(phrase, size, "%s", p);
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
135psuccess(char *f, __ops_validation_t *results, __ops_keyring_t *pubring)
136{
137	const __ops_keydata_t	*pubkey;
138	unsigned		 i;
139	char			 id[MAX_ID_LENGTH + 1];
140
141	for (i = 0; i < results->validc; i++) {
142		printf("Good signature for %s made %susing %s key %s\n",
143		       f,
144		       ctime(&results->valid_sigs[i].creation_time),
145		       __ops_show_pka(results->valid_sigs[i].key_algorithm),
146		       userid_to_id(results->valid_sigs[i].signer_id, id));
147		pubkey = __ops_keyring_find_key_by_id(pubring,
148						    (const unsigned char *)
149					  results->valid_sigs[i].signer_id);
150		__ops_print_public_keydata(pubkey);
151	}
152}
153
154/***************************************************************************/
155/* exported functions start here */
156/***************************************************************************/
157
158/* initialise a netpgp_t structure */
159int
160netpgp_init(netpgp_t *netpgp, char *userid, char *pubring, char *secring)
161{
162	__ops_keyring_t	*keyring;
163	char		*homedir;
164	char		 ringname[MAXPATHLEN];
165	char		 id[MAX_ID_LENGTH];
166
167	(void) memset(netpgp, 0x0, sizeof(*netpgp));
168	homedir = getenv("HOME");
169	if (userid == NULL) {
170		(void) memset(id, 0x0, sizeof(id));
171		conffile(homedir, id, sizeof(id), 1);
172		if (id[0] != 0x0) {
173			userid = id;
174		}
175	}
176	if (userid == NULL) {
177		(void) fprintf(stderr, "Cannot find user id\n");
178		return 0;
179	}
180	if (pubring == NULL) {
181		(void) snprintf(ringname, sizeof(ringname), "%s/.gnupg/pubring.gpg", homedir);
182		pubring = ringname;
183	}
184	keyring = calloc(1, sizeof(*keyring));
185	if (!__ops_keyring_read_from_file(keyring, false, pubring)) {
186		(void) fprintf(stderr, "Cannot read pub keyring %s\n", pubring);
187		return 0;
188	}
189	netpgp->pubring = keyring;
190	netpgp->pubringfile = strdup(pubring);
191	if (secring == NULL) {
192		(void) snprintf(ringname, sizeof(ringname), "%s/.gnupg/secring.gpg", homedir);
193		secring = ringname;
194	}
195	keyring = calloc(1, sizeof(*keyring));
196	if (!__ops_keyring_read_from_file(keyring, false, secring)) {
197		(void) fprintf(stderr, "Cannot read sec keyring %s\n", secring);
198		return 0;
199	}
200	netpgp->secring = keyring;
201	netpgp->secringfile = strdup(secring);
202	netpgp->userid = strdup(userid);
203	return 1;
204}
205
206/* finish off with the netpgp_t struct */
207int
208netpgp_end(netpgp_t *netpgp)
209{
210	if (netpgp->pubring != NULL) {
211		__ops_keyring_free(netpgp->pubring);
212	}
213	if (netpgp->pubringfile != NULL) {
214		(void) free(netpgp->pubringfile);
215	}
216	if (netpgp->secring != NULL) {
217		__ops_keyring_free(netpgp->secring);
218	}
219	if (netpgp->secringfile != NULL) {
220		(void) free(netpgp->secringfile);
221	}
222	(void) free(netpgp->userid);
223	return 1;
224}
225
226/* list the keys in a keyring */
227int
228netpgp_list_keys(netpgp_t *netpgp)
229{
230	__ops_keyring_list(netpgp->pubring);
231	return 1;
232}
233
234/* find a key in a keyring */
235int
236netpgp_find_key(netpgp_t *netpgp, char *id)
237{
238	if (id == NULL) {
239		(void) fprintf(stderr, "NULL id to search for\n");
240		return 0;
241	}
242	return __ops_keyring_find_key_by_userid(netpgp->pubring, id) != NULL;
243}
244
245/* export a given key */
246int
247netpgp_export_key(netpgp_t *netpgp, char *userid)
248{
249	const __ops_keydata_t	*keypair;
250
251	if (userid == NULL) {
252		userid = netpgp->userid;
253	}
254	if ((keypair = __ops_keyring_find_key_by_userid(netpgp->pubring, userid)) == NULL) {
255		(void) fprintf(stderr, "Cannot find own key \"%s\" in keyring\n", userid);
256		return 0;
257	}
258	__ops_export_key(keypair, NULL);
259	return 1;
260}
261
262/* import a key into our keyring */
263int
264netpgp_import_key(netpgp_t *netpgp, char *f)
265{
266	int	done;
267
268	if ((done = __ops_keyring_read_from_file(netpgp->pubring, false, f)) == 0) {
269		done = __ops_keyring_read_from_file(netpgp->pubring, true, f);
270	}
271	if (!done) {
272		(void) fprintf(stderr, "Cannot import key from file %s\n", f);
273		return 0;
274	}
275	__ops_keyring_list(netpgp->pubring);
276	return 1;
277}
278
279/* generate a new key */
280int
281netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
282{
283	__ops_create_info_t	*create;
284	__ops_keydata_t		*keypair;
285	__ops_user_id_t		 uid;
286	int             	 fd;
287
288	(void) memset(&uid, 0x0, sizeof(uid));
289	uid.user_id = (unsigned char *) id;
290	if ((keypair = __ops_rsa_create_selfsigned_keypair(numbits, (const unsigned long)65537, &uid)) == NULL) {
291		(void) fprintf(stderr, "Cannot generate key\n");
292		return 0;
293	}
294	/* write public key */
295	fd = __ops_setup_file_append(&create, netpgp->pubringfile);
296	__ops_write_transferable_public_key(keypair, false, create);
297	__ops_teardown_file_write(create, fd);
298	__ops_keyring_free(netpgp->pubring);
299	if (!__ops_keyring_read_from_file(netpgp->pubring, false, netpgp->pubringfile)) {
300		(void) fprintf(stderr, "Cannot re-read keyring %s\n", netpgp->pubringfile);
301		return 0;
302	}
303	/* write secret key */
304	fd = __ops_setup_file_append(&create, netpgp->secringfile);
305	__ops_write_transferable_secret_key(keypair, NULL, 0, false, create);
306	__ops_teardown_file_write(create, fd);
307	__ops_keyring_free(netpgp->secring);
308	if (!__ops_keyring_read_from_file(netpgp->secring, false, netpgp->secringfile)) {
309		fprintf(stderr, "Cannot re-read keyring %s\n", netpgp->secringfile);
310		return 0;
311	}
312	__ops_keydata_free(keypair);
313	return 1;
314}
315
316/* encrypt a file */
317int
318netpgp_encrypt_file(netpgp_t *netpgp, char *userid, char *f, char *out, int armored)
319{
320	const __ops_keydata_t	*keypair;
321	const char		*suffix;
322	char			 outname[MAXPATHLEN];
323
324	if (userid == NULL) {
325		userid = netpgp->userid;
326	}
327	suffix = (armored) ? ".asc" : ".gpg";
328	if ((keypair = __ops_keyring_find_key_by_userid(netpgp->pubring, userid)) == NULL) {
329		(void) fprintf(stderr, "Userid '%s' not found in keyring\n", userid);
330		return 0;
331	}
332	if (out == NULL) {
333		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
334		out = outname;
335	}
336	__ops_encrypt_file(f, out, keypair, armored, true);
337	return 1;
338}
339
340/* decrypt a file */
341int
342netpgp_decrypt_file(netpgp_t *netpgp, char *f, char *out, int armored)
343{
344	__ops_decrypt_file(f, out, netpgp->secring, armored, true,
345		get_passphrase_cb);
346	return 1;
347}
348
349/* sign a file */
350int
351netpgp_sign_file(netpgp_t *netpgp, char *userid, char *f, char *out, int armored, int cleartext)
352{
353	const __ops_keydata_t	*keypair;
354	__ops_secret_key_t	*seckey;
355	char			 passphrase[MAX_PASSPHRASE_LENGTH];
356
357	if (userid == NULL) {
358		userid = netpgp->userid;
359	}
360	/* get key with which to sign */
361	if ((keypair = __ops_keyring_find_key_by_userid(netpgp->secring, userid)) == NULL) {
362		(void) fprintf(stderr, "Userid '%s' not found in keyring\n", userid);
363		return 0;
364	}
365	do {
366		/* print out the user id */
367		__ops_print_public_keydata(keypair);
368		/* get the passphrase */
369		get_pass_phrase(passphrase, sizeof(passphrase));
370		/* now decrypt key */
371		if ((seckey = __ops_decrypt_secret_key_from_data(keypair, passphrase)) == NULL) {
372			(void) fprintf(stderr, "Bad passphrase\n");
373		}
374	} while (seckey == NULL);
375	/* sign file */
376	if (cleartext) {
377		__ops_sign_file_as_cleartext(f, out, seckey, true);
378	} else {
379		__ops_sign_file(f, out, seckey, armored, true);
380	}
381	(void) memset(passphrase, 0x0, sizeof(passphrase));
382	return 1;
383}
384
385/* verify a file */
386int
387netpgp_verify_file(netpgp_t *netpgp, char *f, int armored)
388{
389	__ops_validation_t	result;
390
391	(void) memset(&result, 0x0, sizeof(result));
392	if (__ops_validate_file(&result, f, armored, netpgp->pubring)) {
393		psuccess(f, &result, netpgp->pubring);
394		return 1;
395	}
396	if (result.validc + result.invalidc + result.unknownc == 0) {
397		(void) fprintf(stderr, "\"%s\": No signatures found - is this a signed file?\n", f);
398		return 0;
399	}
400	(void) fprintf(stderr, "\"%s\": verification failure: %d invalid signatures, %d unknown signatures\n",
401		f, result.invalidc, result.unknownc);
402	return 0;
403}
404
405/* wrappers for the ops_debug_level functions we added to openpgpsdk */
406
407/* set the debugging level per filename */
408int
409netpgp_set_debug(const char *f)
410{
411	return __ops_set_debug_level(f);
412}
413
414/* get the debugging level per filename */
415int
416netpgp_get_debug(const char *f)
417{
418	return __ops_get_debug_level(f);
419}
420
421/* return the version for the library */
422const char *
423netpgp_get_info(const char *type)
424{
425	return __ops_get_info(type);
426}
427