46
111
#include <avahi-common/malloc.h>
47
112
#include <avahi-common/error.h>
50
#include <sys/types.h> /* socket(), inet_pton() */
51
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
52
struct in6_addr, inet_pton() */
53
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
54
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
56
#include <unistd.h> /* close() */
57
#include <netinet/in.h>
58
#include <stdbool.h> /* true */
59
#include <string.h> /* memset */
60
#include <arpa/inet.h> /* inet_pton() */
61
#include <iso646.h> /* not */
64
#include <errno.h> /* perror() */
71
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
73
#define CERTFILE CERT_ROOT "openpgp-client.txt"
74
#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,
75
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"
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
gnutls_session_t session;
82
163
gnutls_certificate_credentials_t cred;
164
unsigned int dh_bits;
83
165
gnutls_dh_params_t dh_params;
87
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
char **new_packet, const char *homedir){
89
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,
93
ssize_t new_packet_capacity = 0;
94
ssize_t new_packet_length = 0;
95
278
gpgme_engine_info_t engine_info;
98
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");
102
321
gpgme_check_version(NULL);
103
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));
105
/* Set GPGME home directory */
106
rc = gpgme_get_engine_info (&engine_info);
107
if (rc != GPG_ERR_NO_ERROR){
108
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
109
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));
112
336
while(engine_info != NULL){
113
337
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
114
338
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
115
engine_info->file_name, homedir);
339
engine_info->file_name, tempdir);
118
342
engine_info = engine_info->next;
120
344
if(engine_info == NULL){
121
fprintf(stderr, "Could not set home dir to %s\n", homedir);
125
/* Create new GPGME data buffer from packet buffer */
126
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
127
if (rc != GPG_ERR_NO_ERROR){
128
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
129
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));
133
394
/* Create new empty GPGME data buffer for the plaintext */
134
395
rc = gpgme_data_new(&dh_plain);
135
if (rc != GPG_ERR_NO_ERROR){
136
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
137
gpgme_strsource(rc), gpgme_strerror(rc));
141
/* Create new GPGME "context" */
142
rc = gpgme_new(&ctx);
143
if (rc != GPG_ERR_NO_ERROR){
144
fprintf(stderr, "bad gpgme_new: %s: %s\n",
145
gpgme_strsource(rc), gpgme_strerror(rc));
149
/* Decrypt data from the FILE pointer to the plaintext data
151
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
152
if (rc != GPG_ERR_NO_ERROR){
153
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
154
gpgme_strsource(rc), gpgme_strerror(rc));
159
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
163
gpgme_decrypt_result_t result;
164
result = gpgme_op_decrypt_result(ctx);
166
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
168
fprintf(stderr, "Unsupported algorithm: %s\n",
169
result->unsupported_algorithm);
170
fprintf(stderr, "Wrong key usage: %d\n",
171
result->wrong_key_usage);
172
if(result->file_name != NULL){
173
fprintf(stderr, "File name: %s\n", result->file_name);
175
gpgme_recipient_t recipient;
176
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;
178
426
while(recipient != NULL){
179
fprintf(stderr, "Public key algorithm: %s\n",
180
gpgme_pubkey_algo_name(recipient->pubkey_algo));
181
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
182
fprintf(stderr, "Secret key available: %s\n",
183
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
185
434
recipient = recipient->next;
191
/* Delete the GPGME FILE pointer cryptotext data buffer */
192
gpgme_data_release(dh_crypto);
442
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
194
445
/* Seek back to the beginning of the GPGME plaintext data buffer */
195
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;
199
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
200
*new_packet = realloc(*new_packet,
201
(unsigned int)new_packet_capacity
203
if (*new_packet == NULL){
207
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;
210
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
463
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
212
465
/* Print the data, if any */
217
perror("gpgme_data_read");
220
new_packet_length += ret;
223
/* FIXME: check characters before printing to screen so to not print
224
terminal control characters */
226
/* fprintf(stderr, "decrypted password is: "); */
227
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
228
/* 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);
231
491
/* Delete the GPGME plaintext data buffer */
232
492
gpgme_data_release(dh_plain);
233
return new_packet_length;
493
return plaintext_length;
236
static const char * safer_gnutls_strerror (int value) {
237
const char *ret = gnutls_strerror (value);
496
__attribute__((warn_unused_result))
497
static const char *safer_gnutls_strerror(int value){
498
const char *ret = gnutls_strerror(value);
239
500
ret = "(unknown)";
243
void debuggnutls(__attribute__((unused)) int level,
245
fprintf(stderr, "%s", string);
504
/* GnuTLS log function callback */
505
__attribute__((nonnull))
506
static void debuggnutls(__attribute__((unused)) int level,
508
fprintf_plus(stderr, "GnuTLS: %s", string);
248
int initgnutls(encrypted_session *es){
511
__attribute__((nonnull, warn_unused_result))
512
static int init_gnutls_global(const char *pubkeyfilename,
513
const char *seckeyfilename,
253
fprintf(stderr, "Initializing GnuTLS\n");
518
fprintf_plus(stderr, "Initializing GnuTLS\n");
256
if ((ret = gnutls_global_init ())
257
!= GNUTLS_E_SUCCESS) {
258
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
521
ret = gnutls_global_init();
522
if(ret != GNUTLS_E_SUCCESS){
523
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
524
safer_gnutls_strerror(ret));
529
/* "Use a log level over 10 to enable all debugging options."
263
532
gnutls_global_set_log_level(11);
264
533
gnutls_global_set_log_function(debuggnutls);
267
/* openpgp credentials */
268
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
269
!= GNUTLS_E_SUCCESS) {
270
fprintf (stderr, "memory error: %s\n",
271
safer_gnutls_strerror(ret));
536
/* OpenPGP credentials */
537
ret = gnutls_certificate_allocate_credentials(&mc->cred);
538
if(ret != GNUTLS_E_SUCCESS){
539
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
540
safer_gnutls_strerror(ret));
541
gnutls_global_deinit();
276
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
277
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
546
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
547
" secret key %s as GnuTLS credentials\n",
281
552
ret = gnutls_certificate_set_openpgp_key_file
282
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
283
if (ret != GNUTLS_E_SUCCESS) {
285
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
287
ret, CERTFILE, KEYFILE);
288
fprintf(stdout, "The Error is: %s\n",
289
safer_gnutls_strerror(ret));
293
//GnuTLS server initialization
294
if ((ret = gnutls_dh_params_init (&es->dh_params))
295
!= GNUTLS_E_SUCCESS) {
296
fprintf (stderr, "Error in dh parameter initialization: %s\n",
297
safer_gnutls_strerror(ret));
301
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
302
!= GNUTLS_E_SUCCESS) {
303
fprintf (stderr, "Error in prime generation: %s\n",
304
safer_gnutls_strerror(ret));
308
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
310
// GnuTLS session creation
311
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
312
!= GNUTLS_E_SUCCESS){
313
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
314
safer_gnutls_strerror(ret));
317
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
318
!= GNUTLS_E_SUCCESS) {
319
fprintf(stderr, "Syntax error at: %s\n", err);
320
fprintf(stderr, "GnuTLS error: %s\n",
321
safer_gnutls_strerror(ret));
325
if ((ret = gnutls_credentials_set
326
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
327
!= GNUTLS_E_SUCCESS) {
328
fprintf(stderr, "Error setting a credentials set: %s\n",
329
safer_gnutls_strerror(ret));
553
(mc->cred, pubkeyfilename, seckeyfilename,
554
GNUTLS_OPENPGP_FMT_BASE64);
555
if(ret != GNUTLS_E_SUCCESS){
557
"Error[%d] while reading the OpenPGP key pair ('%s',"
558
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
559
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
560
safer_gnutls_strerror(ret));
564
/* GnuTLS server initialization */
565
ret = gnutls_dh_params_init(&mc->dh_params);
566
if(ret != GNUTLS_E_SUCCESS){
567
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
568
" initialization: %s\n",
569
safer_gnutls_strerror(ret));
572
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
573
if(ret != GNUTLS_E_SUCCESS){
574
fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
575
safer_gnutls_strerror(ret));
579
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
585
gnutls_certificate_free_credentials(mc->cred);
586
gnutls_global_deinit();
587
gnutls_dh_params_deinit(mc->dh_params);
591
__attribute__((nonnull, warn_unused_result))
592
static int init_gnutls_session(gnutls_session_t *session,
595
/* GnuTLS session creation */
597
ret = gnutls_init(session, GNUTLS_SERVER);
601
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
602
if(ret != GNUTLS_E_SUCCESS){
604
"Error in GnuTLS session initialization: %s\n",
605
safer_gnutls_strerror(ret));
611
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
613
gnutls_deinit(*session);
616
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
617
if(ret != GNUTLS_E_SUCCESS){
618
fprintf_plus(stderr, "Syntax error at: %s\n", err);
619
fprintf_plus(stderr, "GnuTLS error: %s\n",
620
safer_gnutls_strerror(ret));
621
gnutls_deinit(*session);
627
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
630
gnutls_deinit(*session);
633
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
634
if(ret != GNUTLS_E_SUCCESS){
635
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
636
safer_gnutls_strerror(ret));
637
gnutls_deinit(*session);
333
641
/* ignore client certificate if any. */
334
gnutls_certificate_server_set_request (es->session,
642
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
337
gnutls_dh_set_prime_bits (es->session, DH_BITS);
644
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
342
void empty_log(__attribute__((unused)) AvahiLogLevel level,
343
__attribute__((unused)) const char *txt){}
345
int start_mandos_communication(const char *ip, uint16_t port,
346
unsigned int if_index){
348
struct sockaddr_in6 to;
349
encrypted_session es;
649
/* Avahi log function callback */
650
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
651
__attribute__((unused)) const char *txt){}
653
/* Set effective uid to 0, return errno */
654
__attribute__((warn_unused_result))
655
error_t raise_privileges(void){
656
error_t old_errno = errno;
657
error_t ret_errno = 0;
658
if(seteuid(0) == -1){
665
/* Set effective and real user ID to 0. Return errno. */
666
__attribute__((warn_unused_result))
667
error_t raise_privileges_permanently(void){
668
error_t old_errno = errno;
669
error_t ret_errno = raise_privileges();
681
/* Set effective user ID to unprivileged saved user ID */
682
__attribute__((warn_unused_result))
683
error_t lower_privileges(void){
684
error_t old_errno = errno;
685
error_t ret_errno = 0;
686
if(seteuid(uid) == -1){
693
/* Lower privileges permanently */
694
__attribute__((warn_unused_result))
695
error_t lower_privileges_permanently(void){
696
error_t old_errno = errno;
697
error_t ret_errno = 0;
698
if(setuid(uid) == -1){
705
/* Helper function to add_local_route() and delete_local_route() */
706
__attribute__((nonnull, warn_unused_result))
707
static bool add_delete_local_route(const bool add,
709
AvahiIfIndex if_index){
711
char helper[] = "mandos-client-iprouteadddel";
712
char add_arg[] = "add";
713
char delete_arg[] = "delete";
714
char debug_flag[] = "--debug";
715
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
716
if(pluginhelperdir == NULL){
718
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
719
" variable not set; cannot run helper\n");
724
char interface[IF_NAMESIZE];
725
if(if_indextoname((unsigned int)if_index, interface) == NULL){
726
perror_plus("if_indextoname");
730
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
732
perror_plus("open(\"/dev/null\", O_RDONLY)");
738
/* Raise privileges */
739
errno = raise_privileges_permanently();
741
perror_plus("Failed to raise privileges");
742
/* _exit(EX_NOPERM); */
748
perror_plus("setgid");
751
/* Reset supplementary groups */
753
ret = setgroups(0, NULL);
755
perror_plus("setgroups");
759
ret = dup2(devnull, STDIN_FILENO);
761
perror_plus("dup2(devnull, STDIN_FILENO)");
764
ret = (int)TEMP_FAILURE_RETRY(close(devnull));
766
perror_plus("close");
769
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
771
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
774
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
779
if(helperdir_fd == -1){
781
_exit(EX_UNAVAILABLE);
783
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
786
perror_plus("openat");
787
_exit(EX_UNAVAILABLE);
789
TEMP_FAILURE_RETRY(close(helperdir_fd));
791
#pragma GCC diagnostic push
792
#pragma GCC diagnostic ignored "-Wcast-qual"
794
if(fexecve(helper_fd, (char *const [])
795
{ helper, add ? add_arg : delete_arg, (char *)address,
796
interface, debug ? debug_flag : NULL, NULL },
799
#pragma GCC diagnostic pop
801
perror_plus("fexecve");
813
pret = waitpid(pid, &status, 0);
814
if(pret == -1 and errno == EINTR and quit_now){
815
int errno_raising = 0;
816
if((errno = raise_privileges()) != 0){
817
errno_raising = errno;
818
perror_plus("Failed to raise privileges in order to"
819
" kill helper program");
821
if(kill(pid, SIGTERM) == -1){
824
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
825
perror_plus("Failed to lower privileges after killing"
830
} while(pret == -1 and errno == EINTR);
832
perror_plus("waitpid");
835
if(WIFEXITED(status)){
836
if(WEXITSTATUS(status) != 0){
837
fprintf_plus(stderr, "Error: iprouteadddel exited"
838
" with status %d\n", WEXITSTATUS(status));
843
if(WIFSIGNALED(status)){
844
fprintf_plus(stderr, "Error: iprouteadddel died by"
845
" signal %d\n", WTERMSIG(status));
848
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
852
__attribute__((nonnull, warn_unused_result))
853
static bool add_local_route(const char *address,
854
AvahiIfIndex if_index){
856
fprintf_plus(stderr, "Adding route to %s\n", address);
858
return add_delete_local_route(true, address, if_index);
861
__attribute__((nonnull, warn_unused_result))
862
static bool delete_local_route(const char *address,
863
AvahiIfIndex if_index){
865
fprintf_plus(stderr, "Removing route to %s\n", address);
867
return add_delete_local_route(false, address, if_index);
870
/* Called when a Mandos server is found */
871
__attribute__((nonnull, warn_unused_result))
872
static int start_mandos_communication(const char *ip, in_port_t port,
873
AvahiIfIndex if_index,
874
int af, mandos_context *mc){
875
int ret, tcp_sd = -1;
877
struct sockaddr_storage to;
350
878
char *buffer = NULL;
351
char *decrypted_buffer;
879
char *decrypted_buffer = NULL;
352
880
size_t buffer_length = 0;
353
881
size_t buffer_capacity = 0;
354
ssize_t decrypted_buffer_size;
357
char interface[IF_NAMESIZE];
360
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
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);
884
gnutls_session_t session;
885
int pf; /* Protocol family */
886
bool route_added = false;
903
fprintf_plus(stderr, "Bad address family: %d\n", af);
908
/* If the interface is specified and we have a list of interfaces */
909
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
910
/* Check if the interface is one of the interfaces we are using */
913
char *interface = NULL;
914
while((interface=argz_next(mc->interfaces, mc->interfaces_size,
916
if(if_nametoindex(interface) == (unsigned int)if_index){
923
/* This interface does not match any in the list, so we don't
924
connect to the server */
926
char interface[IF_NAMESIZE];
927
if(if_indextoname((unsigned int)if_index, interface) == NULL){
928
perror_plus("if_indextoname");
930
fprintf_plus(stderr, "Skipping server on non-used interface"
932
if_indextoname((unsigned int)if_index,
940
ret = init_gnutls_session(&session, mc);
946
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
947
PRIuMAX "\n", ip, (uintmax_t)port);
950
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
953
perror_plus("socket");
963
memset(&to, 0, sizeof(to));
965
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
966
ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
968
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
969
ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
973
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, port %d\n", ip, port);
398
/* char addrstr[INET6_ADDRSTRLEN]; */
399
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
400
/* sizeof(addrstr)) == NULL){ */
401
/* perror("inet_ntop"); */
403
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
404
/* addrstr, ntohs(to.sin6_port)); */
408
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
414
ret = initgnutls (&es);
420
gnutls_transport_set_ptr (es.session,
421
(gnutls_transport_ptr_t) tcp_sd);
424
fprintf(stderr, "Establishing TLS session with %s\n", ip);
427
ret = gnutls_handshake (es.session);
429
if (ret != GNUTLS_E_SUCCESS){
979
fprintf_plus(stderr, "Bad address: %s\n", ip);
984
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
985
if(IN6_IS_ADDR_LINKLOCAL
986
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
987
if(if_index == AVAHI_IF_UNSPEC){
988
fprintf_plus(stderr, "An IPv6 link-local address is"
989
" incomplete without a network interface\n");
993
/* Set the network interface number as scope */
994
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
997
((struct sockaddr_in *)&to)->sin_port = htons(port);
1006
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1007
char interface[IF_NAMESIZE];
1008
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1009
perror_plus("if_indextoname");
1011
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1012
"\n", ip, interface, (uintmax_t)port);
1015
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1016
ip, (uintmax_t)port);
1018
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1019
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1021
ret = getnameinfo((struct sockaddr *)&to,
1022
sizeof(struct sockaddr_in6),
1023
addrstr, sizeof(addrstr), NULL, 0,
1026
ret = getnameinfo((struct sockaddr *)&to,
1027
sizeof(struct sockaddr_in),
1028
addrstr, sizeof(addrstr), NULL, 0,
1031
if(ret == EAI_SYSTEM){
1032
perror_plus("getnameinfo");
1033
} else if(ret != 0) {
1034
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1035
} else if(strcmp(addrstr, ip) != 0){
1036
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1047
ret = connect(tcp_sd, (struct sockaddr *)&to,
1048
sizeof(struct sockaddr_in6));
1050
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1051
sizeof(struct sockaddr_in));
1054
if(errno == ENETUNREACH
1055
and if_index != AVAHI_IF_UNSPEC
1056
and connect_to == NULL
1057
and not route_added and
1058
((af == AF_INET6 and not
1059
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1061
or (af == AF_INET and
1062
/* Not a a IPv4LL address */
1063
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1064
& 0xFFFF0000L) != 0xA9FE0000L))){
1065
/* Work around Avahi bug - Avahi does not announce link-local
1066
addresses if it has a global address, so local hosts with
1067
*only* a link-local address (e.g. Mandos clients) cannot
1068
connect to a Mandos server announced by Avahi on a server
1069
host with a global address. Work around this by retrying
1070
with an explicit route added with the server's address.
1072
Avahi bug reference:
1073
http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1074
https://bugs.debian.org/587961
1077
fprintf_plus(stderr, "Mandos server unreachable, trying"
1081
route_added = add_local_route(ip, if_index);
1087
if(errno != ECONNREFUSED or debug){
1089
perror_plus("connect");
1102
const char *out = mandos_protocol_version;
1105
size_t out_size = strlen(out);
1106
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1107
out_size - written));
1110
perror_plus("write");
1114
written += (size_t)ret;
1115
if(written < out_size){
1118
if(out == mandos_protocol_version){
1133
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1141
/* This casting via intptr_t is to eliminate warning about casting
1142
an int to a pointer type. This is exactly how the GnuTLS Guile
1143
function "set-session-transport-fd!" does it. */
1144
gnutls_transport_set_ptr(session,
1145
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1153
ret = gnutls_handshake(session);
1158
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1160
if(ret != GNUTLS_E_SUCCESS){
431
fprintf(stderr, "\n*** Handshake failed ***\n");
1162
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
438
//Retrieve OpenPGP packet that contains the wanted password
1169
/* Read OpenPGP packet that contains the wanted password */
441
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1172
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
446
if (buffer_length + BUFFER_SIZE > buffer_capacity){
447
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
452
buffer_capacity += BUFFER_SIZE;
455
ret = gnutls_record_recv
456
(es.session, buffer+buffer_length, BUFFER_SIZE);
1183
buffer_capacity = incbuffer(&buffer, buffer_length,
1185
if(buffer_capacity == 0){
1187
perror_plus("incbuffer");
1197
sret = gnutls_record_recv(session, buffer+buffer_length,
462
1204
case GNUTLS_E_INTERRUPTED:
463
1205
case GNUTLS_E_AGAIN:
465
1207
case GNUTLS_E_REHANDSHAKE:
466
ret = gnutls_handshake (es.session);
468
fprintf(stderr, "\n*** Handshake failed ***\n");
1209
ret = gnutls_handshake(session);
1215
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1217
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
475
fprintf(stderr, "Unknown error while reading data from"
476
" encrypted session with mandos server\n");
478
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1225
fprintf_plus(stderr, "Unknown error while reading data from"
1226
" encrypted session with Mandos server\n");
1227
gnutls_bye(session, GNUTLS_SHUT_RDWR);
482
buffer_length += (size_t) ret;
486
if (buffer_length > 0){
487
decrypted_buffer_size = pgp_packet_decrypt(buffer,
491
if (decrypted_buffer_size >= 0){
1232
buffer_length += (size_t) sret;
1237
fprintf_plus(stderr, "Closing TLS session\n");
1246
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1251
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1253
if(buffer_length > 0){
1254
ssize_t decrypted_buffer_size;
1255
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1256
&decrypted_buffer, mc);
1257
if(decrypted_buffer_size >= 0){
492
1260
while(written < (size_t) decrypted_buffer_size){
493
ret = (int)fwrite (decrypted_buffer + written, 1,
494
(size_t)decrypted_buffer_size - written,
1266
ret = (int)fwrite(decrypted_buffer + written, 1,
1267
(size_t)decrypted_buffer_size - written,
496
1269
if(ret == 0 and ferror(stdout)){
498
fprintf(stderr, "Error writing encrypted data: %s\n",
1272
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
504
1278
written += (size_t)ret;
506
free(decrypted_buffer);
1284
/* Shutdown procedure */
1289
if(not delete_local_route(ip, if_index)){
1290
fprintf_plus(stderr, "Failed to delete local route to %s on"
1291
" interface %d", ip, if_index);
1295
free(decrypted_buffer);
1298
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1304
perror_plus("close");
1306
gnutls_deinit(session);
515
fprintf(stderr, "Closing TLS session\n");
519
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
522
gnutls_deinit (es.session);
523
gnutls_certificate_free_credentials (es.cred);
524
gnutls_global_deinit ();
528
static AvahiSimplePoll *simple_poll = NULL;
529
static AvahiServer *server = NULL;
531
static void resolve_callback(
532
AvahiSServiceResolver *r,
533
AvahiIfIndex interface,
534
AVAHI_GCC_UNUSED AvahiProtocol protocol,
535
AvahiResolverEvent event,
539
const char *host_name,
540
const AvahiAddress *address,
542
AVAHI_GCC_UNUSED AvahiStringList *txt,
543
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
544
AVAHI_GCC_UNUSED void* userdata) {
546
assert(r); /* Spurious warning */
1316
__attribute__((nonnull))
1317
static void resolve_callback(AvahiSServiceResolver *r,
1318
AvahiIfIndex interface,
1319
AvahiProtocol proto,
1320
AvahiResolverEvent event,
1324
const char *host_name,
1325
const AvahiAddress *address,
1327
AVAHI_GCC_UNUSED AvahiStringList *txt,
1328
AVAHI_GCC_UNUSED AvahiLookupResultFlags
548
1335
/* Called whenever a service has been resolved successfully or
1339
avahi_s_service_resolver_free(r);
553
1345
case AVAHI_RESOLVER_FAILURE:
554
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
555
" type '%s' in domain '%s': %s\n", name, type, domain,
556
avahi_strerror(avahi_server_errno(server)));
1346
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1347
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1349
avahi_strerror(avahi_server_errno
1350
(((mandos_context*)mc)->server)));
559
1353
case AVAHI_RESOLVER_FOUND:
561
1355
char ip[AVAHI_ADDRESS_STR_MAX];
562
1356
avahi_address_snprint(ip, sizeof(ip), address);
564
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
565
" port %d\n", name, host_name, ip, port);
1358
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1359
PRIdMAX ") on port %" PRIu16 "\n", name,
1360
host_name, ip, (intmax_t)interface, port);
567
int ret = start_mandos_communication(ip, port,
568
(unsigned int) interface);
1362
int ret = start_mandos_communication(ip, (in_port_t)port,
1364
avahi_proto_to_af(proto),
1367
avahi_simple_poll_quit(simple_poll);
1369
if(not add_server(ip, (in_port_t)port, interface,
1370
avahi_proto_to_af(proto),
1371
&((mandos_context*)mc)->current_server)){
1372
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
574
1378
avahi_s_service_resolver_free(r);
577
static void browse_callback(
578
AvahiSServiceBrowser *b,
579
AvahiIfIndex interface,
580
AvahiProtocol protocol,
581
AvahiBrowserEvent event,
585
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
588
AvahiServer *s = userdata;
589
assert(b); /* Spurious warning */
591
/* Called whenever a new services becomes available on the LAN or
592
is removed from the LAN */
1381
static void browse_callback(AvahiSServiceBrowser *b,
1382
AvahiIfIndex interface,
1383
AvahiProtocol protocol,
1384
AvahiBrowserEvent event,
1388
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1395
/* Called whenever a new services becomes available on the LAN or
1396
is removed from the LAN */
1404
case AVAHI_BROWSER_FAILURE:
1406
fprintf_plus(stderr, "(Avahi browser) %s\n",
1407
avahi_strerror(avahi_server_errno
1408
(((mandos_context*)mc)->server)));
1409
avahi_simple_poll_quit(simple_poll);
1412
case AVAHI_BROWSER_NEW:
1413
/* We ignore the returned Avahi resolver object. In the callback
1414
function we free it. If the Avahi server is terminated before
1415
the callback function is called the Avahi server will free the
1418
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1419
interface, protocol, name, type,
1420
domain, protocol, 0,
1421
resolve_callback, mc) == NULL)
1422
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1424
avahi_strerror(avahi_server_errno
1425
(((mandos_context*)mc)->server)));
1428
case AVAHI_BROWSER_REMOVE:
1431
case AVAHI_BROWSER_ALL_FOR_NOW:
1432
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1434
fprintf_plus(stderr, "No Mandos server found, still"
1441
/* Signal handler that stops main loop after SIGTERM */
1442
static void handle_sigterm(int sig){
1447
signal_received = sig;
1448
int old_errno = errno;
1449
/* set main loop to exit */
1450
if(simple_poll != NULL){
1451
avahi_simple_poll_quit(simple_poll);
1456
__attribute__((nonnull, warn_unused_result))
1457
bool get_flags(const char *ifname, struct ifreq *ifr){
1461
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1464
perror_plus("socket");
1468
strcpy(ifr->ifr_name, ifname);
1469
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1473
perror_plus("ioctl SIOCGIFFLAGS");
1481
__attribute__((nonnull, warn_unused_result))
1482
bool good_flags(const char *ifname, const struct ifreq *ifr){
1484
/* Reject the loopback device */
1485
if(ifr->ifr_flags & IFF_LOOPBACK){
1487
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1492
/* Accept point-to-point devices only if connect_to is specified */
1493
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1495
fprintf_plus(stderr, "Accepting point-to-point interface"
1496
" \"%s\"\n", ifname);
1500
/* Otherwise, reject non-broadcast-capable devices */
1501
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1503
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1504
" \"%s\"\n", ifname);
1508
/* Reject non-ARP interfaces (including dummy interfaces) */
1509
if(ifr->ifr_flags & IFF_NOARP){
1511
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1517
/* Accept this device */
1519
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1525
* This function determines if a directory entry in /sys/class/net
1526
* corresponds to an acceptable network device.
1527
* (This function is passed to scandir(3) as a filter function.)
1529
__attribute__((nonnull, warn_unused_result))
1530
int good_interface(const struct dirent *if_entry){
1531
if(if_entry->d_name[0] == '.'){
1536
if(not get_flags(if_entry->d_name, &ifr)){
1538
fprintf_plus(stderr, "Failed to get flags for interface "
1539
"\"%s\"\n", if_entry->d_name);
1544
if(not good_flags(if_entry->d_name, &ifr)){
1551
* This function determines if a network interface is up.
1553
__attribute__((nonnull, warn_unused_result))
1554
bool interface_is_up(const char *interface){
1556
if(not get_flags(interface, &ifr)){
1558
fprintf_plus(stderr, "Failed to get flags for interface "
1559
"\"%s\"\n", interface);
1564
return (bool)(ifr.ifr_flags & IFF_UP);
1568
* This function determines if a network interface is running
1570
__attribute__((nonnull, warn_unused_result))
1571
bool interface_is_running(const char *interface){
1573
if(not get_flags(interface, &ifr)){
1575
fprintf_plus(stderr, "Failed to get flags for interface "
1576
"\"%s\"\n", interface);
1581
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1584
__attribute__((nonnull, pure, warn_unused_result))
1585
int notdotentries(const struct dirent *direntry){
1586
/* Skip "." and ".." */
1587
if(direntry->d_name[0] == '.'
1588
and (direntry->d_name[1] == '\0'
1589
or (direntry->d_name[1] == '.'
1590
and direntry->d_name[2] == '\0'))){
1596
/* Is this directory entry a runnable program? */
1597
__attribute__((nonnull, warn_unused_result))
1598
int runnable_hook(const struct dirent *direntry){
1603
if((direntry->d_name)[0] == '\0'){
1608
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1609
"abcdefghijklmnopqrstuvwxyz"
1612
if((direntry->d_name)[sret] != '\0'){
1613
/* Contains non-allowed characters */
1615
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1621
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1624
perror_plus("Could not stat hook");
1628
if(not (S_ISREG(st.st_mode))){
1629
/* Not a regular file */
1631
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1636
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1637
/* Not executable */
1639
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1645
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1651
__attribute__((nonnull, warn_unused_result))
1652
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1653
mandos_context *mc){
1655
struct timespec now;
1656
struct timespec waited_time;
1657
intmax_t block_time;
1660
if(mc->current_server == NULL){
1662
fprintf_plus(stderr, "Wait until first server is found."
1665
ret = avahi_simple_poll_iterate(s, -1);
1668
fprintf_plus(stderr, "Check current_server if we should run"
1671
/* the current time */
1672
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1674
perror_plus("clock_gettime");
1677
/* Calculating in ms how long time between now and server
1678
who we visted longest time ago. Now - last seen. */
1679
waited_time.tv_sec = (now.tv_sec
1680
- mc->current_server->last_seen.tv_sec);
1681
waited_time.tv_nsec = (now.tv_nsec
1682
- mc->current_server->last_seen.tv_nsec);
1683
/* total time is 10s/10,000ms.
1684
Converting to s from ms by dividing by 1,000,
1685
and ns to ms by dividing by 1,000,000. */
1686
block_time = ((retry_interval
1687
- ((intmax_t)waited_time.tv_sec * 1000))
1688
- ((intmax_t)waited_time.tv_nsec / 1000000));
1691
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1695
if(block_time <= 0){
1696
ret = start_mandos_communication(mc->current_server->ip,
1697
mc->current_server->port,
1698
mc->current_server->if_index,
1699
mc->current_server->af, mc);
1701
avahi_simple_poll_quit(s);
1704
ret = clock_gettime(CLOCK_MONOTONIC,
1705
&mc->current_server->last_seen);
1707
perror_plus("clock_gettime");
1710
mc->current_server = mc->current_server->next;
1711
block_time = 0; /* Call avahi to find new Mandos
1712
servers, but don't block */
1715
ret = avahi_simple_poll_iterate(s, (int)block_time);
1718
if(ret > 0 or errno != EINTR){
1719
return (ret != 1) ? ret : 0;
1725
__attribute__((nonnull))
1726
void run_network_hooks(const char *mode, const char *interface,
1728
struct dirent **direntries = NULL;
1729
if(hookdir_fd == -1){
1730
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1732
if(hookdir_fd == -1){
1733
if(errno == ENOENT){
1735
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1736
" found\n", hookdir);
1739
perror_plus("open");
1745
#if __GLIBC_PREREQ(2, 15)
1746
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1747
runnable_hook, alphasort);
1748
#else /* not __GLIBC_PREREQ(2, 15) */
1749
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1751
#endif /* not __GLIBC_PREREQ(2, 15) */
1752
#else /* not __GLIBC__ */
1753
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1755
#endif /* not __GLIBC__ */
1757
perror_plus("scandir");
1760
struct dirent *direntry;
1762
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1764
perror_plus("open(\"/dev/null\", O_RDONLY)");
1767
for(int i = 0; i < numhooks; i++){
1768
direntry = direntries[i];
1770
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1773
pid_t hook_pid = fork();
1776
/* Raise privileges */
1777
errno = raise_privileges_permanently();
1779
perror_plus("Failed to raise privileges");
1786
perror_plus("setgid");
1789
/* Reset supplementary groups */
1791
ret = setgroups(0, NULL);
1793
perror_plus("setgroups");
1796
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1798
perror_plus("setenv");
1801
ret = setenv("DEVICE", interface, 1);
1803
perror_plus("setenv");
1806
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1808
perror_plus("setenv");
1811
ret = setenv("MODE", mode, 1);
1813
perror_plus("setenv");
1817
ret = asprintf(&delaystring, "%f", (double)delay);
1819
perror_plus("asprintf");
1822
ret = setenv("DELAY", delaystring, 1);
1825
perror_plus("setenv");
1829
if(connect_to != NULL){
1830
ret = setenv("CONNECT", connect_to, 1);
1832
perror_plus("setenv");
1836
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
1840
perror_plus("openat");
1841
_exit(EXIT_FAILURE);
1843
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1844
perror_plus("close");
1845
_exit(EXIT_FAILURE);
1847
ret = dup2(devnull, STDIN_FILENO);
1849
perror_plus("dup2(devnull, STDIN_FILENO)");
1852
ret = (int)TEMP_FAILURE_RETRY(close(devnull));
1854
perror_plus("close");
1857
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1859
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1862
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
1864
perror_plus("fexecve");
1865
_exit(EXIT_FAILURE);
1869
perror_plus("fork");
1874
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1875
perror_plus("waitpid");
1879
if(WIFEXITED(status)){
1880
if(WEXITSTATUS(status) != 0){
1881
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1882
" with status %d\n", direntry->d_name,
1883
WEXITSTATUS(status));
1887
} else if(WIFSIGNALED(status)){
1888
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1889
" signal %d\n", direntry->d_name,
1894
fprintf_plus(stderr, "Warning: network hook \"%s\""
1895
" crashed\n", direntry->d_name);
1901
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1907
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1908
perror_plus("close");
1915
__attribute__((nonnull, warn_unused_result))
1916
error_t bring_up_interface(const char *const interface,
1918
error_t old_errno = errno;
1920
struct ifreq network;
1921
unsigned int if_index = if_nametoindex(interface);
1923
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1933
if(not interface_is_up(interface)){
1934
error_t ret_errno = 0, ioctl_errno = 0;
1935
if(not get_flags(interface, &network)){
1937
fprintf_plus(stderr, "Failed to get flags for interface "
1938
"\"%s\"\n", interface);
1942
network.ifr_flags |= IFF_UP; /* set flag */
1944
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1947
perror_plus("socket");
1953
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1955
perror_plus("close");
1962
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
1966
/* Raise privileges */
1967
ret_errno = raise_privileges();
1970
perror_plus("Failed to raise privileges");
1975
bool restore_loglevel = false;
1977
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1978
messages about the network interface to mess up the prompt */
1979
ret_linux = klogctl(8, NULL, 5);
1980
if(ret_linux == -1){
1981
perror_plus("klogctl");
1983
restore_loglevel = true;
1986
#endif /* __linux__ */
1987
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1988
ioctl_errno = errno;
1990
if(restore_loglevel){
1991
ret_linux = klogctl(7, NULL, 0);
1992
if(ret_linux == -1){
1993
perror_plus("klogctl");
1996
#endif /* __linux__ */
1998
/* If raise_privileges() succeeded above */
2000
/* Lower privileges */
2001
ret_errno = lower_privileges();
2004
perror_plus("Failed to lower privileges");
2008
/* Close the socket */
2009
ret = (int)TEMP_FAILURE_RETRY(close(sd));
2011
perror_plus("close");
2014
if(ret_setflags == -1){
2015
errno = ioctl_errno;
2016
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2021
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2025
/* Sleep checking until interface is running.
2026
Check every 0.25s, up to total time of delay */
2027
for(int i=0; i < delay * 4; i++){
2028
if(interface_is_running(interface)){
2031
struct timespec sleeptime = { .tv_nsec = 250000000 };
2032
ret = nanosleep(&sleeptime, NULL);
2033
if(ret == -1 and errno != EINTR){
2034
perror_plus("nanosleep");
2042
__attribute__((nonnull, warn_unused_result))
2043
error_t take_down_interface(const char *const interface){
2044
error_t old_errno = errno;
2045
struct ifreq network;
2046
unsigned int if_index = if_nametoindex(interface);
2048
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2052
if(interface_is_up(interface)){
2053
error_t ret_errno = 0, ioctl_errno = 0;
2054
if(not get_flags(interface, &network) and debug){
2056
fprintf_plus(stderr, "Failed to get flags for interface "
2057
"\"%s\"\n", interface);
2061
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2063
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2066
perror_plus("socket");
2072
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2076
/* Raise privileges */
2077
ret_errno = raise_privileges();
2080
perror_plus("Failed to raise privileges");
2083
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2084
ioctl_errno = errno;
2086
/* If raise_privileges() succeeded above */
2088
/* Lower privileges */
2089
ret_errno = lower_privileges();
2092
perror_plus("Failed to lower privileges");
2096
/* Close the socket */
2097
int ret = (int)TEMP_FAILURE_RETRY(close(sd));
2099
perror_plus("close");
2102
if(ret_setflags == -1){
2103
errno = ioctl_errno;
2104
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2109
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2117
int main(int argc, char *argv[]){
2118
mandos_context mc = { .server = NULL, .dh_bits = 1024,
2119
.priority = "SECURE256:!CTYPE-X.509:"
2120
"+CTYPE-OPENPGP", .current_server = NULL,
2121
.interfaces = NULL, .interfaces_size = 0 };
2122
AvahiSServiceBrowser *sb = NULL;
2127
int exitcode = EXIT_SUCCESS;
2128
char *interfaces_to_take_down = NULL;
2129
size_t interfaces_to_take_down_size = 0;
2130
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2131
char old_tempdir[] = "/tmp/mandosXXXXXX";
2132
char *tempdir = NULL;
2133
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2134
const char *seckey = PATHDIR "/" SECKEY;
2135
const char *pubkey = PATHDIR "/" PUBKEY;
2136
char *interfaces_hooks = NULL;
2138
bool gnutls_initialized = false;
2139
bool gpgme_initialized = false;
2141
double retry_interval = 10; /* 10s between trying a server and
2142
retrying the same server again */
2144
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2145
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2150
/* Lower any group privileges we might have, just to be safe */
2154
perror_plus("setgid");
2157
/* Lower user privileges (temporarily) */
2161
perror_plus("seteuid");
2169
struct argp_option options[] = {
2170
{ .name = "debug", .key = 128,
2171
.doc = "Debug mode", .group = 3 },
2172
{ .name = "connect", .key = 'c',
2173
.arg = "ADDRESS:PORT",
2174
.doc = "Connect directly to a specific Mandos server",
2176
{ .name = "interface", .key = 'i',
2178
.doc = "Network interface that will be used to search for"
2181
{ .name = "seckey", .key = 's',
2183
.doc = "OpenPGP secret key file base name",
2185
{ .name = "pubkey", .key = 'p',
2187
.doc = "OpenPGP public key file base name",
2189
{ .name = "dh-bits", .key = 129,
2191
.doc = "Bit length of the prime number used in the"
2192
" Diffie-Hellman key exchange",
2194
{ .name = "priority", .key = 130,
2196
.doc = "GnuTLS priority string for the TLS handshake",
2198
{ .name = "delay", .key = 131,
2200
.doc = "Maximum delay to wait for interface startup",
2202
{ .name = "retry", .key = 132,
2204
.doc = "Retry interval used when denied by the Mandos server",
2206
{ .name = "network-hook-dir", .key = 133,
2208
.doc = "Directory where network hooks are located",
2211
* These reproduce what we would get without ARGP_NO_HELP
2213
{ .name = "help", .key = '?',
2214
.doc = "Give this help list", .group = -1 },
2215
{ .name = "usage", .key = -3,
2216
.doc = "Give a short usage message", .group = -1 },
2217
{ .name = "version", .key = 'V',
2218
.doc = "Print program version", .group = -1 },
2222
error_t parse_opt(int key, char *arg,
2223
struct argp_state *state){
2226
case 128: /* --debug */
2229
case 'c': /* --connect */
2232
case 'i': /* --interface */
2233
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2236
argp_error(state, "%s", strerror(ret_errno));
2239
case 's': /* --seckey */
2242
case 'p': /* --pubkey */
2245
case 129: /* --dh-bits */
2247
tmpmax = strtoimax(arg, &tmp, 10);
2248
if(errno != 0 or tmp == arg or *tmp != '\0'
2249
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2250
argp_error(state, "Bad number of DH bits");
2252
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2254
case 130: /* --priority */
2257
case 131: /* --delay */
2259
delay = strtof(arg, &tmp);
2260
if(errno != 0 or tmp == arg or *tmp != '\0'){
2261
argp_error(state, "Bad delay");
2263
case 132: /* --retry */
2265
retry_interval = strtod(arg, &tmp);
2266
if(errno != 0 or tmp == arg or *tmp != '\0'
2267
or (retry_interval * 1000) > INT_MAX
2268
or retry_interval < 0){
2269
argp_error(state, "Bad retry interval");
2272
case 133: /* --network-hook-dir */
2276
* These reproduce what we would get without ARGP_NO_HELP
2278
case '?': /* --help */
2279
argp_state_help(state, state->out_stream,
2280
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2281
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2282
case -3: /* --usage */
2283
argp_state_help(state, state->out_stream,
2284
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2285
case 'V': /* --version */
2286
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2287
exit(argp_err_exit_status);
2290
return ARGP_ERR_UNKNOWN;
2295
struct argp argp = { .options = options, .parser = parse_opt,
2297
.doc = "Mandos client -- Get and decrypt"
2298
" passwords from a Mandos server" };
2299
ret = argp_parse(&argp, argc, argv,
2300
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
596
case AVAHI_BROWSER_FAILURE:
598
fprintf(stderr, "(Browser) %s\n",
599
avahi_strerror(avahi_server_errno(server)));
600
avahi_simple_poll_quit(simple_poll);
603
case AVAHI_BROWSER_NEW:
604
/* We ignore the returned resolver object. In the callback
605
function we free it. If the server is terminated before
606
the callback function is called the server will free
607
the resolver for us. */
609
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
611
AVAHI_PROTO_INET6, 0,
612
resolve_callback, s)))
613
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
614
avahi_strerror(avahi_server_errno(s)));
617
case AVAHI_BROWSER_REMOVE:
620
case AVAHI_BROWSER_ALL_FOR_NOW:
621
case AVAHI_BROWSER_CACHE_EXHAUSTED:
626
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
2307
perror_plus("argp_parse");
2308
exitcode = EX_OSERR;
2311
exitcode = EX_USAGE;
2317
/* Work around Debian bug #633582:
2318
<http://bugs.debian.org/633582> */
2320
/* Re-raise privileges */
2321
ret_errno = raise_privileges();
2324
perror_plus("Failed to raise privileges");
2328
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2329
int seckey_fd = open(seckey, O_RDONLY);
2330
if(seckey_fd == -1){
2331
perror_plus("open");
2333
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2335
perror_plus("fstat");
2337
if(S_ISREG(st.st_mode)
2338
and st.st_uid == 0 and st.st_gid == 0){
2339
ret = fchown(seckey_fd, uid, gid);
2341
perror_plus("fchown");
2345
TEMP_FAILURE_RETRY(close(seckey_fd));
2349
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2350
int pubkey_fd = open(pubkey, O_RDONLY);
2351
if(pubkey_fd == -1){
2352
perror_plus("open");
2354
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2356
perror_plus("fstat");
2358
if(S_ISREG(st.st_mode)
2359
and st.st_uid == 0 and st.st_gid == 0){
2360
ret = fchown(pubkey_fd, uid, gid);
2362
perror_plus("fchown");
2366
TEMP_FAILURE_RETRY(close(pubkey_fd));
2370
/* Lower privileges */
2371
ret_errno = lower_privileges();
2374
perror_plus("Failed to lower privileges");
2379
/* Remove invalid interface names (except "none") */
2381
char *interface = NULL;
2382
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2384
if(strcmp(interface, "none") != 0
2385
and if_nametoindex(interface) == 0){
2386
if(interface[0] != '\0'){
2387
fprintf_plus(stderr, "Not using nonexisting interface"
2388
" \"%s\"\n", interface);
2390
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2396
/* Run network hooks */
2398
if(mc.interfaces != NULL){
2399
interfaces_hooks = malloc(mc.interfaces_size);
2400
if(interfaces_hooks == NULL){
2401
perror_plus("malloc");
2404
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2405
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2407
run_network_hooks("start", interfaces_hooks != NULL ?
2408
interfaces_hooks : "", delay);
2412
avahi_set_log_function(empty_log);
2415
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2416
from the signal handler */
2417
/* Initialize the pseudo-RNG for Avahi */
2418
srand((unsigned int) time(NULL));
2419
simple_poll = avahi_simple_poll_new();
2420
if(simple_poll == NULL){
2421
fprintf_plus(stderr,
2422
"Avahi: Failed to create simple poll object.\n");
2423
exitcode = EX_UNAVAILABLE;
2427
sigemptyset(&sigterm_action.sa_mask);
2428
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2430
perror_plus("sigaddset");
2431
exitcode = EX_OSERR;
2434
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2436
perror_plus("sigaddset");
2437
exitcode = EX_OSERR;
2440
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2442
perror_plus("sigaddset");
2443
exitcode = EX_OSERR;
2446
/* Need to check if the handler is SIG_IGN before handling:
2447
| [[info:libc:Initial Signal Actions]] |
2448
| [[info:libc:Basic Signal Handling]] |
2450
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2452
perror_plus("sigaction");
2455
if(old_sigterm_action.sa_handler != SIG_IGN){
2456
ret = sigaction(SIGINT, &sigterm_action, NULL);
2458
perror_plus("sigaction");
2459
exitcode = EX_OSERR;
2463
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2465
perror_plus("sigaction");
2468
if(old_sigterm_action.sa_handler != SIG_IGN){
2469
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2471
perror_plus("sigaction");
2472
exitcode = EX_OSERR;
2476
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2478
perror_plus("sigaction");
2481
if(old_sigterm_action.sa_handler != SIG_IGN){
2482
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2484
perror_plus("sigaction");
2485
exitcode = EX_OSERR;
2490
/* If no interfaces were specified, make a list */
2491
if(mc.interfaces == NULL){
2492
struct dirent **direntries = NULL;
2493
/* Look for any good interfaces */
2494
ret = scandir(sys_class_net, &direntries, good_interface,
2497
/* Add all found interfaces to interfaces list */
2498
for(int i = 0; i < ret; ++i){
2499
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2500
direntries[i]->d_name);
2503
perror_plus("argz_add");
2504
free(direntries[i]);
2508
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2509
direntries[i]->d_name);
2511
free(direntries[i]);
2518
fprintf_plus(stderr, "Could not find a network interface\n");
2519
exitcode = EXIT_FAILURE;
2524
/* Bring up interfaces which are down, and remove any "none"s */
2526
char *interface = NULL;
2527
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2529
/* If interface name is "none", stop bringing up interfaces.
2530
Also remove all instances of "none" from the list */
2531
if(strcmp(interface, "none") == 0){
2532
argz_delete(&mc.interfaces, &mc.interfaces_size,
2535
while((interface = argz_next(mc.interfaces,
2536
mc.interfaces_size, interface))){
2537
if(strcmp(interface, "none") == 0){
2538
argz_delete(&mc.interfaces, &mc.interfaces_size,
2545
bool interface_was_up = interface_is_up(interface);
2546
errno = bring_up_interface(interface, delay);
2547
if(not interface_was_up){
2549
perror_plus("Failed to bring up interface");
2551
errno = argz_add(&interfaces_to_take_down,
2552
&interfaces_to_take_down_size,
2555
perror_plus("argz_add");
2560
if(debug and (interfaces_to_take_down == NULL)){
2561
fprintf_plus(stderr, "No interfaces were brought up\n");
2565
/* If we only got one interface, explicitly use only that one */
2566
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2568
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2571
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2578
ret = init_gnutls_global(pubkey, seckey, &mc);
2580
fprintf_plus(stderr, "init_gnutls_global failed\n");
2581
exitcode = EX_UNAVAILABLE;
2584
gnutls_initialized = true;
2591
/* Try /run/tmp before /tmp */
2592
tempdir = mkdtemp(run_tempdir);
2593
if(tempdir == NULL and errno == ENOENT){
2595
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2596
run_tempdir, old_tempdir);
2598
tempdir = mkdtemp(old_tempdir);
2600
if(tempdir == NULL){
2601
perror_plus("mkdtemp");
2609
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2610
fprintf_plus(stderr, "init_gpgme failed\n");
2611
exitcode = EX_UNAVAILABLE;
2614
gpgme_initialized = true;
2621
if(connect_to != NULL){
2622
/* Connect directly, do not use Zeroconf */
2623
/* (Mainly meant for debugging) */
2624
char *address = strrchr(connect_to, ':');
2626
if(address == NULL){
2627
fprintf_plus(stderr, "No colon in address\n");
2628
exitcode = EX_USAGE;
2638
tmpmax = strtoimax(address+1, &tmp, 10);
2639
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2640
or tmpmax != (in_port_t)tmpmax){
2641
fprintf_plus(stderr, "Bad port number\n");
2642
exitcode = EX_USAGE;
2650
port = (in_port_t)tmpmax;
2652
/* Colon in address indicates IPv6 */
2654
if(strchr(connect_to, ':') != NULL){
2656
/* Accept [] around IPv6 address - see RFC 5952 */
2657
if(connect_to[0] == '[' and address[-1] == ']')
2665
address = connect_to;
2671
while(not quit_now){
2672
ret = start_mandos_communication(address, port, if_index, af,
2674
if(quit_now or ret == 0){
2678
fprintf_plus(stderr, "Retrying in %d seconds\n",
2679
(int)retry_interval);
2681
sleep((unsigned int)retry_interval);
2685
exitcode = EXIT_SUCCESS;
627
2696
AvahiServerConfig config;
628
AvahiSServiceBrowser *sb = NULL;
631
int returncode = EXIT_SUCCESS;
632
const char *interface = "eth0";
633
unsigned int if_index;
634
char *connect_to = NULL;
637
static struct option long_options[] = {
638
{"debug", no_argument, (int *)&debug, 1},
639
{"connect", required_argument, 0, 'c'},
640
{"interface", required_argument, 0, 'i'},
643
int option_index = 0;
644
ret = getopt_long (argc, argv, "i:", long_options,
665
if_index = if_nametoindex(interface);
667
fprintf(stderr, "No such interface: \"%s\"\n", interface);
671
if(connect_to != NULL){
672
/* Connect directly, do not use Zeroconf */
673
/* (Mainly meant for debugging) */
674
char *address = strrchr(connect_to, ':');
676
fprintf(stderr, "No colon in address\n");
680
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
682
perror("Bad port number");
686
address = connect_to;
687
ret = start_mandos_communication(address, port, if_index);
696
avahi_set_log_function(empty_log);
699
/* Initialize the psuedo-RNG */
700
srand((unsigned int) time(NULL));
702
/* Allocate main loop object */
703
if (!(simple_poll = avahi_simple_poll_new())) {
704
fprintf(stderr, "Failed to create simple poll object.\n");
709
/* Do not publish any local records */
2697
/* Do not publish any local Zeroconf records */
710
2698
avahi_server_config_init(&config);
711
2699
config.publish_hinfo = 0;
712
2700
config.publish_addresses = 0;
713
2701
config.publish_workstation = 0;
714
2702
config.publish_domain = 0;
716
2704
/* Allocate a new server */
717
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
718
&config, NULL, NULL, &error);
720
/* Free the configuration data */
2705
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2706
&config, NULL, NULL, &ret_errno);
2708
/* Free the Avahi configuration data */
721
2709
avahi_server_config_free(&config);
723
/* Check if creating the server object succeeded */
725
fprintf(stderr, "Failed to create server: %s\n",
726
avahi_strerror(error));
727
returncode = EXIT_FAILURE;
731
/* Create the service browser */
732
sb = avahi_s_service_browser_new(server, (AvahiIfIndex)if_index,
734
"_mandos._tcp", NULL, 0,
735
browse_callback, server);
737
fprintf(stderr, "Failed to create service browser: %s\n",
738
avahi_strerror(avahi_server_errno(server)));
739
returncode = EXIT_FAILURE;
743
/* Run the main loop */
746
fprintf(stderr, "Starting avahi loop search\n");
749
avahi_simple_poll_loop(simple_poll);
754
fprintf(stderr, "%s exiting\n", argv[0]);
759
avahi_s_service_browser_free(sb);
762
avahi_server_free(server);
765
avahi_simple_poll_free(simple_poll);
2712
/* Check if creating the Avahi server object succeeded */
2713
if(mc.server == NULL){
2714
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2715
avahi_strerror(ret_errno));
2716
exitcode = EX_UNAVAILABLE;
2724
/* Create the Avahi service browser */
2725
sb = avahi_s_service_browser_new(mc.server, if_index,
2726
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2727
NULL, 0, browse_callback,
2730
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2731
avahi_strerror(avahi_server_errno(mc.server)));
2732
exitcode = EX_UNAVAILABLE;
2740
/* Run the main loop */
2743
fprintf_plus(stderr, "Starting Avahi loop search\n");
2746
ret = avahi_loop_with_timeout(simple_poll,
2747
(int)(retry_interval * 1000), &mc);
2749
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2750
(ret == 0) ? "successfully" : "with error");
2756
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2759
/* Cleanup things */
2760
free(mc.interfaces);
2763
avahi_s_service_browser_free(sb);
2765
if(mc.server != NULL)
2766
avahi_server_free(mc.server);
2768
if(simple_poll != NULL)
2769
avahi_simple_poll_free(simple_poll);
2771
if(gnutls_initialized){
2772
gnutls_certificate_free_credentials(mc.cred);
2773
gnutls_global_deinit();
2774
gnutls_dh_params_deinit(mc.dh_params);
2777
if(gpgme_initialized){
2778
gpgme_release(mc.ctx);
2781
/* Cleans up the circular linked list of Mandos servers the client
2783
if(mc.current_server != NULL){
2784
mc.current_server->prev->next = NULL;
2785
while(mc.current_server != NULL){
2786
server *next = mc.current_server->next;
2788
#pragma GCC diagnostic push
2789
#pragma GCC diagnostic ignored "-Wcast-qual"
2791
free((char *)(mc.current_server->ip));
2793
#pragma GCC diagnostic pop
2795
free(mc.current_server);
2796
mc.current_server = next;
2800
/* Re-raise privileges */
2802
ret_errno = raise_privileges();
2805
perror_plus("Failed to raise privileges");
2808
/* Run network hooks */
2809
run_network_hooks("stop", interfaces_hooks != NULL ?
2810
interfaces_hooks : "", delay);
2812
/* Take down the network interfaces which were brought up */
2814
char *interface = NULL;
2815
while((interface=argz_next(interfaces_to_take_down,
2816
interfaces_to_take_down_size,
2818
ret_errno = take_down_interface(interface);
2821
perror_plus("Failed to take down interface");
2824
if(debug and (interfaces_to_take_down == NULL)){
2825
fprintf_plus(stderr, "No interfaces needed to be taken"
2831
ret_errno = lower_privileges_permanently();
2834
perror_plus("Failed to lower privileges permanently");
2838
free(interfaces_to_take_down);
2839
free(interfaces_hooks);
2841
/* Removes the GPGME temp directory and all files inside */
2842
if(tempdir != NULL){
2843
struct dirent **direntries = NULL;
2844
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2848
if(tempdir_fd == -1){
2849
perror_plus("open");
2852
#if __GLIBC_PREREQ(2, 15)
2853
int numentries = scandirat(tempdir_fd, ".", &direntries,
2854
notdotentries, alphasort);
2855
#else /* not __GLIBC_PREREQ(2, 15) */
2856
int numentries = scandir(tempdir, &direntries, notdotentries,
2858
#endif /* not __GLIBC_PREREQ(2, 15) */
2859
#else /* not __GLIBC__ */
2860
int numentries = scandir(tempdir, &direntries, notdotentries,
2862
#endif /* not __GLIBC__ */
2863
if(numentries >= 0){
2864
for(int i = 0; i < numentries; i++){
2865
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2867
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2868
" \"%s\", 0): %s\n", tempdir,
2869
direntries[i]->d_name, strerror(errno));
2871
free(direntries[i]);
2874
/* need to clean even if 0 because man page doesn't specify */
2876
if(numentries == -1){
2877
perror_plus("scandir");
2879
ret = rmdir(tempdir);
2880
if(ret == -1 and errno != ENOENT){
2881
perror_plus("rmdir");
2884
TEMP_FAILURE_RETRY(close(tempdir_fd));
2889
sigemptyset(&old_sigterm_action.sa_mask);
2890
old_sigterm_action.sa_handler = SIG_DFL;
2891
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2892
&old_sigterm_action,
2895
perror_plus("sigaction");
2898
ret = raise(signal_received);
2899
} while(ret != 0 and errno == EINTR);
2901
perror_plus("raise");
2904
TEMP_FAILURE_RETRY(pause());