0
|
1
|
|
2 /*
|
|
3 * Copyright (C) Igor Sysoev
|
|
4 */
|
|
5
|
|
6
|
|
7 #include <ngx_config.h>
|
|
8 #include <ngx_core.h>
|
|
9 #include <ngx_event.h>
|
|
10
|
|
11
|
|
12 static ngx_int_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);
|
|
13
|
|
14
|
|
15 ngx_int_t ngx_ssl_init(ngx_log_t *log)
|
|
16 {
|
|
17 SSL_library_init();
|
|
18 SSL_load_error_strings();
|
|
19
|
|
20 return NGX_OK;
|
|
21 }
|
|
22
|
|
23
|
|
24 ngx_int_t ngx_ssl_create_session(ngx_ssl_ctx_t *ssl_ctx, ngx_connection_t *c,
|
|
25 ngx_uint_t flags)
|
|
26 {
|
|
27 ngx_ssl_t *ssl;
|
|
28
|
|
29 if (!(ssl = ngx_pcalloc(c->pool, sizeof(ngx_ssl_t)))) {
|
|
30 return NGX_ERROR;
|
|
31 }
|
|
32
|
|
33 if (!(ssl->buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE))) {
|
|
34 return NGX_ERROR;
|
|
35 }
|
|
36
|
|
37 if (flags & NGX_SSL_BUFFER) {
|
|
38 ssl->buffer = 1;
|
|
39 }
|
|
40
|
|
41 ssl->ssl = SSL_new(ssl_ctx);
|
|
42
|
|
43 if (ssl->ssl == NULL) {
|
|
44 ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed");
|
|
45 return NGX_ERROR;
|
|
46 }
|
|
47
|
|
48 if (SSL_set_fd(ssl->ssl, c->fd) == 0) {
|
|
49 ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed");
|
|
50 return NGX_ERROR;
|
|
51 }
|
|
52
|
|
53 SSL_set_accept_state(ssl->ssl);
|
|
54
|
|
55 c->ssl = ssl;
|
|
56
|
|
57 return NGX_OK;
|
|
58 }
|
|
59
|
|
60
|
|
61 ngx_int_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size)
|
|
62 {
|
|
63 int n, sslerr;
|
|
64 ngx_err_t err;
|
|
65 char *handshake;
|
|
66
|
|
67 n = SSL_read(c->ssl->ssl, buf, size);
|
|
68
|
|
69 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n);
|
|
70
|
|
71 if (n > 0) {
|
|
72 return n;
|
|
73 }
|
|
74
|
2
|
75 if (!SSL_is_init_finished(c->ssl->ssl)) {
|
|
76 handshake = "in SSL handshake";
|
|
77
|
|
78 } else {
|
|
79 handshake = "";
|
|
80 }
|
|
81
|
0
|
82 sslerr = SSL_get_error(c->ssl->ssl, n);
|
|
83
|
|
84 err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
|
|
85
|
|
86 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
|
|
87
|
|
88 if (sslerr == SSL_ERROR_WANT_READ) {
|
4
|
89 c->read->ready = 0;
|
0
|
90 return NGX_AGAIN;
|
|
91 }
|
|
92
|
|
93 if (sslerr == SSL_ERROR_WANT_WRITE) {
|
|
94 ngx_log_error(NGX_LOG_ALERT, c->log, err,
|
|
95 "SSL wants to write%s", handshake);
|
|
96 return NGX_ERROR;
|
|
97 #if 0
|
|
98 return NGX_AGAIN;
|
|
99 #endif
|
|
100 }
|
|
101
|
|
102 c->ssl->no_rcv_shut = 1;
|
|
103
|
|
104 if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
|
|
105 ngx_log_error(NGX_LOG_INFO, c->log, err,
|
|
106 "client closed connection%s", handshake);
|
|
107
|
|
108 return NGX_ERROR;
|
|
109 }
|
|
110
|
|
111 ngx_ssl_error(NGX_LOG_ALERT, c->log, err,
|
|
112 "SSL_read() failed%s", handshake);
|
|
113
|
|
114 return NGX_ERROR;
|
|
115 }
|
|
116
|
|
117
|
|
118 /*
|
|
119 * OpenSSL has no SSL_writev() so we copy several bufs into our 16K buffer
|
|
120 * before SSL_write() call to decrease a SSL overhead.
|
|
121 *
|
|
122 * Besides for protocols such as HTTP it is possible to always buffer
|
|
123 * the output to decrease a SSL overhead some more.
|
|
124 */
|
|
125
|
|
126 ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
|
|
127 off_t limit)
|
|
128 {
|
|
129 int n;
|
|
130 ngx_uint_t flush;
|
|
131 ssize_t send, size;
|
|
132 ngx_buf_t *buf;
|
|
133
|
|
134 buf = c->ssl->buf;
|
|
135
|
|
136 if (in && in->next == NULL && !c->buffered && !c->ssl->buffer) {
|
|
137
|
|
138 /*
|
|
139 * we avoid a buffer copy if the incoming buf is a single,
|
|
140 * our buffer is empty, and we do not need to buffer the output
|
|
141 */
|
|
142
|
|
143 n = ngx_ssl_write(c, in->buf->pos, in->buf->last - in->buf->pos);
|
|
144
|
|
145 if (n == NGX_ERROR) {
|
|
146 return NGX_CHAIN_ERROR;
|
|
147 }
|
|
148
|
|
149 if (n < 0) {
|
|
150 n = 0;
|
|
151 }
|
|
152
|
|
153 in->buf->pos += n;
|
|
154
|
|
155 return in;
|
|
156 }
|
|
157
|
|
158 send = 0;
|
|
159 flush = (in == NULL) ? 1 : 0;
|
|
160
|
|
161 for ( ;; ) {
|
|
162
|
|
163 while (in && buf->last < buf->end) {
|
|
164 if (in->buf->last_buf) {
|
|
165 flush = 1;
|
|
166 }
|
|
167
|
|
168 if (ngx_buf_special(in->buf)) {
|
|
169 in = in->next;
|
|
170 continue;
|
|
171 }
|
|
172
|
|
173 size = in->buf->last - in->buf->pos;
|
|
174
|
|
175 if (size > buf->end - buf->last) {
|
|
176 size = buf->end - buf->last;
|
|
177 }
|
|
178
|
|
179 /*
|
|
180 * TODO: the taking in->buf->flush into account can be
|
|
181 * implemented using the limit on the higher level
|
|
182 */
|
|
183
|
|
184 if (send + size > limit) {
|
|
185 size = limit - send;
|
|
186 flush = 1;
|
|
187 }
|
|
188
|
|
189 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
190 "SSL buf copy: %d", size);
|
|
191
|
|
192 ngx_memcpy(buf->last, in->buf->pos, size);
|
|
193
|
|
194 buf->last += size;
|
|
195
|
|
196 in->buf->pos += size;
|
|
197 if (in->buf->pos == in->buf->last) {
|
|
198 in = in->next;
|
|
199 }
|
|
200 }
|
|
201
|
|
202 size = buf->last - buf->pos;
|
|
203
|
|
204 if (!flush && buf->last < buf->end && c->ssl->buffer) {
|
|
205 break;
|
|
206 }
|
|
207
|
|
208 n = ngx_ssl_write(c, buf->pos, size);
|
|
209
|
|
210 if (n == NGX_ERROR) {
|
|
211 return NGX_CHAIN_ERROR;
|
|
212 }
|
|
213
|
|
214 if (n < 0) {
|
|
215 n = 0;
|
|
216 }
|
|
217
|
|
218 buf->pos += n;
|
|
219 send += n;
|
|
220 c->sent += n;
|
|
221
|
|
222 if (n < size) {
|
|
223 break;
|
|
224 }
|
|
225
|
|
226 if (buf->pos == buf->last) {
|
|
227 buf->pos = buf->start;
|
|
228 buf->last = buf->start;
|
|
229 }
|
|
230
|
|
231 if (in == NULL || send == limit) {
|
|
232 break;
|
|
233 }
|
|
234 }
|
|
235
|
|
236 c->buffered = (buf->pos < buf->last) ? 1 : 0;
|
|
237
|
|
238 return in;
|
|
239 }
|
|
240
|
|
241
|
|
242 static ngx_int_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
|
|
243 {
|
2
|
244 int n, sslerr;
|
|
245 ngx_err_t err;
|
|
246 char *handshake;
|
0
|
247
|
|
248 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size);
|
|
249
|
|
250 n = SSL_write(c->ssl->ssl, data, size);
|
|
251
|
|
252 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_write: %d", n);
|
|
253
|
|
254 if (n > 0) {
|
|
255 return n;
|
|
256 }
|
|
257
|
|
258 sslerr = SSL_get_error(c->ssl->ssl, n);
|
|
259
|
|
260 err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
|
|
261
|
|
262 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
|
|
263
|
|
264 if (sslerr == SSL_ERROR_WANT_WRITE) {
|
|
265 c->write->ready = 0;
|
|
266 return NGX_AGAIN;
|
|
267 }
|
|
268
|
|
269 if (sslerr == SSL_ERROR_WANT_READ) {
|
2
|
270
|
|
271 if (!SSL_is_init_finished(c->ssl->ssl)) {
|
|
272 handshake = "in SSL handshake";
|
|
273
|
|
274 } else {
|
|
275 handshake = "";
|
|
276 }
|
|
277
|
0
|
278 ngx_log_error(NGX_LOG_ALERT, c->log, err,
|
|
279 "SSL wants to read%s", handshake);
|
|
280 return NGX_ERROR;
|
|
281 #if 0
|
|
282 return NGX_AGAIN;
|
2
|
283 #endif
|
0
|
284 }
|
|
285
|
|
286 c->ssl->no_rcv_shut = 1;
|
|
287
|
|
288 ngx_ssl_error(NGX_LOG_ALERT, c->log, err, "SSL_write() failed");
|
|
289
|
|
290 return NGX_ERROR;
|
|
291 }
|
|
292
|
|
293
|
|
294 ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c)
|
|
295 {
|
|
296 int n, sslerr;
|
|
297 ngx_uint_t again;
|
|
298
|
|
299 if (c->timedout) {
|
|
300 SSL_set_shutdown(c->ssl->ssl, SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN);
|
|
301
|
|
302 } else {
|
|
303 if (c->ssl->no_rcv_shut) {
|
|
304 SSL_set_shutdown(c->ssl->ssl, SSL_RECEIVED_SHUTDOWN);
|
|
305 }
|
|
306
|
|
307 if (c->ssl->no_send_shut) {
|
|
308 SSL_set_shutdown(c->ssl->ssl, SSL_SENT_SHUTDOWN);
|
|
309 }
|
|
310 }
|
|
311
|
|
312 again = 0;
|
|
313
|
|
314 for ( ;; ) {
|
|
315 n = SSL_shutdown(c->ssl->ssl);
|
|
316
|
|
317 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n);
|
|
318
|
|
319 if (n == 0) {
|
|
320 again = 1;
|
|
321 break;
|
|
322 }
|
|
323
|
|
324 if (n == 1) {
|
|
325 SSL_free(c->ssl->ssl);
|
|
326 c->ssl = NULL;
|
|
327 return NGX_OK;
|
|
328 }
|
|
329
|
|
330 break;
|
|
331 }
|
|
332
|
|
333 if (!again) {
|
|
334 sslerr = SSL_get_error(c->ssl->ssl, n);
|
|
335
|
|
336 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
337 "SSL_get_error: %d", sslerr);
|
|
338 }
|
|
339
|
|
340 if (again || sslerr == SSL_ERROR_WANT_READ) {
|
|
341
|
|
342 ngx_add_timer(c->read, 10000);
|
|
343
|
|
344 if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
|
|
345 return NGX_ERROR;
|
|
346 }
|
|
347
|
|
348 return NGX_AGAIN;
|
|
349 }
|
|
350
|
|
351 if (sslerr == SSL_ERROR_WANT_WRITE) {
|
|
352
|
|
353 if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
|
|
354 return NGX_ERROR;
|
|
355 }
|
|
356
|
|
357 return NGX_AGAIN;
|
|
358 }
|
|
359
|
|
360 ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_shutdown() failed");
|
|
361
|
|
362 return NGX_ERROR;
|
|
363 }
|
|
364
|
|
365
|
|
366 void ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
|
|
367 char *fmt, ...)
|
|
368 {
|
|
369 int len;
|
|
370 char errstr[NGX_MAX_CONF_ERRSTR];
|
|
371 va_list args;
|
|
372
|
|
373 va_start(args, fmt);
|
|
374 len = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args);
|
|
375 va_end(args);
|
|
376
|
|
377 errstr[len++] = ' ';
|
|
378 errstr[len++] = '(';
|
|
379 errstr[len++] = 'S';
|
|
380 errstr[len++] = 'S';
|
|
381 errstr[len++] = 'L';
|
|
382 errstr[len++] = ':';
|
|
383 errstr[len++] = ' ';
|
|
384
|
|
385 ERR_error_string_n(ERR_get_error(), errstr + len, sizeof(errstr) - len - 1);
|
|
386
|
|
387 ngx_log_error(level, log, err, "%s)", errstr);
|
|
388 }
|