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() */
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,
71
131
#define BUFFER_SIZE 256
74
static const char *certdir = "/conf/conf.d/mandos";
75
static const char *certfile = "openpgp-client.txt";
76
static const char *certkey = "openpgp-client-key.txt";
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"
78
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 */
81
AvahiSimplePoll *simple_poll;
82
162
AvahiServer *server;
83
163
gnutls_certificate_credentials_t cred;
84
164
unsigned int dh_bits;
165
gnutls_dh_params_t dh_params;
85
166
const char *priority;
168
server *current_server;
170
size_t interfaces_size;
88
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
91
gpgme_data_t dh_crypto, dh_plain;
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,
95
ssize_t new_packet_capacity = 0;
96
ssize_t new_packet_length = 0;
97
278
gpgme_engine_info_t engine_info;
100
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));
308
ret = (int)TEMP_FAILURE_RETRY(close(fd));
310
perror_plus("close");
312
gpgme_data_release(pgp_data);
317
fprintf_plus(stderr, "Initializing GPGME\n");
104
321
gpgme_check_version(NULL);
105
322
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
106
if (rc != GPG_ERR_NO_ERROR){
107
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
108
gpgme_strsource(rc), gpgme_strerror(rc));
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));
112
/* Set GPGME home directory */
113
rc = gpgme_get_engine_info (&engine_info);
114
if (rc != GPG_ERR_NO_ERROR){
115
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
116
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));
119
336
while(engine_info != NULL){
120
337
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
121
338
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
122
engine_info->file_name, homedir);
339
engine_info->file_name, tempdir);
125
342
engine_info = engine_info->next;
127
344
if(engine_info == NULL){
128
fprintf(stderr, "Could not set home dir to %s\n", homedir);
132
/* Create new GPGME data buffer from packet buffer */
133
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
134
if (rc != GPG_ERR_NO_ERROR){
135
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
136
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));
140
394
/* Create new empty GPGME data buffer for the plaintext */
141
395
rc = gpgme_data_new(&dh_plain);
142
if (rc != GPG_ERR_NO_ERROR){
143
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
144
gpgme_strsource(rc), gpgme_strerror(rc));
148
/* Create new GPGME "context" */
149
rc = gpgme_new(&ctx);
150
if (rc != GPG_ERR_NO_ERROR){
151
fprintf(stderr, "bad gpgme_new: %s: %s\n",
152
gpgme_strsource(rc), gpgme_strerror(rc));
156
/* Decrypt data from the FILE pointer to the plaintext data
158
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
159
if (rc != GPG_ERR_NO_ERROR){
160
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
161
gpgme_strsource(rc), gpgme_strerror(rc));
166
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
170
gpgme_decrypt_result_t result;
171
result = gpgme_op_decrypt_result(ctx);
173
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
175
fprintf(stderr, "Unsupported algorithm: %s\n",
176
result->unsupported_algorithm);
177
fprintf(stderr, "Wrong key usage: %d\n",
178
result->wrong_key_usage);
179
if(result->file_name != NULL){
180
fprintf(stderr, "File name: %s\n", result->file_name);
182
gpgme_recipient_t recipient;
183
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;
185
426
while(recipient != NULL){
186
fprintf(stderr, "Public key algorithm: %s\n",
187
gpgme_pubkey_algo_name(recipient->pubkey_algo));
188
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
189
fprintf(stderr, "Secret key available: %s\n",
190
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
192
434
recipient = recipient->next;
198
/* Delete the GPGME FILE pointer cryptotext data buffer */
199
gpgme_data_release(dh_crypto);
442
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
201
445
/* Seek back to the beginning of the GPGME plaintext data buffer */
202
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
perror("pgpme_data_seek");
446
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
447
perror_plus("gpgme_data_seek");
448
plaintext_length = -1;
208
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
209
*new_packet = realloc(*new_packet,
210
(unsigned int)new_packet_capacity
212
if (*new_packet == NULL){
216
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;
219
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
463
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
221
465
/* Print the data, if any */
226
perror("gpgme_data_read");
229
new_packet_length += ret;
232
/* FIXME: check characters before printing to screen so to not print
233
terminal control characters */
235
/* fprintf(stderr, "decrypted password is: "); */
236
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
237
/* 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);
240
491
/* Delete the GPGME plaintext data buffer */
241
492
gpgme_data_release(dh_plain);
242
return new_packet_length;
245
static const char * safer_gnutls_strerror (int value) {
246
const char *ret = gnutls_strerror (value);
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))
252
511
static void debuggnutls(__attribute__((unused)) int level,
253
512
const char* string){
254
fprintf(stderr, "%s", string);
513
fprintf_plus(stderr, "GnuTLS: %s", string);
257
static int initgnutls(mandos_context *mc){
516
__attribute__((nonnull, warn_unused_result))
517
static int init_gnutls_global(const char *pubkeyfilename,
518
const char *seckeyfilename,
519
const char *dhparamsfilename,
262
fprintf(stderr, "Initializing GnuTLS\n");
525
fprintf_plus(stderr, "Initializing GnuTLS\n");
265
if ((ret = gnutls_global_init ())
266
!= GNUTLS_E_SUCCESS) {
267
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."
272
539
gnutls_global_set_log_level(11);
273
540
gnutls_global_set_log_function(debuggnutls);
276
/* openpgp credentials */
277
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
278
!= GNUTLS_E_SUCCESS) {
279
fprintf (stderr, "memory error: %s\n",
280
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();
285
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
286
" 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",
290
559
ret = gnutls_certificate_set_openpgp_key_file
291
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
292
if (ret != GNUTLS_E_SUCCESS) {
294
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
296
ret, certfile, certkey);
297
fprintf(stdout, "The Error is: %s\n",
298
safer_gnutls_strerror(ret));
302
//GnuTLS server initialization
303
if ((ret = gnutls_dh_params_init (&es->dh_params))
304
!= GNUTLS_E_SUCCESS) {
305
fprintf (stderr, "Error in dh parameter initialization: %s\n",
306
safer_gnutls_strerror(ret));
310
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
311
!= GNUTLS_E_SUCCESS) {
312
fprintf (stderr, "Error in prime generation: %s\n",
313
safer_gnutls_strerror(ret));
317
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
319
// GnuTLS session creation
320
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
321
!= GNUTLS_E_SUCCESS){
322
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
323
safer_gnutls_strerror(ret));
326
if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
327
!= GNUTLS_E_SUCCESS) {
328
fprintf(stderr, "Syntax error at: %s\n", err);
329
fprintf(stderr, "GnuTLS error: %s\n",
330
safer_gnutls_strerror(ret));
334
if ((ret = gnutls_credentials_set
335
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
336
!= GNUTLS_E_SUCCESS) {
337
fprintf(stderr, "Error setting a credentials set: %s\n",
338
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);
342
813
/* ignore client certificate if any. */
343
gnutls_certificate_server_set_request (es->session,
346
gnutls_dh_set_prime_bits (es->session, DH_BITS);
814
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
819
/* Avahi log function callback */
351
820
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
352
821
__attribute__((unused)) const char *txt){}
354
static int start_mandos_communication(const char *ip, uint16_t port,
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 = (int)TEMP_FAILURE_RETRY(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");
957
_exit(EX_UNAVAILABLE);
959
TEMP_FAILURE_RETRY(close(helperdir_fd));
961
#pragma GCC diagnostic push
962
#pragma GCC diagnostic ignored "-Wcast-qual"
964
if(fexecve(helper_fd, (char *const [])
965
{ helper, add ? add_arg : delete_arg, (char *)address,
966
interface, debug ? debug_flag : NULL, NULL },
969
#pragma GCC diagnostic pop
971
perror_plus("fexecve");
983
pret = waitpid(pid, &status, 0);
984
if(pret == -1 and errno == EINTR and quit_now){
985
int errno_raising = 0;
986
if((errno = raise_privileges()) != 0){
987
errno_raising = errno;
988
perror_plus("Failed to raise privileges in order to"
989
" kill helper program");
991
if(kill(pid, SIGTERM) == -1){
994
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
995
perror_plus("Failed to lower privileges after killing"
1000
} while(pret == -1 and errno == EINTR);
1002
perror_plus("waitpid");
1005
if(WIFEXITED(status)){
1006
if(WEXITSTATUS(status) != 0){
1007
fprintf_plus(stderr, "Error: iprouteadddel exited"
1008
" with status %d\n", WEXITSTATUS(status));
1013
if(WIFSIGNALED(status)){
1014
fprintf_plus(stderr, "Error: iprouteadddel died by"
1015
" signal %d\n", WTERMSIG(status));
1018
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1022
__attribute__((nonnull, warn_unused_result))
1023
static bool add_local_route(const char *address,
1024
AvahiIfIndex if_index){
1026
fprintf_plus(stderr, "Adding route to %s\n", address);
1028
return add_delete_local_route(true, address, if_index);
1031
__attribute__((nonnull, warn_unused_result))
1032
static bool delete_local_route(const char *address,
1033
AvahiIfIndex if_index){
1035
fprintf_plus(stderr, "Removing route to %s\n", address);
1037
return add_delete_local_route(false, address, if_index);
1040
/* Called when a Mandos server is found */
1041
__attribute__((nonnull, warn_unused_result))
1042
static int start_mandos_communication(const char *ip, in_port_t port,
355
1043
AvahiIfIndex if_index,
358
struct sockaddr_in6 to;
359
encrypted_session es;
1044
int af, mandos_context *mc){
1045
int ret, tcp_sd = -1;
1047
struct sockaddr_storage to;
360
1048
char *buffer = NULL;
361
char *decrypted_buffer;
1049
char *decrypted_buffer = NULL;
362
1050
size_t buffer_length = 0;
363
1051
size_t buffer_capacity = 0;
364
ssize_t decrypted_buffer_size;
367
char interface[IF_NAMESIZE];
370
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
374
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
381
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1054
gnutls_session_t session;
1055
int pf; /* Protocol family */
1056
bool route_added = false;
1073
fprintf_plus(stderr, "Bad address family: %d\n", af);
1078
/* If the interface is specified and we have a list of interfaces */
1079
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1080
/* Check if the interface is one of the interfaces we are using */
1083
char *interface = NULL;
1084
while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1086
if(if_nametoindex(interface) == (unsigned int)if_index){
1093
/* This interface does not match any in the list, so we don't
1094
connect to the server */
383
perror("if_indextoname");
1096
char interface[IF_NAMESIZE];
1097
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1098
perror_plus("if_indextoname");
1100
fprintf_plus(stderr, "Skipping server on non-used interface"
1102
if_indextoname((unsigned int)if_index,
388
fprintf(stderr, "Binding to interface %s\n", interface);
391
memset(&to,0,sizeof(to)); /* Spurious warning */
392
to.sin6_family = AF_INET6;
393
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
1110
ret = init_gnutls_session(&session, mc);
1116
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1117
PRIuMAX "\n", ip, (uintmax_t)port);
1120
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1123
perror_plus("socket");
1133
memset(&to, 0, sizeof(to));
1135
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
1136
ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
1138
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
1139
ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
1143
perror_plus("inet_pton");
399
fprintf(stderr, "Bad address: %s\n", ip);
402
to.sin6_port = htons(port); /* Spurious warning */
404
to.sin6_scope_id = (uint32_t)if_index;
407
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
408
/* char addrstr[INET6_ADDRSTRLEN]; */
409
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
410
/* sizeof(addrstr)) == NULL){ */
411
/* perror("inet_ntop"); */
413
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
414
/* addrstr, ntohs(to.sin6_port)); */
418
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
424
ret = initgnutls (&es);
430
gnutls_transport_set_ptr (es.session,
431
(gnutls_transport_ptr_t) tcp_sd);
434
fprintf(stderr, "Establishing TLS session with %s\n", ip);
437
ret = gnutls_handshake (es.session);
439
if (ret != GNUTLS_E_SUCCESS){
1149
fprintf_plus(stderr, "Bad address: %s\n", ip);
1154
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1155
if(IN6_IS_ADDR_LINKLOCAL
1156
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1157
if(if_index == AVAHI_IF_UNSPEC){
1158
fprintf_plus(stderr, "An IPv6 link-local address is"
1159
" incomplete without a network interface\n");
1163
/* Set the network interface number as scope */
1164
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1167
((struct sockaddr_in *)&to)->sin_port = htons(port);
1176
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1177
char interface[IF_NAMESIZE];
1178
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1179
perror_plus("if_indextoname");
1181
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1182
"\n", ip, interface, (uintmax_t)port);
1185
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1186
ip, (uintmax_t)port);
1188
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1189
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1191
ret = getnameinfo((struct sockaddr *)&to,
1192
sizeof(struct sockaddr_in6),
1193
addrstr, sizeof(addrstr), NULL, 0,
1196
ret = getnameinfo((struct sockaddr *)&to,
1197
sizeof(struct sockaddr_in),
1198
addrstr, sizeof(addrstr), NULL, 0,
1201
if(ret == EAI_SYSTEM){
1202
perror_plus("getnameinfo");
1203
} else if(ret != 0) {
1204
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1205
} else if(strcmp(addrstr, ip) != 0){
1206
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1217
ret = connect(tcp_sd, (struct sockaddr *)&to,
1218
sizeof(struct sockaddr_in6));
1220
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1221
sizeof(struct sockaddr_in));
1224
if(errno == ENETUNREACH
1225
and if_index != AVAHI_IF_UNSPEC
1226
and connect_to == NULL
1227
and not route_added and
1228
((af == AF_INET6 and not
1229
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1231
or (af == AF_INET and
1232
/* Not a a IPv4LL address */
1233
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1234
& 0xFFFF0000L) != 0xA9FE0000L))){
1235
/* Work around Avahi bug - Avahi does not announce link-local
1236
addresses if it has a global address, so local hosts with
1237
*only* a link-local address (e.g. Mandos clients) cannot
1238
connect to a Mandos server announced by Avahi on a server
1239
host with a global address. Work around this by retrying
1240
with an explicit route added with the server's address.
1242
Avahi bug reference:
1243
http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1244
https://bugs.debian.org/587961
1247
fprintf_plus(stderr, "Mandos server unreachable, trying"
1251
route_added = add_local_route(ip, if_index);
1257
if(errno != ECONNREFUSED or debug){
1259
perror_plus("connect");
1272
const char *out = mandos_protocol_version;
1275
size_t out_size = strlen(out);
1276
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1277
out_size - written));
1280
perror_plus("write");
1284
written += (size_t)ret;
1285
if(written < out_size){
1288
if(out == mandos_protocol_version){
1303
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1311
/* This casting via intptr_t is to eliminate warning about casting
1312
an int to a pointer type. This is exactly how the GnuTLS Guile
1313
function "set-session-transport-fd!" does it. */
1314
gnutls_transport_set_ptr(session,
1315
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1323
ret = gnutls_handshake(session);
1328
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1330
if(ret != GNUTLS_E_SUCCESS){
441
fprintf(stderr, "\n*** Handshake failed ***\n");
1332
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
448
//Retrieve OpenPGP packet that contains the wanted password
1339
/* Read OpenPGP packet that contains the wanted password */
451
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1342
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
456
if (buffer_length + BUFFER_SIZE > buffer_capacity){
457
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
462
buffer_capacity += BUFFER_SIZE;
465
ret = gnutls_record_recv
466
(es.session, buffer+buffer_length, BUFFER_SIZE);
1353
buffer_capacity = incbuffer(&buffer, buffer_length,
1355
if(buffer_capacity == 0){
1357
perror_plus("incbuffer");
1367
sret = gnutls_record_recv(session, buffer+buffer_length,
472
1374
case GNUTLS_E_INTERRUPTED:
473
1375
case GNUTLS_E_AGAIN:
475
1377
case GNUTLS_E_REHANDSHAKE:
476
ret = gnutls_handshake (es.session);
478
fprintf(stderr, "\n*** Handshake failed ***\n");
1379
ret = gnutls_handshake(session);
1385
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1387
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
485
fprintf(stderr, "Unknown error while reading data from"
486
" encrypted session with mandos server\n");
488
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1395
fprintf_plus(stderr, "Unknown error while reading data from"
1396
" encrypted session with Mandos server\n");
1397
gnutls_bye(session, GNUTLS_SHUT_RDWR);
492
buffer_length += (size_t) ret;
496
if (buffer_length > 0){
497
decrypted_buffer_size = pgp_packet_decrypt(buffer,
501
if (decrypted_buffer_size >= 0){
1402
buffer_length += (size_t) sret;
1407
fprintf_plus(stderr, "Closing TLS session\n");
1416
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1421
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1423
if(buffer_length > 0){
1424
ssize_t decrypted_buffer_size;
1425
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1426
&decrypted_buffer, mc);
1427
if(decrypted_buffer_size >= 0){
502
1430
while(written < (size_t) decrypted_buffer_size){
503
ret = (int)fwrite (decrypted_buffer + written, 1,
504
(size_t)decrypted_buffer_size - written,
1436
ret = (int)fwrite(decrypted_buffer + written, 1,
1437
(size_t)decrypted_buffer_size - written,
506
1439
if(ret == 0 and ferror(stdout)){
508
fprintf(stderr, "Error writing encrypted data: %s\n",
1442
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
514
1448
written += (size_t)ret;
516
free(decrypted_buffer);
1454
/* Shutdown procedure */
1459
if(not delete_local_route(ip, if_index)){
1460
fprintf_plus(stderr, "Failed to delete local route to %s on"
1461
" interface %d", ip, if_index);
1465
free(decrypted_buffer);
1468
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1474
perror_plus("close");
1476
gnutls_deinit(session);
525
fprintf(stderr, "Closing TLS session\n");
529
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
532
gnutls_deinit (es.session);
533
gnutls_certificate_free_credentials (es.cred);
534
gnutls_global_deinit ();
538
static void resolve_callback( AvahiSServiceResolver *r,
539
AvahiIfIndex interface,
540
AVAHI_GCC_UNUSED AvahiProtocol protocol,
541
AvahiResolverEvent event,
545
const char *host_name,
546
const AvahiAddress *address,
548
AVAHI_GCC_UNUSED AvahiStringList *txt,
549
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
550
AVAHI_GCC_UNUSED void* userdata) {
551
mandos_context *mc = userdata;
552
assert(r); /* Spurious warning */
1486
__attribute__((nonnull))
1487
static void resolve_callback(AvahiSServiceResolver *r,
1488
AvahiIfIndex interface,
1489
AvahiProtocol proto,
1490
AvahiResolverEvent event,
1494
const char *host_name,
1495
const AvahiAddress *address,
1497
AVAHI_GCC_UNUSED AvahiStringList *txt,
1498
AVAHI_GCC_UNUSED AvahiLookupResultFlags
554
1505
/* Called whenever a service has been resolved successfully or
1509
avahi_s_service_resolver_free(r);
559
1515
case AVAHI_RESOLVER_FAILURE:
560
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
561
" type '%s' in domain '%s': %s\n", name, type, domain,
562
avahi_strerror(avahi_server_errno(mc->server)));
1516
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1517
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1519
avahi_strerror(avahi_server_errno
1520
(((mandos_context*)mc)->server)));
565
1523
case AVAHI_RESOLVER_FOUND:
567
1525
char ip[AVAHI_ADDRESS_STR_MAX];
568
1526
avahi_address_snprint(ip, sizeof(ip), address);
570
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
" port %d\n", name, host_name, ip, port);
1528
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1529
PRIdMAX ") on port %" PRIu16 "\n", name,
1530
host_name, ip, (intmax_t)interface, port);
573
int ret = start_mandos_communication(ip, port, interface, mc);
1532
int ret = start_mandos_communication(ip, (in_port_t)port,
1534
avahi_proto_to_af(proto),
1537
avahi_simple_poll_quit(simple_poll);
1539
if(not add_server(ip, (in_port_t)port, interface,
1540
avahi_proto_to_af(proto),
1541
&((mandos_context*)mc)->current_server)){
1542
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
579
1548
avahi_s_service_resolver_free(r);
582
static void browse_callback( AvahiSServiceBrowser *b,
583
AvahiIfIndex interface,
584
AvahiProtocol protocol,
585
AvahiBrowserEvent event,
589
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
591
mandos_context *mc = userdata;
592
assert(b); /* Spurious warning */
1551
static void browse_callback(AvahiSServiceBrowser *b,
1552
AvahiIfIndex interface,
1553
AvahiProtocol protocol,
1554
AvahiBrowserEvent event,
1558
AVAHI_GCC_UNUSED AvahiLookupResultFlags
594
1565
/* Called whenever a new services becomes available on the LAN or
595
1566
is removed from the LAN */
599
1574
case AVAHI_BROWSER_FAILURE:
601
fprintf(stderr, "(Browser) %s\n",
602
avahi_strerror(avahi_server_errno(mc->server)));
603
avahi_simple_poll_quit(mc->simple_poll);
1576
fprintf_plus(stderr, "(Avahi browser) %s\n",
1577
avahi_strerror(avahi_server_errno
1578
(((mandos_context*)mc)->server)));
1579
avahi_simple_poll_quit(simple_poll);
606
1582
case AVAHI_BROWSER_NEW:
607
/* We ignore the returned resolver object. In the callback
608
function we free it. If the server is terminated before
609
the callback function is called the server will free
610
the resolver for us. */
612
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
614
AVAHI_PROTO_INET6, 0,
615
resolve_callback, mc)))
616
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
617
avahi_strerror(avahi_server_errno(s)));
1583
/* We ignore the returned Avahi resolver object. In the callback
1584
function we free it. If the Avahi server is terminated before
1585
the callback function is called the Avahi server will free the
1588
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1589
interface, protocol, name, type,
1590
domain, protocol, 0,
1591
resolve_callback, mc) == NULL)
1592
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1594
avahi_strerror(avahi_server_errno
1595
(((mandos_context*)mc)->server)));
620
1598
case AVAHI_BROWSER_REMOVE:
623
1601
case AVAHI_BROWSER_ALL_FOR_NOW:
624
1602
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1604
fprintf_plus(stderr, "No Mandos server found, still"
629
/* Combines file name and path and returns the malloced new
630
string. some sane checks could/should be added */
631
static const char *combinepath(const char *first, const char *second){
632
size_t f_len = strlen(first);
633
size_t s_len = strlen(second);
634
char *tmp = malloc(f_len + s_len + 2);
639
memcpy(tmp, first, f_len);
643
memcpy(tmp + f_len + 1, second, s_len);
645
tmp[f_len + 1 + s_len] = '\0';
650
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1611
/* Signal handler that stops main loop after SIGTERM */
1612
static void handle_sigterm(int sig){
1617
signal_received = sig;
1618
int old_errno = errno;
1619
/* set main loop to exit */
1620
if(simple_poll != NULL){
1621
avahi_simple_poll_quit(simple_poll);
1626
__attribute__((nonnull, warn_unused_result))
1627
bool get_flags(const char *ifname, struct ifreq *ifr){
1631
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1634
perror_plus("socket");
1638
strcpy(ifr->ifr_name, ifname);
1639
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1643
perror_plus("ioctl SIOCGIFFLAGS");
1651
__attribute__((nonnull, warn_unused_result))
1652
bool good_flags(const char *ifname, const struct ifreq *ifr){
1654
/* Reject the loopback device */
1655
if(ifr->ifr_flags & IFF_LOOPBACK){
1657
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1662
/* Accept point-to-point devices only if connect_to is specified */
1663
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1665
fprintf_plus(stderr, "Accepting point-to-point interface"
1666
" \"%s\"\n", ifname);
1670
/* Otherwise, reject non-broadcast-capable devices */
1671
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1673
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1674
" \"%s\"\n", ifname);
1678
/* Reject non-ARP interfaces (including dummy interfaces) */
1679
if(ifr->ifr_flags & IFF_NOARP){
1681
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1687
/* Accept this device */
1689
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1695
* This function determines if a directory entry in /sys/class/net
1696
* corresponds to an acceptable network device.
1697
* (This function is passed to scandir(3) as a filter function.)
1699
__attribute__((nonnull, warn_unused_result))
1700
int good_interface(const struct dirent *if_entry){
1701
if(if_entry->d_name[0] == '.'){
1706
if(not get_flags(if_entry->d_name, &ifr)){
1708
fprintf_plus(stderr, "Failed to get flags for interface "
1709
"\"%s\"\n", if_entry->d_name);
1714
if(not good_flags(if_entry->d_name, &ifr)){
1721
* This function determines if a network interface is up.
1723
__attribute__((nonnull, warn_unused_result))
1724
bool interface_is_up(const char *interface){
1726
if(not get_flags(interface, &ifr)){
1728
fprintf_plus(stderr, "Failed to get flags for interface "
1729
"\"%s\"\n", interface);
1734
return (bool)(ifr.ifr_flags & IFF_UP);
1738
* This function determines if a network interface is running
1740
__attribute__((nonnull, warn_unused_result))
1741
bool interface_is_running(const char *interface){
1743
if(not get_flags(interface, &ifr)){
1745
fprintf_plus(stderr, "Failed to get flags for interface "
1746
"\"%s\"\n", interface);
1751
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1754
__attribute__((nonnull, pure, warn_unused_result))
1755
int notdotentries(const struct dirent *direntry){
1756
/* Skip "." and ".." */
1757
if(direntry->d_name[0] == '.'
1758
and (direntry->d_name[1] == '\0'
1759
or (direntry->d_name[1] == '.'
1760
and direntry->d_name[2] == '\0'))){
1766
/* Is this directory entry a runnable program? */
1767
__attribute__((nonnull, warn_unused_result))
1768
int runnable_hook(const struct dirent *direntry){
1773
if((direntry->d_name)[0] == '\0'){
1778
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1779
"abcdefghijklmnopqrstuvwxyz"
1782
if((direntry->d_name)[sret] != '\0'){
1783
/* Contains non-allowed characters */
1785
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1791
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1794
perror_plus("Could not stat hook");
1798
if(not (S_ISREG(st.st_mode))){
1799
/* Not a regular file */
1801
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1806
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1807
/* Not executable */
1809
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1815
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1821
__attribute__((nonnull, warn_unused_result))
1822
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1823
mandos_context *mc){
1825
struct timespec now;
1826
struct timespec waited_time;
1827
intmax_t block_time;
1830
if(mc->current_server == NULL){
1832
fprintf_plus(stderr, "Wait until first server is found."
1835
ret = avahi_simple_poll_iterate(s, -1);
1838
fprintf_plus(stderr, "Check current_server if we should run"
1841
/* the current time */
1842
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1844
perror_plus("clock_gettime");
1847
/* Calculating in ms how long time between now and server
1848
who we visted longest time ago. Now - last seen. */
1849
waited_time.tv_sec = (now.tv_sec
1850
- mc->current_server->last_seen.tv_sec);
1851
waited_time.tv_nsec = (now.tv_nsec
1852
- mc->current_server->last_seen.tv_nsec);
1853
/* total time is 10s/10,000ms.
1854
Converting to s from ms by dividing by 1,000,
1855
and ns to ms by dividing by 1,000,000. */
1856
block_time = ((retry_interval
1857
- ((intmax_t)waited_time.tv_sec * 1000))
1858
- ((intmax_t)waited_time.tv_nsec / 1000000));
1861
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1865
if(block_time <= 0){
1866
ret = start_mandos_communication(mc->current_server->ip,
1867
mc->current_server->port,
1868
mc->current_server->if_index,
1869
mc->current_server->af, mc);
1871
avahi_simple_poll_quit(s);
1874
ret = clock_gettime(CLOCK_MONOTONIC,
1875
&mc->current_server->last_seen);
1877
perror_plus("clock_gettime");
1880
mc->current_server = mc->current_server->next;
1881
block_time = 0; /* Call avahi to find new Mandos
1882
servers, but don't block */
1885
ret = avahi_simple_poll_iterate(s, (int)block_time);
1888
if(ret > 0 or errno != EINTR){
1889
return (ret != 1) ? ret : 0;
1895
__attribute__((nonnull))
1896
void run_network_hooks(const char *mode, const char *interface,
1898
struct dirent **direntries = NULL;
1899
if(hookdir_fd == -1){
1900
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1902
if(hookdir_fd == -1){
1903
if(errno == ENOENT){
1905
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1906
" found\n", hookdir);
1909
perror_plus("open");
1915
#if __GLIBC_PREREQ(2, 15)
1916
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1917
runnable_hook, alphasort);
1918
#else /* not __GLIBC_PREREQ(2, 15) */
1919
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1921
#endif /* not __GLIBC_PREREQ(2, 15) */
1922
#else /* not __GLIBC__ */
1923
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1925
#endif /* not __GLIBC__ */
1927
perror_plus("scandir");
1930
struct dirent *direntry;
1932
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1934
perror_plus("open(\"/dev/null\", O_RDONLY)");
1937
for(int i = 0; i < numhooks; i++){
1938
direntry = direntries[i];
1940
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1943
pid_t hook_pid = fork();
1946
/* Raise privileges */
1947
errno = raise_privileges_permanently();
1949
perror_plus("Failed to raise privileges");
1956
perror_plus("setgid");
1959
/* Reset supplementary groups */
1961
ret = setgroups(0, NULL);
1963
perror_plus("setgroups");
1966
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1968
perror_plus("setenv");
1971
ret = setenv("DEVICE", interface, 1);
1973
perror_plus("setenv");
1976
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1978
perror_plus("setenv");
1981
ret = setenv("MODE", mode, 1);
1983
perror_plus("setenv");
1987
ret = asprintf(&delaystring, "%f", (double)delay);
1989
perror_plus("asprintf");
1992
ret = setenv("DELAY", delaystring, 1);
1995
perror_plus("setenv");
1999
if(connect_to != NULL){
2000
ret = setenv("CONNECT", connect_to, 1);
2002
perror_plus("setenv");
2006
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2010
perror_plus("openat");
2011
_exit(EXIT_FAILURE);
2013
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2014
perror_plus("close");
2015
_exit(EXIT_FAILURE);
2017
ret = dup2(devnull, STDIN_FILENO);
2019
perror_plus("dup2(devnull, STDIN_FILENO)");
2022
ret = (int)TEMP_FAILURE_RETRY(close(devnull));
2024
perror_plus("close");
2027
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2029
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2032
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2034
perror_plus("fexecve");
2035
_exit(EXIT_FAILURE);
2039
perror_plus("fork");
2044
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2045
perror_plus("waitpid");
2049
if(WIFEXITED(status)){
2050
if(WEXITSTATUS(status) != 0){
2051
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2052
" with status %d\n", direntry->d_name,
2053
WEXITSTATUS(status));
2057
} else if(WIFSIGNALED(status)){
2058
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2059
" signal %d\n", direntry->d_name,
2064
fprintf_plus(stderr, "Warning: network hook \"%s\""
2065
" crashed\n", direntry->d_name);
2071
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2077
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2078
perror_plus("close");
2085
__attribute__((nonnull, warn_unused_result))
2086
error_t bring_up_interface(const char *const interface,
2088
error_t old_errno = errno;
2090
struct ifreq network;
2091
unsigned int if_index = if_nametoindex(interface);
2093
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2103
if(not interface_is_up(interface)){
2104
error_t ret_errno = 0, ioctl_errno = 0;
2105
if(not get_flags(interface, &network)){
2107
fprintf_plus(stderr, "Failed to get flags for interface "
2108
"\"%s\"\n", interface);
2112
network.ifr_flags |= IFF_UP; /* set flag */
2114
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2117
perror_plus("socket");
2123
ret = (int)TEMP_FAILURE_RETRY(close(sd));
2125
perror_plus("close");
2132
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2136
/* Raise privileges */
2137
ret_errno = raise_privileges();
2140
perror_plus("Failed to raise privileges");
2145
bool restore_loglevel = false;
2147
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2148
messages about the network interface to mess up the prompt */
2149
ret_linux = klogctl(8, NULL, 5);
2150
if(ret_linux == -1){
2151
perror_plus("klogctl");
2153
restore_loglevel = true;
2156
#endif /* __linux__ */
2157
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2158
ioctl_errno = errno;
2160
if(restore_loglevel){
2161
ret_linux = klogctl(7, NULL, 0);
2162
if(ret_linux == -1){
2163
perror_plus("klogctl");
2166
#endif /* __linux__ */
2168
/* If raise_privileges() succeeded above */
2170
/* Lower privileges */
2171
ret_errno = lower_privileges();
2174
perror_plus("Failed to lower privileges");
2178
/* Close the socket */
2179
ret = (int)TEMP_FAILURE_RETRY(close(sd));
2181
perror_plus("close");
2184
if(ret_setflags == -1){
2185
errno = ioctl_errno;
2186
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2191
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2195
/* Sleep checking until interface is running.
2196
Check every 0.25s, up to total time of delay */
2197
for(int i=0; i < delay * 4; i++){
2198
if(interface_is_running(interface)){
2201
struct timespec sleeptime = { .tv_nsec = 250000000 };
2202
ret = nanosleep(&sleeptime, NULL);
2203
if(ret == -1 and errno != EINTR){
2204
perror_plus("nanosleep");
2212
__attribute__((nonnull, warn_unused_result))
2213
error_t take_down_interface(const char *const interface){
2214
error_t old_errno = errno;
2215
struct ifreq network;
2216
unsigned int if_index = if_nametoindex(interface);
2218
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2222
if(interface_is_up(interface)){
2223
error_t ret_errno = 0, ioctl_errno = 0;
2224
if(not get_flags(interface, &network) and debug){
2226
fprintf_plus(stderr, "Failed to get flags for interface "
2227
"\"%s\"\n", interface);
2231
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2233
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2236
perror_plus("socket");
2242
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2246
/* Raise privileges */
2247
ret_errno = raise_privileges();
2250
perror_plus("Failed to raise privileges");
2253
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2254
ioctl_errno = errno;
2256
/* If raise_privileges() succeeded above */
2258
/* Lower privileges */
2259
ret_errno = lower_privileges();
2262
perror_plus("Failed to lower privileges");
2266
/* Close the socket */
2267
int ret = (int)TEMP_FAILURE_RETRY(close(sd));
2269
perror_plus("close");
2272
if(ret_setflags == -1){
2273
errno = ioctl_errno;
2274
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2279
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2287
int main(int argc, char *argv[]){
2288
mandos_context mc = { .server = NULL, .dh_bits = 0,
2289
.priority = "SECURE256:!CTYPE-X.509:"
2290
"+CTYPE-OPENPGP:!RSA", .current_server = NULL,
2291
.interfaces = NULL, .interfaces_size = 0 };
2292
AvahiSServiceBrowser *sb = NULL;
2297
int exitcode = EXIT_SUCCESS;
2298
char *interfaces_to_take_down = NULL;
2299
size_t interfaces_to_take_down_size = 0;
2300
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2301
char old_tempdir[] = "/tmp/mandosXXXXXX";
2302
char *tempdir = NULL;
2303
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2304
const char *seckey = PATHDIR "/" SECKEY;
2305
const char *pubkey = PATHDIR "/" PUBKEY;
2306
const char *dh_params_file = NULL;
2307
char *interfaces_hooks = NULL;
2309
bool gnutls_initialized = false;
2310
bool gpgme_initialized = false;
2312
double retry_interval = 10; /* 10s between trying a server and
2313
retrying the same server again */
2315
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2316
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2321
/* Lower any group privileges we might have, just to be safe */
2325
perror_plus("setgid");
2328
/* Lower user privileges (temporarily) */
2332
perror_plus("seteuid");
2340
struct argp_option options[] = {
2341
{ .name = "debug", .key = 128,
2342
.doc = "Debug mode", .group = 3 },
2343
{ .name = "connect", .key = 'c',
2344
.arg = "ADDRESS:PORT",
2345
.doc = "Connect directly to a specific Mandos server",
2347
{ .name = "interface", .key = 'i',
2349
.doc = "Network interface that will be used to search for"
2352
{ .name = "seckey", .key = 's',
2354
.doc = "OpenPGP secret key file base name",
2356
{ .name = "pubkey", .key = 'p',
2358
.doc = "OpenPGP public key file base name",
2360
{ .name = "dh-bits", .key = 129,
2362
.doc = "Bit length of the prime number used in the"
2363
" Diffie-Hellman key exchange",
2365
{ .name = "dh-params", .key = 134,
2367
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2368
" for the Diffie-Hellman key exchange",
2370
{ .name = "priority", .key = 130,
2372
.doc = "GnuTLS priority string for the TLS handshake",
2374
{ .name = "delay", .key = 131,
2376
.doc = "Maximum delay to wait for interface startup",
2378
{ .name = "retry", .key = 132,
2380
.doc = "Retry interval used when denied by the Mandos server",
2382
{ .name = "network-hook-dir", .key = 133,
2384
.doc = "Directory where network hooks are located",
2387
* These reproduce what we would get without ARGP_NO_HELP
2389
{ .name = "help", .key = '?',
2390
.doc = "Give this help list", .group = -1 },
2391
{ .name = "usage", .key = -3,
2392
.doc = "Give a short usage message", .group = -1 },
2393
{ .name = "version", .key = 'V',
2394
.doc = "Print program version", .group = -1 },
2398
error_t parse_opt(int key, char *arg,
2399
struct argp_state *state){
2402
case 128: /* --debug */
2405
case 'c': /* --connect */
2408
case 'i': /* --interface */
2409
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2412
argp_error(state, "%s", strerror(ret_errno));
2415
case 's': /* --seckey */
2418
case 'p': /* --pubkey */
2421
case 129: /* --dh-bits */
2423
tmpmax = strtoimax(arg, &tmp, 10);
2424
if(errno != 0 or tmp == arg or *tmp != '\0'
2425
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2426
argp_error(state, "Bad number of DH bits");
2428
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2430
case 134: /* --dh-params */
2431
dh_params_file = arg;
2433
case 130: /* --priority */
2436
case 131: /* --delay */
2438
delay = strtof(arg, &tmp);
2439
if(errno != 0 or tmp == arg or *tmp != '\0'){
2440
argp_error(state, "Bad delay");
2442
case 132: /* --retry */
2444
retry_interval = strtod(arg, &tmp);
2445
if(errno != 0 or tmp == arg or *tmp != '\0'
2446
or (retry_interval * 1000) > INT_MAX
2447
or retry_interval < 0){
2448
argp_error(state, "Bad retry interval");
2451
case 133: /* --network-hook-dir */
2455
* These reproduce what we would get without ARGP_NO_HELP
2457
case '?': /* --help */
2458
argp_state_help(state, state->out_stream,
2459
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2460
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2461
case -3: /* --usage */
2462
argp_state_help(state, state->out_stream,
2463
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2464
case 'V': /* --version */
2465
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2466
exit(argp_err_exit_status);
2469
return ARGP_ERR_UNKNOWN;
2474
struct argp argp = { .options = options, .parser = parse_opt,
2476
.doc = "Mandos client -- Get and decrypt"
2477
" passwords from a Mandos server" };
2478
ret = argp_parse(&argp, argc, argv,
2479
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2486
perror_plus("argp_parse");
2487
exitcode = EX_OSERR;
2490
exitcode = EX_USAGE;
2496
/* Work around Debian bug #633582:
2497
<http://bugs.debian.org/633582> */
2499
/* Re-raise privileges */
2500
ret_errno = raise_privileges();
2503
perror_plus("Failed to raise privileges");
2507
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2508
int seckey_fd = open(seckey, O_RDONLY);
2509
if(seckey_fd == -1){
2510
perror_plus("open");
2512
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2514
perror_plus("fstat");
2516
if(S_ISREG(st.st_mode)
2517
and st.st_uid == 0 and st.st_gid == 0){
2518
ret = fchown(seckey_fd, uid, gid);
2520
perror_plus("fchown");
2524
TEMP_FAILURE_RETRY(close(seckey_fd));
2528
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2529
int pubkey_fd = open(pubkey, O_RDONLY);
2530
if(pubkey_fd == -1){
2531
perror_plus("open");
2533
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2535
perror_plus("fstat");
2537
if(S_ISREG(st.st_mode)
2538
and st.st_uid == 0 and st.st_gid == 0){
2539
ret = fchown(pubkey_fd, uid, gid);
2541
perror_plus("fchown");
2545
TEMP_FAILURE_RETRY(close(pubkey_fd));
2549
/* Lower privileges */
2550
ret_errno = lower_privileges();
2553
perror_plus("Failed to lower privileges");
2558
/* Remove invalid interface names (except "none") */
2560
char *interface = NULL;
2561
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2563
if(strcmp(interface, "none") != 0
2564
and if_nametoindex(interface) == 0){
2565
if(interface[0] != '\0'){
2566
fprintf_plus(stderr, "Not using nonexisting interface"
2567
" \"%s\"\n", interface);
2569
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2575
/* Run network hooks */
2577
if(mc.interfaces != NULL){
2578
interfaces_hooks = malloc(mc.interfaces_size);
2579
if(interfaces_hooks == NULL){
2580
perror_plus("malloc");
2583
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2584
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2586
run_network_hooks("start", interfaces_hooks != NULL ?
2587
interfaces_hooks : "", delay);
2591
avahi_set_log_function(empty_log);
2594
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2595
from the signal handler */
2596
/* Initialize the pseudo-RNG for Avahi */
2597
srand((unsigned int) time(NULL));
2598
simple_poll = avahi_simple_poll_new();
2599
if(simple_poll == NULL){
2600
fprintf_plus(stderr,
2601
"Avahi: Failed to create simple poll object.\n");
2602
exitcode = EX_UNAVAILABLE;
2606
sigemptyset(&sigterm_action.sa_mask);
2607
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2609
perror_plus("sigaddset");
2610
exitcode = EX_OSERR;
2613
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2615
perror_plus("sigaddset");
2616
exitcode = EX_OSERR;
2619
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2621
perror_plus("sigaddset");
2622
exitcode = EX_OSERR;
2625
/* Need to check if the handler is SIG_IGN before handling:
2626
| [[info:libc:Initial Signal Actions]] |
2627
| [[info:libc:Basic Signal Handling]] |
2629
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2631
perror_plus("sigaction");
2634
if(old_sigterm_action.sa_handler != SIG_IGN){
2635
ret = sigaction(SIGINT, &sigterm_action, NULL);
2637
perror_plus("sigaction");
2638
exitcode = EX_OSERR;
2642
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2644
perror_plus("sigaction");
2647
if(old_sigterm_action.sa_handler != SIG_IGN){
2648
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2650
perror_plus("sigaction");
2651
exitcode = EX_OSERR;
2655
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2657
perror_plus("sigaction");
2660
if(old_sigterm_action.sa_handler != SIG_IGN){
2661
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2663
perror_plus("sigaction");
2664
exitcode = EX_OSERR;
2669
/* If no interfaces were specified, make a list */
2670
if(mc.interfaces == NULL){
2671
struct dirent **direntries = NULL;
2672
/* Look for any good interfaces */
2673
ret = scandir(sys_class_net, &direntries, good_interface,
2676
/* Add all found interfaces to interfaces list */
2677
for(int i = 0; i < ret; ++i){
2678
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2679
direntries[i]->d_name);
2682
perror_plus("argz_add");
2683
free(direntries[i]);
2687
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2688
direntries[i]->d_name);
2690
free(direntries[i]);
2697
fprintf_plus(stderr, "Could not find a network interface\n");
2698
exitcode = EXIT_FAILURE;
2703
/* Bring up interfaces which are down, and remove any "none"s */
2705
char *interface = NULL;
2706
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2708
/* If interface name is "none", stop bringing up interfaces.
2709
Also remove all instances of "none" from the list */
2710
if(strcmp(interface, "none") == 0){
2711
argz_delete(&mc.interfaces, &mc.interfaces_size,
2714
while((interface = argz_next(mc.interfaces,
2715
mc.interfaces_size, interface))){
2716
if(strcmp(interface, "none") == 0){
2717
argz_delete(&mc.interfaces, &mc.interfaces_size,
2724
bool interface_was_up = interface_is_up(interface);
2725
errno = bring_up_interface(interface, delay);
2726
if(not interface_was_up){
2728
perror_plus("Failed to bring up interface");
2730
errno = argz_add(&interfaces_to_take_down,
2731
&interfaces_to_take_down_size,
2734
perror_plus("argz_add");
2739
if(debug and (interfaces_to_take_down == NULL)){
2740
fprintf_plus(stderr, "No interfaces were brought up\n");
2744
/* If we only got one interface, explicitly use only that one */
2745
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2747
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2750
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2757
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2759
fprintf_plus(stderr, "init_gnutls_global failed\n");
2760
exitcode = EX_UNAVAILABLE;
2763
gnutls_initialized = true;
2770
/* Try /run/tmp before /tmp */
2771
tempdir = mkdtemp(run_tempdir);
2772
if(tempdir == NULL and errno == ENOENT){
2774
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2775
run_tempdir, old_tempdir);
2777
tempdir = mkdtemp(old_tempdir);
2779
if(tempdir == NULL){
2780
perror_plus("mkdtemp");
2788
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2789
fprintf_plus(stderr, "init_gpgme failed\n");
2790
exitcode = EX_UNAVAILABLE;
2793
gpgme_initialized = true;
2800
if(connect_to != NULL){
2801
/* Connect directly, do not use Zeroconf */
2802
/* (Mainly meant for debugging) */
2803
char *address = strrchr(connect_to, ':');
2805
if(address == NULL){
2806
fprintf_plus(stderr, "No colon in address\n");
2807
exitcode = EX_USAGE;
2817
tmpmax = strtoimax(address+1, &tmp, 10);
2818
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2819
or tmpmax != (in_port_t)tmpmax){
2820
fprintf_plus(stderr, "Bad port number\n");
2821
exitcode = EX_USAGE;
2829
port = (in_port_t)tmpmax;
2831
/* Colon in address indicates IPv6 */
2833
if(strchr(connect_to, ':') != NULL){
2835
/* Accept [] around IPv6 address - see RFC 5952 */
2836
if(connect_to[0] == '[' and address[-1] == ']')
2844
address = connect_to;
2850
while(not quit_now){
2851
ret = start_mandos_communication(address, port, if_index, af,
2853
if(quit_now or ret == 0){
2857
fprintf_plus(stderr, "Retrying in %d seconds\n",
2858
(int)retry_interval);
2860
sleep((unsigned int)retry_interval);
2864
exitcode = EXIT_SUCCESS;
651
2875
AvahiServerConfig config;
652
AvahiSServiceBrowser *sb = NULL;
655
int returncode = EXIT_SUCCESS;
656
const char *interface = "eth0";
657
struct ifreq network;
659
char *connect_to = NULL;
660
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
661
mandos_context mc = { .simple_poll = NULL, .server = NULL,
662
.dh_bits = 2048, .priority = "SECURE256"};
665
static struct option long_options[] = {
666
{"debug", no_argument, (int *)&debug, 1},
667
{"connect", required_argument, 0, 'C'},
668
{"interface", required_argument, 0, 'i'},
669
{"certdir", required_argument, 0, 'd'},
670
{"certkey", required_argument, 0, 'c'},
671
{"certfile", required_argument, 0, 'k'},
672
{"dh_bits", required_argument, 0, 'D'},
673
{"priority", required_argument, 0, 'p'},
676
int option_index = 0;
677
ret = getopt_long (argc, argv, "i:", long_options,
706
tmp = strtol(optarg, NULL, 10);
707
if (errno == ERANGE){
715
mc.priority = optarg;
722
certfile = combinepath(certdir, certfile);
723
if (certfile == NULL){
724
perror("combinepath");
725
returncode = EXIT_FAILURE;
729
certkey = combinepath(certdir, certkey);
730
if (certkey == NULL){
731
perror("combinepath");
732
returncode = EXIT_FAILURE;
736
if_index = (AvahiIfIndex) if_nametoindex(interface);
738
fprintf(stderr, "No such interface: \"%s\"\n", interface);
742
if(connect_to != NULL){
743
/* Connect directly, do not use Zeroconf */
744
/* (Mainly meant for debugging) */
745
char *address = strrchr(connect_to, ':');
747
fprintf(stderr, "No colon in address\n");
751
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
753
perror("Bad port number");
757
address = connect_to;
758
ret = start_mandos_communication(address, port, if_index);
766
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
769
returncode = EXIT_FAILURE;
772
strcpy(network.ifr_name, interface);
773
ret = ioctl(sd, SIOCGIFFLAGS, &network);
776
perror("ioctl SIOCGIFFLAGS");
777
returncode = EXIT_FAILURE;
780
if((network.ifr_flags & IFF_UP) == 0){
781
network.ifr_flags |= IFF_UP;
782
ret = ioctl(sd, SIOCSIFFLAGS, &network);
784
perror("ioctl SIOCSIFFLAGS");
785
returncode = EXIT_FAILURE;
792
avahi_set_log_function(empty_log);
795
/* Initialize the psuedo-RNG */
796
srand((unsigned int) time(NULL));
798
/* Allocate main loop object */
799
if (!(mc.simple_poll = avahi_simple_poll_new())) {
800
fprintf(stderr, "Failed to create simple poll object.\n");
801
returncode = EXIT_FAILURE;
805
/* Do not publish any local records */
2876
/* Do not publish any local Zeroconf records */
806
2877
avahi_server_config_init(&config);
807
2878
config.publish_hinfo = 0;
808
2879
config.publish_addresses = 0;
809
2880
config.publish_workstation = 0;
810
2881
config.publish_domain = 0;
812
2883
/* Allocate a new server */
813
2884
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
814
&config, NULL, NULL, &error);
816
/* Free the configuration data */
2885
&config, NULL, NULL, &ret_errno);
2887
/* Free the Avahi configuration data */
817
2888
avahi_server_config_free(&config);
819
/* Check if creating the server object succeeded */
821
fprintf(stderr, "Failed to create server: %s\n",
822
avahi_strerror(error));
823
returncode = EXIT_FAILURE;
827
/* Create the service browser */
828
sb = avahi_s_service_browser_new(mc.server, if_index,
830
"_mandos._tcp", NULL, 0,
831
browse_callback, &mc);
833
fprintf(stderr, "Failed to create service browser: %s\n",
834
avahi_strerror(avahi_server_errno(mc.server)));
835
returncode = EXIT_FAILURE;
839
/* Run the main loop */
842
fprintf(stderr, "Starting avahi loop search\n");
845
avahi_simple_poll_loop(simple_poll);
850
fprintf(stderr, "%s exiting\n", argv[0]);
855
avahi_s_service_browser_free(sb);
858
avahi_server_free(mc.server);
861
avahi_simple_poll_free(simple_poll);
2891
/* Check if creating the Avahi server object succeeded */
2892
if(mc.server == NULL){
2893
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2894
avahi_strerror(ret_errno));
2895
exitcode = EX_UNAVAILABLE;
2903
/* Create the Avahi service browser */
2904
sb = avahi_s_service_browser_new(mc.server, if_index,
2905
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2906
NULL, 0, browse_callback,
2909
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2910
avahi_strerror(avahi_server_errno(mc.server)));
2911
exitcode = EX_UNAVAILABLE;
2919
/* Run the main loop */
2922
fprintf_plus(stderr, "Starting Avahi loop search\n");
2925
ret = avahi_loop_with_timeout(simple_poll,
2926
(int)(retry_interval * 1000), &mc);
2928
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2929
(ret == 0) ? "successfully" : "with error");
2935
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2938
/* Cleanup things */
2939
free(mc.interfaces);
2942
avahi_s_service_browser_free(sb);
2944
if(mc.server != NULL)
2945
avahi_server_free(mc.server);
2947
if(simple_poll != NULL)
2948
avahi_simple_poll_free(simple_poll);
2950
if(gnutls_initialized){
2951
gnutls_certificate_free_credentials(mc.cred);
2952
gnutls_global_deinit();
2953
gnutls_dh_params_deinit(mc.dh_params);
2956
if(gpgme_initialized){
2957
gpgme_release(mc.ctx);
2960
/* Cleans up the circular linked list of Mandos servers the client
2962
if(mc.current_server != NULL){
2963
mc.current_server->prev->next = NULL;
2964
while(mc.current_server != NULL){
2965
server *next = mc.current_server->next;
2967
#pragma GCC diagnostic push
2968
#pragma GCC diagnostic ignored "-Wcast-qual"
2970
free((char *)(mc.current_server->ip));
2972
#pragma GCC diagnostic pop
2974
free(mc.current_server);
2975
mc.current_server = next;
2979
/* Re-raise privileges */
2981
ret_errno = raise_privileges();
2984
perror_plus("Failed to raise privileges");
2987
/* Run network hooks */
2988
run_network_hooks("stop", interfaces_hooks != NULL ?
2989
interfaces_hooks : "", delay);
2991
/* Take down the network interfaces which were brought up */
2993
char *interface = NULL;
2994
while((interface=argz_next(interfaces_to_take_down,
2995
interfaces_to_take_down_size,
2997
ret_errno = take_down_interface(interface);
3000
perror_plus("Failed to take down interface");
3003
if(debug and (interfaces_to_take_down == NULL)){
3004
fprintf_plus(stderr, "No interfaces needed to be taken"
3010
ret_errno = lower_privileges_permanently();
3013
perror_plus("Failed to lower privileges permanently");
3017
free(interfaces_to_take_down);
3018
free(interfaces_hooks);
3020
/* Removes the GPGME temp directory and all files inside */
3021
if(tempdir != NULL){
3022
struct dirent **direntries = NULL;
3023
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
3027
if(tempdir_fd == -1){
3028
perror_plus("open");
3031
#if __GLIBC_PREREQ(2, 15)
3032
int numentries = scandirat(tempdir_fd, ".", &direntries,
3033
notdotentries, alphasort);
3034
#else /* not __GLIBC_PREREQ(2, 15) */
3035
int numentries = scandir(tempdir, &direntries, notdotentries,
3037
#endif /* not __GLIBC_PREREQ(2, 15) */
3038
#else /* not __GLIBC__ */
3039
int numentries = scandir(tempdir, &direntries, notdotentries,
3041
#endif /* not __GLIBC__ */
3042
if(numentries >= 0){
3043
for(int i = 0; i < numentries; i++){
3044
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
3046
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
3047
" \"%s\", 0): %s\n", tempdir,
3048
direntries[i]->d_name, strerror(errno));
3050
free(direntries[i]);
3053
/* need to clean even if 0 because man page doesn't specify */
3055
if(numentries == -1){
3056
perror_plus("scandir");
3058
ret = rmdir(tempdir);
3059
if(ret == -1 and errno != ENOENT){
3060
perror_plus("rmdir");
3063
TEMP_FAILURE_RETRY(close(tempdir_fd));
3068
sigemptyset(&old_sigterm_action.sa_mask);
3069
old_sigterm_action.sa_handler = SIG_DFL;
3070
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3071
&old_sigterm_action,
3074
perror_plus("sigaction");
3077
ret = raise(signal_received);
3078
} while(ret != 0 and errno == EINTR);
3080
perror_plus("raise");
3083
TEMP_FAILURE_RETRY(pause());