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