xref: /openbsd-src/usr.sbin/acme-client/chngproc.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$Id: chngproc.c,v 1.7 2016/09/13 17:13:37 deraadt 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 <assert.h>
19 #include <err.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "extern.h"
28 
29 int
30 chngproc(int netsock, const char *root, int remote)
31 {
32 	char		 *tok = NULL, *th = NULL, *fmt = NULL, **fs = NULL;
33 	size_t		  i, fsz = 0;
34 	int		  rc = 0, fd = -1, cc;
35 	long		  lval;
36 	enum chngop	  op;
37 	void		 *pp;
38 
39 	if (chroot(root) == -1) {
40 		warn("chroot");
41 		goto out;
42 	}
43 	if (chdir("/") == -1) {
44 		warn("chdir");
45 		goto out;
46 	}
47 	if (pledge("stdio cpath wpath", NULL) == -1) {
48 		warn("pledge");
49 		goto out;
50 	}
51 
52 	/*
53 	 * Loop while we wait to get a thumbprint and token.
54 	 * We'll get this for each SAN request.
55 	 */
56 
57 	for (;;) {
58 		op = CHNG__MAX;
59 		if (0 == (lval = readop(netsock, COMM_CHNG_OP)))
60 			op = CHNG_STOP;
61 		else if (CHNG_SYN == lval)
62 			op = lval;
63 
64 		if (CHNG__MAX == op) {
65 			warnx("unknown operation from netproc");
66 			goto out;
67 		} else if (CHNG_STOP == op)
68 			break;
69 
70 		assert(CHNG_SYN == op);
71 
72 		/*
73 		 * Read the thumbprint and token.
74 		 * The token is the filename, so store that in a vector
75 		 * of tokens that we'll later clean up.
76 		 */
77 
78 		if (NULL == (th = readstr(netsock, COMM_THUMB)))
79 			goto out;
80 		else if (NULL == (tok = readstr(netsock, COMM_TOK)))
81 			goto out;
82 
83 		/* Vector appending... */
84 
85 		pp = reallocarray(fs, (fsz + 1), sizeof(char *));
86 		if (NULL == pp) {
87 			warn("realloc");
88 			goto out;
89 		}
90 		fs = pp;
91 		fs[fsz] = tok;
92 		tok = NULL;
93 		fsz++;
94 
95 		if (-1 == asprintf(&fmt, "%s.%s", fs[fsz - 1], th)) {
96 			warn("asprintf");
97 			goto out;
98 		}
99 
100 		/*
101 		 * I use this for testing when letskencrypt is being run
102 		 * on machines apart from where I'm hosting the
103 		 * challenge directory.
104 		 * DON'T DEPEND ON THIS FEATURE.
105 		 */
106 		if (remote) {
107 			puts("RUN THIS IN THE CHALLENGE DIRECTORY");
108 			puts("YOU HAVE 20 SECONDS...");
109 			printf("doas sh -c \"echo %s > %s\"\n",
110 			    fmt, fs[fsz - 1]);
111 			sleep(20);
112 			puts("TIME'S UP.");
113 		} else {
114 			/*
115 			 * Create and write to our challenge file.
116 			 * Note: we use file descriptors instead of FILE
117 			 * because we want to minimise our pledges.
118 			 */
119 			fd = open(fs[fsz - 1], O_WRONLY|O_EXCL|O_CREAT, 0444);
120 			if (-1 == fd) {
121 				warn("%s", fs[fsz - 1]);
122 				goto out;
123 			} if (-1 == write(fd, fmt, strlen(fmt))) {
124 				warn("%s", fs[fsz - 1]);
125 				goto out;
126 			} else if (-1 == close(fd)) {
127 				warn("%s", fs[fsz - 1]);
128 				goto out;
129 			}
130 			fd = -1;
131 		}
132 
133 		free(th);
134 		free(fmt);
135 		th = fmt = NULL;
136 
137 		dodbg("%s/%s: created", root, fs[fsz - 1]);
138 
139 		/*
140 		 * Write our acknowledgement.
141 		 * Ignore reader failure.
142 		 */
143 
144 		cc = writeop(netsock, COMM_CHNG_ACK, CHNG_ACK);
145 		if (0 == cc)
146 			break;
147 		if (cc < 0)
148 			goto out;
149 	}
150 
151 	rc = 1;
152 out:
153 	close(netsock);
154 	if (-1 != fd)
155 		close(fd);
156 	for (i = 0; i < fsz; i++) {
157 		if (-1 == unlink(fs[i]) && ENOENT != errno)
158 			warn("%s", fs[i]);
159 		free(fs[i]);
160 	}
161 	free(fs);
162 	free(fmt);
163 	free(th);
164 	free(tok);
165 	return(rc);
166 }
167