1 /* $Id: fileproc.c,v 1.6 2016/09/13 17:13:37 deraadt 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 <time.h> 26 #include <unistd.h> 27 28 #include "extern.h" 29 30 static int 31 serialise(const char *tmp, const char *real, 32 const char *v, size_t vsz, const char *v2, size_t v2sz) 33 { 34 int fd; 35 36 /* 37 * Write into backup location, overwriting. 38 * Then atomically (?) do the rename. 39 */ 40 41 fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0444); 42 if (-1 == fd) { 43 warn("%s", tmp); 44 return (0); 45 } else if ((ssize_t)vsz != write(fd, v, vsz)) { 46 warnx("%s", tmp); 47 close(fd); 48 return (0); 49 } else if (NULL != v2 && (ssize_t)v2sz != write(fd, v2, v2sz)) { 50 warnx("%s", tmp); 51 close(fd); 52 return (0); 53 } else if (-1 == close(fd)) { 54 warn("%s", tmp); 55 return (0); 56 } else if (-1 == rename(tmp, real)) { 57 warn("%s", real); 58 return (0); 59 } 60 61 return (1); 62 } 63 64 int 65 fileproc(int certsock, int backup, const char *certdir) 66 { 67 char *csr = NULL, *ch = NULL; 68 char file[PATH_MAX]; 69 size_t chsz, csz; 70 int rc = 0; 71 long lval; 72 enum fileop op; 73 time_t t; 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 (0 == (lval = readop(certsock, COMM_CHAIN_OP))) 99 op = FILE_STOP; 100 else if (FILE_CREATE == lval || FILE_REMOVE == lval) 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 we're backing up, then copy all files (found) by linking 113 * them to the file followed by the epoch in seconds. 114 * If we're going to remove, the unlink(2) will cause the 115 * original to go away. 116 * If we're going to update, the rename(2) will replace the 117 * certificate, leaving the backup as the only one. 118 */ 119 120 if (backup) { 121 t = time(NULL); 122 snprintf(file, sizeof(file), 123 "cert-%llu.pem", (unsigned long long)t); 124 if (-1 == link(CERT_PEM, file) && ENOENT != errno) { 125 warnx("%s/%s", certdir, CERT_PEM); 126 goto out; 127 } else 128 dodbg("%s/%s: linked to %s", certdir, CERT_PEM, file); 129 130 snprintf(file, sizeof(file), 131 "chain-%llu.pem", (unsigned long long)t); 132 if (-1 == link(CHAIN_PEM, file) && ENOENT != errno) { 133 warnx("%s/%s", certdir, CHAIN_PEM); 134 goto out; 135 } else 136 dodbg("%s/%s: linked to %s", certdir, CHAIN_PEM, file); 137 138 snprintf(file, sizeof(file), 139 "fullchain-%llu.pem", (unsigned long long)t); 140 if (-1 == link(FCHAIN_PEM, file) && ENOENT != errno) { 141 warnx("%s/%s", certdir, FCHAIN_PEM); 142 goto out; 143 } else 144 dodbg("%s/%s: linked to %s", certdir, FCHAIN_PEM, file); 145 } 146 147 /* 148 * If revoking certificates, just unlink the files. 149 * We return the special error code of 2 to indicate that the 150 * certificates were removed. 151 */ 152 153 if (FILE_REMOVE == op) { 154 if (-1 == unlink(CERT_PEM) && ENOENT != errno) { 155 warn("%s/%s", certdir, CERT_PEM); 156 goto out; 157 } else 158 dodbg("%s/%s: unlinked", certdir, CERT_PEM); 159 160 if (-1 == unlink(CHAIN_PEM) && ENOENT != errno) { 161 warn("%s/%s", certdir, CHAIN_PEM); 162 goto out; 163 } else 164 dodbg("%s/%s: unlinked", certdir, CHAIN_PEM); 165 166 if (-1 == unlink(FCHAIN_PEM) && ENOENT != errno) { 167 warn("%s/%s", certdir, FCHAIN_PEM); 168 goto out; 169 } else 170 dodbg("%s/%s: unlinked", certdir, FCHAIN_PEM); 171 172 rc = 2; 173 goto out; 174 } 175 176 /* 177 * Start by downloading the chain PEM as a buffer. 178 * This is not nil-terminated, but we're just going to guess 179 * that it's well-formed and not actually touch the data. 180 * Once downloaded, dump it into CHAIN_BAK. 181 */ 182 183 if (NULL == (ch = readbuf(certsock, COMM_CHAIN, &chsz))) 184 goto out; 185 if (!serialise(CHAIN_BAK, CHAIN_PEM, ch, chsz, NULL, 0)) 186 goto out; 187 188 dodbg("%s/%s: created", certdir, CHAIN_PEM); 189 190 /* 191 * Next, wait until we receive the DER encoded (signed) 192 * certificate from the network process. 193 * This comes as a stream of bytes: we don't know how many, so 194 * just keep downloading. 195 */ 196 197 if (NULL == (csr = readbuf(certsock, COMM_CSR, &csz))) 198 goto out; 199 if (!serialise(CERT_BAK, CERT_PEM, csr, csz, NULL, 0)) 200 goto out; 201 202 dodbg("%s/%s: created", certdir, CERT_PEM); 203 204 /* 205 * Finally, create the full-chain file. 206 * This is just the concatenation of the certificate and chain. 207 * We return the special error code 2 to indicate that the 208 * on-file certificates were changed. 209 */ 210 211 if (!serialise(FCHAIN_BAK, FCHAIN_PEM, csr, csz, ch, chsz)) 212 goto out; 213 214 dodbg("%s/%s: created", certdir, FCHAIN_PEM); 215 216 rc = 2; 217 out: 218 close(certsock); 219 free(csr); 220 free(ch); 221 return (rc); 222 } 223