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