1
/* -*- coding: utf-8 -*- */
3
* Mandos-client - get and decrypt data from a Mandos server
5
* This program is partly derived from an example program for an Avahi
6
* service browser, downloaded from
7
* <http://avahi.org/browser/examples/core-browse-services.c>. This
8
* includes the following functions: "resolve_callback",
9
* "browse_callback", and parts of "main".
12
* Copyright © 2008-2018 Teddy Hogeborn
13
* Copyright © 2008-2018 Björn Påhlsson
15
* This file is part of Mandos.
17
* Mandos is free software: you can redistribute it and/or modify it
18
* under the terms of the GNU General Public License as published by
19
* the Free Software Foundation, either version 3 of the License, or
20
* (at your option) any later version.
22
* Mandos is distributed in the hope that it will be useful, but
23
* WITHOUT ANY WARRANTY; without even the implied warranty of
24
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25
* General Public License for more details.
27
* You should have received a copy of the GNU General Public License
28
* along with Mandos. If not, see <http://www.gnu.org/licenses/>.
30
* Contact the authors at <mandos@recompile.se>.
33
/* Needed by GPGME, specifically gpgme_data_seek() */
34
#ifndef _LARGEFILE_SOURCE
35
#define _LARGEFILE_SOURCE
36
#endif /* not _LARGEFILE_SOURCE */
37
#ifndef _FILE_OFFSET_BITS
38
#define _FILE_OFFSET_BITS 64
39
#endif /* not _FILE_OFFSET_BITS */
41
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
43
#include <stdio.h> /* fprintf(), stderr, fwrite(),
45
#include <stdint.h> /* uint16_t, uint32_t, intptr_t */
46
#include <stddef.h> /* NULL, size_t, ssize_t */
47
#include <stdlib.h> /* free(), EXIT_SUCCESS, srand(),
49
#include <stdbool.h> /* bool, false, true */
50
#include <string.h> /* strcmp(), strlen(), strerror(),
51
asprintf(), strncpy(), strsignal()
53
#include <sys/ioctl.h> /* ioctl */
54
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
55
sockaddr_in6, PF_INET6,
56
SOCK_STREAM, uid_t, gid_t, open(),
58
#include <sys/stat.h> /* open(), S_ISREG */
59
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
60
inet_pton(), connect(),
62
#include <fcntl.h> /* open(), unlinkat(), AT_REMOVEDIR */
63
#include <dirent.h> /* opendir(), struct dirent, readdir()
65
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
67
#include <errno.h> /* perror(), errno, EINTR, EINVAL,
68
EAI_SYSTEM, ENETUNREACH,
69
EHOSTUNREACH, ECONNREFUSED, EPROTO,
70
EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
72
program_invocation_short_name */
73
#include <time.h> /* nanosleep(), time(), sleep() */
74
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
75
SIOCSIFFLAGS, if_indextoname(),
76
if_nametoindex(), IF_NAMESIZE */
77
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
78
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
80
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
81
getuid(), getgid(), seteuid(),
82
setgid(), pause(), _exit(),
84
#include <arpa/inet.h> /* inet_pton(), htons() */
85
#include <iso646.h> /* not, or, and */
86
#include <argp.h> /* struct argp_option, error_t, struct
87
argp_state, struct argp,
88
argp_parse(), ARGP_KEY_ARG,
89
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
90
#include <signal.h> /* sigemptyset(), sigaddset(),
91
sigaction(), SIGTERM, sig_atomic_t,
93
#include <sysexits.h> /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
94
EX_NOHOST, EX_IOERR, EX_PROTOCOL */
95
#include <sys/wait.h> /* waitpid(), WIFEXITED(),
96
WEXITSTATUS(), WTERMSIG() */
97
#include <grp.h> /* setgroups() */
98
#include <argz.h> /* argz_add_sep(), argz_next(),
99
argz_delete(), argz_append(),
100
argz_stringify(), argz_add(),
102
#include <netdb.h> /* getnameinfo(), NI_NUMERICHOST,
103
EAI_SYSTEM, gai_strerror() */
106
#include <sys/klog.h> /* klogctl() */
107
#endif /* __linux__ */
110
/* All Avahi types, constants and functions
113
#include <avahi-core/core.h>
114
#include <avahi-core/lookup.h>
115
#include <avahi-core/log.h>
116
#include <avahi-common/simple-watch.h>
117
#include <avahi-common/malloc.h>
118
#include <avahi-common/error.h>
121
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
124
init_gnutls_session(),
126
#include <gnutls/openpgp.h>
127
/* gnutls_certificate_set_openpgp_key_file(),
128
GNUTLS_OPENPGP_FMT_BASE64 */
131
#include <gpgme.h> /* All GPGME types, constants and
134
GPGME_PROTOCOL_OpenPGP,
137
#define BUFFER_SIZE 256
139
#define PATHDIR "/conf/conf.d/mandos"
140
#define SECKEY "seckey.txt"
141
#define PUBKEY "pubkey.txt"
142
#define HOOKDIR "/lib/mandos/network-hooks.d"
145
static const char mandos_protocol_version[] = "1";
146
const char *argp_program_version = "mandos-client " VERSION;
147
const char *argp_program_bug_address = "<mandos@recompile.se>";
148
static const char sys_class_net[] = "/sys/class/net";
149
char *connect_to = NULL;
150
const char *hookdir = HOOKDIR;
155
/* Doubly linked list that need to be circularly linked when used */
156
typedef struct server{
159
AvahiIfIndex if_index;
161
struct timespec last_seen;
166
/* Used for passing in values through the Avahi callback functions */
169
gnutls_certificate_credentials_t cred;
170
unsigned int dh_bits;
171
gnutls_dh_params_t dh_params;
172
const char *priority;
174
server *current_server;
176
size_t interfaces_size;
179
/* global so signal handler can reach it*/
180
AvahiSimplePoll *simple_poll;
182
sig_atomic_t quit_now = 0;
183
int signal_received = 0;
185
/* Function to use when printing errors */
186
void perror_plus(const char *print_text){
188
fprintf(stderr, "Mandos plugin %s: ",
189
program_invocation_short_name);
194
__attribute__((format (gnu_printf, 2, 3), nonnull))
195
int fprintf_plus(FILE *stream, const char *format, ...){
197
va_start (ap, format);
199
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
200
program_invocation_short_name));
201
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
205
* Make additional room in "buffer" for at least BUFFER_SIZE more
206
* bytes. "buffer_capacity" is how much is currently allocated,
207
* "buffer_length" is how much is already used.
209
__attribute__((nonnull, warn_unused_result))
210
size_t incbuffer(char **buffer, size_t buffer_length,
211
size_t buffer_capacity){
212
if(buffer_length + BUFFER_SIZE > buffer_capacity){
213
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
215
int old_errno = errno;
222
buffer_capacity += BUFFER_SIZE;
224
return buffer_capacity;
227
/* Add server to set of servers to retry periodically */
228
__attribute__((nonnull, warn_unused_result))
229
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
230
int af, server **current_server){
232
server *new_server = malloc(sizeof(server));
233
if(new_server == NULL){
234
perror_plus("malloc");
237
*new_server = (server){ .ip = strdup(ip),
239
.if_index = if_index,
241
if(new_server->ip == NULL){
242
perror_plus("strdup");
246
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
248
perror_plus("clock_gettime");
250
#pragma GCC diagnostic push
251
#pragma GCC diagnostic ignored "-Wcast-qual"
253
free((char *)(new_server->ip));
255
#pragma GCC diagnostic pop
260
/* Special case of first server */
261
if(*current_server == NULL){
262
new_server->next = new_server;
263
new_server->prev = new_server;
264
*current_server = new_server;
266
/* Place the new server last in the list */
267
new_server->next = *current_server;
268
new_server->prev = (*current_server)->prev;
269
new_server->prev->next = new_server;
270
(*current_server)->prev = new_server;
278
__attribute__((nonnull, warn_unused_result))
279
static bool init_gpgme(const char * const seckey,
280
const char * const pubkey,
281
const char * const tempdir,
284
gpgme_engine_info_t engine_info;
287
* Helper function to insert pub and seckey to the engine keyring.
289
bool import_key(const char * const filename){
292
gpgme_data_t pgp_data;
294
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
300
rc = gpgme_data_new_from_fd(&pgp_data, fd);
301
if(rc != GPG_ERR_NO_ERROR){
302
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
303
gpgme_strsource(rc), gpgme_strerror(rc));
307
rc = gpgme_op_import(mc->ctx, pgp_data);
308
if(rc != GPG_ERR_NO_ERROR){
309
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
310
gpgme_strsource(rc), gpgme_strerror(rc));
314
gpgme_import_result_t import_result
315
= gpgme_op_import_result(mc->ctx);
316
if((import_result->imported < 1
317
or import_result->not_imported > 0)
318
and import_result->unchanged == 0){
319
fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
321
"The total number of considered keys: %d\n",
322
import_result->considered);
324
"The number of keys without user ID: %d\n",
325
import_result->no_user_id);
327
"The total number of imported keys: %d\n",
328
import_result->imported);
329
fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
330
import_result->imported_rsa);
331
fprintf_plus(stderr, "The number of unchanged keys: %d\n",
332
import_result->unchanged);
333
fprintf_plus(stderr, "The number of new user IDs: %d\n",
334
import_result->new_user_ids);
335
fprintf_plus(stderr, "The number of new sub keys: %d\n",
336
import_result->new_sub_keys);
337
fprintf_plus(stderr, "The number of new signatures: %d\n",
338
import_result->new_signatures);
339
fprintf_plus(stderr, "The number of new revocations: %d\n",
340
import_result->new_revocations);
342
"The total number of secret keys read: %d\n",
343
import_result->secret_read);
345
"The number of imported secret keys: %d\n",
346
import_result->secret_imported);
348
"The number of unchanged secret keys: %d\n",
349
import_result->secret_unchanged);
350
fprintf_plus(stderr, "The number of keys not imported: %d\n",
351
import_result->not_imported);
352
for(gpgme_import_status_t import_status
353
= import_result->imports;
354
import_status != NULL;
355
import_status = import_status->next){
356
fprintf_plus(stderr, "Import status for key: %s\n",
358
if(import_status->result != GPG_ERR_NO_ERROR){
359
fprintf_plus(stderr, "Import result: %s: %s\n",
360
gpgme_strsource(import_status->result),
361
gpgme_strerror(import_status->result));
363
fprintf_plus(stderr, "Key status:\n");
365
import_status->status & GPGME_IMPORT_NEW
366
? "The key was new.\n"
367
: "The key was not new.\n");
369
import_status->status & GPGME_IMPORT_UID
370
? "The key contained new user IDs.\n"
371
: "The key did not contain new user IDs.\n");
373
import_status->status & GPGME_IMPORT_SIG
374
? "The key contained new signatures.\n"
375
: "The key did not contain new signatures.\n");
377
import_status->status & GPGME_IMPORT_SUBKEY
378
? "The key contained new sub keys.\n"
379
: "The key did not contain new sub keys.\n");
381
import_status->status & GPGME_IMPORT_SECRET
382
? "The key contained a secret key.\n"
383
: "The key did not contain a secret key.\n");
391
perror_plus("close");
393
gpgme_data_release(pgp_data);
398
fprintf_plus(stderr, "Initializing GPGME\n");
402
gpgme_check_version(NULL);
403
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
404
if(rc != GPG_ERR_NO_ERROR){
405
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
406
gpgme_strsource(rc), gpgme_strerror(rc));
410
/* Set GPGME home directory for the OpenPGP engine only */
411
rc = gpgme_get_engine_info(&engine_info);
412
if(rc != GPG_ERR_NO_ERROR){
413
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
414
gpgme_strsource(rc), gpgme_strerror(rc));
417
while(engine_info != NULL){
418
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
419
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
420
engine_info->file_name, tempdir);
423
engine_info = engine_info->next;
425
if(engine_info == NULL){
426
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
431
/* Create new GPGME "context" */
432
rc = gpgme_new(&(mc->ctx));
433
if(rc != GPG_ERR_NO_ERROR){
434
fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
435
gpgme_strsource(rc), gpgme_strerror(rc));
439
if(not import_key(pubkey) or not import_key(seckey)){
447
* Decrypt OpenPGP data.
448
* Returns -1 on error
450
__attribute__((nonnull, warn_unused_result))
451
static ssize_t pgp_packet_decrypt(const char *cryptotext,
455
gpgme_data_t dh_crypto, dh_plain;
458
size_t plaintext_capacity = 0;
459
ssize_t plaintext_length = 0;
462
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
465
/* Create new GPGME data buffer from memory cryptotext */
466
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
468
if(rc != GPG_ERR_NO_ERROR){
469
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
470
gpgme_strsource(rc), gpgme_strerror(rc));
474
/* Create new empty GPGME data buffer for the plaintext */
475
rc = gpgme_data_new(&dh_plain);
476
if(rc != GPG_ERR_NO_ERROR){
477
fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
478
gpgme_strsource(rc), gpgme_strerror(rc));
479
gpgme_data_release(dh_crypto);
483
/* Decrypt data from the cryptotext data buffer to the plaintext
485
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
486
if(rc != GPG_ERR_NO_ERROR){
487
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
488
gpgme_strsource(rc), gpgme_strerror(rc));
489
plaintext_length = -1;
491
gpgme_decrypt_result_t result;
492
result = gpgme_op_decrypt_result(mc->ctx);
494
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
496
if(result->unsupported_algorithm != NULL) {
497
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
498
result->unsupported_algorithm);
500
fprintf_plus(stderr, "Wrong key usage: %s\n",
501
result->wrong_key_usage ? "Yes" : "No");
502
if(result->file_name != NULL){
503
fprintf_plus(stderr, "File name: %s\n", result->file_name);
506
for(gpgme_recipient_t r = result->recipients; r != NULL;
508
fprintf_plus(stderr, "Public key algorithm: %s\n",
509
gpgme_pubkey_algo_name(r->pubkey_algo));
510
fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
511
fprintf_plus(stderr, "Secret key available: %s\n",
512
r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
520
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
523
/* Seek back to the beginning of the GPGME plaintext data buffer */
524
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
525
perror_plus("gpgme_data_seek");
526
plaintext_length = -1;
532
plaintext_capacity = incbuffer(plaintext,
533
(size_t)plaintext_length,
535
if(plaintext_capacity == 0){
536
perror_plus("incbuffer");
537
plaintext_length = -1;
541
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
543
/* Print the data, if any */
549
perror_plus("gpgme_data_read");
550
plaintext_length = -1;
553
plaintext_length += ret;
557
fprintf_plus(stderr, "Decrypted password is: ");
558
for(ssize_t i = 0; i < plaintext_length; i++){
559
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
561
fprintf(stderr, "\n");
566
/* Delete the GPGME cryptotext data buffer */
567
gpgme_data_release(dh_crypto);
569
/* Delete the GPGME plaintext data buffer */
570
gpgme_data_release(dh_plain);
571
return plaintext_length;
574
__attribute__((warn_unused_result, const))
575
static const char *safe_string(const char *str){
581
__attribute__((warn_unused_result))
582
static const char *safer_gnutls_strerror(int value){
583
const char *ret = gnutls_strerror(value);
584
return safe_string(ret);
587
/* GnuTLS log function callback */
588
__attribute__((nonnull))
589
static void debuggnutls(__attribute__((unused)) int level,
591
fprintf_plus(stderr, "GnuTLS: %s", string);
594
__attribute__((nonnull(1, 2, 4), warn_unused_result))
595
static int init_gnutls_global(const char *pubkeyfilename,
596
const char *seckeyfilename,
597
const char *dhparamsfilename,
603
fprintf_plus(stderr, "Initializing GnuTLS\n");
607
/* "Use a log level over 10 to enable all debugging options."
610
gnutls_global_set_log_level(11);
611
gnutls_global_set_log_function(debuggnutls);
614
/* OpenPGP credentials */
615
ret = gnutls_certificate_allocate_credentials(&mc->cred);
616
if(ret != GNUTLS_E_SUCCESS){
617
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
618
safer_gnutls_strerror(ret));
623
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
624
" secret key %s as GnuTLS credentials\n",
629
ret = gnutls_certificate_set_openpgp_key_file
630
(mc->cred, pubkeyfilename, seckeyfilename,
631
GNUTLS_OPENPGP_FMT_BASE64);
632
if(ret != GNUTLS_E_SUCCESS){
634
"Error[%d] while reading the OpenPGP key pair ('%s',"
635
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
636
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
637
safer_gnutls_strerror(ret));
641
/* GnuTLS server initialization */
642
ret = gnutls_dh_params_init(&mc->dh_params);
643
if(ret != GNUTLS_E_SUCCESS){
644
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
645
" initialization: %s\n",
646
safer_gnutls_strerror(ret));
649
/* If a Diffie-Hellman parameters file was given, try to use it */
650
if(dhparamsfilename != NULL){
651
gnutls_datum_t params = { .data = NULL, .size = 0 };
653
int dhpfile = open(dhparamsfilename, O_RDONLY);
656
dhparamsfilename = NULL;
659
size_t params_capacity = 0;
661
params_capacity = incbuffer((char **)¶ms.data,
663
(size_t)params_capacity);
664
if(params_capacity == 0){
665
perror_plus("incbuffer");
668
dhparamsfilename = NULL;
671
ssize_t bytes_read = read(dhpfile,
672
params.data + params.size,
678
/* check bytes_read for failure */
683
dhparamsfilename = NULL;
686
params.size += (unsigned int)bytes_read;
688
ret = close(dhpfile);
690
perror_plus("close");
692
if(params.data == NULL){
693
dhparamsfilename = NULL;
695
if(dhparamsfilename == NULL){
698
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
699
GNUTLS_X509_FMT_PEM);
700
if(ret != GNUTLS_E_SUCCESS){
701
fprintf_plus(stderr, "Failed to parse DH parameters in file"
702
" \"%s\": %s\n", dhparamsfilename,
703
safer_gnutls_strerror(ret));
704
dhparamsfilename = NULL;
709
if(dhparamsfilename == NULL){
710
if(mc->dh_bits == 0){
711
/* Find out the optimal number of DH bits */
712
/* Try to read the private key file */
713
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
715
int secfile = open(seckeyfilename, O_RDONLY);
720
size_t buffer_capacity = 0;
722
buffer_capacity = incbuffer((char **)&buffer.data,
724
(size_t)buffer_capacity);
725
if(buffer_capacity == 0){
726
perror_plus("incbuffer");
731
ssize_t bytes_read = read(secfile,
732
buffer.data + buffer.size,
738
/* check bytes_read for failure */
745
buffer.size += (unsigned int)bytes_read;
749
/* If successful, use buffer to parse private key */
750
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
751
if(buffer.data != NULL){
753
gnutls_openpgp_privkey_t privkey = NULL;
754
ret = gnutls_openpgp_privkey_init(&privkey);
755
if(ret != GNUTLS_E_SUCCESS){
756
fprintf_plus(stderr, "Error initializing OpenPGP key"
758
safer_gnutls_strerror(ret));
762
ret = gnutls_openpgp_privkey_import
763
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
764
if(ret != GNUTLS_E_SUCCESS){
765
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
766
safer_gnutls_strerror(ret));
772
/* Use private key to suggest an appropriate
774
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
775
gnutls_openpgp_privkey_deinit(privkey);
777
fprintf_plus(stderr, "This OpenPGP key implies using"
778
" a GnuTLS security parameter \"%s\".\n",
779
safe_string(gnutls_sec_param_get_name
785
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
786
/* Err on the side of caution */
787
sec_param = GNUTLS_SEC_PARAM_ULTRA;
789
fprintf_plus(stderr, "Falling back to security parameter"
791
safe_string(gnutls_sec_param_get_name
796
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
800
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
801
" implies %u DH bits; using that.\n",
802
safe_string(gnutls_sec_param_get_name
807
fprintf_plus(stderr, "Failed to get implied number of DH"
808
" bits for security parameter \"%s\"): %s\n",
809
safe_string(gnutls_sec_param_get_name
811
safer_gnutls_strerror(ret));
815
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
818
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
819
if(ret != GNUTLS_E_SUCCESS){
820
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
821
" bits): %s\n", mc->dh_bits,
822
safer_gnutls_strerror(ret));
826
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
832
gnutls_certificate_free_credentials(mc->cred);
833
gnutls_dh_params_deinit(mc->dh_params);
837
__attribute__((nonnull, warn_unused_result))
838
static int init_gnutls_session(gnutls_session_t *session,
841
/* GnuTLS session creation */
843
ret = gnutls_init(session, GNUTLS_SERVER);
847
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
848
if(ret != GNUTLS_E_SUCCESS){
850
"Error in GnuTLS session initialization: %s\n",
851
safer_gnutls_strerror(ret));
857
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
859
gnutls_deinit(*session);
862
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
863
if(ret != GNUTLS_E_SUCCESS){
864
fprintf_plus(stderr, "Syntax error at: %s\n", err);
865
fprintf_plus(stderr, "GnuTLS error: %s\n",
866
safer_gnutls_strerror(ret));
867
gnutls_deinit(*session);
873
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
876
gnutls_deinit(*session);
879
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
880
if(ret != GNUTLS_E_SUCCESS){
881
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
882
safer_gnutls_strerror(ret));
883
gnutls_deinit(*session);
887
/* ignore client certificate if any. */
888
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
893
/* Avahi log function callback */
894
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
895
__attribute__((unused)) const char *txt){}
897
/* Set effective uid to 0, return errno */
898
__attribute__((warn_unused_result))
899
int raise_privileges(void){
900
int old_errno = errno;
902
if(seteuid(0) == -1){
909
/* Set effective and real user ID to 0. Return errno. */
910
__attribute__((warn_unused_result))
911
int raise_privileges_permanently(void){
912
int old_errno = errno;
913
int ret = raise_privileges();
925
/* Set effective user ID to unprivileged saved user ID */
926
__attribute__((warn_unused_result))
927
int lower_privileges(void){
928
int old_errno = errno;
930
if(seteuid(uid) == -1){
937
/* Lower privileges permanently */
938
__attribute__((warn_unused_result))
939
int lower_privileges_permanently(void){
940
int old_errno = errno;
942
if(setuid(uid) == -1){
949
/* Helper function to add_local_route() and delete_local_route() */
950
__attribute__((nonnull, warn_unused_result))
951
static bool add_delete_local_route(const bool add,
953
AvahiIfIndex if_index){
955
char helper[] = "mandos-client-iprouteadddel";
956
char add_arg[] = "add";
957
char delete_arg[] = "delete";
958
char debug_flag[] = "--debug";
959
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
960
if(pluginhelperdir == NULL){
962
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
963
" variable not set; cannot run helper\n");
968
char interface[IF_NAMESIZE];
969
if(if_indextoname((unsigned int)if_index, interface) == NULL){
970
perror_plus("if_indextoname");
974
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
976
perror_plus("open(\"/dev/null\", O_RDONLY)");
982
/* Raise privileges */
983
errno = raise_privileges_permanently();
985
perror_plus("Failed to raise privileges");
986
/* _exit(EX_NOPERM); */
992
perror_plus("setgid");
995
/* Reset supplementary groups */
997
ret = setgroups(0, NULL);
999
perror_plus("setgroups");
1003
ret = dup2(devnull, STDIN_FILENO);
1005
perror_plus("dup2(devnull, STDIN_FILENO)");
1008
ret = close(devnull);
1010
perror_plus("close");
1013
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1015
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1018
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
1023
if(helperdir_fd == -1){
1024
perror_plus("open");
1025
_exit(EX_UNAVAILABLE);
1027
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
1029
if(helper_fd == -1){
1030
perror_plus("openat");
1031
close(helperdir_fd);
1032
_exit(EX_UNAVAILABLE);
1034
close(helperdir_fd);
1036
#pragma GCC diagnostic push
1037
#pragma GCC diagnostic ignored "-Wcast-qual"
1039
if(fexecve(helper_fd, (char *const [])
1040
{ helper, add ? add_arg : delete_arg, (char *)address,
1041
interface, debug ? debug_flag : NULL, NULL },
1044
#pragma GCC diagnostic pop
1046
perror_plus("fexecve");
1047
_exit(EXIT_FAILURE);
1051
perror_plus("fork");
1058
pret = waitpid(pid, &status, 0);
1059
if(pret == -1 and errno == EINTR and quit_now){
1060
int errno_raising = 0;
1061
if((errno = raise_privileges()) != 0){
1062
errno_raising = errno;
1063
perror_plus("Failed to raise privileges in order to"
1064
" kill helper program");
1066
if(kill(pid, SIGTERM) == -1){
1067
perror_plus("kill");
1069
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
1070
perror_plus("Failed to lower privileges after killing"
1075
} while(pret == -1 and errno == EINTR);
1077
perror_plus("waitpid");
1080
if(WIFEXITED(status)){
1081
if(WEXITSTATUS(status) != 0){
1082
fprintf_plus(stderr, "Error: iprouteadddel exited"
1083
" with status %d\n", WEXITSTATUS(status));
1088
if(WIFSIGNALED(status)){
1089
fprintf_plus(stderr, "Error: iprouteadddel died by"
1090
" signal %d\n", WTERMSIG(status));
1093
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1097
__attribute__((nonnull, warn_unused_result))
1098
static bool add_local_route(const char *address,
1099
AvahiIfIndex if_index){
1101
fprintf_plus(stderr, "Adding route to %s\n", address);
1103
return add_delete_local_route(true, address, if_index);
1106
__attribute__((nonnull, warn_unused_result))
1107
static bool delete_local_route(const char *address,
1108
AvahiIfIndex if_index){
1110
fprintf_plus(stderr, "Removing route to %s\n", address);
1112
return add_delete_local_route(false, address, if_index);
1115
/* Called when a Mandos server is found */
1116
__attribute__((nonnull, warn_unused_result))
1117
static int start_mandos_communication(const char *ip, in_port_t port,
1118
AvahiIfIndex if_index,
1119
int af, mandos_context *mc){
1120
int ret, tcp_sd = -1;
1122
struct sockaddr_storage to;
1123
char *buffer = NULL;
1124
char *decrypted_buffer = NULL;
1125
size_t buffer_length = 0;
1126
size_t buffer_capacity = 0;
1129
gnutls_session_t session;
1130
int pf; /* Protocol family */
1131
bool route_added = false;
1148
fprintf_plus(stderr, "Bad address family: %d\n", af);
1153
/* If the interface is specified and we have a list of interfaces */
1154
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1155
/* Check if the interface is one of the interfaces we are using */
1158
char *interface = NULL;
1159
while((interface = argz_next(mc->interfaces,
1160
mc->interfaces_size,
1162
if(if_nametoindex(interface) == (unsigned int)if_index){
1169
/* This interface does not match any in the list, so we don't
1170
connect to the server */
1172
char interface[IF_NAMESIZE];
1173
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1174
perror_plus("if_indextoname");
1176
fprintf_plus(stderr, "Skipping server on non-used interface"
1178
if_indextoname((unsigned int)if_index,
1186
ret = init_gnutls_session(&session, mc);
1192
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1193
PRIuMAX "\n", ip, (uintmax_t)port);
1196
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1199
perror_plus("socket");
1210
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1211
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1212
ret = inet_pton(af, ip, &to6->sin6_addr);
1214
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1215
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1216
ret = inet_pton(af, ip, &to4->sin_addr);
1220
perror_plus("inet_pton");
1226
fprintf_plus(stderr, "Bad address: %s\n", ip);
1231
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1232
if(IN6_IS_ADDR_LINKLOCAL
1233
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1234
if(if_index == AVAHI_IF_UNSPEC){
1235
fprintf_plus(stderr, "An IPv6 link-local address is"
1236
" incomplete without a network interface\n");
1240
/* Set the network interface number as scope */
1241
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1244
((struct sockaddr_in *)&to)->sin_port = htons(port);
1253
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1254
char interface[IF_NAMESIZE];
1255
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1256
perror_plus("if_indextoname");
1258
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1259
"\n", ip, interface, (uintmax_t)port);
1262
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1263
ip, (uintmax_t)port);
1265
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1266
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1268
ret = getnameinfo((struct sockaddr *)&to,
1269
sizeof(struct sockaddr_in6),
1270
addrstr, sizeof(addrstr), NULL, 0,
1273
ret = getnameinfo((struct sockaddr *)&to,
1274
sizeof(struct sockaddr_in),
1275
addrstr, sizeof(addrstr), NULL, 0,
1278
if(ret == EAI_SYSTEM){
1279
perror_plus("getnameinfo");
1280
} else if(ret != 0) {
1281
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1282
} else if(strcmp(addrstr, ip) != 0){
1283
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1294
ret = connect(tcp_sd, (struct sockaddr *)&to,
1295
sizeof(struct sockaddr_in6));
1297
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1298
sizeof(struct sockaddr_in));
1301
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1302
and if_index != AVAHI_IF_UNSPEC
1303
and connect_to == NULL
1304
and not route_added and
1305
((af == AF_INET6 and not
1306
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1308
or (af == AF_INET and
1309
/* Not a a IPv4LL address */
1310
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1311
& 0xFFFF0000L) != 0xA9FE0000L))){
1312
/* Work around Avahi bug - Avahi does not announce link-local
1313
addresses if it has a global address, so local hosts with
1314
*only* a link-local address (e.g. Mandos clients) cannot
1315
connect to a Mandos server announced by Avahi on a server
1316
host with a global address. Work around this by retrying
1317
with an explicit route added with the server's address.
1319
Avahi bug reference:
1320
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1321
https://bugs.debian.org/587961
1324
fprintf_plus(stderr, "Mandos server unreachable, trying"
1328
route_added = add_local_route(ip, if_index);
1334
if(errno != ECONNREFUSED or debug){
1336
perror_plus("connect");
1349
const char *out = mandos_protocol_version;
1352
size_t out_size = strlen(out);
1353
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1354
out_size - written));
1357
perror_plus("write");
1361
written += (size_t)ret;
1362
if(written < out_size){
1365
if(out == mandos_protocol_version){
1380
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1388
/* This casting via intptr_t is to eliminate warning about casting
1389
an int to a pointer type. This is exactly how the GnuTLS Guile
1390
function "set-session-transport-fd!" does it. */
1391
gnutls_transport_set_ptr(session,
1392
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1400
ret = gnutls_handshake(session);
1405
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1407
if(ret != GNUTLS_E_SUCCESS){
1409
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1416
/* Read OpenPGP packet that contains the wanted password */
1419
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1430
buffer_capacity = incbuffer(&buffer, buffer_length,
1432
if(buffer_capacity == 0){
1434
perror_plus("incbuffer");
1444
sret = gnutls_record_recv(session, buffer+buffer_length,
1451
case GNUTLS_E_INTERRUPTED:
1452
case GNUTLS_E_AGAIN:
1454
case GNUTLS_E_REHANDSHAKE:
1456
ret = gnutls_handshake(session);
1462
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1464
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
1472
fprintf_plus(stderr, "Unknown error while reading data from"
1473
" encrypted session with Mandos server\n");
1474
gnutls_bye(session, GNUTLS_SHUT_RDWR);
1479
buffer_length += (size_t) sret;
1484
fprintf_plus(stderr, "Closing TLS session\n");
1493
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1498
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1500
if(buffer_length > 0){
1501
ssize_t decrypted_buffer_size;
1502
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1503
&decrypted_buffer, mc);
1504
if(decrypted_buffer_size >= 0){
1508
while(written < (size_t) decrypted_buffer_size){
1514
ret = (int)fwrite(decrypted_buffer + written, 1,
1515
(size_t)decrypted_buffer_size - written,
1517
if(ret == 0 and ferror(stdout)){
1520
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1526
written += (size_t)ret;
1528
ret = fflush(stdout);
1532
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1542
/* Shutdown procedure */
1547
if(not delete_local_route(ip, if_index)){
1548
fprintf_plus(stderr, "Failed to delete local route to %s on"
1549
" interface %d", ip, if_index);
1553
free(decrypted_buffer);
1556
ret = close(tcp_sd);
1562
perror_plus("close");
1564
gnutls_deinit(session);
1574
static void resolve_callback(AvahiSServiceResolver *r,
1575
AvahiIfIndex interface,
1576
AvahiProtocol proto,
1577
AvahiResolverEvent event,
1581
const char *host_name,
1582
const AvahiAddress *address,
1584
AVAHI_GCC_UNUSED AvahiStringList *txt,
1585
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1592
/* Called whenever a service has been resolved successfully or
1596
avahi_s_service_resolver_free(r);
1602
case AVAHI_RESOLVER_FAILURE:
1603
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1604
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1606
avahi_strerror(avahi_server_errno
1607
(((mandos_context*)mc)->server)));
1610
case AVAHI_RESOLVER_FOUND:
1612
char ip[AVAHI_ADDRESS_STR_MAX];
1613
avahi_address_snprint(ip, sizeof(ip), address);
1615
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1616
PRIdMAX ") on port %" PRIu16 "\n", name,
1617
host_name, ip, (intmax_t)interface, port);
1619
int ret = start_mandos_communication(ip, (in_port_t)port,
1621
avahi_proto_to_af(proto),
1624
avahi_simple_poll_quit(simple_poll);
1626
if(not add_server(ip, (in_port_t)port, interface,
1627
avahi_proto_to_af(proto),
1628
&((mandos_context*)mc)->current_server)){
1629
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1635
avahi_s_service_resolver_free(r);
1638
static void browse_callback(AvahiSServiceBrowser *b,
1639
AvahiIfIndex interface,
1640
AvahiProtocol protocol,
1641
AvahiBrowserEvent event,
1645
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1652
/* Called whenever a new services becomes available on the LAN or
1653
is removed from the LAN */
1661
case AVAHI_BROWSER_FAILURE:
1663
fprintf_plus(stderr, "(Avahi browser) %s\n",
1664
avahi_strerror(avahi_server_errno
1665
(((mandos_context*)mc)->server)));
1666
avahi_simple_poll_quit(simple_poll);
1669
case AVAHI_BROWSER_NEW:
1670
/* We ignore the returned Avahi resolver object. In the callback
1671
function we free it. If the Avahi server is terminated before
1672
the callback function is called the Avahi server will free the
1675
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1676
interface, protocol, name, type,
1677
domain, protocol, 0,
1678
resolve_callback, mc) == NULL)
1679
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1681
avahi_strerror(avahi_server_errno
1682
(((mandos_context*)mc)->server)));
1685
case AVAHI_BROWSER_REMOVE:
1688
case AVAHI_BROWSER_ALL_FOR_NOW:
1689
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1691
fprintf_plus(stderr, "No Mandos server found, still"
1698
/* Signal handler that stops main loop after SIGTERM */
1699
static void handle_sigterm(int sig){
1704
signal_received = sig;
1705
int old_errno = errno;
1706
/* set main loop to exit */
1707
if(simple_poll != NULL){
1708
avahi_simple_poll_quit(simple_poll);
1713
__attribute__((nonnull, warn_unused_result))
1714
bool get_flags(const char *ifname, struct ifreq *ifr){
1718
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1721
perror_plus("socket");
1725
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1726
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1727
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1731
perror_plus("ioctl SIOCGIFFLAGS");
1734
if((close(s) == -1) and debug){
1736
perror_plus("close");
1741
if((close(s) == -1) and debug){
1743
perror_plus("close");
1749
__attribute__((nonnull, warn_unused_result))
1750
bool good_flags(const char *ifname, const struct ifreq *ifr){
1752
/* Reject the loopback device */
1753
if(ifr->ifr_flags & IFF_LOOPBACK){
1755
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1760
/* Accept point-to-point devices only if connect_to is specified */
1761
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1763
fprintf_plus(stderr, "Accepting point-to-point interface"
1764
" \"%s\"\n", ifname);
1768
/* Otherwise, reject non-broadcast-capable devices */
1769
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1771
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1772
" \"%s\"\n", ifname);
1776
/* Reject non-ARP interfaces (including dummy interfaces) */
1777
if(ifr->ifr_flags & IFF_NOARP){
1779
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1785
/* Accept this device */
1787
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1793
* This function determines if a directory entry in /sys/class/net
1794
* corresponds to an acceptable network device.
1795
* (This function is passed to scandir(3) as a filter function.)
1797
__attribute__((nonnull, warn_unused_result))
1798
int good_interface(const struct dirent *if_entry){
1799
if(if_entry->d_name[0] == '.'){
1804
if(not get_flags(if_entry->d_name, &ifr)){
1806
fprintf_plus(stderr, "Failed to get flags for interface "
1807
"\"%s\"\n", if_entry->d_name);
1812
if(not good_flags(if_entry->d_name, &ifr)){
1819
* This function determines if a network interface is up.
1821
__attribute__((nonnull, warn_unused_result))
1822
bool interface_is_up(const char *interface){
1824
if(not get_flags(interface, &ifr)){
1826
fprintf_plus(stderr, "Failed to get flags for interface "
1827
"\"%s\"\n", interface);
1832
return (bool)(ifr.ifr_flags & IFF_UP);
1836
* This function determines if a network interface is running
1838
__attribute__((nonnull, warn_unused_result))
1839
bool interface_is_running(const char *interface){
1841
if(not get_flags(interface, &ifr)){
1843
fprintf_plus(stderr, "Failed to get flags for interface "
1844
"\"%s\"\n", interface);
1849
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1852
__attribute__((nonnull, pure, warn_unused_result))
1853
int notdotentries(const struct dirent *direntry){
1854
/* Skip "." and ".." */
1855
if(direntry->d_name[0] == '.'
1856
and (direntry->d_name[1] == '\0'
1857
or (direntry->d_name[1] == '.'
1858
and direntry->d_name[2] == '\0'))){
1864
/* Is this directory entry a runnable program? */
1865
__attribute__((nonnull, warn_unused_result))
1866
int runnable_hook(const struct dirent *direntry){
1871
if((direntry->d_name)[0] == '\0'){
1876
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1877
"abcdefghijklmnopqrstuvwxyz"
1880
if((direntry->d_name)[sret] != '\0'){
1881
/* Contains non-allowed characters */
1883
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1889
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1892
perror_plus("Could not stat hook");
1896
if(not (S_ISREG(st.st_mode))){
1897
/* Not a regular file */
1899
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1904
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1905
/* Not executable */
1907
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1913
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1919
__attribute__((nonnull, warn_unused_result))
1920
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1921
mandos_context *mc){
1923
struct timespec now;
1924
struct timespec waited_time;
1925
intmax_t block_time;
1928
if(mc->current_server == NULL){
1930
fprintf_plus(stderr, "Wait until first server is found."
1933
ret = avahi_simple_poll_iterate(s, -1);
1936
fprintf_plus(stderr, "Check current_server if we should run"
1939
/* the current time */
1940
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1942
perror_plus("clock_gettime");
1945
/* Calculating in ms how long time between now and server
1946
who we visted longest time ago. Now - last seen. */
1947
waited_time.tv_sec = (now.tv_sec
1948
- mc->current_server->last_seen.tv_sec);
1949
waited_time.tv_nsec = (now.tv_nsec
1950
- mc->current_server->last_seen.tv_nsec);
1951
/* total time is 10s/10,000ms.
1952
Converting to s from ms by dividing by 1,000,
1953
and ns to ms by dividing by 1,000,000. */
1954
block_time = ((retry_interval
1955
- ((intmax_t)waited_time.tv_sec * 1000))
1956
- ((intmax_t)waited_time.tv_nsec / 1000000));
1959
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1963
if(block_time <= 0){
1964
ret = start_mandos_communication(mc->current_server->ip,
1965
mc->current_server->port,
1966
mc->current_server->if_index,
1967
mc->current_server->af, mc);
1969
avahi_simple_poll_quit(s);
1972
ret = clock_gettime(CLOCK_MONOTONIC,
1973
&mc->current_server->last_seen);
1975
perror_plus("clock_gettime");
1978
mc->current_server = mc->current_server->next;
1979
block_time = 0; /* Call avahi to find new Mandos
1980
servers, but don't block */
1983
ret = avahi_simple_poll_iterate(s, (int)block_time);
1986
if(ret > 0 or errno != EINTR){
1987
return (ret != 1) ? ret : 0;
1993
__attribute__((nonnull))
1994
void run_network_hooks(const char *mode, const char *interface,
1996
struct dirent **direntries = NULL;
1997
if(hookdir_fd == -1){
1998
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
2000
if(hookdir_fd == -1){
2001
if(errno == ENOENT){
2003
fprintf_plus(stderr, "Network hook directory \"%s\" not"
2004
" found\n", hookdir);
2007
perror_plus("open");
2012
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2014
perror_plus("open(\"/dev/null\", O_RDONLY)");
2017
int numhooks = scandirat(hookdir_fd, ".", &direntries,
2018
runnable_hook, alphasort);
2020
perror_plus("scandir");
2024
struct dirent *direntry;
2026
for(int i = 0; i < numhooks; i++){
2027
direntry = direntries[i];
2029
fprintf_plus(stderr, "Running network hook \"%s\"\n",
2032
pid_t hook_pid = fork();
2035
/* Raise privileges */
2036
errno = raise_privileges_permanently();
2038
perror_plus("Failed to raise privileges");
2045
perror_plus("setgid");
2048
/* Reset supplementary groups */
2050
ret = setgroups(0, NULL);
2052
perror_plus("setgroups");
2055
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
2057
perror_plus("setenv");
2060
ret = setenv("DEVICE", interface, 1);
2062
perror_plus("setenv");
2065
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
2067
perror_plus("setenv");
2070
ret = setenv("MODE", mode, 1);
2072
perror_plus("setenv");
2076
ret = asprintf(&delaystring, "%f", (double)delay);
2078
perror_plus("asprintf");
2081
ret = setenv("DELAY", delaystring, 1);
2084
perror_plus("setenv");
2088
if(connect_to != NULL){
2089
ret = setenv("CONNECT", connect_to, 1);
2091
perror_plus("setenv");
2095
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2099
perror_plus("openat");
2100
_exit(EXIT_FAILURE);
2102
if(close(hookdir_fd) == -1){
2103
perror_plus("close");
2104
_exit(EXIT_FAILURE);
2106
ret = dup2(devnull, STDIN_FILENO);
2108
perror_plus("dup2(devnull, STDIN_FILENO)");
2111
ret = close(devnull);
2113
perror_plus("close");
2116
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2118
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2121
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2123
perror_plus("fexecve");
2124
_exit(EXIT_FAILURE);
2128
perror_plus("fork");
2133
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2134
perror_plus("waitpid");
2138
if(WIFEXITED(status)){
2139
if(WEXITSTATUS(status) != 0){
2140
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2141
" with status %d\n", direntry->d_name,
2142
WEXITSTATUS(status));
2146
} else if(WIFSIGNALED(status)){
2147
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2148
" signal %d\n", direntry->d_name,
2153
fprintf_plus(stderr, "Warning: network hook \"%s\""
2154
" crashed\n", direntry->d_name);
2160
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2166
if(close(hookdir_fd) == -1){
2167
perror_plus("close");
2174
__attribute__((nonnull, warn_unused_result))
2175
int bring_up_interface(const char *const interface,
2177
int old_errno = errno;
2179
struct ifreq network;
2180
unsigned int if_index = if_nametoindex(interface);
2182
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2192
if(not interface_is_up(interface)){
2194
int ioctl_errno = 0;
2195
if(not get_flags(interface, &network)){
2197
fprintf_plus(stderr, "Failed to get flags for interface "
2198
"\"%s\"\n", interface);
2202
network.ifr_flags |= IFF_UP; /* set flag */
2204
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2207
perror_plus("socket");
2215
perror_plus("close");
2222
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2226
/* Raise privileges */
2227
ret_errno = raise_privileges();
2230
perror_plus("Failed to raise privileges");
2235
bool restore_loglevel = false;
2237
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2238
messages about the network interface to mess up the prompt */
2239
ret_linux = klogctl(8, NULL, 5);
2240
if(ret_linux == -1){
2241
perror_plus("klogctl");
2243
restore_loglevel = true;
2246
#endif /* __linux__ */
2247
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2248
ioctl_errno = errno;
2250
if(restore_loglevel){
2251
ret_linux = klogctl(7, NULL, 0);
2252
if(ret_linux == -1){
2253
perror_plus("klogctl");
2256
#endif /* __linux__ */
2258
/* If raise_privileges() succeeded above */
2260
/* Lower privileges */
2261
ret_errno = lower_privileges();
2264
perror_plus("Failed to lower privileges");
2268
/* Close the socket */
2271
perror_plus("close");
2274
if(ret_setflags == -1){
2275
errno = ioctl_errno;
2276
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2281
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2285
/* Sleep checking until interface is running.
2286
Check every 0.25s, up to total time of delay */
2287
for(int i = 0; i < delay * 4; i++){
2288
if(interface_is_running(interface)){
2291
struct timespec sleeptime = { .tv_nsec = 250000000 };
2292
ret = nanosleep(&sleeptime, NULL);
2293
if(ret == -1 and errno != EINTR){
2294
perror_plus("nanosleep");
2302
__attribute__((nonnull, warn_unused_result))
2303
int take_down_interface(const char *const interface){
2304
int old_errno = errno;
2305
struct ifreq network;
2306
unsigned int if_index = if_nametoindex(interface);
2308
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2312
if(interface_is_up(interface)){
2314
int ioctl_errno = 0;
2315
if(not get_flags(interface, &network) and debug){
2317
fprintf_plus(stderr, "Failed to get flags for interface "
2318
"\"%s\"\n", interface);
2322
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2324
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2327
perror_plus("socket");
2333
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2337
/* Raise privileges */
2338
ret_errno = raise_privileges();
2341
perror_plus("Failed to raise privileges");
2344
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2345
ioctl_errno = errno;
2347
/* If raise_privileges() succeeded above */
2349
/* Lower privileges */
2350
ret_errno = lower_privileges();
2353
perror_plus("Failed to lower privileges");
2357
/* Close the socket */
2358
int ret = close(sd);
2360
perror_plus("close");
2363
if(ret_setflags == -1){
2364
errno = ioctl_errno;
2365
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2370
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2378
int main(int argc, char *argv[]){
2379
mandos_context mc = { .server = NULL, .dh_bits = 0,
2380
.priority = "SECURE256:!CTYPE-X.509"
2381
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2382
.current_server = NULL, .interfaces = NULL,
2383
.interfaces_size = 0 };
2384
AvahiSServiceBrowser *sb = NULL;
2389
int exitcode = EXIT_SUCCESS;
2390
char *interfaces_to_take_down = NULL;
2391
size_t interfaces_to_take_down_size = 0;
2392
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2393
char old_tempdir[] = "/tmp/mandosXXXXXX";
2394
char *tempdir = NULL;
2395
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2396
const char *seckey = PATHDIR "/" SECKEY;
2397
const char *pubkey = PATHDIR "/" PUBKEY;
2398
const char *dh_params_file = NULL;
2399
char *interfaces_hooks = NULL;
2401
bool gnutls_initialized = false;
2402
bool gpgme_initialized = false;
2404
double retry_interval = 10; /* 10s between trying a server and
2405
retrying the same server again */
2407
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2408
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2413
/* Lower any group privileges we might have, just to be safe */
2417
perror_plus("setgid");
2420
/* Lower user privileges (temporarily) */
2424
perror_plus("seteuid");
2432
struct argp_option options[] = {
2433
{ .name = "debug", .key = 128,
2434
.doc = "Debug mode", .group = 3 },
2435
{ .name = "connect", .key = 'c',
2436
.arg = "ADDRESS:PORT",
2437
.doc = "Connect directly to a specific Mandos server",
2439
{ .name = "interface", .key = 'i',
2441
.doc = "Network interface that will be used to search for"
2444
{ .name = "seckey", .key = 's',
2446
.doc = "OpenPGP secret key file base name",
2448
{ .name = "pubkey", .key = 'p',
2450
.doc = "OpenPGP public key file base name",
2452
{ .name = "dh-bits", .key = 129,
2454
.doc = "Bit length of the prime number used in the"
2455
" Diffie-Hellman key exchange",
2457
{ .name = "dh-params", .key = 134,
2459
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2460
" for the Diffie-Hellman key exchange",
2462
{ .name = "priority", .key = 130,
2464
.doc = "GnuTLS priority string for the TLS handshake",
2466
{ .name = "delay", .key = 131,
2468
.doc = "Maximum delay to wait for interface startup",
2470
{ .name = "retry", .key = 132,
2472
.doc = "Retry interval used when denied by the Mandos server",
2474
{ .name = "network-hook-dir", .key = 133,
2476
.doc = "Directory where network hooks are located",
2479
* These reproduce what we would get without ARGP_NO_HELP
2481
{ .name = "help", .key = '?',
2482
.doc = "Give this help list", .group = -1 },
2483
{ .name = "usage", .key = -3,
2484
.doc = "Give a short usage message", .group = -1 },
2485
{ .name = "version", .key = 'V',
2486
.doc = "Print program version", .group = -1 },
2490
error_t parse_opt(int key, char *arg,
2491
struct argp_state *state){
2494
case 128: /* --debug */
2497
case 'c': /* --connect */
2500
case 'i': /* --interface */
2501
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2504
argp_error(state, "%s", strerror(ret_errno));
2507
case 's': /* --seckey */
2510
case 'p': /* --pubkey */
2513
case 129: /* --dh-bits */
2515
tmpmax = strtoimax(arg, &tmp, 10);
2516
if(errno != 0 or tmp == arg or *tmp != '\0'
2517
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2518
argp_error(state, "Bad number of DH bits");
2520
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2522
case 134: /* --dh-params */
2523
dh_params_file = arg;
2525
case 130: /* --priority */
2528
case 131: /* --delay */
2530
delay = strtof(arg, &tmp);
2531
if(errno != 0 or tmp == arg or *tmp != '\0'){
2532
argp_error(state, "Bad delay");
2534
case 132: /* --retry */
2536
retry_interval = strtod(arg, &tmp);
2537
if(errno != 0 or tmp == arg or *tmp != '\0'
2538
or (retry_interval * 1000) > INT_MAX
2539
or retry_interval < 0){
2540
argp_error(state, "Bad retry interval");
2543
case 133: /* --network-hook-dir */
2547
* These reproduce what we would get without ARGP_NO_HELP
2549
case '?': /* --help */
2550
argp_state_help(state, state->out_stream,
2551
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2552
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2553
case -3: /* --usage */
2554
argp_state_help(state, state->out_stream,
2555
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2556
case 'V': /* --version */
2557
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2558
exit(argp_err_exit_status);
2561
return ARGP_ERR_UNKNOWN;
2566
struct argp argp = { .options = options, .parser = parse_opt,
2568
.doc = "Mandos client -- Get and decrypt"
2569
" passwords from a Mandos server" };
2570
ret_errno = argp_parse(&argp, argc, argv,
2571
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2578
perror_plus("argp_parse");
2579
exitcode = EX_OSERR;
2582
exitcode = EX_USAGE;
2588
/* Work around Debian bug #633582:
2589
<https://bugs.debian.org/633582> */
2591
/* Re-raise privileges */
2592
ret = raise_privileges();
2595
perror_plus("Failed to raise privileges");
2599
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2600
int seckey_fd = open(seckey, O_RDONLY);
2601
if(seckey_fd == -1){
2602
perror_plus("open");
2604
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2606
perror_plus("fstat");
2608
if(S_ISREG(st.st_mode)
2609
and st.st_uid == 0 and st.st_gid == 0){
2610
ret = fchown(seckey_fd, uid, gid);
2612
perror_plus("fchown");
2620
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2621
int pubkey_fd = open(pubkey, O_RDONLY);
2622
if(pubkey_fd == -1){
2623
perror_plus("open");
2625
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2627
perror_plus("fstat");
2629
if(S_ISREG(st.st_mode)
2630
and st.st_uid == 0 and st.st_gid == 0){
2631
ret = fchown(pubkey_fd, uid, gid);
2633
perror_plus("fchown");
2641
if(dh_params_file != NULL
2642
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2643
int dhparams_fd = open(dh_params_file, O_RDONLY);
2644
if(dhparams_fd == -1){
2645
perror_plus("open");
2647
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2649
perror_plus("fstat");
2651
if(S_ISREG(st.st_mode)
2652
and st.st_uid == 0 and st.st_gid == 0){
2653
ret = fchown(dhparams_fd, uid, gid);
2655
perror_plus("fchown");
2663
/* Lower privileges */
2664
ret = lower_privileges();
2667
perror_plus("Failed to lower privileges");
2672
/* Remove invalid interface names (except "none") */
2674
char *interface = NULL;
2675
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2677
if(strcmp(interface, "none") != 0
2678
and if_nametoindex(interface) == 0){
2679
if(interface[0] != '\0'){
2680
fprintf_plus(stderr, "Not using nonexisting interface"
2681
" \"%s\"\n", interface);
2683
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2689
/* Run network hooks */
2691
if(mc.interfaces != NULL){
2692
interfaces_hooks = malloc(mc.interfaces_size);
2693
if(interfaces_hooks == NULL){
2694
perror_plus("malloc");
2697
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2698
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2700
run_network_hooks("start", interfaces_hooks != NULL ?
2701
interfaces_hooks : "", delay);
2705
avahi_set_log_function(empty_log);
2708
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2709
from the signal handler */
2710
/* Initialize the pseudo-RNG for Avahi */
2711
srand((unsigned int) time(NULL));
2712
simple_poll = avahi_simple_poll_new();
2713
if(simple_poll == NULL){
2714
fprintf_plus(stderr,
2715
"Avahi: Failed to create simple poll object.\n");
2716
exitcode = EX_UNAVAILABLE;
2720
sigemptyset(&sigterm_action.sa_mask);
2721
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2723
perror_plus("sigaddset");
2724
exitcode = EX_OSERR;
2727
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2729
perror_plus("sigaddset");
2730
exitcode = EX_OSERR;
2733
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2735
perror_plus("sigaddset");
2736
exitcode = EX_OSERR;
2739
/* Need to check if the handler is SIG_IGN before handling:
2740
| [[info:libc:Initial Signal Actions]] |
2741
| [[info:libc:Basic Signal Handling]] |
2743
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2745
perror_plus("sigaction");
2748
if(old_sigterm_action.sa_handler != SIG_IGN){
2749
ret = sigaction(SIGINT, &sigterm_action, NULL);
2751
perror_plus("sigaction");
2752
exitcode = EX_OSERR;
2756
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2758
perror_plus("sigaction");
2761
if(old_sigterm_action.sa_handler != SIG_IGN){
2762
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2764
perror_plus("sigaction");
2765
exitcode = EX_OSERR;
2769
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2771
perror_plus("sigaction");
2774
if(old_sigterm_action.sa_handler != SIG_IGN){
2775
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2777
perror_plus("sigaction");
2778
exitcode = EX_OSERR;
2783
/* If no interfaces were specified, make a list */
2784
if(mc.interfaces == NULL){
2785
struct dirent **direntries = NULL;
2786
/* Look for any good interfaces */
2787
ret = scandir(sys_class_net, &direntries, good_interface,
2790
/* Add all found interfaces to interfaces list */
2791
for(int i = 0; i < ret; ++i){
2792
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2793
direntries[i]->d_name);
2796
perror_plus("argz_add");
2797
free(direntries[i]);
2801
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2802
direntries[i]->d_name);
2804
free(direntries[i]);
2811
fprintf_plus(stderr, "Could not find a network interface\n");
2812
exitcode = EXIT_FAILURE;
2817
/* Bring up interfaces which are down, and remove any "none"s */
2819
char *interface = NULL;
2820
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2822
/* If interface name is "none", stop bringing up interfaces.
2823
Also remove all instances of "none" from the list */
2824
if(strcmp(interface, "none") == 0){
2825
argz_delete(&mc.interfaces, &mc.interfaces_size,
2828
while((interface = argz_next(mc.interfaces,
2829
mc.interfaces_size, interface))){
2830
if(strcmp(interface, "none") == 0){
2831
argz_delete(&mc.interfaces, &mc.interfaces_size,
2838
bool interface_was_up = interface_is_up(interface);
2839
errno = bring_up_interface(interface, delay);
2840
if(not interface_was_up){
2842
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2843
" %s\n", interface, strerror(errno));
2845
errno = argz_add(&interfaces_to_take_down,
2846
&interfaces_to_take_down_size,
2849
perror_plus("argz_add");
2854
if(debug and (interfaces_to_take_down == NULL)){
2855
fprintf_plus(stderr, "No interfaces were brought up\n");
2859
/* If we only got one interface, explicitly use only that one */
2860
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2862
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2865
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2872
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2874
fprintf_plus(stderr, "init_gnutls_global failed\n");
2875
exitcode = EX_UNAVAILABLE;
2878
gnutls_initialized = true;
2885
/* Try /run/tmp before /tmp */
2886
tempdir = mkdtemp(run_tempdir);
2887
if(tempdir == NULL and errno == ENOENT){
2889
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2890
run_tempdir, old_tempdir);
2892
tempdir = mkdtemp(old_tempdir);
2894
if(tempdir == NULL){
2895
perror_plus("mkdtemp");
2903
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2904
fprintf_plus(stderr, "init_gpgme failed\n");
2905
exitcode = EX_UNAVAILABLE;
2908
gpgme_initialized = true;
2915
if(connect_to != NULL){
2916
/* Connect directly, do not use Zeroconf */
2917
/* (Mainly meant for debugging) */
2918
char *address = strrchr(connect_to, ':');
2920
if(address == NULL){
2921
fprintf_plus(stderr, "No colon in address\n");
2922
exitcode = EX_USAGE;
2932
tmpmax = strtoimax(address+1, &tmp, 10);
2933
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2934
or tmpmax != (in_port_t)tmpmax){
2935
fprintf_plus(stderr, "Bad port number\n");
2936
exitcode = EX_USAGE;
2944
port = (in_port_t)tmpmax;
2946
/* Colon in address indicates IPv6 */
2948
if(strchr(connect_to, ':') != NULL){
2950
/* Accept [] around IPv6 address - see RFC 5952 */
2951
if(connect_to[0] == '[' and address[-1] == ']')
2959
address = connect_to;
2965
while(not quit_now){
2966
ret = start_mandos_communication(address, port, if_index, af,
2968
if(quit_now or ret == 0){
2972
fprintf_plus(stderr, "Retrying in %d seconds\n",
2973
(int)retry_interval);
2975
sleep((unsigned int)retry_interval);
2979
exitcode = EXIT_SUCCESS;
2990
AvahiServerConfig config;
2991
/* Do not publish any local Zeroconf records */
2992
avahi_server_config_init(&config);
2993
config.publish_hinfo = 0;
2994
config.publish_addresses = 0;
2995
config.publish_workstation = 0;
2996
config.publish_domain = 0;
2998
/* Allocate a new server */
2999
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3000
&config, NULL, NULL, &ret);
3002
/* Free the Avahi configuration data */
3003
avahi_server_config_free(&config);
3006
/* Check if creating the Avahi server object succeeded */
3007
if(mc.server == NULL){
3008
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3009
avahi_strerror(ret));
3010
exitcode = EX_UNAVAILABLE;
3018
/* Create the Avahi service browser */
3019
sb = avahi_s_service_browser_new(mc.server, if_index,
3020
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
3021
NULL, 0, browse_callback,
3024
fprintf_plus(stderr, "Failed to create service browser: %s\n",
3025
avahi_strerror(avahi_server_errno(mc.server)));
3026
exitcode = EX_UNAVAILABLE;
3034
/* Run the main loop */
3037
fprintf_plus(stderr, "Starting Avahi loop search\n");
3040
ret = avahi_loop_with_timeout(simple_poll,
3041
(int)(retry_interval * 1000), &mc);
3043
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
3044
(ret == 0) ? "successfully" : "with error");
3050
if(signal_received){
3051
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3052
argv[0], signal_received,
3053
strsignal(signal_received));
3055
fprintf_plus(stderr, "%s exiting\n", argv[0]);
3059
/* Cleanup things */
3060
free(mc.interfaces);
3063
avahi_s_service_browser_free(sb);
3065
if(mc.server != NULL)
3066
avahi_server_free(mc.server);
3068
if(simple_poll != NULL)
3069
avahi_simple_poll_free(simple_poll);
3071
if(gnutls_initialized){
3072
gnutls_certificate_free_credentials(mc.cred);
3073
gnutls_dh_params_deinit(mc.dh_params);
3076
if(gpgme_initialized){
3077
gpgme_release(mc.ctx);
3080
/* Cleans up the circular linked list of Mandos servers the client
3082
if(mc.current_server != NULL){
3083
mc.current_server->prev->next = NULL;
3084
while(mc.current_server != NULL){
3085
server *next = mc.current_server->next;
3087
#pragma GCC diagnostic push
3088
#pragma GCC diagnostic ignored "-Wcast-qual"
3090
free((char *)(mc.current_server->ip));
3092
#pragma GCC diagnostic pop
3094
free(mc.current_server);
3095
mc.current_server = next;
3099
/* Re-raise privileges */
3101
ret = raise_privileges();
3104
perror_plus("Failed to raise privileges");
3107
/* Run network hooks */
3108
run_network_hooks("stop", interfaces_hooks != NULL ?
3109
interfaces_hooks : "", delay);
3111
/* Take down the network interfaces which were brought up */
3113
char *interface = NULL;
3114
while((interface = argz_next(interfaces_to_take_down,
3115
interfaces_to_take_down_size,
3117
ret = take_down_interface(interface);
3120
perror_plus("Failed to take down interface");
3123
if(debug and (interfaces_to_take_down == NULL)){
3124
fprintf_plus(stderr, "No interfaces needed to be taken"
3130
ret = lower_privileges_permanently();
3133
perror_plus("Failed to lower privileges permanently");
3137
free(interfaces_to_take_down);
3138
free(interfaces_hooks);
3140
void clean_dir_at(int base, const char * const dirname,
3142
struct dirent **direntries = NULL;
3144
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3150
perror_plus("open");
3153
int numentries = scandirat(dir_fd, ".", &direntries,
3154
notdotentries, alphasort);
3155
if(numentries >= 0){
3156
for(int i = 0; i < numentries; i++){
3158
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3159
dirname, direntries[i]->d_name);
3161
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3163
if(errno == EISDIR){
3164
dret = unlinkat(dir_fd, direntries[i]->d_name,
3167
if((dret == -1) and (errno == ENOTEMPTY)
3168
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3169
== 0) and (level == 0)){
3170
/* Recurse only in this special case */
3171
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3174
if((dret == -1) and (errno != ENOENT)){
3175
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3176
direntries[i]->d_name, strerror(errno));
3179
free(direntries[i]);
3182
/* need to clean even if 0 because man page doesn't specify */
3184
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3185
if(dret == -1 and errno != ENOENT){
3186
perror_plus("rmdir");
3189
perror_plus("scandirat");
3194
/* Removes the GPGME temp directory and all files inside */
3195
if(tempdir != NULL){
3196
clean_dir_at(-1, tempdir, 0);
3200
sigemptyset(&old_sigterm_action.sa_mask);
3201
old_sigterm_action.sa_handler = SIG_DFL;
3202
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3203
&old_sigterm_action,
3206
perror_plus("sigaction");
3209
ret = raise(signal_received);
3210
} while(ret != 0 and errno == EINTR);
3212
perror_plus("raise");
3215
TEMP_FAILURE_RETRY(pause());