Skip to content

Commit af9a44a

Browse files
committed
Introduces DTLS for UDP
1 parent e7d2fa8 commit af9a44a

12 files changed

+436
-10
lines changed

meson.build

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ conf.set('GPERF_LEN_TYPE', gperf_len_type,
128128

129129
############################################################
130130

131+
libopenssl = dependency('openssl',
132+
version : '>= 1.1.0',
133+
required : get_option('openssl'))
134+
conf.set10('HAVE_OPENSSL', libopenssl.found())
135+
136+
############################################################
131137
config_h = configure_file(
132138
output : 'config.h',
133139
configuration : conf)
@@ -165,6 +171,7 @@ systemd_netlogd = executable(
165171
link_with : libshared,
166172
dependencies : [
167173
libcap,
174+
libopenssl,
168175
libsystemd],
169176
install : true,
170177
install_dir : get_option('prefix'))

meson_options.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
option('version-tag', type : 'string',
2+
description : 'override the git version string')
3+
4+
option('openssl', type : 'boolean', value : true,
5+
description : 'enable openssl support')

src/meson.build

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ libshared_sources = files('''
4141
share/ioprio.h
4242
share/io-util.c
4343
share/io-util.h
44+
share/iovec-util.c
45+
share/iovec-util.h
4446
share/escape.c
4547
share/escape.h
4648
share/user-util.c
@@ -103,6 +105,9 @@ systemd_netlogd_sources = files('''
103105
netlog/netlog-manager.c
104106
netlog/netlog-manager.h
105107
netlog/netlog-network.c
108+
netlog/netlog-network.h
109+
netlog/netlog-dtls.c
110+
netlog/netlog-dtls.h
106111
'''.split())
107112

108113
netlogd_gperf_c = custom_target(

src/netlog/netlog-dtls.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include <openssl/bio.h>
4+
#include <openssl/err.h>
5+
#include <sys/epoll.h>
6+
#include <sys/socket.h>
7+
#include <netinet/in.h>
8+
#include <arpa/inet.h>
9+
10+
#include "alloc-util.h"
11+
#include "io-util.h"
12+
#include "iovec-util.h"
13+
#include "netlog-dtls.h"
14+
#include "fd-util.h"
15+
16+
static char *tls_error_string(int ssl_error, char *buf, size_t count) {
17+
assert(buf || count == 0);
18+
if (ssl_error == SSL_ERROR_SSL)
19+
ERR_error_string_n(ERR_get_error(), buf, count);
20+
else
21+
snprintf(buf, count, "SSL_get_error()=%d", ssl_error);
22+
return buf;
23+
}
24+
25+
#define TLS_ERROR_BUFSIZE 256
26+
#define TLS_ERROR_STRING(error) \
27+
tls_error_string((error), (char[TLS_ERROR_BUFSIZE]){}, TLS_ERROR_BUFSIZE)
28+
29+
static ssize_t dtls_write(DTLSManager *m, const char *buf, size_t count) {
30+
int error, r;
31+
ssize_t ss;
32+
33+
assert(m);
34+
35+
ERR_clear_error();
36+
ss = r = SSL_write(m->ssl, buf, count);
37+
if (r <= 0) {
38+
error = SSL_get_error(m->ssl, r);
39+
if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
40+
m->events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
41+
ss = -EAGAIN;
42+
} else if (error == SSL_ERROR_ZERO_RETURN) {
43+
m->events = 0;
44+
ss = 0;
45+
} else {
46+
log_debug("Failed to invoke SSL_write: %s", TLS_ERROR_STRING(error));
47+
m->events = 0;
48+
ss = -EPIPE;
49+
}
50+
} else
51+
m->events = 0;
52+
53+
return ss;
54+
}
55+
56+
ssize_t dtls_stream_writev(DTLSManager *m, const struct iovec *iov, size_t iovcnt) {
57+
_cleanup_free_ char *buf = NULL;
58+
size_t count;
59+
60+
assert(m);
61+
assert(m->ssl);
62+
assert(iov);
63+
assert(iovec_total_size(iov, iovcnt) > 0);
64+
65+
/* single buffer. Suboptimal, but better than multiple SSL_write calls. */
66+
count = iovec_total_size(iov, iovcnt);
67+
buf = new(char, count);
68+
for (size_t i = 0, pos = 0; i < iovcnt; pos += iov[i].iov_len, i++)
69+
memcpy(buf + pos, iov[i].iov_base, iov[i].iov_len);
70+
71+
return dtls_write(m, buf, count);
72+
}
73+
74+
int dtls_connect(DTLSManager *m, SocketAddress *address) {
75+
_cleanup_(BIO_freep) BIO *bio = NULL;
76+
_cleanup_(SSL_freep) SSL *ssl = NULL;
77+
const SSL_CIPHER *cipher;
78+
union sockaddr_union sa;
79+
socklen_t salen;
80+
SSL_CTX *ctx;
81+
struct timeval timeout = {
82+
.tv_sec = 3,
83+
.tv_usec = 0,
84+
};
85+
int fd, r;
86+
87+
assert(m);
88+
89+
switch (address->sockaddr.sa.sa_family) {
90+
case AF_INET:
91+
sa = (union sockaddr_union) {
92+
.in.sin_family = address->sockaddr.sa.sa_family,
93+
.in.sin_port = address->sockaddr.in.sin_port,
94+
.in.sin_addr = address->sockaddr.in.sin_addr,
95+
};
96+
salen = sizeof(sa.in);
97+
break;
98+
case AF_INET6:
99+
sa = (union sockaddr_union) {
100+
.in6.sin6_family = address->sockaddr.sa.sa_family,
101+
.in6.sin6_port = address->sockaddr.in6.sin6_port,
102+
.in6.sin6_addr = address->sockaddr.in6.sin6_addr,
103+
};
104+
salen = sizeof(sa.in6);
105+
break;
106+
default:
107+
return -EAFNOSUPPORT;
108+
}
109+
110+
fd = socket(AF_INET, SOCK_DGRAM, 0);
111+
if (fd < 0)
112+
return log_error_errno(errno, "Failed to allocate socket: %m");;
113+
114+
r = connect(fd, &address->sockaddr.sa, salen);
115+
if (r < 0 && errno != EINPROGRESS)
116+
return log_error_errno(errno, "Failed to connect dtls socket: %m");;
117+
118+
ctx = SSL_CTX_new(DTLS_method());
119+
if (!ctx)
120+
return log_error_errno(SYNTHETIC_ERRNO(ENOMEM),
121+
"Failed to allocate memory for SSL CTX: %m");
122+
123+
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
124+
SSL_CTX_set_default_verify_paths(ctx);
125+
126+
ssl = SSL_new(ctx);
127+
if (!ssl)
128+
return log_error_errno(SYNTHETIC_ERRNO(ENOMEM),
129+
"Failed to allocate memory for ssl: %s",
130+
ERR_error_string(ERR_get_error(), NULL));
131+
132+
/* Create BIO from socket array! */
133+
bio = BIO_new_dgram(fd, BIO_NOCLOSE);
134+
if (!bio)
135+
return log_error_errno(SYNTHETIC_ERRNO(ENOMEM),
136+
"Failed to allocate memory for bio: %m");
137+
138+
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, &address);
139+
SSL_set_bio(ssl , bio, bio);
140+
141+
r = SSL_connect(ssl);
142+
if (r <= 0)
143+
return log_error_errno(SYNTHETIC_ERRNO(ENOMEM),
144+
"Failed to SSL_connect: %s",
145+
ERR_error_string(ERR_get_error(), NULL));
146+
147+
cipher = SSL_get_current_cipher(ssl);
148+
log_debug("dtls_connect: Cipher Version: %s Name: %s", SSL_CIPHER_get_version(cipher), SSL_CIPHER_get_name(cipher));
149+
150+
/* Set reference in SSL obj */
151+
SSL_set_ex_data(ssl, 0, NULL);
152+
SSL_set_ex_data(ssl, 1, NULL);
153+
154+
/* Set and activate timeouts */
155+
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);
156+
157+
m->bio = TAKE_PTR(bio);
158+
m->ssl = TAKE_PTR(ssl);
159+
m->ctx = ctx;
160+
m->fd = fd;
161+
162+
m->connected = true;
163+
return 0;
164+
}
165+
166+
void dtls_disconnect(DTLSManager *m) {
167+
if (!m)
168+
return;
169+
170+
ERR_clear_error();
171+
172+
if (m->ssl) {
173+
SSL_shutdown(m->ssl);
174+
SSL_free(m->ssl);
175+
m->ssl = NULL;
176+
}
177+
178+
if (m->bio) {
179+
BIO_free(m->bio);
180+
m->bio = NULL;
181+
}
182+
183+
m->fd = safe_close(m->fd);
184+
}
185+
186+
void dtls_manager_free(DTLSManager *m) {
187+
if (!m)
188+
return;
189+
190+
if (m->ctx)
191+
SSL_CTX_free(m->ctx);
192+
193+
free(m);
194+
}
195+
196+
int dtls_manager_init(DTLSManager **ret) {
197+
_cleanup_(dtls_manager_freep) DTLSManager *m = NULL;
198+
199+
m = new0(DTLSManager, 1);
200+
if (!m)
201+
return log_oom();
202+
203+
*ret = TAKE_PTR(m);
204+
return 0;
205+
}

src/netlog/netlog-dtls.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
#pragma once
3+
4+
#include <openssl/ssl.h>
5+
#include <openssl/bio.h>
6+
#include <stdbool.h>
7+
8+
#include "socket-util.h"
9+
10+
typedef struct DTLSManager DTLSManager;
11+
12+
struct DTLSManager {
13+
SSL_SESSION *session;
14+
SSL_CTX *ctx;
15+
BIO *bio;
16+
SSL *ssl;
17+
18+
uint32_t events;
19+
int fd;
20+
21+
bool shutdown;
22+
bool connected;
23+
24+
BUF_MEM *write_buffer;
25+
size_t buffer_offset;
26+
};
27+
28+
void dtls_manager_free(DTLSManager *m);
29+
int dtls_manager_init(DTLSManager **m);
30+
31+
int dtls_connect(DTLSManager *m, SocketAddress *addr);
32+
void dtls_disconnect(DTLSManager *m);
33+
34+
ssize_t dtls_stream_writev(DTLSManager *m, const struct iovec *iov, size_t iovcnt);
35+
36+
DEFINE_TRIVIAL_CLEANUP_FUNC(DTLSManager*, dtls_manager_free);
37+
38+
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL);
39+
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL);

src/netlog/netlog-manager.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828
for (sd_journal_restart_data(j); ((retval) = sd_journal_enumerate_data((j), &(data), &(l))) > 0; )
2929

3030
static const char *const protocol_table[_SYSLOG_TRANSMISSION_PROTOCOL_MAX] = {
31-
[SYSLOG_TRANSMISSION_PROTOCOL_UDP] = "udp",
32-
[SYSLOG_TRANSMISSION_PROTOCOL_TCP] = "tcp",
31+
[SYSLOG_TRANSMISSION_PROTOCOL_UDP] = "udp",
32+
[SYSLOG_TRANSMISSION_PROTOCOL_TCP] = "tcp",
33+
[SYSLOG_TRANSMISSION_PROTOCOL_DTLS] = "dtls",
3334
};
3435

3536
DEFINE_STRING_TABLE_LOOKUP(protocol, int);
@@ -387,9 +388,15 @@ int manager_connect(Manager *m) {
387388

388389
manager_disconnect(m);
389390

390-
r = manager_open_network_socket(m);
391-
if (r < 0)
392-
return log_error_errno(r, "Failed to create network socket: %m");
391+
if (m->protocol == SYSLOG_TRANSMISSION_PROTOCOL_DTLS) {
392+
r = dtls_connect(m->dtls, &m->address);
393+
if (r < 0)
394+
return r;
395+
} else {
396+
r = manager_open_network_socket(m);
397+
if (r < 0)
398+
return log_error_errno(r, "Failed to create network socket: %m");
399+
}
393400

394401
r = manager_journal_monitor_listen(m);
395402
if (r < 0)
@@ -404,6 +411,7 @@ void manager_disconnect(Manager *m) {
404411
close_journal_input(m);
405412

406413
manager_close_network_socket(m);
414+
dtls_disconnect(m->dtls);
407415

408416
m->event_journal_input = sd_event_source_unref(m->event_journal_input);
409417

src/netlog/netlog-manager.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
#include "sd-network.h"
88
#include "socket-util.h"
9-
#include "netlog-tls.h"
9+
#include "netlog-dtls.h"
1010

1111
typedef enum SysLogTransmissionProtocol {
1212
SYSLOG_TRANSMISSION_PROTOCOL_UDP = 1 << 0,
@@ -61,7 +61,7 @@ struct Manager {
6161
bool syslog_msgid;
6262
bool encrypt;
6363

64-
TLSManager *tls;
64+
DTLSManager *dtls;
6565
};
6666

6767
int manager_new(const char *state_file, const char *cursor, Manager **ret);

src/netlog/netlog-network.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ static int format_rfc5424(Manager *m,
168168
if (m->protocol == SYSLOG_TRANSMISSION_PROTOCOL_TCP)
169169
IOVEC_SET_STRING(iov[n++], "\n");
170170

171-
return network_send(m, iov, n);
171+
172+
if (m->protocol == SYSLOG_TRANSMISSION_PROTOCOL_DTLS)
173+
return dtls_stream_writev(m->dtls, iov, n);
174+
else
175+
return network_send(m, iov, n);
172176
}
173177

174178
static int format_rfc3339(Manager *m,
@@ -236,7 +240,10 @@ static int format_rfc3339(Manager *m,
236240
if (m->protocol == SYSLOG_TRANSMISSION_PROTOCOL_TCP)
237241
IOVEC_SET_STRING(iov[n++], "\n");
238242

239-
return network_send(m, iov, n);
243+
if (m->protocol == SYSLOG_TRANSMISSION_PROTOCOL_DTLS)
244+
return dtls_stream_writev(m->dtls, iov, n);
245+
else
246+
return network_send(m, iov, n);
240247
}
241248

242249
int manager_push_to_network(Manager *m,
@@ -305,7 +312,7 @@ int manager_network_connect_socket(Manager *m) {
305312
salen = sizeof(sa.in6);
306313
break;
307314
default:
308-
return EAFNOSUPPORT;
315+
return -EAFNOSUPPORT;
309316
}
310317

311318
r = connect(m->socket, &m->address.sockaddr.sa, salen);

src/netlog/systemd-netlogd.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ int main(int argc, char **argv) {
171171
goto finish;
172172
}
173173

174+
if (m->protocol == SYSLOG_TRANSMISSION_PROTOCOL_DTLS) {
175+
r = dtls_manager_init(&m->dtls);
176+
if (r < 0)
177+
return r;
178+
}
179+
174180
r = setup_cursor_state_file(m, uid, gid);
175181
if (r < 0)
176182
goto cleanup;

0 commit comments

Comments
 (0)