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 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 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