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