1 /* $Id: fileproc.c,v 1.14 2017/01/24 13:32:55 jsing 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 <err.h> 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <limits.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "extern.h" 28 29 static int 30 serialise(const char *tmp, const char *real, 31 const char *v, size_t vsz, const char *v2, size_t v2sz) 32 { 33 int fd; 34 35 /* 36 * Write into backup location, overwriting. 37 * Then atomically (?) do the rename. 38 */ 39 40 fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0444); 41 if (fd == -1) { 42 warn("%s", tmp); 43 return 0; 44 } else if ((ssize_t)vsz != write(fd, v, vsz)) { 45 warnx("%s", tmp); 46 close(fd); 47 return 0; 48 } else if (v2 != NULL && write(fd, v2, v2sz) != (ssize_t)v2sz) { 49 warnx("%s", tmp); 50 close(fd); 51 return 0; 52 } else if (close(fd) == -1) { 53 warn("%s", tmp); 54 return 0; 55 } else if (rename(tmp, real) == -1) { 56 warn("%s", real); 57 return 0; 58 } 59 60 return 1; 61 } 62 63 int 64 fileproc(int certsock, const char *certdir, const char *certfile, const char 65 *chainfile, const char *fullchainfile) 66 { 67 char *csr = NULL, *ch = NULL; 68 char *certfile_bak = NULL, *chainfile_bak = NULL; 69 char *fullchainfile_bak = NULL; 70 size_t chsz, csz; 71 int rc = 0; 72 long lval; 73 enum fileop op; 74 75 /* File-system and sandbox jailing. */ 76 77 if (chroot(certdir) == -1) { 78 warn("chroot"); 79 goto out; 80 } 81 if (chdir("/") == -1) { 82 warn("chdir"); 83 goto out; 84 } 85 86 /* 87 * rpath and cpath for rename, wpath and cpath for 88 * writing to the temporary. 89 */ 90 if (pledge("stdio cpath wpath rpath", NULL) == -1) { 91 warn("pledge"); 92 goto out; 93 } 94 95 /* Read our operation. */ 96 97 op = FILE__MAX; 98 if ((lval = readop(certsock, COMM_CHAIN_OP)) == 0) 99 op = FILE_STOP; 100 else if (lval == FILE_CREATE || lval == FILE_REMOVE) 101 op = lval; 102 103 if (FILE_STOP == op) { 104 rc = 1; 105 goto out; 106 } else if (FILE__MAX == op) { 107 warnx("unknown operation from certproc"); 108 goto out; 109 } 110 111 /* 112 * If revoking certificates, just unlink the files. 113 * We return the special error code of 2 to indicate that the 114 * certificates were removed. 115 */ 116 117 if (FILE_REMOVE == op) { 118 if (certfile) { 119 if (unlink(certfile) == -1 && errno != ENOENT) { 120 warn("%s/%s", certdir, certfile); 121 goto out; 122 } else 123 dodbg("%s/%s: unlinked", certdir, certfile); 124 } 125 126 if (chainfile) { 127 if (unlink(chainfile) == -1 && errno != ENOENT) { 128 warn("%s/%s", certdir, chainfile); 129 goto out; 130 } else 131 dodbg("%s/%s: unlinked", certdir, chainfile); 132 } 133 134 if (fullchainfile) { 135 if (unlink(fullchainfile) == -1 && errno != ENOENT) { 136 warn("%s/%s", certdir, fullchainfile); 137 goto out; 138 } else 139 dodbg("%s/%s: unlinked", certdir, 140 fullchainfile); 141 } 142 143 rc = 2; 144 goto out; 145 } 146 147 /* 148 * Start by downloading the chain PEM as a buffer. 149 * This is not NUL-terminated, but we're just going to guess 150 * that it's well-formed and not actually touch the data. 151 * Once downloaded, dump it into CHAIN_BAK. 152 */ 153 154 if (certfile) 155 if (asprintf(&certfile_bak, "%s~", certfile) == -1) { 156 warn("asprintf"); 157 goto out; 158 } 159 160 if (chainfile) 161 if (asprintf(&chainfile_bak, "%s~", chainfile) == -1) { 162 warn("asprintf"); 163 goto out; 164 } 165 166 if (fullchainfile) 167 if (asprintf(&fullchainfile_bak, "%s~", fullchainfile) == -1) { 168 warn("asprintf"); 169 goto out; 170 } 171 172 if ((ch = readbuf(certsock, COMM_CHAIN, &chsz)) == NULL) 173 goto out; 174 175 if (chainfile) { 176 if (!serialise(chainfile_bak, chainfile, ch, chsz, NULL, 0)) 177 goto out; 178 179 dodbg("%s/%s: created", certdir, 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_bak, certfile, csr, csz, NULL, 0)) 194 goto out; 195 196 dodbg("%s/%s: created", certdir, 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_bak, fullchainfile, csr, csz, ch, 207 chsz)) 208 goto out; 209 210 dodbg("%s/%s: created", certdir, fullchainfile); 211 } 212 213 rc = 2; 214 out: 215 close(certsock); 216 free(csr); 217 free(ch); 218 free(certfile_bak); 219 free(chainfile_bak); 220 free(fullchainfile_bak); 221 return rc; 222 } 223