47
111
#include <avahi-common/malloc.h>
48
112
#include <avahi-common/error.h>
51
#include <sys/types.h> /* socket(), inet_pton() */
52
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
53
struct in6_addr, inet_pton() */
54
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
55
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
57
#include <unistd.h> /* close() */
58
#include <netinet/in.h>
59
#include <stdbool.h> /* true */
60
#include <string.h> /* memset */
61
#include <arpa/inet.h> /* inet_pton() */
62
#include <iso646.h> /* not */
65
#include <errno.h> /* perror() */
72
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
74
#define CERTFILE CERT_ROOT "openpgp-client.txt"
75
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
115
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
118
init_gnutls_session(),
120
#include <gnutls/openpgp.h>
121
/* gnutls_certificate_set_openpgp_key_file(),
122
GNUTLS_OPENPGP_FMT_BASE64 */
125
#include <gpgme.h> /* All GPGME types, constants and
128
GPGME_PROTOCOL_OpenPGP,
76
131
#define BUFFER_SIZE 256
133
#define PATHDIR "/conf/conf.d/mandos"
134
#define SECKEY "seckey.txt"
135
#define PUBKEY "pubkey.txt"
136
#define HOOKDIR "/lib/mandos/network-hooks.d"
79
138
bool debug = false;
139
static const char mandos_protocol_version[] = "1";
140
const char *argp_program_version = "mandos-client " VERSION;
141
const char *argp_program_bug_address = "<mandos@recompile.se>";
142
static const char sys_class_net[] = "/sys/class/net";
143
char *connect_to = NULL;
144
const char *hookdir = HOOKDIR;
149
/* Doubly linked list that need to be circularly linked when used */
150
typedef struct server{
153
AvahiIfIndex if_index;
155
struct timespec last_seen;
160
/* Used for passing in values through the Avahi callback functions */
82
gnutls_session_t session;
83
163
gnutls_certificate_credentials_t cred;
164
unsigned int dh_bits;
84
165
gnutls_dh_params_t dh_params;
88
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
char **new_packet, const char *homedir){
90
gpgme_data_t dh_crypto, dh_plain;
166
const char *priority;
168
server *current_server;
170
size_t interfaces_size;
173
/* global so signal handler can reach it*/
174
AvahiSimplePoll *simple_poll;
176
sig_atomic_t quit_now = 0;
177
int signal_received = 0;
179
/* Function to use when printing errors */
180
void perror_plus(const char *print_text){
182
fprintf(stderr, "Mandos plugin %s: ",
183
program_invocation_short_name);
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
189
int fprintf_plus(FILE *stream, const char *format, ...){
191
va_start (ap, format);
193
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
194
program_invocation_short_name));
195
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
199
* Make additional room in "buffer" for at least BUFFER_SIZE more
200
* bytes. "buffer_capacity" is how much is currently allocated,
201
* "buffer_length" is how much is already used.
203
__attribute__((nonnull, warn_unused_result))
204
size_t incbuffer(char **buffer, size_t buffer_length,
205
size_t buffer_capacity){
206
if(buffer_length + BUFFER_SIZE > buffer_capacity){
207
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
209
int old_errno = errno;
216
buffer_capacity += BUFFER_SIZE;
218
return buffer_capacity;
221
/* Add server to set of servers to retry periodically */
222
__attribute__((nonnull, warn_unused_result))
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
224
int af, server **current_server){
226
server *new_server = malloc(sizeof(server));
227
if(new_server == NULL){
228
perror_plus("malloc");
231
*new_server = (server){ .ip = strdup(ip),
233
.if_index = if_index,
235
if(new_server->ip == NULL){
236
perror_plus("strdup");
240
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
242
perror_plus("clock_gettime");
244
#pragma GCC diagnostic push
245
#pragma GCC diagnostic ignored "-Wcast-qual"
247
free((char *)(new_server->ip));
249
#pragma GCC diagnostic pop
254
/* Special case of first server */
255
if(*current_server == NULL){
256
new_server->next = new_server;
257
new_server->prev = new_server;
258
*current_server = new_server;
260
/* Place the new server last in the list */
261
new_server->next = *current_server;
262
new_server->prev = (*current_server)->prev;
263
new_server->prev->next = new_server;
264
(*current_server)->prev = new_server;
272
__attribute__((nonnull, warn_unused_result))
273
static bool init_gpgme(const char * const seckey,
274
const char * const pubkey,
275
const char * const tempdir,
94
ssize_t new_packet_capacity = 0;
95
ssize_t new_packet_length = 0;
96
278
gpgme_engine_info_t engine_info;
99
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
281
* Helper function to insert pub and seckey to the engine keyring.
283
bool import_key(const char * const filename){
286
gpgme_data_t pgp_data;
288
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
294
rc = gpgme_data_new_from_fd(&pgp_data, fd);
295
if(rc != GPG_ERR_NO_ERROR){
296
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
297
gpgme_strsource(rc), gpgme_strerror(rc));
301
rc = gpgme_op_import(mc->ctx, pgp_data);
302
if(rc != GPG_ERR_NO_ERROR){
303
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
304
gpgme_strsource(rc), gpgme_strerror(rc));
310
perror_plus("close");
312
gpgme_data_release(pgp_data);
317
fprintf_plus(stderr, "Initializing GPGME\n");
103
321
gpgme_check_version(NULL);
104
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
322
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
323
if(rc != GPG_ERR_NO_ERROR){
324
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
325
gpgme_strsource(rc), gpgme_strerror(rc));
106
/* Set GPGME home directory */
107
rc = gpgme_get_engine_info (&engine_info);
108
if (rc != GPG_ERR_NO_ERROR){
109
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
110
gpgme_strsource(rc), gpgme_strerror(rc));
329
/* Set GPGME home directory for the OpenPGP engine only */
330
rc = gpgme_get_engine_info(&engine_info);
331
if(rc != GPG_ERR_NO_ERROR){
332
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
333
gpgme_strsource(rc), gpgme_strerror(rc));
113
336
while(engine_info != NULL){
114
337
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
115
338
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
116
engine_info->file_name, homedir);
339
engine_info->file_name, tempdir);
119
342
engine_info = engine_info->next;
121
344
if(engine_info == NULL){
122
fprintf(stderr, "Could not set home dir to %s\n", homedir);
126
/* Create new GPGME data buffer from packet buffer */
127
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
128
if (rc != GPG_ERR_NO_ERROR){
129
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
130
gpgme_strsource(rc), gpgme_strerror(rc));
345
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
350
/* Create new GPGME "context" */
351
rc = gpgme_new(&(mc->ctx));
352
if(rc != GPG_ERR_NO_ERROR){
353
fprintf_plus(stderr, "Mandos plugin mandos-client: "
354
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
359
if(not import_key(pubkey) or not import_key(seckey)){
367
* Decrypt OpenPGP data.
368
* Returns -1 on error
370
__attribute__((nonnull, warn_unused_result))
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
375
gpgme_data_t dh_crypto, dh_plain;
378
size_t plaintext_capacity = 0;
379
ssize_t plaintext_length = 0;
382
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
385
/* Create new GPGME data buffer from memory cryptotext */
386
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
388
if(rc != GPG_ERR_NO_ERROR){
389
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
390
gpgme_strsource(rc), gpgme_strerror(rc));
134
394
/* Create new empty GPGME data buffer for the plaintext */
135
395
rc = gpgme_data_new(&dh_plain);
136
if (rc != GPG_ERR_NO_ERROR){
137
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
138
gpgme_strsource(rc), gpgme_strerror(rc));
142
/* Create new GPGME "context" */
143
rc = gpgme_new(&ctx);
144
if (rc != GPG_ERR_NO_ERROR){
145
fprintf(stderr, "bad gpgme_new: %s: %s\n",
146
gpgme_strsource(rc), gpgme_strerror(rc));
150
/* Decrypt data from the FILE pointer to the plaintext data
152
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
153
if (rc != GPG_ERR_NO_ERROR){
154
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
155
gpgme_strsource(rc), gpgme_strerror(rc));
160
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
164
gpgme_decrypt_result_t result;
165
result = gpgme_op_decrypt_result(ctx);
167
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
169
fprintf(stderr, "Unsupported algorithm: %s\n",
170
result->unsupported_algorithm);
171
fprintf(stderr, "Wrong key usage: %d\n",
172
result->wrong_key_usage);
173
if(result->file_name != NULL){
174
fprintf(stderr, "File name: %s\n", result->file_name);
176
gpgme_recipient_t recipient;
177
recipient = result->recipients;
396
if(rc != GPG_ERR_NO_ERROR){
397
fprintf_plus(stderr, "Mandos plugin mandos-client: "
398
"bad gpgme_data_new: %s: %s\n",
399
gpgme_strsource(rc), gpgme_strerror(rc));
400
gpgme_data_release(dh_crypto);
404
/* Decrypt data from the cryptotext data buffer to the plaintext
406
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
407
if(rc != GPG_ERR_NO_ERROR){
408
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
409
gpgme_strsource(rc), gpgme_strerror(rc));
410
plaintext_length = -1;
412
gpgme_decrypt_result_t result;
413
result = gpgme_op_decrypt_result(mc->ctx);
415
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
417
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
418
result->unsupported_algorithm);
419
fprintf_plus(stderr, "Wrong key usage: %u\n",
420
result->wrong_key_usage);
421
if(result->file_name != NULL){
422
fprintf_plus(stderr, "File name: %s\n", result->file_name);
424
gpgme_recipient_t recipient;
425
recipient = result->recipients;
179
426
while(recipient != NULL){
180
fprintf(stderr, "Public key algorithm: %s\n",
181
gpgme_pubkey_algo_name(recipient->pubkey_algo));
182
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
183
fprintf(stderr, "Secret key available: %s\n",
184
recipient->status == GPG_ERR_NO_SECKEY
427
fprintf_plus(stderr, "Public key algorithm: %s\n",
428
gpgme_pubkey_algo_name
429
(recipient->pubkey_algo));
430
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
431
fprintf_plus(stderr, "Secret key available: %s\n",
432
recipient->status == GPG_ERR_NO_SECKEY
186
434
recipient = recipient->next;
192
/* Delete the GPGME FILE pointer cryptotext data buffer */
193
gpgme_data_release(dh_crypto);
442
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
195
445
/* Seek back to the beginning of the GPGME plaintext data buffer */
196
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
446
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
447
perror_plus("gpgme_data_seek");
448
plaintext_length = -1;
200
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
201
*new_packet = realloc(*new_packet,
202
(unsigned int)new_packet_capacity
204
if (*new_packet == NULL){
208
new_packet_capacity += BUFFER_SIZE;
454
plaintext_capacity = incbuffer(plaintext,
455
(size_t)plaintext_length,
457
if(plaintext_capacity == 0){
458
perror_plus("incbuffer");
459
plaintext_length = -1;
211
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
463
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
213
465
/* Print the data, if any */
218
perror("gpgme_data_read");
221
new_packet_length += ret;
224
/* FIXME: check characters before printing to screen so to not print
225
terminal control characters */
227
/* fprintf(stderr, "decrypted password is: "); */
228
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
229
/* fprintf(stderr, "\n"); */
471
perror_plus("gpgme_data_read");
472
plaintext_length = -1;
475
plaintext_length += ret;
479
fprintf_plus(stderr, "Decrypted password is: ");
480
for(ssize_t i = 0; i < plaintext_length; i++){
481
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
483
fprintf(stderr, "\n");
488
/* Delete the GPGME cryptotext data buffer */
489
gpgme_data_release(dh_crypto);
232
491
/* Delete the GPGME plaintext data buffer */
233
492
gpgme_data_release(dh_plain);
234
return new_packet_length;
237
static const char * safer_gnutls_strerror (int value) {
238
const char *ret = gnutls_strerror (value);
244
void debuggnutls(__attribute__((unused)) int level,
246
fprintf(stderr, "%s", string);
249
int initgnutls(encrypted_session *es){
493
return plaintext_length;
496
__attribute__((warn_unused_result, const))
497
static const char *safe_string(const char *str){
503
__attribute__((warn_unused_result))
504
static const char *safer_gnutls_strerror(int value){
505
const char *ret = gnutls_strerror(value);
506
return safe_string(ret);
509
/* GnuTLS log function callback */
510
__attribute__((nonnull))
511
static void debuggnutls(__attribute__((unused)) int level,
513
fprintf_plus(stderr, "GnuTLS: %s", string);
516
__attribute__((nonnull(1, 2, 4), warn_unused_result))
517
static int init_gnutls_global(const char *pubkeyfilename,
518
const char *seckeyfilename,
519
const char *dhparamsfilename,
254
fprintf(stderr, "Initializing GnuTLS\n");
525
fprintf_plus(stderr, "Initializing GnuTLS\n");
257
if ((ret = gnutls_global_init ())
258
!= GNUTLS_E_SUCCESS) {
259
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
528
ret = gnutls_global_init();
529
if(ret != GNUTLS_E_SUCCESS){
530
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
531
safer_gnutls_strerror(ret));
536
/* "Use a log level over 10 to enable all debugging options."
264
539
gnutls_global_set_log_level(11);
265
540
gnutls_global_set_log_function(debuggnutls);
268
/* openpgp credentials */
269
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
270
!= GNUTLS_E_SUCCESS) {
271
fprintf (stderr, "memory error: %s\n",
272
safer_gnutls_strerror(ret));
543
/* OpenPGP credentials */
544
ret = gnutls_certificate_allocate_credentials(&mc->cred);
545
if(ret != GNUTLS_E_SUCCESS){
546
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
547
safer_gnutls_strerror(ret));
548
gnutls_global_deinit();
277
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
278
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
553
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
554
" secret key %s as GnuTLS credentials\n",
282
559
ret = gnutls_certificate_set_openpgp_key_file
283
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
284
if (ret != GNUTLS_E_SUCCESS) {
286
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
288
ret, CERTFILE, KEYFILE);
289
fprintf(stdout, "The Error is: %s\n",
290
safer_gnutls_strerror(ret));
294
//GnuTLS server initialization
295
if ((ret = gnutls_dh_params_init (&es->dh_params))
296
!= GNUTLS_E_SUCCESS) {
297
fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
safer_gnutls_strerror(ret));
302
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
303
!= GNUTLS_E_SUCCESS) {
304
fprintf (stderr, "Error in prime generation: %s\n",
305
safer_gnutls_strerror(ret));
309
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
311
// GnuTLS session creation
312
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
313
!= GNUTLS_E_SUCCESS){
314
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
315
safer_gnutls_strerror(ret));
318
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
319
!= GNUTLS_E_SUCCESS) {
320
fprintf(stderr, "Syntax error at: %s\n", err);
321
fprintf(stderr, "GnuTLS error: %s\n",
322
safer_gnutls_strerror(ret));
326
if ((ret = gnutls_credentials_set
327
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Error setting a credentials set: %s\n",
330
safer_gnutls_strerror(ret));
560
(mc->cred, pubkeyfilename, seckeyfilename,
561
GNUTLS_OPENPGP_FMT_BASE64);
562
if(ret != GNUTLS_E_SUCCESS){
564
"Error[%d] while reading the OpenPGP key pair ('%s',"
565
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
566
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
567
safer_gnutls_strerror(ret));
571
/* GnuTLS server initialization */
572
ret = gnutls_dh_params_init(&mc->dh_params);
573
if(ret != GNUTLS_E_SUCCESS){
574
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
575
" initialization: %s\n",
576
safer_gnutls_strerror(ret));
579
/* If a Diffie-Hellman parameters file was given, try to use it */
580
if(dhparamsfilename != NULL){
581
gnutls_datum_t params = { .data = NULL, .size = 0 };
583
int dhpfile = open(dhparamsfilename, O_RDONLY);
586
dhparamsfilename = NULL;
589
size_t params_capacity = 0;
591
params_capacity = incbuffer((char **)¶ms.data,
593
(size_t)params_capacity);
594
if(params_capacity == 0){
595
perror_plus("incbuffer");
598
dhparamsfilename = NULL;
601
ssize_t bytes_read = read(dhpfile,
602
params.data + params.size,
608
/* check bytes_read for failure */
613
dhparamsfilename = NULL;
616
params.size += (unsigned int)bytes_read;
618
if(params.data == NULL){
619
dhparamsfilename = NULL;
621
if(dhparamsfilename == NULL){
624
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
625
GNUTLS_X509_FMT_PEM);
626
if(ret != GNUTLS_E_SUCCESS){
627
fprintf_plus(stderr, "Failed to parse DH parameters in file"
628
" \"%s\": %s\n", dhparamsfilename,
629
safer_gnutls_strerror(ret));
630
dhparamsfilename = NULL;
634
if(dhparamsfilename == NULL){
635
if(mc->dh_bits == 0){
636
/* Find out the optimal number of DH bits */
637
/* Try to read the private key file */
638
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
640
int secfile = open(seckeyfilename, O_RDONLY);
645
size_t buffer_capacity = 0;
647
buffer_capacity = incbuffer((char **)&buffer.data,
649
(size_t)buffer_capacity);
650
if(buffer_capacity == 0){
651
perror_plus("incbuffer");
656
ssize_t bytes_read = read(secfile,
657
buffer.data + buffer.size,
663
/* check bytes_read for failure */
670
buffer.size += (unsigned int)bytes_read;
674
/* If successful, use buffer to parse private key */
675
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
676
if(buffer.data != NULL){
678
gnutls_openpgp_privkey_t privkey = NULL;
679
ret = gnutls_openpgp_privkey_init(&privkey);
680
if(ret != GNUTLS_E_SUCCESS){
681
fprintf_plus(stderr, "Error initializing OpenPGP key"
683
safer_gnutls_strerror(ret));
687
ret = gnutls_openpgp_privkey_import
688
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
689
if(ret != GNUTLS_E_SUCCESS){
690
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
691
safer_gnutls_strerror(ret));
697
/* Use private key to suggest an appropriate
699
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
700
gnutls_openpgp_privkey_deinit(privkey);
702
fprintf_plus(stderr, "This OpenPGP key implies using"
703
" a GnuTLS security parameter \"%s\".\n",
704
safe_string(gnutls_sec_param_get_name
710
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
711
/* Err on the side of caution */
712
sec_param = GNUTLS_SEC_PARAM_ULTRA;
714
fprintf_plus(stderr, "Falling back to security parameter"
716
safe_string(gnutls_sec_param_get_name
721
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
725
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
726
" implies %u DH bits; using that.\n",
727
safe_string(gnutls_sec_param_get_name
732
fprintf_plus(stderr, "Failed to get implied number of DH"
733
" bits for security parameter \"%s\"): %s\n",
734
safe_string(gnutls_sec_param_get_name
736
safer_gnutls_strerror(ret));
740
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
743
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
744
if(ret != GNUTLS_E_SUCCESS){
745
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
746
" bits): %s\n", mc->dh_bits,
747
safer_gnutls_strerror(ret));
751
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
757
gnutls_certificate_free_credentials(mc->cred);
758
gnutls_global_deinit();
759
gnutls_dh_params_deinit(mc->dh_params);
763
__attribute__((nonnull, warn_unused_result))
764
static int init_gnutls_session(gnutls_session_t *session,
767
/* GnuTLS session creation */
769
ret = gnutls_init(session, GNUTLS_SERVER);
773
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
774
if(ret != GNUTLS_E_SUCCESS){
776
"Error in GnuTLS session initialization: %s\n",
777
safer_gnutls_strerror(ret));
783
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
785
gnutls_deinit(*session);
788
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
789
if(ret != GNUTLS_E_SUCCESS){
790
fprintf_plus(stderr, "Syntax error at: %s\n", err);
791
fprintf_plus(stderr, "GnuTLS error: %s\n",
792
safer_gnutls_strerror(ret));
793
gnutls_deinit(*session);
799
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
802
gnutls_deinit(*session);
805
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
806
if(ret != GNUTLS_E_SUCCESS){
807
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
808
safer_gnutls_strerror(ret));
809
gnutls_deinit(*session);
334
813
/* ignore client certificate if any. */
335
gnutls_certificate_server_set_request (es->session,
338
gnutls_dh_set_prime_bits (es->session, DH_BITS);
814
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
343
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
__attribute__((unused)) const char *txt){}
346
int start_mandos_communication(const char *ip, uint16_t port,
347
unsigned int if_index){
349
struct sockaddr_in6 to;
350
encrypted_session es;
819
/* Avahi log function callback */
820
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
821
__attribute__((unused)) const char *txt){}
823
/* Set effective uid to 0, return errno */
824
__attribute__((warn_unused_result))
825
error_t raise_privileges(void){
826
error_t old_errno = errno;
827
error_t ret_errno = 0;
828
if(seteuid(0) == -1){
835
/* Set effective and real user ID to 0. Return errno. */
836
__attribute__((warn_unused_result))
837
error_t raise_privileges_permanently(void){
838
error_t old_errno = errno;
839
error_t ret_errno = raise_privileges();
851
/* Set effective user ID to unprivileged saved user ID */
852
__attribute__((warn_unused_result))
853
error_t lower_privileges(void){
854
error_t old_errno = errno;
855
error_t ret_errno = 0;
856
if(seteuid(uid) == -1){
863
/* Lower privileges permanently */
864
__attribute__((warn_unused_result))
865
error_t lower_privileges_permanently(void){
866
error_t old_errno = errno;
867
error_t ret_errno = 0;
868
if(setuid(uid) == -1){
875
/* Helper function to add_local_route() and delete_local_route() */
876
__attribute__((nonnull, warn_unused_result))
877
static bool add_delete_local_route(const bool add,
879
AvahiIfIndex if_index){
881
char helper[] = "mandos-client-iprouteadddel";
882
char add_arg[] = "add";
883
char delete_arg[] = "delete";
884
char debug_flag[] = "--debug";
885
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
886
if(pluginhelperdir == NULL){
888
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
889
" variable not set; cannot run helper\n");
894
char interface[IF_NAMESIZE];
895
if(if_indextoname((unsigned int)if_index, interface) == NULL){
896
perror_plus("if_indextoname");
900
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
902
perror_plus("open(\"/dev/null\", O_RDONLY)");
908
/* Raise privileges */
909
errno = raise_privileges_permanently();
911
perror_plus("Failed to raise privileges");
912
/* _exit(EX_NOPERM); */
918
perror_plus("setgid");
921
/* Reset supplementary groups */
923
ret = setgroups(0, NULL);
925
perror_plus("setgroups");
929
ret = dup2(devnull, STDIN_FILENO);
931
perror_plus("dup2(devnull, STDIN_FILENO)");
934
ret = close(devnull);
936
perror_plus("close");
939
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
941
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
944
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
949
if(helperdir_fd == -1){
951
_exit(EX_UNAVAILABLE);
953
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
956
perror_plus("openat");
958
_exit(EX_UNAVAILABLE);
962
#pragma GCC diagnostic push
963
#pragma GCC diagnostic ignored "-Wcast-qual"
965
if(fexecve(helper_fd, (char *const [])
966
{ helper, add ? add_arg : delete_arg, (char *)address,
967
interface, debug ? debug_flag : NULL, NULL },
970
#pragma GCC diagnostic pop
972
perror_plus("fexecve");
984
pret = waitpid(pid, &status, 0);
985
if(pret == -1 and errno == EINTR and quit_now){
986
int errno_raising = 0;
987
if((errno = raise_privileges()) != 0){
988
errno_raising = errno;
989
perror_plus("Failed to raise privileges in order to"
990
" kill helper program");
992
if(kill(pid, SIGTERM) == -1){
995
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
996
perror_plus("Failed to lower privileges after killing"
1001
} while(pret == -1 and errno == EINTR);
1003
perror_plus("waitpid");
1006
if(WIFEXITED(status)){
1007
if(WEXITSTATUS(status) != 0){
1008
fprintf_plus(stderr, "Error: iprouteadddel exited"
1009
" with status %d\n", WEXITSTATUS(status));
1014
if(WIFSIGNALED(status)){
1015
fprintf_plus(stderr, "Error: iprouteadddel died by"
1016
" signal %d\n", WTERMSIG(status));
1019
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1023
__attribute__((nonnull, warn_unused_result))
1024
static bool add_local_route(const char *address,
1025
AvahiIfIndex if_index){
1027
fprintf_plus(stderr, "Adding route to %s\n", address);
1029
return add_delete_local_route(true, address, if_index);
1032
__attribute__((nonnull, warn_unused_result))
1033
static bool delete_local_route(const char *address,
1034
AvahiIfIndex if_index){
1036
fprintf_plus(stderr, "Removing route to %s\n", address);
1038
return add_delete_local_route(false, address, if_index);
1041
/* Called when a Mandos server is found */
1042
__attribute__((nonnull, warn_unused_result))
1043
static int start_mandos_communication(const char *ip, in_port_t port,
1044
AvahiIfIndex if_index,
1045
int af, mandos_context *mc){
1046
int ret, tcp_sd = -1;
1048
struct sockaddr_storage to;
351
1049
char *buffer = NULL;
352
char *decrypted_buffer;
1050
char *decrypted_buffer = NULL;
353
1051
size_t buffer_length = 0;
354
1052
size_t buffer_capacity = 0;
355
ssize_t decrypted_buffer_size;
358
char interface[IF_NAMESIZE];
361
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
364
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
370
if(if_indextoname(if_index, interface) == NULL){
372
perror("if_indextoname");
378
fprintf(stderr, "Binding to interface %s\n", interface);
381
memset(&to,0,sizeof(to)); /* Spurious warning */
382
to.sin6_family = AF_INET6;
383
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
1055
gnutls_session_t session;
1056
int pf; /* Protocol family */
1057
bool route_added = false;
1074
fprintf_plus(stderr, "Bad address family: %d\n", af);
1079
/* If the interface is specified and we have a list of interfaces */
1080
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1081
/* Check if the interface is one of the interfaces we are using */
1084
char *interface = NULL;
1085
while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1087
if(if_nametoindex(interface) == (unsigned int)if_index){
1094
/* This interface does not match any in the list, so we don't
1095
connect to the server */
1097
char interface[IF_NAMESIZE];
1098
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1099
perror_plus("if_indextoname");
1101
fprintf_plus(stderr, "Skipping server on non-used interface"
1103
if_indextoname((unsigned int)if_index,
1111
ret = init_gnutls_session(&session, mc);
1117
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1118
PRIuMAX "\n", ip, (uintmax_t)port);
1121
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1124
perror_plus("socket");
1135
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1136
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1137
ret = inet_pton(af, ip, &to6->sin6_addr);
1139
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1140
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1141
ret = inet_pton(af, ip, &to4->sin_addr);
1145
perror_plus("inet_pton");
389
fprintf(stderr, "Bad address: %s\n", ip);
392
to.sin6_port = htons(port); /* Spurious warning */
394
to.sin6_scope_id = (uint32_t)if_index;
397
fprintf(stderr, "Connection to: %s\n", ip);
400
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
406
ret = initgnutls (&es);
412
gnutls_transport_set_ptr (es.session,
413
(gnutls_transport_ptr_t) tcp_sd);
416
fprintf(stderr, "Establishing TLS session with %s\n", ip);
419
ret = gnutls_handshake (es.session);
421
if (ret != GNUTLS_E_SUCCESS){
1151
fprintf_plus(stderr, "Bad address: %s\n", ip);
1156
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1157
if(IN6_IS_ADDR_LINKLOCAL
1158
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1159
if(if_index == AVAHI_IF_UNSPEC){
1160
fprintf_plus(stderr, "An IPv6 link-local address is"
1161
" incomplete without a network interface\n");
1165
/* Set the network interface number as scope */
1166
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1169
((struct sockaddr_in *)&to)->sin_port = htons(port);
1178
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1179
char interface[IF_NAMESIZE];
1180
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1181
perror_plus("if_indextoname");
1183
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1184
"\n", ip, interface, (uintmax_t)port);
1187
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1188
ip, (uintmax_t)port);
1190
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1191
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1193
ret = getnameinfo((struct sockaddr *)&to,
1194
sizeof(struct sockaddr_in6),
1195
addrstr, sizeof(addrstr), NULL, 0,
1198
ret = getnameinfo((struct sockaddr *)&to,
1199
sizeof(struct sockaddr_in),
1200
addrstr, sizeof(addrstr), NULL, 0,
1203
if(ret == EAI_SYSTEM){
1204
perror_plus("getnameinfo");
1205
} else if(ret != 0) {
1206
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1207
} else if(strcmp(addrstr, ip) != 0){
1208
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1219
ret = connect(tcp_sd, (struct sockaddr *)&to,
1220
sizeof(struct sockaddr_in6));
1222
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1223
sizeof(struct sockaddr_in));
1226
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1227
and if_index != AVAHI_IF_UNSPEC
1228
and connect_to == NULL
1229
and not route_added and
1230
((af == AF_INET6 and not
1231
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1233
or (af == AF_INET and
1234
/* Not a a IPv4LL address */
1235
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1236
& 0xFFFF0000L) != 0xA9FE0000L))){
1237
/* Work around Avahi bug - Avahi does not announce link-local
1238
addresses if it has a global address, so local hosts with
1239
*only* a link-local address (e.g. Mandos clients) cannot
1240
connect to a Mandos server announced by Avahi on a server
1241
host with a global address. Work around this by retrying
1242
with an explicit route added with the server's address.
1244
Avahi bug reference:
1245
http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1246
https://bugs.debian.org/587961
1249
fprintf_plus(stderr, "Mandos server unreachable, trying"
1253
route_added = add_local_route(ip, if_index);
1259
if(errno != ECONNREFUSED or debug){
1261
perror_plus("connect");
1274
const char *out = mandos_protocol_version;
1277
size_t out_size = strlen(out);
1278
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1279
out_size - written));
1282
perror_plus("write");
1286
written += (size_t)ret;
1287
if(written < out_size){
1290
if(out == mandos_protocol_version){
1305
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1313
/* This casting via intptr_t is to eliminate warning about casting
1314
an int to a pointer type. This is exactly how the GnuTLS Guile
1315
function "set-session-transport-fd!" does it. */
1316
gnutls_transport_set_ptr(session,
1317
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1325
ret = gnutls_handshake(session);
1330
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1332
if(ret != GNUTLS_E_SUCCESS){
423
fprintf(stderr, "\n*** Handshake failed ***\n");
1334
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
430
//Retrieve OpenPGP packet that contains the wanted password
1341
/* Read OpenPGP packet that contains the wanted password */
433
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1344
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
438
if (buffer_length + BUFFER_SIZE > buffer_capacity){
439
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
444
buffer_capacity += BUFFER_SIZE;
447
ret = gnutls_record_recv
448
(es.session, buffer+buffer_length, BUFFER_SIZE);
1355
buffer_capacity = incbuffer(&buffer, buffer_length,
1357
if(buffer_capacity == 0){
1359
perror_plus("incbuffer");
1369
sret = gnutls_record_recv(session, buffer+buffer_length,
454
1376
case GNUTLS_E_INTERRUPTED:
455
1377
case GNUTLS_E_AGAIN:
457
1379
case GNUTLS_E_REHANDSHAKE:
458
ret = gnutls_handshake (es.session);
460
fprintf(stderr, "\n*** Handshake failed ***\n");
1381
ret = gnutls_handshake(session);
1387
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1389
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
467
fprintf(stderr, "Unknown error while reading data from"
468
" encrypted session with mandos server\n");
470
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1397
fprintf_plus(stderr, "Unknown error while reading data from"
1398
" encrypted session with Mandos server\n");
1399
gnutls_bye(session, GNUTLS_SHUT_RDWR);
474
buffer_length += (size_t) ret;
478
if (buffer_length > 0){
479
decrypted_buffer_size = pgp_packet_decrypt(buffer,
483
if (decrypted_buffer_size >= 0){
484
while(written < decrypted_buffer_size){
485
ret = (int)fwrite (decrypted_buffer + written, 1,
486
(size_t)decrypted_buffer_size - written,
1404
buffer_length += (size_t) sret;
1409
fprintf_plus(stderr, "Closing TLS session\n");
1418
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1423
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1425
if(buffer_length > 0){
1426
ssize_t decrypted_buffer_size;
1427
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1428
&decrypted_buffer, mc);
1429
if(decrypted_buffer_size >= 0){
1432
while(written < (size_t) decrypted_buffer_size){
1438
ret = (int)fwrite(decrypted_buffer + written, 1,
1439
(size_t)decrypted_buffer_size - written,
488
1441
if(ret == 0 and ferror(stdout)){
490
fprintf(stderr, "Error writing encrypted data: %s\n",
1444
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
496
1450
written += (size_t)ret;
498
free(decrypted_buffer);
1456
/* Shutdown procedure */
1461
if(not delete_local_route(ip, if_index)){
1462
fprintf_plus(stderr, "Failed to delete local route to %s on"
1463
" interface %d", ip, if_index);
1467
free(decrypted_buffer);
1470
ret = close(tcp_sd);
1476
perror_plus("close");
1478
gnutls_deinit(session);
507
fprintf(stderr, "Closing TLS session\n");
511
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
514
gnutls_deinit (es.session);
515
gnutls_certificate_free_credentials (es.cred);
516
gnutls_global_deinit ();
520
static AvahiSimplePoll *simple_poll = NULL;
521
static AvahiServer *server = NULL;
523
static void resolve_callback(
524
AvahiSServiceResolver *r,
525
AvahiIfIndex interface,
526
AVAHI_GCC_UNUSED AvahiProtocol protocol,
527
AvahiResolverEvent event,
531
const char *host_name,
532
const AvahiAddress *address,
534
AVAHI_GCC_UNUSED AvahiStringList *txt,
535
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
536
AVAHI_GCC_UNUSED void* userdata) {
538
assert(r); /* Spurious warning */
1488
__attribute__((nonnull))
1489
static void resolve_callback(AvahiSServiceResolver *r,
1490
AvahiIfIndex interface,
1491
AvahiProtocol proto,
1492
AvahiResolverEvent event,
1496
const char *host_name,
1497
const AvahiAddress *address,
1499
AVAHI_GCC_UNUSED AvahiStringList *txt,
1500
AVAHI_GCC_UNUSED AvahiLookupResultFlags
540
1507
/* Called whenever a service has been resolved successfully or
1511
avahi_s_service_resolver_free(r);
545
1517
case AVAHI_RESOLVER_FAILURE:
546
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
547
" type '%s' in domain '%s': %s\n", name, type, domain,
548
avahi_strerror(avahi_server_errno(server)));
1518
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1519
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1521
avahi_strerror(avahi_server_errno
1522
(((mandos_context*)mc)->server)));
551
1525
case AVAHI_RESOLVER_FOUND:
553
1527
char ip[AVAHI_ADDRESS_STR_MAX];
554
1528
avahi_address_snprint(ip, sizeof(ip), address);
556
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
557
" port %d\n", name, host_name, ip, port);
1530
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1531
PRIdMAX ") on port %" PRIu16 "\n", name,
1532
host_name, ip, (intmax_t)interface, port);
559
int ret = start_mandos_communication(ip, port,
560
(unsigned int) interface);
1534
int ret = start_mandos_communication(ip, (in_port_t)port,
1536
avahi_proto_to_af(proto),
1539
avahi_simple_poll_quit(simple_poll);
1541
if(not add_server(ip, (in_port_t)port, interface,
1542
avahi_proto_to_af(proto),
1543
&((mandos_context*)mc)->current_server)){
1544
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
566
1550
avahi_s_service_resolver_free(r);
569
static void browse_callback(
570
AvahiSServiceBrowser *b,
571
AvahiIfIndex interface,
572
AvahiProtocol protocol,
573
AvahiBrowserEvent event,
577
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
580
AvahiServer *s = userdata;
581
assert(b); /* Spurious warning */
583
/* Called whenever a new services becomes available on the LAN or
584
is removed from the LAN */
1553
static void browse_callback(AvahiSServiceBrowser *b,
1554
AvahiIfIndex interface,
1555
AvahiProtocol protocol,
1556
AvahiBrowserEvent event,
1560
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1567
/* Called whenever a new services becomes available on the LAN or
1568
is removed from the LAN */
1576
case AVAHI_BROWSER_FAILURE:
1578
fprintf_plus(stderr, "(Avahi browser) %s\n",
1579
avahi_strerror(avahi_server_errno
1580
(((mandos_context*)mc)->server)));
1581
avahi_simple_poll_quit(simple_poll);
1584
case AVAHI_BROWSER_NEW:
1585
/* We ignore the returned Avahi resolver object. In the callback
1586
function we free it. If the Avahi server is terminated before
1587
the callback function is called the Avahi server will free the
1590
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1591
interface, protocol, name, type,
1592
domain, protocol, 0,
1593
resolve_callback, mc) == NULL)
1594
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1596
avahi_strerror(avahi_server_errno
1597
(((mandos_context*)mc)->server)));
1600
case AVAHI_BROWSER_REMOVE:
1603
case AVAHI_BROWSER_ALL_FOR_NOW:
1604
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1606
fprintf_plus(stderr, "No Mandos server found, still"
1613
/* Signal handler that stops main loop after SIGTERM */
1614
static void handle_sigterm(int sig){
1619
signal_received = sig;
1620
int old_errno = errno;
1621
/* set main loop to exit */
1622
if(simple_poll != NULL){
1623
avahi_simple_poll_quit(simple_poll);
1628
__attribute__((nonnull, warn_unused_result))
1629
bool get_flags(const char *ifname, struct ifreq *ifr){
1633
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1636
perror_plus("socket");
1640
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1641
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1642
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1646
perror_plus("ioctl SIOCGIFFLAGS");
1654
__attribute__((nonnull, warn_unused_result))
1655
bool good_flags(const char *ifname, const struct ifreq *ifr){
1657
/* Reject the loopback device */
1658
if(ifr->ifr_flags & IFF_LOOPBACK){
1660
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1665
/* Accept point-to-point devices only if connect_to is specified */
1666
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1668
fprintf_plus(stderr, "Accepting point-to-point interface"
1669
" \"%s\"\n", ifname);
1673
/* Otherwise, reject non-broadcast-capable devices */
1674
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1676
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1677
" \"%s\"\n", ifname);
1681
/* Reject non-ARP interfaces (including dummy interfaces) */
1682
if(ifr->ifr_flags & IFF_NOARP){
1684
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1690
/* Accept this device */
1692
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1698
* This function determines if a directory entry in /sys/class/net
1699
* corresponds to an acceptable network device.
1700
* (This function is passed to scandir(3) as a filter function.)
1702
__attribute__((nonnull, warn_unused_result))
1703
int good_interface(const struct dirent *if_entry){
1704
if(if_entry->d_name[0] == '.'){
1709
if(not get_flags(if_entry->d_name, &ifr)){
1711
fprintf_plus(stderr, "Failed to get flags for interface "
1712
"\"%s\"\n", if_entry->d_name);
1717
if(not good_flags(if_entry->d_name, &ifr)){
1724
* This function determines if a network interface is up.
1726
__attribute__((nonnull, warn_unused_result))
1727
bool interface_is_up(const char *interface){
1729
if(not get_flags(interface, &ifr)){
1731
fprintf_plus(stderr, "Failed to get flags for interface "
1732
"\"%s\"\n", interface);
1737
return (bool)(ifr.ifr_flags & IFF_UP);
1741
* This function determines if a network interface is running
1743
__attribute__((nonnull, warn_unused_result))
1744
bool interface_is_running(const char *interface){
1746
if(not get_flags(interface, &ifr)){
1748
fprintf_plus(stderr, "Failed to get flags for interface "
1749
"\"%s\"\n", interface);
1754
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1757
__attribute__((nonnull, pure, warn_unused_result))
1758
int notdotentries(const struct dirent *direntry){
1759
/* Skip "." and ".." */
1760
if(direntry->d_name[0] == '.'
1761
and (direntry->d_name[1] == '\0'
1762
or (direntry->d_name[1] == '.'
1763
and direntry->d_name[2] == '\0'))){
1769
/* Is this directory entry a runnable program? */
1770
__attribute__((nonnull, warn_unused_result))
1771
int runnable_hook(const struct dirent *direntry){
1776
if((direntry->d_name)[0] == '\0'){
1781
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1782
"abcdefghijklmnopqrstuvwxyz"
1785
if((direntry->d_name)[sret] != '\0'){
1786
/* Contains non-allowed characters */
1788
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1794
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1797
perror_plus("Could not stat hook");
1801
if(not (S_ISREG(st.st_mode))){
1802
/* Not a regular file */
1804
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1809
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1810
/* Not executable */
1812
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1818
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1824
__attribute__((nonnull, warn_unused_result))
1825
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1826
mandos_context *mc){
1828
struct timespec now;
1829
struct timespec waited_time;
1830
intmax_t block_time;
1833
if(mc->current_server == NULL){
1835
fprintf_plus(stderr, "Wait until first server is found."
1838
ret = avahi_simple_poll_iterate(s, -1);
1841
fprintf_plus(stderr, "Check current_server if we should run"
1844
/* the current time */
1845
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1847
perror_plus("clock_gettime");
1850
/* Calculating in ms how long time between now and server
1851
who we visted longest time ago. Now - last seen. */
1852
waited_time.tv_sec = (now.tv_sec
1853
- mc->current_server->last_seen.tv_sec);
1854
waited_time.tv_nsec = (now.tv_nsec
1855
- mc->current_server->last_seen.tv_nsec);
1856
/* total time is 10s/10,000ms.
1857
Converting to s from ms by dividing by 1,000,
1858
and ns to ms by dividing by 1,000,000. */
1859
block_time = ((retry_interval
1860
- ((intmax_t)waited_time.tv_sec * 1000))
1861
- ((intmax_t)waited_time.tv_nsec / 1000000));
1864
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1868
if(block_time <= 0){
1869
ret = start_mandos_communication(mc->current_server->ip,
1870
mc->current_server->port,
1871
mc->current_server->if_index,
1872
mc->current_server->af, mc);
1874
avahi_simple_poll_quit(s);
1877
ret = clock_gettime(CLOCK_MONOTONIC,
1878
&mc->current_server->last_seen);
1880
perror_plus("clock_gettime");
1883
mc->current_server = mc->current_server->next;
1884
block_time = 0; /* Call avahi to find new Mandos
1885
servers, but don't block */
1888
ret = avahi_simple_poll_iterate(s, (int)block_time);
1891
if(ret > 0 or errno != EINTR){
1892
return (ret != 1) ? ret : 0;
1898
__attribute__((nonnull))
1899
void run_network_hooks(const char *mode, const char *interface,
1901
struct dirent **direntries = NULL;
1902
if(hookdir_fd == -1){
1903
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1905
if(hookdir_fd == -1){
1906
if(errno == ENOENT){
1908
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1909
" found\n", hookdir);
1912
perror_plus("open");
1917
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1918
runnable_hook, alphasort);
1920
perror_plus("scandir");
1923
struct dirent *direntry;
1925
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1927
perror_plus("open(\"/dev/null\", O_RDONLY)");
1930
for(int i = 0; i < numhooks; i++){
1931
direntry = direntries[i];
1933
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1936
pid_t hook_pid = fork();
1939
/* Raise privileges */
1940
errno = raise_privileges_permanently();
1942
perror_plus("Failed to raise privileges");
1949
perror_plus("setgid");
1952
/* Reset supplementary groups */
1954
ret = setgroups(0, NULL);
1956
perror_plus("setgroups");
1959
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1961
perror_plus("setenv");
1964
ret = setenv("DEVICE", interface, 1);
1966
perror_plus("setenv");
1969
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1971
perror_plus("setenv");
1974
ret = setenv("MODE", mode, 1);
1976
perror_plus("setenv");
1980
ret = asprintf(&delaystring, "%f", (double)delay);
1982
perror_plus("asprintf");
1985
ret = setenv("DELAY", delaystring, 1);
1988
perror_plus("setenv");
1992
if(connect_to != NULL){
1993
ret = setenv("CONNECT", connect_to, 1);
1995
perror_plus("setenv");
1999
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2003
perror_plus("openat");
2004
_exit(EXIT_FAILURE);
2006
if(close(hookdir_fd) == -1){
2007
perror_plus("close");
2008
_exit(EXIT_FAILURE);
2010
ret = dup2(devnull, STDIN_FILENO);
2012
perror_plus("dup2(devnull, STDIN_FILENO)");
2015
ret = close(devnull);
2017
perror_plus("close");
2020
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2022
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2025
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2027
perror_plus("fexecve");
2028
_exit(EXIT_FAILURE);
2032
perror_plus("fork");
2037
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2038
perror_plus("waitpid");
2042
if(WIFEXITED(status)){
2043
if(WEXITSTATUS(status) != 0){
2044
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2045
" with status %d\n", direntry->d_name,
2046
WEXITSTATUS(status));
2050
} else if(WIFSIGNALED(status)){
2051
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2052
" signal %d\n", direntry->d_name,
2057
fprintf_plus(stderr, "Warning: network hook \"%s\""
2058
" crashed\n", direntry->d_name);
2064
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2070
if(close(hookdir_fd) == -1){
2071
perror_plus("close");
2078
__attribute__((nonnull, warn_unused_result))
2079
error_t bring_up_interface(const char *const interface,
2081
error_t old_errno = errno;
2083
struct ifreq network;
2084
unsigned int if_index = if_nametoindex(interface);
2086
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2096
if(not interface_is_up(interface)){
2097
error_t ret_errno = 0, ioctl_errno = 0;
2098
if(not get_flags(interface, &network)){
2100
fprintf_plus(stderr, "Failed to get flags for interface "
2101
"\"%s\"\n", interface);
2105
network.ifr_flags |= IFF_UP; /* set flag */
2107
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2110
perror_plus("socket");
2118
perror_plus("close");
2125
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2129
/* Raise privileges */
2130
ret_errno = raise_privileges();
2133
perror_plus("Failed to raise privileges");
2138
bool restore_loglevel = false;
2140
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2141
messages about the network interface to mess up the prompt */
2142
ret_linux = klogctl(8, NULL, 5);
2143
if(ret_linux == -1){
2144
perror_plus("klogctl");
2146
restore_loglevel = true;
2149
#endif /* __linux__ */
2150
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2151
ioctl_errno = errno;
2153
if(restore_loglevel){
2154
ret_linux = klogctl(7, NULL, 0);
2155
if(ret_linux == -1){
2156
perror_plus("klogctl");
2159
#endif /* __linux__ */
2161
/* If raise_privileges() succeeded above */
2163
/* Lower privileges */
2164
ret_errno = lower_privileges();
2167
perror_plus("Failed to lower privileges");
2171
/* Close the socket */
2174
perror_plus("close");
2177
if(ret_setflags == -1){
2178
errno = ioctl_errno;
2179
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2184
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2188
/* Sleep checking until interface is running.
2189
Check every 0.25s, up to total time of delay */
2190
for(int i=0; i < delay * 4; i++){
2191
if(interface_is_running(interface)){
2194
struct timespec sleeptime = { .tv_nsec = 250000000 };
2195
ret = nanosleep(&sleeptime, NULL);
2196
if(ret == -1 and errno != EINTR){
2197
perror_plus("nanosleep");
2205
__attribute__((nonnull, warn_unused_result))
2206
error_t take_down_interface(const char *const interface){
2207
error_t old_errno = errno;
2208
struct ifreq network;
2209
unsigned int if_index = if_nametoindex(interface);
2211
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2215
if(interface_is_up(interface)){
2216
error_t ret_errno = 0, ioctl_errno = 0;
2217
if(not get_flags(interface, &network) and debug){
2219
fprintf_plus(stderr, "Failed to get flags for interface "
2220
"\"%s\"\n", interface);
2224
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2226
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2229
perror_plus("socket");
2235
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2239
/* Raise privileges */
2240
ret_errno = raise_privileges();
2243
perror_plus("Failed to raise privileges");
2246
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2247
ioctl_errno = errno;
2249
/* If raise_privileges() succeeded above */
2251
/* Lower privileges */
2252
ret_errno = lower_privileges();
2255
perror_plus("Failed to lower privileges");
2259
/* Close the socket */
2260
int ret = close(sd);
2262
perror_plus("close");
2265
if(ret_setflags == -1){
2266
errno = ioctl_errno;
2267
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2272
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2280
int main(int argc, char *argv[]){
2281
mandos_context mc = { .server = NULL, .dh_bits = 0,
2282
.priority = "SECURE256:!CTYPE-X.509"
2283
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2284
.current_server = NULL, .interfaces = NULL,
2285
.interfaces_size = 0 };
2286
AvahiSServiceBrowser *sb = NULL;
2291
int exitcode = EXIT_SUCCESS;
2292
char *interfaces_to_take_down = NULL;
2293
size_t interfaces_to_take_down_size = 0;
2294
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2295
char old_tempdir[] = "/tmp/mandosXXXXXX";
2296
char *tempdir = NULL;
2297
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2298
const char *seckey = PATHDIR "/" SECKEY;
2299
const char *pubkey = PATHDIR "/" PUBKEY;
2300
const char *dh_params_file = NULL;
2301
char *interfaces_hooks = NULL;
2303
bool gnutls_initialized = false;
2304
bool gpgme_initialized = false;
2306
double retry_interval = 10; /* 10s between trying a server and
2307
retrying the same server again */
2309
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2310
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2315
/* Lower any group privileges we might have, just to be safe */
2319
perror_plus("setgid");
2322
/* Lower user privileges (temporarily) */
2326
perror_plus("seteuid");
2334
struct argp_option options[] = {
2335
{ .name = "debug", .key = 128,
2336
.doc = "Debug mode", .group = 3 },
2337
{ .name = "connect", .key = 'c',
2338
.arg = "ADDRESS:PORT",
2339
.doc = "Connect directly to a specific Mandos server",
2341
{ .name = "interface", .key = 'i',
2343
.doc = "Network interface that will be used to search for"
2346
{ .name = "seckey", .key = 's',
2348
.doc = "OpenPGP secret key file base name",
2350
{ .name = "pubkey", .key = 'p',
2352
.doc = "OpenPGP public key file base name",
2354
{ .name = "dh-bits", .key = 129,
2356
.doc = "Bit length of the prime number used in the"
2357
" Diffie-Hellman key exchange",
2359
{ .name = "dh-params", .key = 134,
2361
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2362
" for the Diffie-Hellman key exchange",
2364
{ .name = "priority", .key = 130,
2366
.doc = "GnuTLS priority string for the TLS handshake",
2368
{ .name = "delay", .key = 131,
2370
.doc = "Maximum delay to wait for interface startup",
2372
{ .name = "retry", .key = 132,
2374
.doc = "Retry interval used when denied by the Mandos server",
2376
{ .name = "network-hook-dir", .key = 133,
2378
.doc = "Directory where network hooks are located",
2381
* These reproduce what we would get without ARGP_NO_HELP
2383
{ .name = "help", .key = '?',
2384
.doc = "Give this help list", .group = -1 },
2385
{ .name = "usage", .key = -3,
2386
.doc = "Give a short usage message", .group = -1 },
2387
{ .name = "version", .key = 'V',
2388
.doc = "Print program version", .group = -1 },
2392
error_t parse_opt(int key, char *arg,
2393
struct argp_state *state){
2396
case 128: /* --debug */
2399
case 'c': /* --connect */
2402
case 'i': /* --interface */
2403
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2406
argp_error(state, "%s", strerror(ret_errno));
2409
case 's': /* --seckey */
2412
case 'p': /* --pubkey */
2415
case 129: /* --dh-bits */
2417
tmpmax = strtoimax(arg, &tmp, 10);
2418
if(errno != 0 or tmp == arg or *tmp != '\0'
2419
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2420
argp_error(state, "Bad number of DH bits");
2422
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2424
case 134: /* --dh-params */
2425
dh_params_file = arg;
2427
case 130: /* --priority */
2430
case 131: /* --delay */
2432
delay = strtof(arg, &tmp);
2433
if(errno != 0 or tmp == arg or *tmp != '\0'){
2434
argp_error(state, "Bad delay");
2436
case 132: /* --retry */
2438
retry_interval = strtod(arg, &tmp);
2439
if(errno != 0 or tmp == arg or *tmp != '\0'
2440
or (retry_interval * 1000) > INT_MAX
2441
or retry_interval < 0){
2442
argp_error(state, "Bad retry interval");
2445
case 133: /* --network-hook-dir */
2449
* These reproduce what we would get without ARGP_NO_HELP
2451
case '?': /* --help */
2452
argp_state_help(state, state->out_stream,
2453
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2454
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2455
case -3: /* --usage */
2456
argp_state_help(state, state->out_stream,
2457
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2458
case 'V': /* --version */
2459
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2460
exit(argp_err_exit_status);
2463
return ARGP_ERR_UNKNOWN;
2468
struct argp argp = { .options = options, .parser = parse_opt,
2470
.doc = "Mandos client -- Get and decrypt"
2471
" passwords from a Mandos server" };
2472
ret = argp_parse(&argp, argc, argv,
2473
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
588
case AVAHI_BROWSER_FAILURE:
590
fprintf(stderr, "(Browser) %s\n",
591
avahi_strerror(avahi_server_errno(server)));
592
avahi_simple_poll_quit(simple_poll);
595
case AVAHI_BROWSER_NEW:
596
/* We ignore the returned resolver object. In the callback
597
function we free it. If the server is terminated before
598
the callback function is called the server will free
599
the resolver for us. */
601
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
603
AVAHI_PROTO_INET6, 0,
604
resolve_callback, s)))
605
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
606
avahi_strerror(avahi_server_errno(s)));
609
case AVAHI_BROWSER_REMOVE:
612
case AVAHI_BROWSER_ALL_FOR_NOW:
613
case AVAHI_BROWSER_CACHE_EXHAUSTED:
618
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
2480
perror_plus("argp_parse");
2481
exitcode = EX_OSERR;
2484
exitcode = EX_USAGE;
2490
/* Work around Debian bug #633582:
2491
<http://bugs.debian.org/633582> */
2493
/* Re-raise privileges */
2494
ret_errno = raise_privileges();
2497
perror_plus("Failed to raise privileges");
2501
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2502
int seckey_fd = open(seckey, O_RDONLY);
2503
if(seckey_fd == -1){
2504
perror_plus("open");
2506
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2508
perror_plus("fstat");
2510
if(S_ISREG(st.st_mode)
2511
and st.st_uid == 0 and st.st_gid == 0){
2512
ret = fchown(seckey_fd, uid, gid);
2514
perror_plus("fchown");
2522
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2523
int pubkey_fd = open(pubkey, O_RDONLY);
2524
if(pubkey_fd == -1){
2525
perror_plus("open");
2527
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2529
perror_plus("fstat");
2531
if(S_ISREG(st.st_mode)
2532
and st.st_uid == 0 and st.st_gid == 0){
2533
ret = fchown(pubkey_fd, uid, gid);
2535
perror_plus("fchown");
2543
if(dh_params_file != NULL
2544
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2545
int dhparams_fd = open(dh_params_file, O_RDONLY);
2546
if(dhparams_fd == -1){
2547
perror_plus("open");
2549
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2551
perror_plus("fstat");
2553
if(S_ISREG(st.st_mode)
2554
and st.st_uid == 0 and st.st_gid == 0){
2555
ret = fchown(dhparams_fd, uid, gid);
2557
perror_plus("fchown");
2565
/* Lower privileges */
2566
ret_errno = lower_privileges();
2569
perror_plus("Failed to lower privileges");
2574
/* Remove invalid interface names (except "none") */
2576
char *interface = NULL;
2577
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2579
if(strcmp(interface, "none") != 0
2580
and if_nametoindex(interface) == 0){
2581
if(interface[0] != '\0'){
2582
fprintf_plus(stderr, "Not using nonexisting interface"
2583
" \"%s\"\n", interface);
2585
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2591
/* Run network hooks */
2593
if(mc.interfaces != NULL){
2594
interfaces_hooks = malloc(mc.interfaces_size);
2595
if(interfaces_hooks == NULL){
2596
perror_plus("malloc");
2599
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2600
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2602
run_network_hooks("start", interfaces_hooks != NULL ?
2603
interfaces_hooks : "", delay);
2607
avahi_set_log_function(empty_log);
2610
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2611
from the signal handler */
2612
/* Initialize the pseudo-RNG for Avahi */
2613
srand((unsigned int) time(NULL));
2614
simple_poll = avahi_simple_poll_new();
2615
if(simple_poll == NULL){
2616
fprintf_plus(stderr,
2617
"Avahi: Failed to create simple poll object.\n");
2618
exitcode = EX_UNAVAILABLE;
2622
sigemptyset(&sigterm_action.sa_mask);
2623
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2625
perror_plus("sigaddset");
2626
exitcode = EX_OSERR;
2629
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2631
perror_plus("sigaddset");
2632
exitcode = EX_OSERR;
2635
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2637
perror_plus("sigaddset");
2638
exitcode = EX_OSERR;
2641
/* Need to check if the handler is SIG_IGN before handling:
2642
| [[info:libc:Initial Signal Actions]] |
2643
| [[info:libc:Basic Signal Handling]] |
2645
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2647
perror_plus("sigaction");
2650
if(old_sigterm_action.sa_handler != SIG_IGN){
2651
ret = sigaction(SIGINT, &sigterm_action, NULL);
2653
perror_plus("sigaction");
2654
exitcode = EX_OSERR;
2658
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2660
perror_plus("sigaction");
2663
if(old_sigterm_action.sa_handler != SIG_IGN){
2664
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2666
perror_plus("sigaction");
2667
exitcode = EX_OSERR;
2671
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2673
perror_plus("sigaction");
2676
if(old_sigterm_action.sa_handler != SIG_IGN){
2677
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2679
perror_plus("sigaction");
2680
exitcode = EX_OSERR;
2685
/* If no interfaces were specified, make a list */
2686
if(mc.interfaces == NULL){
2687
struct dirent **direntries = NULL;
2688
/* Look for any good interfaces */
2689
ret = scandir(sys_class_net, &direntries, good_interface,
2692
/* Add all found interfaces to interfaces list */
2693
for(int i = 0; i < ret; ++i){
2694
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2695
direntries[i]->d_name);
2698
perror_plus("argz_add");
2699
free(direntries[i]);
2703
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2704
direntries[i]->d_name);
2706
free(direntries[i]);
2713
fprintf_plus(stderr, "Could not find a network interface\n");
2714
exitcode = EXIT_FAILURE;
2719
/* Bring up interfaces which are down, and remove any "none"s */
2721
char *interface = NULL;
2722
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2724
/* If interface name is "none", stop bringing up interfaces.
2725
Also remove all instances of "none" from the list */
2726
if(strcmp(interface, "none") == 0){
2727
argz_delete(&mc.interfaces, &mc.interfaces_size,
2730
while((interface = argz_next(mc.interfaces,
2731
mc.interfaces_size, interface))){
2732
if(strcmp(interface, "none") == 0){
2733
argz_delete(&mc.interfaces, &mc.interfaces_size,
2740
bool interface_was_up = interface_is_up(interface);
2741
errno = bring_up_interface(interface, delay);
2742
if(not interface_was_up){
2744
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2745
" %s\n", interface, strerror(errno));
2747
errno = argz_add(&interfaces_to_take_down,
2748
&interfaces_to_take_down_size,
2751
perror_plus("argz_add");
2756
if(debug and (interfaces_to_take_down == NULL)){
2757
fprintf_plus(stderr, "No interfaces were brought up\n");
2761
/* If we only got one interface, explicitly use only that one */
2762
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2764
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2767
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2774
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2776
fprintf_plus(stderr, "init_gnutls_global failed\n");
2777
exitcode = EX_UNAVAILABLE;
2780
gnutls_initialized = true;
2787
/* Try /run/tmp before /tmp */
2788
tempdir = mkdtemp(run_tempdir);
2789
if(tempdir == NULL and errno == ENOENT){
2791
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2792
run_tempdir, old_tempdir);
2794
tempdir = mkdtemp(old_tempdir);
2796
if(tempdir == NULL){
2797
perror_plus("mkdtemp");
2805
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2806
fprintf_plus(stderr, "init_gpgme failed\n");
2807
exitcode = EX_UNAVAILABLE;
2810
gpgme_initialized = true;
2817
if(connect_to != NULL){
2818
/* Connect directly, do not use Zeroconf */
2819
/* (Mainly meant for debugging) */
2820
char *address = strrchr(connect_to, ':');
2822
if(address == NULL){
2823
fprintf_plus(stderr, "No colon in address\n");
2824
exitcode = EX_USAGE;
2834
tmpmax = strtoimax(address+1, &tmp, 10);
2835
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2836
or tmpmax != (in_port_t)tmpmax){
2837
fprintf_plus(stderr, "Bad port number\n");
2838
exitcode = EX_USAGE;
2846
port = (in_port_t)tmpmax;
2848
/* Colon in address indicates IPv6 */
2850
if(strchr(connect_to, ':') != NULL){
2852
/* Accept [] around IPv6 address - see RFC 5952 */
2853
if(connect_to[0] == '[' and address[-1] == ']')
2861
address = connect_to;
2867
while(not quit_now){
2868
ret = start_mandos_communication(address, port, if_index, af,
2870
if(quit_now or ret == 0){
2874
fprintf_plus(stderr, "Retrying in %d seconds\n",
2875
(int)retry_interval);
2877
sleep((unsigned int)retry_interval);
2881
exitcode = EXIT_SUCCESS;
619
2892
AvahiServerConfig config;
620
AvahiSServiceBrowser *sb = NULL;
623
int returncode = EXIT_SUCCESS;
624
const char *interface = "eth0";
627
static struct option long_options[] = {
628
{"debug", no_argument, (int *)&debug, 1},
629
{"interface", required_argument, 0, 'i'},
632
int option_index = 0;
633
ret = getopt_long (argc, argv, "i:", long_options,
652
avahi_set_log_function(empty_log);
655
/* Initialize the psuedo-RNG */
656
srand((unsigned int) time(NULL));
658
/* Allocate main loop object */
659
if (!(simple_poll = avahi_simple_poll_new())) {
660
fprintf(stderr, "Failed to create simple poll object.\n");
665
/* Do not publish any local records */
2893
/* Do not publish any local Zeroconf records */
666
2894
avahi_server_config_init(&config);
667
2895
config.publish_hinfo = 0;
668
2896
config.publish_addresses = 0;
669
2897
config.publish_workstation = 0;
670
2898
config.publish_domain = 0;
672
2900
/* Allocate a new server */
673
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
&config, NULL, NULL, &error);
676
/* Free the configuration data */
2901
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2902
&config, NULL, NULL, &ret_errno);
2904
/* Free the Avahi configuration data */
677
2905
avahi_server_config_free(&config);
679
/* Check if creating the server object succeeded */
681
fprintf(stderr, "Failed to create server: %s\n",
682
avahi_strerror(error));
683
returncode = EXIT_FAILURE;
687
/* Create the service browser */
688
sb = avahi_s_service_browser_new(server,
690
if_nametoindex(interface),
692
"_mandos._tcp", NULL, 0,
693
browse_callback, server);
695
fprintf(stderr, "Failed to create service browser: %s\n",
696
avahi_strerror(avahi_server_errno(server)));
697
returncode = EXIT_FAILURE;
701
/* Run the main loop */
704
fprintf(stderr, "Starting avahi loop search\n");
707
avahi_simple_poll_loop(simple_poll);
712
fprintf(stderr, "%s exiting\n", argv[0]);
717
avahi_s_service_browser_free(sb);
720
avahi_server_free(server);
723
avahi_simple_poll_free(simple_poll);
2908
/* Check if creating the Avahi server object succeeded */
2909
if(mc.server == NULL){
2910
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2911
avahi_strerror(ret_errno));
2912
exitcode = EX_UNAVAILABLE;
2920
/* Create the Avahi service browser */
2921
sb = avahi_s_service_browser_new(mc.server, if_index,
2922
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2923
NULL, 0, browse_callback,
2926
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2927
avahi_strerror(avahi_server_errno(mc.server)));
2928
exitcode = EX_UNAVAILABLE;
2936
/* Run the main loop */
2939
fprintf_plus(stderr, "Starting Avahi loop search\n");
2942
ret = avahi_loop_with_timeout(simple_poll,
2943
(int)(retry_interval * 1000), &mc);
2945
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2946
(ret == 0) ? "successfully" : "with error");
2952
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2955
/* Cleanup things */
2956
free(mc.interfaces);
2959
avahi_s_service_browser_free(sb);
2961
if(mc.server != NULL)
2962
avahi_server_free(mc.server);
2964
if(simple_poll != NULL)
2965
avahi_simple_poll_free(simple_poll);
2967
if(gnutls_initialized){
2968
gnutls_certificate_free_credentials(mc.cred);
2969
gnutls_global_deinit();
2970
gnutls_dh_params_deinit(mc.dh_params);
2973
if(gpgme_initialized){
2974
gpgme_release(mc.ctx);
2977
/* Cleans up the circular linked list of Mandos servers the client
2979
if(mc.current_server != NULL){
2980
mc.current_server->prev->next = NULL;
2981
while(mc.current_server != NULL){
2982
server *next = mc.current_server->next;
2984
#pragma GCC diagnostic push
2985
#pragma GCC diagnostic ignored "-Wcast-qual"
2987
free((char *)(mc.current_server->ip));
2989
#pragma GCC diagnostic pop
2991
free(mc.current_server);
2992
mc.current_server = next;
2996
/* Re-raise privileges */
2998
ret_errno = raise_privileges();
3001
perror_plus("Failed to raise privileges");
3004
/* Run network hooks */
3005
run_network_hooks("stop", interfaces_hooks != NULL ?
3006
interfaces_hooks : "", delay);
3008
/* Take down the network interfaces which were brought up */
3010
char *interface = NULL;
3011
while((interface=argz_next(interfaces_to_take_down,
3012
interfaces_to_take_down_size,
3014
ret_errno = take_down_interface(interface);
3017
perror_plus("Failed to take down interface");
3020
if(debug and (interfaces_to_take_down == NULL)){
3021
fprintf_plus(stderr, "No interfaces needed to be taken"
3027
ret_errno = lower_privileges_permanently();
3030
perror_plus("Failed to lower privileges permanently");
3034
free(interfaces_to_take_down);
3035
free(interfaces_hooks);
3037
/* Removes the GPGME temp directory and all files inside */
3038
if(tempdir != NULL){
3039
struct dirent **direntries = NULL;
3040
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
3044
if(tempdir_fd == -1){
3045
perror_plus("open");
3047
int numentries = scandirat(tempdir_fd, ".", &direntries,
3048
notdotentries, alphasort);
3049
if(numentries >= 0){
3050
for(int i = 0; i < numentries; i++){
3051
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
3053
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
3054
" \"%s\", 0): %s\n", tempdir,
3055
direntries[i]->d_name, strerror(errno));
3057
free(direntries[i]);
3060
/* need to clean even if 0 because man page doesn't specify */
3062
if(numentries == -1){
3063
perror_plus("scandir");
3065
ret = rmdir(tempdir);
3066
if(ret == -1 and errno != ENOENT){
3067
perror_plus("rmdir");
3075
sigemptyset(&old_sigterm_action.sa_mask);
3076
old_sigterm_action.sa_handler = SIG_DFL;
3077
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3078
&old_sigterm_action,
3081
perror_plus("sigaction");
3084
ret = raise(signal_received);
3085
} while(ret != 0 and errno == EINTR);
3087
perror_plus("raise");
3090
TEMP_FAILURE_RETRY(pause());