/* $Id: fileproc.c,v 1.15 2018/07/29 20:15:23 benno 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" #ifdef __VMS #include "wcme_config.h" #include "vms.h" #endif static int serialise(const char *real, const char *v, size_t vsz, const char *v2, size_t v2sz) { int fd; char *tmp; /* * Write into backup location, overwriting. * Then atomically do the rename. */ #ifdef WCME_IDIOM tmp = UtilOds2FileName (NULL, (char*)real, ".tmp"); /* use 0777 to propagate protections */ if ((fd = creat(tmp, 0777)) == -1) { warnx("%s: creat", tmp); goto out; } if ((ssize_t)vsz != write(fd, v, vsz)) { warnx("%s: write", tmp); goto out; } if (v2 != NULL && write(fd, v2, v2sz) != (ssize_t)v2sz) { warnx("%s: write", tmp); goto out; } if (close(fd) == -1) goto out; real = UtilOds2FileName (NULL, (char*)real, NULL); if (rename(tmp, real) == -1) { warn("%s %s: rename", tmp, real); goto out; } if (CertManAppendPrivKey (real) == -1) { warn("%s: append private key", real); goto out; } return 1; #else 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; #endif 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; #ifdef WCME_DBG_FILEPROC doddbg ("(%d) %d |%s|%s|%s|%s|", __LINE__, certsock, certdir, certfile, chainfile, fullchainfile); #endif #ifdef WCME_IDIOM if (chdir(certdir) == -1) { warn("chdir"); goto out; } #else if (chroot(certdir) == -1) { warn("chroot"); goto out; } if (chdir("/") == -1) { warn("chdir"); goto out; } #endif /* * 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/%s", certdir, certfile); goto out; } else dodbg("%s/%s: unlinked", certdir, certfile); } if (chainfile) { if (unlink(chainfile) == -1 && errno != ENOENT) { warn("%s/%s", certdir, chainfile); goto out; } else dodbg("%s/%s: unlinked", certdir, chainfile); } if (fullchainfile) { if (unlink(fullchainfile) == -1 && errno != ENOENT) { warn("%s/%s", certdir, fullchainfile); goto out; } else dodbg("%s/%s: unlinked", certdir, 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/%s: created", certdir, 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/%s: created", certdir, 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/%s: created", certdir, fullchainfile); #ifdef WCME_IDIOM if (CertManInstall (fullchainfile) < 0) goto out; #endif } rc = 2; out: close(certsock); free(csr); free(ch); return rc; }