1255767Sdes/*	$Id: util.c,v 1.14 2024/06/19 13:13:25 claudio Exp $ */
276259Sgreen/*
365668Skris * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
492555Sdes *
565668Skris * Permission to use, copy, modify, and distribute this software for any
665668Skris * purpose with or without fee is hereby granted, provided that the above
765668Skris * copyright notice and this permission notice appear in all copies.
865668Skris *
965668Skris * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1065668Skris * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1165668Skris * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1265668Skris * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1365668Skris * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1465668Skris * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1565668Skris * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1665668Skris */
1765668Skris
1865668Skris#include <sys/wait.h>
1965668Skris
2065668Skris#include <assert.h>
2165668Skris#include <err.h>
2265668Skris#include <errno.h>
2365668Skris#include <limits.h>
2465668Skris#include <stdarg.h>
2565668Skris#include <stdio.h>
2658582Skris#include <stdlib.h>
2758582Skris#include <stdint.h>
2858582Skris#include <string.h>
29204917Sdes#include <unistd.h>
3076259Sgreen
3176259Sgreen#include "extern.h"
32221420Sdes
33221420Sdesstatic	const char *const comps[COMP__MAX] = {
34221420Sdes	"netproc", /* COMP_NET */
3576259Sgreen	"keyproc", /* COMP_KEY */
3658582Skris	"certproc", /* COMP_CERT */
3758582Skris	"acctproc", /* COMP_ACCOUNT */
3876259Sgreen	"challengeproc", /* COMP_CHALLENGE */
3958582Skris	"fileproc", /* COMP_FILE */
4058582Skris	"dnsproc", /* COMP_DNS */
41221420Sdes	"revokeproc", /* COMP_REVOKE */
42204917Sdes};
43204917Sdes
44221420Sdesstatic	const char *const comms[COMM__MAX] = {
45215116Sdes	"req", /* COMM_REQ */
46215116Sdes	"thumbprint", /* COMM_THUMB */
4776259Sgreen	"cert", /* COMM_CERT */
4858582Skris	"payload", /* COMM_PAY */
4976259Sgreen	"nonce", /* COMM_NONCE */
5076259Sgreen	"token", /* COMM_TOK */
51240075Sdes	"challenge-op", /* COMM_CHNG_OP */
52240075Sdes	"challenge-ack", /* COMM_CHNG_ACK */
5376259Sgreen	"account", /* COMM_ACCT */
5476259Sgreen	"acctpro-status", /* COMM_ACCT_STAT */
5576259Sgreen	"csr", /* COMM_CSR */
56181111Sdes	"csr-op", /* COMM_CSR_OP */
57181111Sdes	"issuer", /* COMM_ISSUER */
5876259Sgreen	"chain", /* COMM_CHAIN */
5992555Sdes	"chain-op", /* COMM_CHAIN_OP */
6092555Sdes	"dns", /* COMM_DNS */
6192555Sdes	"dnsq", /* COMM_DNSQ */
6292555Sdes	"dns-address", /* COMM_DNSA */
63204917Sdes	"dns-family", /* COMM_DNSF */
64204917Sdes	"dns-length", /* COMM_DNSLEN */
65204917Sdes	"keyproc-status", /* COMM_KEY_STAT */
66204917Sdes	"revoke-op", /* COMM_REVOKE_OP */
67215116Sdes	"revoke-check", /* COMM_REVOKE_CHECK */
68204917Sdes	"revoke-response", /* COMM_REVOKE_RESP */
69204917Sdes};
70204917Sdes
71204917Sdes/*
72215116Sdes * This will read a long-sized operation.
73215116Sdes * Operations are usually enums, so this should be alright.
74204917Sdes * We return 0 on EOF and LONG_MAX on failure.
75204917Sdes */
76204917Sdeslong
7758582Skrisreadop(int fd, enum comm comm)
7892555Sdes{
7992555Sdes	ssize_t		 ssz;
8058582Skris	long		 op;
8158582Skris
82221420Sdes	ssz = read(fd, &op, sizeof(long));
83221420Sdes	if (ssz == -1) {
84221420Sdes		warn("read: %s", comms[comm]);
85221420Sdes		return LONG_MAX;
86221420Sdes	} else if (ssz && ssz != sizeof(long)) {
87221420Sdes		warnx("short read: %s", comms[comm]);
88204917Sdes		return LONG_MAX;
8958582Skris	} else if (ssz == 0)
9058582Skris		return 0;
91126274Sdes
92204917Sdes	return op;
93126274Sdes}
94126274Sdes
95126274Sdeschar *
96204917Sdesreadstr(int fd, enum comm comm)
97126274Sdes{
98255767Sdes	size_t	 sz;
99248619Sdes
100126274Sdes	return readbuf(fd, comm, &sz);
101207319Sdes}
102126274Sdes
103126274Sdes/*
104126274Sdes * Read a buffer from the sender.
10558582Skris * This consists of two parts: the length of the buffer, and the buffer
10692555Sdes * itself.
107126274Sdes * We allow the buffer to be binary, but NUL-terminate it anyway.
10892555Sdes */
109204917Sdeschar *
110204917Sdesreadbuf(int fd, enum comm comm, size_t *sz)
111215116Sdes{
112204917Sdes	ssize_t		 ssz;
113204917Sdes	size_t		 rsz, lsz;
114204917Sdes	char		*p = NULL;
115204917Sdes
116204917Sdes	if ((ssz = read(fd, sz, sizeof(size_t))) == -1) {
117248619Sdes		warn("read: %s length", comms[comm]);
11876259Sgreen		return NULL;
119221420Sdes	} else if ((size_t)ssz != sizeof(size_t)) {
120221420Sdes		warnx("short read: %s length", comms[comm]);
121255767Sdes		return NULL;
122221420Sdes	} else if (*sz > SIZE_MAX - 1) {
123221420Sdes		warnx("integer overflow");
124221420Sdes		return NULL;
125221420Sdes	} else if ((p = calloc(1, *sz + 1)) == NULL) {
126255767Sdes		warn("malloc");
127221420Sdes		return NULL;
128221420Sdes	}
129221420Sdes
130255767Sdes	/* Catch this over several reads. */
131221420Sdes
132126274Sdes	rsz = 0;
133126274Sdes	lsz = *sz;
134126274Sdes	while (lsz) {
135221420Sdes		if ((ssz = read(fd, p + rsz, lsz)) == -1) {
136126274Sdes			warn("read: %s", comms[comm]);
13776259Sgreen			break;
138126274Sdes		} else if (ssz > 0) {
139126274Sdes			assert((size_t)ssz <= lsz);
14076259Sgreen			rsz += (size_t)ssz;
141126274Sdes			lsz -= (size_t)ssz;
142126274Sdes		}
143221420Sdes	}
144221420Sdes
145126274Sdes	if (lsz) {
146126274Sdes		warnx("couldn't read buffer: %s", comms[comm]);
147113908Sdes		free(p);
148221420Sdes		return NULL;
149221420Sdes	}
150221420Sdes
15158582Skris	return p;
152221420Sdes}
153221420Sdes
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