1 #include <sys/types.h>
2 #include <sys/socket.h>
3 
4 #include <netinet/in.h>
5 
6 #include <assert.h>
7 #include <limits.h>
8 #include <netdb.h>
9 #include <stdint.h>
10 #include <string.h>
11 #include <unistd.h>
12 
13 #include <openssl/ssl.h>
14 #include <openssl/x509v3.h>
15 
16 #include "sslreq.h"
17 
18 /*
19  * LibreSSL claims to be OpenSSL 2.0, but (mostly) has APIs compatible with
20  * OpenSSL 1.0.1g.
21  */
22 #ifdef LIBRESSL_VERSION_NUMBER
23 #undef OPENSSL_VERSION_NUMBER
24 #define OPENSSL_VERSION_NUMBER 0x1000107fL
25 #if LIBRESSL_VERSION_NUMBER >= 0x2090000fL
26 #define HAVE_SSL_SET1_HOST
27 #endif
28 #if LIBRESSL_VERSION_NUMBER >= 0x3030200fL
29 #define HAVE_SSL_SET_HOSTFLAGS
30 #endif
31 #endif
32 
33 /* Compatibility for OpenSSL pre-1.1.0 */
34 #if OPENSSL_VERSION_NUMBER < 0x10100000L
35 #ifndef HAVE_SSL_SET1_HOST
36 static int
SSL_set1_host(SSL * ssl,const char * hostname)37 SSL_set1_host(SSL * ssl, const char * hostname)
38 {
39 	X509_VERIFY_PARAM * param;
40 
41 	param = SSL_get0_param(ssl);
42 	return (X509_VERIFY_PARAM_set1_host(param, hostname, strlen(hostname)));
43 }
44 #endif
45 
46 #ifndef HAVE_SSL_SET_HOSTFLAGS
47 static void
SSL_set_hostflags(SSL * ssl,unsigned int flags)48 SSL_set_hostflags(SSL * ssl, unsigned int flags)
49 {
50 	X509_VERIFY_PARAM * param;
51 
52 	param = SSL_get0_param(ssl);
53 	X509_VERIFY_PARAM_set_hostflags(param, flags);
54 }
55 #endif
56 #endif
57 
58 /* Compatibility for OpenSSL pre-1.1.1. */
59 #if OPENSSL_VERSION_NUMBER < 0x10101000L
60 static int
SSL_write_ex(SSL * ssl,const void * buf,size_t num,size_t * written)61 SSL_write_ex(SSL * ssl, const void * buf, size_t num,
62     size_t * written)
63 {
64 	int towrite;
65 	int ret;
66 
67 	/* Sanity check. */
68 	assert(num > 0);
69 
70 	/* Nothing written yet. */
71 
72 	/* Loop until we've written everything. */
73 	while(1) {
74 		if (num > INT_MAX)
75 			towrite = INT_MAX;
76 		else
77 			towrite = num;
78 
79 		/* Attempt to send data. */
80 		ret = SSL_write(ssl, buf, towrite);
81 		if (ret > 0) {
82 			/* Sanity check. */
83 			assert(ret <= towrite);
84 
85 			/* Record the number of bytes written. */
86 			*written += (size_t)ret;
87 			buf = (const uint8_t *)(buf) + (size_t)ret;
88 			num -= (size_t)ret;
89 
90 			/* Are we finished? */
91 			if (num == 0) {
92 				ret = 1;
93 				break;
94 			}
95 
96 			/* Write some more. */
97 			continue;
98 		} else {
99 			/*
100 			 * Do nothing here, because ret is a meaningful value for
101 			 * determining the error.
102 			 */
103 			break;
104 		}
105 	}
106 
107 	return (ret);
108 }
109 #endif
110 
111 /**
112  * sslreq2(host, port, certfile, req, reqlen, payload, plen, resp, resplen):
113  * Establish an SSL connection to ${host}:${port}; verify the authenticity of
114  * the server using certificates in ${certfile}; send ${reqlen} bytes from
115  * ${req} and ${plen} bytes from ${payload}; and read a response of up to
116  * ${*resplen} bytes into ${resp}.  Set ${*resplen} to the length of the
117  * response read.  Return NULL on success or an error string.
118  */
119 const char *
sslreq2(const char * host,const char * port,const char * certfile,const uint8_t * req,int reqlen,const uint8_t * payload,size_t plen,uint8_t * resp,size_t * resplen)120 sslreq2(const char * host, const char * port, const char * certfile,
121     const uint8_t * req, int reqlen, const uint8_t * payload, size_t plen,
122     uint8_t * resp, size_t * resplen)
123 {
124 	struct addrinfo hints;
125 	struct addrinfo * res;
126 	struct addrinfo * r;
127 	int error;
128 	int s;
129 	const SSL_METHOD * meth;
130 	SSL_CTX * ctx;
131 	BIO * b;
132 	SSL * ssl;
133 	int readlen;
134 	size_t resppos;
135 	const char * errstr = NULL;
136 
137 	/* Create resolver hints structure. */
138 	memset(&hints, 0, sizeof(hints));
139 	hints.ai_family = AF_UNSPEC;
140 	hints.ai_socktype = SOCK_STREAM;
141 	hints.ai_protocol = IPPROTO_TCP;
142 
143 	/* Perform DNS lookup. */
144 	if ((error = getaddrinfo(host, port, &hints, &res)) != 0) {
145 		errstr = "DNS lookup failed";
146 		goto out0;
147 	}
148 
149 	/* Iterate through the addresses we obtained trying to connect. */
150 	for (r = res; r != NULL; r = r->ai_next) {
151 		/* Create a socket. */
152 		if ((s = socket(r->ai_family, r->ai_socktype, 0)) == -1)
153 			continue;
154 
155 		/* Attempt to connect. */
156 		if (connect(s, r->ai_addr, r->ai_addrlen) == 0)
157 			break;
158 
159 		/* Close the socket; this address didn't work. */
160 		close(s);
161 	}
162 
163 	/* Free the addresses. */
164 	freeaddrinfo(res);
165 
166 	/* Did we manage to connect? */
167 	if (r == NULL) {
168 		errstr = "Could not connect";
169 		goto out0;
170 	}
171 
172 	/* Launch SSL. */
173 	if (!SSL_library_init()) {
174 		errstr = "Could not initialize SSL";
175 		close(s);
176 		goto out0;
177 	}
178 
179 	/* Opt for compatibility. */
180 	if ((meth = SSLv23_client_method()) == NULL) {
181 		errstr = "Could not obtain SSL method";
182 		close(s);
183 		goto out0;
184 	}
185 
186 	/* Create an SSL context. */
187 	if ((ctx =
188 	    SSL_CTX_new((void *)(uintptr_t)(const void *)meth)) == NULL) {
189 		errstr = "Could not create SSL context";
190 		close(s);
191 		goto out0;
192 	}
193 
194 	/* Disable SSLv2 and SSLv3. */
195 	SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
196 
197 	/* We want blocking I/O; tell OpenSSL to keep trying reads/writes. */
198 	SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
199 
200 	/* Load root certificates. */
201 	if (certfile) {
202 		if (!SSL_CTX_load_verify_locations(ctx, certfile, NULL)) {
203 			errstr = "Could not load root certificates";
204 			close(s);
205 			goto out1;
206 		}
207 	} else {
208 		if (!SSL_CTX_set_default_verify_paths(ctx)) {
209 			errstr = "Could not load root certificates";
210 			close(s);
211 			goto out1;
212 		}
213 	}
214 
215 	/* Create an SSL connection within the specified context. */
216 	if ((ssl = SSL_new(ctx)) == NULL) {
217 		errstr = "Could not create SSL connection";
218 		close(s);
219 		goto out1;
220 	}
221 
222 	/* Attach the socket we opened earlier. */
223 	if ((b = BIO_new_socket(s, 1)) == NULL) {
224 		errstr = "Could not create BIO";
225 		close(s);
226 		goto out2;
227 	}
228 	SSL_set_bio(ssl, b, b);
229 
230 	/* Enable SNI; some servers need this to send us the right cert. */
231 	if (!SSL_set_tlsext_host_name(ssl, host)) {
232 		errstr = "Could not enable SNI";
233 		goto out2;
234 	}
235 
236 	/* Tell OpenSSL which host we're trying to talk to... */
237 	if (!SSL_set1_host(ssl, host)) {
238 		errstr = "SSL_set1_host failed";
239 		goto out2;
240 	}
241 
242 	/* ... and ask it to make sure that this is what is happening. */
243 	SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
244 	SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
245 
246 	/* Set ssl to work in client mode. */
247 	SSL_set_connect_state(ssl);
248 
249 	/* Perform the SSL handshake. */
250 	if (SSL_connect(ssl) != 1) {
251 		errstr = "SSL handshake failed";
252 		goto out2;
253 	}
254 
255 	/* Write our HTTP request. */
256 	if (SSL_write(ssl, req, reqlen) < reqlen) {
257 		errstr = "Could not write request";
258 		goto out3;
259 	}
260 
261 	/* Write the payload. */
262 	if (payload && !SSL_write_ex(ssl, payload, plen, &plen)) {
263 		errstr = "Could not write payload";
264 		goto out3;
265 	}
266 
267 	/* Read the response. */
268 	for (resppos = 0; ; resppos += readlen) {
269 		if ((readlen = SSL_read(ssl, &resp[resppos], *resplen)) <= 0)
270 			break;
271 		*resplen -= readlen;
272 	}
273 	*resplen = resppos;
274 
275 	/* Did the read fail? */
276 	if (readlen == -1) {
277 		errstr = "Could not read response";
278 		goto out3;
279 	}
280 
281 	/* Shut down SSL. */
282 out3:
283 	SSL_shutdown(ssl);
284 out2:
285 	SSL_free(ssl);
286 out1:
287 	SSL_CTX_free(ctx);
288 out0:
289 	return (errstr);
290 }
291