key.c
1 /*- 2 * Copyright (c) 2021 Kyle Evans <kevans@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer 9 * in this position and unchanged. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/uio.h> 27 28 #include <bsd_compat.h> 29 #include <assert.h> 30 #include <err.h> 31 #include <getopt.h> 32 #include <signal.h> 33 #include <stdio.h> 34 #include <string.h> 35 36 #if __has_include(<readpassphrase.h>) 37 #include <readpassphrase.h> 38 #elif __has_include(<bsd/readpassphrase.h>) 39 #include <bsd/readpassphrase.h> 40 #else 41 #include "readpassphrase_compat.h" 42 #endif 43 44 #include <unistd.h> 45 46 #include <pkg.h> 47 #include "pkgcli.h" 48 49 enum { 50 ARG_CREATE = CHAR_MAX + 1, 51 ARG_PUBLIC, 52 ARG_SIGN, 53 }; 54 55 typedef enum { 56 MODE_UNSPECIFIED = 0, 57 MODE_CREATE, 58 MODE_PUBLIC, 59 MODE_SIGN, 60 } key_mode_t; 61 62 void 63 usage_key(void) 64 { 65 fprintf(stderr, "Usage: pkg key [--create | --public | --sign] [-t <type>] " 66 "<key-path>\n"); 67 fprintf(stderr, "For more information see 'pkg help key'.\n"); 68 } 69 70 static int 71 key_create(struct pkg_key *key, int argc __unused, char *argv[] __unused) 72 { 73 /* No arguments to setup for now. */ 74 return (pkg_key_create(key, NULL, 0)); 75 } 76 77 static int 78 key_pubout(struct pkg_key *key) 79 { 80 char *keybuf = NULL; 81 size_t keylen; 82 int ret; 83 84 ret = pkg_key_pubkey(key, &keybuf, &keylen); 85 if (ret != EPKG_OK) 86 return (ret); 87 88 fwrite(keybuf, keylen, 1, stdout); 89 free(keybuf); 90 return (0); 91 } 92 93 static int 94 key_sign_data(struct pkg_key *key, const char *name) 95 { 96 char buf[BUFSIZ]; 97 xstring *datastr; 98 char *data; 99 unsigned char *sig; 100 size_t datasz, readsz, siglen; 101 FILE *datafile; 102 int rc; 103 104 datafile = NULL; 105 datastr = NULL; 106 rc = EPKG_FATAL; 107 if (STREQ(name, "-")) { 108 datafile = stdin; /* XXX Make it configurable? */ 109 name = "stdin"; 110 } else { 111 datafile = fopen(name, "rb"); 112 if (datafile == NULL) 113 err(EXIT_FAILURE, "fopen"); 114 } 115 116 datastr = xstring_new(); 117 while (!feof(datafile)) { 118 readsz = fread(&buf[0], 1, sizeof(buf), datafile); 119 if (readsz == 0 && ferror(datafile)) { 120 fprintf(stderr, "%s: I/O error\n", name); 121 goto out; 122 } 123 124 fwrite(buf, readsz, 1, datastr->fp); 125 } 126 127 data = xstring_get_binary(datastr, &datasz); 128 datastr = NULL; 129 130 sig = NULL; 131 rc = pkg_key_sign_data(key, (unsigned char *)data, datasz, &sig, &siglen); 132 free(data); 133 134 #if 0 135 fprintf(stderr, "SIGNED: %s\n", data); 136 #endif 137 /* 138 +SIGNED: 64628d55add8b281b9868aea00c4829a3ad260cfc4262e9d1244a1ab67584935 139 +SIGNED: a2eb46d60cd26657b273ec55a0909e642ef522f35074a9c62c3c4b42608e55e1 140 */ 141 142 if (rc == EPKG_OK) { 143 size_t writesz; 144 145 if ((writesz = fwrite(sig, 1, siglen, stdout)) < siglen) { 146 fprintf(stderr, "Failed to write signature out [%zu/%zu]\n", 147 writesz, siglen); 148 rc = EPKG_FATAL; 149 } 150 } 151 free(sig); 152 153 out: 154 xstring_free(datastr); 155 if (datafile != stdin) 156 fclose(datafile); 157 return rc; 158 } 159 160 static int 161 key_info(struct pkg_key *key, const char *file, const char *type) 162 { 163 struct iovec *iov; 164 int niov, rc; 165 166 iov = NULL; 167 rc = pkg_key_info(key, &iov, &niov); 168 if (rc != EPKG_OK) 169 return (rc); 170 171 assert((niov % 2) == 0); 172 173 printf("Key file '%s' (type %s)\n", file, type); 174 for (int i = 0; i < niov; i += 2) { 175 const char *kv_name = iov[i].iov_base; 176 const char *kv_val = iov[i + 1].iov_base; 177 printf(" - %s: %s\n", kv_name, kv_val); 178 179 free(iov[i + 1].iov_base); 180 } 181 182 free(iov); 183 return (EPKG_OK); 184 } 185 186 int 187 password_cb(char *buf, int size, int rwflag, void *key) 188 { 189 int len = 0; 190 char pass[BUFSIZ]; 191 sigset_t sig, oldsig; 192 193 (void)rwflag; 194 (void)key; 195 196 /* Block sigalarm temporary */ 197 sigemptyset(&sig); 198 sigaddset(&sig, SIGALRM); 199 sigprocmask(SIG_BLOCK, &sig, &oldsig); 200 201 if (readpassphrase("\nEnter passphrase: ", pass, BUFSIZ, RPP_ECHO_OFF) == NULL) 202 return 0; 203 204 len = strlen(pass); 205 206 if (len <= 0) return 0; 207 if (len > size) len = size; 208 209 memset(buf, '\0', size); 210 memcpy(buf, pass, len); 211 memset(pass, 0, BUFSIZ); 212 213 sigprocmask(SIG_SETMASK, &oldsig, NULL); 214 215 return (len); 216 } 217 218 int 219 exec_key(int argc, char **argv) 220 { 221 int ret; 222 int ch; 223 struct pkg_key *key = NULL; 224 const char *keypath, *keytype = NULL; 225 key_mode_t keymode; 226 227 struct option longopts[] = { 228 { "create", no_argument, NULL, ARG_CREATE }, 229 { "public", no_argument, NULL, ARG_PUBLIC }, 230 { "sign", no_argument, NULL, ARG_SIGN }, 231 { NULL, 0, NULL, 0 }, 232 }; 233 234 keymode = MODE_UNSPECIFIED; 235 236 /* XXX maybe eventually we can just derive the key type. */ 237 while ((ch = getopt_long(argc, argv, "t:", longopts, NULL)) != -1) { 238 switch (ch) { 239 case ARG_CREATE: 240 if (keymode != MODE_UNSPECIFIED) { 241 usage_key(); 242 return (EXIT_FAILURE); 243 } 244 keymode = MODE_CREATE; 245 break; 246 case ARG_PUBLIC: 247 if (keymode != MODE_UNSPECIFIED) { 248 usage_key(); 249 return (EXIT_FAILURE); 250 } 251 keymode = MODE_PUBLIC; 252 break; 253 case ARG_SIGN: 254 if (keymode != MODE_UNSPECIFIED) { 255 usage_key(); 256 return (EXIT_FAILURE); 257 } 258 keymode = MODE_SIGN; 259 break; 260 case 't': 261 keytype = optarg; 262 break; 263 default: 264 usage_key(); 265 return (EXIT_FAILURE); 266 } 267 } 268 argc -= optind; 269 argv += optind; 270 271 if (argc != 1) { 272 usage_key(); 273 return (EXIT_FAILURE); 274 } 275 276 if (keytype == NULL) 277 keytype = "rsa"; 278 279 keypath = argv[0]; 280 if (*keypath == '\0') { 281 fprintf(stderr, "keypath must not be empty.\n"); 282 usage_key(); 283 return (EXIT_FAILURE); 284 } 285 286 ret = pkg_key_new(&key, keytype, keypath, password_cb); 287 if (ret != EPKG_OK) { 288 fprintf(stderr, "Failed to create key context.\n"); 289 return (EXIT_FAILURE); 290 } 291 292 switch (keymode) { 293 case MODE_CREATE: 294 ret = key_create(key, argc, argv); 295 if (ret != EPKG_OK) { 296 switch (ret) { 297 case EPKG_OPNOTSUPP: 298 fprintf(stderr, "Type '%s' does not support generation.\n", 299 keytype); 300 break; 301 default: 302 fprintf(stderr, "Failed to generate the key.\n"); 303 break; 304 } 305 306 goto out; 307 } 308 309 fprintf(stderr, "Created '%s' private key at %s\n", keytype, keypath); 310 /* FALLTHROUGH */ 311 case MODE_PUBLIC: 312 ret = key_pubout(key); 313 if (ret != EPKG_OK) { 314 switch (ret) { 315 case EPKG_OPNOTSUPP: 316 fprintf(stderr, "Type '%s' does not support pubout.\n", 317 keytype); 318 break; 319 default: 320 fprintf(stderr, "Failed to get keyinfo.\n"); 321 break; 322 } 323 324 goto out; 325 } 326 327 break; 328 case MODE_SIGN: 329 ret = key_sign_data(key, "-"); 330 if (ret != EPKG_OK) { 331 switch (ret) { 332 case EPKG_OPNOTSUPP: 333 fprintf(stderr, "Type '%s' does not support signing.\n", 334 keytype); 335 break; 336 default: 337 fprintf(stderr, "Failed to sign.\n"); 338 break; 339 } 340 341 goto out; 342 } 343 break; 344 case MODE_UNSPECIFIED: 345 ret = key_info(key, keypath, keytype); 346 if (ret != EPKG_OK) { 347 switch (ret) { 348 case EPKG_OPNOTSUPP: 349 printf("Type '%s' does not support keyinfo.\n", 350 keytype); 351 break; 352 default: 353 printf("Failed to get keyinfo.\n"); 354 break; 355 } 356 357 goto out; 358 } 359 360 break; 361 } 362 363 out: 364 pkg_key_free(key); 365 return (ret == EPKG_OK ? EXIT_SUCCESS : EXIT_FAILURE); 366 }