xref: /openbsd/usr.sbin/acme-client/util.c (revision d7c05bd5)
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 
33 static	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 
44 static	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  */
76 long
readop(int fd,enum comm comm)77 readop(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 
95 char *
readstr(int fd,enum comm comm)96 readstr(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  */
109 char *
readbuf(int fd,enum comm comm,size_t * sz)110 readbuf(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  */
158 int
writeop(int fd,enum comm comm,long op)159 writeop(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  */
182 int
writebuf(int fd,enum comm comm,const void * v,size_t sz)183 writebuf(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 
217 int
writestr(int fd,enum comm comm,const char * v)218 writestr(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  */
229 int
checkexit(pid_t pid,enum comp comp)230 checkexit(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  */
260 int
checkexit_ext(int * rc,pid_t pid,enum comp comp)261 checkexit_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