-
-
Notifications
You must be signed in to change notification settings - Fork 38
/
nts.c
422 lines (366 loc) · 10.9 KB
/
nts.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
/*
* nts.c - Network Time Security (NTS) support
* Copyright the NTPsec project contributors
* SPDX-License-Identifier: BSD-2-Clause
*
* Section references are to
* https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-15
*
* This module exposes mostly functions and structure pointers (not
* structures) so that the NTS implementation can be sealed off from
* the rest of the code. It supports both the client and server sides.
*
* The exception is client configuration, for which various bits have
* to be set by the config parser.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/err.h>
#include "ntp_types.h"
#include "ntp_stdlib.h"
#include "ntpd.h"
#include "nts.h"
#include "nts2.h"
struct ntsconfig_t ntsconfig = {
.ntsenable = false,
.mintls = NULL,
.maxtls = NULL,
.tlsciphersuites = NULL,
.tlsecdhcurves = NULL,
.cert = NULL,
.key = NULL,
.KI = NULL,
.ca = NULL,
.aead = NULL
};
void nts_log_version(void);
/*****************************************************/
/* More SSL initialization in ssl_init() from libntp/ssl_init.c */
void nts_init(void) {
bool ok = true;
nts_log_version();
if (ntsconfig.ntsenable) {
ok &= nts_server_init();
}
ok &= nts_client_init();
ok &= nts_cookie_init();
ok &= extens_init();
if (!ok) {
msyslog(LOG_ERR, "NTS: troubles during init. Bailing.");
exit(1);
}
}
void nts_init2(void) {
bool ok = true;
if (ntsconfig.ntsenable) {
ok &= nts_server_init2();
ok &= nts_cookie_init2();
}
if (!ok) {
msyslog(LOG_ERR, "NTS: troubles during init2. Bailing.");
exit(1);
}
}
/* There are 2 cases:
* 1: match, log version
* 2: mismatch, log both build and run
*/
void nts_log_version(void) {
unsigned long buildVersion = OPENSSL_VERSION_NUMBER;
const char * text = OpenSSL_version(OPENSSL_VERSION);
bool match = (buildVersion == OpenSSL_version_num()) &&
(0 == strcmp(OPENSSL_VERSION_TEXT, text));
if (match) {
/* Case 1 */
msyslog(LOG_INFO, "INIT: %s, %lx",
OPENSSL_VERSION_TEXT, buildVersion);
} else {
/* Case 2 */
msyslog(LOG_INFO, "INIT: Built with %s, %lx",
OPENSSL_VERSION_TEXT, buildVersion);
msyslog(LOG_INFO, "INIT: Running with %s, %lx",
OpenSSL_version(OPENSSL_VERSION),
OpenSSL_version_num());
if (buildVersion > OpenSSL_version_num()) {
msyslog(LOG_ERR, "INIT: Old OpenSSL library, bailing");
exit(1);
}
}
/*
* If the runtime OpenSSL is 1.1.1a, then bail, since we'll run into errors with the
* TLSv1.3 maximum label length
*/
if (OpenSSL_version_num() == 0x1010101fL) {
msyslog(LOG_ERR, "INIT: OpenSSL 1.1.1a has a maximum label length bug, bailing");
exit(1);
}
}
/*****************************************************/
void nts_timer(void) {
nts_cert_timer();
nts_cookie_timer();
}
/*****************************************************/
/* 0 is default, -1 is error */
int nts_translate_version(const char *arg) {
if (NULL == arg) {
return 0;
}
if (0 == strcmp(arg, "TLS1.3")) {
return TLS1_3_VERSION;
}
msyslog(LOG_ERR, "NTS: TLS unrecognized version string: %s.", arg);
return -1;
}
/* Translate text to AEAD code. NO_AEAD for none/error */
uint16_t nts_string_to_aead(const char* text) {
if (false) {
} else if (0 == strcmp(text, "AES_SIV_CMAC_256")) {
return AEAD_AES_SIV_CMAC_256;
} else if (0 == strcmp(text, "AES_SIV_CMAC_384")) {
return AEAD_AES_SIV_CMAC_384;
} else if (0 == strcmp(text, "AES_SIV_CMAC_512")) {
return AEAD_AES_SIV_CMAC_512;
} else {
return NO_AEAD;
}
}
/* returns key length, 0 if unknown arg */
int nts_get_key_length(uint16_t aead) {
switch (aead) {
case AEAD_AES_SIV_CMAC_256:
return AEAD_AES_SIV_CMAC_256_KEYLEN;
case AEAD_AES_SIV_CMAC_384:
return AEAD_AES_SIV_CMAC_384_KEYLEN;
case AEAD_AES_SIV_CMAC_512:
return AEAD_AES_SIV_CMAC_512_KEYLEN;
default:
return 0;
}
}
/*****************************************************/
bool nts_load_versions(SSL_CTX *ctx) {
int minver, maxver;
minver = nts_translate_version(ntsconfig.mintls);
maxver = nts_translate_version(ntsconfig.maxtls);
if ((-1 == minver) || (-1 == maxver))
return false;
if(0 == minver) minver = TLS1_3_VERSION; // 3.
SSL_CTX_set_min_proto_version(ctx, minver);
SSL_CTX_set_max_proto_version(ctx, maxver);
return true;
}
bool nts_load_ciphers(SSL_CTX *ctx) {
/* SSL set_ciphers(uites) ignores typos or ciphers it doesn't support.
* There is no SSL_CTX_get_cipher_list, so we can't easily read back
* the ciphers to see what it took.
* We could make a dummy SSL, read the list, then free it.
*/
if (NULL != ntsconfig.tlsciphersuites) {
if (1 != SSL_CTX_set_ciphersuites(ctx, ntsconfig.tlsciphersuites)) {
msyslog(LOG_ERR, "NTS: troubles setting ciphersuites.");
return false;
} else {
msyslog(LOG_INFO, "NTS: set ciphersuites.");
}
}
return true;
}
bool nts_load_ecdhcurves(SSL_CTX *ctx) {
/* SSL_CTX_set1_groups_list ignores typos or curves it doesn't support.
* There is no SSL_CTX_get_curves_list, so we can't easily read back
* the ecdhcurves to see what it took.
* We could make a dummy SSL, read the list, then free it.
*/
if (NULL != ntsconfig.tlsecdhcurves) {
if (1 != SSL_CTX_set1_groups_list(ctx, ntsconfig.tlsecdhcurves)) {
msyslog(LOG_ERR, "NTS: troubles setting ecdhcurves.");
return false;
} else {
msyslog(LOG_INFO, "NTS: set ecdhcurves.");
}
}
return true;
}
static struct stat certfile_stat;
void nts_reload_certificate(SSL_CTX *ctx) {
struct stat temp_stat;
const char *cert = NTS_CERT_FILE;
if (NULL != ntsconfig.cert)
cert = ntsconfig.cert;
if (0 != stat(cert, &temp_stat)) {
return;
}
if ((certfile_stat.st_mtime == temp_stat.st_mtime)
&& (certfile_stat.st_ctime == temp_stat.st_ctime)) {
return; /* avoid clutter in log file */
}
nts_load_certificate(ctx);
}
bool nts_load_certificate(SSL_CTX *ctx) {
const char *cert = NTS_CERT_FILE;
const char *key = NTS_KEY_FILE;
char errbuf[100];
if (NULL != ntsconfig.cert)
cert = ntsconfig.cert;
if (NULL != ntsconfig.key)
key = ntsconfig.key;
/* for reload checking */
if (0 != stat(cert, &certfile_stat)) {
ntp_strerror_r(errno, errbuf, sizeof(errbuf));
msyslog(LOG_ERR, "NTSs: can't stat certificate (chain) from %s: %s", cert, errbuf);
return false;
}
if (1 != SSL_CTX_use_certificate_chain_file(ctx, cert)) {
msyslog(LOG_ERR, "NTSs: can't load certificate (chain) from %s", cert);
nts_log_ssl_error();
return false;
} else {
msyslog(LOG_ERR, "NTSs: loaded certificate (chain) from %s", cert);
}
if (1 != SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
msyslog(LOG_ERR, "NTSs: can't load private key from %s", key);
nts_log_ssl_error();
return false;
} else {
msyslog(LOG_ERR, "NTSs: loaded private key from %s", key);
}
if (1 != SSL_CTX_check_private_key(ctx)) {
msyslog(LOG_ERR, "NTSs: Private Key doesn't work ******");
return false;
} else {
msyslog(LOG_INFO, "NTSs: Private Key OK");
}
return true;
}
int nts_ssl_read(SSL *ssl, uint8_t *buff, int buff_length) {
int bytes_read;
char errbuf[100];
bytes_read = SSL_read(ssl, buff, buff_length);
if (0 >= bytes_read) {
ntp_strerror_r(errno, errbuf, sizeof(errbuf));
msyslog(LOG_INFO, "NTS: SSL_read error: %s", errbuf);
nts_log_ssl_error();
return -1;
}
return bytes_read;
}
int nts_ssl_write(SSL *ssl, uint8_t *buff, int buff_length) {
int bytes_written;
char errbuf[100];
bytes_written = SSL_write(ssl, buff, buff_length);
if (0 >= bytes_written) {
ntp_strerror_r(errno, errbuf, sizeof(errbuf));
msyslog(LOG_INFO, "NTS: SSL_write error: %s", errbuf);
nts_log_ssl_error();
return -1;
}
return bytes_written;
}
void nts_log_ssl_error(void) {
char buff[256];
int err = ERR_get_error();
SSL_load_error_strings(); /* Needed on NetBSD */
while (0 != err) {
ERR_error_string_n(err, buff, sizeof(buff));
msyslog(LOG_INFO, "NTS: %s", buff);
err = ERR_get_error();
}
}
/*****************************************************/
/* NB: KE length is body length, Extension length includes header. */
/* Troubles with signed/unsigned compares when using sizeof() */
void ke_append_record_null(BufCtl* buf, uint16_t type) {
append_header(buf, type, 0);
}
void ke_append_record_uint16(BufCtl* buf, uint16_t type, uint16_t data) {
if (NTS_KE_HDR_LNG+NTS_KE_U16_LNG > buf->left)
return;
append_header(buf, type, NTS_KE_U16_LNG);
append_uint16(buf, data);
}
void ke_append_record_bytes(BufCtl* buf, uint16_t type, uint8_t *data, int length) {
if (NTS_KE_HDR_LNG+length > buf->left)
return;
append_header(buf, type, length);
append_bytes(buf, data, length);
}
void ex_append_record_null(BufCtl* buf, uint16_t type) {
append_header(buf, type, NTS_KE_HDR_LNG);
}
void ex_append_record_uint16(BufCtl* buf, uint16_t type, uint16_t data) {
if (NTS_KE_HDR_LNG+NTS_KE_U16_LNG > buf->left)
return;
append_header(buf, type, NTS_KE_HDR_LNG+NTS_KE_U16_LNG);
append_uint16(buf, data);
}
void ex_append_record_bytes(BufCtl* buf, uint16_t type, uint8_t *data, int length) {
if (NTS_KE_HDR_LNG+length > buf->left)
return;
append_header(buf, type, NTS_KE_HDR_LNG+length);
append_bytes(buf, data, length);
}
void ex_append_header(BufCtl* buf, uint16_t type, uint16_t length) {
append_header(buf, type, NTS_KE_HDR_LNG+length);
}
void append_header(BufCtl* buf, uint16_t type, uint16_t length) {
uint16_t * ptr = (uint16_t *)buf->next;
if (NTS_KE_HDR_LNG > buf->left)
return;
*ptr++ = htons(type);
*ptr++ = htons(length);
buf->next += NTS_KE_HDR_LNG;
buf->left -= NTS_KE_HDR_LNG;
/* leaves buf pointing to where data will go */
}
void append_uint16(BufCtl* buf, uint16_t data) {
uint16_t * ptr = (uint16_t *)buf->next;
if (NTS_KE_U16_LNG > buf->left)
return;
*ptr++ = htons(data);
buf->next += NTS_KE_U16_LNG;
buf->left -= NTS_KE_U16_LNG;
}
void append_bytes(BufCtl* buf, uint8_t *data, int length) {
if (length > buf->left)
return;
memcpy(buf->next, data, length);
buf->next += length;
buf->left -= length;
}
/* Reads type and length of the next record, and moves cursor to the data */
uint16_t ke_next_record(BufCtl* buf, int *length) {
uint16_t *ptr = (uint16_t *)buf->next;
uint16_t type = ntohs(*ptr++);
*length = ntohs(*ptr++);
buf->next += NTS_KE_HDR_LNG;
buf->left -= NTS_KE_HDR_LNG;
return type;
}
uint16_t ex_next_record(BufCtl* buf, int *length) {
uint16_t *ptr = (uint16_t *)buf->next;
uint16_t type = ntohs(*ptr++);
*length = ntohs(*ptr++)-NTS_KE_HDR_LNG;
buf->next += NTS_KE_HDR_LNG;
buf->left -= NTS_KE_HDR_LNG;
return type;
}
/* Reads a uint16 from the record and advances to the next data */
uint16_t next_uint16(BufCtl* buf) {
uint16_t *ptr = (uint16_t *)buf->next;
uint16_t data = ntohs(*ptr++);
buf->next += NTS_KE_U16_LNG;
buf->left -= NTS_KE_U16_LNG;
return data;
}
/* Reads a string of bytes from the record and advances to the next data */
uint16_t next_bytes(BufCtl* buf, uint8_t *data, int length) {
memcpy(data, buf->next, length);
buf->next += length;
buf->left -= length;
return length;
}
/* end */