c6839ea773a687477d5cefde601b212dfd94dcc0
[oweals/openwrt-ustream-ssl.git] / ustream-openssl.c
1 /*
2  * ustream-ssl - library for SSL over ustream
3  *
4  * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <string.h>
20 #include <ctype.h>
21 #include <openssl/x509v3.h>
22 #include "ustream-ssl.h"
23 #include "ustream-internal.h"
24
25 __hidden struct ustream_ssl_ctx *
26 __ustream_ssl_context_new(bool server)
27 {
28         const void *m;
29         SSL_CTX *c;
30
31 #if OPENSSL_VERSION_NUMBER < 0x10100000L
32         static bool _init = false;
33
34         if (!_init) {
35                 SSL_load_error_strings();
36                 SSL_library_init();
37                 _init = true;
38         }
39 # define TLS_server_method SSLv23_server_method
40 # define TLS_client_method SSLv23_client_method
41 #endif
42
43         if (server) {
44                 m = TLS_server_method();
45         } else
46                 m = TLS_client_method();
47
48         c = SSL_CTX_new((void *) m);
49         if (!c)
50                 return NULL;
51
52         SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL);
53         SSL_CTX_set_options (c, SSL_OP_NO_COMPRESSION); /* avoid CRIME attack */
54 #if !defined(OPENSSL_NO_ECDH) && !defined(CYASSL_OPENSSL_H_) && OPENSSL_VERSION_NUMBER < 0x10100000L
55         SSL_CTX_set_ecdh_auto(c, 1);
56 #endif
57         if (server) {
58 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
59                 SSL_CTX_set_min_proto_version(c, TLS1_2_VERSION);
60 #else
61                 SSL_CTX_set_options (c, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
62 #endif
63                 SSL_CTX_set_cipher_list(c, "DEFAULT:!RC4:@STRENGTH");
64         }
65         SSL_CTX_set_quiet_shutdown(c, 1);
66
67         return (void *) c;
68 }
69
70 __hidden int __ustream_ssl_add_ca_crt_file(struct ustream_ssl_ctx *ctx, const char *file)
71 {
72         int ret;
73
74         ret = SSL_CTX_load_verify_locations((void *) ctx, file, NULL);
75         if (ret < 1)
76                 return -1;
77
78         return 0;
79 }
80
81 __hidden int __ustream_ssl_set_crt_file(struct ustream_ssl_ctx *ctx, const char *file)
82 {
83         int ret;
84
85         ret = SSL_CTX_use_certificate_chain_file((void *) ctx, file);
86         if (ret < 1)
87                 ret = SSL_CTX_use_certificate_file((void *) ctx, file, SSL_FILETYPE_ASN1);
88
89         if (ret < 1)
90                 return -1;
91
92         return 0;
93 }
94
95 __hidden int __ustream_ssl_set_key_file(struct ustream_ssl_ctx *ctx, const char *file)
96 {
97         int ret;
98
99         ret = SSL_CTX_use_PrivateKey_file((void *) ctx, file, SSL_FILETYPE_PEM);
100         if (ret < 1)
101                 ret = SSL_CTX_use_PrivateKey_file((void *) ctx, file, SSL_FILETYPE_ASN1);
102
103         if (ret < 1)
104                 return -1;
105
106         return 0;
107 }
108
109 __hidden void __ustream_ssl_context_free(struct ustream_ssl_ctx *ctx)
110 {
111         SSL_CTX_free((void *) ctx);
112 }
113
114 void __ustream_ssl_session_free(void *ssl)
115 {
116         SSL_shutdown(ssl);
117         SSL_free(ssl);
118 }
119
120 static void ustream_ssl_error(struct ustream_ssl *us, int ret)
121 {
122         us->error = ret;
123         uloop_timeout_set(&us->error_timer, 0);
124 }
125
126 #ifndef CYASSL_OPENSSL_H_
127
128 static bool ustream_ssl_verify_cn(struct ustream_ssl *us, X509 *cert)
129 {
130         int ret;
131
132         if (!us->peer_cn)
133                 return false;
134
135         ret = X509_check_host(cert, us->peer_cn, 0, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, NULL);
136         return ret == 1;
137 }
138
139
140 static void ustream_ssl_verify_cert(struct ustream_ssl *us)
141 {
142         void *ssl = us->ssl;
143         X509 *cert;
144         int res;
145
146         res = SSL_get_verify_result(ssl);
147         if (res != X509_V_OK) {
148                 if (us->notify_verify_error)
149                         us->notify_verify_error(us, res, X509_verify_cert_error_string(res));
150                 return;
151         }
152
153         cert = SSL_get_peer_certificate(ssl);
154         if (!cert)
155                 return;
156
157         us->valid_cert = true;
158         us->valid_cn = ustream_ssl_verify_cn(us, cert);
159         X509_free(cert);
160 }
161
162 #endif
163
164 __hidden enum ssl_conn_status __ustream_ssl_connect(struct ustream_ssl *us)
165 {
166         void *ssl = us->ssl;
167         int r;
168
169         if (us->server)
170                 r = SSL_accept(ssl);
171         else
172                 r = SSL_connect(ssl);
173
174         if (r == 1) {
175 #ifndef CYASSL_OPENSSL_H_
176                 ustream_ssl_verify_cert(us);
177 #endif
178                 return U_SSL_OK;
179         }
180
181         r = SSL_get_error(ssl, r);
182         if (r == SSL_ERROR_WANT_READ || r == SSL_ERROR_WANT_WRITE)
183                 return U_SSL_PENDING;
184
185         ustream_ssl_error(us, r);
186         return U_SSL_ERROR;
187 }
188
189 __hidden int __ustream_ssl_write(struct ustream_ssl *us, const char *buf, int len)
190 {
191         void *ssl = us->ssl;
192         int ret = SSL_write(ssl, buf, len);
193
194         if (ret < 0) {
195                 int err = SSL_get_error(ssl, ret);
196                 if (err == SSL_ERROR_WANT_WRITE)
197                         return 0;
198
199                 ustream_ssl_error(us, err);
200                 return -1;
201         }
202
203         return ret;
204 }
205
206 __hidden int __ustream_ssl_read(struct ustream_ssl *us, char *buf, int len)
207 {
208         int ret = SSL_read(us->ssl, buf, len);
209
210         if (ret < 0) {
211                 ret = SSL_get_error(us->ssl, ret);
212                 if (ret == SSL_ERROR_WANT_READ)
213                         return U_SSL_PENDING;
214
215                 ustream_ssl_error(us, ret);
216                 return U_SSL_ERROR;
217         }
218
219         return ret;
220 }
221