fixed some Windows makefile build issues; added code signing to Windows makefiles
[public/netxms.git] / src / libstrophe / tls_openssl.c
1 /* tls_openssl.c
2 ** strophe XMPP client library -- TLS abstraction openssl impl.
3 **
4 ** Copyright (C) 2005-008 Collecta, Inc.
5 **
6 ** This software is provided AS-IS with no warranty, either express
7 ** or implied.
8 **
9 ** This program is dual licensed under the MIT and GPLv3 licenses.
10 */
11
12 /** @file
13 * TLS implementation with OpenSSL.
14 */
15
16 #include <nms_common.h>
17 #include <openssl/ssl.h>
18 #include <openssl/rand.h>
19 #include <openssl/err.h>
20
21 #include "common.h"
22 #include "tls.h"
23 #include "sock.h"
24
25 struct _tls {
26 xmpp_ctx_t *ctx;
27 sock_t sock;
28 SSL_CTX *ssl_ctx;
29 SSL *ssl;
30 int lasterror;
31 };
32
33 enum {
34 TLS_SHUTDOWN_MAX_RETRIES = 10,
35 TLS_TIMEOUT_SEC = 0,
36 TLS_TIMEOUT_USEC = 100000,
37 };
38
39 static void _tls_sock_wait(tls_t *tls, int error);
40 static void _tls_set_error(tls_t *tls, int error);
41 static void _tls_log_error(xmpp_ctx_t *ctx);
42
43 void tls_initialize(void)
44 {
45 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
46 OPENSSL_init_ssl(0, NULL);
47 #else
48 SSL_library_init();
49 SSL_load_error_strings();
50 #endif
51 }
52
53 void tls_shutdown(void)
54 {
55 return;
56 }
57
58 int tls_error(tls_t *tls)
59 {
60 return tls->lasterror;
61 }
62
63 tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
64 {
65 tls_t *tls = xmpp_alloc(ctx, sizeof(*tls));
66
67 if (tls) {
68 int ret;
69 memset(tls, 0, sizeof(*tls));
70
71 tls->ctx = ctx;
72 tls->sock = sock;
73 tls->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
74 if (tls->ssl_ctx == NULL)
75 goto err;
76
77 SSL_CTX_set_client_cert_cb(tls->ssl_ctx, NULL);
78 SSL_CTX_set_mode(tls->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
79 SSL_CTX_set_verify(tls->ssl_ctx, SSL_VERIFY_NONE, NULL);
80
81 tls->ssl = SSL_new(tls->ssl_ctx);
82 if (tls->ssl == NULL)
83 goto err_free_ctx;
84
85 ret = SSL_set_fd(tls->ssl, sock);
86 if (ret <= 0)
87 goto err_free_ssl;
88 }
89
90 return tls;
91
92 err_free_ssl:
93 SSL_free(tls->ssl);
94 err_free_ctx:
95 SSL_CTX_free(tls->ssl_ctx);
96 err:
97 xmpp_free(ctx, tls);
98 _tls_log_error(ctx);
99 return NULL;
100 }
101
102 void tls_free(tls_t *tls)
103 {
104 SSL_free(tls->ssl);
105 SSL_CTX_free(tls->ssl_ctx);
106 xmpp_free(tls->ctx, tls);
107 }
108
109 int tls_set_credentials(tls_t *tls, const char *cafilename)
110 {
111 return -1;
112 }
113
114 int tls_start(tls_t *tls)
115 {
116 int error;
117 int ret;
118
119 /* Since we're non-blocking, loop the connect call until it
120 succeeds or fails */
121 while (1) {
122 ret = SSL_connect(tls->ssl);
123 error = ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0;
124
125 if (ret == -1 && tls_is_recoverable(error)) {
126 /* wait for something to happen on the sock before looping back */
127 _tls_sock_wait(tls, error);
128 continue;
129 }
130
131 /* success or fatal error */
132 break;
133 }
134 _tls_set_error(tls, error);
135
136 return ret <= 0 ? 0 : 1;
137 }
138
139 int tls_stop(tls_t *tls)
140 {
141 int retries = 0;
142 int error;
143 int ret;
144
145 while (1) {
146 ++retries;
147 ret = SSL_shutdown(tls->ssl);
148 error = ret < 0 ? SSL_get_error(tls->ssl, ret) : 0;
149 if (ret == 1 || !tls_is_recoverable(error) ||
150 retries >= TLS_SHUTDOWN_MAX_RETRIES) {
151 break;
152 }
153 _tls_sock_wait(tls, error);
154 }
155 _tls_set_error(tls, error);
156
157 return ret <= 0 ? 0 : 1;
158 }
159
160 int tls_is_recoverable(int error)
161 {
162 return (error == SSL_ERROR_NONE || error == SSL_ERROR_WANT_READ
163 || error == SSL_ERROR_WANT_WRITE
164 || error == SSL_ERROR_WANT_CONNECT
165 || error == SSL_ERROR_WANT_ACCEPT);
166 }
167
168 int tls_pending(tls_t *tls)
169 {
170 return SSL_pending(tls->ssl);
171 }
172
173 int tls_read(tls_t *tls, void * const buff, const size_t len)
174 {
175 int ret;
176
177 ret = SSL_read(tls->ssl, buff, len);
178 _tls_set_error(tls, ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0);
179
180 return ret;
181 }
182
183 int tls_write(tls_t *tls, const void * const buff, const size_t len)
184 {
185 int ret;
186
187 ret = SSL_write(tls->ssl, buff, len);
188 _tls_set_error(tls, ret <= 0 ? SSL_get_error(tls->ssl, ret) : 0);
189
190 return ret;
191 }
192
193 int tls_clear_pending_write(tls_t *tls)
194 {
195 return 0;
196 }
197
198 static void _tls_sock_wait(tls_t *tls, int error)
199 {
200 struct timeval tv;
201 fd_set rfds;
202 fd_set wfds;
203 int nfds;
204 int ret;
205
206 FD_ZERO(&rfds);
207 FD_ZERO(&wfds);
208 if (error == SSL_ERROR_WANT_READ)
209 FD_SET(tls->sock, &rfds);
210 if (error == SSL_ERROR_WANT_WRITE)
211 FD_SET(tls->sock, &wfds);
212 nfds = (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) ?
213 tls->sock + 1 : 0;
214 do {
215 tv.tv_sec = TLS_TIMEOUT_SEC;
216 tv.tv_usec = TLS_TIMEOUT_USEC;
217 ret = select(nfds, &rfds, &wfds, NULL, &tv);
218 } while (ret == -1 && WSAGetLastError() == WSAEINTR);
219 }
220
221 static void _tls_set_error(tls_t *tls, int error)
222 {
223 if (error != 0 && !tls_is_recoverable(error)) {
224 _tls_log_error(tls->ctx);
225 }
226 tls->lasterror = error;
227 }
228
229 static void _tls_log_error(xmpp_ctx_t *ctx)
230 {
231 unsigned long e;
232 char buf[256];
233
234 do {
235 e = ERR_get_error();
236 if (e != 0) {
237 ERR_error_string_n(e, buf, sizeof(buf));
238 xmpp_debug(ctx, "tls", "%s", buf);
239 }
240 } while (e != 0);
241 }