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