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