/* * Contact dnsproc and resolve a host. * Place the answers in "v" and return the number of answers, which can * be at most MAX_SERVERS_DNS. * Return <0 on failure. */ static ssize_t urlresolve(int fd, const char *host, struct source *v) { char *addr; size_t i, sz; long lval; if (writeop(fd, COMM_DNS, DNS_LOOKUP) <= 0) return(-1); else if (writestr(fd, COMM_DNSQ, host) <= 0) return(-1); else if ((lval = readop(fd, COMM_DNSLEN)) < 0) return(-1); sz = lval; assert(sz <= MAX_SERVERS_DNS); for (i = 0; i < sz; i++) { memset(&v[i], 0, sizeof(struct source)); if ((lval = readop(fd, COMM_DNSF)) < 0) goto err; else if (4 != lval && 6 != lval) goto err; else if (NULL == (addr = readstr(fd, COMM_DNSA))) goto err; v[i].family = lval; v[i].ip = addr; } return(sz); err: for (i = 0; i < sz; i++) free(v[i].ip); return(-1); }
void assem() { union token ttok; while(1) { readop(); if(tok.u != TOKFILE && tok.u != '<') { if(checkeos()) goto ealoop; ttok.u = tok.u; if(tok.u == TOKINT) { ttok.u = 2; agetw(); numval = tok.u; } readop(); switch(tok.u) { case '=': doequal(&ttok); goto ealoop; case ':': docolon(&ttok); continue; default: savop = tok.u; tok.u = ttok.u; break; } } opline(); dotmax(); ealoop: if(tok.u == '\n') ++line; if(DEBUG) printf("\nLine %d: ",line); if(tok.u == TOKEOF) return; } }
int fileproc(int certsock, const char *certdir, const struct config *cfg) { char *csr = NULL, *ch = NULL; char file[PATH_MAX]; size_t chsz, csz; int rc = 0; long lval; enum fileop op; time_t t; /* File-system and sandbox jailing. */ if ( ! sandbox_before()) goto out; else if ( ! dropfs(certdir)) goto out; else if ( ! sandbox_after(0)) goto out; /* Read our operation. */ op = FILE__MAX; if (0 == (lval = readop(certsock, COMM_CHAIN_OP))) op = FILE_STOP; else if (FILE_CREATE == lval || FILE_REMOVE == lval) op = lval; if (FILE_STOP == op) { rc = 1; goto out; } else if (FILE__MAX == op) { warnx("unknown operation from certproc"); goto out; } /* * If we're backing up, then copy all files (found) by linking * them to the file followed by the epoch in seconds. * If we're going to remove, the unlink(2) will cause the * original to go away. * If we're going to update, the rename(2) will replace the * certificate, leaving the backup as the only one. */ if (cfg->backup) { t = time(NULL); snprintf(file, sizeof(file), "cert-%llu.pem", (unsigned long long)t); if (-1 == link(CERT_PEM, file) && ENOENT != errno) { warnx("%s/%s", certdir, CERT_PEM); goto out; } else dodbg("%s/%s: linked to %s", certdir, CERT_PEM, file); snprintf(file, sizeof(file), "chain-%llu.pem", (unsigned long long)t); if (-1 == link(CHAIN_PEM, file) && ENOENT != errno) { warnx("%s/%s", certdir, CHAIN_PEM); goto out; } else dodbg("%s/%s: linked to %s", certdir, CHAIN_PEM, file); snprintf(file, sizeof(file), "fullchain-%llu.pem", (unsigned long long)t); if (-1 == link(FCHAIN_PEM, file) && ENOENT != errno) { warnx("%s/%s", certdir, FCHAIN_PEM); goto out; } else dodbg("%s/%s: linked to %s", certdir, FCHAIN_PEM, file); } /* * If revoking certificates, just unlink the files. * We return the special error code of 2 to indicate that the * certificates were removed. */ if (FILE_REMOVE == op) { if (-1 == unlink(CERT_PEM) && ENOENT != errno) { warn("%s/%s", certdir, CERT_PEM); goto out; } else dodbg("%s/%s: unlinked", certdir, CERT_PEM); if (-1 == unlink(CHAIN_PEM) && ENOENT != errno) { warn("%s/%s", certdir, CHAIN_PEM); goto out; } else dodbg("%s/%s: unlinked", certdir, CHAIN_PEM); if (-1 == unlink(FCHAIN_PEM) && ENOENT != errno) { warn("%s/%s", certdir, FCHAIN_PEM); goto out; } else dodbg("%s/%s: unlinked", certdir, FCHAIN_PEM); rc = 2; goto out; } /* * Start by downloading the chain PEM as a buffer. * This is not nil-terminated, but we're just going to guess * that it's well-formed and not actually touch the data. * Once downloaded, dump it into CHAIN_BAK. */ if (NULL == (ch = readbuf(certsock, COMM_CHAIN, &chsz))) goto out; if ( ! serialise(CHAIN_BAK, CHAIN_PEM, ch, chsz, NULL, 0)) goto out; dodbg("%s/%s: created", certdir, CHAIN_PEM); /* * Next, wait until we receive the DER encoded (signed) * certificate from the network process. * This comes as a stream of bytes: we don't know how many, so * just keep downloading. */ if (NULL == (csr = readbuf(certsock, COMM_CSR, &csz))) goto out; if ( ! serialise(CERT_BAK, CERT_PEM, csr, csz, NULL, 0)) goto out; dodbg("%s/%s: created", certdir, CERT_PEM); /* * Finally, create the full-chain file. * This is just the concatenation of the certificate and chain. * We return the special error code 2 to indicate that the * on-file certificates were changed. */ if ( ! serialise(FCHAIN_BAK, FCHAIN_PEM, csr, csz, ch, chsz)) goto out; dodbg("%s/%s: created", certdir, FCHAIN_PEM); rc = 2; out: close(certsock); free(csr); free(ch); return (rc); }
static signed int read32(unsigned pc) { return readop(pc) | (readop(pc+1) << 8)| (readop(pc+2) << 16)| (readop(pc+3) << 24); }
static signed short read16(unsigned pc) { return readop(pc) | (readop(pc+1) << 8); }
static signed char read8(unsigned pc) { return readop(pc); }
static int decode_AM(unsigned ipc, unsigned pc, int m, int opsize, char *out) { unsigned char mod = readop(pc); if(m) { switch(mod>>5) { case 0: // Double displacement (8 bit) out_AM_DoubleDisplacement(mod&0x1F, read8(pc+1), read8(pc+2), opsize, out); return 3; case 1: // Double displacement (16 bit) out_AM_DoubleDisplacement(mod&0x1F, read16(pc+1), read16(pc+3), opsize, out); return 5; case 2: // Double displacement (32 bit) out_AM_DoubleDisplacement(mod&0x1F, read32(pc+1), read32(pc+5), opsize, out); return 9; case 3: // Register out_AM_Register(mod&0x1F, out); return 1; case 4: // Autoincrement out_AM_Autoincrement(mod&0x1F, opsize, out); return 1; case 5: // Autodecrement out_AM_Autodecrement(mod&0x1F, opsize, out); return 1; case 6: switch (readop(pc+1)>>5) { case 0: // Displacement indexed (8 bit) out_AM_DisplacementIndexed(readop(pc+1)&0x1F, mod&0x1F, read8(pc+2), opsize, out); return 3; case 1: // Displacement indexed (16 bit) out_AM_DisplacementIndexed(readop(pc+1)&0x1F, mod&0x1F, read16(pc+2), opsize, out); return 4; case 2: // Displacement indexed (32 bit) out_AM_DisplacementIndexed(readop(pc+1)&0x1F, mod&0x1F, read32(pc+2), opsize, out); return 6; case 3: // Register indirect indexed out_AM_RegisterIndirectIndexed(readop(pc+1)&0x1F, mod&0x1F, opsize, out); return 2; case 4: // Displacement indirect indexed (8 bit) out_AM_DisplacementIndirectIndexed(readop(pc+1)&0x1F, mod&0x1F, read8(pc+2), opsize, out); return 3; case 5: // Displacement indirect indexed (16 bit) out_AM_DisplacementIndirectIndexed(readop(pc+1)&0x1F, mod&0x1F, read16(pc+2), opsize, out); return 4; case 6: // Displacement indirect indexed (32 bit) out_AM_DisplacementIndirectIndexed(readop(pc+1)&0x1F, mod&0x1F, read32(pc+2), opsize, out); return 6; case 7: switch (readop(pc+1)&0x1F) { case 16: // PC Displacement Indexed (8 bit) out_AM_PCDisplacementIndexed(ipc, read8(pc+2), mod&0x1F, opsize, out); return 3; case 17: // PC Displacement Indexed (16 bit) out_AM_PCDisplacementIndexed(ipc, read16(pc+2), mod&0x1F, opsize, out); return 4; case 18: // PC Displacement Indexed (32 bit) out_AM_PCDisplacementIndexed(ipc, read32(pc+2), mod&0x1F, opsize, out); return 6; case 19: // Direct Address Indexed out_AM_DirectAddressIndexed(read32(pc+2), mod&0x1F, opsize, out); return 6; case 24: // PC Displacement Indirect Indexed(8 bit) out_AM_PCDisplacementIndirectIndexed(ipc, read8(pc+2), mod&0x1F, opsize, out); return 3; case 25: // PC Displacement Indirect Indexed (16 bit) out_AM_PCDisplacementIndirectIndexed(ipc, read16(pc+2), mod&0x1F, opsize, out); return 4; case 26: // PC Displacement Indirect Indexed (32 bit) out_AM_PCDisplacementIndirectIndexed(ipc, read32(pc+2), mod&0x1F, opsize, out); return 6; case 27: // Direct Address Deferred Indexed out_AM_DirectAddressDeferredIndexed(read32(pc+2), mod&0x1F, opsize, out); return 6; default: strcat(out, "!ERRAM3"); return 1; } default: strcat(out, "!ERRAM2"); return 1; } default: strcat(out, "!ERRAM1"); return 1; } } else { switch(mod>>5) {
int dnsproc(int nfd) { int rc, cc; char *look, *last; struct addr v[MAX_SERVERS_DNS]; long lval; size_t i; ssize_t vsz; enum dnsop op; rc = 0; look = last = NULL; vsz = 0; /* * Why don't we chroot() here? * On OpenBSD, the pledge(2) takes care of our constraining the * environment to DNS resolution only, so the chroot(2) is * unnecessary. * On Mac OS X, we can't chroot(2): we'd need to have an mdns * responder thing in each jail. * On Linux, forget it. getaddrinfo(2) pulls on all sorts of * mystery meat. */ if ( ! sandbox_before()) goto out; else if ( ! dropprivs()) goto out; else if ( ! sandbox_after()) goto out; /* * This is simple: just loop on a request operation, and each * time we write back zero or more entries. * Also do a simple trick and cache the last lookup. */ for (;;) { op = DNS__MAX; if (0 == (lval = readop(nfd, COMM_DNS))) op = DNS_STOP; else if (DNS_LOOKUP == lval) op = lval; if (DNS__MAX == op) { warnx("unknown operation from netproc"); goto out; } else if (DNS_STOP == op) break; if (NULL == (look = readstr(nfd, COMM_DNSQ))) goto out; /* * Check if we're asked to repeat the lookup. * If not, request it from host_dns(). */ if (NULL == last || strcmp(look, last)) { if ((vsz = host_dns(look, v)) < 0) goto out; free(last); last = look; look = NULL; } else { doddbg("%s: cached", look); free(look); look = NULL; } if (0 == (cc = writeop(nfd, COMM_DNSLEN, vsz))) break; else if (cc < 0) goto out; for (i = 0; i < (size_t)vsz; i++) { if (writeop(nfd, COMM_DNSF, v[i].family) <= 0) goto out; if (writestr(nfd, COMM_DNSA, v[i].ip) <= 0) goto out; } } rc = 1; out: close(nfd); free(look); free(last); return(rc); }
int certproc(int netsock, int filesock) { char *csr, *chain, *url; unsigned char *csrcp, *chaincp; size_t csrsz, chainsz; int i, rc, idx, cc; enum certop op; long lval; X509 *x, *chainx; X509_EXTENSION *ext; X509V3_EXT_METHOD *method; void *entries; STACK_OF(CONF_VALUE) *val; CONF_VALUE *nval; ext = NULL; idx = -1; method = NULL; chain = csr = url = NULL; rc = 0; x = chainx = NULL; /* File-system and sandbox jailing. */ if ( ! sandbox_before()) goto out; ERR_load_crypto_strings(); if ( ! dropfs(PATH_VAR_EMPTY)) goto out; else if ( ! dropprivs()) goto out; else if ( ! sandbox_after()) goto out; /* Read what the netproc wants us to do. */ op = CERT__MAX; if (0 == (lval = readop(netsock, COMM_CSR_OP))) op = CERT_STOP; else if (CERT_REVOKE == lval || CERT_UPDATE == lval) op = lval; if (CERT_STOP == op) { rc = 1; goto out; } else if (CERT__MAX == op) { warnx("unknown operation from netproc"); goto out; } /* * Pass revocation right through to fileproc. * If the reader is terminated, ignore it. */ if (CERT_REVOKE == op) { if (writeop(filesock, COMM_CHAIN_OP, FILE_REMOVE) >= 0) rc = 1; goto out; } /* * Wait until we receive the DER encoded (signed) certificate * from the network process. * Then convert the DER encoding into an X509 certificate. */ if (NULL == (csr = readbuf(netsock, COMM_CSR, &csrsz))) goto out; csrcp = (u_char *)csr; x = d2i_X509(NULL, (const u_char **)&csrcp, csrsz); if (NULL == x) { warnx("d2i_X509"); goto out; } /* * Extract the CA Issuers from its NID. * TODO: I have no idea what I'm doing. */ idx = X509_get_ext_by_NID(x, NID_info_access, idx); if (idx >= 0 && NULL != (ext = X509_get_ext(x, idx))) method = (X509V3_EXT_METHOD *)X509V3_EXT_get(ext); entries = X509_get_ext_d2i(x, NID_info_access, 0, 0); if (NULL != method && NULL != entries) { val = method->i2v(method, entries, 0); for (i = 0; i < sk_CONF_VALUE_num(val); i++) { nval = sk_CONF_VALUE_value(val, i); if (strcmp(nval->name, "CA Issuers - URI")) continue; url = strdup(nval->value); if (NULL == url) { warn("strdup"); goto out; } break; } } if (NULL == url) { warnx("no CA issuer registered with certificate"); goto out; } /* Write the CA issuer to the netsock. */ if (writestr(netsock, COMM_ISSUER, url) <= 0) goto out; /* Read the full-chain back from the netsock. */ if (NULL == (chain = readbuf(netsock, COMM_CHAIN, &chainsz))) goto out; /* * Then check if the chain is PEM-encoded by looking to see if * it begins with the PEM marker. * If so, ship it as-is; otherwise, convert to a PEM encoded * buffer and ship that. * FIXME: if PEM, re-parse it. */ if (chainsz <= strlen(MARKER) || strncmp(chain, MARKER, strlen(MARKER))) { chaincp = (u_char *)chain; chainx = d2i_X509(NULL, (const u_char **)&chaincp, chainsz); if (NULL == chainx) { warnx("d2i_X509"); goto out; } free(chain); if (NULL == (chain = x509buf(chainx, &chainsz))) goto out; } /* Allow reader termination to just push us out. */ if (0 == (cc = writeop(filesock, COMM_CHAIN_OP, FILE_CREATE))) rc = 1; if (cc <= 0) goto out; if (0 == (cc = writebuf(filesock, COMM_CHAIN, chain, chainsz))) rc = 1; if (cc <= 0) goto out; /* * Next, convert the X509 to a buffer and send that. * Reader failure doesn't change anything. */ free(chain); if (NULL == (chain = x509buf(x, &chainsz))) goto out; if (writebuf(filesock, COMM_CSR, chain, chainsz) < 0) goto out; rc = 1; out: close(netsock); close(filesock); if (NULL != x) X509_free(x); if (NULL != chainx) X509_free(chainx); free(csr); free(url); free(chain); ERR_print_errors_fp(stderr); ERR_free_strings(); return(rc); }
int chngproc(int netsock, const char *root, int remote) { int rc; long lval; enum chngop op; char *tok, *th, *fmt; char **fs; size_t i, fsz; void *pp; int fd, cc; rc = 0; th = tok = fmt = NULL; fd = -1; fs = NULL; fsz = 0; /* File-system and sandbox jailing. */ if ( ! sandbox_before()) goto out; else if ( ! dropfs(root)) goto out; else if ( ! sandbox_after()) goto out; /* * Loop while we wait to get a thumbprint and token. * We'll get this for each SAN request. */ for (;;) { op = CHNG__MAX; if (0 == (lval = readop(netsock, COMM_CHNG_OP))) op = CHNG_STOP; else if (CHNG_SYN == lval) op = lval; if (CHNG__MAX == op) { warnx("unknown operation from netproc"); goto out; } else if (CHNG_STOP == op) break; assert(CHNG_SYN == op); /* * Read the thumbprint and token. * The token is the filename, so store that in a vector * of tokens that we'll later clean up. */ if (NULL == (th = readstr(netsock, COMM_THUMB))) goto out; else if (NULL == (tok = readstr(netsock, COMM_TOK))) goto out; /* Vector appending... */ pp = realloc(fs, (fsz + 1) * sizeof(char *)); if (NULL == pp) { warn("realloc"); goto out; } fs = pp; fs[fsz] = tok; tok = NULL; fsz++; if (-1 == asprintf(&fmt, "%s.%s", fs[fsz - 1], th)) { warn("asprintf"); goto out; } /* * I use this for testing when letskencrypt is being run * on machines apart from where I'm hosting the * challenge directory. * DON'T DEPEND ON THIS FEATURE. */ if (remote) { puts("RUN THIS IN THE CHALLENGE DIRECTORY"); puts("YOU HAVE 20 SECONDS..."); printf("doas sh -c \"echo %s > %s\"\n", fmt, fs[fsz - 1]); sleep(20); puts("TIME'S UP."); } else { /* * Create and write to our challenge file. * Note: we use file descriptors instead of FILE * because we want to minimise our pledges. */ fd = open(fs[fsz - 1], O_WRONLY|O_EXCL|O_CREAT, 0444); if (-1 == fd) { warn("%s", fs[fsz - 1]); goto out; } if (-1 == write(fd, fmt, strlen(fmt))) { warn("%s", fs[fsz - 1]); goto out; } else if (-1 == close(fd)) { warn("%s", fs[fsz - 1]); goto out; } fd = -1; } free(th); free(fmt); th = fmt = NULL; dodbg("%s/%s: created", root, fs[fsz - 1]); /* * Write our acknowledgement. * Ignore reader failure. */ cc = writeop(netsock, COMM_CHNG_ACK, CHNG_ACK); if (0 == cc) break; if (cc < 0) goto out; } rc = 1; out: close(netsock); if (-1 != fd) close(fd); for (i = 0; i < fsz; i++) { if (-1 == unlink(fs[i]) && ENOENT != errno) warn("%s", fs[i]); free(fs[i]); } free(fs); free(fmt); free(th); free(tok); return(rc); }
/* * Here we communicate with the letsencrypt server. * For this, we'll need the certificate we want to upload and our * account key information. */ int netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd, int newacct, int revoke, int staging, const char *const *alts, size_t altsz) { int rc; size_t i; char *cert, *thumb, *url; struct conn c; struct capaths paths; struct chng *chngs; long lval; rc = 0; memset(&paths, 0, sizeof(struct capaths)); memset(&c, 0, sizeof(struct conn)); url = cert = thumb = NULL; chngs = NULL; /* File-system, user, and sandbox jail. */ if ( ! sandbox_before()) goto out; else if ( ! dropfs(PATH_VAR_EMPTY)) goto out; else if ( ! dropprivs()) goto out; else if ( ! sandbox_after()) goto out; /* * Wait until the acctproc, keyproc, and revokeproc have started * up and are ready to serve us data. * There's no point in running if these don't work. * Then check whether revokeproc indicates that the certificate * on file (if any) can be updated. */ if (0 == (lval = readop(afd, COMM_ACCT_STAT))) { rc = 1; goto out; } else if (ACCT_READY != lval) { warnx("unknown operation from acctproc"); goto out; } if (0 == (lval = readop(kfd, COMM_KEY_STAT))) { rc = 1; goto out; } else if (KEY_READY != lval) { warnx("unknown operation from keyproc"); goto out; } if (0 == (lval = readop(rfd, COMM_REVOKE_RESP))) { rc = 1; goto out; } else if (REVOKE_EXP != lval && REVOKE_OK != lval) { warnx("unknown operation from revokeproc"); goto out; } /* If our certificate is up-to-date, return now. */ if (REVOKE_OK == lval) { rc = 1; goto out; } /* Allocate main state. */ chngs = calloc(altsz, sizeof(struct chng)); if (NULL == chngs) { warn("calloc"); goto out; } c.dfd = dfd; c.fd = afd; c.na = staging ? URL_STAGE_CA : URL_REAL_CA; /* * Look up the domain of the ACME server. * We'll use this ourselves instead of having libcurl do the DNS * resolution itself. */ if ( ! dodirs(&c, c.na, &paths)) goto out; /* * If we're meant to revoke, then wait for revokeproc to send us * the certificate (if it's found at all). * Following that, submit the request to the CA then notify the * certproc, which will in turn notify the fileproc. */ if (revoke) { if (NULL == (cert = readstr(rfd, COMM_CSR))) goto out; if ( ! dorevoke(&c, paths.revokecert, cert)) goto out; else if (writeop(cfd, COMM_CSR_OP, CERT_REVOKE) > 0) rc = 1; goto out; } /* If new, register with the CA server. */ if (newacct && ! donewreg(&c, &paths)) goto out; /* Pre-authorise all domains with CA server. */ for (i = 0; i < altsz; i++) if ( ! dochngreq(&c, alts[i], &chngs[i], &paths)) goto out; /* * We now have our challenges. * We need to ask the acctproc for the thumbprint. * We'll combine this to the challenge to create our response, * which will be orchestrated by the chngproc. */ if (writeop(afd, COMM_ACCT, ACCT_THUMBPRINT) <= 0) goto out; else if (NULL == (thumb = readstr(afd, COMM_THUMB))) goto out; /* We'll now ask chngproc to build the challenge. */ for (i = 0; i < altsz; i++) { if (writeop(Cfd, COMM_CHNG_OP, CHNG_SYN) <= 0) goto out; else if (writestr(Cfd, COMM_THUMB, thumb) <= 0) goto out; else if (writestr(Cfd, COMM_TOK, chngs[i].token) <= 0) goto out; /* Read that the challenge has been made. */ if (CHNG_ACK != readop(Cfd, COMM_CHNG_ACK)) goto out; /* Write to the CA that it's ready. */ if ( ! dochngresp(&c, &chngs[i], thumb)) goto out; } /* * We now wait on the ACME server for each domain. * Connect to the server (assume it's the same server) once * every five seconds. */ for (i = 0; i < altsz; i++) { if (1 == chngs[i].status) continue; if (chngs[i].retry++ >= RETRY_MAX) { warnx("%s: too many tries", chngs[i].uri); goto out; } /* Sleep before every attempt. */ sleep(RETRY_DELAY); if ( ! dochngcheck(&c, &chngs[i])) goto out; } /* * Write our acknowledgement that the challenges are over. * The challenge process will remove all of the files. */ if (writeop(Cfd, COMM_CHNG_OP, CHNG_STOP) <= 0) goto out; /* Wait to receive the certificate itself. */ if (NULL == (cert = readstr(kfd, COMM_CERT))) goto out; /* * Otherwise, submit the CA for signing, download the signed * copy, and ship that into the certificate process for copying. */ if ( ! docert(&c, paths.newcert, cert)) goto out; else if (writeop(cfd, COMM_CSR_OP, CERT_UPDATE) <= 0) goto out; else if (writebuf(cfd, COMM_CSR, c.buf.buf, c.buf.sz) <= 0) goto out; /* * Read back the issuer from the certproc. * Then contact the issuer to get the certificate chain. * Write this chain directly back to the certproc. */ if (NULL == (url = readstr(cfd, COMM_ISSUER))) goto out; else if ( ! dofullchain(&c, url)) goto out; else if (writebuf(cfd, COMM_CHAIN, c.buf.buf, c.buf.sz) <= 0) goto out; rc = 1; out: close(cfd); close(kfd); close(afd); close(Cfd); close(dfd); close(rfd); free(cert); free(url); free(thumb); free(c.buf.buf); if (NULL != chngs) for (i = 0; i < altsz; i++) json_free_challenge(&chngs[i]); free(chngs); json_free_capaths(&paths); return(rc); }