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