1 /* $Id: util.c,v 1.3 2016/09/01 00:35:22 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/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 < 0) { 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 (0 == ssz) 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 lenght of the buffer, and the buffer 117 * itself. 118 * We allow the buffer to be binary, but nil-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; 126 127 p = NULL; 128 129 if ((ssz = read(fd, sz, sizeof(size_t))) < 0) { 130 warn("read: %s length", comms[comm]); 131 return (NULL); 132 } else if ((size_t)ssz != sizeof(size_t)) { 133 warnx("short read: %s length", comms[comm]); 134 return (NULL); 135 } else if (*sz > SIZE_MAX - 1) { 136 warnx("integer overflow"); 137 return (NULL); 138 } else if (NULL == (p = calloc(1, *sz + 1))) { 139 warn("malloc"); 140 return (NULL); 141 } 142 143 /* Catch this over several reads. */ 144 145 rsz = 0; 146 lsz = *sz; 147 while (lsz) { 148 if ((ssz = read(fd, p + rsz, lsz)) < 0) { 149 warn("read: %s", comms[comm]); 150 break; 151 } else if (ssz > 0) { 152 assert((size_t)ssz <= lsz); 153 rsz += (size_t)ssz; 154 lsz -= (size_t)ssz; 155 } 156 } 157 158 if (lsz) { 159 warnx("couldn't read buffer: %s", comms[comm]); 160 free(p); 161 return (NULL); 162 } 163 164 return (p); 165 } 166 167 /* 168 * Wring a long-value to a communication pipe. 169 * Returns 0 if the reader has terminated, -1 on error, 1 on success. 170 */ 171 int 172 writeop(int fd, enum comm comm, long op) 173 { 174 void (*sigfp)(int); 175 ssize_t ssz; 176 int er; 177 178 sigfp = signal(SIGPIPE, sigpipe); 179 180 if ((ssz = write(fd, &op, sizeof(long))) < 0) { 181 if (EPIPE != (er = errno)) 182 warn("write: %s", comms[comm]); 183 signal(SIGPIPE, sigfp); 184 return (EPIPE == er ? 0 : -1); 185 } 186 187 signal(SIGPIPE, sigfp); 188 189 if ((size_t)ssz != sizeof(long)) { 190 warnx("short write: %s", comms[comm]); 191 return (-1); 192 } 193 194 return (1); 195 } 196 197 /* 198 * Fully write the given buffer. 199 * Returns 0 if the reader has terminated, -1 on error, 1 on success. 200 */ 201 int 202 writebuf(int fd, enum comm comm, const void *v, size_t sz) 203 { 204 ssize_t ssz; 205 int er, rc; 206 void (*sigfp)(int); 207 208 rc = -1; 209 210 /* 211 * First, try to write the length. 212 * If the other end of the pipe has closed, we allow the short 213 * write to propogate as a return value of zero. 214 * To detect this, catch SIGPIPE. 215 */ 216 217 sigfp = signal(SIGPIPE, sigpipe); 218 219 if ((ssz = write(fd, &sz, sizeof(size_t))) < 0) { 220 if (EPIPE != (er = errno)) 221 warn("write: %s length", comms[comm]); 222 signal(SIGPIPE, sigfp); 223 return (EPIPE == er ? 0 : -1); 224 } 225 226 /* Now write errors cause us to bail. */ 227 228 if ((size_t)ssz != sizeof(size_t)) 229 warnx("short write: %s length", comms[comm]); 230 else if ((ssz = write(fd, v, sz)) < 0) 231 warn("write: %s", comms[comm]); 232 else if ((size_t)ssz != sz) 233 warnx("short write: %s", comms[comm]); 234 else 235 rc = 1; 236 237 signal(SIGPIPE, sigfp); 238 return (rc); 239 } 240 241 int 242 writestr(int fd, enum comm comm, const char *v) 243 { 244 245 return (writebuf(fd, comm, v, strlen(v))); 246 } 247 248 /* 249 * Make sure that the given process exits properly, i.e., properly 250 * exiting with EXIT_SUCCESS. 251 * Returns non-zero on success and zero on failure. 252 */ 253 int 254 checkexit(pid_t pid, enum comp comp) 255 { 256 int c, cc; 257 const char *cp; 258 259 if (-1 == waitpid(pid, &c, 0)) { 260 warn("waitpid"); 261 return (0); 262 } else if ( ! WIFEXITED(c) && WIFSIGNALED(c)) { 263 cp = strsignal(WTERMSIG(c)); 264 warnx("signal: %s(%u): %s", comps[comp], pid, cp); 265 return (0); 266 } else if ( ! WIFEXITED(c)) { 267 warnx("did not exit: %s(%u)", comps[comp], pid); 268 return (0); 269 } else if (EXIT_SUCCESS != WEXITSTATUS(c)) { 270 cc = WEXITSTATUS(c); 271 dodbg("bad exit: %s(%u): %d", comps[comp], pid, cc); 272 return (0); 273 } 274 275 return (1); 276 } 277 278 /* 279 * Make sure that the given process exits properly, i.e., properly 280 * exiting with EXIT_SUCCESS *or* 2. 281 * Returns non-zero on success and zero on failure and sets the "rc" 282 * value to be the exit status. 283 */ 284 int 285 checkexit_ext(int *rc, pid_t pid, enum comp comp) 286 { 287 int c; 288 const char *cp; 289 290 *rc = EXIT_FAILURE; 291 292 if (-1 == waitpid(pid, &c, 0)) { 293 warn("waitpid"); 294 return (0); 295 } 296 297 if ( ! WIFEXITED(c) && WIFSIGNALED(c)) { 298 cp = strsignal(WTERMSIG(c)); 299 warnx("signal: %s(%u): %s", comps[comp], pid, cp); 300 return (0); 301 } else if ( ! WIFEXITED(c)) { 302 warnx("did not exit: %s(%u)", comps[comp], pid); 303 return (0); 304 } 305 306 /* Now check extended status. */ 307 308 if (EXIT_SUCCESS != (*rc = WEXITSTATUS(c)) && 2 != *rc) { 309 dodbg("bad exit: %s(%u): %d", comps[comp], pid, *rc); 310 return (0); 311 } 312 return (1); 313 } 314