47
111
#include <avahi-common/malloc.h>
48
112
#include <avahi-common/error.h>
51
#include <sys/types.h> /* socket(), inet_pton() */
52
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
53
struct in6_addr, inet_pton() */
54
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
55
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
57
#include <unistd.h> /* close() */
58
#include <netinet/in.h>
59
#include <stdbool.h> /* true */
60
#include <string.h> /* memset */
61
#include <arpa/inet.h> /* inet_pton() */
62
#include <iso646.h> /* not */
65
#include <errno.h> /* perror() */
72
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
74
#define CERTFILE CERT_ROOT "openpgp-client.txt"
75
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
115
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
118
init_gnutls_session(),
120
#include <gnutls/openpgp.h>
121
/* gnutls_certificate_set_openpgp_key_file(),
122
GNUTLS_OPENPGP_FMT_BASE64 */
125
#include <gpgme.h> /* All GPGME types, constants and
128
GPGME_PROTOCOL_OpenPGP,
76
131
#define BUFFER_SIZE 256
133
#define PATHDIR "/conf/conf.d/mandos"
134
#define SECKEY "seckey.txt"
135
#define PUBKEY "pubkey.txt"
136
#define HOOKDIR "/lib/mandos/network-hooks.d"
79
138
bool debug = false;
139
static const char mandos_protocol_version[] = "1";
140
const char *argp_program_version = "mandos-client " VERSION;
141
const char *argp_program_bug_address = "<mandos@recompile.se>";
142
static const char sys_class_net[] = "/sys/class/net";
143
char *connect_to = NULL;
144
const char *hookdir = HOOKDIR;
149
/* Doubly linked list that need to be circularly linked when used */
150
typedef struct server{
153
AvahiIfIndex if_index;
155
struct timespec last_seen;
160
/* Used for passing in values through the Avahi callback functions */
82
gnutls_session_t session;
83
163
gnutls_certificate_credentials_t cred;
164
unsigned int dh_bits;
84
165
gnutls_dh_params_t dh_params;
88
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
char **new_packet, const char *homedir){
90
gpgme_data_t dh_crypto, dh_plain;
166
const char *priority;
168
server *current_server;
170
size_t interfaces_size;
173
/* global so signal handler can reach it*/
174
AvahiSimplePoll *simple_poll;
176
sig_atomic_t quit_now = 0;
177
int signal_received = 0;
179
/* Function to use when printing errors */
180
void perror_plus(const char *print_text){
182
fprintf(stderr, "Mandos plugin %s: ",
183
program_invocation_short_name);
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
189
int fprintf_plus(FILE *stream, const char *format, ...){
191
va_start (ap, format);
193
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
194
program_invocation_short_name));
195
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
199
* Make additional room in "buffer" for at least BUFFER_SIZE more
200
* bytes. "buffer_capacity" is how much is currently allocated,
201
* "buffer_length" is how much is already used.
203
__attribute__((nonnull, warn_unused_result))
204
size_t incbuffer(char **buffer, size_t buffer_length,
205
size_t buffer_capacity){
206
if(buffer_length + BUFFER_SIZE > buffer_capacity){
207
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
209
int old_errno = errno;
216
buffer_capacity += BUFFER_SIZE;
218
return buffer_capacity;
221
/* Add server to set of servers to retry periodically */
222
__attribute__((nonnull, warn_unused_result))
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
224
int af, server **current_server){
226
server *new_server = malloc(sizeof(server));
227
if(new_server == NULL){
228
perror_plus("malloc");
231
*new_server = (server){ .ip = strdup(ip),
233
.if_index = if_index,
235
if(new_server->ip == NULL){
236
perror_plus("strdup");
240
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
242
perror_plus("clock_gettime");
244
#pragma GCC diagnostic push
245
#pragma GCC diagnostic ignored "-Wcast-qual"
247
free((char *)(new_server->ip));
249
#pragma GCC diagnostic pop
254
/* Special case of first server */
255
if(*current_server == NULL){
256
new_server->next = new_server;
257
new_server->prev = new_server;
258
*current_server = new_server;
260
/* Place the new server last in the list */
261
new_server->next = *current_server;
262
new_server->prev = (*current_server)->prev;
263
new_server->prev->next = new_server;
264
(*current_server)->prev = new_server;
272
__attribute__((nonnull, warn_unused_result))
273
static bool init_gpgme(const char * const seckey,
274
const char * const pubkey,
275
const char * const tempdir,
94
ssize_t new_packet_capacity = 0;
95
ssize_t new_packet_length = 0;
96
278
gpgme_engine_info_t engine_info;
99
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
281
* Helper function to insert pub and seckey to the engine keyring.
283
bool import_key(const char * const filename){
286
gpgme_data_t pgp_data;
288
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
294
rc = gpgme_data_new_from_fd(&pgp_data, fd);
295
if(rc != GPG_ERR_NO_ERROR){
296
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
297
gpgme_strsource(rc), gpgme_strerror(rc));
301
rc = gpgme_op_import(mc->ctx, pgp_data);
302
if(rc != GPG_ERR_NO_ERROR){
303
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
304
gpgme_strsource(rc), gpgme_strerror(rc));
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");
103
321
gpgme_check_version(NULL);
104
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
322
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
323
if(rc != GPG_ERR_NO_ERROR){
324
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
325
gpgme_strsource(rc), gpgme_strerror(rc));
106
/* Set GPGME home directory */
107
rc = gpgme_get_engine_info (&engine_info);
108
if (rc != GPG_ERR_NO_ERROR){
109
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
110
gpgme_strsource(rc), gpgme_strerror(rc));
329
/* Set GPGME home directory for the OpenPGP engine only */
330
rc = gpgme_get_engine_info(&engine_info);
331
if(rc != GPG_ERR_NO_ERROR){
332
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
333
gpgme_strsource(rc), gpgme_strerror(rc));
113
336
while(engine_info != NULL){
114
337
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
115
338
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
116
engine_info->file_name, homedir);
339
engine_info->file_name, tempdir);
119
342
engine_info = engine_info->next;
121
344
if(engine_info == NULL){
122
fprintf(stderr, "Could not set home dir to %s\n", homedir);
126
/* Create new GPGME data buffer from packet buffer */
127
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
128
if (rc != GPG_ERR_NO_ERROR){
129
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
130
gpgme_strsource(rc), gpgme_strerror(rc));
345
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
350
/* Create new GPGME "context" */
351
rc = gpgme_new(&(mc->ctx));
352
if(rc != GPG_ERR_NO_ERROR){
353
fprintf_plus(stderr, "Mandos plugin mandos-client: "
354
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
359
if(not import_key(pubkey) or not import_key(seckey)){
367
* Decrypt OpenPGP data.
368
* Returns -1 on error
370
__attribute__((nonnull, warn_unused_result))
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
375
gpgme_data_t dh_crypto, dh_plain;
378
size_t plaintext_capacity = 0;
379
ssize_t plaintext_length = 0;
382
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
385
/* Create new GPGME data buffer from memory cryptotext */
386
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
388
if(rc != GPG_ERR_NO_ERROR){
389
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
390
gpgme_strsource(rc), gpgme_strerror(rc));
134
394
/* Create new empty GPGME data buffer for the plaintext */
135
395
rc = gpgme_data_new(&dh_plain);
136
if (rc != GPG_ERR_NO_ERROR){
137
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
138
gpgme_strsource(rc), gpgme_strerror(rc));
142
/* Create new GPGME "context" */
143
rc = gpgme_new(&ctx);
144
if (rc != GPG_ERR_NO_ERROR){
145
fprintf(stderr, "bad gpgme_new: %s: %s\n",
146
gpgme_strsource(rc), gpgme_strerror(rc));
150
/* Decrypt data from the FILE pointer to the plaintext data
152
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
153
if (rc != GPG_ERR_NO_ERROR){
154
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
155
gpgme_strsource(rc), gpgme_strerror(rc));
160
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
164
gpgme_decrypt_result_t result;
165
result = gpgme_op_decrypt_result(ctx);
167
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
169
fprintf(stderr, "Unsupported algorithm: %s\n",
170
result->unsupported_algorithm);
171
fprintf(stderr, "Wrong key usage: %d\n",
172
result->wrong_key_usage);
173
if(result->file_name != NULL){
174
fprintf(stderr, "File name: %s\n", result->file_name);
176
gpgme_recipient_t recipient;
177
recipient = result->recipients;
396
if(rc != GPG_ERR_NO_ERROR){
397
fprintf_plus(stderr, "Mandos plugin mandos-client: "
398
"bad gpgme_data_new: %s: %s\n",
399
gpgme_strsource(rc), gpgme_strerror(rc));
400
gpgme_data_release(dh_crypto);
404
/* Decrypt data from the cryptotext data buffer to the plaintext
406
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
407
if(rc != GPG_ERR_NO_ERROR){
408
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
409
gpgme_strsource(rc), gpgme_strerror(rc));
410
plaintext_length = -1;
412
gpgme_decrypt_result_t result;
413
result = gpgme_op_decrypt_result(mc->ctx);
415
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
417
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
418
result->unsupported_algorithm);
419
fprintf_plus(stderr, "Wrong key usage: %u\n",
420
result->wrong_key_usage);
421
if(result->file_name != NULL){
422
fprintf_plus(stderr, "File name: %s\n", result->file_name);
424
gpgme_recipient_t recipient;
425
recipient = result->recipients;
179
426
while(recipient != NULL){
180
fprintf(stderr, "Public key algorithm: %s\n",
181
gpgme_pubkey_algo_name(recipient->pubkey_algo));
182
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
183
fprintf(stderr, "Secret key available: %s\n",
184
recipient->status == GPG_ERR_NO_SECKEY
427
fprintf_plus(stderr, "Public key algorithm: %s\n",
428
gpgme_pubkey_algo_name
429
(recipient->pubkey_algo));
430
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
431
fprintf_plus(stderr, "Secret key available: %s\n",
432
recipient->status == GPG_ERR_NO_SECKEY
186
434
recipient = recipient->next;
192
/* Delete the GPGME FILE pointer cryptotext data buffer */
193
gpgme_data_release(dh_crypto);
442
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
195
445
/* Seek back to the beginning of the GPGME plaintext data buffer */
196
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
446
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
447
perror_plus("gpgme_data_seek");
448
plaintext_length = -1;
200
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
201
*new_packet = realloc(*new_packet,
202
(unsigned int)new_packet_capacity
204
if (*new_packet == NULL){
208
new_packet_capacity += BUFFER_SIZE;
454
plaintext_capacity = incbuffer(plaintext,
455
(size_t)plaintext_length,
457
if(plaintext_capacity == 0){
458
perror_plus("incbuffer");
459
plaintext_length = -1;
211
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
463
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
213
465
/* Print the data, if any */
218
perror("gpgme_data_read");
221
new_packet_length += ret;
224
/* FIXME: check characters before printing to screen so to not print
225
terminal control characters */
227
/* fprintf(stderr, "decrypted password is: "); */
228
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
229
/* fprintf(stderr, "\n"); */
471
perror_plus("gpgme_data_read");
472
plaintext_length = -1;
475
plaintext_length += ret;
479
fprintf_plus(stderr, "Decrypted password is: ");
480
for(ssize_t i = 0; i < plaintext_length; i++){
481
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
483
fprintf(stderr, "\n");
488
/* Delete the GPGME cryptotext data buffer */
489
gpgme_data_release(dh_crypto);
232
491
/* Delete the GPGME plaintext data buffer */
233
492
gpgme_data_release(dh_plain);
234
return new_packet_length;
493
return plaintext_length;
237
static const char * safer_gnutls_strerror (int value) {
238
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);
240
500
ret = "(unknown)";
244
void debuggnutls(__attribute__((unused)) int level,
246
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);
249
int initgnutls(encrypted_session *es){
511
__attribute__((nonnull, warn_unused_result))
512
static int init_gnutls_global(const char *pubkeyfilename,
513
const char *seckeyfilename,
254
fprintf(stderr, "Initializing GnuTLS\n");
518
fprintf_plus(stderr, "Initializing GnuTLS\n");
257
if ((ret = gnutls_global_init ())
258
!= GNUTLS_E_SUCCESS) {
259
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
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."
264
532
gnutls_global_set_log_level(11);
265
533
gnutls_global_set_log_function(debuggnutls);
268
/* openpgp credentials */
269
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
270
!= GNUTLS_E_SUCCESS) {
271
fprintf (stderr, "memory error: %s\n",
272
safer_gnutls_strerror(ret));
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();
277
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
278
" 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",
282
552
ret = gnutls_certificate_set_openpgp_key_file
283
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
284
if (ret != GNUTLS_E_SUCCESS) {
286
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
288
ret, CERTFILE, KEYFILE);
289
fprintf(stdout, "The Error is: %s\n",
290
safer_gnutls_strerror(ret));
294
//GnuTLS server initialization
295
if ((ret = gnutls_dh_params_init (&es->dh_params))
296
!= GNUTLS_E_SUCCESS) {
297
fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
safer_gnutls_strerror(ret));
302
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
303
!= GNUTLS_E_SUCCESS) {
304
fprintf (stderr, "Error in prime generation: %s\n",
305
safer_gnutls_strerror(ret));
309
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
311
// GnuTLS session creation
312
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
313
!= GNUTLS_E_SUCCESS){
314
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
315
safer_gnutls_strerror(ret));
318
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
319
!= GNUTLS_E_SUCCESS) {
320
fprintf(stderr, "Syntax error at: %s\n", err);
321
fprintf(stderr, "GnuTLS error: %s\n",
322
safer_gnutls_strerror(ret));
326
if ((ret = gnutls_credentials_set
327
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Error setting a credentials set: %s\n",
330
safer_gnutls_strerror(ret));
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);
334
641
/* ignore client certificate if any. */
335
gnutls_certificate_server_set_request (es->session,
642
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
338
gnutls_dh_set_prime_bits (es->session, DH_BITS);
644
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
343
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
__attribute__((unused)) const char *txt){}
346
int start_mandos_communication(const char *ip, uint16_t port,
347
unsigned int if_index){
349
struct sockaddr_in6 to;
350
encrypted_session es;
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;
351
878
char *buffer = NULL;
352
char *decrypted_buffer;
879
char *decrypted_buffer = NULL;
353
880
size_t buffer_length = 0;
354
881
size_t buffer_capacity = 0;
355
ssize_t decrypted_buffer_size;
358
char interface[IF_NAMESIZE];
361
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
364
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
370
if(if_indextoname(if_index, interface) == NULL){
372
perror("if_indextoname");
378
fprintf(stderr, "Binding to interface %s\n", interface);
381
memset(&to,0,sizeof(to)); /* Spurious warning */
382
to.sin6_family = AF_INET6;
383
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
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\n", ip);
400
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
406
ret = initgnutls (&es);
412
gnutls_transport_set_ptr (es.session,
413
(gnutls_transport_ptr_t) tcp_sd);
416
fprintf(stderr, "Establishing TLS session with %s\n", ip);
419
ret = gnutls_handshake (es.session);
421
if (ret != GNUTLS_E_SUCCESS){
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){
423
fprintf(stderr, "\n*** Handshake failed ***\n");
1162
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
430
//Retrieve OpenPGP packet that contains the wanted password
1169
/* Read OpenPGP packet that contains the wanted password */
433
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1172
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
438
if (buffer_length + BUFFER_SIZE > buffer_capacity){
439
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
444
buffer_capacity += BUFFER_SIZE;
447
ret = gnutls_record_recv
448
(es.session, buffer+buffer_length, BUFFER_SIZE);
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,
454
1204
case GNUTLS_E_INTERRUPTED:
455
1205
case GNUTLS_E_AGAIN:
457
1207
case GNUTLS_E_REHANDSHAKE:
458
ret = gnutls_handshake (es.session);
460
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 "
467
fprintf(stderr, "Unknown error while reading data from"
468
" encrypted session with mandos server\n");
470
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);
474
buffer_length += (size_t) ret;
478
if (buffer_length > 0){
479
decrypted_buffer_size = pgp_packet_decrypt(buffer,
483
if (decrypted_buffer_size >= 0){
484
while(written < decrypted_buffer_size){
485
ret = (int)fwrite (decrypted_buffer + written, 1,
486
(size_t)decrypted_buffer_size - written,
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){
1260
while(written < (size_t) decrypted_buffer_size){
1266
ret = (int)fwrite(decrypted_buffer + written, 1,
1267
(size_t)decrypted_buffer_size - written,
488
1269
if(ret == 0 and ferror(stdout)){
490
fprintf(stderr, "Error writing encrypted data: %s\n",
1272
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
496
1278
written += (size_t)ret;
498
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);
507
fprintf(stderr, "Closing TLS session\n");
511
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
514
gnutls_deinit (es.session);
515
gnutls_certificate_free_credentials (es.cred);
516
gnutls_global_deinit ();
520
static AvahiSimplePoll *simple_poll = NULL;
521
static AvahiServer *server = NULL;
523
static void resolve_callback(
524
AvahiSServiceResolver *r,
525
AvahiIfIndex interface,
526
AVAHI_GCC_UNUSED AvahiProtocol protocol,
527
AvahiResolverEvent event,
531
const char *host_name,
532
const AvahiAddress *address,
534
AVAHI_GCC_UNUSED AvahiStringList *txt,
535
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
536
AVAHI_GCC_UNUSED void* userdata) {
538
assert(r); /* Spurious warning */
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
540
1335
/* Called whenever a service has been resolved successfully or
1339
avahi_s_service_resolver_free(r);
545
1345
case AVAHI_RESOLVER_FAILURE:
546
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
547
" type '%s' in domain '%s': %s\n", name, type, domain,
548
avahi_strerror(avahi_server_errno(server)));
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)));
551
1353
case AVAHI_RESOLVER_FOUND:
553
1355
char ip[AVAHI_ADDRESS_STR_MAX];
554
1356
avahi_address_snprint(ip, sizeof(ip), address);
556
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
557
" port %d\n", name, host_name, ip, port);
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);
559
int ret = start_mandos_communication(ip, port,
560
(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"
566
1378
avahi_s_service_resolver_free(r);
569
static void browse_callback(
570
AvahiSServiceBrowser *b,
571
AvahiIfIndex interface,
572
AvahiProtocol protocol,
573
AvahiBrowserEvent event,
577
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
580
AvahiServer *s = userdata;
581
assert(b); /* Spurious warning */
583
/* Called whenever a new services becomes available on the LAN or
584
is removed from the LAN */
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);
588
case AVAHI_BROWSER_FAILURE:
590
fprintf(stderr, "(Browser) %s\n",
591
avahi_strerror(avahi_server_errno(server)));
592
avahi_simple_poll_quit(simple_poll);
595
case AVAHI_BROWSER_NEW:
596
/* We ignore the returned resolver object. In the callback
597
function we free it. If the server is terminated before
598
the callback function is called the server will free
599
the resolver for us. */
601
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
603
AVAHI_PROTO_INET6, 0,
604
resolve_callback, s)))
605
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
606
avahi_strerror(avahi_server_errno(s)));
609
case AVAHI_BROWSER_REMOVE:
612
case AVAHI_BROWSER_ALL_FOR_NOW:
613
case AVAHI_BROWSER_CACHE_EXHAUSTED:
618
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
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;
619
2696
AvahiServerConfig config;
620
AvahiSServiceBrowser *sb = NULL;
623
int returncode = EXIT_SUCCESS;
624
const char *interface = "eth0";
627
static struct option long_options[] = {
628
{"debug", no_argument, (int *)&debug, 1},
629
{"interface", required_argument, 0, 'i'},
632
int option_index = 0;
633
ret = getopt_long (argc, argv, "i:", long_options,
652
avahi_set_log_function(empty_log);
655
/* Initialize the psuedo-RNG */
656
srand((unsigned int) time(NULL));
658
/* Allocate main loop object */
659
if (!(simple_poll = avahi_simple_poll_new())) {
660
fprintf(stderr, "Failed to create simple poll object.\n");
665
/* Do not publish any local records */
2697
/* Do not publish any local Zeroconf records */
666
2698
avahi_server_config_init(&config);
667
2699
config.publish_hinfo = 0;
668
2700
config.publish_addresses = 0;
669
2701
config.publish_workstation = 0;
670
2702
config.publish_domain = 0;
672
2704
/* Allocate a new server */
673
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
&config, NULL, NULL, &error);
676
/* Free the configuration data */
2705
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2706
&config, NULL, NULL, &ret_errno);
2708
/* Free the Avahi configuration data */
677
2709
avahi_server_config_free(&config);
679
/* Check if creating the server object succeeded */
681
fprintf(stderr, "Failed to create server: %s\n",
682
avahi_strerror(error));
683
returncode = EXIT_FAILURE;
687
/* Create the service browser */
688
sb = avahi_s_service_browser_new(server,
690
if_nametoindex(interface),
692
"_mandos._tcp", NULL, 0,
693
browse_callback, server);
695
fprintf(stderr, "Failed to create service browser: %s\n",
696
avahi_strerror(avahi_server_errno(server)));
697
returncode = EXIT_FAILURE;
701
/* Run the main loop */
704
fprintf(stderr, "Starting avahi loop search\n");
707
avahi_simple_poll_loop(simple_poll);
712
fprintf(stderr, "%s exiting\n", argv[0]);
717
avahi_s_service_browser_free(sb);
720
avahi_server_free(server);
723
avahi_simple_poll_free(simple_poll);
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());