/* $Id: fileproc.c,v 1.17 2021/01/03 16:32:38 florian Exp $ */ /* * Copyright (c) 2016 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "extern.h" static int serialise(const char *real, const char *v, size_t vsz, const char *v2, size_t v2sz) { int fd; char *tmp; /* create backup hardlink */ if (asprintf(&tmp, "%s.1", real) == -1) { warn("asprintf"); return 0; } (void) unlink(tmp); if (link(real, tmp) == -1 && errno != ENOENT) { warn("link"); free(tmp); return 0; } free(tmp); /* * Write into backup location, overwriting. * Then atomically do the rename. */ if (asprintf(&tmp, "%s.XXXXXXXXXX", real) == -1) { warn("asprintf"); return 0; } if ((fd = mkstemp(tmp)) == -1) { warn("mkstemp"); goto out; } if (fchmod(fd, 0444) == -1) { warn("fchmod"); goto out; } if ((ssize_t)vsz != write(fd, v, vsz)) { warnx("write"); goto out; } if (v2 != NULL && write(fd, v2, v2sz) != (ssize_t)v2sz) { warnx("write"); goto out; } if (close(fd) == -1) goto out; if (rename(tmp, real) == -1) { warn("%s", real); goto out; } free(tmp); return 1; out: if (fd != -1) close(fd); (void) unlink(tmp); free(tmp); return 0; } int fileproc(int certsock, const char *certdir, const char *certfile, const char *chainfile, const char *fullchainfile) { char *csr = NULL, *ch = NULL; size_t chsz, csz; int rc = 0; long lval; enum fileop op; if (unveil(certdir, "rwc") == -1) { warn("unveil"); goto out; } /* * rpath and cpath for rename, wpath and cpath for * writing to the temporary. fattr for fchmod. */ if (pledge("stdio cpath wpath rpath fattr", NULL) == -1) { warn("pledge"); goto out; } /* Read our operation. */ op = FILE__MAX; if ((lval = readop(certsock, COMM_CHAIN_OP)) == 0) op = FILE_STOP; else if (lval == FILE_CREATE || lval == FILE_REMOVE) op = lval; if (FILE_STOP == op) { rc = 1; goto out; } else if (FILE__MAX == op) { warnx("unknown operation from certproc"); goto out; } /* * 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 (certfile) { if (unlink(certfile) == -1 && errno != ENOENT) { warn("%s", certfile); goto out; } else dodbg("%s: unlinked", certfile); } if (chainfile) { if (unlink(chainfile) == -1 && errno != ENOENT) { warn("%s", chainfile); goto out; } else dodbg("%s: unlinked", chainfile); } if (fullchainfile) { if (unlink(fullchainfile) == -1 && errno != ENOENT) { warn("%s", fullchainfile); goto out; } else dodbg("%s: unlinked", fullchainfile); } rc = 2; goto out; } /* * Start by downloading the chain PEM as a buffer. * This is not NUL-terminated, but we're just going to guess * that it's well-formed and not actually touch the data. */ if ((ch = readbuf(certsock, COMM_CHAIN, &chsz)) == NULL) goto out; if (chainfile) { if (!serialise(chainfile, ch, chsz, NULL, 0)) goto out; dodbg("%s: created", chainfile); } /* * 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 ((csr = readbuf(certsock, COMM_CSR, &csz)) == NULL) goto out; if (certfile) { if (!serialise(certfile, csr, csz, NULL, 0)) goto out; dodbg("%s: created", certfile); } /* * 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 (fullchainfile) { if (!serialise(fullchainfile, csr, csz, ch, chsz)) goto out; dodbg("%s: created", fullchainfile); } rc = 2; out: close(certsock); free(csr); free(ch); return rc; }