111
45
#include <avahi-common/malloc.h>
112
46
#include <avahi-common/error.h>
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,
49
#include <sys/types.h> /* socket(), inet_pton() */
50
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
51
struct in6_addr, inet_pton() */
52
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
53
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
55
#include <unistd.h> /* close() */
56
#include <netinet/in.h>
57
#include <stdbool.h> /* true */
58
#include <string.h> /* memset */
59
#include <arpa/inet.h> /* inet_pton() */
60
#include <iso646.h> /* not */
63
#include <errno.h> /* perror() */
131
69
#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"
72
static const char *certdir = "/conf/conf.d/mandos";
73
static const char *certfile = "openpgp-client.txt";
74
static const char *certkey = "openpgp-client-key.txt";
138
76
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 */
79
gnutls_session_t session;
163
80
gnutls_certificate_credentials_t cred;
164
unsigned int dh_bits;
165
81
gnutls_dh_params_t dh_params;
166
const char *priority;
85
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
gpgme_data_t dh_crypto, dh_plain;
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,
92
ssize_t new_packet_capacity = 0;
93
ssize_t new_packet_length = 0;
278
94
gpgme_engine_info_t engine_info;
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");
97
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
321
101
gpgme_check_version(NULL);
322
102
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));
103
if (rc != GPG_ERR_NO_ERROR){
104
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
105
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));
109
/* Set GPGME home directory */
110
rc = gpgme_get_engine_info (&engine_info);
111
if (rc != GPG_ERR_NO_ERROR){
112
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
113
gpgme_strsource(rc), gpgme_strerror(rc));
336
116
while(engine_info != NULL){
337
117
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
338
118
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
339
engine_info->file_name, tempdir);
119
engine_info->file_name, homedir);
342
122
engine_info = engine_info->next;
344
124
if(engine_info == NULL){
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));
125
fprintf(stderr, "Could not set home dir to %s\n", homedir);
129
/* Create new GPGME data buffer from packet buffer */
130
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
131
if (rc != GPG_ERR_NO_ERROR){
132
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
133
gpgme_strsource(rc), gpgme_strerror(rc));
394
137
/* Create new empty GPGME data buffer for the plaintext */
395
138
rc = gpgme_data_new(&dh_plain);
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;
139
if (rc != GPG_ERR_NO_ERROR){
140
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
141
gpgme_strsource(rc), gpgme_strerror(rc));
145
/* Create new GPGME "context" */
146
rc = gpgme_new(&ctx);
147
if (rc != GPG_ERR_NO_ERROR){
148
fprintf(stderr, "bad gpgme_new: %s: %s\n",
149
gpgme_strsource(rc), gpgme_strerror(rc));
153
/* Decrypt data from the FILE pointer to the plaintext data
155
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
156
if (rc != GPG_ERR_NO_ERROR){
157
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
158
gpgme_strsource(rc), gpgme_strerror(rc));
163
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
167
gpgme_decrypt_result_t result;
168
result = gpgme_op_decrypt_result(ctx);
170
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
172
fprintf(stderr, "Unsupported algorithm: %s\n",
173
result->unsupported_algorithm);
174
fprintf(stderr, "Wrong key usage: %d\n",
175
result->wrong_key_usage);
176
if(result->file_name != NULL){
177
fprintf(stderr, "File name: %s\n", result->file_name);
179
gpgme_recipient_t recipient;
180
recipient = result->recipients;
426
182
while(recipient != NULL){
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
183
fprintf(stderr, "Public key algorithm: %s\n",
184
gpgme_pubkey_algo_name(recipient->pubkey_algo));
185
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
186
fprintf(stderr, "Secret key available: %s\n",
187
recipient->status == GPG_ERR_NO_SECKEY
434
189
recipient = recipient->next;
442
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
195
/* Delete the GPGME FILE pointer cryptotext data buffer */
196
gpgme_data_release(dh_crypto);
445
198
/* Seek back to the beginning of the GPGME plaintext data buffer */
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 (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
200
perror("pgpme_data_seek");
454
plaintext_capacity = incbuffer(plaintext,
455
(size_t)plaintext_length,
457
if(plaintext_capacity == 0){
458
perror_plus("incbuffer");
459
plaintext_length = -1;
205
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
206
*new_packet = realloc(*new_packet,
207
(unsigned int)new_packet_capacity
209
if (*new_packet == NULL){
213
new_packet_capacity += BUFFER_SIZE;
463
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
216
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
465
218
/* Print the data, if any */
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);
223
perror("gpgme_data_read");
226
new_packet_length += ret;
229
/* FIXME: check characters before printing to screen so to not print
230
terminal control characters */
232
/* fprintf(stderr, "decrypted password is: "); */
233
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
234
/* fprintf(stderr, "\n"); */
491
237
/* Delete the GPGME plaintext data buffer */
492
238
gpgme_data_release(dh_plain);
493
return plaintext_length;
239
return new_packet_length;
496
__attribute__((warn_unused_result))
497
static const char *safer_gnutls_strerror(int value){
498
const char *ret = gnutls_strerror(value);
242
static const char * safer_gnutls_strerror (int value) {
243
const char *ret = gnutls_strerror (value);
500
245
ret = "(unknown)";
504
/* GnuTLS log function callback */
505
__attribute__((nonnull))
506
249
static void debuggnutls(__attribute__((unused)) int level,
507
250
const char* string){
508
fprintf_plus(stderr, "GnuTLS: %s", string);
251
fprintf(stderr, "%s", string);
511
__attribute__((nonnull, warn_unused_result))
512
static int init_gnutls_global(const char *pubkeyfilename,
513
const char *seckeyfilename,
254
static int initgnutls(encrypted_session *es){
518
fprintf_plus(stderr, "Initializing GnuTLS\n");
259
fprintf(stderr, "Initializing GnuTLS\n");
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));
262
if ((ret = gnutls_global_init ())
263
!= GNUTLS_E_SUCCESS) {
264
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
529
/* "Use a log level over 10 to enable all debugging options."
532
269
gnutls_global_set_log_level(11);
533
270
gnutls_global_set_log_function(debuggnutls);
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();
273
/* openpgp credentials */
274
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
275
!= GNUTLS_E_SUCCESS) {
276
fprintf (stderr, "memory error: %s\n",
277
safer_gnutls_strerror(ret));
546
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
547
" secret key %s as GnuTLS credentials\n",
282
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
283
" and keyfile %s as GnuTLS credentials\n", certfile,
552
287
ret = gnutls_certificate_set_openpgp_key_file
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);
288
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
289
if (ret != GNUTLS_E_SUCCESS) {
291
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
293
ret, certfile, certkey);
294
fprintf(stdout, "The Error is: %s\n",
295
safer_gnutls_strerror(ret));
299
//GnuTLS server initialization
300
if ((ret = gnutls_dh_params_init (&es->dh_params))
301
!= GNUTLS_E_SUCCESS) {
302
fprintf (stderr, "Error in dh parameter initialization: %s\n",
303
safer_gnutls_strerror(ret));
307
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
308
!= GNUTLS_E_SUCCESS) {
309
fprintf (stderr, "Error in prime generation: %s\n",
310
safer_gnutls_strerror(ret));
314
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
316
// GnuTLS session creation
317
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
318
!= GNUTLS_E_SUCCESS){
319
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
320
safer_gnutls_strerror(ret));
323
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
324
!= GNUTLS_E_SUCCESS) {
325
fprintf(stderr, "Syntax error at: %s\n", err);
326
fprintf(stderr, "GnuTLS error: %s\n",
327
safer_gnutls_strerror(ret));
331
if ((ret = gnutls_credentials_set
332
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
333
!= GNUTLS_E_SUCCESS) {
334
fprintf(stderr, "Error setting a credentials set: %s\n",
335
safer_gnutls_strerror(ret));
641
339
/* ignore client certificate if any. */
642
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
340
gnutls_certificate_server_set_request (es->session,
644
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
343
gnutls_dh_set_prime_bits (es->session, DH_BITS);
649
/* Avahi log function callback */
650
348
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
651
349
__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 remove_local_route() */
706
__attribute__((nonnull, warn_unused_result))
707
static bool add_remove_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 *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
715
if(pluginhelperdir == NULL){
717
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
718
" variable not set; cannot run helper\n");
723
char interface[IF_NAMESIZE];
724
if(if_indextoname((unsigned int)if_index, interface) == NULL){
725
perror_plus("if_indextoname");
729
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
731
perror_plus("open(\"/dev/null\", O_RDONLY)");
737
/* Raise privileges */
738
errno = raise_privileges_permanently();
740
perror_plus("Failed to raise privileges");
741
/* _exit(EX_NOPERM); */
747
perror_plus("setgid");
750
/* Reset supplementary groups */
752
ret = setgroups(0, NULL);
754
perror_plus("setgroups");
758
ret = dup2(devnull, STDIN_FILENO);
760
perror_plus("dup2(devnull, STDIN_FILENO)");
763
ret = (int)TEMP_FAILURE_RETRY(close(devnull));
765
perror_plus("close");
768
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
770
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
773
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
778
if(helperdir_fd == -1){
780
_exit(EX_UNAVAILABLE);
782
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
785
perror_plus("openat");
786
_exit(EX_UNAVAILABLE);
788
TEMP_FAILURE_RETRY(close(helperdir_fd));
790
#pragma GCC diagnostic push
791
#pragma GCC diagnostic ignored "-Wcast-qual"
793
if(fexecve(helper_fd, (char *const [])
794
{ helper, add ? add_arg : delete_arg, (char *)address,
795
interface, NULL }, environ) == -1){
797
#pragma GCC diagnostic pop
799
perror_plus("fexecve");
811
pret = waitpid(pid, &status, 0);
812
if(pret == -1 and errno == EINTR and quit_now){
813
int errno_raising = 0;
814
if((errno = raise_privileges()) != 0){
815
errno_raising = errno;
816
perror_plus("Failed to raise privileges in order to"
817
" kill helper program");
819
if(kill(pid, SIGTERM) == -1){
822
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
823
perror_plus("Failed to lower privileges after killing"
828
} while(pret == -1 and errno == EINTR);
830
perror_plus("waitpid");
833
if(WIFEXITED(status)){
834
if(WEXITSTATUS(status) != 0){
835
fprintf_plus(stderr, "Error: iprouteadddel exited"
836
" with status %d\n", WEXITSTATUS(status));
841
if(WIFSIGNALED(status)){
842
fprintf_plus(stderr, "Error: iprouteadddel died by"
843
" signal %d\n", WTERMSIG(status));
846
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
850
__attribute__((nonnull, warn_unused_result))
851
static bool add_local_route(const char *address,
852
AvahiIfIndex if_index){
853
return add_remove_local_route(true, address, if_index);
856
__attribute__((nonnull, warn_unused_result))
857
static bool remove_local_route(const char *address,
858
AvahiIfIndex if_index){
859
return add_remove_local_route(false, address, if_index);
862
/* Called when a Mandos server is found */
863
__attribute__((nonnull, warn_unused_result))
864
static int start_mandos_communication(const char *ip, in_port_t port,
865
AvahiIfIndex if_index,
866
int af, mandos_context *mc){
867
int ret, tcp_sd = -1;
869
struct sockaddr_storage to;
351
static int start_mandos_communication(const char *ip, uint16_t port,
352
AvahiIfIndex if_index){
354
struct sockaddr_in6 to;
355
encrypted_session es;
870
356
char *buffer = NULL;
871
char *decrypted_buffer = NULL;
357
char *decrypted_buffer;
872
358
size_t buffer_length = 0;
873
359
size_t buffer_capacity = 0;
876
gnutls_session_t session;
877
int pf; /* Protocol family */
878
bool route_added = false;
895
fprintf_plus(stderr, "Bad address family: %d\n", af);
900
/* If the interface is specified and we have a list of interfaces */
901
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
902
/* Check if the interface is one of the interfaces we are using */
905
char *interface = NULL;
906
while((interface=argz_next(mc->interfaces, mc->interfaces_size,
908
if(if_nametoindex(interface) == (unsigned int)if_index){
915
/* This interface does not match any in the list, so we don't
916
connect to the server */
918
char interface[IF_NAMESIZE];
919
if(if_indextoname((unsigned int)if_index, interface) == NULL){
920
perror_plus("if_indextoname");
922
fprintf_plus(stderr, "Skipping server on non-used interface"
924
if_indextoname((unsigned int)if_index,
932
ret = init_gnutls_session(&session, mc);
938
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
939
PRIuMAX "\n", ip, (uintmax_t)port);
942
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
945
perror_plus("socket");
955
memset(&to, 0, sizeof(to));
957
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
958
ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
960
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
961
ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
965
perror_plus("inet_pton");
360
ssize_t decrypted_buffer_size;
363
char interface[IF_NAMESIZE];
366
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
370
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
376
if(if_indextoname((unsigned int)if_index, interface) == NULL){
378
perror("if_indextoname");
384
fprintf(stderr, "Binding to interface %s\n", interface);
387
memset(&to,0,sizeof(to)); /* Spurious warning */
388
to.sin6_family = AF_INET6;
389
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
971
fprintf_plus(stderr, "Bad address: %s\n", ip);
976
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
977
if(IN6_IS_ADDR_LINKLOCAL
978
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
979
if(if_index == AVAHI_IF_UNSPEC){
980
fprintf_plus(stderr, "An IPv6 link-local address is"
981
" incomplete without a network interface\n");
985
/* Set the network interface number as scope */
986
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
989
((struct sockaddr_in *)&to)->sin_port = htons(port);
998
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
999
char interface[IF_NAMESIZE];
1000
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1001
perror_plus("if_indextoname");
1003
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1004
"\n", ip, interface, (uintmax_t)port);
1007
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1008
ip, (uintmax_t)port);
1010
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1011
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1013
ret = getnameinfo((struct sockaddr *)&to,
1014
sizeof(struct sockaddr_in6),
1015
addrstr, sizeof(addrstr), NULL, 0,
1018
ret = getnameinfo((struct sockaddr *)&to,
1019
sizeof(struct sockaddr_in),
1020
addrstr, sizeof(addrstr), NULL, 0,
1023
if(ret == EAI_SYSTEM){
1024
perror_plus("getnameinfo");
1025
} else if(ret != 0) {
1026
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1027
} else if(strcmp(addrstr, ip) != 0){
1028
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1039
ret = connect(tcp_sd, (struct sockaddr *)&to,
1040
sizeof(struct sockaddr_in6));
1042
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1043
sizeof(struct sockaddr_in));
1046
if(errno == ENETUNREACH
1047
and if_index != AVAHI_IF_UNSPEC
1048
and connect_to == NULL
1049
and not route_added and
1050
((af == AF_INET6 and not
1051
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1053
or (af == AF_INET and
1054
/* Not a a IPv4LL address */
1055
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1056
& 0xFFFF0000L) != 0xA9FE0000L))){
1057
/* Work around Avahi bug - Avahi does not announce link-local
1058
addresses if it has a global address, so local hosts with
1059
*only* a link-local address (e.g. Mandos clients) cannot
1060
connect to a Mandos server announced by Avahi on a server
1061
host with a global address. Work around this by retrying
1062
with an explicit route added with the server's address.
1064
Avahi bug reference:
1065
http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1066
https://bugs.debian.org/587961
1069
route_added = add_local_route(ip, if_index);
1075
if(errno != ECONNREFUSED or debug){
1077
perror_plus("connect");
1090
const char *out = mandos_protocol_version;
1093
size_t out_size = strlen(out);
1094
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1095
out_size - written));
1098
perror_plus("write");
1102
written += (size_t)ret;
1103
if(written < out_size){
1106
if(out == mandos_protocol_version){
1121
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1129
/* This casting via intptr_t is to eliminate warning about casting
1130
an int to a pointer type. This is exactly how the GnuTLS Guile
1131
function "set-session-transport-fd!" does it. */
1132
gnutls_transport_set_ptr(session,
1133
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1141
ret = gnutls_handshake(session);
1146
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1148
if(ret != GNUTLS_E_SUCCESS){
395
fprintf(stderr, "Bad address: %s\n", ip);
398
to.sin6_port = htons(port); /* Spurious warning */
400
to.sin6_scope_id = (uint32_t)if_index;
403
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
404
/* char addrstr[INET6_ADDRSTRLEN]; */
405
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
406
/* sizeof(addrstr)) == NULL){ */
407
/* perror("inet_ntop"); */
409
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
410
/* addrstr, ntohs(to.sin6_port)); */
414
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
420
ret = initgnutls (&es);
426
gnutls_transport_set_ptr (es.session,
427
(gnutls_transport_ptr_t) tcp_sd);
430
fprintf(stderr, "Establishing TLS session with %s\n", ip);
433
ret = gnutls_handshake (es.session);
435
if (ret != GNUTLS_E_SUCCESS){
1150
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
437
fprintf(stderr, "\n*** Handshake failed ***\n");
1157
/* Read OpenPGP packet that contains the wanted password */
444
//Retrieve OpenPGP packet that contains the wanted password
1160
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
447
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1171
buffer_capacity = incbuffer(&buffer, buffer_length,
1173
if(buffer_capacity == 0){
1175
perror_plus("incbuffer");
1185
sret = gnutls_record_recv(session, buffer+buffer_length,
452
if (buffer_length + BUFFER_SIZE > buffer_capacity){
453
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
458
buffer_capacity += BUFFER_SIZE;
461
ret = gnutls_record_recv
462
(es.session, buffer+buffer_length, BUFFER_SIZE);
1192
468
case GNUTLS_E_INTERRUPTED:
1193
469
case GNUTLS_E_AGAIN:
1195
471
case GNUTLS_E_REHANDSHAKE:
1197
ret = gnutls_handshake(session);
1203
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1205
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
472
ret = gnutls_handshake (es.session);
474
fprintf(stderr, "\n*** Handshake failed ***\n");
1213
fprintf_plus(stderr, "Unknown error while reading data from"
1214
" encrypted session with Mandos server\n");
1215
gnutls_bye(session, GNUTLS_SHUT_RDWR);
481
fprintf(stderr, "Unknown error while reading data from"
482
" encrypted session with mandos server\n");
484
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1220
buffer_length += (size_t) sret;
1225
fprintf_plus(stderr, "Closing TLS session\n");
1234
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1239
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1241
if(buffer_length > 0){
1242
ssize_t decrypted_buffer_size;
1243
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1244
&decrypted_buffer, mc);
1245
if(decrypted_buffer_size >= 0){
488
buffer_length += (size_t) ret;
492
if (buffer_length > 0){
493
decrypted_buffer_size = pgp_packet_decrypt(buffer,
497
if (decrypted_buffer_size >= 0){
1248
498
while(written < (size_t) decrypted_buffer_size){
1254
ret = (int)fwrite(decrypted_buffer + written, 1,
1255
(size_t)decrypted_buffer_size - written,
499
ret = (int)fwrite (decrypted_buffer + written, 1,
500
(size_t)decrypted_buffer_size - written,
1257
502
if(ret == 0 and ferror(stdout)){
1260
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
504
fprintf(stderr, "Error writing encrypted data: %s\n",
1266
510
written += (size_t)ret;
1272
/* Shutdown procedure */
1277
if(not remove_local_route(ip, if_index)){
1278
fprintf_plus(stderr, "Failed to remove local route to %s on"
1279
" interface %d", ip, if_index);
1283
free(decrypted_buffer);
1286
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1292
perror_plus("close");
1294
gnutls_deinit(session);
512
free(decrypted_buffer);
521
fprintf(stderr, "Closing TLS session\n");
525
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
528
gnutls_deinit (es.session);
529
gnutls_certificate_free_credentials (es.cred);
530
gnutls_global_deinit ();
1304
__attribute__((nonnull))
1305
static void resolve_callback(AvahiSServiceResolver *r,
1306
AvahiIfIndex interface,
1307
AvahiProtocol proto,
1308
AvahiResolverEvent event,
1312
const char *host_name,
1313
const AvahiAddress *address,
1315
AVAHI_GCC_UNUSED AvahiStringList *txt,
1316
AVAHI_GCC_UNUSED AvahiLookupResultFlags
534
static AvahiSimplePoll *simple_poll = NULL;
535
static AvahiServer *server = NULL;
537
static void resolve_callback(
538
AvahiSServiceResolver *r,
539
AvahiIfIndex interface,
540
AVAHI_GCC_UNUSED AvahiProtocol protocol,
541
AvahiResolverEvent event,
545
const char *host_name,
546
const AvahiAddress *address,
548
AVAHI_GCC_UNUSED AvahiStringList *txt,
549
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
550
AVAHI_GCC_UNUSED void* userdata) {
552
assert(r); /* Spurious warning */
1323
554
/* Called whenever a service has been resolved successfully or
1327
avahi_s_service_resolver_free(r);
1333
559
case AVAHI_RESOLVER_FAILURE:
1334
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1335
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1337
avahi_strerror(avahi_server_errno
1338
(((mandos_context*)mc)->server)));
560
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
561
" type '%s' in domain '%s': %s\n", name, type, domain,
562
avahi_strerror(avahi_server_errno(server)));
1341
565
case AVAHI_RESOLVER_FOUND:
1343
567
char ip[AVAHI_ADDRESS_STR_MAX];
1344
568
avahi_address_snprint(ip, sizeof(ip), address);
1346
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1347
PRIdMAX ") on port %" PRIu16 "\n", name,
1348
host_name, ip, (intmax_t)interface, port);
570
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
" port %d\n", name, host_name, ip, port);
1350
int ret = start_mandos_communication(ip, (in_port_t)port,
1352
avahi_proto_to_af(proto),
1355
avahi_simple_poll_quit(simple_poll);
1357
if(not add_server(ip, (in_port_t)port, interface,
1358
avahi_proto_to_af(proto),
1359
&((mandos_context*)mc)->current_server)){
1360
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
573
int ret = start_mandos_communication(ip, port, interface);
1366
579
avahi_s_service_resolver_free(r);
1369
static void browse_callback(AvahiSServiceBrowser *b,
1370
AvahiIfIndex interface,
1371
AvahiProtocol protocol,
1372
AvahiBrowserEvent event,
1376
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1383
/* Called whenever a new services becomes available on the LAN or
1384
is removed from the LAN */
1392
case AVAHI_BROWSER_FAILURE:
1394
fprintf_plus(stderr, "(Avahi browser) %s\n",
1395
avahi_strerror(avahi_server_errno
1396
(((mandos_context*)mc)->server)));
1397
avahi_simple_poll_quit(simple_poll);
1400
case AVAHI_BROWSER_NEW:
1401
/* We ignore the returned Avahi resolver object. In the callback
1402
function we free it. If the Avahi server is terminated before
1403
the callback function is called the Avahi server will free the
1406
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1407
interface, protocol, name, type,
1408
domain, protocol, 0,
1409
resolve_callback, mc) == NULL)
1410
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1412
avahi_strerror(avahi_server_errno
1413
(((mandos_context*)mc)->server)));
1416
case AVAHI_BROWSER_REMOVE:
1419
case AVAHI_BROWSER_ALL_FOR_NOW:
1420
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1422
fprintf_plus(stderr, "No Mandos server found, still"
1429
/* Signal handler that stops main loop after SIGTERM */
1430
static void handle_sigterm(int sig){
1435
signal_received = sig;
1436
int old_errno = errno;
1437
/* set main loop to exit */
1438
if(simple_poll != NULL){
1439
avahi_simple_poll_quit(simple_poll);
1444
__attribute__((nonnull, warn_unused_result))
1445
bool get_flags(const char *ifname, struct ifreq *ifr){
1449
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1452
perror_plus("socket");
1456
strcpy(ifr->ifr_name, ifname);
1457
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1461
perror_plus("ioctl SIOCGIFFLAGS");
1469
__attribute__((nonnull, warn_unused_result))
1470
bool good_flags(const char *ifname, const struct ifreq *ifr){
1472
/* Reject the loopback device */
1473
if(ifr->ifr_flags & IFF_LOOPBACK){
1475
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1480
/* Accept point-to-point devices only if connect_to is specified */
1481
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1483
fprintf_plus(stderr, "Accepting point-to-point interface"
1484
" \"%s\"\n", ifname);
1488
/* Otherwise, reject non-broadcast-capable devices */
1489
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1491
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1492
" \"%s\"\n", ifname);
1496
/* Reject non-ARP interfaces (including dummy interfaces) */
1497
if(ifr->ifr_flags & IFF_NOARP){
1499
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1505
/* Accept this device */
1507
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1513
* This function determines if a directory entry in /sys/class/net
1514
* corresponds to an acceptable network device.
1515
* (This function is passed to scandir(3) as a filter function.)
1517
__attribute__((nonnull, warn_unused_result))
1518
int good_interface(const struct dirent *if_entry){
1519
if(if_entry->d_name[0] == '.'){
1524
if(not get_flags(if_entry->d_name, &ifr)){
1526
fprintf_plus(stderr, "Failed to get flags for interface "
1527
"\"%s\"\n", if_entry->d_name);
1532
if(not good_flags(if_entry->d_name, &ifr)){
1539
* This function determines if a network interface is up.
1541
__attribute__((nonnull, warn_unused_result))
1542
bool interface_is_up(const char *interface){
1544
if(not get_flags(interface, &ifr)){
1546
fprintf_plus(stderr, "Failed to get flags for interface "
1547
"\"%s\"\n", interface);
1552
return (bool)(ifr.ifr_flags & IFF_UP);
1556
* This function determines if a network interface is running
1558
__attribute__((nonnull, warn_unused_result))
1559
bool interface_is_running(const char *interface){
1561
if(not get_flags(interface, &ifr)){
1563
fprintf_plus(stderr, "Failed to get flags for interface "
1564
"\"%s\"\n", interface);
1569
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1572
__attribute__((nonnull, pure, warn_unused_result))
1573
int notdotentries(const struct dirent *direntry){
1574
/* Skip "." and ".." */
1575
if(direntry->d_name[0] == '.'
1576
and (direntry->d_name[1] == '\0'
1577
or (direntry->d_name[1] == '.'
1578
and direntry->d_name[2] == '\0'))){
1584
/* Is this directory entry a runnable program? */
1585
__attribute__((nonnull, warn_unused_result))
1586
int runnable_hook(const struct dirent *direntry){
1591
if((direntry->d_name)[0] == '\0'){
1596
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1597
"abcdefghijklmnopqrstuvwxyz"
1600
if((direntry->d_name)[sret] != '\0'){
1601
/* Contains non-allowed characters */
1603
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1609
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1612
perror_plus("Could not stat hook");
1616
if(not (S_ISREG(st.st_mode))){
1617
/* Not a regular file */
1619
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1624
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1625
/* Not executable */
1627
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1633
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1639
__attribute__((nonnull, warn_unused_result))
1640
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1641
mandos_context *mc){
1643
struct timespec now;
1644
struct timespec waited_time;
1645
intmax_t block_time;
1648
if(mc->current_server == NULL){
1650
fprintf_plus(stderr, "Wait until first server is found."
1653
ret = avahi_simple_poll_iterate(s, -1);
1656
fprintf_plus(stderr, "Check current_server if we should run"
1659
/* the current time */
1660
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1662
perror_plus("clock_gettime");
1665
/* Calculating in ms how long time between now and server
1666
who we visted longest time ago. Now - last seen. */
1667
waited_time.tv_sec = (now.tv_sec
1668
- mc->current_server->last_seen.tv_sec);
1669
waited_time.tv_nsec = (now.tv_nsec
1670
- mc->current_server->last_seen.tv_nsec);
1671
/* total time is 10s/10,000ms.
1672
Converting to s from ms by dividing by 1,000,
1673
and ns to ms by dividing by 1,000,000. */
1674
block_time = ((retry_interval
1675
- ((intmax_t)waited_time.tv_sec * 1000))
1676
- ((intmax_t)waited_time.tv_nsec / 1000000));
1679
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1683
if(block_time <= 0){
1684
ret = start_mandos_communication(mc->current_server->ip,
1685
mc->current_server->port,
1686
mc->current_server->if_index,
1687
mc->current_server->af, mc);
1689
avahi_simple_poll_quit(s);
1692
ret = clock_gettime(CLOCK_MONOTONIC,
1693
&mc->current_server->last_seen);
1695
perror_plus("clock_gettime");
1698
mc->current_server = mc->current_server->next;
1699
block_time = 0; /* Call avahi to find new Mandos
1700
servers, but don't block */
1703
ret = avahi_simple_poll_iterate(s, (int)block_time);
1706
if(ret > 0 or errno != EINTR){
1707
return (ret != 1) ? ret : 0;
1713
__attribute__((nonnull))
1714
void run_network_hooks(const char *mode, const char *interface,
1716
struct dirent **direntries = NULL;
1717
if(hookdir_fd == -1){
1718
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1720
if(hookdir_fd == -1){
1721
if(errno == ENOENT){
1723
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1724
" found\n", hookdir);
1727
perror_plus("open");
582
static void browse_callback(
583
AvahiSServiceBrowser *b,
584
AvahiIfIndex interface,
585
AvahiProtocol protocol,
586
AvahiBrowserEvent event,
590
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
593
AvahiServer *s = userdata;
594
assert(b); /* Spurious warning */
596
/* Called whenever a new services becomes available on the LAN or
597
is removed from the LAN */
601
case AVAHI_BROWSER_FAILURE:
603
fprintf(stderr, "(Browser) %s\n",
604
avahi_strerror(avahi_server_errno(server)));
605
avahi_simple_poll_quit(simple_poll);
1733
#if __GLIBC_PREREQ(2, 15)
1734
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1735
runnable_hook, alphasort);
1736
#else /* not __GLIBC_PREREQ(2, 15) */
1737
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1739
#endif /* not __GLIBC_PREREQ(2, 15) */
1740
#else /* not __GLIBC__ */
1741
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1743
#endif /* not __GLIBC__ */
1745
perror_plus("scandir");
1748
struct dirent *direntry;
1750
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1752
perror_plus("open(\"/dev/null\", O_RDONLY)");
1755
for(int i = 0; i < numhooks; i++){
1756
direntry = direntries[i];
1758
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1761
pid_t hook_pid = fork();
1764
/* Raise privileges */
1765
errno = raise_privileges_permanently();
1767
perror_plus("Failed to raise privileges");
1774
perror_plus("setgid");
1777
/* Reset supplementary groups */
1779
ret = setgroups(0, NULL);
1781
perror_plus("setgroups");
1784
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1786
perror_plus("setenv");
1789
ret = setenv("DEVICE", interface, 1);
1791
perror_plus("setenv");
1794
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1796
perror_plus("setenv");
1799
ret = setenv("MODE", mode, 1);
1801
perror_plus("setenv");
1805
ret = asprintf(&delaystring, "%f", (double)delay);
1807
perror_plus("asprintf");
1810
ret = setenv("DELAY", delaystring, 1);
1813
perror_plus("setenv");
1817
if(connect_to != NULL){
1818
ret = setenv("CONNECT", connect_to, 1);
1820
perror_plus("setenv");
1824
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
1828
perror_plus("openat");
1829
_exit(EXIT_FAILURE);
1831
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1832
perror_plus("close");
1833
_exit(EXIT_FAILURE);
1835
ret = dup2(devnull, STDIN_FILENO);
1837
perror_plus("dup2(devnull, STDIN_FILENO)");
1840
ret = (int)TEMP_FAILURE_RETRY(close(devnull));
1842
perror_plus("close");
1845
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1847
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1850
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
1852
perror_plus("fexecve");
1853
_exit(EXIT_FAILURE);
1857
perror_plus("fork");
1862
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1863
perror_plus("waitpid");
1867
if(WIFEXITED(status)){
1868
if(WEXITSTATUS(status) != 0){
1869
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1870
" with status %d\n", direntry->d_name,
1871
WEXITSTATUS(status));
1875
} else if(WIFSIGNALED(status)){
1876
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1877
" signal %d\n", direntry->d_name,
1882
fprintf_plus(stderr, "Warning: network hook \"%s\""
1883
" crashed\n", direntry->d_name);
1889
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1895
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1896
perror_plus("close");
1903
__attribute__((nonnull, warn_unused_result))
1904
error_t bring_up_interface(const char *const interface,
1906
error_t old_errno = errno;
1908
struct ifreq network;
1909
unsigned int if_index = if_nametoindex(interface);
1911
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1921
if(not interface_is_up(interface)){
1922
error_t ret_errno = 0, ioctl_errno = 0;
1923
if(not get_flags(interface, &network)){
1925
fprintf_plus(stderr, "Failed to get flags for interface "
1926
"\"%s\"\n", interface);
1930
network.ifr_flags |= IFF_UP; /* set flag */
1932
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1935
perror_plus("socket");
1941
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1943
perror_plus("close");
1950
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
1954
/* Raise privileges */
1955
ret_errno = raise_privileges();
1958
perror_plus("Failed to raise privileges");
1963
bool restore_loglevel = false;
1965
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1966
messages about the network interface to mess up the prompt */
1967
ret_linux = klogctl(8, NULL, 5);
1968
if(ret_linux == -1){
1969
perror_plus("klogctl");
1971
restore_loglevel = true;
1974
#endif /* __linux__ */
1975
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1976
ioctl_errno = errno;
1978
if(restore_loglevel){
1979
ret_linux = klogctl(7, NULL, 0);
1980
if(ret_linux == -1){
1981
perror_plus("klogctl");
1984
#endif /* __linux__ */
1986
/* If raise_privileges() succeeded above */
1988
/* Lower privileges */
1989
ret_errno = lower_privileges();
1992
perror_plus("Failed to lower privileges");
1996
/* Close the socket */
1997
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1999
perror_plus("close");
2002
if(ret_setflags == -1){
2003
errno = ioctl_errno;
2004
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2009
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2013
/* Sleep checking until interface is running.
2014
Check every 0.25s, up to total time of delay */
2015
for(int i=0; i < delay * 4; i++){
2016
if(interface_is_running(interface)){
2019
struct timespec sleeptime = { .tv_nsec = 250000000 };
2020
ret = nanosleep(&sleeptime, NULL);
2021
if(ret == -1 and errno != EINTR){
2022
perror_plus("nanosleep");
2030
__attribute__((nonnull, warn_unused_result))
2031
error_t take_down_interface(const char *const interface){
2032
error_t old_errno = errno;
2033
struct ifreq network;
2034
unsigned int if_index = if_nametoindex(interface);
2036
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2040
if(interface_is_up(interface)){
2041
error_t ret_errno = 0, ioctl_errno = 0;
2042
if(not get_flags(interface, &network) and debug){
2044
fprintf_plus(stderr, "Failed to get flags for interface "
2045
"\"%s\"\n", interface);
2049
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2051
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2054
perror_plus("socket");
2060
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2064
/* Raise privileges */
2065
ret_errno = raise_privileges();
2068
perror_plus("Failed to raise privileges");
2071
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2072
ioctl_errno = errno;
2074
/* If raise_privileges() succeeded above */
2076
/* Lower privileges */
2077
ret_errno = lower_privileges();
2080
perror_plus("Failed to lower privileges");
2084
/* Close the socket */
2085
int ret = (int)TEMP_FAILURE_RETRY(close(sd));
2087
perror_plus("close");
2090
if(ret_setflags == -1){
2091
errno = ioctl_errno;
2092
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2097
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2105
int main(int argc, char *argv[]){
2106
mandos_context mc = { .server = NULL, .dh_bits = 1024,
2107
.priority = "SECURE256:!CTYPE-X.509:"
2108
"+CTYPE-OPENPGP", .current_server = NULL,
2109
.interfaces = NULL, .interfaces_size = 0 };
2110
AvahiSServiceBrowser *sb = NULL;
2115
int exitcode = EXIT_SUCCESS;
2116
char *interfaces_to_take_down = NULL;
2117
size_t interfaces_to_take_down_size = 0;
2118
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2119
char old_tempdir[] = "/tmp/mandosXXXXXX";
2120
char *tempdir = NULL;
2121
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2122
const char *seckey = PATHDIR "/" SECKEY;
2123
const char *pubkey = PATHDIR "/" PUBKEY;
2124
char *interfaces_hooks = NULL;
2126
bool gnutls_initialized = false;
2127
bool gpgme_initialized = false;
2129
double retry_interval = 10; /* 10s between trying a server and
2130
retrying the same server again */
2132
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2133
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2138
/* Lower any group privileges we might have, just to be safe */
2142
perror_plus("setgid");
2145
/* Lower user privileges (temporarily) */
2149
perror_plus("seteuid");
2157
struct argp_option options[] = {
2158
{ .name = "debug", .key = 128,
2159
.doc = "Debug mode", .group = 3 },
2160
{ .name = "connect", .key = 'c',
2161
.arg = "ADDRESS:PORT",
2162
.doc = "Connect directly to a specific Mandos server",
2164
{ .name = "interface", .key = 'i',
2166
.doc = "Network interface that will be used to search for"
2169
{ .name = "seckey", .key = 's',
2171
.doc = "OpenPGP secret key file base name",
2173
{ .name = "pubkey", .key = 'p',
2175
.doc = "OpenPGP public key file base name",
2177
{ .name = "dh-bits", .key = 129,
2179
.doc = "Bit length of the prime number used in the"
2180
" Diffie-Hellman key exchange",
2182
{ .name = "priority", .key = 130,
2184
.doc = "GnuTLS priority string for the TLS handshake",
2186
{ .name = "delay", .key = 131,
2188
.doc = "Maximum delay to wait for interface startup",
2190
{ .name = "retry", .key = 132,
2192
.doc = "Retry interval used when denied by the Mandos server",
2194
{ .name = "network-hook-dir", .key = 133,
2196
.doc = "Directory where network hooks are located",
2199
* These reproduce what we would get without ARGP_NO_HELP
2201
{ .name = "help", .key = '?',
2202
.doc = "Give this help list", .group = -1 },
2203
{ .name = "usage", .key = -3,
2204
.doc = "Give a short usage message", .group = -1 },
2205
{ .name = "version", .key = 'V',
2206
.doc = "Print program version", .group = -1 },
2210
error_t parse_opt(int key, char *arg,
2211
struct argp_state *state){
2214
case 128: /* --debug */
2217
case 'c': /* --connect */
2220
case 'i': /* --interface */
2221
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2224
argp_error(state, "%s", strerror(ret_errno));
2227
case 's': /* --seckey */
2230
case 'p': /* --pubkey */
2233
case 129: /* --dh-bits */
2235
tmpmax = strtoimax(arg, &tmp, 10);
2236
if(errno != 0 or tmp == arg or *tmp != '\0'
2237
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2238
argp_error(state, "Bad number of DH bits");
2240
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2242
case 130: /* --priority */
2245
case 131: /* --delay */
2247
delay = strtof(arg, &tmp);
2248
if(errno != 0 or tmp == arg or *tmp != '\0'){
2249
argp_error(state, "Bad delay");
2251
case 132: /* --retry */
2253
retry_interval = strtod(arg, &tmp);
2254
if(errno != 0 or tmp == arg or *tmp != '\0'
2255
or (retry_interval * 1000) > INT_MAX
2256
or retry_interval < 0){
2257
argp_error(state, "Bad retry interval");
2260
case 133: /* --network-hook-dir */
2264
* These reproduce what we would get without ARGP_NO_HELP
2266
case '?': /* --help */
2267
argp_state_help(state, state->out_stream,
2268
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2269
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2270
case -3: /* --usage */
2271
argp_state_help(state, state->out_stream,
2272
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2273
case 'V': /* --version */
2274
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2275
exit(argp_err_exit_status);
2278
return ARGP_ERR_UNKNOWN;
2283
struct argp argp = { .options = options, .parser = parse_opt,
2285
.doc = "Mandos client -- Get and decrypt"
2286
" passwords from a Mandos server" };
2287
ret = argp_parse(&argp, argc, argv,
2288
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2295
perror_plus("argp_parse");
2296
exitcode = EX_OSERR;
2299
exitcode = EX_USAGE;
2305
/* Work around Debian bug #633582:
2306
<http://bugs.debian.org/633582> */
2308
/* Re-raise privileges */
2309
ret_errno = raise_privileges();
2312
perror_plus("Failed to raise privileges");
2316
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2317
int seckey_fd = open(seckey, O_RDONLY);
2318
if(seckey_fd == -1){
2319
perror_plus("open");
2321
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2323
perror_plus("fstat");
2325
if(S_ISREG(st.st_mode)
2326
and st.st_uid == 0 and st.st_gid == 0){
2327
ret = fchown(seckey_fd, uid, gid);
2329
perror_plus("fchown");
2333
TEMP_FAILURE_RETRY(close(seckey_fd));
2337
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2338
int pubkey_fd = open(pubkey, O_RDONLY);
2339
if(pubkey_fd == -1){
2340
perror_plus("open");
2342
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2344
perror_plus("fstat");
2346
if(S_ISREG(st.st_mode)
2347
and st.st_uid == 0 and st.st_gid == 0){
2348
ret = fchown(pubkey_fd, uid, gid);
2350
perror_plus("fchown");
2354
TEMP_FAILURE_RETRY(close(pubkey_fd));
2358
/* Lower privileges */
2359
ret_errno = lower_privileges();
2362
perror_plus("Failed to lower privileges");
2367
/* Remove invalid interface names (except "none") */
2369
char *interface = NULL;
2370
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2372
if(strcmp(interface, "none") != 0
2373
and if_nametoindex(interface) == 0){
2374
if(interface[0] != '\0'){
2375
fprintf_plus(stderr, "Not using nonexisting interface"
2376
" \"%s\"\n", interface);
2378
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2384
/* Run network hooks */
2386
if(mc.interfaces != NULL){
2387
interfaces_hooks = malloc(mc.interfaces_size);
2388
if(interfaces_hooks == NULL){
2389
perror_plus("malloc");
2392
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2393
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2395
run_network_hooks("start", interfaces_hooks != NULL ?
2396
interfaces_hooks : "", delay);
2400
avahi_set_log_function(empty_log);
2403
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2404
from the signal handler */
2405
/* Initialize the pseudo-RNG for Avahi */
2406
srand((unsigned int) time(NULL));
2407
simple_poll = avahi_simple_poll_new();
2408
if(simple_poll == NULL){
2409
fprintf_plus(stderr,
2410
"Avahi: Failed to create simple poll object.\n");
2411
exitcode = EX_UNAVAILABLE;
2415
sigemptyset(&sigterm_action.sa_mask);
2416
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2418
perror_plus("sigaddset");
2419
exitcode = EX_OSERR;
2422
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2424
perror_plus("sigaddset");
2425
exitcode = EX_OSERR;
2428
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2430
perror_plus("sigaddset");
2431
exitcode = EX_OSERR;
2434
/* Need to check if the handler is SIG_IGN before handling:
2435
| [[info:libc:Initial Signal Actions]] |
2436
| [[info:libc:Basic Signal Handling]] |
2438
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2440
perror_plus("sigaction");
2443
if(old_sigterm_action.sa_handler != SIG_IGN){
2444
ret = sigaction(SIGINT, &sigterm_action, NULL);
2446
perror_plus("sigaction");
2447
exitcode = EX_OSERR;
2451
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2453
perror_plus("sigaction");
2456
if(old_sigterm_action.sa_handler != SIG_IGN){
2457
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2459
perror_plus("sigaction");
2460
exitcode = EX_OSERR;
2464
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2466
perror_plus("sigaction");
2469
if(old_sigterm_action.sa_handler != SIG_IGN){
2470
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2472
perror_plus("sigaction");
2473
exitcode = EX_OSERR;
2478
/* If no interfaces were specified, make a list */
2479
if(mc.interfaces == NULL){
2480
struct dirent **direntries = NULL;
2481
/* Look for any good interfaces */
2482
ret = scandir(sys_class_net, &direntries, good_interface,
2485
/* Add all found interfaces to interfaces list */
2486
for(int i = 0; i < ret; ++i){
2487
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2488
direntries[i]->d_name);
2491
perror_plus("argz_add");
2492
free(direntries[i]);
2496
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2497
direntries[i]->d_name);
2499
free(direntries[i]);
2506
fprintf_plus(stderr, "Could not find a network interface\n");
2507
exitcode = EXIT_FAILURE;
2512
/* Bring up interfaces which are down, and remove any "none"s */
2514
char *interface = NULL;
2515
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2517
/* If interface name is "none", stop bringing up interfaces.
2518
Also remove all instances of "none" from the list */
2519
if(strcmp(interface, "none") == 0){
2520
argz_delete(&mc.interfaces, &mc.interfaces_size,
2523
while((interface = argz_next(mc.interfaces,
2524
mc.interfaces_size, interface))){
2525
if(strcmp(interface, "none") == 0){
2526
argz_delete(&mc.interfaces, &mc.interfaces_size,
2533
bool interface_was_up = interface_is_up(interface);
2534
errno = bring_up_interface(interface, delay);
2535
if(not interface_was_up){
2537
perror_plus("Failed to bring up interface");
2539
errno = argz_add(&interfaces_to_take_down,
2540
&interfaces_to_take_down_size,
2543
perror_plus("argz_add");
2548
if(debug and (interfaces_to_take_down == NULL)){
2549
fprintf_plus(stderr, "No interfaces were brought up\n");
2553
/* If we only got one interface, explicitly use only that one */
2554
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2556
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2559
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2566
ret = init_gnutls_global(pubkey, seckey, &mc);
2568
fprintf_plus(stderr, "init_gnutls_global failed\n");
2569
exitcode = EX_UNAVAILABLE;
2572
gnutls_initialized = true;
2579
/* Try /run/tmp before /tmp */
2580
tempdir = mkdtemp(run_tempdir);
2581
if(tempdir == NULL and errno == ENOENT){
2583
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2584
run_tempdir, old_tempdir);
2586
tempdir = mkdtemp(old_tempdir);
2588
if(tempdir == NULL){
2589
perror_plus("mkdtemp");
2597
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2598
fprintf_plus(stderr, "init_gpgme failed\n");
2599
exitcode = EX_UNAVAILABLE;
2602
gpgme_initialized = true;
2609
if(connect_to != NULL){
2610
/* Connect directly, do not use Zeroconf */
2611
/* (Mainly meant for debugging) */
2612
char *address = strrchr(connect_to, ':');
2614
if(address == NULL){
2615
fprintf_plus(stderr, "No colon in address\n");
2616
exitcode = EX_USAGE;
2626
tmpmax = strtoimax(address+1, &tmp, 10);
2627
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2628
or tmpmax != (in_port_t)tmpmax){
2629
fprintf_plus(stderr, "Bad port number\n");
2630
exitcode = EX_USAGE;
2638
port = (in_port_t)tmpmax;
2640
/* Colon in address indicates IPv6 */
2642
if(strchr(connect_to, ':') != NULL){
2644
/* Accept [] around IPv6 address - see RFC 5952 */
2645
if(connect_to[0] == '[' and address[-1] == ']')
2653
address = connect_to;
2659
while(not quit_now){
2660
ret = start_mandos_communication(address, port, if_index, af,
2662
if(quit_now or ret == 0){
2666
fprintf_plus(stderr, "Retrying in %d seconds\n",
2667
(int)retry_interval);
2669
sleep((unsigned int)retry_interval);
2673
exitcode = EXIT_SUCCESS;
608
case AVAHI_BROWSER_NEW:
609
/* We ignore the returned resolver object. In the callback
610
function we free it. If the server is terminated before
611
the callback function is called the server will free
612
the resolver for us. */
614
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
616
AVAHI_PROTO_INET6, 0,
617
resolve_callback, s)))
618
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
619
avahi_strerror(avahi_server_errno(s)));
622
case AVAHI_BROWSER_REMOVE:
625
case AVAHI_BROWSER_ALL_FOR_NOW:
626
case AVAHI_BROWSER_CACHE_EXHAUSTED:
631
/* Combines file name and path and returns the malloced new
632
string. some sane checks could/should be added */
633
static const char *combinepath(const char *first, const char *second){
634
size_t f_len = strlen(first);
635
size_t s_len = strlen(second);
636
char *tmp = malloc(f_len + s_len + 2);
641
memcpy(tmp, first, f_len);
645
memcpy(tmp + f_len + 1, second, s_len);
647
tmp[f_len + 1 + s_len] = '\0';
652
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
2684
653
AvahiServerConfig config;
2685
/* Do not publish any local Zeroconf records */
654
AvahiSServiceBrowser *sb = NULL;
657
int returncode = EXIT_SUCCESS;
658
const char *interface = NULL;
659
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
660
char *connect_to = NULL;
663
static struct option long_options[] = {
664
{"debug", no_argument, (int *)&debug, 1},
665
{"connect", required_argument, 0, 'C'},
666
{"interface", required_argument, 0, 'i'},
667
{"certdir", required_argument, 0, 'd'},
668
{"certkey", required_argument, 0, 'c'},
669
{"certfile", required_argument, 0, 'k'},
672
int option_index = 0;
673
ret = getopt_long (argc, argv, "i:", long_options,
703
certfile = combinepath(certdir, certfile);
704
if (certfile == NULL){
705
perror("combinepath");
709
if(interface != NULL){
710
if_index = (AvahiIfIndex) if_nametoindex(interface);
712
fprintf(stderr, "No such interface: \"%s\"\n", interface);
717
if(connect_to != NULL){
718
/* Connect directly, do not use Zeroconf */
719
/* (Mainly meant for debugging) */
720
char *address = strrchr(connect_to, ':');
722
fprintf(stderr, "No colon in address\n");
726
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
728
perror("Bad port number");
732
address = connect_to;
733
ret = start_mandos_communication(address, port, if_index);
741
certkey = combinepath(certdir, certkey);
742
if (certkey == NULL){
743
perror("combinepath");
748
avahi_set_log_function(empty_log);
751
/* Initialize the psuedo-RNG */
752
srand((unsigned int) time(NULL));
754
/* Allocate main loop object */
755
if (!(simple_poll = avahi_simple_poll_new())) {
756
fprintf(stderr, "Failed to create simple poll object.\n");
761
/* Do not publish any local records */
2686
762
avahi_server_config_init(&config);
2687
763
config.publish_hinfo = 0;
2688
764
config.publish_addresses = 0;
2689
765
config.publish_workstation = 0;
2690
766
config.publish_domain = 0;
2692
768
/* Allocate a new server */
2693
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2694
&config, NULL, NULL, &ret_errno);
2696
/* Free the Avahi configuration data */
769
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
770
&config, NULL, NULL, &error);
772
/* Free the configuration data */
2697
773
avahi_server_config_free(&config);
2700
/* Check if creating the Avahi server object succeeded */
2701
if(mc.server == NULL){
2702
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2703
avahi_strerror(ret_errno));
2704
exitcode = EX_UNAVAILABLE;
2712
/* Create the Avahi service browser */
2713
sb = avahi_s_service_browser_new(mc.server, if_index,
2714
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2715
NULL, 0, browse_callback,
2718
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2719
avahi_strerror(avahi_server_errno(mc.server)));
2720
exitcode = EX_UNAVAILABLE;
2728
/* Run the main loop */
2731
fprintf_plus(stderr, "Starting Avahi loop search\n");
2734
ret = avahi_loop_with_timeout(simple_poll,
2735
(int)(retry_interval * 1000), &mc);
2737
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2738
(ret == 0) ? "successfully" : "with error");
2744
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2747
/* Cleanup things */
2748
free(mc.interfaces);
2751
avahi_s_service_browser_free(sb);
2753
if(mc.server != NULL)
2754
avahi_server_free(mc.server);
2756
if(simple_poll != NULL)
2757
avahi_simple_poll_free(simple_poll);
2759
if(gnutls_initialized){
2760
gnutls_certificate_free_credentials(mc.cred);
2761
gnutls_global_deinit();
2762
gnutls_dh_params_deinit(mc.dh_params);
2765
if(gpgme_initialized){
2766
gpgme_release(mc.ctx);
2769
/* Cleans up the circular linked list of Mandos servers the client
2771
if(mc.current_server != NULL){
2772
mc.current_server->prev->next = NULL;
2773
while(mc.current_server != NULL){
2774
server *next = mc.current_server->next;
2776
#pragma GCC diagnostic push
2777
#pragma GCC diagnostic ignored "-Wcast-qual"
2779
free((char *)(mc.current_server->ip));
2781
#pragma GCC diagnostic pop
2783
free(mc.current_server);
2784
mc.current_server = next;
2788
/* Re-raise privileges */
2790
ret_errno = raise_privileges();
2793
perror_plus("Failed to raise privileges");
2796
/* Run network hooks */
2797
run_network_hooks("stop", interfaces_hooks != NULL ?
2798
interfaces_hooks : "", delay);
2800
/* Take down the network interfaces which were brought up */
2802
char *interface = NULL;
2803
while((interface=argz_next(interfaces_to_take_down,
2804
interfaces_to_take_down_size,
2806
ret_errno = take_down_interface(interface);
2809
perror_plus("Failed to take down interface");
2812
if(debug and (interfaces_to_take_down == NULL)){
2813
fprintf_plus(stderr, "No interfaces needed to be taken"
2819
ret_errno = lower_privileges_permanently();
2822
perror_plus("Failed to lower privileges permanently");
2826
free(interfaces_to_take_down);
2827
free(interfaces_hooks);
2829
/* Removes the GPGME temp directory and all files inside */
2830
if(tempdir != NULL){
2831
struct dirent **direntries = NULL;
2832
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2836
if(tempdir_fd == -1){
2837
perror_plus("open");
2840
#if __GLIBC_PREREQ(2, 15)
2841
int numentries = scandirat(tempdir_fd, ".", &direntries,
2842
notdotentries, alphasort);
2843
#else /* not __GLIBC_PREREQ(2, 15) */
2844
int numentries = scandir(tempdir, &direntries, notdotentries,
2846
#endif /* not __GLIBC_PREREQ(2, 15) */
2847
#else /* not __GLIBC__ */
2848
int numentries = scandir(tempdir, &direntries, notdotentries,
2850
#endif /* not __GLIBC__ */
2851
if(numentries >= 0){
2852
for(int i = 0; i < numentries; i++){
2853
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2855
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2856
" \"%s\", 0): %s\n", tempdir,
2857
direntries[i]->d_name, strerror(errno));
2859
free(direntries[i]);
2862
/* need to clean even if 0 because man page doesn't specify */
2864
if(numentries == -1){
2865
perror_plus("scandir");
2867
ret = rmdir(tempdir);
2868
if(ret == -1 and errno != ENOENT){
2869
perror_plus("rmdir");
2872
TEMP_FAILURE_RETRY(close(tempdir_fd));
2877
sigemptyset(&old_sigterm_action.sa_mask);
2878
old_sigterm_action.sa_handler = SIG_DFL;
2879
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2880
&old_sigterm_action,
2883
perror_plus("sigaction");
2886
ret = raise(signal_received);
2887
} while(ret != 0 and errno == EINTR);
2889
perror_plus("raise");
2892
TEMP_FAILURE_RETRY(pause());
775
/* Check if creating the server object succeeded */
777
fprintf(stderr, "Failed to create server: %s\n",
778
avahi_strerror(error));
779
returncode = EXIT_FAILURE;
783
/* Create the service browser */
784
sb = avahi_s_service_browser_new(server, if_index,
786
"_mandos._tcp", NULL, 0,
787
browse_callback, server);
789
fprintf(stderr, "Failed to create service browser: %s\n",
790
avahi_strerror(avahi_server_errno(server)));
791
returncode = EXIT_FAILURE;
795
/* Run the main loop */
798
fprintf(stderr, "Starting avahi loop search\n");
801
avahi_simple_poll_loop(simple_poll);
806
fprintf(stderr, "%s exiting\n", argv[0]);
811
avahi_s_service_browser_free(sb);
814
avahi_server_free(server);
817
avahi_simple_poll_free(simple_poll);