1 /* $Id: fileproc.c,v 1.16 2019/06/16 19:49:13 florian 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 (unveil(certdir, "rwc") == -1) { 90 warn("unveil"); 91 goto out; 92 } 93 94 /* 95 * rpath and cpath for rename, wpath and cpath for 96 * writing to the temporary. fattr for fchmod. 97 */ 98 if (pledge("stdio cpath wpath rpath fattr", NULL) == -1) { 99 warn("pledge"); 100 goto out; 101 } 102 103 /* Read our operation. */ 104 105 op = FILE__MAX; 106 if ((lval = readop(certsock, COMM_CHAIN_OP)) == 0) 107 op = FILE_STOP; 108 else if (lval == FILE_CREATE || lval == FILE_REMOVE) 109 op = lval; 110 111 if (FILE_STOP == op) { 112 rc = 1; 113 goto out; 114 } else if (FILE__MAX == op) { 115 warnx("unknown operation from certproc"); 116 goto out; 117 } 118 119 /* 120 * If revoking certificates, just unlink the files. 121 * We return the special error code of 2 to indicate that the 122 * certificates were removed. 123 */ 124 125 if (FILE_REMOVE == op) { 126 if (certfile) { 127 if (unlink(certfile) == -1 && errno != ENOENT) { 128 warn("%s", certfile); 129 goto out; 130 } else 131 dodbg("%s: unlinked", certfile); 132 } 133 134 if (chainfile) { 135 if (unlink(chainfile) == -1 && errno != ENOENT) { 136 warn("%s", chainfile); 137 goto out; 138 } else 139 dodbg("%s: unlinked", chainfile); 140 } 141 142 if (fullchainfile) { 143 if (unlink(fullchainfile) == -1 && errno != ENOENT) { 144 warn("%s", fullchainfile); 145 goto out; 146 } else 147 dodbg("%s: unlinked", fullchainfile); 148 } 149 150 rc = 2; 151 goto out; 152 } 153 154 /* 155 * Start by downloading the chain PEM as a buffer. 156 * This is not NUL-terminated, but we're just going to guess 157 * that it's well-formed and not actually touch the data. 158 */ 159 if ((ch = readbuf(certsock, COMM_CHAIN, &chsz)) == NULL) 160 goto out; 161 162 if (chainfile) { 163 if (!serialise(chainfile, ch, chsz, NULL, 0)) 164 goto out; 165 166 dodbg("%s: created", chainfile); 167 } 168 169 /* 170 * Next, wait until we receive the DER encoded (signed) 171 * certificate from the network process. 172 * This comes as a stream of bytes: we don't know how many, so 173 * just keep downloading. 174 */ 175 176 if ((csr = readbuf(certsock, COMM_CSR, &csz)) == NULL) 177 goto out; 178 179 if (certfile) { 180 if (!serialise(certfile, csr, csz, NULL, 0)) 181 goto out; 182 183 dodbg("%s: created", certfile); 184 } 185 186 /* 187 * Finally, create the full-chain file. 188 * This is just the concatenation of the certificate and chain. 189 * We return the special error code 2 to indicate that the 190 * on-file certificates were changed. 191 */ 192 if (fullchainfile) { 193 if (!serialise(fullchainfile, csr, csz, ch, 194 chsz)) 195 goto out; 196 197 dodbg("%s: created", fullchainfile); 198 } 199 200 rc = 2; 201 out: 202 close(certsock); 203 free(csr); 204 free(ch); 205 return rc; 206 } 207