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