1 /* $Id: main.c,v 1.14 2016/09/18 20:18:25 benno Exp $ */ 2 /* 3 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/socket.h> 19 20 #include <ctype.h> 21 #include <err.h> 22 #include <stdarg.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "extern.h" 29 #include "parse.h" 30 31 #define SSL_DIR "/etc/ssl/acme" 32 #define SSL_PRIV_DIR "/etc/ssl/acme/private" 33 #define ETC_DIR "/etc/acme" 34 #define WWW_DIR "/var/www/acme" 35 #define PRIVKEY_FILE "privkey.pem" 36 37 struct authority authorities[] = { 38 #define DEFAULT_AUTHORITY 0 39 {"letsencrypt", 40 "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf", 41 "https://acme-v01.api.letsencrypt.org/directory"}, 42 {"letsencrypt-staging", 43 "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf", 44 "https://acme-staging.api.letsencrypt.org/directory"}, 45 }; 46 47 /* 48 * Wrap around asprintf(3), which sometimes nullifies the input values, 49 * sometimes not, but always returns <0 on error. 50 * Returns NULL on failure or the pointer on success. 51 */ 52 static char * 53 doasprintf(const char *fmt, ...) 54 { 55 int c; 56 char *cp; 57 va_list ap; 58 59 va_start(ap, fmt); 60 c = vasprintf(&cp, fmt, ap); 61 va_end(ap); 62 return (c < 0 ? NULL : cp); 63 } 64 65 int 66 main(int argc, char *argv[]) 67 { 68 const char *domain, *agreement = NULL, **alts = NULL; 69 char *certdir = NULL, *acctkey = NULL, *chngdir = NULL; 70 char *keyfile = NULL; 71 int key_fds[2], acct_fds[2], chng_fds[2], cert_fds[2]; 72 int file_fds[2], dns_fds[2], rvk_fds[2]; 73 int newacct = 0, remote = 0, backup = 0; 74 int force = 0, multidir = 0, newkey = 0; 75 int c, rc, revocate = 0; 76 int authority = DEFAULT_AUTHORITY; 77 pid_t pids[COMP__MAX]; 78 extern int verbose; 79 extern enum comp proccomp; 80 size_t i, altsz, ne; 81 82 while (-1 != (c = getopt(argc, argv, "bFmnNrs:tva:f:c:C:k:"))) 83 switch (c) { 84 case 'a': 85 agreement = optarg; 86 break; 87 case 'b': 88 backup = 1; 89 break; 90 case 'c': 91 free(certdir); 92 if (NULL == (certdir = strdup(optarg))) 93 err(EXIT_FAILURE, "strdup"); 94 break; 95 case 'C': 96 free(chngdir); 97 if (NULL == (chngdir = strdup(optarg))) 98 err(EXIT_FAILURE, "strdup"); 99 break; 100 case 'f': 101 free(acctkey); 102 if (NULL == (acctkey = strdup(optarg))) 103 err(EXIT_FAILURE, "strdup"); 104 break; 105 case 'F': 106 force = 1; 107 break; 108 case 'k': 109 free(keyfile); 110 if (NULL == (keyfile = strdup(optarg))) 111 err(EXIT_FAILURE, "strdup"); 112 break; 113 case 'm': 114 multidir = 1; 115 break; 116 case 'n': 117 newacct = 1; 118 break; 119 case 'N': 120 newkey = 1; 121 break; 122 case 'r': 123 revocate = 1; 124 break; 125 case 's': 126 authority = -1; 127 for (i = 0; i < nitems(authorities); i++) { 128 if (strcmp(authorities[i].name, optarg) == 0) { 129 authority = i; 130 break; 131 } 132 } 133 if (-1 == authority) 134 errx(EXIT_FAILURE, "unknown acme authority"); 135 break; 136 case 't': 137 /* 138 / Undocumented feature. 139 * Don't use it. 140 */ 141 remote = 1; 142 break; 143 case 'v': 144 verbose = verbose ? 2 : 1; 145 break; 146 default: 147 goto usage; 148 } 149 150 if (NULL == agreement) 151 agreement = authorities[authority].agreement; 152 153 argc -= optind; 154 argv += optind; 155 if (0 == argc) 156 goto usage; 157 158 /* Make sure that the domains are sane. */ 159 160 for (i = 0; i < (size_t)argc; i++) { 161 if (domain_valid(argv[i])) 162 continue; 163 errx(EXIT_FAILURE, "%s: bad domain syntax", argv[i]); 164 } 165 166 domain = argv[0]; 167 argc--; 168 argv++; 169 170 if (getuid() != 0) 171 errx(EXIT_FAILURE, "must be run as root"); 172 173 /* 174 * Now we allocate our directories and file paths IFF we haven't 175 * specified them on the command-line. 176 * If we're in "multidir" (-m) mode, we use our initial domain 177 * name when specifying the prefixes. 178 * Otherwise, we put them all in a known location. 179 */ 180 181 if (NULL == certdir) 182 certdir = multidir ? 183 doasprintf(SSL_DIR "/%s", domain) : 184 strdup(SSL_DIR); 185 if (NULL == keyfile) 186 keyfile = multidir ? 187 doasprintf(SSL_PRIV_DIR "/%s/" 188 PRIVKEY_FILE, domain) : 189 strdup(SSL_PRIV_DIR "/" PRIVKEY_FILE); 190 if (NULL == acctkey) 191 acctkey = multidir ? 192 doasprintf(ETC_DIR "/%s/" 193 PRIVKEY_FILE, domain) : 194 strdup(ETC_DIR "/" PRIVKEY_FILE); 195 if (NULL == chngdir) 196 chngdir = strdup(WWW_DIR); 197 198 if (NULL == certdir || NULL == keyfile || 199 NULL == acctkey || NULL == chngdir) 200 err(EXIT_FAILURE, "strdup"); 201 202 /* 203 * Do some quick checks to see if our paths exist. 204 * This will be done in the children, but we might as well check 205 * now before the fork. 206 */ 207 208 ne = 0; 209 210 if (-1 == access(certdir, R_OK)) { 211 warnx("%s: -c directory must exist", certdir); 212 ne++; 213 } 214 215 if (!newkey && -1 == access(keyfile, R_OK)) { 216 warnx("%s: -k file must exist", keyfile); 217 ne++; 218 } else if (newkey && -1 != access(keyfile, R_OK)) { 219 dodbg("%s: domain key exists (not creating)", keyfile); 220 newkey = 0; 221 } 222 223 if (-1 == access(chngdir, R_OK)) { 224 warnx("%s: -C directory must exist", chngdir); 225 ne++; 226 } 227 228 if (!newacct && -1 == access(acctkey, R_OK)) { 229 warnx("%s: -f file must exist", acctkey); 230 ne++; 231 } else if (newacct && -1 != access(acctkey, R_OK)) { 232 dodbg("%s: account key exists (not creating)", acctkey); 233 newacct = 0; 234 } 235 236 if (ne > 0) 237 exit(EXIT_FAILURE); 238 239 /* Set the zeroth altname as our domain. */ 240 241 altsz = argc + 1; 242 alts = calloc(altsz, sizeof(char *)); 243 if (NULL == alts) 244 err(EXIT_FAILURE, "calloc"); 245 alts[0] = domain; 246 for (i = 0; i < (size_t)argc; i++) 247 alts[i + 1] = argv[i]; 248 249 /* 250 * Open channels between our components. 251 */ 252 253 if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, key_fds)) 254 err(EXIT_FAILURE, "socketpair"); 255 if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, acct_fds)) 256 err(EXIT_FAILURE, "socketpair"); 257 if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, chng_fds)) 258 err(EXIT_FAILURE, "socketpair"); 259 if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, cert_fds)) 260 err(EXIT_FAILURE, "socketpair"); 261 if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, file_fds)) 262 err(EXIT_FAILURE, "socketpair"); 263 if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, dns_fds)) 264 err(EXIT_FAILURE, "socketpair"); 265 if (-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, rvk_fds)) 266 err(EXIT_FAILURE, "socketpair"); 267 268 /* Start with the network-touching process. */ 269 270 if (-1 == (pids[COMP_NET] = fork())) 271 err(EXIT_FAILURE, "fork"); 272 273 if (0 == pids[COMP_NET]) { 274 proccomp = COMP_NET; 275 close(key_fds[0]); 276 close(acct_fds[0]); 277 close(chng_fds[0]); 278 close(cert_fds[0]); 279 close(file_fds[0]); 280 close(file_fds[1]); 281 close(dns_fds[0]); 282 close(rvk_fds[0]); 283 c = netproc(key_fds[1], acct_fds[1], 284 chng_fds[1], cert_fds[1], 285 dns_fds[1], rvk_fds[1], 286 newacct, revocate, authority, 287 (const char *const *)alts, altsz, 288 agreement); 289 free(alts); 290 exit(c ? EXIT_SUCCESS : EXIT_FAILURE); 291 } 292 293 close(key_fds[1]); 294 close(acct_fds[1]); 295 close(chng_fds[1]); 296 close(cert_fds[1]); 297 close(dns_fds[1]); 298 close(rvk_fds[1]); 299 300 /* Now the key-touching component. */ 301 302 if (-1 == (pids[COMP_KEY] = fork())) 303 err(EXIT_FAILURE, "fork"); 304 305 if (0 == pids[COMP_KEY]) { 306 proccomp = COMP_KEY; 307 close(cert_fds[0]); 308 close(dns_fds[0]); 309 close(rvk_fds[0]); 310 close(acct_fds[0]); 311 close(chng_fds[0]); 312 close(file_fds[0]); 313 close(file_fds[1]); 314 c = keyproc(key_fds[0], keyfile, 315 (const char **)alts, altsz, newkey); 316 free(alts); 317 exit(c ? EXIT_SUCCESS : EXIT_FAILURE); 318 } 319 320 close(key_fds[0]); 321 322 /* The account-touching component. */ 323 324 if (-1 == (pids[COMP_ACCOUNT] = fork())) 325 err(EXIT_FAILURE, "fork"); 326 327 if (0 == pids[COMP_ACCOUNT]) { 328 proccomp = COMP_ACCOUNT; 329 free(alts); 330 close(cert_fds[0]); 331 close(dns_fds[0]); 332 close(rvk_fds[0]); 333 close(chng_fds[0]); 334 close(file_fds[0]); 335 close(file_fds[1]); 336 c = acctproc(acct_fds[0], acctkey, newacct); 337 exit(c ? EXIT_SUCCESS : EXIT_FAILURE); 338 } 339 340 close(acct_fds[0]); 341 342 /* The challenge-accepting component. */ 343 344 if (-1 == (pids[COMP_CHALLENGE] = fork())) 345 err(EXIT_FAILURE, "fork"); 346 347 if (0 == pids[COMP_CHALLENGE]) { 348 proccomp = COMP_CHALLENGE; 349 free(alts); 350 close(cert_fds[0]); 351 close(dns_fds[0]); 352 close(rvk_fds[0]); 353 close(file_fds[0]); 354 close(file_fds[1]); 355 c = chngproc(chng_fds[0], chngdir, remote); 356 exit(c ? EXIT_SUCCESS : EXIT_FAILURE); 357 } 358 359 close(chng_fds[0]); 360 361 /* The certificate-handling component. */ 362 363 if (-1 == (pids[COMP_CERT] = fork())) 364 err(EXIT_FAILURE, "fork"); 365 366 if (0 == pids[COMP_CERT]) { 367 proccomp = COMP_CERT; 368 free(alts); 369 close(dns_fds[0]); 370 close(rvk_fds[0]); 371 close(file_fds[1]); 372 c = certproc(cert_fds[0], file_fds[0]); 373 exit(c ? EXIT_SUCCESS : EXIT_FAILURE); 374 } 375 376 close(cert_fds[0]); 377 close(file_fds[0]); 378 379 /* The certificate-handling component. */ 380 381 if (-1 == (pids[COMP_FILE] = fork())) 382 err(EXIT_FAILURE, "fork"); 383 384 if (0 == pids[COMP_FILE]) { 385 proccomp = COMP_FILE; 386 free(alts); 387 close(dns_fds[0]); 388 close(rvk_fds[0]); 389 c = fileproc(file_fds[1], backup, certdir); 390 /* 391 * This is different from the other processes in that it 392 * can return 2 if the certificates were updated. 393 */ 394 exit(c > 1 ? 2 : (c ? EXIT_SUCCESS : EXIT_FAILURE)); 395 } 396 397 close(file_fds[1]); 398 399 /* The DNS lookup component. */ 400 401 if (-1 == (pids[COMP_DNS] = fork())) 402 err(EXIT_FAILURE, "fork"); 403 404 if (0 == pids[COMP_DNS]) { 405 proccomp = COMP_DNS; 406 free(alts); 407 close(rvk_fds[0]); 408 c = dnsproc(dns_fds[0]); 409 exit(c ? EXIT_SUCCESS : EXIT_FAILURE); 410 } 411 412 close(dns_fds[0]); 413 414 /* The expiration component. */ 415 416 if (-1 == (pids[COMP_REVOKE] = fork())) 417 err(EXIT_FAILURE, "fork"); 418 419 if (0 == pids[COMP_REVOKE]) { 420 proccomp = COMP_REVOKE; 421 c = revokeproc(rvk_fds[0], certdir, force, revocate, 422 (const char *const *)alts, altsz); 423 free(alts); 424 exit(c ? EXIT_SUCCESS : EXIT_FAILURE); 425 } 426 427 close(rvk_fds[0]); 428 429 /* Jail: sandbox, file-system, user. */ 430 431 if (pledge("stdio", NULL) == -1) { 432 warn("pledge"); 433 exit(EXIT_FAILURE); 434 } 435 436 /* 437 * Collect our subprocesses. 438 * Require that they both have exited cleanly. 439 */ 440 441 rc = checkexit(pids[COMP_KEY], COMP_KEY) + 442 checkexit(pids[COMP_CERT], COMP_CERT) + 443 checkexit(pids[COMP_NET], COMP_NET) + 444 checkexit_ext(&c, pids[COMP_FILE], COMP_FILE) + 445 checkexit(pids[COMP_ACCOUNT], COMP_ACCOUNT) + 446 checkexit(pids[COMP_CHALLENGE], COMP_CHALLENGE) + 447 checkexit(pids[COMP_DNS], COMP_DNS) + 448 checkexit(pids[COMP_REVOKE], COMP_REVOKE); 449 450 free(certdir); 451 free(keyfile); 452 free(acctkey); 453 free(chngdir); 454 free(alts); 455 return (COMP__MAX != rc ? EXIT_FAILURE : 456 (2 == c ? EXIT_SUCCESS : 2)); 457 usage: 458 fprintf(stderr, 459 "usage: acme-client [-bFmnNrv] [-a agreement] [-C challengedir]\n" 460 " [-c certdir] [-f accountkey] [-k domainkey]\n" 461 " [-s authority] domain [altnames...]\n"); 462 free(certdir); 463 free(keyfile); 464 free(acctkey); 465 free(chngdir); 466 return (EXIT_FAILURE); 467 } 468