polarssl: check us->notify_verify_error for NULL before calling it
[project/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 <libubox/ustream.h>
22
23 #include "ustream-ssl.h"
24 #include "ustream-internal.h"
25
26 static void ustream_ssl_error_cb(struct uloop_timeout *t)
27 {
28 struct ustream_ssl *us = container_of(t, struct ustream_ssl, error_timer);
29 static char buffer[128];
30 int error = us->error;
31
32 if (us->notify_error)
33 us->notify_error(us, error, __ustream_ssl_strerror(us->error, buffer, sizeof(buffer)));
34 }
35
36 static void ustream_ssl_check_conn(struct ustream_ssl *us)
37 {
38 if (us->connected || us->error)
39 return;
40
41 if (__ustream_ssl_connect(us) == U_SSL_OK) {
42 us->connected = true;
43 if (us->notify_connected)
44 us->notify_connected(us);
45 ustream_write_pending(&us->stream);
46 }
47 }
48
49 static bool __ustream_ssl_poll(struct ustream *s)
50 {
51 struct ustream_ssl *us = container_of(s->next, struct ustream_ssl, stream);
52 char *buf;
53 int len, ret;
54 bool more = false;
55
56 ustream_ssl_check_conn(us);
57 if (!us->connected || us->error)
58 return false;
59
60 do {
61 buf = ustream_reserve(&us->stream, 1, &len);
62 if (!len)
63 break;
64
65 ret = __ustream_ssl_read(us, buf, len);
66 switch (ret) {
67 case U_SSL_PENDING:
68 return more;
69 case U_SSL_ERROR:
70 return false;
71 case 0:
72 us->stream.eof = true;
73 ustream_state_change(&us->stream);
74 return false;
75 default:
76 ustream_fill_read(&us->stream, ret);
77 more = true;
78 continue;
79 }
80 } while (1);
81
82 return more;
83 }
84
85 static void ustream_ssl_notify_read(struct ustream *s, int bytes)
86 {
87 __ustream_ssl_poll(s);
88 }
89
90 static void ustream_ssl_notify_write(struct ustream *s, int bytes)
91 {
92 struct ustream_ssl *us = container_of(s->next, struct ustream_ssl, stream);
93
94 ustream_ssl_check_conn(us);
95 ustream_write_pending(s->next);
96 }
97
98 static void ustream_ssl_notify_state(struct ustream *s)
99 {
100 s->next->write_error = true;
101 ustream_state_change(s->next);
102 }
103
104 static int ustream_ssl_write(struct ustream *s, const char *buf, int len, bool more)
105 {
106 struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
107
108 if (!us->connected || us->error)
109 return 0;
110
111 if (us->conn->w.data_bytes)
112 return 0;
113
114 return __ustream_ssl_write(us, buf, len);
115 }
116
117 static void ustream_ssl_set_read_blocked(struct ustream *s)
118 {
119 struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
120
121 ustream_set_read_blocked(us->conn, !!s->read_blocked);
122 }
123
124 static void ustream_ssl_free(struct ustream *s)
125 {
126 struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
127
128 if (us->conn) {
129 us->conn->next = NULL;
130 us->conn->notify_read = NULL;
131 us->conn->notify_write = NULL;
132 us->conn->notify_state = NULL;
133 }
134
135 uloop_timeout_cancel(&us->error_timer);
136 __ustream_ssl_session_free(us->ssl);
137 free(us->peer_cn);
138
139 us->ctx = NULL;
140 us->ssl = NULL;
141 us->conn = NULL;
142 us->peer_cn = NULL;
143 us->connected = false;
144 us->error = false;
145 us->valid_cert = false;
146 us->valid_cn = false;
147 }
148
149 static bool ustream_ssl_poll(struct ustream *s)
150 {
151 struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
152 bool fd_poll;
153
154 fd_poll = ustream_poll(us->conn);
155 return __ustream_ssl_poll(s) || fd_poll;
156 }
157
158 static void ustream_ssl_stream_init(struct ustream_ssl *us)
159 {
160 struct ustream *conn = us->conn;
161 struct ustream *s = &us->stream;
162
163 conn->notify_read = ustream_ssl_notify_read;
164 conn->notify_write = ustream_ssl_notify_write;
165 conn->notify_state = ustream_ssl_notify_state;
166
167 s->free = ustream_ssl_free;
168 s->write = ustream_ssl_write;
169 s->poll = ustream_ssl_poll;
170 s->set_read_blocked = ustream_ssl_set_read_blocked;
171 ustream_init_defaults(s);
172 }
173
174 static int _ustream_ssl_init(struct ustream_ssl *us, struct ustream *conn, struct ustream_ssl_ctx *ctx, bool server)
175 {
176 us->error_timer.cb = ustream_ssl_error_cb;
177 us->server = server;
178 us->conn = conn;
179 us->ctx = ctx;
180
181 us->ssl = __ustream_ssl_session_new(us->ctx);
182 if (!us->ssl)
183 return -ENOMEM;
184
185 conn->next = &us->stream;
186 ustream_set_io(ctx, us->ssl, conn);
187 ustream_ssl_stream_init(us);
188 ustream_ssl_check_conn(us);
189
190 return 0;
191 }
192
193 static int _ustream_ssl_set_peer_cn(struct ustream_ssl *us, const char *name)
194 {
195 us->peer_cn = strdup(name);
196 __ustream_ssl_update_peer_cn(us);
197
198 return 0;
199 }
200
201 const struct ustream_ssl_ops ustream_ssl_ops = {
202 .context_new = __ustream_ssl_context_new,
203 .context_set_crt_file = __ustream_ssl_set_crt_file,
204 .context_set_key_file = __ustream_ssl_set_key_file,
205 .context_add_ca_crt_file = __ustream_ssl_add_ca_crt_file,
206 .context_free = __ustream_ssl_context_free,
207 .init = _ustream_ssl_init,
208 .set_peer_cn = _ustream_ssl_set_peer_cn,
209 };