2 * IRC - Internet Relay Chat, ircd/ssl.c
3 * Copyright (C) 2015 pk910 (Philipp Kreil)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 1, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * @brief Implementation of functions for handling local clients.
29 #include "ircd_features.h"
31 #include "ircd_reply.h"
40 /* #include <assert.h> -- Now using assert in ircd_log.h */
44 #define IOV_MAX 16 /**< minimum required length of an iovec array */
47 #if defined(HAVE_OPENSSL_SSL_H)
49 static struct SSLPendingConections {
50 struct SSLConnection *connection;
51 struct SSLPendingConections *next;
54 enum SSLDataType datatype;
57 struct SSLPendingConections *firstPendingConection = NULL;
58 int ssl_is_initialized = 0;
60 static void ssl_init() {
61 if(ssl_is_initialized)
63 ssl_is_initialized = 1;
65 OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
66 SSL_load_error_strings();
69 void ssl_free_connection(struct SSLConnection *connection) {
70 SSL_CTX *context = NULL;
71 if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
72 struct SSLOutConnection *outconn = (struct SSLOutConnection *)connection;
73 context = outconn->context;
75 SSL_shutdown(connection->session);
76 SSL_free(connection->session);
78 SSL_CTX_free(context);
82 void ssl_free_listener(struct SSLListener *listener) {
83 SSL_CTX_free(listener->context);
87 static void ssl_handshake_completed(struct SSLConnection *connection, int success) {
88 struct SSLPendingConections *pending, *lastPending = NULL;
89 for(pending = firstPendingConection; pending; pending = pending->next) {
90 if(pending->connection == connection) {
92 lastPending->next = pending->next;
94 firstPendingConection = pending->next;
95 switch(pending->datatype) {
96 case SSLData_Client: {
97 struct Client *cptr = (struct Client *) pending->data;
99 if(FlagHas(&connection->flags, SSLFLAG_INCOMING))
101 else if(!completed_connection(cptr))
102 exit_client_msg(cptr, cptr, &me, "Registration failed.");
104 exit_client_msg(cptr, cptr, &me, "SSL Handshake failed.");
110 lastPending = pending;
114 static int ssl_handshake_outgoing(struct SSLConnection *connection) {
115 int ret = SSL_do_handshake(connection->session);
116 FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
117 FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_W);
119 switch(SSL_get_error(connection->session, ret)) {
121 FlagClr(&connection->flags, SSLFLAG_HANDSHAKE);
122 FlagSet(&connection->flags, SSLFLAG_READY);
124 ssl_handshake_completed(connection, 1);
126 case SSL_ERROR_WANT_READ:
127 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
129 case SSL_ERROR_WANT_WRITE:
130 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
138 struct SSLConnection *ssl_create_connect(int fd, void *data, enum SSLDataType datatype) {
139 struct SSLOutConnection *connection = malloc(sizeof(*connection));
140 struct SSLConnection *sslconn = (struct SSLConnection *)connection;
141 struct SSLPendingConections *pending = NULL;
146 if(!ssl_is_initialized)
149 connection->context = SSL_CTX_new(SSLv23_client_method());
150 if(!connection->context) {
151 goto ssl_create_connect_failed;
153 connection->session = SSL_new(connection->context);
154 if(!connection->session) {
155 goto ssl_create_connect_failed;
157 if(!SSL_set_fd(connection->session, fd)) {
158 goto ssl_create_connect_failed;
160 SSL_set_connect_state(connection->session);
161 FlagSet(&connection->flags, SSLFLAG_OUTGOING);
162 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
164 pending = malloc(sizeof(*pending));
166 goto ssl_create_connect_failed;
168 pending->connection = connection;
169 pending->next = firstPendingConection;
170 firstPendingConection = pending;
172 pending->data = data;
173 pending->datatype = datatype;
176 ssl_create_connect_failed:
181 void ssl_start_handshake_connect(struct SSLConnection *connection) {
182 ssl_handshake_outgoing(connection);
185 struct SSLListener *ssl_create_listener() {
186 if(!ssl_is_initialized)
189 struct SSLListener *listener = calloc(1, sizeof(*listener));
190 listener->context = SSL_CTX_new(SSLv23_server_method());
191 if(!listener->context) {
192 goto ssl_create_listener_failed;
195 char *certfile = conf_get_local()->sslcertfile;
196 char *keyfile = conf_get_local()->sslkeyfile;
197 char *cafile = conf_get_local()->sslcafile;
200 goto ssl_create_listener_failed;
206 /* load certificate */
207 if(SSL_CTX_use_certificate_file(listener->context, certfile, SSL_FILETYPE_PEM) <= 0) {
208 goto ssl_create_listener_failed;
211 if(SSL_CTX_use_PrivateKey_file(listener->context, keyfile, SSL_FILETYPE_PEM) <= 0) {
212 goto ssl_create_listener_failed;
214 /* check certificate and keyfile */
215 if(!SSL_CTX_check_private_key(listener->context)) {
216 goto ssl_create_listener_failed;
219 if(cafile && cafile[0] && SSL_CTX_load_verify_locations(listener->context, cafile, NULL) <= 0) {
220 goto ssl_create_listener_failed;
222 FlagSet(&listener->flags, SSLFLAG_READY);
224 ssl_create_listener_failed:
229 static int ssl_handshake_incoming(struct SSLConnection *connection) {
230 int result = SSL_accept(connection->session);
231 FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
232 FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_W);
233 switch(SSL_get_error(connection->session, result)) {
235 FlagClr(&connection->flags, SSLFLAG_HANDSHAKE);
236 FlagSet(&connection->flags, SSLFLAG_READY);
238 ssl_handshake_completed(connection, 1);
240 case SSL_ERROR_WANT_READ:
241 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
243 case SSL_ERROR_WANT_WRITE:
244 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
249 ssl_handshake_completed(connection, 0);
255 struct SSLConnection *ssl_start_handshake_listener(struct SSLListener *listener, int fd, void *data, enum SSLDataType datatype) {
258 struct SSLPendingConections *pending = NULL;
259 struct SSLConnection *connection = malloc(sizeof(*connection));
260 connection->session = SSL_new(listener->context);
261 if(!connection->session) {
262 goto ssl_start_handshake_listener_failed;
264 if(!SSL_set_fd(connection->session, fd)) {
265 goto ssl_start_handshake_listener_failed;
267 FlagSet(&connection->flags, SSLFLAG_INCOMING);
268 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
269 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
271 pending = malloc(sizeof(*pending));
273 goto ssl_start_handshake_listener_failed;
275 pending->connection = connection;
276 pending->next = firstPendingConection;
277 firstPendingConection = pending;
279 pending->data = data;
280 pending->datatype = datatype;
282 ssl_handshake_incoming(connection);
284 ssl_start_handshake_listener_failed:
289 IOResult ssl_recv_decrypt(struct SSLConnection *connection, char *buf, unsigned int buflen, unsigned int *len) {
290 if(FlagHas(&connection->flags, SSLFLAG_HANDSHAKE)) {
291 if(FlagHas(&connection->flags, SSLFLAG_INCOMING)) {
292 ssl_handshake_incoming(connection);
295 if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
296 ssl_handshake_outgoing(connection);
301 *len = SSL_read(connection->session, buf, buflen);
302 FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
303 int err = SSL_get_error(connection->session, *len);
307 case SSL_ERROR_ZERO_RETURN:
309 case SSL_ERROR_WANT_READ:
310 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
312 case SSL_ERROR_WANT_WRITE:
313 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
315 case SSL_ERROR_SYSCALL:
322 static ssize_t ssl_writev(SSL *ssl, const struct iovec *vector, int count) {
325 size_t bytes, to_copy;
328 /* Find the total number of bytes to be written. */
330 for (i = 0; i < count; ++i)
331 bytes += vector[i].iov_len;
333 /* Allocate a temporary buffer to hold the data. */
334 buffer = (char *) alloca (bytes);
336 /* Copy the data into BUFFER. */
339 for (i = 0; i < count; ++i) {
340 size_t copy = ((vector[i].iov_len) > (to_copy) ? (to_copy) : (vector[i].iov_len));
341 memcpy ((void *) bp, (void *) vector[i].iov_base, copy);
347 return SSL_write(ssl, buffer, bytes);
350 IOResult ssl_send_encrypt_plain(struct SSLConnection *connection, char* buf, int len) {
351 return SSL_write(connection->session, buf, len);
354 IOResult ssl_send_encrypt(struct SSLConnection *connection, struct MsgQ* buf, unsigned int *count_in, unsigned int *count_out) {
357 struct iovec iov[IOV_MAX];
360 assert(0 != count_in);
361 assert(0 != count_out);
364 count = msgq_mapiov(buf, iov, IOV_MAX, count_in);
365 res = ssl_writev(connection->session, iov, count);
367 switch(SSL_get_error(connection->session, res)) {
369 case SSL_ERROR_ZERO_RETURN:
370 *count_out = (unsigned) res;
372 case SSL_ERROR_WANT_READ:
373 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
375 case SSL_ERROR_WANT_WRITE:
376 FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
384 int ssl_connection_flush(struct SSLConnection *connection) {
386 if(ssl_handshake(connection)) {
387 if(FlagHas(&connection->flags, SSLFLAG_INCOMING)) {
388 return ssl_handshake_incoming(connection);
390 if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
391 return ssl_handshake_outgoing(connection);
395 struct SSLPendingConections *curr, *last = NULL, *next;
396 for(curr = firstPendingConection; curr; curr = next) {
398 if(!ssl_connection_flush(curr->connection)) {
399 // connection is already in auth process here, curr is freed!
409 void ssl_free_connection(struct SSLConnection *connection) {}
410 void ssl_free_listener(struct SSLConnection *listener) {}
411 struct SSLListener *ssl_create_listener() { return NULL; }
412 struct SSLConnection *ssl_start_handshake_listener(struct SSLListener *listener, int fd, void *data, enum SSLDataType datatype) { return NULL; }
413 IOResult ssl_recv_decrypt(struct SSLConnection *connection, char *buf, int *len) { return IO_FAILURE; }
414 int ssl_connection_flush(struct SSLConnection *connection) { return 0; };