1 /* $Id: fileproc.c,v 1.18 2021/07/12 15:09:20 beck 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
serialise(const char * real,const char * v,size_t vsz,const char * v2,size_t v2sz)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 /* create backup hardlink */
38 if (asprintf(&tmp, "%s.1", real) == -1) {
39 warn("asprintf");
40 return 0;
41 }
42 (void) unlink(tmp);
43 if (link(real, tmp) == -1 && errno != ENOENT) {
44 warn("link");
45 free(tmp);
46 return 0;
47 }
48 free(tmp);
49
50 /*
51 * Write into backup location, overwriting.
52 * Then atomically do the rename.
53 */
54
55 if (asprintf(&tmp, "%s.XXXXXXXXXX", real) == -1) {
56 warn("asprintf");
57 return 0;
58 }
59 if ((fd = mkstemp(tmp)) == -1) {
60 warn("mkstemp");
61 goto out;
62 }
63 if (fchmod(fd, 0444) == -1) {
64 warn("fchmod");
65 goto out;
66 }
67 if ((ssize_t)vsz != write(fd, v, vsz)) {
68 warnx("write");
69 goto out;
70 }
71 if (v2 != NULL && write(fd, v2, v2sz) != (ssize_t)v2sz) {
72 warnx("write");
73 goto out;
74 }
75 if (close(fd) == -1)
76 goto out;
77 if (rename(tmp, real) == -1) {
78 warn("%s", real);
79 goto out;
80 }
81
82 free(tmp);
83 return 1;
84 out:
85 if (fd != -1)
86 close(fd);
87 (void) unlink(tmp);
88 free(tmp);
89 return 0;
90 }
91
92 int
fileproc(int certsock,const char * certdir,const char * certfile,const char * chainfile,const char * fullchainfile)93 fileproc(int certsock, const char *certdir, const char *certfile, const char
94 *chainfile, const char *fullchainfile)
95 {
96 char *csr = NULL, *ch = NULL;
97 size_t chsz, csz;
98 int rc = 0;
99 long lval;
100 enum fileop op;
101
102 if (unveil(certdir, "rwc") == -1) {
103 warn("unveil %s", certdir);
104 goto out;
105 }
106
107 /*
108 * rpath and cpath for rename, wpath and cpath for
109 * writing to the temporary. fattr for fchmod.
110 */
111 if (pledge("stdio cpath wpath rpath fattr", NULL) == -1) {
112 warn("pledge");
113 goto out;
114 }
115
116 /* Read our operation. */
117
118 op = FILE__MAX;
119 if ((lval = readop(certsock, COMM_CHAIN_OP)) == 0)
120 op = FILE_STOP;
121 else if (lval == FILE_CREATE || lval == FILE_REMOVE)
122 op = lval;
123
124 if (FILE_STOP == op) {
125 rc = 1;
126 goto out;
127 } else if (FILE__MAX == op) {
128 warnx("unknown operation from certproc");
129 goto out;
130 }
131
132 /*
133 * If revoking certificates, just unlink the files.
134 * We return the special error code of 2 to indicate that the
135 * certificates were removed.
136 */
137
138 if (FILE_REMOVE == op) {
139 if (certfile) {
140 if (unlink(certfile) == -1 && errno != ENOENT) {
141 warn("%s", certfile);
142 goto out;
143 } else
144 dodbg("%s: unlinked", certfile);
145 }
146
147 if (chainfile) {
148 if (unlink(chainfile) == -1 && errno != ENOENT) {
149 warn("%s", chainfile);
150 goto out;
151 } else
152 dodbg("%s: unlinked", chainfile);
153 }
154
155 if (fullchainfile) {
156 if (unlink(fullchainfile) == -1 && errno != ENOENT) {
157 warn("%s", fullchainfile);
158 goto out;
159 } else
160 dodbg("%s: unlinked", fullchainfile);
161 }
162
163 rc = 2;
164 goto out;
165 }
166
167 /*
168 * Start by downloading the chain PEM as a buffer.
169 * This is not NUL-terminated, but we're just going to guess
170 * that it's well-formed and not actually touch the data.
171 */
172 if ((ch = readbuf(certsock, COMM_CHAIN, &chsz)) == NULL)
173 goto out;
174
175 if (chainfile) {
176 if (!serialise(chainfile, ch, chsz, NULL, 0))
177 goto out;
178
179 dodbg("%s: created", chainfile);
180 }
181
182 /*
183 * Next, wait until we receive the DER encoded (signed)
184 * certificate from the network process.
185 * This comes as a stream of bytes: we don't know how many, so
186 * just keep downloading.
187 */
188
189 if ((csr = readbuf(certsock, COMM_CSR, &csz)) == NULL)
190 goto out;
191
192 if (certfile) {
193 if (!serialise(certfile, csr, csz, NULL, 0))
194 goto out;
195
196 dodbg("%s: created", certfile);
197 }
198
199 /*
200 * Finally, create the full-chain file.
201 * This is just the concatenation of the certificate and chain.
202 * We return the special error code 2 to indicate that the
203 * on-file certificates were changed.
204 */
205 if (fullchainfile) {
206 if (!serialise(fullchainfile, csr, csz, ch,
207 chsz))
208 goto out;
209
210 dodbg("%s: created", fullchainfile);
211 }
212
213 rc = 2;
214 out:
215 close(certsock);
216 free(csr);
217 free(ch);
218 return rc;
219 }
220