xref: /openbsd/usr.sbin/acme-client/util.c (revision fd0cc40e)
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