xref: /openbsd-src/usr.sbin/acme-client/fileproc.c (revision 9f11ffb7133c203312a01e4b986886bc88c7d74b)
1 /*	$Id: fileproc.c,v 1.15 2018/07/29 20:15:23 benno Exp $ */
2 /*
3  * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/stat.h>
19 
20 #include <err.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include "extern.h"
30 
31 static int
32 serialise(const char *real, const char *v, size_t vsz, const char *v2, size_t v2sz)
33 {
34 	int	  fd;
35 	char	 *tmp;
36 
37 	/*
38 	 * Write into backup location, overwriting.
39 	 * Then atomically do the rename.
40 	 */
41 
42 	if (asprintf(&tmp, "%s.XXXXXXXXXX", real) == -1) {
43 		warn("asprintf");
44 		return 0;
45 	}
46 	if ((fd = mkstemp(tmp)) == -1) {
47 		warn("mkstemp");
48 		goto out;
49 	}
50 	if (fchmod(fd, 0444) == -1) {
51 		warn("fchmod");
52 		goto out;
53 	}
54 	if ((ssize_t)vsz != write(fd, v, vsz)) {
55 		warnx("write");
56 		goto out;
57 	}
58 	if (v2 != NULL && write(fd, v2, v2sz) != (ssize_t)v2sz) {
59 		warnx("write");
60 		goto out;
61 	}
62 	if (close(fd) == -1)
63 		goto out;
64 	if (rename(tmp, real) == -1) {
65 		warn("%s", real);
66 		goto out;
67 	}
68 
69 	free(tmp);
70 	return 1;
71 out:
72 	if (fd != -1)
73 		close(fd);
74 	(void) unlink(tmp);
75 	free(tmp);
76 	return 0;
77 }
78 
79 int
80 fileproc(int certsock, const char *certdir, const char *certfile, const char
81     *chainfile, const char *fullchainfile)
82 {
83 	char		*csr = NULL, *ch = NULL;
84 	size_t		 chsz, csz;
85 	int		 rc = 0;
86 	long		 lval;
87 	enum fileop	 op;
88 
89 	if (chroot(certdir) == -1) {
90 		warn("chroot");
91 		goto out;
92 	}
93 	if (chdir("/") == -1) {
94 		warn("chdir");
95 		goto out;
96 	}
97 
98 	/*
99 	 * rpath and cpath for rename, wpath and cpath for
100 	 * writing to the temporary. fattr for fchmod.
101 	 */
102 	if (pledge("stdio cpath wpath rpath fattr", NULL) == -1) {
103 		warn("pledge");
104 		goto out;
105 	}
106 
107 	/* Read our operation. */
108 
109 	op = FILE__MAX;
110 	if ((lval = readop(certsock, COMM_CHAIN_OP)) == 0)
111 		op = FILE_STOP;
112 	else if (lval == FILE_CREATE || lval == FILE_REMOVE)
113 		op = lval;
114 
115 	if (FILE_STOP == op) {
116 		rc = 1;
117 		goto out;
118 	} else if (FILE__MAX == op) {
119 		warnx("unknown operation from certproc");
120 		goto out;
121 	}
122 
123 	/*
124 	 * If revoking certificates, just unlink the files.
125 	 * We return the special error code of 2 to indicate that the
126 	 * certificates were removed.
127 	 */
128 
129 	if (FILE_REMOVE == op) {
130 		if (certfile) {
131 			if (unlink(certfile) == -1 && errno != ENOENT) {
132 				warn("%s/%s", certdir, certfile);
133 				goto out;
134 			} else
135 				dodbg("%s/%s: unlinked", certdir, certfile);
136 		}
137 
138 		if (chainfile) {
139 			if (unlink(chainfile) == -1 && errno != ENOENT) {
140 				warn("%s/%s", certdir, chainfile);
141 				goto out;
142 			} else
143 				dodbg("%s/%s: unlinked", certdir, chainfile);
144 		}
145 
146 		if (fullchainfile) {
147 			if (unlink(fullchainfile) == -1 && errno != ENOENT) {
148 				warn("%s/%s", certdir, fullchainfile);
149 				goto out;
150 			} else
151 				dodbg("%s/%s: unlinked", certdir,
152 				    fullchainfile);
153 		}
154 
155 		rc = 2;
156 		goto out;
157 	}
158 
159 	/*
160 	 * Start by downloading the chain PEM as a buffer.
161 	 * This is not NUL-terminated, but we're just going to guess
162 	 * that it's well-formed and not actually touch the data.
163 	 */
164 	if ((ch = readbuf(certsock, COMM_CHAIN, &chsz)) == NULL)
165 		goto out;
166 
167 	if (chainfile) {
168 		if (!serialise(chainfile, ch, chsz, NULL, 0))
169 			goto out;
170 
171 		dodbg("%s/%s: created", certdir, chainfile);
172 	}
173 
174 	/*
175 	 * Next, wait until we receive the DER encoded (signed)
176 	 * certificate from the network process.
177 	 * This comes as a stream of bytes: we don't know how many, so
178 	 * just keep downloading.
179 	 */
180 
181 	if ((csr = readbuf(certsock, COMM_CSR, &csz)) == NULL)
182 		goto out;
183 
184 	if (certfile) {
185 		if (!serialise(certfile, csr, csz, NULL, 0))
186 			goto out;
187 
188 		dodbg("%s/%s: created", certdir, certfile);
189 	}
190 
191 	/*
192 	 * Finally, create the full-chain file.
193 	 * This is just the concatenation of the certificate and chain.
194 	 * We return the special error code 2 to indicate that the
195 	 * on-file certificates were changed.
196 	 */
197 	if (fullchainfile) {
198 		if (!serialise(fullchainfile, csr, csz, ch,
199 		    chsz))
200 			goto out;
201 
202 		dodbg("%s/%s: created", certdir, fullchainfile);
203 	}
204 
205 	rc = 2;
206 out:
207 	close(certsock);
208 	free(csr);
209 	free(ch);
210 	return rc;
211 }
212