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
30 /* Command line program to perform netpgp operations */
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34
35 #include <getopt.h>
36 #include <regex.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include <mj.h>
44 #include <netpgp.h>
45
46 /*
47 * 2048 is the absolute minimum, really - we should really look at
48 * bumping this to 4096 or even higher - agc, 20090522
49 */
50 #define DEFAULT_NUMBITS 2048
51
52 #define DEFAULT_HASH_ALG "SHA256"
53
54 static const char *usage =
55 " --help OR\n"
56 "\t--export-key [options] OR\n"
57 "\t--find-key [options] OR\n"
58 "\t--generate-key [options] OR\n"
59 "\t--import-key [options] OR\n"
60 "\t--list-keys [options] OR\n"
61 "\t--list-sigs [options] OR\n"
62 "\t--trusted-keys [options] OR\n"
63 "\t--get-key keyid [options] OR\n"
64 "\t--version\n"
65 "where options are:\n"
66 "\t[--cipher=<cipher name>] AND/OR\n"
67 "\t[--coredumps] AND/OR\n"
68 "\t[--hash=<hash alg>] AND/OR\n"
69 "\t[--homedir=<homedir>] AND/OR\n"
70 "\t[--keyring=<keyring>] AND/OR\n"
71 "\t[--userid=<userid>] AND/OR\n"
72 "\t[--verbose]\n";
73
74 enum optdefs {
75 /* commands */
76 LIST_KEYS = 260,
77 LIST_SIGS,
78 FIND_KEY,
79 EXPORT_KEY,
80 IMPORT_KEY,
81 GENERATE_KEY,
82 VERSION_CMD,
83 HELP_CMD,
84 GET_KEY,
85 TRUSTED_KEYS,
86
87 /* options */
88 SSHKEYS,
89 KEYRING,
90 USERID,
91 HOMEDIR,
92 NUMBITS,
93 HASH_ALG,
94 VERBOSE,
95 COREDUMPS,
96 PASSWDFD,
97 RESULTS,
98 SSHKEYFILE,
99 CIPHER,
100 FORMAT,
101
102 /* debug */
103 OPS_DEBUG
104
105 };
106
107 #define EXIT_ERROR 2
108
109 static struct option options[] = {
110 /* key-management commands */
111 {"list-keys", no_argument, NULL, LIST_KEYS},
112 {"list-sigs", no_argument, NULL, LIST_SIGS},
113 {"find-key", optional_argument, NULL, FIND_KEY},
114 {"export", no_argument, NULL, EXPORT_KEY},
115 {"export-key", no_argument, NULL, EXPORT_KEY},
116 {"import", no_argument, NULL, IMPORT_KEY},
117 {"import-key", no_argument, NULL, IMPORT_KEY},
118 {"gen", optional_argument, NULL, GENERATE_KEY},
119 {"gen-key", optional_argument, NULL, GENERATE_KEY},
120 {"generate", optional_argument, NULL, GENERATE_KEY},
121 {"generate-key", optional_argument, NULL, GENERATE_KEY},
122 {"get-key", no_argument, NULL, GET_KEY},
123 {"trusted-keys",optional_argument, NULL, TRUSTED_KEYS},
124 {"trusted", optional_argument, NULL, TRUSTED_KEYS},
125 /* debugging commands */
126 {"help", no_argument, NULL, HELP_CMD},
127 {"version", no_argument, NULL, VERSION_CMD},
128 {"debug", required_argument, NULL, OPS_DEBUG},
129 /* options */
130 {"coredumps", no_argument, NULL, COREDUMPS},
131 {"keyring", required_argument, NULL, KEYRING},
132 {"userid", required_argument, NULL, USERID},
133 {"format", required_argument, NULL, FORMAT},
134 {"hash-alg", required_argument, NULL, HASH_ALG},
135 {"hash", required_argument, NULL, HASH_ALG},
136 {"algorithm", required_argument, NULL, HASH_ALG},
137 {"home", required_argument, NULL, HOMEDIR},
138 {"homedir", required_argument, NULL, HOMEDIR},
139 {"numbits", required_argument, NULL, NUMBITS},
140 {"ssh", no_argument, NULL, SSHKEYS},
141 {"ssh-keys", no_argument, NULL, SSHKEYS},
142 {"sshkeyfile", required_argument, NULL, SSHKEYFILE},
143 {"verbose", no_argument, NULL, VERBOSE},
144 {"pass-fd", required_argument, NULL, PASSWDFD},
145 {"results", required_argument, NULL, RESULTS},
146 {"cipher", required_argument, NULL, CIPHER},
147 { NULL, 0, NULL, 0},
148 };
149
150 /* gather up program variables into one struct */
151 typedef struct prog_t {
152 char keyring[MAXPATHLEN + 1]; /* name of keyring */
153 char *progname; /* program name */
154 int numbits; /* # of bits */
155 int cmd; /* netpgpkeys command */
156 } prog_t;
157
158
159 /* print a usage message */
160 static void
print_usage(const char * usagemsg,char * progname)161 print_usage(const char *usagemsg, char *progname)
162 {
163 (void) fprintf(stderr,
164 "%s\nAll bug reports, praise and chocolate, please, to:\n%s\n",
165 netpgp_get_info("version"),
166 netpgp_get_info("maintainer"));
167 (void) fprintf(stderr, "Usage: %s COMMAND OPTIONS:\n%s %s",
168 progname, progname, usagemsg);
169 }
170
171 /* match keys, decoding from json if we do find any */
172 static int
match_keys(netpgp_t * netpgp,FILE * fp,char * f,const int psigs)173 match_keys(netpgp_t *netpgp, FILE *fp, char *f, const int psigs)
174 {
175 char *json;
176 int idc;
177
178 if (f == NULL) {
179 if (!netpgp_list_keys_json(netpgp, &json, psigs)) {
180 return 0;
181 }
182 } else {
183 if (netpgp_match_keys_json(netpgp, &json, f,
184 netpgp_getvar(netpgp, "format"), psigs) == 0) {
185 return 0;
186 }
187 }
188 idc = netpgp_format_json(fp, json, psigs);
189 /* clean up */
190 free(json);
191 return idc;
192 }
193
194 /* do a command once for a specified file 'f' */
195 static int
netpgp_cmd(netpgp_t * netpgp,prog_t * p,char * f)196 netpgp_cmd(netpgp_t *netpgp, prog_t *p, char *f)
197 {
198 char *key;
199 char *s;
200
201 switch (p->cmd) {
202 case LIST_KEYS:
203 case LIST_SIGS:
204 return match_keys(netpgp, stdout, f, (p->cmd == LIST_SIGS));
205 case FIND_KEY:
206 if ((key = f) == NULL) {
207 key = netpgp_getvar(netpgp, "userid");
208 }
209 return netpgp_find_key(netpgp, key);
210 case EXPORT_KEY:
211 if ((key = f) == NULL) {
212 key = netpgp_getvar(netpgp, "userid");
213 }
214 if (key) {
215 if ((s = netpgp_export_key(netpgp, key)) != NULL) {
216 printf("%s", s);
217 return 1;
218 }
219 }
220 (void) fprintf(stderr, "key '%s' not found\n", f);
221 return 0;
222 case IMPORT_KEY:
223 return netpgp_import_key(netpgp, f);
224 case GENERATE_KEY:
225 return netpgp_generate_key(netpgp, f, p->numbits);
226 case GET_KEY:
227 key = netpgp_get_key(netpgp, f, netpgp_getvar(netpgp, "format"));
228 if (key) {
229 printf("%s", key);
230 return 1;
231 }
232 (void) fprintf(stderr, "key '%s' not found\n", f);
233 return 0;
234 case TRUSTED_KEYS:
235 return netpgp_match_pubkeys(netpgp, f, stdout);
236 case HELP_CMD:
237 default:
238 print_usage(usage, p->progname);
239 exit(EXIT_SUCCESS);
240 }
241 }
242
243 /* set the option */
244 static int
setoption(netpgp_t * netpgp,prog_t * p,int val,char * arg,int * homeset)245 setoption(netpgp_t *netpgp, prog_t *p, int val, char *arg, int *homeset)
246 {
247 switch (val) {
248 case COREDUMPS:
249 netpgp_setvar(netpgp, "coredumps", "allowed");
250 break;
251 case GENERATE_KEY:
252 netpgp_setvar(netpgp, "userid checks", "skip");
253 p->cmd = val;
254 break;
255 case LIST_KEYS:
256 case LIST_SIGS:
257 case FIND_KEY:
258 case EXPORT_KEY:
259 case IMPORT_KEY:
260 case GET_KEY:
261 case TRUSTED_KEYS:
262 case HELP_CMD:
263 p->cmd = val;
264 break;
265 case VERSION_CMD:
266 printf(
267 "%s\nAll bug reports, praise and chocolate, please, to:\n%s\n",
268 netpgp_get_info("version"),
269 netpgp_get_info("maintainer"));
270 exit(EXIT_SUCCESS);
271 /* options */
272 case SSHKEYS:
273 netpgp_setvar(netpgp, "ssh keys", "1");
274 break;
275 case KEYRING:
276 if (arg == NULL) {
277 (void) fprintf(stderr,
278 "No keyring argument provided\n");
279 exit(EXIT_ERROR);
280 }
281 snprintf(p->keyring, sizeof(p->keyring), "%s", arg);
282 break;
283 case USERID:
284 if (optarg == NULL) {
285 (void) fprintf(stderr,
286 "no userid argument provided\n");
287 exit(EXIT_ERROR);
288 }
289 netpgp_setvar(netpgp, "userid", arg);
290 break;
291 case VERBOSE:
292 netpgp_incvar(netpgp, "verbose", 1);
293 break;
294 case HOMEDIR:
295 if (arg == NULL) {
296 (void) fprintf(stderr,
297 "no home directory argument provided\n");
298 exit(EXIT_ERROR);
299 }
300 netpgp_set_homedir(netpgp, arg, NULL, 0);
301 *homeset = 1;
302 break;
303 case NUMBITS:
304 if (arg == NULL) {
305 (void) fprintf(stderr,
306 "no number of bits argument provided\n");
307 exit(EXIT_ERROR);
308 }
309 p->numbits = atoi(arg);
310 break;
311 case HASH_ALG:
312 if (arg == NULL) {
313 (void) fprintf(stderr,
314 "No hash algorithm argument provided\n");
315 exit(EXIT_ERROR);
316 }
317 netpgp_setvar(netpgp, "hash", arg);
318 break;
319 case PASSWDFD:
320 if (arg == NULL) {
321 (void) fprintf(stderr,
322 "no pass-fd argument provided\n");
323 exit(EXIT_ERROR);
324 }
325 netpgp_setvar(netpgp, "pass-fd", arg);
326 break;
327 case RESULTS:
328 if (arg == NULL) {
329 (void) fprintf(stderr,
330 "No output filename argument provided\n");
331 exit(EXIT_ERROR);
332 }
333 netpgp_setvar(netpgp, "res", arg);
334 break;
335 case SSHKEYFILE:
336 netpgp_setvar(netpgp, "ssh keys", "1");
337 netpgp_setvar(netpgp, "sshkeyfile", arg);
338 break;
339 case FORMAT:
340 netpgp_setvar(netpgp, "format", arg);
341 break;
342 case CIPHER:
343 netpgp_setvar(netpgp, "cipher", arg);
344 break;
345 case OPS_DEBUG:
346 netpgp_set_debug(arg);
347 break;
348 default:
349 p->cmd = HELP_CMD;
350 break;
351 }
352 return 1;
353 }
354
355 /* we have -o option=value -- parse, and process */
356 static int
parse_option(netpgp_t * netpgp,prog_t * p,const char * s,int * homeset)357 parse_option(netpgp_t *netpgp, prog_t *p, const char *s, int *homeset)
358 {
359 static regex_t opt;
360 struct option *op;
361 static int compiled;
362 regmatch_t matches[10];
363 char option[128];
364 char value[128];
365
366 if (!compiled) {
367 compiled = 1;
368 (void) regcomp(&opt, "([^=]{1,128})(=(.*))?", REG_EXTENDED);
369 }
370 if (regexec(&opt, s, 10, matches, 0) == 0) {
371 (void) snprintf(option, sizeof(option), "%.*s",
372 (int)(matches[1].rm_eo - matches[1].rm_so), &s[matches[1].rm_so]);
373 if (matches[2].rm_so > 0) {
374 (void) snprintf(value, sizeof(value), "%.*s",
375 (int)(matches[3].rm_eo - matches[3].rm_so), &s[matches[3].rm_so]);
376 } else {
377 value[0] = 0x0;
378 }
379 for (op = options ; op->name ; op++) {
380 if (strcmp(op->name, option) == 0) {
381 return setoption(netpgp, p, op->val, value, homeset);
382 }
383 }
384 }
385 return 0;
386 }
387
388 int
main(int argc,char ** argv)389 main(int argc, char **argv)
390 {
391 struct stat st;
392 netpgp_t netpgp;
393 prog_t p;
394 int homeset;
395 int optindex;
396 int ret;
397 int ch;
398 int i;
399
400 (void) memset(&p, 0x0, sizeof(p));
401 (void) memset(&netpgp, 0x0, sizeof(netpgp));
402 homeset = 0;
403 p.progname = argv[0];
404 p.numbits = DEFAULT_NUMBITS;
405 if (argc < 2) {
406 print_usage(usage, p.progname);
407 exit(EXIT_ERROR);
408 }
409 /* set some defaults */
410 netpgp_setvar(&netpgp, "sshkeydir", "/etc/ssh");
411 netpgp_setvar(&netpgp, "res", "<stdout>");
412 netpgp_setvar(&netpgp, "hash", DEFAULT_HASH_ALG);
413 netpgp_setvar(&netpgp, "format", "human");
414 optindex = 0;
415 while ((ch = getopt_long(argc, argv, "S:Vglo:s", options, &optindex)) != -1) {
416 if (ch >= LIST_KEYS) {
417 /* getopt_long returns 0 for long options */
418 if (!setoption(&netpgp, &p, options[optindex].val, optarg, &homeset)) {
419 (void) fprintf(stderr, "Bad setoption result %d\n", ch);
420 }
421 } else {
422 switch (ch) {
423 case 'S':
424 netpgp_setvar(&netpgp, "ssh keys", "1");
425 netpgp_setvar(&netpgp, "sshkeyfile", optarg);
426 break;
427 case 'V':
428 printf(
429 "%s\nAll bug reports, praise and chocolate, please, to:\n%s\n",
430 netpgp_get_info("version"),
431 netpgp_get_info("maintainer"));
432 exit(EXIT_SUCCESS);
433 case 'g':
434 p.cmd = GENERATE_KEY;
435 break;
436 case 'l':
437 p.cmd = LIST_KEYS;
438 break;
439 case 'o':
440 if (!parse_option(&netpgp, &p, optarg, &homeset)) {
441 (void) fprintf(stderr, "Bad parse_option\n");
442 }
443 break;
444 case 's':
445 p.cmd = LIST_SIGS;
446 break;
447 default:
448 p.cmd = HELP_CMD;
449 break;
450 }
451 }
452 }
453 if (!homeset) {
454 netpgp_set_homedir(&netpgp, getenv("HOME"),
455 netpgp_getvar(&netpgp, "ssh keys") ? "/.ssh" : "/.gnupg", 1);
456 }
457 /* initialise, and read keys from file */
458 if (!netpgp_init(&netpgp)) {
459 if (stat(netpgp_getvar(&netpgp, "homedir"), &st) < 0) {
460 (void) mkdir(netpgp_getvar(&netpgp, "homedir"), 0700);
461 }
462 if (stat(netpgp_getvar(&netpgp, "homedir"), &st) < 0) {
463 (void) fprintf(stderr, "can't create home directory '%s'\n",
464 netpgp_getvar(&netpgp, "homedir"));
465 exit(EXIT_ERROR);
466 }
467 }
468 /* now do the required action for each of the command line args */
469 ret = EXIT_SUCCESS;
470 if (optind == argc) {
471 if (!netpgp_cmd(&netpgp, &p, NULL)) {
472 ret = EXIT_FAILURE;
473 }
474 } else {
475 for (i = optind; i < argc; i++) {
476 if (!netpgp_cmd(&netpgp, &p, argv[i])) {
477 ret = EXIT_FAILURE;
478 }
479 }
480 }
481 netpgp_end(&netpgp);
482 exit(ret);
483 }
484