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-2019 Teddy Hogeborn
13
* Copyright © 2008-2019 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
#if GNUTLS_VERSION_NUMBER < 0x030600
127
#include <gnutls/openpgp.h>
128
/* gnutls_certificate_set_openpgp_key_file(),
129
GNUTLS_OPENPGP_FMT_BASE64 */
130
#elif GNUTLS_VERSION_NUMBER >= 0x030606
131
#include <gnutls/x509.h> /* gnutls_pkcs_encrypt_flags_t,
133
GNUTLS_PKCS_NULL_PASSWORD */
137
#include <gpgme.h> /* All GPGME types, constants and
140
GPGME_PROTOCOL_OpenPGP,
143
#define BUFFER_SIZE 256
145
#define PATHDIR "/conf/conf.d/mandos"
146
#define SECKEY "seckey.txt"
147
#define PUBKEY "pubkey.txt"
148
#define TLS_PRIVKEY "tls-privkey.pem"
149
#define TLS_PUBKEY "tls-pubkey.pem"
150
#define HOOKDIR "/lib/mandos/network-hooks.d"
153
static const char mandos_protocol_version[] = "1";
154
const char *argp_program_version = "mandos-client " VERSION;
155
const char *argp_program_bug_address = "<mandos@recompile.se>";
156
static const char sys_class_net[] = "/sys/class/net";
157
char *connect_to = NULL;
158
const char *hookdir = HOOKDIR;
163
/* Doubly linked list that need to be circularly linked when used */
164
typedef struct server{
167
AvahiIfIndex if_index;
169
struct timespec last_seen;
174
/* Used for passing in values through the Avahi callback functions */
177
gnutls_certificate_credentials_t cred;
178
unsigned int dh_bits;
179
gnutls_dh_params_t dh_params;
180
const char *priority;
182
server *current_server;
184
size_t interfaces_size;
187
/* global so signal handler can reach it*/
188
AvahiSimplePoll *simple_poll;
190
sig_atomic_t quit_now = 0;
191
int signal_received = 0;
193
/* Function to use when printing errors */
194
void perror_plus(const char *print_text){
196
fprintf(stderr, "Mandos plugin %s: ",
197
program_invocation_short_name);
202
__attribute__((format (gnu_printf, 2, 3), nonnull))
203
int fprintf_plus(FILE *stream, const char *format, ...){
205
va_start (ap, format);
207
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
208
program_invocation_short_name));
209
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
213
* Make additional room in "buffer" for at least BUFFER_SIZE more
214
* bytes. "buffer_capacity" is how much is currently allocated,
215
* "buffer_length" is how much is already used.
217
__attribute__((nonnull, warn_unused_result))
218
size_t incbuffer(char **buffer, size_t buffer_length,
219
size_t buffer_capacity){
220
if(buffer_length + BUFFER_SIZE > buffer_capacity){
221
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
223
int old_errno = errno;
230
buffer_capacity += BUFFER_SIZE;
232
return buffer_capacity;
235
/* Add server to set of servers to retry periodically */
236
__attribute__((nonnull, warn_unused_result))
237
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
238
int af, server **current_server){
240
server *new_server = malloc(sizeof(server));
241
if(new_server == NULL){
242
perror_plus("malloc");
245
*new_server = (server){ .ip = strdup(ip),
247
.if_index = if_index,
249
if(new_server->ip == NULL){
250
perror_plus("strdup");
254
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
256
perror_plus("clock_gettime");
258
#pragma GCC diagnostic push
259
#pragma GCC diagnostic ignored "-Wcast-qual"
261
free((char *)(new_server->ip));
263
#pragma GCC diagnostic pop
268
/* Special case of first server */
269
if(*current_server == NULL){
270
new_server->next = new_server;
271
new_server->prev = new_server;
272
*current_server = new_server;
274
/* Place the new server last in the list */
275
new_server->next = *current_server;
276
new_server->prev = (*current_server)->prev;
277
new_server->prev->next = new_server;
278
(*current_server)->prev = new_server;
283
/* Set effective uid to 0, return errno */
284
__attribute__((warn_unused_result))
285
int raise_privileges(void){
286
int old_errno = errno;
288
if(seteuid(0) == -1){
295
/* Set effective and real user ID to 0. Return errno. */
296
__attribute__((warn_unused_result))
297
int raise_privileges_permanently(void){
298
int old_errno = errno;
299
int ret = raise_privileges();
311
/* Set effective user ID to unprivileged saved user ID */
312
__attribute__((warn_unused_result))
313
int lower_privileges(void){
314
int old_errno = errno;
316
if(seteuid(uid) == -1){
323
/* Lower privileges permanently */
324
__attribute__((warn_unused_result))
325
int lower_privileges_permanently(void){
326
int old_errno = errno;
328
if(setuid(uid) == -1){
338
__attribute__((nonnull, warn_unused_result))
339
static bool init_gpgme(const char * const seckey,
340
const char * const pubkey,
341
const char * const tempdir,
344
gpgme_engine_info_t engine_info;
347
* Helper function to insert pub and seckey to the engine keyring.
349
bool import_key(const char * const filename){
352
gpgme_data_t pgp_data;
354
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
360
/* Workaround for systems without a real-time clock; see also
361
Debian bug #894495: <https://bugs.debian.org/894495> */
364
time_t currtime = time(NULL);
365
if(currtime != (time_t)-1){
367
if(gmtime_r(&currtime, &tm) == NULL) {
368
perror_plus("gmtime_r");
371
if(tm.tm_year != 70 or tm.tm_mon != 0){
375
fprintf_plus(stderr, "System clock is January 1970");
379
fprintf_plus(stderr, "System clock is invalid");
384
ret = fstat(fd, &keystat);
386
perror_plus("fstat");
389
ret = raise_privileges();
392
perror_plus("Failed to raise privileges");
397
"Setting system clock to key file mtime");
399
time_t keytime = keystat.st_mtim.tv_sec;
400
if(stime(&keytime) != 0){
401
perror_plus("stime");
403
ret = lower_privileges();
406
perror_plus("Failed to lower privileges");
410
rc = gpgme_data_new_from_fd(&pgp_data, fd);
411
if(rc != GPG_ERR_NO_ERROR){
412
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
413
gpgme_strsource(rc), gpgme_strerror(rc));
417
rc = gpgme_op_import(mc->ctx, pgp_data);
418
if(rc != GPG_ERR_NO_ERROR){
419
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
420
gpgme_strsource(rc), gpgme_strerror(rc));
424
gpgme_import_result_t import_result
425
= gpgme_op_import_result(mc->ctx);
426
if((import_result->imported < 1
427
or import_result->not_imported > 0)
428
and import_result->unchanged == 0){
429
fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
431
"The total number of considered keys: %d\n",
432
import_result->considered);
434
"The number of keys without user ID: %d\n",
435
import_result->no_user_id);
437
"The total number of imported keys: %d\n",
438
import_result->imported);
439
fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
440
import_result->imported_rsa);
441
fprintf_plus(stderr, "The number of unchanged keys: %d\n",
442
import_result->unchanged);
443
fprintf_plus(stderr, "The number of new user IDs: %d\n",
444
import_result->new_user_ids);
445
fprintf_plus(stderr, "The number of new sub keys: %d\n",
446
import_result->new_sub_keys);
447
fprintf_plus(stderr, "The number of new signatures: %d\n",
448
import_result->new_signatures);
449
fprintf_plus(stderr, "The number of new revocations: %d\n",
450
import_result->new_revocations);
452
"The total number of secret keys read: %d\n",
453
import_result->secret_read);
455
"The number of imported secret keys: %d\n",
456
import_result->secret_imported);
458
"The number of unchanged secret keys: %d\n",
459
import_result->secret_unchanged);
460
fprintf_plus(stderr, "The number of keys not imported: %d\n",
461
import_result->not_imported);
462
for(gpgme_import_status_t import_status
463
= import_result->imports;
464
import_status != NULL;
465
import_status = import_status->next){
466
fprintf_plus(stderr, "Import status for key: %s\n",
468
if(import_status->result != GPG_ERR_NO_ERROR){
469
fprintf_plus(stderr, "Import result: %s: %s\n",
470
gpgme_strsource(import_status->result),
471
gpgme_strerror(import_status->result));
473
fprintf_plus(stderr, "Key status:\n");
475
import_status->status & GPGME_IMPORT_NEW
476
? "The key was new.\n"
477
: "The key was not new.\n");
479
import_status->status & GPGME_IMPORT_UID
480
? "The key contained new user IDs.\n"
481
: "The key did not contain new user IDs.\n");
483
import_status->status & GPGME_IMPORT_SIG
484
? "The key contained new signatures.\n"
485
: "The key did not contain new signatures.\n");
487
import_status->status & GPGME_IMPORT_SUBKEY
488
? "The key contained new sub keys.\n"
489
: "The key did not contain new sub keys.\n");
491
import_status->status & GPGME_IMPORT_SECRET
492
? "The key contained a secret key.\n"
493
: "The key did not contain a secret key.\n");
501
perror_plus("close");
503
gpgme_data_release(pgp_data);
508
fprintf_plus(stderr, "Initializing GPGME\n");
512
gpgme_check_version(NULL);
513
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
514
if(rc != GPG_ERR_NO_ERROR){
515
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
516
gpgme_strsource(rc), gpgme_strerror(rc));
520
/* Set GPGME home directory for the OpenPGP engine only */
521
rc = gpgme_get_engine_info(&engine_info);
522
if(rc != GPG_ERR_NO_ERROR){
523
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
524
gpgme_strsource(rc), gpgme_strerror(rc));
527
while(engine_info != NULL){
528
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
529
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
530
engine_info->file_name, tempdir);
533
engine_info = engine_info->next;
535
if(engine_info == NULL){
536
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
541
/* Create new GPGME "context" */
542
rc = gpgme_new(&(mc->ctx));
543
if(rc != GPG_ERR_NO_ERROR){
544
fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
545
gpgme_strsource(rc), gpgme_strerror(rc));
549
if(not import_key(pubkey) or not import_key(seckey)){
557
* Decrypt OpenPGP data.
558
* Returns -1 on error
560
__attribute__((nonnull, warn_unused_result))
561
static ssize_t pgp_packet_decrypt(const char *cryptotext,
565
gpgme_data_t dh_crypto, dh_plain;
568
size_t plaintext_capacity = 0;
569
ssize_t plaintext_length = 0;
572
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
575
/* Create new GPGME data buffer from memory cryptotext */
576
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
578
if(rc != GPG_ERR_NO_ERROR){
579
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
580
gpgme_strsource(rc), gpgme_strerror(rc));
584
/* Create new empty GPGME data buffer for the plaintext */
585
rc = gpgme_data_new(&dh_plain);
586
if(rc != GPG_ERR_NO_ERROR){
587
fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
588
gpgme_strsource(rc), gpgme_strerror(rc));
589
gpgme_data_release(dh_crypto);
593
/* Decrypt data from the cryptotext data buffer to the plaintext
595
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
596
if(rc != GPG_ERR_NO_ERROR){
597
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
598
gpgme_strsource(rc), gpgme_strerror(rc));
599
plaintext_length = -1;
601
gpgme_decrypt_result_t result;
602
result = gpgme_op_decrypt_result(mc->ctx);
604
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
606
if(result->unsupported_algorithm != NULL) {
607
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
608
result->unsupported_algorithm);
610
fprintf_plus(stderr, "Wrong key usage: %s\n",
611
result->wrong_key_usage ? "Yes" : "No");
612
if(result->file_name != NULL){
613
fprintf_plus(stderr, "File name: %s\n", result->file_name);
616
for(gpgme_recipient_t r = result->recipients; r != NULL;
618
fprintf_plus(stderr, "Public key algorithm: %s\n",
619
gpgme_pubkey_algo_name(r->pubkey_algo));
620
fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
621
fprintf_plus(stderr, "Secret key available: %s\n",
622
r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
630
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
633
/* Seek back to the beginning of the GPGME plaintext data buffer */
634
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
635
perror_plus("gpgme_data_seek");
636
plaintext_length = -1;
642
plaintext_capacity = incbuffer(plaintext,
643
(size_t)plaintext_length,
645
if(plaintext_capacity == 0){
646
perror_plus("incbuffer");
647
plaintext_length = -1;
651
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
653
/* Print the data, if any */
659
perror_plus("gpgme_data_read");
660
plaintext_length = -1;
663
plaintext_length += ret;
667
fprintf_plus(stderr, "Decrypted password is: ");
668
for(ssize_t i = 0; i < plaintext_length; i++){
669
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
671
fprintf(stderr, "\n");
676
/* Delete the GPGME cryptotext data buffer */
677
gpgme_data_release(dh_crypto);
679
/* Delete the GPGME plaintext data buffer */
680
gpgme_data_release(dh_plain);
681
return plaintext_length;
684
__attribute__((warn_unused_result, const))
685
static const char *safe_string(const char *str){
691
__attribute__((warn_unused_result))
692
static const char *safer_gnutls_strerror(int value){
693
const char *ret = gnutls_strerror(value);
694
return safe_string(ret);
697
/* GnuTLS log function callback */
698
__attribute__((nonnull))
699
static void debuggnutls(__attribute__((unused)) int level,
701
fprintf_plus(stderr, "GnuTLS: %s", string);
704
__attribute__((nonnull(1, 2, 4), warn_unused_result))
705
static int init_gnutls_global(const char *pubkeyfilename,
706
const char *seckeyfilename,
707
const char *dhparamsfilename,
712
fprintf_plus(stderr, "Initializing GnuTLS\n");
716
/* "Use a log level over 10 to enable all debugging options."
719
gnutls_global_set_log_level(11);
720
gnutls_global_set_log_function(debuggnutls);
723
/* OpenPGP credentials */
724
ret = gnutls_certificate_allocate_credentials(&mc->cred);
725
if(ret != GNUTLS_E_SUCCESS){
726
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
727
safer_gnutls_strerror(ret));
732
fprintf_plus(stderr, "Attempting to use public key %s and"
733
" private key %s as GnuTLS credentials\n",
738
#if GNUTLS_VERSION_NUMBER >= 0x030606
739
ret = gnutls_certificate_set_rawpk_key_file
740
(mc->cred, pubkeyfilename, seckeyfilename,
741
GNUTLS_X509_FMT_PEM, /* format */
744
GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
746
0, /* names_length */
748
GNUTLS_PKCS_PLAIN | GNUTLS_PKCS_NULL_PASSWORD,
749
0); /* pkcs11_flags */
750
#elif GNUTLS_VERSION_NUMBER < 0x030600
751
ret = gnutls_certificate_set_openpgp_key_file
752
(mc->cred, pubkeyfilename, seckeyfilename,
753
GNUTLS_OPENPGP_FMT_BASE64);
755
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
757
if(ret != GNUTLS_E_SUCCESS){
759
"Error[%d] while reading the key pair ('%s',"
760
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
761
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
762
safer_gnutls_strerror(ret));
766
/* GnuTLS server initialization */
767
ret = gnutls_dh_params_init(&mc->dh_params);
768
if(ret != GNUTLS_E_SUCCESS){
769
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
770
" initialization: %s\n",
771
safer_gnutls_strerror(ret));
774
/* If a Diffie-Hellman parameters file was given, try to use it */
775
if(dhparamsfilename != NULL){
776
gnutls_datum_t params = { .data = NULL, .size = 0 };
778
int dhpfile = open(dhparamsfilename, O_RDONLY);
781
dhparamsfilename = NULL;
784
size_t params_capacity = 0;
786
params_capacity = incbuffer((char **)¶ms.data,
788
(size_t)params_capacity);
789
if(params_capacity == 0){
790
perror_plus("incbuffer");
793
dhparamsfilename = NULL;
796
ssize_t bytes_read = read(dhpfile,
797
params.data + params.size,
803
/* check bytes_read for failure */
808
dhparamsfilename = NULL;
811
params.size += (unsigned int)bytes_read;
813
ret = close(dhpfile);
815
perror_plus("close");
817
if(params.data == NULL){
818
dhparamsfilename = NULL;
820
if(dhparamsfilename == NULL){
823
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
824
GNUTLS_X509_FMT_PEM);
825
if(ret != GNUTLS_E_SUCCESS){
826
fprintf_plus(stderr, "Failed to parse DH parameters in file"
827
" \"%s\": %s\n", dhparamsfilename,
828
safer_gnutls_strerror(ret));
829
dhparamsfilename = NULL;
834
if(dhparamsfilename == NULL){
835
if(mc->dh_bits == 0){
836
#if GNUTLS_VERSION_NUMBER < 0x030600
837
/* Find out the optimal number of DH bits */
838
/* Try to read the private key file */
839
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
841
int secfile = open(seckeyfilename, O_RDONLY);
846
size_t buffer_capacity = 0;
848
buffer_capacity = incbuffer((char **)&buffer.data,
850
(size_t)buffer_capacity);
851
if(buffer_capacity == 0){
852
perror_plus("incbuffer");
857
ssize_t bytes_read = read(secfile,
858
buffer.data + buffer.size,
864
/* check bytes_read for failure */
871
buffer.size += (unsigned int)bytes_read;
875
/* If successful, use buffer to parse private key */
876
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
877
if(buffer.data != NULL){
879
gnutls_openpgp_privkey_t privkey = NULL;
880
ret = gnutls_openpgp_privkey_init(&privkey);
881
if(ret != GNUTLS_E_SUCCESS){
882
fprintf_plus(stderr, "Error initializing OpenPGP key"
884
safer_gnutls_strerror(ret));
888
ret = gnutls_openpgp_privkey_import
889
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
890
if(ret != GNUTLS_E_SUCCESS){
891
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
892
safer_gnutls_strerror(ret));
898
/* Use private key to suggest an appropriate
900
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
901
gnutls_openpgp_privkey_deinit(privkey);
903
fprintf_plus(stderr, "This OpenPGP key implies using"
904
" a GnuTLS security parameter \"%s\".\n",
905
safe_string(gnutls_sec_param_get_name
911
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
912
/* Err on the side of caution */
913
sec_param = GNUTLS_SEC_PARAM_ULTRA;
915
fprintf_plus(stderr, "Falling back to security parameter"
917
safe_string(gnutls_sec_param_get_name
922
unsigned int uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
926
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
927
" implies %u DH bits; using that.\n",
928
safe_string(gnutls_sec_param_get_name
933
fprintf_plus(stderr, "Failed to get implied number of DH"
934
" bits for security parameter \"%s\"): %s\n",
935
safe_string(gnutls_sec_param_get_name
937
safer_gnutls_strerror(ret));
941
} else { /* dh_bits != 0 */
943
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
946
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
947
if(ret != GNUTLS_E_SUCCESS){
948
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
949
" bits): %s\n", mc->dh_bits,
950
safer_gnutls_strerror(ret));
953
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
961
gnutls_certificate_free_credentials(mc->cred);
962
gnutls_dh_params_deinit(mc->dh_params);
966
__attribute__((nonnull, warn_unused_result))
967
static int init_gnutls_session(gnutls_session_t *session,
970
/* GnuTLS session creation */
972
ret = gnutls_init(session, (GNUTLS_SERVER
973
#if GNUTLS_VERSION_NUMBER >= 0x030506
976
#if GNUTLS_VERSION_NUMBER >= 0x030606
977
| GNUTLS_ENABLE_RAWPK
983
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
984
if(ret != GNUTLS_E_SUCCESS){
986
"Error in GnuTLS session initialization: %s\n",
987
safer_gnutls_strerror(ret));
993
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
995
gnutls_deinit(*session);
998
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
999
if(ret != GNUTLS_E_SUCCESS){
1000
fprintf_plus(stderr, "Syntax error at: %s\n", err);
1001
fprintf_plus(stderr, "GnuTLS error: %s\n",
1002
safer_gnutls_strerror(ret));
1003
gnutls_deinit(*session);
1009
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
1012
gnutls_deinit(*session);
1015
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
1016
if(ret != GNUTLS_E_SUCCESS){
1017
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
1018
safer_gnutls_strerror(ret));
1019
gnutls_deinit(*session);
1023
/* ignore client certificate if any. */
1024
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
1029
/* Avahi log function callback */
1030
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
1031
__attribute__((unused)) const char *txt){}
1033
/* Helper function to add_local_route() and delete_local_route() */
1034
__attribute__((nonnull, warn_unused_result))
1035
static bool add_delete_local_route(const bool add,
1036
const char *address,
1037
AvahiIfIndex if_index){
1039
char helper[] = "mandos-client-iprouteadddel";
1040
char add_arg[] = "add";
1041
char delete_arg[] = "delete";
1042
char debug_flag[] = "--debug";
1043
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
1044
if(pluginhelperdir == NULL){
1046
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
1047
" variable not set; cannot run helper\n");
1052
char interface[IF_NAMESIZE];
1053
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1054
perror_plus("if_indextoname");
1058
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1060
perror_plus("open(\"/dev/null\", O_RDONLY)");
1066
/* Raise privileges */
1067
errno = raise_privileges_permanently();
1069
perror_plus("Failed to raise privileges");
1070
/* _exit(EX_NOPERM); */
1076
perror_plus("setgid");
1080
/* Reset supplementary groups */
1082
ret = setgroups(0, NULL);
1084
perror_plus("setgroups");
1089
ret = dup2(devnull, STDIN_FILENO);
1091
perror_plus("dup2(devnull, STDIN_FILENO)");
1095
ret = close(devnull);
1097
perror_plus("close");
1099
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1101
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1104
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
1109
if(helperdir_fd == -1){
1110
perror_plus("open");
1111
_exit(EX_UNAVAILABLE);
1113
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
1115
if(helper_fd == -1){
1116
perror_plus("openat");
1117
close(helperdir_fd);
1118
_exit(EX_UNAVAILABLE);
1120
close(helperdir_fd);
1122
#pragma GCC diagnostic push
1123
#pragma GCC diagnostic ignored "-Wcast-qual"
1125
if(fexecve(helper_fd, (char *const [])
1126
{ helper, add ? add_arg : delete_arg, (char *)address,
1127
interface, debug ? debug_flag : NULL, NULL },
1130
#pragma GCC diagnostic pop
1132
perror_plus("fexecve");
1133
_exit(EXIT_FAILURE);
1137
perror_plus("fork");
1141
ret = close(devnull);
1143
perror_plus("close");
1149
pret = waitpid(pid, &status, 0);
1150
if(pret == -1 and errno == EINTR and quit_now){
1151
int errno_raising = 0;
1152
if((errno = raise_privileges()) != 0){
1153
errno_raising = errno;
1154
perror_plus("Failed to raise privileges in order to"
1155
" kill helper program");
1157
if(kill(pid, SIGTERM) == -1){
1158
perror_plus("kill");
1160
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
1161
perror_plus("Failed to lower privileges after killing"
1166
} while(pret == -1 and errno == EINTR);
1168
perror_plus("waitpid");
1171
if(WIFEXITED(status)){
1172
if(WEXITSTATUS(status) != 0){
1173
fprintf_plus(stderr, "Error: iprouteadddel exited"
1174
" with status %d\n", WEXITSTATUS(status));
1179
if(WIFSIGNALED(status)){
1180
fprintf_plus(stderr, "Error: iprouteadddel died by"
1181
" signal %d\n", WTERMSIG(status));
1184
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1188
__attribute__((nonnull, warn_unused_result))
1189
static bool add_local_route(const char *address,
1190
AvahiIfIndex if_index){
1192
fprintf_plus(stderr, "Adding route to %s\n", address);
1194
return add_delete_local_route(true, address, if_index);
1197
__attribute__((nonnull, warn_unused_result))
1198
static bool delete_local_route(const char *address,
1199
AvahiIfIndex if_index){
1201
fprintf_plus(stderr, "Removing route to %s\n", address);
1203
return add_delete_local_route(false, address, if_index);
1206
/* Called when a Mandos server is found */
1207
__attribute__((nonnull, warn_unused_result))
1208
static int start_mandos_communication(const char *ip, in_port_t port,
1209
AvahiIfIndex if_index,
1210
int af, mandos_context *mc){
1211
int ret, tcp_sd = -1;
1213
struct sockaddr_storage to;
1214
char *buffer = NULL;
1215
char *decrypted_buffer = NULL;
1216
size_t buffer_length = 0;
1217
size_t buffer_capacity = 0;
1220
gnutls_session_t session;
1221
int pf; /* Protocol family */
1222
bool route_added = false;
1239
fprintf_plus(stderr, "Bad address family: %d\n", af);
1244
/* If the interface is specified and we have a list of interfaces */
1245
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1246
/* Check if the interface is one of the interfaces we are using */
1249
char *interface = NULL;
1250
while((interface = argz_next(mc->interfaces,
1251
mc->interfaces_size,
1253
if(if_nametoindex(interface) == (unsigned int)if_index){
1260
/* This interface does not match any in the list, so we don't
1261
connect to the server */
1263
char interface[IF_NAMESIZE];
1264
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1265
perror_plus("if_indextoname");
1267
fprintf_plus(stderr, "Skipping server on non-used interface"
1269
if_indextoname((unsigned int)if_index,
1277
ret = init_gnutls_session(&session, mc);
1283
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1284
PRIuMAX "\n", ip, (uintmax_t)port);
1287
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1290
perror_plus("socket");
1301
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1302
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1303
ret = inet_pton(af, ip, &to6->sin6_addr);
1305
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1306
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1307
ret = inet_pton(af, ip, &to4->sin_addr);
1311
perror_plus("inet_pton");
1317
fprintf_plus(stderr, "Bad address: %s\n", ip);
1322
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1323
if(IN6_IS_ADDR_LINKLOCAL
1324
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1325
if(if_index == AVAHI_IF_UNSPEC){
1326
fprintf_plus(stderr, "An IPv6 link-local address is"
1327
" incomplete without a network interface\n");
1331
/* Set the network interface number as scope */
1332
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1335
((struct sockaddr_in *)&to)->sin_port = htons(port);
1344
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1345
char interface[IF_NAMESIZE];
1346
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1347
perror_plus("if_indextoname");
1349
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1350
"\n", ip, interface, (uintmax_t)port);
1353
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1354
ip, (uintmax_t)port);
1356
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1357
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1359
ret = getnameinfo((struct sockaddr *)&to,
1360
sizeof(struct sockaddr_in6),
1361
addrstr, sizeof(addrstr), NULL, 0,
1364
ret = getnameinfo((struct sockaddr *)&to,
1365
sizeof(struct sockaddr_in),
1366
addrstr, sizeof(addrstr), NULL, 0,
1369
if(ret == EAI_SYSTEM){
1370
perror_plus("getnameinfo");
1371
} else if(ret != 0) {
1372
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1373
} else if(strcmp(addrstr, ip) != 0){
1374
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1385
ret = connect(tcp_sd, (struct sockaddr *)&to,
1386
sizeof(struct sockaddr_in6));
1388
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1389
sizeof(struct sockaddr_in));
1392
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1393
and if_index != AVAHI_IF_UNSPEC
1394
and connect_to == NULL
1395
and not route_added and
1396
((af == AF_INET6 and not
1397
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1399
or (af == AF_INET and
1400
/* Not a a IPv4LL address */
1401
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1402
& 0xFFFF0000L) != 0xA9FE0000L))){
1403
/* Work around Avahi bug - Avahi does not announce link-local
1404
addresses if it has a global address, so local hosts with
1405
*only* a link-local address (e.g. Mandos clients) cannot
1406
connect to a Mandos server announced by Avahi on a server
1407
host with a global address. Work around this by retrying
1408
with an explicit route added with the server's address.
1410
Avahi bug reference:
1411
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1412
https://bugs.debian.org/587961
1415
fprintf_plus(stderr, "Mandos server unreachable, trying"
1419
route_added = add_local_route(ip, if_index);
1425
if(errno != ECONNREFUSED or debug){
1427
perror_plus("connect");
1440
const char *out = mandos_protocol_version;
1443
size_t out_size = strlen(out);
1444
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1445
out_size - written));
1448
perror_plus("write");
1452
written += (size_t)ret;
1453
if(written < out_size){
1456
if(out == mandos_protocol_version){
1471
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1479
/* This casting via intptr_t is to eliminate warning about casting
1480
an int to a pointer type. This is exactly how the GnuTLS Guile
1481
function "set-session-transport-fd!" does it. */
1482
gnutls_transport_set_ptr(session,
1483
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1491
ret = gnutls_handshake(session);
1496
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1498
if(ret != GNUTLS_E_SUCCESS){
1500
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1507
/* Read OpenPGP packet that contains the wanted password */
1510
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1521
buffer_capacity = incbuffer(&buffer, buffer_length,
1523
if(buffer_capacity == 0){
1525
perror_plus("incbuffer");
1535
sret = gnutls_record_recv(session, buffer+buffer_length,
1542
case GNUTLS_E_INTERRUPTED:
1543
case GNUTLS_E_AGAIN:
1545
case GNUTLS_E_REHANDSHAKE:
1547
ret = gnutls_handshake(session);
1553
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1555
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
1563
fprintf_plus(stderr, "Unknown error while reading data from"
1564
" encrypted session with Mandos server\n");
1565
gnutls_bye(session, GNUTLS_SHUT_RDWR);
1570
buffer_length += (size_t) sret;
1575
fprintf_plus(stderr, "Closing TLS session\n");
1584
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1589
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1591
if(buffer_length > 0){
1592
ssize_t decrypted_buffer_size;
1593
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1594
&decrypted_buffer, mc);
1595
if(decrypted_buffer_size >= 0){
1599
while(written < (size_t) decrypted_buffer_size){
1605
ret = (int)fwrite(decrypted_buffer + written, 1,
1606
(size_t)decrypted_buffer_size - written,
1608
if(ret == 0 and ferror(stdout)){
1611
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1617
written += (size_t)ret;
1619
ret = fflush(stdout);
1623
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1633
/* Shutdown procedure */
1638
if(not delete_local_route(ip, if_index)){
1639
fprintf_plus(stderr, "Failed to delete local route to %s on"
1640
" interface %d", ip, if_index);
1644
free(decrypted_buffer);
1647
ret = close(tcp_sd);
1653
perror_plus("close");
1655
gnutls_deinit(session);
1665
static void resolve_callback(AvahiSServiceResolver *r,
1666
AvahiIfIndex interface,
1667
AvahiProtocol proto,
1668
AvahiResolverEvent event,
1672
const char *host_name,
1673
const AvahiAddress *address,
1675
AVAHI_GCC_UNUSED AvahiStringList *txt,
1676
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1683
/* Called whenever a service has been resolved successfully or
1687
avahi_s_service_resolver_free(r);
1693
case AVAHI_RESOLVER_FAILURE:
1694
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1695
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1697
avahi_strerror(avahi_server_errno
1698
(((mandos_context*)mc)->server)));
1701
case AVAHI_RESOLVER_FOUND:
1703
char ip[AVAHI_ADDRESS_STR_MAX];
1704
avahi_address_snprint(ip, sizeof(ip), address);
1706
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1707
PRIdMAX ") on port %" PRIu16 "\n", name,
1708
host_name, ip, (intmax_t)interface, port);
1710
int ret = start_mandos_communication(ip, (in_port_t)port,
1712
avahi_proto_to_af(proto),
1715
avahi_simple_poll_quit(simple_poll);
1717
if(not add_server(ip, (in_port_t)port, interface,
1718
avahi_proto_to_af(proto),
1719
&((mandos_context*)mc)->current_server)){
1720
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1726
avahi_s_service_resolver_free(r);
1729
static void browse_callback(AvahiSServiceBrowser *b,
1730
AvahiIfIndex interface,
1731
AvahiProtocol protocol,
1732
AvahiBrowserEvent event,
1736
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1743
/* Called whenever a new services becomes available on the LAN or
1744
is removed from the LAN */
1752
case AVAHI_BROWSER_FAILURE:
1754
fprintf_plus(stderr, "(Avahi browser) %s\n",
1755
avahi_strerror(avahi_server_errno
1756
(((mandos_context*)mc)->server)));
1757
avahi_simple_poll_quit(simple_poll);
1760
case AVAHI_BROWSER_NEW:
1761
/* We ignore the returned Avahi resolver object. In the callback
1762
function we free it. If the Avahi server is terminated before
1763
the callback function is called the Avahi server will free the
1766
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1767
interface, protocol, name, type,
1768
domain, protocol, 0,
1769
resolve_callback, mc) == NULL)
1770
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1772
avahi_strerror(avahi_server_errno
1773
(((mandos_context*)mc)->server)));
1776
case AVAHI_BROWSER_REMOVE:
1779
case AVAHI_BROWSER_ALL_FOR_NOW:
1780
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1782
fprintf_plus(stderr, "No Mandos server found, still"
1789
/* Signal handler that stops main loop after SIGTERM */
1790
static void handle_sigterm(int sig){
1795
signal_received = sig;
1796
int old_errno = errno;
1797
/* set main loop to exit */
1798
if(simple_poll != NULL){
1799
avahi_simple_poll_quit(simple_poll);
1804
__attribute__((nonnull, warn_unused_result))
1805
bool get_flags(const char *ifname, struct ifreq *ifr){
1809
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1812
perror_plus("socket");
1816
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1817
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1818
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1822
perror_plus("ioctl SIOCGIFFLAGS");
1825
if((close(s) == -1) and debug){
1827
perror_plus("close");
1832
if((close(s) == -1) and debug){
1834
perror_plus("close");
1840
__attribute__((nonnull, warn_unused_result))
1841
bool good_flags(const char *ifname, const struct ifreq *ifr){
1843
/* Reject the loopback device */
1844
if(ifr->ifr_flags & IFF_LOOPBACK){
1846
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1851
/* Accept point-to-point devices only if connect_to is specified */
1852
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1854
fprintf_plus(stderr, "Accepting point-to-point interface"
1855
" \"%s\"\n", ifname);
1859
/* Otherwise, reject non-broadcast-capable devices */
1860
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1862
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1863
" \"%s\"\n", ifname);
1867
/* Reject non-ARP interfaces (including dummy interfaces) */
1868
if(ifr->ifr_flags & IFF_NOARP){
1870
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1876
/* Accept this device */
1878
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1884
* This function determines if a directory entry in /sys/class/net
1885
* corresponds to an acceptable network device.
1886
* (This function is passed to scandir(3) as a filter function.)
1888
__attribute__((nonnull, warn_unused_result))
1889
int good_interface(const struct dirent *if_entry){
1890
if(if_entry->d_name[0] == '.'){
1895
if(not get_flags(if_entry->d_name, &ifr)){
1897
fprintf_plus(stderr, "Failed to get flags for interface "
1898
"\"%s\"\n", if_entry->d_name);
1903
if(not good_flags(if_entry->d_name, &ifr)){
1910
* This function determines if a network interface is up.
1912
__attribute__((nonnull, warn_unused_result))
1913
bool interface_is_up(const char *interface){
1915
if(not get_flags(interface, &ifr)){
1917
fprintf_plus(stderr, "Failed to get flags for interface "
1918
"\"%s\"\n", interface);
1923
return (bool)(ifr.ifr_flags & IFF_UP);
1927
* This function determines if a network interface is running
1929
__attribute__((nonnull, warn_unused_result))
1930
bool interface_is_running(const char *interface){
1932
if(not get_flags(interface, &ifr)){
1934
fprintf_plus(stderr, "Failed to get flags for interface "
1935
"\"%s\"\n", interface);
1940
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1943
__attribute__((nonnull, pure, warn_unused_result))
1944
int notdotentries(const struct dirent *direntry){
1945
/* Skip "." and ".." */
1946
if(direntry->d_name[0] == '.'
1947
and (direntry->d_name[1] == '\0'
1948
or (direntry->d_name[1] == '.'
1949
and direntry->d_name[2] == '\0'))){
1955
/* Is this directory entry a runnable program? */
1956
__attribute__((nonnull, warn_unused_result))
1957
int runnable_hook(const struct dirent *direntry){
1962
if((direntry->d_name)[0] == '\0'){
1967
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1968
"abcdefghijklmnopqrstuvwxyz"
1971
if((direntry->d_name)[sret] != '\0'){
1972
/* Contains non-allowed characters */
1974
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1980
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1983
perror_plus("Could not stat hook");
1987
if(not (S_ISREG(st.st_mode))){
1988
/* Not a regular file */
1990
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1995
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1996
/* Not executable */
1998
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
2004
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
2010
__attribute__((nonnull, warn_unused_result))
2011
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
2012
mandos_context *mc){
2014
struct timespec now;
2015
struct timespec waited_time;
2016
intmax_t block_time;
2019
if(mc->current_server == NULL){
2021
fprintf_plus(stderr, "Wait until first server is found."
2024
ret = avahi_simple_poll_iterate(s, -1);
2027
fprintf_plus(stderr, "Check current_server if we should run"
2030
/* the current time */
2031
ret = clock_gettime(CLOCK_MONOTONIC, &now);
2033
perror_plus("clock_gettime");
2036
/* Calculating in ms how long time between now and server
2037
who we visted longest time ago. Now - last seen. */
2038
waited_time.tv_sec = (now.tv_sec
2039
- mc->current_server->last_seen.tv_sec);
2040
waited_time.tv_nsec = (now.tv_nsec
2041
- mc->current_server->last_seen.tv_nsec);
2042
/* total time is 10s/10,000ms.
2043
Converting to s from ms by dividing by 1,000,
2044
and ns to ms by dividing by 1,000,000. */
2045
block_time = ((retry_interval
2046
- ((intmax_t)waited_time.tv_sec * 1000))
2047
- ((intmax_t)waited_time.tv_nsec / 1000000));
2050
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
2054
if(block_time <= 0){
2055
ret = start_mandos_communication(mc->current_server->ip,
2056
mc->current_server->port,
2057
mc->current_server->if_index,
2058
mc->current_server->af, mc);
2060
avahi_simple_poll_quit(s);
2063
ret = clock_gettime(CLOCK_MONOTONIC,
2064
&mc->current_server->last_seen);
2066
perror_plus("clock_gettime");
2069
mc->current_server = mc->current_server->next;
2070
block_time = 0; /* Call avahi to find new Mandos
2071
servers, but don't block */
2074
ret = avahi_simple_poll_iterate(s, (int)block_time);
2077
if(ret > 0 or errno != EINTR){
2078
return (ret != 1) ? ret : 0;
2084
__attribute__((nonnull))
2085
void run_network_hooks(const char *mode, const char *interface,
2087
struct dirent **direntries = NULL;
2088
if(hookdir_fd == -1){
2089
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
2091
if(hookdir_fd == -1){
2092
if(errno == ENOENT){
2094
fprintf_plus(stderr, "Network hook directory \"%s\" not"
2095
" found\n", hookdir);
2098
perror_plus("open");
2103
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2105
perror_plus("open(\"/dev/null\", O_RDONLY)");
2108
int numhooks = scandirat(hookdir_fd, ".", &direntries,
2109
runnable_hook, alphasort);
2111
perror_plus("scandir");
2115
struct dirent *direntry;
2117
for(int i = 0; i < numhooks; i++){
2118
direntry = direntries[i];
2120
fprintf_plus(stderr, "Running network hook \"%s\"\n",
2123
pid_t hook_pid = fork();
2126
/* Raise privileges */
2127
errno = raise_privileges_permanently();
2129
perror_plus("Failed to raise privileges");
2136
perror_plus("setgid");
2139
/* Reset supplementary groups */
2141
ret = setgroups(0, NULL);
2143
perror_plus("setgroups");
2146
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
2148
perror_plus("setenv");
2151
ret = setenv("DEVICE", interface, 1);
2153
perror_plus("setenv");
2156
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
2158
perror_plus("setenv");
2161
ret = setenv("MODE", mode, 1);
2163
perror_plus("setenv");
2167
ret = asprintf(&delaystring, "%f", (double)delay);
2169
perror_plus("asprintf");
2172
ret = setenv("DELAY", delaystring, 1);
2175
perror_plus("setenv");
2179
if(connect_to != NULL){
2180
ret = setenv("CONNECT", connect_to, 1);
2182
perror_plus("setenv");
2186
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2190
perror_plus("openat");
2191
_exit(EXIT_FAILURE);
2193
if(close(hookdir_fd) == -1){
2194
perror_plus("close");
2195
_exit(EXIT_FAILURE);
2197
ret = dup2(devnull, STDIN_FILENO);
2199
perror_plus("dup2(devnull, STDIN_FILENO)");
2202
ret = close(devnull);
2204
perror_plus("close");
2207
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2209
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2212
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2214
perror_plus("fexecve");
2215
_exit(EXIT_FAILURE);
2219
perror_plus("fork");
2224
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2225
perror_plus("waitpid");
2229
if(WIFEXITED(status)){
2230
if(WEXITSTATUS(status) != 0){
2231
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2232
" with status %d\n", direntry->d_name,
2233
WEXITSTATUS(status));
2237
} else if(WIFSIGNALED(status)){
2238
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2239
" signal %d\n", direntry->d_name,
2244
fprintf_plus(stderr, "Warning: network hook \"%s\""
2245
" crashed\n", direntry->d_name);
2251
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2257
if(close(hookdir_fd) == -1){
2258
perror_plus("close");
2265
__attribute__((nonnull, warn_unused_result))
2266
int bring_up_interface(const char *const interface,
2268
int old_errno = errno;
2270
struct ifreq network;
2271
unsigned int if_index = if_nametoindex(interface);
2273
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2283
if(not interface_is_up(interface)){
2285
int ioctl_errno = 0;
2286
if(not get_flags(interface, &network)){
2288
fprintf_plus(stderr, "Failed to get flags for interface "
2289
"\"%s\"\n", interface);
2293
network.ifr_flags |= IFF_UP; /* set flag */
2295
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2298
perror_plus("socket");
2306
perror_plus("close");
2313
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2317
/* Raise privileges */
2318
ret_errno = raise_privileges();
2321
perror_plus("Failed to raise privileges");
2326
bool restore_loglevel = false;
2328
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2329
messages about the network interface to mess up the prompt */
2330
ret_linux = klogctl(8, NULL, 5);
2331
if(ret_linux == -1){
2332
perror_plus("klogctl");
2334
restore_loglevel = true;
2337
#endif /* __linux__ */
2338
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2339
ioctl_errno = errno;
2341
if(restore_loglevel){
2342
ret_linux = klogctl(7, NULL, 0);
2343
if(ret_linux == -1){
2344
perror_plus("klogctl");
2347
#endif /* __linux__ */
2349
/* If raise_privileges() succeeded above */
2351
/* Lower privileges */
2352
ret_errno = lower_privileges();
2355
perror_plus("Failed to lower privileges");
2359
/* Close the socket */
2362
perror_plus("close");
2365
if(ret_setflags == -1){
2366
errno = ioctl_errno;
2367
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2372
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2376
/* Sleep checking until interface is running.
2377
Check every 0.25s, up to total time of delay */
2378
for(int i = 0; i < delay * 4; i++){
2379
if(interface_is_running(interface)){
2382
struct timespec sleeptime = { .tv_nsec = 250000000 };
2383
ret = nanosleep(&sleeptime, NULL);
2384
if(ret == -1 and errno != EINTR){
2385
perror_plus("nanosleep");
2393
__attribute__((nonnull, warn_unused_result))
2394
int take_down_interface(const char *const interface){
2395
int old_errno = errno;
2396
struct ifreq network;
2397
unsigned int if_index = if_nametoindex(interface);
2399
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2403
if(interface_is_up(interface)){
2405
int ioctl_errno = 0;
2406
if(not get_flags(interface, &network) and debug){
2408
fprintf_plus(stderr, "Failed to get flags for interface "
2409
"\"%s\"\n", interface);
2413
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2415
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2418
perror_plus("socket");
2424
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2428
/* Raise privileges */
2429
ret_errno = raise_privileges();
2432
perror_plus("Failed to raise privileges");
2435
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2436
ioctl_errno = errno;
2438
/* If raise_privileges() succeeded above */
2440
/* Lower privileges */
2441
ret_errno = lower_privileges();
2444
perror_plus("Failed to lower privileges");
2448
/* Close the socket */
2449
int ret = close(sd);
2451
perror_plus("close");
2454
if(ret_setflags == -1){
2455
errno = ioctl_errno;
2456
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2461
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2469
int main(int argc, char *argv[]){
2470
mandos_context mc = { .server = NULL, .dh_bits = 0,
2471
#if GNUTLS_VERSION_NUMBER >= 0x030606
2472
.priority = "SECURE128:!CTYPE-X.509"
2473
":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
2475
#elif GNUTLS_VERSION_NUMBER < 0x030600
2476
.priority = "SECURE256:!CTYPE-X.509"
2477
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2479
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
2481
.current_server = NULL, .interfaces = NULL,
2482
.interfaces_size = 0 };
2483
AvahiSServiceBrowser *sb = NULL;
2488
int exitcode = EXIT_SUCCESS;
2489
char *interfaces_to_take_down = NULL;
2490
size_t interfaces_to_take_down_size = 0;
2491
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2492
char old_tempdir[] = "/tmp/mandosXXXXXX";
2493
char *tempdir = NULL;
2494
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2495
const char *seckey = PATHDIR "/" SECKEY;
2496
const char *pubkey = PATHDIR "/" PUBKEY;
2497
#if GNUTLS_VERSION_NUMBER >= 0x030606
2498
const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
2499
const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
2501
const char *dh_params_file = NULL;
2502
char *interfaces_hooks = NULL;
2504
bool gnutls_initialized = false;
2505
bool gpgme_initialized = false;
2507
double retry_interval = 10; /* 10s between trying a server and
2508
retrying the same server again */
2510
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2511
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2516
/* Lower any group privileges we might have, just to be safe */
2520
perror_plus("setgid");
2523
/* Lower user privileges (temporarily) */
2527
perror_plus("seteuid");
2535
struct argp_option options[] = {
2536
{ .name = "debug", .key = 128,
2537
.doc = "Debug mode", .group = 3 },
2538
{ .name = "connect", .key = 'c',
2539
.arg = "ADDRESS:PORT",
2540
.doc = "Connect directly to a specific Mandos server",
2542
{ .name = "interface", .key = 'i',
2544
.doc = "Network interface that will be used to search for"
2547
{ .name = "seckey", .key = 's',
2549
.doc = "OpenPGP secret key file base name",
2551
{ .name = "pubkey", .key = 'p',
2553
.doc = "OpenPGP public key file base name",
2555
{ .name = "tls-privkey", .key = 't',
2557
#if GNUTLS_VERSION_NUMBER >= 0x030606
2558
.doc = "TLS private key file base name",
2560
.doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2563
{ .name = "tls-pubkey", .key = 'T',
2565
#if GNUTLS_VERSION_NUMBER >= 0x030606
2566
.doc = "TLS public key file base name",
2568
.doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2571
{ .name = "dh-bits", .key = 129,
2573
.doc = "Bit length of the prime number used in the"
2574
" Diffie-Hellman key exchange",
2576
{ .name = "dh-params", .key = 134,
2578
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2579
" for the Diffie-Hellman key exchange",
2581
{ .name = "priority", .key = 130,
2583
.doc = "GnuTLS priority string for the TLS handshake",
2585
{ .name = "delay", .key = 131,
2587
.doc = "Maximum delay to wait for interface startup",
2589
{ .name = "retry", .key = 132,
2591
.doc = "Retry interval used when denied by the Mandos server",
2593
{ .name = "network-hook-dir", .key = 133,
2595
.doc = "Directory where network hooks are located",
2598
* These reproduce what we would get without ARGP_NO_HELP
2600
{ .name = "help", .key = '?',
2601
.doc = "Give this help list", .group = -1 },
2602
{ .name = "usage", .key = -3,
2603
.doc = "Give a short usage message", .group = -1 },
2604
{ .name = "version", .key = 'V',
2605
.doc = "Print program version", .group = -1 },
2609
error_t parse_opt(int key, char *arg,
2610
struct argp_state *state){
2613
case 128: /* --debug */
2616
case 'c': /* --connect */
2619
case 'i': /* --interface */
2620
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2623
argp_error(state, "%s", strerror(ret_errno));
2626
case 's': /* --seckey */
2629
case 'p': /* --pubkey */
2632
case 't': /* --tls-privkey */
2633
#if GNUTLS_VERSION_NUMBER >= 0x030606
2637
case 'T': /* --tls-pubkey */
2638
#if GNUTLS_VERSION_NUMBER >= 0x030606
2642
case 129: /* --dh-bits */
2644
tmpmax = strtoimax(arg, &tmp, 10);
2645
if(errno != 0 or tmp == arg or *tmp != '\0'
2646
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2647
argp_error(state, "Bad number of DH bits");
2649
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2651
case 134: /* --dh-params */
2652
dh_params_file = arg;
2654
case 130: /* --priority */
2657
case 131: /* --delay */
2659
delay = strtof(arg, &tmp);
2660
if(errno != 0 or tmp == arg or *tmp != '\0'){
2661
argp_error(state, "Bad delay");
2663
case 132: /* --retry */
2665
retry_interval = strtod(arg, &tmp);
2666
if(errno != 0 or tmp == arg or *tmp != '\0'
2667
or (retry_interval * 1000) > INT_MAX
2668
or retry_interval < 0){
2669
argp_error(state, "Bad retry interval");
2672
case 133: /* --network-hook-dir */
2676
* These reproduce what we would get without ARGP_NO_HELP
2678
case '?': /* --help */
2679
argp_state_help(state, state->out_stream,
2680
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2681
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2682
__builtin_unreachable();
2683
case -3: /* --usage */
2684
argp_state_help(state, state->out_stream,
2685
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2686
__builtin_unreachable();
2687
case 'V': /* --version */
2688
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2689
exit(argp_err_exit_status);
2692
return ARGP_ERR_UNKNOWN;
2697
struct argp argp = { .options = options, .parser = parse_opt,
2699
.doc = "Mandos client -- Get and decrypt"
2700
" passwords from a Mandos server" };
2701
ret_errno = argp_parse(&argp, argc, argv,
2702
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2709
perror_plus("argp_parse");
2710
exitcode = EX_OSERR;
2713
exitcode = EX_USAGE;
2719
/* Work around Debian bug #633582:
2720
<https://bugs.debian.org/633582> */
2722
/* Re-raise privileges */
2723
ret = raise_privileges();
2726
perror_plus("Failed to raise privileges");
2730
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2731
int seckey_fd = open(seckey, O_RDONLY);
2732
if(seckey_fd == -1){
2733
perror_plus("open");
2735
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2737
perror_plus("fstat");
2739
if(S_ISREG(st.st_mode)
2740
and st.st_uid == 0 and st.st_gid == 0){
2741
ret = fchown(seckey_fd, uid, gid);
2743
perror_plus("fchown");
2751
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2752
int pubkey_fd = open(pubkey, O_RDONLY);
2753
if(pubkey_fd == -1){
2754
perror_plus("open");
2756
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2758
perror_plus("fstat");
2760
if(S_ISREG(st.st_mode)
2761
and st.st_uid == 0 and st.st_gid == 0){
2762
ret = fchown(pubkey_fd, uid, gid);
2764
perror_plus("fchown");
2772
if(dh_params_file != NULL
2773
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2774
int dhparams_fd = open(dh_params_file, O_RDONLY);
2775
if(dhparams_fd == -1){
2776
perror_plus("open");
2778
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2780
perror_plus("fstat");
2782
if(S_ISREG(st.st_mode)
2783
and st.st_uid == 0 and st.st_gid == 0){
2784
ret = fchown(dhparams_fd, uid, gid);
2786
perror_plus("fchown");
2794
/* Lower privileges */
2795
ret = lower_privileges();
2798
perror_plus("Failed to lower privileges");
2803
/* Remove invalid interface names (except "none") */
2805
char *interface = NULL;
2806
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2808
if(strcmp(interface, "none") != 0
2809
and if_nametoindex(interface) == 0){
2810
if(interface[0] != '\0'){
2811
fprintf_plus(stderr, "Not using nonexisting interface"
2812
" \"%s\"\n", interface);
2814
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2820
/* Run network hooks */
2822
if(mc.interfaces != NULL){
2823
interfaces_hooks = malloc(mc.interfaces_size);
2824
if(interfaces_hooks == NULL){
2825
perror_plus("malloc");
2828
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2829
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2831
run_network_hooks("start", interfaces_hooks != NULL ?
2832
interfaces_hooks : "", delay);
2836
avahi_set_log_function(empty_log);
2839
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2840
from the signal handler */
2841
/* Initialize the pseudo-RNG for Avahi */
2842
srand((unsigned int) time(NULL));
2843
simple_poll = avahi_simple_poll_new();
2844
if(simple_poll == NULL){
2845
fprintf_plus(stderr,
2846
"Avahi: Failed to create simple poll object.\n");
2847
exitcode = EX_UNAVAILABLE;
2851
sigemptyset(&sigterm_action.sa_mask);
2852
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2854
perror_plus("sigaddset");
2855
exitcode = EX_OSERR;
2858
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2860
perror_plus("sigaddset");
2861
exitcode = EX_OSERR;
2864
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2866
perror_plus("sigaddset");
2867
exitcode = EX_OSERR;
2870
/* Need to check if the handler is SIG_IGN before handling:
2871
| [[info:libc:Initial Signal Actions]] |
2872
| [[info:libc:Basic Signal Handling]] |
2874
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2876
perror_plus("sigaction");
2879
if(old_sigterm_action.sa_handler != SIG_IGN){
2880
ret = sigaction(SIGINT, &sigterm_action, NULL);
2882
perror_plus("sigaction");
2883
exitcode = EX_OSERR;
2887
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2889
perror_plus("sigaction");
2892
if(old_sigterm_action.sa_handler != SIG_IGN){
2893
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2895
perror_plus("sigaction");
2896
exitcode = EX_OSERR;
2900
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2902
perror_plus("sigaction");
2905
if(old_sigterm_action.sa_handler != SIG_IGN){
2906
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2908
perror_plus("sigaction");
2909
exitcode = EX_OSERR;
2914
/* If no interfaces were specified, make a list */
2915
if(mc.interfaces == NULL){
2916
struct dirent **direntries = NULL;
2917
/* Look for any good interfaces */
2918
ret = scandir(sys_class_net, &direntries, good_interface,
2921
/* Add all found interfaces to interfaces list */
2922
for(int i = 0; i < ret; ++i){
2923
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2924
direntries[i]->d_name);
2927
perror_plus("argz_add");
2928
free(direntries[i]);
2932
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2933
direntries[i]->d_name);
2935
free(direntries[i]);
2942
fprintf_plus(stderr, "Could not find a network interface\n");
2943
exitcode = EXIT_FAILURE;
2948
/* Bring up interfaces which are down, and remove any "none"s */
2950
char *interface = NULL;
2951
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2953
/* If interface name is "none", stop bringing up interfaces.
2954
Also remove all instances of "none" from the list */
2955
if(strcmp(interface, "none") == 0){
2956
argz_delete(&mc.interfaces, &mc.interfaces_size,
2959
while((interface = argz_next(mc.interfaces,
2960
mc.interfaces_size, interface))){
2961
if(strcmp(interface, "none") == 0){
2962
argz_delete(&mc.interfaces, &mc.interfaces_size,
2969
bool interface_was_up = interface_is_up(interface);
2970
errno = bring_up_interface(interface, delay);
2971
if(not interface_was_up){
2973
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2974
" %s\n", interface, strerror(errno));
2976
errno = argz_add(&interfaces_to_take_down,
2977
&interfaces_to_take_down_size,
2980
perror_plus("argz_add");
2985
if(debug and (interfaces_to_take_down == NULL)){
2986
fprintf_plus(stderr, "No interfaces were brought up\n");
2990
/* If we only got one interface, explicitly use only that one */
2991
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2993
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2996
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
3003
#if GNUTLS_VERSION_NUMBER >= 0x030606
3004
ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
3005
#elif GNUTLS_VERSION_NUMBER < 0x030600
3006
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
3008
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
3011
fprintf_plus(stderr, "init_gnutls_global failed\n");
3012
exitcode = EX_UNAVAILABLE;
3015
gnutls_initialized = true;
3022
/* Try /run/tmp before /tmp */
3023
tempdir = mkdtemp(run_tempdir);
3024
if(tempdir == NULL and errno == ENOENT){
3026
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
3027
run_tempdir, old_tempdir);
3029
tempdir = mkdtemp(old_tempdir);
3031
if(tempdir == NULL){
3032
perror_plus("mkdtemp");
3040
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
3041
fprintf_plus(stderr, "init_gpgme failed\n");
3042
exitcode = EX_UNAVAILABLE;
3045
gpgme_initialized = true;
3052
if(connect_to != NULL){
3053
/* Connect directly, do not use Zeroconf */
3054
/* (Mainly meant for debugging) */
3055
char *address = strrchr(connect_to, ':');
3057
if(address == NULL){
3058
fprintf_plus(stderr, "No colon in address\n");
3059
exitcode = EX_USAGE;
3069
tmpmax = strtoimax(address+1, &tmp, 10);
3070
if(errno != 0 or tmp == address+1 or *tmp != '\0'
3071
or tmpmax != (in_port_t)tmpmax){
3072
fprintf_plus(stderr, "Bad port number\n");
3073
exitcode = EX_USAGE;
3081
port = (in_port_t)tmpmax;
3083
/* Colon in address indicates IPv6 */
3085
if(strchr(connect_to, ':') != NULL){
3087
/* Accept [] around IPv6 address - see RFC 5952 */
3088
if(connect_to[0] == '[' and address[-1] == ']')
3096
address = connect_to;
3102
while(not quit_now){
3103
ret = start_mandos_communication(address, port, if_index, af,
3105
if(quit_now or ret == 0){
3109
fprintf_plus(stderr, "Retrying in %d seconds\n",
3110
(int)retry_interval);
3112
sleep((unsigned int)retry_interval);
3116
exitcode = EXIT_SUCCESS;
3127
AvahiServerConfig config;
3128
/* Do not publish any local Zeroconf records */
3129
avahi_server_config_init(&config);
3130
config.publish_hinfo = 0;
3131
config.publish_addresses = 0;
3132
config.publish_workstation = 0;
3133
config.publish_domain = 0;
3135
/* Allocate a new server */
3136
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3137
&config, NULL, NULL, &ret);
3139
/* Free the Avahi configuration data */
3140
avahi_server_config_free(&config);
3143
/* Check if creating the Avahi server object succeeded */
3144
if(mc.server == NULL){
3145
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3146
avahi_strerror(ret));
3147
exitcode = EX_UNAVAILABLE;
3155
/* Create the Avahi service browser */
3156
sb = avahi_s_service_browser_new(mc.server, if_index,
3157
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
3158
NULL, 0, browse_callback,
3161
fprintf_plus(stderr, "Failed to create service browser: %s\n",
3162
avahi_strerror(avahi_server_errno(mc.server)));
3163
exitcode = EX_UNAVAILABLE;
3171
/* Run the main loop */
3174
fprintf_plus(stderr, "Starting Avahi loop search\n");
3177
ret = avahi_loop_with_timeout(simple_poll,
3178
(int)(retry_interval * 1000), &mc);
3180
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
3181
(ret == 0) ? "successfully" : "with error");
3187
if(signal_received){
3188
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3189
argv[0], signal_received,
3190
strsignal(signal_received));
3192
fprintf_plus(stderr, "%s exiting\n", argv[0]);
3196
/* Cleanup things */
3197
free(mc.interfaces);
3200
avahi_s_service_browser_free(sb);
3202
if(mc.server != NULL)
3203
avahi_server_free(mc.server);
3205
if(simple_poll != NULL)
3206
avahi_simple_poll_free(simple_poll);
3208
if(gnutls_initialized){
3209
gnutls_certificate_free_credentials(mc.cred);
3210
gnutls_dh_params_deinit(mc.dh_params);
3213
if(gpgme_initialized){
3214
gpgme_release(mc.ctx);
3217
/* Cleans up the circular linked list of Mandos servers the client
3219
if(mc.current_server != NULL){
3220
mc.current_server->prev->next = NULL;
3221
while(mc.current_server != NULL){
3222
server *next = mc.current_server->next;
3224
#pragma GCC diagnostic push
3225
#pragma GCC diagnostic ignored "-Wcast-qual"
3227
free((char *)(mc.current_server->ip));
3229
#pragma GCC diagnostic pop
3231
free(mc.current_server);
3232
mc.current_server = next;
3236
/* Re-raise privileges */
3238
ret = raise_privileges();
3241
perror_plus("Failed to raise privileges");
3244
/* Run network hooks */
3245
run_network_hooks("stop", interfaces_hooks != NULL ?
3246
interfaces_hooks : "", delay);
3248
/* Take down the network interfaces which were brought up */
3250
char *interface = NULL;
3251
while((interface = argz_next(interfaces_to_take_down,
3252
interfaces_to_take_down_size,
3254
ret = take_down_interface(interface);
3257
perror_plus("Failed to take down interface");
3260
if(debug and (interfaces_to_take_down == NULL)){
3261
fprintf_plus(stderr, "No interfaces needed to be taken"
3267
ret = lower_privileges_permanently();
3270
perror_plus("Failed to lower privileges permanently");
3274
free(interfaces_to_take_down);
3275
free(interfaces_hooks);
3277
void clean_dir_at(int base, const char * const dirname,
3279
struct dirent **direntries = NULL;
3281
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3287
perror_plus("open");
3290
int numentries = scandirat(dir_fd, ".", &direntries,
3291
notdotentries, alphasort);
3292
if(numentries >= 0){
3293
for(int i = 0; i < numentries; i++){
3295
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3296
dirname, direntries[i]->d_name);
3298
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3300
if(errno == EISDIR){
3301
dret = unlinkat(dir_fd, direntries[i]->d_name,
3304
if((dret == -1) and (errno == ENOTEMPTY)
3305
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3306
== 0) and (level == 0)){
3307
/* Recurse only in this special case */
3308
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3311
if((dret == -1) and (errno != ENOENT)){
3312
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3313
direntries[i]->d_name, strerror(errno));
3316
free(direntries[i]);
3319
/* need to clean even if 0 because man page doesn't specify */
3321
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3322
if(dret == -1 and errno != ENOENT){
3323
perror_plus("rmdir");
3326
perror_plus("scandirat");
3331
/* Removes the GPGME temp directory and all files inside */
3332
if(tempdir != NULL){
3333
clean_dir_at(-1, tempdir, 0);
3337
sigemptyset(&old_sigterm_action.sa_mask);
3338
old_sigterm_action.sa_handler = SIG_DFL;
3339
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3340
&old_sigterm_action,
3343
perror_plus("sigaction");
3346
ret = raise(signal_received);
3347
} while(ret != 0 and errno == EINTR);
3349
perror_plus("raise");
3352
TEMP_FAILURE_RETRY(pause());