/* $Id: fileproc.c,v 1.12 2016/10/29 22:25:48 kristaps 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. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "extern.h" static int serialise(const char *tmp, const char *real, const char *v, size_t vsz, const char *v2, size_t v2sz) { int fd; #ifdef WCMEDBGFILEPROC doddbg ("(%d) %s %s", __LINE__, tmp, real); #endif /* * Write into backup location, overwriting. * Then atomically (?) do the rename. */ #ifndef __VMS fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0444); #else { UtilSysPrv(); /* use 0777 to propagate protections */ fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0777); UtilMereMortal(); } #endif if (-1 == fd) { warn("%s", tmp); return (0); } else if ((ssize_t)vsz != write(fd, v, vsz)) { warnx("%s", tmp); close(fd); return (0); } else if (NULL != v2 && (ssize_t)v2sz != write(fd, v2, v2sz)) { warnx("%s", tmp); close(fd); return (0); } else if (-1 == close(fd)) { warn("%s", tmp); return (0); #ifndef __VMS } else if (-1 == rename(tmp, real)) { warn("%s", real); return (0); } #else } else { int retval; #ifdef WCMEIDIOM retval = CertManAppendPrivKey (tmp); #endif UtilSysPrv(); if (!retval) retval = rename (tmp, real); UtilMereMortal(); if (-1 == retval) { warn("%s", real); return (0); } } #endif /* __VMS */ return (1); } int fileproc(int certsock, int backup, const char *certdir) { char *csr = NULL, *ch = NULL; char file[PATH_MAX]; size_t chsz, csz; int rc = 0; long lval; enum fileop op; time_t t; #ifdef WCMEDBGFILEPROC doddbg ("(%d) %d %s", __LINE__, backup, certdir); #endif /* 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. */ #ifdef WCMEDBGFILEPROC doddbg ("(%d)", __LINE__); #endif 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; } #ifdef WCMEDBGFILEPROC doddbg ("(%d) %d", __LINE__, op); #endif /* * 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 (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) { #ifndef __VMS if (-1 == unlink(CERT_PEM) && ENOENT != errno) { warn("%s/%s", certdir, CERT_PEM); goto out; } else dodbg("%s/%s: unlinked", certdir, CERT_PEM); #else { char *fname; UtilSysPrv(); #ifndef WCMEIDIOM fname = doasprintf ("%s/%s", (char*)certdir, CERT_PEM); #else fname = strdup (UtilOds2CertName ((char*)certdir, CERT_PEM)); #endif if (remove(fname) && ENOENT != errno) { UtilMereMortal(); warn("%s", fname); goto out; } /* all versions */ while (!remove (fname)); UtilMereMortal(); dodbg("%s: unlinked", fname); free (fname); } #endif /* __VMS */ 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; #ifndef __VMS if ( ! serialise(CHAIN_BAK, CHAIN_PEM, ch, chsz, NULL, 0)) goto out; #else { int retval; char *bname, *pname; #ifndef WCMEIDIOM bname = doasprintf ("%s/%s", (char*)certdir, CHAIN_BAK); pname = doasprintf ("%s/%s", (char*)certdir, CHAIN_PEM); #else bname = strdup (UtilOds2CertName ((char*)certdir, CHAIN_BAK)); pname = strdup (UtilOds2CertName ((char*)certdir, CHAIN_PEM)); #endif retval = serialise(bname, pname, ch, chsz, NULL, 0); free (bname); free (pname); if (!retval) goto out; } #endif /* __VMS */ 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; #ifndef __VMS if ( ! serialise(CERT_BAK, CERT_PEM, csr, csz, NULL, 0)) goto out; #else { int retval; char *bname, *pname; #ifndef WCMEIDIOM bname = doasprintf ("%s/%s", (char*)certdir, CERT_BAK); pname = doasprintf ("%s/%s", (char*)certdir, CERT_PEM); #else bname = strdup (UtilOds2CertName ((char*)certdir, CERT_BAK)); pname = strdup (UtilOds2CertName ((char*)certdir, CERT_PEM)); #endif retval = serialise(bname, pname, ch, chsz, NULL, 0); free (bname); free (pname); if (!retval) goto out; } #endif /* __VMS */ 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. */ #ifndef __VMS if ( ! serialise(FCHAIN_BAK, FCHAIN_PEM, csr, csz, ch, chsz)) goto out; dodbg("%s/%s: created", certdir, FCHAIN_PEM); #else { int retval; char *bname, *pname; #ifndef WCMEIDIOM bname = doasprintf ("%s/%s", (char*)certdir, FCHAIN_BAK); pname = doasprintf ("%s/%s", (char*)certdir, FCHAIN_PEM); #else bname = strdup (UtilOds2CertName ((char*)certdir, FCHAIN_BAK)); pname = strdup (UtilOds2CertName ((char*)certdir, FCHAIN_PEM)); #endif retval = serialise(bname, pname, csr, csz, ch, chsz); #ifdef WCMEIDIOM if (retval) { dodbg("%s/%s: created", certdir, FCHAIN_PEM); retval = CertManInstall (pname); if (-1 == retval) retval = 0; } #endif free (bname); free (pname); if (!retval) goto out; } #endif /* __VMS */ rc = 2; out: #ifdef WCMEDBGFILEPROC doddbg ("(%d) %d", __LINE__, rc); #endif close(certsock); free(csr); free(ch); return (rc); }