1 /* $Id: util.c,v 1.13 2022/12/28 21:30:15 jmc 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/wait.h> 19 20 #include <assert.h> 21 #include <err.h> 22 #include <errno.h> 23 #include <limits.h> 24 #include <signal.h> 25 #include <stdarg.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <stdint.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 #include "extern.h" 33 34 static volatile sig_atomic_t sig; 35 36 static const char *const comps[COMP__MAX] = { 37 "netproc", /* COMP_NET */ 38 "keyproc", /* COMP_KEY */ 39 "certproc", /* COMP_CERT */ 40 "acctproc", /* COMP_ACCOUNT */ 41 "challengeproc", /* COMP_CHALLENGE */ 42 "fileproc", /* COMP_FILE */ 43 "dnsproc", /* COMP_DNS */ 44 "revokeproc", /* COMP_REVOKE */ 45 }; 46 47 static const char *const comms[COMM__MAX] = { 48 "req", /* COMM_REQ */ 49 "thumbprint", /* COMM_THUMB */ 50 "cert", /* COMM_CERT */ 51 "payload", /* COMM_PAY */ 52 "nonce", /* COMM_NONCE */ 53 "token", /* COMM_TOK */ 54 "challenge-op", /* COMM_CHNG_OP */ 55 "challenge-ack", /* COMM_CHNG_ACK */ 56 "account", /* COMM_ACCT */ 57 "acctpro-status", /* COMM_ACCT_STAT */ 58 "csr", /* COMM_CSR */ 59 "csr-op", /* COMM_CSR_OP */ 60 "issuer", /* COMM_ISSUER */ 61 "chain", /* COMM_CHAIN */ 62 "chain-op", /* COMM_CHAIN_OP */ 63 "dns", /* COMM_DNS */ 64 "dnsq", /* COMM_DNSQ */ 65 "dns-address", /* COMM_DNSA */ 66 "dns-family", /* COMM_DNSF */ 67 "dns-length", /* COMM_DNSLEN */ 68 "keyproc-status", /* COMM_KEY_STAT */ 69 "revoke-op", /* COMM_REVOKE_OP */ 70 "revoke-check", /* COMM_REVOKE_CHECK */ 71 "revoke-response", /* COMM_REVOKE_RESP */ 72 }; 73 74 static void 75 sigpipe(int code) 76 { 77 78 (void)code; 79 sig = 1; 80 } 81 82 /* 83 * This will read a long-sized operation. 84 * Operations are usually enums, so this should be alright. 85 * We return 0 on EOF and LONG_MAX on failure. 86 */ 87 long 88 readop(int fd, enum comm comm) 89 { 90 ssize_t ssz; 91 long op; 92 93 ssz = read(fd, &op, sizeof(long)); 94 if (ssz == -1) { 95 warn("read: %s", comms[comm]); 96 return LONG_MAX; 97 } else if (ssz && ssz != sizeof(long)) { 98 warnx("short read: %s", comms[comm]); 99 return LONG_MAX; 100 } else if (ssz == 0) 101 return 0; 102 103 return op; 104 } 105 106 char * 107 readstr(int fd, enum comm comm) 108 { 109 size_t sz; 110 111 return readbuf(fd, comm, &sz); 112 } 113 114 /* 115 * Read a buffer from the sender. 116 * This consists of two parts: the length of the buffer, and the buffer 117 * itself. 118 * We allow the buffer to be binary, but NUL-terminate it anyway. 119 */ 120 char * 121 readbuf(int fd, enum comm comm, size_t *sz) 122 { 123 ssize_t ssz; 124 size_t rsz, lsz; 125 char *p = NULL; 126 127 if ((ssz = read(fd, sz, sizeof(size_t))) == -1) { 128 warn("read: %s length", comms[comm]); 129 return NULL; 130 } else if ((size_t)ssz != sizeof(size_t)) { 131 warnx("short read: %s length", comms[comm]); 132 return NULL; 133 } else if (*sz > SIZE_MAX - 1) { 134 warnx("integer overflow"); 135 return NULL; 136 } else if ((p = calloc(1, *sz + 1)) == NULL) { 137 warn("malloc"); 138 return NULL; 139 } 140 141 /* Catch this over several reads. */ 142 143 rsz = 0; 144 lsz = *sz; 145 while (lsz) { 146 if ((ssz = read(fd, p + rsz, lsz)) == -1) { 147 warn("read: %s", comms[comm]); 148 break; 149 } else if (ssz > 0) { 150 assert((size_t)ssz <= lsz); 151 rsz += (size_t)ssz; 152 lsz -= (size_t)ssz; 153 } 154 } 155 156 if (lsz) { 157 warnx("couldn't read buffer: %s", comms[comm]); 158 free(p); 159 return NULL; 160 } 161 162 return p; 163 } 164 165 /* 166 * Wring a long-value to a communication pipe. 167 * Returns 0 if the reader has terminated, -1 on error, 1 on success. 168 */ 169 int 170 writeop(int fd, enum comm comm, long op) 171 { 172 void (*sigfp)(int); 173 ssize_t ssz; 174 int er; 175 176 sigfp = signal(SIGPIPE, sigpipe); 177 178 if ((ssz = write(fd, &op, sizeof(long))) == -1) { 179 if ((er = errno) != EPIPE) 180 warn("write: %s", comms[comm]); 181 signal(SIGPIPE, sigfp); 182 return er == EPIPE ? 0 : -1; 183 } 184 185 signal(SIGPIPE, sigfp); 186 187 if ((size_t)ssz != sizeof(long)) { 188 warnx("short write: %s", comms[comm]); 189 return -1; 190 } 191 192 return 1; 193 } 194 195 /* 196 * Fully write the given buffer. 197 * Returns 0 if the reader has terminated, -1 on error, 1 on success. 198 */ 199 int 200 writebuf(int fd, enum comm comm, const void *v, size_t sz) 201 { 202 ssize_t ssz; 203 int er, rc = -1; 204 void (*sigfp)(int); 205 206 /* 207 * First, try to write the length. 208 * If the other end of the pipe has closed, we allow the short 209 * write to propagate as a return value of zero. 210 * To detect this, catch SIGPIPE. 211 */ 212 213 sigfp = signal(SIGPIPE, sigpipe); 214 215 if ((ssz = write(fd, &sz, sizeof(size_t))) == -1) { 216 if ((er = errno) != EPIPE) 217 warn("write: %s length", comms[comm]); 218 signal(SIGPIPE, sigfp); 219 return er == EPIPE ? 0 : -1; 220 } 221 222 /* Now write errors cause us to bail. */ 223 224 if ((size_t)ssz != sizeof(size_t)) 225 warnx("short write: %s length", comms[comm]); 226 else if ((ssz = write(fd, v, sz)) == -1) { 227 if (errno == EPIPE) 228 rc = 0; 229 else 230 warn("write: %s", comms[comm]); 231 } else if (sz != (size_t)ssz) 232 warnx("short write: %s", comms[comm]); 233 else 234 rc = 1; 235 236 signal(SIGPIPE, sigfp); 237 return rc; 238 } 239 240 int 241 writestr(int fd, enum comm comm, const char *v) 242 { 243 244 return writebuf(fd, comm, v, strlen(v)); 245 } 246 247 /* 248 * Make sure that the given process exits properly, i.e., properly 249 * exiting with EXIT_SUCCESS. 250 * Returns non-zero on success and zero on failure. 251 */ 252 int 253 checkexit(pid_t pid, enum comp comp) 254 { 255 int c, cc; 256 const char *cp; 257 258 if (waitpid(pid, &c, 0) == -1) { 259 warn("waitpid"); 260 return 0; 261 } else if (!WIFEXITED(c) && WIFSIGNALED(c)) { 262 cp = strsignal(WTERMSIG(c)); 263 warnx("signal: %s(%u): %s", comps[comp], pid, cp); 264 return 0; 265 } else if (!WIFEXITED(c)) { 266 warnx("did not exit: %s(%u)", comps[comp], pid); 267 return 0; 268 } else if (WEXITSTATUS(c) != EXIT_SUCCESS) { 269 cc = WEXITSTATUS(c); 270 dodbg("bad exit: %s(%u): %d", comps[comp], pid, cc); 271 return 0; 272 } 273 274 return 1; 275 } 276 277 /* 278 * Make sure that the given process exits properly, i.e., properly 279 * exiting with EXIT_SUCCESS *or* 2. 280 * Returns non-zero on success and zero on failure and sets the "rc" 281 * value to be the exit status. 282 */ 283 int 284 checkexit_ext(int *rc, pid_t pid, enum comp comp) 285 { 286 int c; 287 const char *cp; 288 289 *rc = EXIT_FAILURE; 290 291 if (waitpid(pid, &c, 0) == -1) { 292 warn("waitpid"); 293 return 0; 294 } 295 296 if (!WIFEXITED(c) && WIFSIGNALED(c)) { 297 cp = strsignal(WTERMSIG(c)); 298 warnx("signal: %s(%u): %s", comps[comp], pid, cp); 299 return 0; 300 } else if (!WIFEXITED(c)) { 301 warnx("did not exit: %s(%u)", comps[comp], pid); 302 return 0; 303 } 304 305 /* Now check extended status. */ 306 307 if ((*rc = WEXITSTATUS(c)) != EXIT_SUCCESS && *rc != 2) { 308 dodbg("bad exit: %s(%u): %d", comps[comp], pid, *rc); 309 return 0; 310 } 311 return 1; 312 } 313