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 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 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 196 netpgp_cmd(netpgp_t *netpgp, prog_t *p, char *f) 197 { 198 char *key; 199 200 switch (p->cmd) { 201 case LIST_KEYS: 202 case LIST_SIGS: 203 return match_keys(netpgp, stdout, f, (p->cmd == LIST_SIGS)); 204 case FIND_KEY: 205 if ((key = f) == NULL) { 206 key = netpgp_getvar(netpgp, "userid"); 207 } 208 return netpgp_find_key(netpgp, key); 209 case EXPORT_KEY: 210 if ((key = f) == NULL) { 211 key = netpgp_getvar(netpgp, "userid"); 212 } 213 if (key) { 214 printf("%s", key); 215 return 1; 216 } 217 (void) fprintf(stderr, "key '%s' not found\n", f); 218 return 0; 219 case IMPORT_KEY: 220 return netpgp_import_key(netpgp, f); 221 case GENERATE_KEY: 222 return netpgp_generate_key(netpgp, f, p->numbits); 223 case GET_KEY: 224 key = netpgp_get_key(netpgp, f, netpgp_getvar(netpgp, "format")); 225 if (key) { 226 printf("%s", key); 227 return 1; 228 } 229 (void) fprintf(stderr, "key '%s' not found\n", f); 230 return 0; 231 case TRUSTED_KEYS: 232 return netpgp_match_pubkeys(netpgp, f, stdout); 233 case HELP_CMD: 234 default: 235 print_usage(usage, p->progname); 236 exit(EXIT_SUCCESS); 237 } 238 } 239 240 /* set the option */ 241 static int 242 setoption(netpgp_t *netpgp, prog_t *p, int val, char *arg, int *homeset) 243 { 244 switch (val) { 245 case COREDUMPS: 246 netpgp_setvar(netpgp, "coredumps", "allowed"); 247 break; 248 case GENERATE_KEY: 249 netpgp_setvar(netpgp, "userid checks", "skip"); 250 p->cmd = val; 251 break; 252 case LIST_KEYS: 253 case LIST_SIGS: 254 case FIND_KEY: 255 case EXPORT_KEY: 256 case IMPORT_KEY: 257 case GET_KEY: 258 case TRUSTED_KEYS: 259 case HELP_CMD: 260 p->cmd = val; 261 break; 262 case VERSION_CMD: 263 printf( 264 "%s\nAll bug reports, praise and chocolate, please, to:\n%s\n", 265 netpgp_get_info("version"), 266 netpgp_get_info("maintainer")); 267 exit(EXIT_SUCCESS); 268 /* options */ 269 case SSHKEYS: 270 netpgp_setvar(netpgp, "ssh keys", "1"); 271 break; 272 case KEYRING: 273 if (arg == NULL) { 274 (void) fprintf(stderr, 275 "No keyring argument provided\n"); 276 exit(EXIT_ERROR); 277 } 278 snprintf(p->keyring, sizeof(p->keyring), "%s", arg); 279 break; 280 case USERID: 281 if (optarg == NULL) { 282 (void) fprintf(stderr, 283 "no userid argument provided\n"); 284 exit(EXIT_ERROR); 285 } 286 netpgp_setvar(netpgp, "userid", arg); 287 break; 288 case VERBOSE: 289 netpgp_incvar(netpgp, "verbose", 1); 290 break; 291 case HOMEDIR: 292 if (arg == NULL) { 293 (void) fprintf(stderr, 294 "no home directory argument provided\n"); 295 exit(EXIT_ERROR); 296 } 297 netpgp_set_homedir(netpgp, arg, NULL, 0); 298 *homeset = 1; 299 break; 300 case NUMBITS: 301 if (arg == NULL) { 302 (void) fprintf(stderr, 303 "no number of bits argument provided\n"); 304 exit(EXIT_ERROR); 305 } 306 p->numbits = atoi(arg); 307 break; 308 case HASH_ALG: 309 if (arg == NULL) { 310 (void) fprintf(stderr, 311 "No hash algorithm argument provided\n"); 312 exit(EXIT_ERROR); 313 } 314 netpgp_setvar(netpgp, "hash", arg); 315 break; 316 case PASSWDFD: 317 if (arg == NULL) { 318 (void) fprintf(stderr, 319 "no pass-fd argument provided\n"); 320 exit(EXIT_ERROR); 321 } 322 netpgp_setvar(netpgp, "pass-fd", arg); 323 break; 324 case RESULTS: 325 if (arg == NULL) { 326 (void) fprintf(stderr, 327 "No output filename argument provided\n"); 328 exit(EXIT_ERROR); 329 } 330 netpgp_setvar(netpgp, "res", arg); 331 break; 332 case SSHKEYFILE: 333 netpgp_setvar(netpgp, "ssh keys", "1"); 334 netpgp_setvar(netpgp, "sshkeyfile", arg); 335 break; 336 case FORMAT: 337 netpgp_setvar(netpgp, "format", arg); 338 break; 339 case CIPHER: 340 netpgp_setvar(netpgp, "cipher", arg); 341 break; 342 case OPS_DEBUG: 343 netpgp_set_debug(arg); 344 break; 345 default: 346 p->cmd = HELP_CMD; 347 break; 348 } 349 return 1; 350 } 351 352 /* we have -o option=value -- parse, and process */ 353 static int 354 parse_option(netpgp_t *netpgp, prog_t *p, const char *s, int *homeset) 355 { 356 static regex_t opt; 357 struct option *op; 358 static int compiled; 359 regmatch_t matches[10]; 360 char option[128]; 361 char value[128]; 362 363 if (!compiled) { 364 compiled = 1; 365 (void) regcomp(&opt, "([^=]{1,128})(=(.*))?", REG_EXTENDED); 366 } 367 if (regexec(&opt, s, 10, matches, 0) == 0) { 368 (void) snprintf(option, sizeof(option), "%.*s", 369 (int)(matches[1].rm_eo - matches[1].rm_so), &s[matches[1].rm_so]); 370 if (matches[2].rm_so > 0) { 371 (void) snprintf(value, sizeof(value), "%.*s", 372 (int)(matches[3].rm_eo - matches[3].rm_so), &s[matches[3].rm_so]); 373 } else { 374 value[0] = 0x0; 375 } 376 for (op = options ; op->name ; op++) { 377 if (strcmp(op->name, option) == 0) { 378 return setoption(netpgp, p, op->val, value, homeset); 379 } 380 } 381 } 382 return 0; 383 } 384 385 int 386 main(int argc, char **argv) 387 { 388 struct stat st; 389 netpgp_t netpgp; 390 prog_t p; 391 int homeset; 392 int optindex; 393 int ret; 394 int ch; 395 int i; 396 397 (void) memset(&p, 0x0, sizeof(p)); 398 (void) memset(&netpgp, 0x0, sizeof(netpgp)); 399 homeset = 0; 400 p.progname = argv[0]; 401 p.numbits = DEFAULT_NUMBITS; 402 if (argc < 2) { 403 print_usage(usage, p.progname); 404 exit(EXIT_ERROR); 405 } 406 /* set some defaults */ 407 netpgp_setvar(&netpgp, "sshkeydir", "/etc/ssh"); 408 netpgp_setvar(&netpgp, "res", "<stdout>"); 409 netpgp_setvar(&netpgp, "hash", DEFAULT_HASH_ALG); 410 netpgp_setvar(&netpgp, "format", "human"); 411 optindex = 0; 412 while ((ch = getopt_long(argc, argv, "S:Vglo:s", options, &optindex)) != -1) { 413 if (ch >= LIST_KEYS) { 414 /* getopt_long returns 0 for long options */ 415 if (!setoption(&netpgp, &p, options[optindex].val, optarg, &homeset)) { 416 (void) fprintf(stderr, "Bad setoption result %d\n", ch); 417 } 418 } else { 419 switch (ch) { 420 case 'S': 421 netpgp_setvar(&netpgp, "ssh keys", "1"); 422 netpgp_setvar(&netpgp, "sshkeyfile", optarg); 423 break; 424 case 'V': 425 printf( 426 "%s\nAll bug reports, praise and chocolate, please, to:\n%s\n", 427 netpgp_get_info("version"), 428 netpgp_get_info("maintainer")); 429 exit(EXIT_SUCCESS); 430 case 'g': 431 p.cmd = GENERATE_KEY; 432 break; 433 case 'l': 434 p.cmd = LIST_KEYS; 435 break; 436 case 'o': 437 if (!parse_option(&netpgp, &p, optarg, &homeset)) { 438 (void) fprintf(stderr, "Bad parse_option\n"); 439 } 440 break; 441 case 's': 442 p.cmd = LIST_SIGS; 443 break; 444 default: 445 p.cmd = HELP_CMD; 446 break; 447 } 448 } 449 } 450 if (!homeset) { 451 netpgp_set_homedir(&netpgp, getenv("HOME"), 452 netpgp_getvar(&netpgp, "ssh keys") ? "/.ssh" : "/.gnupg", 1); 453 } 454 /* initialise, and read keys from file */ 455 if (!netpgp_init(&netpgp)) { 456 if (stat(netpgp_getvar(&netpgp, "homedir"), &st) < 0) { 457 (void) mkdir(netpgp_getvar(&netpgp, "homedir"), 0700); 458 } 459 if (stat(netpgp_getvar(&netpgp, "homedir"), &st) < 0) { 460 (void) fprintf(stderr, "can't create home directory '%s'\n", 461 netpgp_getvar(&netpgp, "homedir")); 462 exit(EXIT_ERROR); 463 } 464 } 465 /* now do the required action for each of the command line args */ 466 ret = EXIT_SUCCESS; 467 if (optind == argc) { 468 if (!netpgp_cmd(&netpgp, &p, NULL)) { 469 ret = EXIT_FAILURE; 470 } 471 } else { 472 for (i = optind; i < argc; i++) { 473 if (!netpgp_cmd(&netpgp, &p, argv[i])) { 474 ret = EXIT_FAILURE; 475 } 476 } 477 } 478 netpgp_end(&netpgp); 479 exit(ret); 480 } 481