ustream-openssl: clear error stack before SSL_read/SSL_write
[oweals/openwrt-ustream-ssl.git] / ustream-ssl.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 <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <libubox/ustream.h>
23
24 #include "ustream-ssl.h"
25 #include "ustream-internal.h"
26
27 static void ustream_ssl_error_cb(struct uloop_timeout *t)
28 {
29         struct ustream_ssl *us = container_of(t, struct ustream_ssl, error_timer);
30         static char buffer[128];
31         int error = us->error;
32
33         if (us->notify_error)
34                 us->notify_error(us, error, __ustream_ssl_strerror(us->error, buffer, sizeof(buffer)));
35 }
36
37 static void ustream_ssl_check_conn(struct ustream_ssl *us)
38 {
39         if (us->connected || us->error)
40                 return;
41
42         if (__ustream_ssl_connect(us) == U_SSL_OK) {
43
44                 /* __ustream_ssl_connect() will also return U_SSL_OK when certificate
45                  * verification failed!
46                  *
47                  * Applications may register a custom .notify_verify_error callback in the
48                  * struct ustream_ssl which is called upon verification failures, but there
49                  * is no straight forward way for the callback to terminate the connection
50                  * initiation right away, e.g. through a true or false return value.
51                  *
52                  * Instead, existing implementations appear to set .eof field of the underlying
53                  * ustream in the hope that this inhibits further operations on the stream.
54                  *
55                  * Declare this informal behaviour "official" and check for the state of the
56                  * .eof member after __ustream_ssl_connect() returned, and do not write the
57                  * pending data if it is set to true.
58                  */
59
60                 if (us->stream.eof)
61                         return;
62
63                 us->connected = true;
64                 if (us->notify_connected)
65                         us->notify_connected(us);
66                 ustream_write_pending(&us->stream);
67         }
68 }
69
70 static bool __ustream_ssl_poll(struct ustream *s)
71 {
72         struct ustream_ssl *us = container_of(s->next, struct ustream_ssl, stream);
73         char *buf;
74         int len, ret;
75         bool more = false;
76
77         ustream_ssl_check_conn(us);
78         if (!us->connected || us->error)
79                 return false;
80
81         do {
82                 buf = ustream_reserve(&us->stream, 1, &len);
83                 if (!len)
84                         break;
85
86                 ret = __ustream_ssl_read(us, buf, len);
87                 switch (ret) {
88                 case U_SSL_PENDING:
89                         return more;
90                 case U_SSL_ERROR:
91                         return false;
92                 case 0:
93                         us->stream.eof = true;
94                         ustream_state_change(&us->stream);
95                         return false;
96                 default:
97                         ustream_fill_read(&us->stream, ret);
98                         more = true;
99                         continue;
100                 }
101         } while (1);
102
103         return more;
104 }
105
106 static void ustream_ssl_notify_read(struct ustream *s, int bytes)
107 {
108         __ustream_ssl_poll(s);
109 }
110
111 static void ustream_ssl_notify_write(struct ustream *s, int bytes)
112 {
113         struct ustream_ssl *us = container_of(s->next, struct ustream_ssl, stream);
114
115         ustream_ssl_check_conn(us);
116         ustream_write_pending(s->next);
117 }
118
119 static void ustream_ssl_notify_state(struct ustream *s)
120 {
121         s->next->write_error = true;
122         ustream_state_change(s->next);
123 }
124
125 static int ustream_ssl_write(struct ustream *s, const char *buf, int len, bool more)
126 {
127         struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
128
129         if (!us->connected || us->error)
130                 return 0;
131
132         if (us->conn->w.data_bytes)
133                 return 0;
134
135         return __ustream_ssl_write(us, buf, len);
136 }
137
138 static void ustream_ssl_set_read_blocked(struct ustream *s)
139 {
140         struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
141
142         ustream_set_read_blocked(us->conn, !!s->read_blocked);
143 }
144
145 static void ustream_ssl_free(struct ustream *s)
146 {
147         struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
148
149         if (us->conn) {
150                 us->conn->next = NULL;
151                 us->conn->notify_read = NULL;
152                 us->conn->notify_write = NULL;
153                 us->conn->notify_state = NULL;
154         }
155
156         uloop_timeout_cancel(&us->error_timer);
157         __ustream_ssl_session_free(us->ssl);
158         free(us->peer_cn);
159
160         us->ctx = NULL;
161         us->ssl = NULL;
162         us->conn = NULL;
163         us->peer_cn = NULL;
164         us->connected = false;
165         us->error = false;
166         us->valid_cert = false;
167         us->valid_cn = false;
168 }
169
170 static bool ustream_ssl_poll(struct ustream *s)
171 {
172         struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
173         bool fd_poll;
174
175         fd_poll = ustream_poll(us->conn);
176         return __ustream_ssl_poll(us->conn) || fd_poll;
177 }
178
179 static void ustream_ssl_stream_init(struct ustream_ssl *us)
180 {
181         struct ustream *conn = us->conn;
182         struct ustream *s = &us->stream;
183
184         conn->notify_read = ustream_ssl_notify_read;
185         conn->notify_write = ustream_ssl_notify_write;
186         conn->notify_state = ustream_ssl_notify_state;
187
188         s->free = ustream_ssl_free;
189         s->write = ustream_ssl_write;
190         s->poll = ustream_ssl_poll;
191         s->set_read_blocked = ustream_ssl_set_read_blocked;
192         ustream_init_defaults(s);
193 }
194
195 static int _ustream_ssl_init(struct ustream_ssl *us, struct ustream *conn, struct ustream_ssl_ctx *ctx, bool server)
196 {
197         us->error_timer.cb = ustream_ssl_error_cb;
198         us->server = server;
199         us->conn = conn;
200         us->ctx = ctx;
201
202 #if defined(HAVE_WOLFSSL) && defined(NO_WOLFSSL_SSLSETIO_SEND_RECV)
203         ustream_set_io(ctx, NULL, conn);
204 #endif
205         us->ssl = __ustream_ssl_session_new(us->ctx);
206         if (!us->ssl)
207                 return -ENOMEM;
208
209         conn->next = &us->stream;
210         ustream_set_io(ctx, us->ssl, conn);
211         ustream_ssl_stream_init(us);
212
213         if (us->server_name)
214                 __ustream_ssl_set_server_name(us);
215
216         ustream_ssl_check_conn(us);
217
218         return 0;
219 }
220
221 static int _ustream_ssl_set_peer_cn(struct ustream_ssl *us, const char *name)
222 {
223         us->peer_cn = strdup(name);
224         __ustream_ssl_update_peer_cn(us);
225
226         return 0;
227 }
228
229 const struct ustream_ssl_ops ustream_ssl_ops = {
230         .context_new = __ustream_ssl_context_new,
231         .context_set_crt_file = __ustream_ssl_set_crt_file,
232         .context_set_key_file = __ustream_ssl_set_key_file,
233         .context_add_ca_crt_file = __ustream_ssl_add_ca_crt_file,
234         .context_set_ciphers = __ustream_ssl_set_ciphers,
235         .context_free = __ustream_ssl_context_free,
236         .init = _ustream_ssl_init,
237         .set_peer_cn = _ustream_ssl_set_peer_cn,
238 };