GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/acme-client/certproc.c Lines: 0 92 0.0 %
Date: 2017-11-07 Branches: 0 70 0.0 %

Line Branch Exec Source
1
/*	$Id: certproc.c,v 1.10 2017/01/24 13:32:55 jsing 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 <err.h>
19
#include <stdio.h>
20
#include <stdlib.h>
21
#include <string.h>
22
#include <unistd.h>
23
24
#include <openssl/pem.h>
25
#include <openssl/x509.h>
26
#include <openssl/x509v3.h>
27
#include <openssl/err.h>
28
29
#include "extern.h"
30
31
#define MARKER "-----BEGIN CERTIFICATE-----"
32
33
/*
34
 * Convert an X509 certificate to a buffer of "sz".
35
 * We don't guarantee that it's NUL-terminated.
36
 * Returns NULL on failure.
37
 */
38
static char *
39
x509buf(X509 *x, size_t *sz)
40
{
41
	BIO	*bio;
42
	char	*p;
43
	int	 ssz;
44
45
	/* Convert X509 to PEM in BIO. */
46
47
	if ((bio = BIO_new(BIO_s_mem())) == NULL) {
48
		warnx("BIO_new");
49
		return NULL;
50
	} else if (!PEM_write_bio_X509(bio, x)) {
51
		warnx("PEM_write_bio_X509");
52
		BIO_free(bio);
53
		return NULL;
54
	}
55
56
	/*
57
	 * Now convert bio to string.
58
	 * Make into NUL-terminated, just in case.
59
	 */
60
61
	if ((p = calloc(1, bio->num_write + 1)) == NULL) {
62
		warn("calloc");
63
		BIO_free(bio);
64
		return NULL;
65
	}
66
67
	ssz = BIO_read(bio, p, bio->num_write);
68
	if (ssz < 0 || (unsigned)ssz != bio->num_write) {
69
		warnx("BIO_read");
70
		BIO_free(bio);
71
		return NULL;
72
	}
73
74
	*sz = ssz;
75
	BIO_free(bio);
76
	return p;
77
}
78
79
int
80
certproc(int netsock, int filesock)
81
{
82
	char		*csr = NULL, *chain = NULL, *url = NULL;
83
	unsigned char	*csrcp, *chaincp;
84
	size_t		 csrsz, chainsz;
85
	int		 i, rc = 0, idx = -1, cc;
86
	enum certop	 op;
87
	long		 lval;
88
	X509		*x = NULL, *chainx = NULL;
89
	X509_EXTENSION	*ext = NULL;
90
	X509V3_EXT_METHOD *method = NULL;
91
	void		*entries;
92
	STACK_OF(CONF_VALUE) *val;
93
	CONF_VALUE	*nval;
94
95
	/* File-system and sandbox jailing. */
96
97
	ERR_load_crypto_strings();
98
99
	if (pledge("stdio flock rpath cpath wpath", NULL) == -1) {
100
		warn("pledge");
101
		goto out;
102
	}
103
104
	/* Read what the netproc wants us to do. */
105
106
	op = CERT__MAX;
107
	if ((lval = readop(netsock, COMM_CSR_OP)) == 0)
108
		op = CERT_STOP;
109
	else if (lval == CERT_REVOKE || lval == CERT_UPDATE)
110
		op = lval;
111
112
	if (CERT_STOP == op) {
113
		rc = 1;
114
		goto out;
115
	} else if (CERT__MAX == op) {
116
		warnx("unknown operation from netproc");
117
		goto out;
118
	}
119
120
	/*
121
	 * Pass revocation right through to fileproc.
122
	 * If the reader is terminated, ignore it.
123
	 */
124
125
	if (CERT_REVOKE == op) {
126
		if (writeop(filesock, COMM_CHAIN_OP, FILE_REMOVE) >= 0)
127
			rc = 1;
128
		goto out;
129
	}
130
131
	/*
132
	 * Wait until we receive the DER encoded (signed) certificate
133
	 * from the network process.
134
	 * Then convert the DER encoding into an X509 certificate.
135
	 */
136
137
	if ((csr = readbuf(netsock, COMM_CSR, &csrsz)) == NULL)
138
		goto out;
139
140
	csrcp = (u_char *)csr;
141
	x = d2i_X509(NULL, (const u_char **)&csrcp, csrsz);
142
	if (x == NULL) {
143
		warnx("d2i_X509");
144
		goto out;
145
	}
146
147
	/*
148
	 * Extract the CA Issuers from its NID.
149
	 * TODO: I have no idea what I'm doing.
150
	 */
151
152
	idx = X509_get_ext_by_NID(x, NID_info_access, idx);
153
	if (idx >= 0 && (ext = X509_get_ext(x, idx)) != NULL)
154
		method = (X509V3_EXT_METHOD *)X509V3_EXT_get(ext);
155
156
	entries = X509_get_ext_d2i(x, NID_info_access, 0, 0);
157
	if (method != NULL && entries != NULL) {
158
		val = method->i2v(method, entries, 0);
159
		for (i = 0; i < sk_CONF_VALUE_num(val); i++) {
160
			nval = sk_CONF_VALUE_value(val, i);
161
			if (strcmp(nval->name, "CA Issuers - URI"))
162
				continue;
163
			url = strdup(nval->value);
164
			if (url == NULL) {
165
				warn("strdup");
166
				goto out;
167
			}
168
			break;
169
		}
170
	}
171
172
	if (url == NULL) {
173
		warnx("no CA issuer registered with certificate");
174
		goto out;
175
	}
176
177
	/* Write the CA issuer to the netsock. */
178
179
	if (writestr(netsock, COMM_ISSUER, url) <= 0)
180
		goto out;
181
182
	/* Read the full-chain back from the netsock. */
183
184
	if ((chain = readbuf(netsock, COMM_CHAIN, &chainsz)) == NULL)
185
		goto out;
186
187
	/*
188
	 * Then check if the chain is PEM-encoded by looking to see if
189
	 * it begins with the PEM marker.
190
	 * If so, ship it as-is; otherwise, convert to a PEM encoded
191
	 * buffer and ship that.
192
	 * FIXME: if PEM, re-parse it.
193
	 */
194
195
	if (chainsz <= strlen(MARKER) ||
196
	    strncmp(chain, MARKER, strlen(MARKER))) {
197
		chaincp = (u_char *)chain;
198
		chainx = d2i_X509(NULL, (const u_char **)&chaincp, chainsz);
199
		if (chainx == NULL) {
200
			warnx("d2i_X509");
201
			goto out;
202
		}
203
		free(chain);
204
		if ((chain = x509buf(chainx, &chainsz)) == NULL)
205
			goto out;
206
	}
207
208
	/* Allow reader termination to just push us out. */
209
210
	if ((cc = writeop(filesock, COMM_CHAIN_OP, FILE_CREATE)) == 0)
211
		rc = 1;
212
	if (cc <= 0)
213
		goto out;
214
	if ((cc = writebuf(filesock, COMM_CHAIN, chain, chainsz)) == 0)
215
		rc = 1;
216
	if (cc <= 0)
217
		goto out;
218
219
	/*
220
	 * Next, convert the X509 to a buffer and send that.
221
	 * Reader failure doesn't change anything.
222
	 */
223
224
	free(chain);
225
	if ((chain = x509buf(x, &chainsz)) == NULL)
226
		goto out;
227
	if (writebuf(filesock, COMM_CSR, chain, chainsz) < 0)
228
		goto out;
229
230
	rc = 1;
231
out:
232
	close(netsock);
233
	close(filesock);
234
	if (x != NULL)
235
		X509_free(x);
236
	if (chainx != NULL)
237
		X509_free(chainx);
238
	free(csr);
239
	free(url);
240
	free(chain);
241
	ERR_print_errors_fp(stderr);
242
	ERR_free_strings();
243
	return rc;
244
}