49
117
#include <avahi-common/malloc.h>
50
118
#include <avahi-common/error.h>
53
#include <sys/types.h> /* socket(), inet_pton() */
54
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
55
struct in6_addr, inet_pton() */
56
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
57
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
59
#include <unistd.h> /* close() */
60
#include <netinet/in.h>
61
#include <stdbool.h> /* true */
62
#include <string.h> /* memset */
63
#include <arpa/inet.h> /* inet_pton() */
64
#include <iso646.h> /* not */
67
#include <errno.h> /* perror() */
121
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
124
init_gnutls_session(),
126
#if GNUTLS_VERSION_NUMBER < 0x030600
127
#include <gnutls/openpgp.h>
128
/* gnutls_certificate_set_openpgp_key_file(),
129
GNUTLS_OPENPGP_FMT_BASE64 */
130
#elif GNUTLS_VERSION_NUMBER >= 0x030606
131
#include <gnutls/x509.h> /* gnutls_pkcs_encrypt_flags_t,
133
GNUTLS_PKCS_NULL_PASSWORD */
137
#include <gpgme.h> /* All GPGME types, constants and
140
GPGME_PROTOCOL_OpenPGP,
73
143
#define BUFFER_SIZE 256
76
static const char *certdir = "/conf/conf.d/mandos";
77
static const char *certfile = "openpgp-client.txt";
78
static const char *certkey = "openpgp-client-key.txt";
145
#define PATHDIR "/conf/conf.d/mandos"
146
#define SECKEY "seckey.txt"
147
#define PUBKEY "pubkey.txt"
148
#define TLS_PRIVKEY "tls-privkey.pem"
149
#define TLS_PUBKEY "tls-pubkey.pem"
150
#define HOOKDIR "/lib/mandos/network-hooks.d"
80
152
bool debug = false;
82
const char mandos_protocol_version[] = "1";
153
static const char mandos_protocol_version[] = "1";
154
const char *argp_program_version = "mandos-client " VERSION;
155
const char *argp_program_bug_address = "<mandos@recompile.se>";
156
static const char sys_class_net[] = "/sys/class/net";
157
char *connect_to = NULL;
158
const char *hookdir = HOOKDIR;
163
/* Doubly linked list that need to be circularly linked when used */
164
typedef struct server{
167
AvahiIfIndex if_index;
169
struct timespec last_seen;
174
/* Used for passing in values through the Avahi callback functions */
85
AvahiSimplePoll *simple_poll;
86
176
AvahiServer *server;
87
177
gnutls_certificate_credentials_t cred;
88
178
unsigned int dh_bits;
179
gnutls_dh_params_t dh_params;
89
180
const char *priority;
182
server *current_server;
184
size_t interfaces_size;
92
size_t adjustbuffer(char *buffer, size_t buffer_length,
93
size_t buffer_capacity){
94
if (buffer_length + BUFFER_SIZE > buffer_capacity){
95
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
187
/* global so signal handler can reach it*/
188
AvahiSimplePoll *simple_poll;
190
sig_atomic_t quit_now = 0;
191
int signal_received = 0;
193
/* Function to use when printing errors */
194
void perror_plus(const char *print_text){
196
fprintf(stderr, "Mandos plugin %s: ",
197
program_invocation_short_name);
202
__attribute__((format (gnu_printf, 2, 3), nonnull))
203
int fprintf_plus(FILE *stream, const char *format, ...){
205
va_start (ap, format);
207
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
208
program_invocation_short_name));
209
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
213
* Make additional room in "buffer" for at least BUFFER_SIZE more
214
* bytes. "buffer_capacity" is how much is currently allocated,
215
* "buffer_length" is how much is already used.
217
__attribute__((nonnull, warn_unused_result))
218
size_t incbuffer(char **buffer, size_t buffer_length,
219
size_t buffer_capacity){
220
if(buffer_length + BUFFER_SIZE > buffer_capacity){
221
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
223
int old_errno = errno;
99
230
buffer_capacity += BUFFER_SIZE;
101
232
return buffer_capacity;
104
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
106
const char *homedir){
107
gpgme_data_t dh_crypto, dh_plain;
235
/* Add server to set of servers to retry periodically */
236
__attribute__((nonnull, warn_unused_result))
237
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
238
int af, server **current_server){
240
server *new_server = malloc(sizeof(server));
241
if(new_server == NULL){
242
perror_plus("malloc");
245
*new_server = (server){ .ip = strdup(ip),
247
.if_index = if_index,
249
if(new_server->ip == NULL){
250
perror_plus("strdup");
254
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
256
perror_plus("clock_gettime");
258
#pragma GCC diagnostic push
259
#pragma GCC diagnostic ignored "-Wcast-qual"
261
free((char *)(new_server->ip));
263
#pragma GCC diagnostic pop
268
/* Special case of first server */
269
if(*current_server == NULL){
270
new_server->next = new_server;
271
new_server->prev = new_server;
272
*current_server = new_server;
274
/* Place the new server last in the list */
275
new_server->next = *current_server;
276
new_server->prev = (*current_server)->prev;
277
new_server->prev->next = new_server;
278
(*current_server)->prev = new_server;
283
/* Set effective uid to 0, return errno */
284
__attribute__((warn_unused_result))
285
int raise_privileges(void){
286
int old_errno = errno;
288
if(seteuid(0) == -1){
295
/* Set effective and real user ID to 0. Return errno. */
296
__attribute__((warn_unused_result))
297
int raise_privileges_permanently(void){
298
int old_errno = errno;
299
int ret = raise_privileges();
311
/* Set effective user ID to unprivileged saved user ID */
312
__attribute__((warn_unused_result))
313
int lower_privileges(void){
314
int old_errno = errno;
316
if(seteuid(uid) == -1){
323
/* Lower privileges permanently */
324
__attribute__((warn_unused_result))
325
int lower_privileges_permanently(void){
326
int old_errno = errno;
328
if(setuid(uid) == -1){
338
__attribute__((nonnull, warn_unused_result))
339
static bool init_gpgme(const char * const seckey,
340
const char * const pubkey,
341
const char * const tempdir,
109
343
gpgme_error_t rc;
111
size_t new_packet_capacity = 0;
112
ssize_t new_packet_length = 0;
113
344
gpgme_engine_info_t engine_info;
347
* Helper function to insert pub and seckey to the engine keyring.
349
bool import_key(const char * const filename){
352
gpgme_data_t pgp_data;
354
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
360
/* Workaround for systems without a real-time clock; see also
361
Debian bug #894495: <https://bugs.debian.org/894495> */
364
time_t currtime = time(NULL);
365
if(currtime != (time_t)-1){
367
if(gmtime_r(&currtime, &tm) == NULL) {
368
perror_plus("gmtime_r");
371
if(tm.tm_year != 70 or tm.tm_mon != 0){
375
fprintf_plus(stderr, "System clock is January 1970");
379
fprintf_plus(stderr, "System clock is invalid");
384
ret = fstat(fd, &keystat);
386
perror_plus("fstat");
389
ret = raise_privileges();
392
perror_plus("Failed to raise privileges");
397
"Setting system clock to key file mtime");
399
time_t keytime = keystat.st_mtim.tv_sec;
400
if(stime(&keytime) != 0){
401
perror_plus("stime");
403
ret = lower_privileges();
406
perror_plus("Failed to lower privileges");
116
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
410
rc = gpgme_data_new_from_fd(&pgp_data, fd);
411
if(rc != GPG_ERR_NO_ERROR){
412
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
413
gpgme_strsource(rc), gpgme_strerror(rc));
417
rc = gpgme_op_import(mc->ctx, pgp_data);
418
if(rc != GPG_ERR_NO_ERROR){
419
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
420
gpgme_strsource(rc), gpgme_strerror(rc));
424
gpgme_import_result_t import_result
425
= gpgme_op_import_result(mc->ctx);
426
if((import_result->imported < 1
427
or import_result->not_imported > 0)
428
and import_result->unchanged == 0){
429
fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
431
"The total number of considered keys: %d\n",
432
import_result->considered);
434
"The number of keys without user ID: %d\n",
435
import_result->no_user_id);
437
"The total number of imported keys: %d\n",
438
import_result->imported);
439
fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
440
import_result->imported_rsa);
441
fprintf_plus(stderr, "The number of unchanged keys: %d\n",
442
import_result->unchanged);
443
fprintf_plus(stderr, "The number of new user IDs: %d\n",
444
import_result->new_user_ids);
445
fprintf_plus(stderr, "The number of new sub keys: %d\n",
446
import_result->new_sub_keys);
447
fprintf_plus(stderr, "The number of new signatures: %d\n",
448
import_result->new_signatures);
449
fprintf_plus(stderr, "The number of new revocations: %d\n",
450
import_result->new_revocations);
452
"The total number of secret keys read: %d\n",
453
import_result->secret_read);
455
"The number of imported secret keys: %d\n",
456
import_result->secret_imported);
458
"The number of unchanged secret keys: %d\n",
459
import_result->secret_unchanged);
460
fprintf_plus(stderr, "The number of keys not imported: %d\n",
461
import_result->not_imported);
462
for(gpgme_import_status_t import_status
463
= import_result->imports;
464
import_status != NULL;
465
import_status = import_status->next){
466
fprintf_plus(stderr, "Import status for key: %s\n",
468
if(import_status->result != GPG_ERR_NO_ERROR){
469
fprintf_plus(stderr, "Import result: %s: %s\n",
470
gpgme_strsource(import_status->result),
471
gpgme_strerror(import_status->result));
473
fprintf_plus(stderr, "Key status:\n");
475
import_status->status & GPGME_IMPORT_NEW
476
? "The key was new.\n"
477
: "The key was not new.\n");
479
import_status->status & GPGME_IMPORT_UID
480
? "The key contained new user IDs.\n"
481
: "The key did not contain new user IDs.\n");
483
import_status->status & GPGME_IMPORT_SIG
484
? "The key contained new signatures.\n"
485
: "The key did not contain new signatures.\n");
487
import_status->status & GPGME_IMPORT_SUBKEY
488
? "The key contained new sub keys.\n"
489
: "The key did not contain new sub keys.\n");
491
import_status->status & GPGME_IMPORT_SECRET
492
? "The key contained a secret key.\n"
493
: "The key did not contain a secret key.\n");
501
perror_plus("close");
503
gpgme_data_release(pgp_data);
508
fprintf_plus(stderr, "Initializing GPGME\n");
120
512
gpgme_check_version(NULL);
121
513
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
122
if (rc != GPG_ERR_NO_ERROR){
123
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
124
gpgme_strsource(rc), gpgme_strerror(rc));
514
if(rc != GPG_ERR_NO_ERROR){
515
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
516
gpgme_strsource(rc), gpgme_strerror(rc));
128
/* Set GPGME home directory */
129
rc = gpgme_get_engine_info (&engine_info);
130
if (rc != GPG_ERR_NO_ERROR){
131
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
132
gpgme_strsource(rc), gpgme_strerror(rc));
520
/* Set GPGME home directory for the OpenPGP engine only */
521
rc = gpgme_get_engine_info(&engine_info);
522
if(rc != GPG_ERR_NO_ERROR){
523
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
524
gpgme_strsource(rc), gpgme_strerror(rc));
135
527
while(engine_info != NULL){
136
528
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
137
529
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
138
engine_info->file_name, homedir);
530
engine_info->file_name, tempdir);
141
533
engine_info = engine_info->next;
143
535
if(engine_info == NULL){
144
fprintf(stderr, "Could not set home dir to %s\n", homedir);
148
/* Create new GPGME data buffer from packet buffer */
149
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
150
if (rc != GPG_ERR_NO_ERROR){
151
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
152
gpgme_strsource(rc), gpgme_strerror(rc));
536
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
541
/* Create new GPGME "context" */
542
rc = gpgme_new(&(mc->ctx));
543
if(rc != GPG_ERR_NO_ERROR){
544
fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
545
gpgme_strsource(rc), gpgme_strerror(rc));
549
if(not import_key(pubkey) or not import_key(seckey)){
557
* Decrypt OpenPGP data.
558
* Returns -1 on error
560
__attribute__((nonnull, warn_unused_result))
561
static ssize_t pgp_packet_decrypt(const char *cryptotext,
565
gpgme_data_t dh_crypto, dh_plain;
568
size_t plaintext_capacity = 0;
569
ssize_t plaintext_length = 0;
572
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
575
/* Create new GPGME data buffer from memory cryptotext */
576
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
578
if(rc != GPG_ERR_NO_ERROR){
579
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
580
gpgme_strsource(rc), gpgme_strerror(rc));
156
584
/* Create new empty GPGME data buffer for the plaintext */
157
585
rc = gpgme_data_new(&dh_plain);
158
if (rc != GPG_ERR_NO_ERROR){
159
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
160
gpgme_strsource(rc), gpgme_strerror(rc));
164
/* Create new GPGME "context" */
165
rc = gpgme_new(&ctx);
166
if (rc != GPG_ERR_NO_ERROR){
167
fprintf(stderr, "bad gpgme_new: %s: %s\n",
168
gpgme_strsource(rc), gpgme_strerror(rc));
172
/* Decrypt data from the FILE pointer to the plaintext data
174
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
175
if (rc != GPG_ERR_NO_ERROR){
176
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
177
gpgme_strsource(rc), gpgme_strerror(rc));
586
if(rc != GPG_ERR_NO_ERROR){
587
fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
588
gpgme_strsource(rc), gpgme_strerror(rc));
589
gpgme_data_release(dh_crypto);
593
/* Decrypt data from the cryptotext data buffer to the plaintext
595
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
596
if(rc != GPG_ERR_NO_ERROR){
597
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
598
gpgme_strsource(rc), gpgme_strerror(rc));
599
plaintext_length = -1;
601
gpgme_decrypt_result_t result;
602
result = gpgme_op_decrypt_result(mc->ctx);
604
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
606
if(result->unsupported_algorithm != NULL) {
607
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
608
result->unsupported_algorithm);
610
fprintf_plus(stderr, "Wrong key usage: %s\n",
611
result->wrong_key_usage ? "Yes" : "No");
612
if(result->file_name != NULL){
613
fprintf_plus(stderr, "File name: %s\n", result->file_name);
616
for(gpgme_recipient_t r = result->recipients; r != NULL;
618
fprintf_plus(stderr, "Public key algorithm: %s\n",
619
gpgme_pubkey_algo_name(r->pubkey_algo));
620
fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
621
fprintf_plus(stderr, "Secret key available: %s\n",
622
r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
182
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
186
gpgme_decrypt_result_t result;
187
result = gpgme_op_decrypt_result(ctx);
189
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
191
fprintf(stderr, "Unsupported algorithm: %s\n",
192
result->unsupported_algorithm);
193
fprintf(stderr, "Wrong key usage: %d\n",
194
result->wrong_key_usage);
195
if(result->file_name != NULL){
196
fprintf(stderr, "File name: %s\n", result->file_name);
198
gpgme_recipient_t recipient;
199
recipient = result->recipients;
201
while(recipient != NULL){
202
fprintf(stderr, "Public key algorithm: %s\n",
203
gpgme_pubkey_algo_name(recipient->pubkey_algo));
204
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
205
fprintf(stderr, "Secret key available: %s\n",
206
recipient->status == GPG_ERR_NO_SECKEY
208
recipient = recipient->next;
214
/* Delete the GPGME FILE pointer cryptotext data buffer */
215
gpgme_data_release(dh_crypto);
630
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
217
633
/* Seek back to the beginning of the GPGME plaintext data buffer */
218
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
219
perror("pgpme_data_seek");
634
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
635
perror_plus("gpgme_data_seek");
636
plaintext_length = -1;
224
new_packet_capacity = adjustbuffer(*new_packet, new_packet_length,
225
new_packet_capacity);
226
if (new_packet_capacity == 0){
227
perror("adjustbuffer");
230
new_packet_capacity += BUFFER_SIZE;
642
plaintext_capacity = incbuffer(plaintext,
643
(size_t)plaintext_length,
645
if(plaintext_capacity == 0){
646
perror_plus("incbuffer");
647
plaintext_length = -1;
233
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
651
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
235
653
/* Print the data, if any */
240
perror("gpgme_data_read");
243
new_packet_length += ret;
246
/* FIXME: check characters before printing to screen so to not print
247
terminal control characters */
249
/* fprintf(stderr, "decrypted password is: "); */
250
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
251
/* fprintf(stderr, "\n"); */
659
perror_plus("gpgme_data_read");
660
plaintext_length = -1;
663
plaintext_length += ret;
667
fprintf_plus(stderr, "Decrypted password is: ");
668
for(ssize_t i = 0; i < plaintext_length; i++){
669
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
671
fprintf(stderr, "\n");
676
/* Delete the GPGME cryptotext data buffer */
677
gpgme_data_release(dh_crypto);
254
679
/* Delete the GPGME plaintext data buffer */
255
680
gpgme_data_release(dh_plain);
256
return new_packet_length;
259
static const char * safer_gnutls_strerror (int value) {
260
const char *ret = gnutls_strerror (value);
681
return plaintext_length;
684
__attribute__((warn_unused_result, const))
685
static const char *safe_string(const char *str){
691
__attribute__((warn_unused_result))
692
static const char *safer_gnutls_strerror(int value){
693
const char *ret = gnutls_strerror(value);
694
return safe_string(ret);
697
/* GnuTLS log function callback */
698
__attribute__((nonnull))
266
699
static void debuggnutls(__attribute__((unused)) int level,
267
700
const char* string){
268
fprintf(stderr, "%s", string);
701
fprintf_plus(stderr, "GnuTLS: %s", string);
271
static int initgnutls(mandos_context *mc){
704
__attribute__((nonnull(1, 2, 4), warn_unused_result))
705
static int init_gnutls_global(const char *pubkeyfilename,
706
const char *seckeyfilename,
707
const char *dhparamsfilename,
276
fprintf(stderr, "Initializing GnuTLS\n");
279
if ((ret = gnutls_global_init ())
280
!= GNUTLS_E_SUCCESS) {
281
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
712
fprintf_plus(stderr, "Initializing GnuTLS\n");
716
/* "Use a log level over 10 to enable all debugging options."
286
719
gnutls_global_set_log_level(11);
287
720
gnutls_global_set_log_function(debuggnutls);
290
/* openpgp credentials */
291
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
292
!= GNUTLS_E_SUCCESS) {
293
fprintf (stderr, "memory error: %s\n",
294
safer_gnutls_strerror(ret));
723
/* OpenPGP credentials */
724
ret = gnutls_certificate_allocate_credentials(&mc->cred);
725
if(ret != GNUTLS_E_SUCCESS){
726
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
727
safer_gnutls_strerror(ret));
299
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
300
" and keyfile %s as GnuTLS credentials\n", certfile,
732
fprintf_plus(stderr, "Attempting to use public key %s and"
733
" private key %s as GnuTLS credentials\n",
738
#if GNUTLS_VERSION_NUMBER >= 0x030606
739
ret = gnutls_certificate_set_rawpk_key_file
740
(mc->cred, pubkeyfilename, seckeyfilename,
741
GNUTLS_X509_FMT_PEM, /* format */
744
GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
746
0, /* names_length */
748
GNUTLS_PKCS_PLAIN | GNUTLS_PKCS_NULL_PASSWORD,
749
0); /* pkcs11_flags */
750
#elif GNUTLS_VERSION_NUMBER < 0x030600
304
751
ret = gnutls_certificate_set_openpgp_key_file
305
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
306
if (ret != GNUTLS_E_SUCCESS) {
308
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
310
ret, certfile, certkey);
311
fprintf(stdout, "The Error is: %s\n",
312
safer_gnutls_strerror(ret));
316
//GnuTLS server initialization
317
if ((ret = gnutls_dh_params_init (&es->dh_params))
318
!= GNUTLS_E_SUCCESS) {
319
fprintf (stderr, "Error in dh parameter initialization: %s\n",
320
safer_gnutls_strerror(ret));
324
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
325
!= GNUTLS_E_SUCCESS) {
326
fprintf (stderr, "Error in prime generation: %s\n",
327
safer_gnutls_strerror(ret));
331
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
333
// GnuTLS session creation
334
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
335
!= GNUTLS_E_SUCCESS){
336
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
337
safer_gnutls_strerror(ret));
340
if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
341
!= GNUTLS_E_SUCCESS) {
342
fprintf(stderr, "Syntax error at: %s\n", err);
343
fprintf(stderr, "GnuTLS error: %s\n",
344
safer_gnutls_strerror(ret));
348
if ((ret = gnutls_credentials_set
349
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
350
!= GNUTLS_E_SUCCESS) {
351
fprintf(stderr, "Error setting a credentials set: %s\n",
352
safer_gnutls_strerror(ret));
752
(mc->cred, pubkeyfilename, seckeyfilename,
753
GNUTLS_OPENPGP_FMT_BASE64);
755
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
757
if(ret != GNUTLS_E_SUCCESS){
759
"Error[%d] while reading the key pair ('%s',"
760
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
761
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
762
safer_gnutls_strerror(ret));
766
/* GnuTLS server initialization */
767
ret = gnutls_dh_params_init(&mc->dh_params);
768
if(ret != GNUTLS_E_SUCCESS){
769
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
770
" initialization: %s\n",
771
safer_gnutls_strerror(ret));
774
/* If a Diffie-Hellman parameters file was given, try to use it */
775
if(dhparamsfilename != NULL){
776
gnutls_datum_t params = { .data = NULL, .size = 0 };
778
int dhpfile = open(dhparamsfilename, O_RDONLY);
781
dhparamsfilename = NULL;
784
size_t params_capacity = 0;
786
params_capacity = incbuffer((char **)¶ms.data,
788
(size_t)params_capacity);
789
if(params_capacity == 0){
790
perror_plus("incbuffer");
793
dhparamsfilename = NULL;
796
ssize_t bytes_read = read(dhpfile,
797
params.data + params.size,
803
/* check bytes_read for failure */
808
dhparamsfilename = NULL;
811
params.size += (unsigned int)bytes_read;
813
ret = close(dhpfile);
815
perror_plus("close");
817
if(params.data == NULL){
818
dhparamsfilename = NULL;
820
if(dhparamsfilename == NULL){
823
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
824
GNUTLS_X509_FMT_PEM);
825
if(ret != GNUTLS_E_SUCCESS){
826
fprintf_plus(stderr, "Failed to parse DH parameters in file"
827
" \"%s\": %s\n", dhparamsfilename,
828
safer_gnutls_strerror(ret));
829
dhparamsfilename = NULL;
834
if(dhparamsfilename == NULL){
835
if(mc->dh_bits == 0){
836
#if GNUTLS_VERSION_NUMBER < 0x030600
837
/* Find out the optimal number of DH bits */
838
/* Try to read the private key file */
839
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
841
int secfile = open(seckeyfilename, O_RDONLY);
846
size_t buffer_capacity = 0;
848
buffer_capacity = incbuffer((char **)&buffer.data,
850
(size_t)buffer_capacity);
851
if(buffer_capacity == 0){
852
perror_plus("incbuffer");
857
ssize_t bytes_read = read(secfile,
858
buffer.data + buffer.size,
864
/* check bytes_read for failure */
871
buffer.size += (unsigned int)bytes_read;
875
/* If successful, use buffer to parse private key */
876
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
877
if(buffer.data != NULL){
879
gnutls_openpgp_privkey_t privkey = NULL;
880
ret = gnutls_openpgp_privkey_init(&privkey);
881
if(ret != GNUTLS_E_SUCCESS){
882
fprintf_plus(stderr, "Error initializing OpenPGP key"
884
safer_gnutls_strerror(ret));
888
ret = gnutls_openpgp_privkey_import
889
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
890
if(ret != GNUTLS_E_SUCCESS){
891
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
892
safer_gnutls_strerror(ret));
898
/* Use private key to suggest an appropriate
900
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
901
gnutls_openpgp_privkey_deinit(privkey);
903
fprintf_plus(stderr, "This OpenPGP key implies using"
904
" a GnuTLS security parameter \"%s\".\n",
905
safe_string(gnutls_sec_param_get_name
911
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
912
/* Err on the side of caution */
913
sec_param = GNUTLS_SEC_PARAM_ULTRA;
915
fprintf_plus(stderr, "Falling back to security parameter"
917
safe_string(gnutls_sec_param_get_name
922
unsigned int uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
926
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
927
" implies %u DH bits; using that.\n",
928
safe_string(gnutls_sec_param_get_name
933
fprintf_plus(stderr, "Failed to get implied number of DH"
934
" bits for security parameter \"%s\"): %s\n",
935
safe_string(gnutls_sec_param_get_name
937
safer_gnutls_strerror(ret));
941
} else { /* dh_bits != 0 */
943
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
946
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
947
if(ret != GNUTLS_E_SUCCESS){
948
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
949
" bits): %s\n", mc->dh_bits,
950
safer_gnutls_strerror(ret));
953
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
961
gnutls_certificate_free_credentials(mc->cred);
962
gnutls_dh_params_deinit(mc->dh_params);
966
__attribute__((nonnull, warn_unused_result))
967
static int init_gnutls_session(gnutls_session_t *session,
970
/* GnuTLS session creation */
972
ret = gnutls_init(session, (GNUTLS_SERVER
973
#if GNUTLS_VERSION_NUMBER >= 0x030506
976
#if GNUTLS_VERSION_NUMBER >= 0x030606
977
| GNUTLS_ENABLE_RAWPK
983
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
984
if(ret != GNUTLS_E_SUCCESS){
986
"Error in GnuTLS session initialization: %s\n",
987
safer_gnutls_strerror(ret));
993
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
995
gnutls_deinit(*session);
998
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
999
if(ret != GNUTLS_E_SUCCESS){
1000
fprintf_plus(stderr, "Syntax error at: %s\n", err);
1001
fprintf_plus(stderr, "GnuTLS error: %s\n",
1002
safer_gnutls_strerror(ret));
1003
gnutls_deinit(*session);
1009
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
1012
gnutls_deinit(*session);
1015
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
1016
if(ret != GNUTLS_E_SUCCESS){
1017
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
1018
safer_gnutls_strerror(ret));
1019
gnutls_deinit(*session);
356
1023
/* ignore client certificate if any. */
357
gnutls_certificate_server_set_request (es->session,
360
gnutls_dh_set_prime_bits (es->session, DH_BITS);
1024
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
1029
/* Avahi log function callback */
365
1030
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
366
1031
__attribute__((unused)) const char *txt){}
368
static int start_mandos_communication(const char *ip, uint16_t port,
1033
/* Helper function to add_local_route() and delete_local_route() */
1034
__attribute__((nonnull, warn_unused_result))
1035
static bool add_delete_local_route(const bool add,
1036
const char *address,
1037
AvahiIfIndex if_index){
1039
char helper[] = "mandos-client-iprouteadddel";
1040
char add_arg[] = "add";
1041
char delete_arg[] = "delete";
1042
char debug_flag[] = "--debug";
1043
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
1044
if(pluginhelperdir == NULL){
1046
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
1047
" variable not set; cannot run helper\n");
1052
char interface[IF_NAMESIZE];
1053
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1054
perror_plus("if_indextoname");
1058
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1060
perror_plus("open(\"/dev/null\", O_RDONLY)");
1066
/* Raise privileges */
1067
errno = raise_privileges_permanently();
1069
perror_plus("Failed to raise privileges");
1070
/* _exit(EX_NOPERM); */
1076
perror_plus("setgid");
1079
/* Reset supplementary groups */
1081
ret = setgroups(0, NULL);
1083
perror_plus("setgroups");
1087
ret = dup2(devnull, STDIN_FILENO);
1089
perror_plus("dup2(devnull, STDIN_FILENO)");
1092
ret = close(devnull);
1094
perror_plus("close");
1097
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1099
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1102
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
1107
if(helperdir_fd == -1){
1108
perror_plus("open");
1109
_exit(EX_UNAVAILABLE);
1111
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
1113
if(helper_fd == -1){
1114
perror_plus("openat");
1115
close(helperdir_fd);
1116
_exit(EX_UNAVAILABLE);
1118
close(helperdir_fd);
1120
#pragma GCC diagnostic push
1121
#pragma GCC diagnostic ignored "-Wcast-qual"
1123
if(fexecve(helper_fd, (char *const [])
1124
{ helper, add ? add_arg : delete_arg, (char *)address,
1125
interface, debug ? debug_flag : NULL, NULL },
1128
#pragma GCC diagnostic pop
1130
perror_plus("fexecve");
1131
_exit(EXIT_FAILURE);
1135
perror_plus("fork");
1142
pret = waitpid(pid, &status, 0);
1143
if(pret == -1 and errno == EINTR and quit_now){
1144
int errno_raising = 0;
1145
if((errno = raise_privileges()) != 0){
1146
errno_raising = errno;
1147
perror_plus("Failed to raise privileges in order to"
1148
" kill helper program");
1150
if(kill(pid, SIGTERM) == -1){
1151
perror_plus("kill");
1153
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
1154
perror_plus("Failed to lower privileges after killing"
1159
} while(pret == -1 and errno == EINTR);
1161
perror_plus("waitpid");
1164
if(WIFEXITED(status)){
1165
if(WEXITSTATUS(status) != 0){
1166
fprintf_plus(stderr, "Error: iprouteadddel exited"
1167
" with status %d\n", WEXITSTATUS(status));
1172
if(WIFSIGNALED(status)){
1173
fprintf_plus(stderr, "Error: iprouteadddel died by"
1174
" signal %d\n", WTERMSIG(status));
1177
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1181
__attribute__((nonnull, warn_unused_result))
1182
static bool add_local_route(const char *address,
1183
AvahiIfIndex if_index){
1185
fprintf_plus(stderr, "Adding route to %s\n", address);
1187
return add_delete_local_route(true, address, if_index);
1190
__attribute__((nonnull, warn_unused_result))
1191
static bool delete_local_route(const char *address,
1192
AvahiIfIndex if_index){
1194
fprintf_plus(stderr, "Removing route to %s\n", address);
1196
return add_delete_local_route(false, address, if_index);
1199
/* Called when a Mandos server is found */
1200
__attribute__((nonnull, warn_unused_result))
1201
static int start_mandos_communication(const char *ip, in_port_t port,
369
1202
AvahiIfIndex if_index,
372
struct sockaddr_in6 to;
373
encrypted_session es;
1203
int af, mandos_context *mc){
1204
int ret, tcp_sd = -1;
1206
struct sockaddr_storage to;
374
1207
char *buffer = NULL;
375
char *decrypted_buffer;
1208
char *decrypted_buffer = NULL;
376
1209
size_t buffer_length = 0;
377
1210
size_t buffer_capacity = 0;
378
ssize_t decrypted_buffer_size;
381
char interface[IF_NAMESIZE];
384
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
388
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
395
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1213
gnutls_session_t session;
1214
int pf; /* Protocol family */
1215
bool route_added = false;
1232
fprintf_plus(stderr, "Bad address family: %d\n", af);
1237
/* If the interface is specified and we have a list of interfaces */
1238
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1239
/* Check if the interface is one of the interfaces we are using */
1242
char *interface = NULL;
1243
while((interface = argz_next(mc->interfaces,
1244
mc->interfaces_size,
1246
if(if_nametoindex(interface) == (unsigned int)if_index){
1253
/* This interface does not match any in the list, so we don't
1254
connect to the server */
397
perror("if_indextoname");
1256
char interface[IF_NAMESIZE];
1257
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1258
perror_plus("if_indextoname");
1260
fprintf_plus(stderr, "Skipping server on non-used interface"
1262
if_indextoname((unsigned int)if_index,
402
fprintf(stderr, "Binding to interface %s\n", interface);
405
memset(&to,0,sizeof(to)); /* Spurious warning */
406
to.sin6_family = AF_INET6;
407
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
1270
ret = init_gnutls_session(&session, mc);
1276
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1277
PRIuMAX "\n", ip, (uintmax_t)port);
1280
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1283
perror_plus("socket");
1294
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1295
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1296
ret = inet_pton(af, ip, &to6->sin6_addr);
1298
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1299
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1300
ret = inet_pton(af, ip, &to4->sin_addr);
1304
perror_plus("inet_pton");
413
fprintf(stderr, "Bad address: %s\n", ip);
416
to.sin6_port = htons(port); /* Spurious warning */
1310
fprintf_plus(stderr, "Bad address: %s\n", ip);
1315
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1316
if(IN6_IS_ADDR_LINKLOCAL
1317
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1318
if(if_index == AVAHI_IF_UNSPEC){
1319
fprintf_plus(stderr, "An IPv6 link-local address is"
1320
" incomplete without a network interface\n");
1324
/* Set the network interface number as scope */
1325
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1328
((struct sockaddr_in *)&to)->sin_port = htons(port);
418
to.sin6_scope_id = (uint32_t)if_index;
421
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
422
/* char addrstr[INET6_ADDRSTRLEN]; */
423
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
424
/* sizeof(addrstr)) == NULL){ */
425
/* perror("inet_ntop"); */
427
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
428
/* addrstr, ntohs(to.sin6_port)); */
432
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
438
char *out = mandos_protocol_version;
1337
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1338
char interface[IF_NAMESIZE];
1339
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1340
perror_plus("if_indextoname");
1342
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1343
"\n", ip, interface, (uintmax_t)port);
1346
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1347
ip, (uintmax_t)port);
1349
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1350
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1352
ret = getnameinfo((struct sockaddr *)&to,
1353
sizeof(struct sockaddr_in6),
1354
addrstr, sizeof(addrstr), NULL, 0,
1357
ret = getnameinfo((struct sockaddr *)&to,
1358
sizeof(struct sockaddr_in),
1359
addrstr, sizeof(addrstr), NULL, 0,
1362
if(ret == EAI_SYSTEM){
1363
perror_plus("getnameinfo");
1364
} else if(ret != 0) {
1365
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1366
} else if(strcmp(addrstr, ip) != 0){
1367
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1378
ret = connect(tcp_sd, (struct sockaddr *)&to,
1379
sizeof(struct sockaddr_in6));
1381
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1382
sizeof(struct sockaddr_in));
1385
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1386
and if_index != AVAHI_IF_UNSPEC
1387
and connect_to == NULL
1388
and not route_added and
1389
((af == AF_INET6 and not
1390
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1392
or (af == AF_INET and
1393
/* Not a a IPv4LL address */
1394
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1395
& 0xFFFF0000L) != 0xA9FE0000L))){
1396
/* Work around Avahi bug - Avahi does not announce link-local
1397
addresses if it has a global address, so local hosts with
1398
*only* a link-local address (e.g. Mandos clients) cannot
1399
connect to a Mandos server announced by Avahi on a server
1400
host with a global address. Work around this by retrying
1401
with an explicit route added with the server's address.
1403
Avahi bug reference:
1404
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1405
https://bugs.debian.org/587961
1408
fprintf_plus(stderr, "Mandos server unreachable, trying"
1412
route_added = add_local_route(ip, if_index);
1418
if(errno != ECONNREFUSED or debug){
1420
perror_plus("connect");
1433
const char *out = mandos_protocol_version;
441
1436
size_t out_size = strlen(out);
442
ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
443
out_size - written));
1437
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1438
out_size - written));
1441
perror_plus("write");
1445
written += (size_t)ret;
450
1446
if(written < out_size){
453
if (out == mandos_protocol_version){
1449
if(out == mandos_protocol_version){
462
ret = initgnutls (&es);
468
gnutls_transport_set_ptr (es.session,
469
(gnutls_transport_ptr_t) tcp_sd);
472
fprintf(stderr, "Establishing TLS session with %s\n", ip);
475
ret = gnutls_handshake (es.session);
477
if (ret != GNUTLS_E_SUCCESS){
1464
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1472
/* This casting via intptr_t is to eliminate warning about casting
1473
an int to a pointer type. This is exactly how the GnuTLS Guile
1474
function "set-session-transport-fd!" does it. */
1475
gnutls_transport_set_ptr(session,
1476
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1484
ret = gnutls_handshake(session);
1489
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1491
if(ret != GNUTLS_E_SUCCESS){
479
fprintf(stderr, "\n*** Handshake failed ***\n");
1493
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
486
//Retrieve OpenPGP packet that contains the wanted password
1500
/* Read OpenPGP packet that contains the wanted password */
489
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1503
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
494
buffer_capacity = adjustbuffer(buffer, buffer_length, buffer_capacity);
495
if (buffer_capacity == 0){
496
perror("adjustbuffer");
501
ret = gnutls_record_recv
502
(es.session, buffer+buffer_length, BUFFER_SIZE);
1514
buffer_capacity = incbuffer(&buffer, buffer_length,
1516
if(buffer_capacity == 0){
1518
perror_plus("incbuffer");
1528
sret = gnutls_record_recv(session, buffer+buffer_length,
508
1535
case GNUTLS_E_INTERRUPTED:
509
1536
case GNUTLS_E_AGAIN:
511
1538
case GNUTLS_E_REHANDSHAKE:
512
ret = gnutls_handshake (es.session);
514
fprintf(stderr, "\n*** Handshake failed ***\n");
1540
ret = gnutls_handshake(session);
1546
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1548
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
521
fprintf(stderr, "Unknown error while reading data from"
522
" encrypted session with mandos server\n");
524
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1556
fprintf_plus(stderr, "Unknown error while reading data from"
1557
" encrypted session with Mandos server\n");
1558
gnutls_bye(session, GNUTLS_SHUT_RDWR);
528
buffer_length += (size_t) ret;
532
if (buffer_length > 0){
533
decrypted_buffer_size = pgp_packet_decrypt(buffer,
537
if (decrypted_buffer_size >= 0){
1563
buffer_length += (size_t) sret;
1568
fprintf_plus(stderr, "Closing TLS session\n");
1577
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1582
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1584
if(buffer_length > 0){
1585
ssize_t decrypted_buffer_size;
1586
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1587
&decrypted_buffer, mc);
1588
if(decrypted_buffer_size >= 0){
539
1592
while(written < (size_t) decrypted_buffer_size){
540
ret = (int)fwrite (decrypted_buffer + written, 1,
541
(size_t)decrypted_buffer_size - written,
1598
ret = (int)fwrite(decrypted_buffer + written, 1,
1599
(size_t)decrypted_buffer_size - written,
543
1601
if(ret == 0 and ferror(stdout)){
545
fprintf(stderr, "Error writing encrypted data: %s\n",
1604
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
551
1610
written += (size_t)ret;
553
free(decrypted_buffer);
1612
ret = fflush(stdout);
1616
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1626
/* Shutdown procedure */
1631
if(not delete_local_route(ip, if_index)){
1632
fprintf_plus(stderr, "Failed to delete local route to %s on"
1633
" interface %d", ip, if_index);
1637
free(decrypted_buffer);
1640
ret = close(tcp_sd);
1646
perror_plus("close");
1648
gnutls_deinit(session);
562
fprintf(stderr, "Closing TLS session\n");
566
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
569
gnutls_deinit (es.session);
570
gnutls_certificate_free_credentials (es.cred);
571
gnutls_global_deinit ();
575
static void resolve_callback( AvahiSServiceResolver *r,
576
AvahiIfIndex interface,
577
AVAHI_GCC_UNUSED AvahiProtocol protocol,
578
AvahiResolverEvent event,
582
const char *host_name,
583
const AvahiAddress *address,
585
AVAHI_GCC_UNUSED AvahiStringList *txt,
586
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
587
AVAHI_GCC_UNUSED void* userdata) {
588
mandos_context *mc = userdata;
589
assert(r); /* Spurious warning */
1658
static void resolve_callback(AvahiSServiceResolver *r,
1659
AvahiIfIndex interface,
1660
AvahiProtocol proto,
1661
AvahiResolverEvent event,
1665
const char *host_name,
1666
const AvahiAddress *address,
1668
AVAHI_GCC_UNUSED AvahiStringList *txt,
1669
AVAHI_GCC_UNUSED AvahiLookupResultFlags
591
1676
/* Called whenever a service has been resolved successfully or
1680
avahi_s_service_resolver_free(r);
596
1686
case AVAHI_RESOLVER_FAILURE:
597
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
598
" type '%s' in domain '%s': %s\n", name, type, domain,
599
avahi_strerror(avahi_server_errno(mc->server)));
1687
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1688
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1690
avahi_strerror(avahi_server_errno
1691
(((mandos_context*)mc)->server)));
602
1694
case AVAHI_RESOLVER_FOUND:
604
1696
char ip[AVAHI_ADDRESS_STR_MAX];
605
1697
avahi_address_snprint(ip, sizeof(ip), address);
607
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
608
" port %d\n", name, host_name, ip, port);
1699
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1700
PRIdMAX ") on port %" PRIu16 "\n", name,
1701
host_name, ip, (intmax_t)interface, port);
610
int ret = start_mandos_communication(ip, port, interface, mc);
1703
int ret = start_mandos_communication(ip, (in_port_t)port,
1705
avahi_proto_to_af(proto),
1708
avahi_simple_poll_quit(simple_poll);
1710
if(not add_server(ip, (in_port_t)port, interface,
1711
avahi_proto_to_af(proto),
1712
&((mandos_context*)mc)->current_server)){
1713
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
616
1719
avahi_s_service_resolver_free(r);
619
static void browse_callback( AvahiSServiceBrowser *b,
620
AvahiIfIndex interface,
621
AvahiProtocol protocol,
622
AvahiBrowserEvent event,
626
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
628
mandos_context *mc = userdata;
629
assert(b); /* Spurious warning */
1722
static void browse_callback(AvahiSServiceBrowser *b,
1723
AvahiIfIndex interface,
1724
AvahiProtocol protocol,
1725
AvahiBrowserEvent event,
1729
AVAHI_GCC_UNUSED AvahiLookupResultFlags
631
1736
/* Called whenever a new services becomes available on the LAN or
632
1737
is removed from the LAN */
636
1745
case AVAHI_BROWSER_FAILURE:
638
fprintf(stderr, "(Browser) %s\n",
639
avahi_strerror(avahi_server_errno(mc->server)));
640
avahi_simple_poll_quit(mc->simple_poll);
1747
fprintf_plus(stderr, "(Avahi browser) %s\n",
1748
avahi_strerror(avahi_server_errno
1749
(((mandos_context*)mc)->server)));
1750
avahi_simple_poll_quit(simple_poll);
643
1753
case AVAHI_BROWSER_NEW:
644
/* We ignore the returned resolver object. In the callback
645
function we free it. If the server is terminated before
646
the callback function is called the server will free
647
the resolver for us. */
649
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
651
AVAHI_PROTO_INET6, 0,
652
resolve_callback, mc)))
653
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
654
avahi_strerror(avahi_server_errno(s)));
1754
/* We ignore the returned Avahi resolver object. In the callback
1755
function we free it. If the Avahi server is terminated before
1756
the callback function is called the Avahi server will free the
1759
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1760
interface, protocol, name, type,
1761
domain, protocol, 0,
1762
resolve_callback, mc) == NULL)
1763
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1765
avahi_strerror(avahi_server_errno
1766
(((mandos_context*)mc)->server)));
657
1769
case AVAHI_BROWSER_REMOVE:
660
1772
case AVAHI_BROWSER_ALL_FOR_NOW:
661
1773
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1775
fprintf_plus(stderr, "No Mandos server found, still"
666
/* Combines file name and path and returns the malloced new
667
string. some sane checks could/should be added */
668
static const char *combinepath(const char *first, const char *second){
669
size_t f_len = strlen(first);
670
size_t s_len = strlen(second);
671
char *tmp = malloc(f_len + s_len + 2);
676
memcpy(tmp, first, f_len);
680
memcpy(tmp + f_len + 1, second, s_len);
682
tmp[f_len + 1 + s_len] = '\0';
687
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1782
/* Signal handler that stops main loop after SIGTERM */
1783
static void handle_sigterm(int sig){
1788
signal_received = sig;
1789
int old_errno = errno;
1790
/* set main loop to exit */
1791
if(simple_poll != NULL){
1792
avahi_simple_poll_quit(simple_poll);
1797
__attribute__((nonnull, warn_unused_result))
1798
bool get_flags(const char *ifname, struct ifreq *ifr){
1802
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1805
perror_plus("socket");
1809
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1810
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1811
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1815
perror_plus("ioctl SIOCGIFFLAGS");
1818
if((close(s) == -1) and debug){
1820
perror_plus("close");
1825
if((close(s) == -1) and debug){
1827
perror_plus("close");
1833
__attribute__((nonnull, warn_unused_result))
1834
bool good_flags(const char *ifname, const struct ifreq *ifr){
1836
/* Reject the loopback device */
1837
if(ifr->ifr_flags & IFF_LOOPBACK){
1839
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1844
/* Accept point-to-point devices only if connect_to is specified */
1845
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1847
fprintf_plus(stderr, "Accepting point-to-point interface"
1848
" \"%s\"\n", ifname);
1852
/* Otherwise, reject non-broadcast-capable devices */
1853
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1855
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1856
" \"%s\"\n", ifname);
1860
/* Reject non-ARP interfaces (including dummy interfaces) */
1861
if(ifr->ifr_flags & IFF_NOARP){
1863
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1869
/* Accept this device */
1871
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1877
* This function determines if a directory entry in /sys/class/net
1878
* corresponds to an acceptable network device.
1879
* (This function is passed to scandir(3) as a filter function.)
1881
__attribute__((nonnull, warn_unused_result))
1882
int good_interface(const struct dirent *if_entry){
1883
if(if_entry->d_name[0] == '.'){
1888
if(not get_flags(if_entry->d_name, &ifr)){
1890
fprintf_plus(stderr, "Failed to get flags for interface "
1891
"\"%s\"\n", if_entry->d_name);
1896
if(not good_flags(if_entry->d_name, &ifr)){
1903
* This function determines if a network interface is up.
1905
__attribute__((nonnull, warn_unused_result))
1906
bool interface_is_up(const char *interface){
1908
if(not get_flags(interface, &ifr)){
1910
fprintf_plus(stderr, "Failed to get flags for interface "
1911
"\"%s\"\n", interface);
1916
return (bool)(ifr.ifr_flags & IFF_UP);
1920
* This function determines if a network interface is running
1922
__attribute__((nonnull, warn_unused_result))
1923
bool interface_is_running(const char *interface){
1925
if(not get_flags(interface, &ifr)){
1927
fprintf_plus(stderr, "Failed to get flags for interface "
1928
"\"%s\"\n", interface);
1933
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1936
__attribute__((nonnull, pure, warn_unused_result))
1937
int notdotentries(const struct dirent *direntry){
1938
/* Skip "." and ".." */
1939
if(direntry->d_name[0] == '.'
1940
and (direntry->d_name[1] == '\0'
1941
or (direntry->d_name[1] == '.'
1942
and direntry->d_name[2] == '\0'))){
1948
/* Is this directory entry a runnable program? */
1949
__attribute__((nonnull, warn_unused_result))
1950
int runnable_hook(const struct dirent *direntry){
1955
if((direntry->d_name)[0] == '\0'){
1960
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1961
"abcdefghijklmnopqrstuvwxyz"
1964
if((direntry->d_name)[sret] != '\0'){
1965
/* Contains non-allowed characters */
1967
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1973
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1976
perror_plus("Could not stat hook");
1980
if(not (S_ISREG(st.st_mode))){
1981
/* Not a regular file */
1983
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1988
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1989
/* Not executable */
1991
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1997
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
2003
__attribute__((nonnull, warn_unused_result))
2004
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
2005
mandos_context *mc){
2007
struct timespec now;
2008
struct timespec waited_time;
2009
intmax_t block_time;
2012
if(mc->current_server == NULL){
2014
fprintf_plus(stderr, "Wait until first server is found."
2017
ret = avahi_simple_poll_iterate(s, -1);
2020
fprintf_plus(stderr, "Check current_server if we should run"
2023
/* the current time */
2024
ret = clock_gettime(CLOCK_MONOTONIC, &now);
2026
perror_plus("clock_gettime");
2029
/* Calculating in ms how long time between now and server
2030
who we visted longest time ago. Now - last seen. */
2031
waited_time.tv_sec = (now.tv_sec
2032
- mc->current_server->last_seen.tv_sec);
2033
waited_time.tv_nsec = (now.tv_nsec
2034
- mc->current_server->last_seen.tv_nsec);
2035
/* total time is 10s/10,000ms.
2036
Converting to s from ms by dividing by 1,000,
2037
and ns to ms by dividing by 1,000,000. */
2038
block_time = ((retry_interval
2039
- ((intmax_t)waited_time.tv_sec * 1000))
2040
- ((intmax_t)waited_time.tv_nsec / 1000000));
2043
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
2047
if(block_time <= 0){
2048
ret = start_mandos_communication(mc->current_server->ip,
2049
mc->current_server->port,
2050
mc->current_server->if_index,
2051
mc->current_server->af, mc);
2053
avahi_simple_poll_quit(s);
2056
ret = clock_gettime(CLOCK_MONOTONIC,
2057
&mc->current_server->last_seen);
2059
perror_plus("clock_gettime");
2062
mc->current_server = mc->current_server->next;
2063
block_time = 0; /* Call avahi to find new Mandos
2064
servers, but don't block */
2067
ret = avahi_simple_poll_iterate(s, (int)block_time);
2070
if(ret > 0 or errno != EINTR){
2071
return (ret != 1) ? ret : 0;
2077
__attribute__((nonnull))
2078
void run_network_hooks(const char *mode, const char *interface,
2080
struct dirent **direntries = NULL;
2081
if(hookdir_fd == -1){
2082
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
2084
if(hookdir_fd == -1){
2085
if(errno == ENOENT){
2087
fprintf_plus(stderr, "Network hook directory \"%s\" not"
2088
" found\n", hookdir);
2091
perror_plus("open");
2096
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2098
perror_plus("open(\"/dev/null\", O_RDONLY)");
2101
int numhooks = scandirat(hookdir_fd, ".", &direntries,
2102
runnable_hook, alphasort);
2104
perror_plus("scandir");
2108
struct dirent *direntry;
2110
for(int i = 0; i < numhooks; i++){
2111
direntry = direntries[i];
2113
fprintf_plus(stderr, "Running network hook \"%s\"\n",
2116
pid_t hook_pid = fork();
2119
/* Raise privileges */
2120
errno = raise_privileges_permanently();
2122
perror_plus("Failed to raise privileges");
2129
perror_plus("setgid");
2132
/* Reset supplementary groups */
2134
ret = setgroups(0, NULL);
2136
perror_plus("setgroups");
2139
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
2141
perror_plus("setenv");
2144
ret = setenv("DEVICE", interface, 1);
2146
perror_plus("setenv");
2149
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
2151
perror_plus("setenv");
2154
ret = setenv("MODE", mode, 1);
2156
perror_plus("setenv");
2160
ret = asprintf(&delaystring, "%f", (double)delay);
2162
perror_plus("asprintf");
2165
ret = setenv("DELAY", delaystring, 1);
2168
perror_plus("setenv");
2172
if(connect_to != NULL){
2173
ret = setenv("CONNECT", connect_to, 1);
2175
perror_plus("setenv");
2179
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2183
perror_plus("openat");
2184
_exit(EXIT_FAILURE);
2186
if(close(hookdir_fd) == -1){
2187
perror_plus("close");
2188
_exit(EXIT_FAILURE);
2190
ret = dup2(devnull, STDIN_FILENO);
2192
perror_plus("dup2(devnull, STDIN_FILENO)");
2195
ret = close(devnull);
2197
perror_plus("close");
2200
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2202
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2205
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2207
perror_plus("fexecve");
2208
_exit(EXIT_FAILURE);
2212
perror_plus("fork");
2217
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2218
perror_plus("waitpid");
2222
if(WIFEXITED(status)){
2223
if(WEXITSTATUS(status) != 0){
2224
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2225
" with status %d\n", direntry->d_name,
2226
WEXITSTATUS(status));
2230
} else if(WIFSIGNALED(status)){
2231
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2232
" signal %d\n", direntry->d_name,
2237
fprintf_plus(stderr, "Warning: network hook \"%s\""
2238
" crashed\n", direntry->d_name);
2244
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2250
if(close(hookdir_fd) == -1){
2251
perror_plus("close");
2258
__attribute__((nonnull, warn_unused_result))
2259
int bring_up_interface(const char *const interface,
2261
int old_errno = errno;
2263
struct ifreq network;
2264
unsigned int if_index = if_nametoindex(interface);
2266
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2276
if(not interface_is_up(interface)){
2278
int ioctl_errno = 0;
2279
if(not get_flags(interface, &network)){
2281
fprintf_plus(stderr, "Failed to get flags for interface "
2282
"\"%s\"\n", interface);
2286
network.ifr_flags |= IFF_UP; /* set flag */
2288
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2291
perror_plus("socket");
2299
perror_plus("close");
2306
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2310
/* Raise privileges */
2311
ret_errno = raise_privileges();
2314
perror_plus("Failed to raise privileges");
2319
bool restore_loglevel = false;
2321
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2322
messages about the network interface to mess up the prompt */
2323
ret_linux = klogctl(8, NULL, 5);
2324
if(ret_linux == -1){
2325
perror_plus("klogctl");
2327
restore_loglevel = true;
2330
#endif /* __linux__ */
2331
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2332
ioctl_errno = errno;
2334
if(restore_loglevel){
2335
ret_linux = klogctl(7, NULL, 0);
2336
if(ret_linux == -1){
2337
perror_plus("klogctl");
2340
#endif /* __linux__ */
2342
/* If raise_privileges() succeeded above */
2344
/* Lower privileges */
2345
ret_errno = lower_privileges();
2348
perror_plus("Failed to lower privileges");
2352
/* Close the socket */
2355
perror_plus("close");
2358
if(ret_setflags == -1){
2359
errno = ioctl_errno;
2360
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2365
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2369
/* Sleep checking until interface is running.
2370
Check every 0.25s, up to total time of delay */
2371
for(int i = 0; i < delay * 4; i++){
2372
if(interface_is_running(interface)){
2375
struct timespec sleeptime = { .tv_nsec = 250000000 };
2376
ret = nanosleep(&sleeptime, NULL);
2377
if(ret == -1 and errno != EINTR){
2378
perror_plus("nanosleep");
2386
__attribute__((nonnull, warn_unused_result))
2387
int take_down_interface(const char *const interface){
2388
int old_errno = errno;
2389
struct ifreq network;
2390
unsigned int if_index = if_nametoindex(interface);
2392
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2396
if(interface_is_up(interface)){
2398
int ioctl_errno = 0;
2399
if(not get_flags(interface, &network) and debug){
2401
fprintf_plus(stderr, "Failed to get flags for interface "
2402
"\"%s\"\n", interface);
2406
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2408
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2411
perror_plus("socket");
2417
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2421
/* Raise privileges */
2422
ret_errno = raise_privileges();
2425
perror_plus("Failed to raise privileges");
2428
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2429
ioctl_errno = errno;
2431
/* If raise_privileges() succeeded above */
2433
/* Lower privileges */
2434
ret_errno = lower_privileges();
2437
perror_plus("Failed to lower privileges");
2441
/* Close the socket */
2442
int ret = close(sd);
2444
perror_plus("close");
2447
if(ret_setflags == -1){
2448
errno = ioctl_errno;
2449
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2454
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2462
int main(int argc, char *argv[]){
2463
mandos_context mc = { .server = NULL, .dh_bits = 0,
2464
#if GNUTLS_VERSION_NUMBER >= 0x030606
2465
.priority = "SECURE128:!CTYPE-X.509"
2466
":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
2468
#elif GNUTLS_VERSION_NUMBER < 0x030600
2469
.priority = "SECURE256:!CTYPE-X.509"
2470
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2472
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
2474
.current_server = NULL, .interfaces = NULL,
2475
.interfaces_size = 0 };
2476
AvahiSServiceBrowser *sb = NULL;
2481
int exitcode = EXIT_SUCCESS;
2482
char *interfaces_to_take_down = NULL;
2483
size_t interfaces_to_take_down_size = 0;
2484
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2485
char old_tempdir[] = "/tmp/mandosXXXXXX";
2486
char *tempdir = NULL;
2487
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2488
const char *seckey = PATHDIR "/" SECKEY;
2489
const char *pubkey = PATHDIR "/" PUBKEY;
2490
#if GNUTLS_VERSION_NUMBER >= 0x030606
2491
const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
2492
const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
2494
const char *dh_params_file = NULL;
2495
char *interfaces_hooks = NULL;
2497
bool gnutls_initialized = false;
2498
bool gpgme_initialized = false;
2500
double retry_interval = 10; /* 10s between trying a server and
2501
retrying the same server again */
2503
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2504
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2509
/* Lower any group privileges we might have, just to be safe */
2513
perror_plus("setgid");
2516
/* Lower user privileges (temporarily) */
2520
perror_plus("seteuid");
2528
struct argp_option options[] = {
2529
{ .name = "debug", .key = 128,
2530
.doc = "Debug mode", .group = 3 },
2531
{ .name = "connect", .key = 'c',
2532
.arg = "ADDRESS:PORT",
2533
.doc = "Connect directly to a specific Mandos server",
2535
{ .name = "interface", .key = 'i',
2537
.doc = "Network interface that will be used to search for"
2540
{ .name = "seckey", .key = 's',
2542
.doc = "OpenPGP secret key file base name",
2544
{ .name = "pubkey", .key = 'p',
2546
.doc = "OpenPGP public key file base name",
2548
{ .name = "tls-privkey", .key = 't',
2550
#if GNUTLS_VERSION_NUMBER >= 0x030606
2551
.doc = "TLS private key file base name",
2553
.doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2556
{ .name = "tls-pubkey", .key = 'T',
2558
#if GNUTLS_VERSION_NUMBER >= 0x030606
2559
.doc = "TLS public key file base name",
2561
.doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2564
{ .name = "dh-bits", .key = 129,
2566
.doc = "Bit length of the prime number used in the"
2567
" Diffie-Hellman key exchange",
2569
{ .name = "dh-params", .key = 134,
2571
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2572
" for the Diffie-Hellman key exchange",
2574
{ .name = "priority", .key = 130,
2576
.doc = "GnuTLS priority string for the TLS handshake",
2578
{ .name = "delay", .key = 131,
2580
.doc = "Maximum delay to wait for interface startup",
2582
{ .name = "retry", .key = 132,
2584
.doc = "Retry interval used when denied by the Mandos server",
2586
{ .name = "network-hook-dir", .key = 133,
2588
.doc = "Directory where network hooks are located",
2591
* These reproduce what we would get without ARGP_NO_HELP
2593
{ .name = "help", .key = '?',
2594
.doc = "Give this help list", .group = -1 },
2595
{ .name = "usage", .key = -3,
2596
.doc = "Give a short usage message", .group = -1 },
2597
{ .name = "version", .key = 'V',
2598
.doc = "Print program version", .group = -1 },
2602
error_t parse_opt(int key, char *arg,
2603
struct argp_state *state){
2606
case 128: /* --debug */
2609
case 'c': /* --connect */
2612
case 'i': /* --interface */
2613
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2616
argp_error(state, "%s", strerror(ret_errno));
2619
case 's': /* --seckey */
2622
case 'p': /* --pubkey */
2625
case 't': /* --tls-privkey */
2626
#if GNUTLS_VERSION_NUMBER >= 0x030606
2630
case 'T': /* --tls-pubkey */
2631
#if GNUTLS_VERSION_NUMBER >= 0x030606
2635
case 129: /* --dh-bits */
2637
tmpmax = strtoimax(arg, &tmp, 10);
2638
if(errno != 0 or tmp == arg or *tmp != '\0'
2639
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2640
argp_error(state, "Bad number of DH bits");
2642
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2644
case 134: /* --dh-params */
2645
dh_params_file = arg;
2647
case 130: /* --priority */
2650
case 131: /* --delay */
2652
delay = strtof(arg, &tmp);
2653
if(errno != 0 or tmp == arg or *tmp != '\0'){
2654
argp_error(state, "Bad delay");
2656
case 132: /* --retry */
2658
retry_interval = strtod(arg, &tmp);
2659
if(errno != 0 or tmp == arg or *tmp != '\0'
2660
or (retry_interval * 1000) > INT_MAX
2661
or retry_interval < 0){
2662
argp_error(state, "Bad retry interval");
2665
case 133: /* --network-hook-dir */
2669
* These reproduce what we would get without ARGP_NO_HELP
2671
case '?': /* --help */
2672
argp_state_help(state, state->out_stream,
2673
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2674
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2675
__builtin_unreachable();
2676
case -3: /* --usage */
2677
argp_state_help(state, state->out_stream,
2678
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2679
__builtin_unreachable();
2680
case 'V': /* --version */
2681
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2682
exit(argp_err_exit_status);
2685
return ARGP_ERR_UNKNOWN;
2690
struct argp argp = { .options = options, .parser = parse_opt,
2692
.doc = "Mandos client -- Get and decrypt"
2693
" passwords from a Mandos server" };
2694
ret_errno = argp_parse(&argp, argc, argv,
2695
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2702
perror_plus("argp_parse");
2703
exitcode = EX_OSERR;
2706
exitcode = EX_USAGE;
2712
/* Work around Debian bug #633582:
2713
<https://bugs.debian.org/633582> */
2715
/* Re-raise privileges */
2716
ret = raise_privileges();
2719
perror_plus("Failed to raise privileges");
2723
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2724
int seckey_fd = open(seckey, O_RDONLY);
2725
if(seckey_fd == -1){
2726
perror_plus("open");
2728
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2730
perror_plus("fstat");
2732
if(S_ISREG(st.st_mode)
2733
and st.st_uid == 0 and st.st_gid == 0){
2734
ret = fchown(seckey_fd, uid, gid);
2736
perror_plus("fchown");
2744
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2745
int pubkey_fd = open(pubkey, O_RDONLY);
2746
if(pubkey_fd == -1){
2747
perror_plus("open");
2749
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2751
perror_plus("fstat");
2753
if(S_ISREG(st.st_mode)
2754
and st.st_uid == 0 and st.st_gid == 0){
2755
ret = fchown(pubkey_fd, uid, gid);
2757
perror_plus("fchown");
2765
if(dh_params_file != NULL
2766
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2767
int dhparams_fd = open(dh_params_file, O_RDONLY);
2768
if(dhparams_fd == -1){
2769
perror_plus("open");
2771
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2773
perror_plus("fstat");
2775
if(S_ISREG(st.st_mode)
2776
and st.st_uid == 0 and st.st_gid == 0){
2777
ret = fchown(dhparams_fd, uid, gid);
2779
perror_plus("fchown");
2787
/* Lower privileges */
2788
ret = lower_privileges();
2791
perror_plus("Failed to lower privileges");
2796
/* Remove invalid interface names (except "none") */
2798
char *interface = NULL;
2799
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2801
if(strcmp(interface, "none") != 0
2802
and if_nametoindex(interface) == 0){
2803
if(interface[0] != '\0'){
2804
fprintf_plus(stderr, "Not using nonexisting interface"
2805
" \"%s\"\n", interface);
2807
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2813
/* Run network hooks */
2815
if(mc.interfaces != NULL){
2816
interfaces_hooks = malloc(mc.interfaces_size);
2817
if(interfaces_hooks == NULL){
2818
perror_plus("malloc");
2821
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2822
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2824
run_network_hooks("start", interfaces_hooks != NULL ?
2825
interfaces_hooks : "", delay);
2829
avahi_set_log_function(empty_log);
2832
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2833
from the signal handler */
2834
/* Initialize the pseudo-RNG for Avahi */
2835
srand((unsigned int) time(NULL));
2836
simple_poll = avahi_simple_poll_new();
2837
if(simple_poll == NULL){
2838
fprintf_plus(stderr,
2839
"Avahi: Failed to create simple poll object.\n");
2840
exitcode = EX_UNAVAILABLE;
2844
sigemptyset(&sigterm_action.sa_mask);
2845
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2847
perror_plus("sigaddset");
2848
exitcode = EX_OSERR;
2851
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2853
perror_plus("sigaddset");
2854
exitcode = EX_OSERR;
2857
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2859
perror_plus("sigaddset");
2860
exitcode = EX_OSERR;
2863
/* Need to check if the handler is SIG_IGN before handling:
2864
| [[info:libc:Initial Signal Actions]] |
2865
| [[info:libc:Basic Signal Handling]] |
2867
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2869
perror_plus("sigaction");
2872
if(old_sigterm_action.sa_handler != SIG_IGN){
2873
ret = sigaction(SIGINT, &sigterm_action, NULL);
2875
perror_plus("sigaction");
2876
exitcode = EX_OSERR;
2880
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2882
perror_plus("sigaction");
2885
if(old_sigterm_action.sa_handler != SIG_IGN){
2886
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2888
perror_plus("sigaction");
2889
exitcode = EX_OSERR;
2893
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2895
perror_plus("sigaction");
2898
if(old_sigterm_action.sa_handler != SIG_IGN){
2899
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2901
perror_plus("sigaction");
2902
exitcode = EX_OSERR;
2907
/* If no interfaces were specified, make a list */
2908
if(mc.interfaces == NULL){
2909
struct dirent **direntries = NULL;
2910
/* Look for any good interfaces */
2911
ret = scandir(sys_class_net, &direntries, good_interface,
2914
/* Add all found interfaces to interfaces list */
2915
for(int i = 0; i < ret; ++i){
2916
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2917
direntries[i]->d_name);
2920
perror_plus("argz_add");
2921
free(direntries[i]);
2925
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2926
direntries[i]->d_name);
2928
free(direntries[i]);
2935
fprintf_plus(stderr, "Could not find a network interface\n");
2936
exitcode = EXIT_FAILURE;
2941
/* Bring up interfaces which are down, and remove any "none"s */
2943
char *interface = NULL;
2944
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2946
/* If interface name is "none", stop bringing up interfaces.
2947
Also remove all instances of "none" from the list */
2948
if(strcmp(interface, "none") == 0){
2949
argz_delete(&mc.interfaces, &mc.interfaces_size,
2952
while((interface = argz_next(mc.interfaces,
2953
mc.interfaces_size, interface))){
2954
if(strcmp(interface, "none") == 0){
2955
argz_delete(&mc.interfaces, &mc.interfaces_size,
2962
bool interface_was_up = interface_is_up(interface);
2963
errno = bring_up_interface(interface, delay);
2964
if(not interface_was_up){
2966
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2967
" %s\n", interface, strerror(errno));
2969
errno = argz_add(&interfaces_to_take_down,
2970
&interfaces_to_take_down_size,
2973
perror_plus("argz_add");
2978
if(debug and (interfaces_to_take_down == NULL)){
2979
fprintf_plus(stderr, "No interfaces were brought up\n");
2983
/* If we only got one interface, explicitly use only that one */
2984
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2986
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2989
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2996
#if GNUTLS_VERSION_NUMBER >= 0x030606
2997
ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
2998
#elif GNUTLS_VERSION_NUMBER < 0x030600
2999
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
3001
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
3004
fprintf_plus(stderr, "init_gnutls_global failed\n");
3005
exitcode = EX_UNAVAILABLE;
3008
gnutls_initialized = true;
3015
/* Try /run/tmp before /tmp */
3016
tempdir = mkdtemp(run_tempdir);
3017
if(tempdir == NULL and errno == ENOENT){
3019
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
3020
run_tempdir, old_tempdir);
3022
tempdir = mkdtemp(old_tempdir);
3024
if(tempdir == NULL){
3025
perror_plus("mkdtemp");
3033
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
3034
fprintf_plus(stderr, "init_gpgme failed\n");
3035
exitcode = EX_UNAVAILABLE;
3038
gpgme_initialized = true;
3045
if(connect_to != NULL){
3046
/* Connect directly, do not use Zeroconf */
3047
/* (Mainly meant for debugging) */
3048
char *address = strrchr(connect_to, ':');
3050
if(address == NULL){
3051
fprintf_plus(stderr, "No colon in address\n");
3052
exitcode = EX_USAGE;
3062
tmpmax = strtoimax(address+1, &tmp, 10);
3063
if(errno != 0 or tmp == address+1 or *tmp != '\0'
3064
or tmpmax != (in_port_t)tmpmax){
3065
fprintf_plus(stderr, "Bad port number\n");
3066
exitcode = EX_USAGE;
3074
port = (in_port_t)tmpmax;
3076
/* Colon in address indicates IPv6 */
3078
if(strchr(connect_to, ':') != NULL){
3080
/* Accept [] around IPv6 address - see RFC 5952 */
3081
if(connect_to[0] == '[' and address[-1] == ']')
3089
address = connect_to;
3095
while(not quit_now){
3096
ret = start_mandos_communication(address, port, if_index, af,
3098
if(quit_now or ret == 0){
3102
fprintf_plus(stderr, "Retrying in %d seconds\n",
3103
(int)retry_interval);
3105
sleep((unsigned int)retry_interval);
3109
exitcode = EXIT_SUCCESS;
688
3120
AvahiServerConfig config;
689
AvahiSServiceBrowser *sb = NULL;
692
int returncode = EXIT_SUCCESS;
693
const char *interface = "eth0";
694
struct ifreq network;
696
char *connect_to = NULL;
697
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
698
mandos_context mc = { .simple_poll = NULL, .server = NULL,
699
.dh_bits = 2048, .priority = "SECURE256"};
702
static struct option long_options[] = {
703
{"debug", no_argument, (int *)&debug, 1},
704
{"connect", required_argument, 0, 'C'},
705
{"interface", required_argument, 0, 'i'},
706
{"certdir", required_argument, 0, 'd'},
707
{"certkey", required_argument, 0, 'c'},
708
{"certfile", required_argument, 0, 'k'},
709
{"dh_bits", required_argument, 0, 'D'},
710
{"priority", required_argument, 0, 'p'},
713
int option_index = 0;
714
ret = getopt_long (argc, argv, "i:", long_options,
743
tmp = strtol(optarg, NULL, 10);
744
if (errno == ERANGE){
752
mc.priority = optarg;
759
certfile = combinepath(certdir, certfile);
760
if (certfile == NULL){
761
perror("combinepath");
762
returncode = EXIT_FAILURE;
766
certkey = combinepath(certdir, certkey);
767
if (certkey == NULL){
768
perror("combinepath");
769
returncode = EXIT_FAILURE;
773
if_index = (AvahiIfIndex) if_nametoindex(interface);
775
fprintf(stderr, "No such interface: \"%s\"\n", interface);
779
if(connect_to != NULL){
780
/* Connect directly, do not use Zeroconf */
781
/* (Mainly meant for debugging) */
782
char *address = strrchr(connect_to, ':');
784
fprintf(stderr, "No colon in address\n");
788
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
790
perror("Bad port number");
794
address = connect_to;
795
ret = start_mandos_communication(address, port, if_index);
803
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
806
returncode = EXIT_FAILURE;
809
strcpy(network.ifr_name, interface);
810
ret = ioctl(sd, SIOCGIFFLAGS, &network);
813
perror("ioctl SIOCGIFFLAGS");
814
returncode = EXIT_FAILURE;
817
if((network.ifr_flags & IFF_UP) == 0){
818
network.ifr_flags |= IFF_UP;
819
ret = ioctl(sd, SIOCSIFFLAGS, &network);
821
perror("ioctl SIOCSIFFLAGS");
822
returncode = EXIT_FAILURE;
829
avahi_set_log_function(empty_log);
832
/* Initialize the psuedo-RNG */
833
srand((unsigned int) time(NULL));
835
/* Allocate main loop object */
836
if (!(mc.simple_poll = avahi_simple_poll_new())) {
837
fprintf(stderr, "Failed to create simple poll object.\n");
838
returncode = EXIT_FAILURE;
842
/* Do not publish any local records */
3121
/* Do not publish any local Zeroconf records */
843
3122
avahi_server_config_init(&config);
844
3123
config.publish_hinfo = 0;
845
3124
config.publish_addresses = 0;
846
3125
config.publish_workstation = 0;
847
3126
config.publish_domain = 0;
849
3128
/* Allocate a new server */
850
3129
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
851
&config, NULL, NULL, &error);
853
/* Free the configuration data */
3130
&config, NULL, NULL, &ret);
3132
/* Free the Avahi configuration data */
854
3133
avahi_server_config_free(&config);
856
/* Check if creating the server object succeeded */
858
fprintf(stderr, "Failed to create server: %s\n",
859
avahi_strerror(error));
860
returncode = EXIT_FAILURE;
864
/* Create the service browser */
865
sb = avahi_s_service_browser_new(mc.server, if_index,
867
"_mandos._tcp", NULL, 0,
868
browse_callback, &mc);
870
fprintf(stderr, "Failed to create service browser: %s\n",
871
avahi_strerror(avahi_server_errno(mc.server)));
872
returncode = EXIT_FAILURE;
876
/* Run the main loop */
879
fprintf(stderr, "Starting avahi loop search\n");
882
avahi_simple_poll_loop(simple_poll);
887
fprintf(stderr, "%s exiting\n", argv[0]);
892
avahi_s_service_browser_free(sb);
895
avahi_server_free(mc.server);
898
avahi_simple_poll_free(simple_poll);
3136
/* Check if creating the Avahi server object succeeded */
3137
if(mc.server == NULL){
3138
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3139
avahi_strerror(ret));
3140
exitcode = EX_UNAVAILABLE;
3148
/* Create the Avahi service browser */
3149
sb = avahi_s_service_browser_new(mc.server, if_index,
3150
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
3151
NULL, 0, browse_callback,
3154
fprintf_plus(stderr, "Failed to create service browser: %s\n",
3155
avahi_strerror(avahi_server_errno(mc.server)));
3156
exitcode = EX_UNAVAILABLE;
3164
/* Run the main loop */
3167
fprintf_plus(stderr, "Starting Avahi loop search\n");
3170
ret = avahi_loop_with_timeout(simple_poll,
3171
(int)(retry_interval * 1000), &mc);
3173
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
3174
(ret == 0) ? "successfully" : "with error");
3180
if(signal_received){
3181
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3182
argv[0], signal_received,
3183
strsignal(signal_received));
3185
fprintf_plus(stderr, "%s exiting\n", argv[0]);
3189
/* Cleanup things */
3190
free(mc.interfaces);
3193
avahi_s_service_browser_free(sb);
3195
if(mc.server != NULL)
3196
avahi_server_free(mc.server);
3198
if(simple_poll != NULL)
3199
avahi_simple_poll_free(simple_poll);
3201
if(gnutls_initialized){
3202
gnutls_certificate_free_credentials(mc.cred);
3203
gnutls_dh_params_deinit(mc.dh_params);
3206
if(gpgme_initialized){
3207
gpgme_release(mc.ctx);
3210
/* Cleans up the circular linked list of Mandos servers the client
3212
if(mc.current_server != NULL){
3213
mc.current_server->prev->next = NULL;
3214
while(mc.current_server != NULL){
3215
server *next = mc.current_server->next;
3217
#pragma GCC diagnostic push
3218
#pragma GCC diagnostic ignored "-Wcast-qual"
3220
free((char *)(mc.current_server->ip));
3222
#pragma GCC diagnostic pop
3224
free(mc.current_server);
3225
mc.current_server = next;
3229
/* Re-raise privileges */
3231
ret = raise_privileges();
3234
perror_plus("Failed to raise privileges");
3237
/* Run network hooks */
3238
run_network_hooks("stop", interfaces_hooks != NULL ?
3239
interfaces_hooks : "", delay);
3241
/* Take down the network interfaces which were brought up */
3243
char *interface = NULL;
3244
while((interface = argz_next(interfaces_to_take_down,
3245
interfaces_to_take_down_size,
3247
ret = take_down_interface(interface);
3250
perror_plus("Failed to take down interface");
3253
if(debug and (interfaces_to_take_down == NULL)){
3254
fprintf_plus(stderr, "No interfaces needed to be taken"
3260
ret = lower_privileges_permanently();
3263
perror_plus("Failed to lower privileges permanently");
3267
free(interfaces_to_take_down);
3268
free(interfaces_hooks);
3270
void clean_dir_at(int base, const char * const dirname,
3272
struct dirent **direntries = NULL;
3274
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3280
perror_plus("open");
3283
int numentries = scandirat(dir_fd, ".", &direntries,
3284
notdotentries, alphasort);
3285
if(numentries >= 0){
3286
for(int i = 0; i < numentries; i++){
3288
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3289
dirname, direntries[i]->d_name);
3291
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3293
if(errno == EISDIR){
3294
dret = unlinkat(dir_fd, direntries[i]->d_name,
3297
if((dret == -1) and (errno == ENOTEMPTY)
3298
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3299
== 0) and (level == 0)){
3300
/* Recurse only in this special case */
3301
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3304
if((dret == -1) and (errno != ENOENT)){
3305
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3306
direntries[i]->d_name, strerror(errno));
3309
free(direntries[i]);
3312
/* need to clean even if 0 because man page doesn't specify */
3314
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3315
if(dret == -1 and errno != ENOENT){
3316
perror_plus("rmdir");
3319
perror_plus("scandirat");
3324
/* Removes the GPGME temp directory and all files inside */
3325
if(tempdir != NULL){
3326
clean_dir_at(-1, tempdir, 0);
3330
sigemptyset(&old_sigterm_action.sa_mask);
3331
old_sigterm_action.sa_handler = SIG_DFL;
3332
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3333
&old_sigterm_action,
3336
perror_plus("sigaction");
3339
ret = raise(signal_received);
3340
} while(ret != 0 and errno == EINTR);
3342
perror_plus("raise");
3345
TEMP_FAILURE_RETRY(pause());