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