xref: /openbsd-src/usr.sbin/acme-client/fileproc.c (revision bc5a8259a456844c67e446bf6bd66575acc64837)
1*bc5a8259Sbeck /*	$Id: fileproc.c,v 1.18 2021/07/12 15:09:20 beck Exp $ */
2de579d12Sflorian /*
3de579d12Sflorian  * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4de579d12Sflorian  *
5de579d12Sflorian  * Permission to use, copy, modify, and distribute this software for any
6de579d12Sflorian  * purpose with or without fee is hereby granted, provided that the above
7de579d12Sflorian  * copyright notice and this permission notice appear in all copies.
8de579d12Sflorian  *
9de579d12Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10de579d12Sflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11de579d12Sflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12de579d12Sflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13de579d12Sflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14de579d12Sflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15de579d12Sflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16de579d12Sflorian  */
17de579d12Sflorian 
184716c154Sbenno #include <sys/stat.h>
194716c154Sbenno 
20de579d12Sflorian #include <err.h>
21de579d12Sflorian #include <errno.h>
22de579d12Sflorian #include <fcntl.h>
23de579d12Sflorian #include <limits.h>
24de579d12Sflorian #include <stdio.h>
25de579d12Sflorian #include <stdlib.h>
26de579d12Sflorian #include <string.h>
27de579d12Sflorian #include <unistd.h>
28de579d12Sflorian 
29de579d12Sflorian #include "extern.h"
30de579d12Sflorian 
31de579d12Sflorian static int
serialise(const char * real,const char * v,size_t vsz,const char * v2,size_t v2sz)324716c154Sbenno serialise(const char *real, const char *v, size_t vsz, const char *v2, size_t v2sz)
33de579d12Sflorian {
34de579d12Sflorian 	int	  fd;
354716c154Sbenno 	char	 *tmp;
36de579d12Sflorian 
3747e51140Sflorian 	/* create backup hardlink */
3847e51140Sflorian 	if (asprintf(&tmp, "%s.1", real) == -1) {
3947e51140Sflorian 		warn("asprintf");
4047e51140Sflorian 		return 0;
4147e51140Sflorian 	}
4247e51140Sflorian 	(void) unlink(tmp);
4347e51140Sflorian 	if (link(real, tmp) == -1 && errno != ENOENT) {
4447e51140Sflorian 		warn("link");
4547e51140Sflorian 		free(tmp);
4647e51140Sflorian 		return 0;
4747e51140Sflorian 	}
4847e51140Sflorian 	free(tmp);
4947e51140Sflorian 
50de579d12Sflorian 	/*
51de579d12Sflorian 	 * Write into backup location, overwriting.
524716c154Sbenno 	 * Then atomically do the rename.
53de579d12Sflorian 	 */
54de579d12Sflorian 
554716c154Sbenno 	if (asprintf(&tmp, "%s.XXXXXXXXXX", real) == -1) {
564716c154Sbenno 		warn("asprintf");
5734335c11Sjsing 		return 0;
58de579d12Sflorian 	}
594716c154Sbenno 	if ((fd = mkstemp(tmp)) == -1) {
604716c154Sbenno 		warn("mkstemp");
614716c154Sbenno 		goto out;
624716c154Sbenno 	}
634716c154Sbenno 	if (fchmod(fd, 0444) == -1) {
644716c154Sbenno 		warn("fchmod");
654716c154Sbenno 		goto out;
664716c154Sbenno 	}
674716c154Sbenno 	if ((ssize_t)vsz != write(fd, v, vsz)) {
684716c154Sbenno 		warnx("write");
694716c154Sbenno 		goto out;
704716c154Sbenno 	}
714716c154Sbenno 	if (v2 != NULL && write(fd, v2, v2sz) != (ssize_t)v2sz) {
724716c154Sbenno 		warnx("write");
734716c154Sbenno 		goto out;
744716c154Sbenno 	}
754716c154Sbenno 	if (close(fd) == -1)
764716c154Sbenno 		goto out;
774716c154Sbenno 	if (rename(tmp, real) == -1) {
784716c154Sbenno 		warn("%s", real);
794716c154Sbenno 		goto out;
804716c154Sbenno 	}
81de579d12Sflorian 
824716c154Sbenno 	free(tmp);
8334335c11Sjsing 	return 1;
844716c154Sbenno out:
854716c154Sbenno 	if (fd != -1)
864716c154Sbenno 		close(fd);
874716c154Sbenno 	(void) unlink(tmp);
884716c154Sbenno 	free(tmp);
894716c154Sbenno 	return 0;
90de579d12Sflorian }
91de579d12Sflorian 
92de579d12Sflorian int
fileproc(int certsock,const char * certdir,const char * certfile,const char * chainfile,const char * fullchainfile)9362492c74Sflorian fileproc(int certsock, const char *certdir, const char *certfile, const char
9462492c74Sflorian     *chainfile, const char *fullchainfile)
95de579d12Sflorian {
967bce6888Sderaadt 	char		*csr = NULL, *ch = NULL;
97de579d12Sflorian 	size_t		 chsz, csz;
987bce6888Sderaadt 	int		 rc = 0;
99de579d12Sflorian 	long		 lval;
100de579d12Sflorian 	enum fileop	 op;
101de579d12Sflorian 
10261075b4cSflorian 	if (unveil(certdir, "rwc") == -1) {
103*bc5a8259Sbeck 		warn("unveil %s", certdir);
104de579d12Sflorian 		goto out;
105ec0d8c8bSderaadt 	}
106ec0d8c8bSderaadt 
107ec0d8c8bSderaadt 	/*
108ec0d8c8bSderaadt 	 * rpath and cpath for rename, wpath and cpath for
1094716c154Sbenno 	 * writing to the temporary. fattr for fchmod.
110ec0d8c8bSderaadt 	 */
1114716c154Sbenno 	if (pledge("stdio cpath wpath rpath fattr", NULL) == -1) {
112ec0d8c8bSderaadt 		warn("pledge");
113de579d12Sflorian 		goto out;
114ec0d8c8bSderaadt 	}
115de579d12Sflorian 
116de579d12Sflorian 	/* Read our operation. */
117de579d12Sflorian 
118de579d12Sflorian 	op = FILE__MAX;
1197cd8f039Sjsing 	if ((lval = readop(certsock, COMM_CHAIN_OP)) == 0)
120de579d12Sflorian 		op = FILE_STOP;
1217cd8f039Sjsing 	else if (lval == FILE_CREATE || lval == FILE_REMOVE)
122de579d12Sflorian 		op = lval;
123de579d12Sflorian 
124de579d12Sflorian 	if (FILE_STOP == op) {
125de579d12Sflorian 		rc = 1;
126de579d12Sflorian 		goto out;
127de579d12Sflorian 	} else if (FILE__MAX == op) {
128de579d12Sflorian 		warnx("unknown operation from certproc");
129de579d12Sflorian 		goto out;
130de579d12Sflorian 	}
131de579d12Sflorian 
132de579d12Sflorian 	/*
133de579d12Sflorian 	 * If revoking certificates, just unlink the files.
134de579d12Sflorian 	 * We return the special error code of 2 to indicate that the
135de579d12Sflorian 	 * certificates were removed.
136de579d12Sflorian 	 */
137de579d12Sflorian 
138de579d12Sflorian 	if (FILE_REMOVE == op) {
13962492c74Sflorian 		if (certfile) {
1407cd8f039Sjsing 			if (unlink(certfile) == -1 && errno != ENOENT) {
14161075b4cSflorian 				warn("%s", certfile);
142de579d12Sflorian 				goto out;
143de579d12Sflorian 			} else
14461075b4cSflorian 				dodbg("%s: unlinked", certfile);
14562492c74Sflorian 		}
146de579d12Sflorian 
14762492c74Sflorian 		if (chainfile) {
1487cd8f039Sjsing 			if (unlink(chainfile) == -1 && errno != ENOENT) {
14961075b4cSflorian 				warn("%s", chainfile);
150de579d12Sflorian 				goto out;
151de579d12Sflorian 			} else
15261075b4cSflorian 				dodbg("%s: unlinked", chainfile);
15362492c74Sflorian 		}
154de579d12Sflorian 
15562492c74Sflorian 		if (fullchainfile) {
1567cd8f039Sjsing 			if (unlink(fullchainfile) == -1 && errno != ENOENT) {
15761075b4cSflorian 				warn("%s", fullchainfile);
158de579d12Sflorian 				goto out;
159de579d12Sflorian 			} else
16061075b4cSflorian 				dodbg("%s: unlinked", fullchainfile);
16162492c74Sflorian 		}
162de579d12Sflorian 
163de579d12Sflorian 		rc = 2;
164de579d12Sflorian 		goto out;
165de579d12Sflorian 	}
166de579d12Sflorian 
167de579d12Sflorian 	/*
168de579d12Sflorian 	 * Start by downloading the chain PEM as a buffer.
169b8ee2fe2Sderaadt 	 * This is not NUL-terminated, but we're just going to guess
170de579d12Sflorian 	 * that it's well-formed and not actually touch the data.
171de579d12Sflorian 	 */
1727cd8f039Sjsing 	if ((ch = readbuf(certsock, COMM_CHAIN, &chsz)) == NULL)
173de579d12Sflorian 		goto out;
17462492c74Sflorian 
17562492c74Sflorian 	if (chainfile) {
1764716c154Sbenno 		if (!serialise(chainfile, ch, chsz, NULL, 0))
177de579d12Sflorian 			goto out;
178de579d12Sflorian 
17961075b4cSflorian 		dodbg("%s: created", chainfile);
18062492c74Sflorian 	}
181de579d12Sflorian 
182de579d12Sflorian 	/*
183de579d12Sflorian 	 * Next, wait until we receive the DER encoded (signed)
184de579d12Sflorian 	 * certificate from the network process.
185de579d12Sflorian 	 * This comes as a stream of bytes: we don't know how many, so
186de579d12Sflorian 	 * just keep downloading.
187de579d12Sflorian 	 */
188de579d12Sflorian 
1897cd8f039Sjsing 	if ((csr = readbuf(certsock, COMM_CSR, &csz)) == NULL)
190de579d12Sflorian 		goto out;
191d40ebfdcSflorian 
192d40ebfdcSflorian 	if (certfile) {
1934716c154Sbenno 		if (!serialise(certfile, csr, csz, NULL, 0))
194de579d12Sflorian 			goto out;
195de579d12Sflorian 
19661075b4cSflorian 		dodbg("%s: created", certfile);
197d40ebfdcSflorian 	}
198de579d12Sflorian 
199de579d12Sflorian 	/*
200de579d12Sflorian 	 * Finally, create the full-chain file.
201de579d12Sflorian 	 * This is just the concatenation of the certificate and chain.
202de579d12Sflorian 	 * We return the special error code 2 to indicate that the
203de579d12Sflorian 	 * on-file certificates were changed.
204de579d12Sflorian 	 */
20562492c74Sflorian 	if (fullchainfile) {
2064716c154Sbenno 		if (!serialise(fullchainfile, csr, csz, ch,
20762492c74Sflorian 		    chsz))
208de579d12Sflorian 			goto out;
209de579d12Sflorian 
21061075b4cSflorian 		dodbg("%s: created", fullchainfile);
21162492c74Sflorian 	}
212de579d12Sflorian 
213de579d12Sflorian 	rc = 2;
214de579d12Sflorian out:
215de579d12Sflorian 	close(certsock);
216de579d12Sflorian 	free(csr);
217de579d12Sflorian 	free(ch);
21834335c11Sjsing 	return rc;
219de579d12Sflorian }
220