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
#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");
1079
/* Reset supplementary groups */
1081
ret = setgroups(0, NULL);
1083
perror_plus("setgroups");
1087
ret = dup2(devnull, STDIN_FILENO);
1089
perror_plus("dup2(devnull, STDIN_FILENO)");
1092
ret = close(devnull);
1094
perror_plus("close");
1097
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1099
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1102
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
1107
if(helperdir_fd == -1){
1108
perror_plus("open");
1109
_exit(EX_UNAVAILABLE);
1111
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
1113
if(helper_fd == -1){
1114
perror_plus("openat");
1115
close(helperdir_fd);
1116
_exit(EX_UNAVAILABLE);
1118
close(helperdir_fd);
1120
#pragma GCC diagnostic push
1121
#pragma GCC diagnostic ignored "-Wcast-qual"
1123
if(fexecve(helper_fd, (char *const [])
1124
{ helper, add ? add_arg : delete_arg, (char *)address,
1125
interface, debug ? debug_flag : NULL, NULL },
1128
#pragma GCC diagnostic pop
1130
perror_plus("fexecve");
1131
_exit(EXIT_FAILURE);
1135
perror_plus("fork");
1142
pret = waitpid(pid, &status, 0);
1143
if(pret == -1 and errno == EINTR and quit_now){
1144
int errno_raising = 0;
1145
if((errno = raise_privileges()) != 0){
1146
errno_raising = errno;
1147
perror_plus("Failed to raise privileges in order to"
1148
" kill helper program");
1150
if(kill(pid, SIGTERM) == -1){
1151
perror_plus("kill");
1153
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
1154
perror_plus("Failed to lower privileges after killing"
1159
} while(pret == -1 and errno == EINTR);
1161
perror_plus("waitpid");
1164
if(WIFEXITED(status)){
1165
if(WEXITSTATUS(status) != 0){
1166
fprintf_plus(stderr, "Error: iprouteadddel exited"
1167
" with status %d\n", WEXITSTATUS(status));
1172
if(WIFSIGNALED(status)){
1173
fprintf_plus(stderr, "Error: iprouteadddel died by"
1174
" signal %d\n", WTERMSIG(status));
1177
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1181
__attribute__((nonnull, warn_unused_result))
1182
static bool add_local_route(const char *address,
1183
AvahiIfIndex if_index){
1185
fprintf_plus(stderr, "Adding route to %s\n", address);
1187
return add_delete_local_route(true, address, if_index);
1190
__attribute__((nonnull, warn_unused_result))
1191
static bool delete_local_route(const char *address,
1192
AvahiIfIndex if_index){
1194
fprintf_plus(stderr, "Removing route to %s\n", address);
1196
return add_delete_local_route(false, address, if_index);
1199
/* Called when a Mandos server is found */
1200
__attribute__((nonnull, warn_unused_result))
1201
static int start_mandos_communication(const char *ip, in_port_t port,
1202
AvahiIfIndex if_index,
1203
int af, mandos_context *mc){
1204
int ret, tcp_sd = -1;
1206
struct sockaddr_storage to;
1207
char *buffer = NULL;
1208
char *decrypted_buffer = NULL;
1209
size_t buffer_length = 0;
1210
size_t buffer_capacity = 0;
1213
gnutls_session_t session;
1214
int pf; /* Protocol family */
1215
bool route_added = false;
1232
fprintf_plus(stderr, "Bad address family: %d\n", af);
1237
/* If the interface is specified and we have a list of interfaces */
1238
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1239
/* Check if the interface is one of the interfaces we are using */
1242
char *interface = NULL;
1243
while((interface = argz_next(mc->interfaces,
1244
mc->interfaces_size,
1246
if(if_nametoindex(interface) == (unsigned int)if_index){
1253
/* This interface does not match any in the list, so we don't
1254
connect to the server */
1256
char interface[IF_NAMESIZE];
1257
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1258
perror_plus("if_indextoname");
1260
fprintf_plus(stderr, "Skipping server on non-used interface"
1262
if_indextoname((unsigned int)if_index,
1270
ret = init_gnutls_session(&session, mc);
1276
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1277
PRIuMAX "\n", ip, (uintmax_t)port);
1280
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1283
perror_plus("socket");
1294
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1295
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1296
ret = inet_pton(af, ip, &to6->sin6_addr);
1298
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1299
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1300
ret = inet_pton(af, ip, &to4->sin_addr);
1304
perror_plus("inet_pton");
1310
fprintf_plus(stderr, "Bad address: %s\n", ip);
1315
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1316
if(IN6_IS_ADDR_LINKLOCAL
1317
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1318
if(if_index == AVAHI_IF_UNSPEC){
1319
fprintf_plus(stderr, "An IPv6 link-local address is"
1320
" incomplete without a network interface\n");
1324
/* Set the network interface number as scope */
1325
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1328
((struct sockaddr_in *)&to)->sin_port = htons(port);
1337
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1338
char interface[IF_NAMESIZE];
1339
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1340
perror_plus("if_indextoname");
1342
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1343
"\n", ip, interface, (uintmax_t)port);
1346
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1347
ip, (uintmax_t)port);
1349
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1350
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1352
ret = getnameinfo((struct sockaddr *)&to,
1353
sizeof(struct sockaddr_in6),
1354
addrstr, sizeof(addrstr), NULL, 0,
1357
ret = getnameinfo((struct sockaddr *)&to,
1358
sizeof(struct sockaddr_in),
1359
addrstr, sizeof(addrstr), NULL, 0,
1362
if(ret == EAI_SYSTEM){
1363
perror_plus("getnameinfo");
1364
} else if(ret != 0) {
1365
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1366
} else if(strcmp(addrstr, ip) != 0){
1367
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1378
ret = connect(tcp_sd, (struct sockaddr *)&to,
1379
sizeof(struct sockaddr_in6));
1381
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1382
sizeof(struct sockaddr_in));
1385
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1386
and if_index != AVAHI_IF_UNSPEC
1387
and connect_to == NULL
1388
and not route_added and
1389
((af == AF_INET6 and not
1390
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1392
or (af == AF_INET and
1393
/* Not a a IPv4LL address */
1394
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1395
& 0xFFFF0000L) != 0xA9FE0000L))){
1396
/* Work around Avahi bug - Avahi does not announce link-local
1397
addresses if it has a global address, so local hosts with
1398
*only* a link-local address (e.g. Mandos clients) cannot
1399
connect to a Mandos server announced by Avahi on a server
1400
host with a global address. Work around this by retrying
1401
with an explicit route added with the server's address.
1403
Avahi bug reference:
1404
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1405
https://bugs.debian.org/587961
1408
fprintf_plus(stderr, "Mandos server unreachable, trying"
1412
route_added = add_local_route(ip, if_index);
1418
if(errno != ECONNREFUSED or debug){
1420
perror_plus("connect");
1433
const char *out = mandos_protocol_version;
1436
size_t out_size = strlen(out);
1437
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1438
out_size - written));
1441
perror_plus("write");
1445
written += (size_t)ret;
1446
if(written < out_size){
1449
if(out == mandos_protocol_version){
1464
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1472
/* This casting via intptr_t is to eliminate warning about casting
1473
an int to a pointer type. This is exactly how the GnuTLS Guile
1474
function "set-session-transport-fd!" does it. */
1475
gnutls_transport_set_ptr(session,
1476
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1484
ret = gnutls_handshake(session);
1489
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1491
if(ret != GNUTLS_E_SUCCESS){
1493
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1500
/* Read OpenPGP packet that contains the wanted password */
1503
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1514
buffer_capacity = incbuffer(&buffer, buffer_length,
1516
if(buffer_capacity == 0){
1518
perror_plus("incbuffer");
1528
sret = gnutls_record_recv(session, buffer+buffer_length,
1535
case GNUTLS_E_INTERRUPTED:
1536
case GNUTLS_E_AGAIN:
1538
case GNUTLS_E_REHANDSHAKE:
1540
ret = gnutls_handshake(session);
1546
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1548
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
1556
fprintf_plus(stderr, "Unknown error while reading data from"
1557
" encrypted session with Mandos server\n");
1558
gnutls_bye(session, GNUTLS_SHUT_RDWR);
1563
buffer_length += (size_t) sret;
1568
fprintf_plus(stderr, "Closing TLS session\n");
1577
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1582
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1584
if(buffer_length > 0){
1585
ssize_t decrypted_buffer_size;
1586
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1587
&decrypted_buffer, mc);
1588
if(decrypted_buffer_size >= 0){
1592
while(written < (size_t) decrypted_buffer_size){
1598
ret = (int)fwrite(decrypted_buffer + written, 1,
1599
(size_t)decrypted_buffer_size - written,
1601
if(ret == 0 and ferror(stdout)){
1604
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1610
written += (size_t)ret;
1612
ret = fflush(stdout);
1616
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1626
/* Shutdown procedure */
1631
if(not delete_local_route(ip, if_index)){
1632
fprintf_plus(stderr, "Failed to delete local route to %s on"
1633
" interface %d", ip, if_index);
1637
free(decrypted_buffer);
1640
ret = close(tcp_sd);
1646
perror_plus("close");
1648
gnutls_deinit(session);
1658
static void resolve_callback(AvahiSServiceResolver *r,
1659
AvahiIfIndex interface,
1660
AvahiProtocol proto,
1661
AvahiResolverEvent event,
1665
const char *host_name,
1666
const AvahiAddress *address,
1668
AVAHI_GCC_UNUSED AvahiStringList *txt,
1669
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1676
/* Called whenever a service has been resolved successfully or
1680
avahi_s_service_resolver_free(r);
1686
case AVAHI_RESOLVER_FAILURE:
1687
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1688
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1690
avahi_strerror(avahi_server_errno
1691
(((mandos_context*)mc)->server)));
1694
case AVAHI_RESOLVER_FOUND:
1696
char ip[AVAHI_ADDRESS_STR_MAX];
1697
avahi_address_snprint(ip, sizeof(ip), address);
1699
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1700
PRIdMAX ") on port %" PRIu16 "\n", name,
1701
host_name, ip, (intmax_t)interface, port);
1703
int ret = start_mandos_communication(ip, (in_port_t)port,
1705
avahi_proto_to_af(proto),
1708
avahi_simple_poll_quit(simple_poll);
1710
if(not add_server(ip, (in_port_t)port, interface,
1711
avahi_proto_to_af(proto),
1712
&((mandos_context*)mc)->current_server)){
1713
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1719
avahi_s_service_resolver_free(r);
1722
static void browse_callback(AvahiSServiceBrowser *b,
1723
AvahiIfIndex interface,
1724
AvahiProtocol protocol,
1725
AvahiBrowserEvent event,
1729
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1736
/* Called whenever a new services becomes available on the LAN or
1737
is removed from the LAN */
1745
case AVAHI_BROWSER_FAILURE:
1747
fprintf_plus(stderr, "(Avahi browser) %s\n",
1748
avahi_strerror(avahi_server_errno
1749
(((mandos_context*)mc)->server)));
1750
avahi_simple_poll_quit(simple_poll);
1753
case AVAHI_BROWSER_NEW:
1754
/* We ignore the returned Avahi resolver object. In the callback
1755
function we free it. If the Avahi server is terminated before
1756
the callback function is called the Avahi server will free the
1759
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1760
interface, protocol, name, type,
1761
domain, protocol, 0,
1762
resolve_callback, mc) == NULL)
1763
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1765
avahi_strerror(avahi_server_errno
1766
(((mandos_context*)mc)->server)));
1769
case AVAHI_BROWSER_REMOVE:
1772
case AVAHI_BROWSER_ALL_FOR_NOW:
1773
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1775
fprintf_plus(stderr, "No Mandos server found, still"
1782
/* Signal handler that stops main loop after SIGTERM */
1783
static void handle_sigterm(int sig){
1788
signal_received = sig;
1789
int old_errno = errno;
1790
/* set main loop to exit */
1791
if(simple_poll != NULL){
1792
avahi_simple_poll_quit(simple_poll);
1797
__attribute__((nonnull, warn_unused_result))
1798
bool get_flags(const char *ifname, struct ifreq *ifr){
1802
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1805
perror_plus("socket");
1809
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1810
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1811
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1815
perror_plus("ioctl SIOCGIFFLAGS");
1818
if((close(s) == -1) and debug){
1820
perror_plus("close");
1825
if((close(s) == -1) and debug){
1827
perror_plus("close");
1833
__attribute__((nonnull, warn_unused_result))
1834
bool good_flags(const char *ifname, const struct ifreq *ifr){
1836
/* Reject the loopback device */
1837
if(ifr->ifr_flags & IFF_LOOPBACK){
1839
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1844
/* Accept point-to-point devices only if connect_to is specified */
1845
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1847
fprintf_plus(stderr, "Accepting point-to-point interface"
1848
" \"%s\"\n", ifname);
1852
/* Otherwise, reject non-broadcast-capable devices */
1853
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1855
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1856
" \"%s\"\n", ifname);
1860
/* Reject non-ARP interfaces (including dummy interfaces) */
1861
if(ifr->ifr_flags & IFF_NOARP){
1863
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1869
/* Accept this device */
1871
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1877
* This function determines if a directory entry in /sys/class/net
1878
* corresponds to an acceptable network device.
1879
* (This function is passed to scandir(3) as a filter function.)
1881
__attribute__((nonnull, warn_unused_result))
1882
int good_interface(const struct dirent *if_entry){
1883
if(if_entry->d_name[0] == '.'){
1888
if(not get_flags(if_entry->d_name, &ifr)){
1890
fprintf_plus(stderr, "Failed to get flags for interface "
1891
"\"%s\"\n", if_entry->d_name);
1896
if(not good_flags(if_entry->d_name, &ifr)){
1903
* This function determines if a network interface is up.
1905
__attribute__((nonnull, warn_unused_result))
1906
bool interface_is_up(const char *interface){
1908
if(not get_flags(interface, &ifr)){
1910
fprintf_plus(stderr, "Failed to get flags for interface "
1911
"\"%s\"\n", interface);
1916
return (bool)(ifr.ifr_flags & IFF_UP);
1920
* This function determines if a network interface is running
1922
__attribute__((nonnull, warn_unused_result))
1923
bool interface_is_running(const char *interface){
1925
if(not get_flags(interface, &ifr)){
1927
fprintf_plus(stderr, "Failed to get flags for interface "
1928
"\"%s\"\n", interface);
1933
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1936
__attribute__((nonnull, pure, warn_unused_result))
1937
int notdotentries(const struct dirent *direntry){
1938
/* Skip "." and ".." */
1939
if(direntry->d_name[0] == '.'
1940
and (direntry->d_name[1] == '\0'
1941
or (direntry->d_name[1] == '.'
1942
and direntry->d_name[2] == '\0'))){
1948
/* Is this directory entry a runnable program? */
1949
__attribute__((nonnull, warn_unused_result))
1950
int runnable_hook(const struct dirent *direntry){
1955
if((direntry->d_name)[0] == '\0'){
1960
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1961
"abcdefghijklmnopqrstuvwxyz"
1964
if((direntry->d_name)[sret] != '\0'){
1965
/* Contains non-allowed characters */
1967
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1973
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1976
perror_plus("Could not stat hook");
1980
if(not (S_ISREG(st.st_mode))){
1981
/* Not a regular file */
1983
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1988
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1989
/* Not executable */
1991
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1997
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
2003
__attribute__((nonnull, warn_unused_result))
2004
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
2005
mandos_context *mc){
2007
struct timespec now;
2008
struct timespec waited_time;
2009
intmax_t block_time;
2012
if(mc->current_server == NULL){
2014
fprintf_plus(stderr, "Wait until first server is found."
2017
ret = avahi_simple_poll_iterate(s, -1);
2020
fprintf_plus(stderr, "Check current_server if we should run"
2023
/* the current time */
2024
ret = clock_gettime(CLOCK_MONOTONIC, &now);
2026
perror_plus("clock_gettime");
2029
/* Calculating in ms how long time between now and server
2030
who we visted longest time ago. Now - last seen. */
2031
waited_time.tv_sec = (now.tv_sec
2032
- mc->current_server->last_seen.tv_sec);
2033
waited_time.tv_nsec = (now.tv_nsec
2034
- mc->current_server->last_seen.tv_nsec);
2035
/* total time is 10s/10,000ms.
2036
Converting to s from ms by dividing by 1,000,
2037
and ns to ms by dividing by 1,000,000. */
2038
block_time = ((retry_interval
2039
- ((intmax_t)waited_time.tv_sec * 1000))
2040
- ((intmax_t)waited_time.tv_nsec / 1000000));
2043
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
2047
if(block_time <= 0){
2048
ret = start_mandos_communication(mc->current_server->ip,
2049
mc->current_server->port,
2050
mc->current_server->if_index,
2051
mc->current_server->af, mc);
2053
avahi_simple_poll_quit(s);
2056
ret = clock_gettime(CLOCK_MONOTONIC,
2057
&mc->current_server->last_seen);
2059
perror_plus("clock_gettime");
2062
mc->current_server = mc->current_server->next;
2063
block_time = 0; /* Call avahi to find new Mandos
2064
servers, but don't block */
2067
ret = avahi_simple_poll_iterate(s, (int)block_time);
2070
if(ret > 0 or errno != EINTR){
2071
return (ret != 1) ? ret : 0;
2077
__attribute__((nonnull))
2078
void run_network_hooks(const char *mode, const char *interface,
2080
struct dirent **direntries = NULL;
2081
if(hookdir_fd == -1){
2082
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
2084
if(hookdir_fd == -1){
2085
if(errno == ENOENT){
2087
fprintf_plus(stderr, "Network hook directory \"%s\" not"
2088
" found\n", hookdir);
2091
perror_plus("open");
2096
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2098
perror_plus("open(\"/dev/null\", O_RDONLY)");
2101
int numhooks = scandirat(hookdir_fd, ".", &direntries,
2102
runnable_hook, alphasort);
2104
perror_plus("scandir");
2108
struct dirent *direntry;
2110
for(int i = 0; i < numhooks; i++){
2111
direntry = direntries[i];
2113
fprintf_plus(stderr, "Running network hook \"%s\"\n",
2116
pid_t hook_pid = fork();
2119
/* Raise privileges */
2120
errno = raise_privileges_permanently();
2122
perror_plus("Failed to raise privileges");
2129
perror_plus("setgid");
2132
/* Reset supplementary groups */
2134
ret = setgroups(0, NULL);
2136
perror_plus("setgroups");
2139
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
2141
perror_plus("setenv");
2144
ret = setenv("DEVICE", interface, 1);
2146
perror_plus("setenv");
2149
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
2151
perror_plus("setenv");
2154
ret = setenv("MODE", mode, 1);
2156
perror_plus("setenv");
2160
ret = asprintf(&delaystring, "%f", (double)delay);
2162
perror_plus("asprintf");
2165
ret = setenv("DELAY", delaystring, 1);
2168
perror_plus("setenv");
2172
if(connect_to != NULL){
2173
ret = setenv("CONNECT", connect_to, 1);
2175
perror_plus("setenv");
2179
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2183
perror_plus("openat");
2184
_exit(EXIT_FAILURE);
2186
if(close(hookdir_fd) == -1){
2187
perror_plus("close");
2188
_exit(EXIT_FAILURE);
2190
ret = dup2(devnull, STDIN_FILENO);
2192
perror_plus("dup2(devnull, STDIN_FILENO)");
2195
ret = close(devnull);
2197
perror_plus("close");
2200
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2202
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2205
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2207
perror_plus("fexecve");
2208
_exit(EXIT_FAILURE);
2212
perror_plus("fork");
2217
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2218
perror_plus("waitpid");
2222
if(WIFEXITED(status)){
2223
if(WEXITSTATUS(status) != 0){
2224
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2225
" with status %d\n", direntry->d_name,
2226
WEXITSTATUS(status));
2230
} else if(WIFSIGNALED(status)){
2231
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2232
" signal %d\n", direntry->d_name,
2237
fprintf_plus(stderr, "Warning: network hook \"%s\""
2238
" crashed\n", direntry->d_name);
2244
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2250
if(close(hookdir_fd) == -1){
2251
perror_plus("close");
2258
__attribute__((nonnull, warn_unused_result))
2259
int bring_up_interface(const char *const interface,
2261
int old_errno = errno;
2263
struct ifreq network;
2264
unsigned int if_index = if_nametoindex(interface);
2266
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2276
if(not interface_is_up(interface)){
2278
int ioctl_errno = 0;
2279
if(not get_flags(interface, &network)){
2281
fprintf_plus(stderr, "Failed to get flags for interface "
2282
"\"%s\"\n", interface);
2286
network.ifr_flags |= IFF_UP; /* set flag */
2288
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2291
perror_plus("socket");
2299
perror_plus("close");
2306
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2310
/* Raise privileges */
2311
ret_errno = raise_privileges();
2314
perror_plus("Failed to raise privileges");
2319
bool restore_loglevel = false;
2321
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2322
messages about the network interface to mess up the prompt */
2323
ret_linux = klogctl(8, NULL, 5);
2324
if(ret_linux == -1){
2325
perror_plus("klogctl");
2327
restore_loglevel = true;
2330
#endif /* __linux__ */
2331
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2332
ioctl_errno = errno;
2334
if(restore_loglevel){
2335
ret_linux = klogctl(7, NULL, 0);
2336
if(ret_linux == -1){
2337
perror_plus("klogctl");
2340
#endif /* __linux__ */
2342
/* If raise_privileges() succeeded above */
2344
/* Lower privileges */
2345
ret_errno = lower_privileges();
2348
perror_plus("Failed to lower privileges");
2352
/* Close the socket */
2355
perror_plus("close");
2358
if(ret_setflags == -1){
2359
errno = ioctl_errno;
2360
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2365
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2369
/* Sleep checking until interface is running.
2370
Check every 0.25s, up to total time of delay */
2371
for(int i = 0; i < delay * 4; i++){
2372
if(interface_is_running(interface)){
2375
struct timespec sleeptime = { .tv_nsec = 250000000 };
2376
ret = nanosleep(&sleeptime, NULL);
2377
if(ret == -1 and errno != EINTR){
2378
perror_plus("nanosleep");
2386
__attribute__((nonnull, warn_unused_result))
2387
int take_down_interface(const char *const interface){
2388
int old_errno = errno;
2389
struct ifreq network;
2390
unsigned int if_index = if_nametoindex(interface);
2392
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2396
if(interface_is_up(interface)){
2398
int ioctl_errno = 0;
2399
if(not get_flags(interface, &network) and debug){
2401
fprintf_plus(stderr, "Failed to get flags for interface "
2402
"\"%s\"\n", interface);
2406
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2408
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2411
perror_plus("socket");
2417
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2421
/* Raise privileges */
2422
ret_errno = raise_privileges();
2425
perror_plus("Failed to raise privileges");
2428
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2429
ioctl_errno = errno;
2431
/* If raise_privileges() succeeded above */
2433
/* Lower privileges */
2434
ret_errno = lower_privileges();
2437
perror_plus("Failed to lower privileges");
2441
/* Close the socket */
2442
int ret = close(sd);
2444
perror_plus("close");
2447
if(ret_setflags == -1){
2448
errno = ioctl_errno;
2449
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2454
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2462
int main(int argc, char *argv[]){
2463
mandos_context mc = { .server = NULL, .dh_bits = 0,
2464
#if GNUTLS_VERSION_NUMBER >= 0x030606
2465
.priority = "SECURE128:!CTYPE-X.509"
2466
":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
2468
#elif GNUTLS_VERSION_NUMBER < 0x030600
2469
.priority = "SECURE256:!CTYPE-X.509"
2470
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2472
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
2474
.current_server = NULL, .interfaces = NULL,
2475
.interfaces_size = 0 };
2476
AvahiSServiceBrowser *sb = NULL;
2481
int exitcode = EXIT_SUCCESS;
2482
char *interfaces_to_take_down = NULL;
2483
size_t interfaces_to_take_down_size = 0;
2484
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2485
char old_tempdir[] = "/tmp/mandosXXXXXX";
2486
char *tempdir = NULL;
2487
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2488
const char *seckey = PATHDIR "/" SECKEY;
2489
const char *pubkey = PATHDIR "/" PUBKEY;
2490
#if GNUTLS_VERSION_NUMBER >= 0x030606
2491
const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
2492
const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
2494
const char *dh_params_file = NULL;
2495
char *interfaces_hooks = NULL;
2497
bool gnutls_initialized = false;
2498
bool gpgme_initialized = false;
2500
double retry_interval = 10; /* 10s between trying a server and
2501
retrying the same server again */
2503
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2504
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2509
/* Lower any group privileges we might have, just to be safe */
2513
perror_plus("setgid");
2516
/* Lower user privileges (temporarily) */
2520
perror_plus("seteuid");
2528
struct argp_option options[] = {
2529
{ .name = "debug", .key = 128,
2530
.doc = "Debug mode", .group = 3 },
2531
{ .name = "connect", .key = 'c',
2532
.arg = "ADDRESS:PORT",
2533
.doc = "Connect directly to a specific Mandos server",
2535
{ .name = "interface", .key = 'i',
2537
.doc = "Network interface that will be used to search for"
2540
{ .name = "seckey", .key = 's',
2542
.doc = "OpenPGP secret key file base name",
2544
{ .name = "pubkey", .key = 'p',
2546
.doc = "OpenPGP public key file base name",
2548
{ .name = "tls-privkey", .key = 't',
2550
#if GNUTLS_VERSION_NUMBER >= 0x030606
2551
.doc = "TLS private key file base name",
2553
.doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2556
{ .name = "tls-pubkey", .key = 'T',
2558
#if GNUTLS_VERSION_NUMBER >= 0x030606
2559
.doc = "TLS public key file base name",
2561
.doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2564
{ .name = "dh-bits", .key = 129,
2566
.doc = "Bit length of the prime number used in the"
2567
" Diffie-Hellman key exchange",
2569
{ .name = "dh-params", .key = 134,
2571
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2572
" for the Diffie-Hellman key exchange",
2574
{ .name = "priority", .key = 130,
2576
.doc = "GnuTLS priority string for the TLS handshake",
2578
{ .name = "delay", .key = 131,
2580
.doc = "Maximum delay to wait for interface startup",
2582
{ .name = "retry", .key = 132,
2584
.doc = "Retry interval used when denied by the Mandos server",
2586
{ .name = "network-hook-dir", .key = 133,
2588
.doc = "Directory where network hooks are located",
2591
* These reproduce what we would get without ARGP_NO_HELP
2593
{ .name = "help", .key = '?',
2594
.doc = "Give this help list", .group = -1 },
2595
{ .name = "usage", .key = -3,
2596
.doc = "Give a short usage message", .group = -1 },
2597
{ .name = "version", .key = 'V',
2598
.doc = "Print program version", .group = -1 },
2602
error_t parse_opt(int key, char *arg,
2603
struct argp_state *state){
2606
case 128: /* --debug */
2609
case 'c': /* --connect */
2612
case 'i': /* --interface */
2613
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2616
argp_error(state, "%s", strerror(ret_errno));
2619
case 's': /* --seckey */
2622
case 'p': /* --pubkey */
2625
case 't': /* --tls-privkey */
2626
#if GNUTLS_VERSION_NUMBER >= 0x030606
2630
case 'T': /* --tls-pubkey */
2631
#if GNUTLS_VERSION_NUMBER >= 0x030606
2635
case 129: /* --dh-bits */
2637
tmpmax = strtoimax(arg, &tmp, 10);
2638
if(errno != 0 or tmp == arg or *tmp != '\0'
2639
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2640
argp_error(state, "Bad number of DH bits");
2642
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2644
case 134: /* --dh-params */
2645
dh_params_file = arg;
2647
case 130: /* --priority */
2650
case 131: /* --delay */
2652
delay = strtof(arg, &tmp);
2653
if(errno != 0 or tmp == arg or *tmp != '\0'){
2654
argp_error(state, "Bad delay");
2656
case 132: /* --retry */
2658
retry_interval = strtod(arg, &tmp);
2659
if(errno != 0 or tmp == arg or *tmp != '\0'
2660
or (retry_interval * 1000) > INT_MAX
2661
or retry_interval < 0){
2662
argp_error(state, "Bad retry interval");
2665
case 133: /* --network-hook-dir */
2669
* These reproduce what we would get without ARGP_NO_HELP
2671
case '?': /* --help */
2672
argp_state_help(state, state->out_stream,
2673
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2674
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2675
case -3: /* --usage */
2676
argp_state_help(state, state->out_stream,
2677
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2678
case 'V': /* --version */
2679
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2680
exit(argp_err_exit_status);
2683
return ARGP_ERR_UNKNOWN;
2688
struct argp argp = { .options = options, .parser = parse_opt,
2690
.doc = "Mandos client -- Get and decrypt"
2691
" passwords from a Mandos server" };
2692
ret_errno = argp_parse(&argp, argc, argv,
2693
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2700
perror_plus("argp_parse");
2701
exitcode = EX_OSERR;
2704
exitcode = EX_USAGE;
2710
/* Work around Debian bug #633582:
2711
<https://bugs.debian.org/633582> */
2713
/* Re-raise privileges */
2714
ret = raise_privileges();
2717
perror_plus("Failed to raise privileges");
2721
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2722
int seckey_fd = open(seckey, O_RDONLY);
2723
if(seckey_fd == -1){
2724
perror_plus("open");
2726
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2728
perror_plus("fstat");
2730
if(S_ISREG(st.st_mode)
2731
and st.st_uid == 0 and st.st_gid == 0){
2732
ret = fchown(seckey_fd, uid, gid);
2734
perror_plus("fchown");
2742
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2743
int pubkey_fd = open(pubkey, O_RDONLY);
2744
if(pubkey_fd == -1){
2745
perror_plus("open");
2747
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2749
perror_plus("fstat");
2751
if(S_ISREG(st.st_mode)
2752
and st.st_uid == 0 and st.st_gid == 0){
2753
ret = fchown(pubkey_fd, uid, gid);
2755
perror_plus("fchown");
2763
if(dh_params_file != NULL
2764
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2765
int dhparams_fd = open(dh_params_file, O_RDONLY);
2766
if(dhparams_fd == -1){
2767
perror_plus("open");
2769
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2771
perror_plus("fstat");
2773
if(S_ISREG(st.st_mode)
2774
and st.st_uid == 0 and st.st_gid == 0){
2775
ret = fchown(dhparams_fd, uid, gid);
2777
perror_plus("fchown");
2785
/* Lower privileges */
2786
ret = lower_privileges();
2789
perror_plus("Failed to lower privileges");
2794
/* Remove invalid interface names (except "none") */
2796
char *interface = NULL;
2797
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2799
if(strcmp(interface, "none") != 0
2800
and if_nametoindex(interface) == 0){
2801
if(interface[0] != '\0'){
2802
fprintf_plus(stderr, "Not using nonexisting interface"
2803
" \"%s\"\n", interface);
2805
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2811
/* Run network hooks */
2813
if(mc.interfaces != NULL){
2814
interfaces_hooks = malloc(mc.interfaces_size);
2815
if(interfaces_hooks == NULL){
2816
perror_plus("malloc");
2819
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2820
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2822
run_network_hooks("start", interfaces_hooks != NULL ?
2823
interfaces_hooks : "", delay);
2827
avahi_set_log_function(empty_log);
2830
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2831
from the signal handler */
2832
/* Initialize the pseudo-RNG for Avahi */
2833
srand((unsigned int) time(NULL));
2834
simple_poll = avahi_simple_poll_new();
2835
if(simple_poll == NULL){
2836
fprintf_plus(stderr,
2837
"Avahi: Failed to create simple poll object.\n");
2838
exitcode = EX_UNAVAILABLE;
2842
sigemptyset(&sigterm_action.sa_mask);
2843
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2845
perror_plus("sigaddset");
2846
exitcode = EX_OSERR;
2849
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2851
perror_plus("sigaddset");
2852
exitcode = EX_OSERR;
2855
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2857
perror_plus("sigaddset");
2858
exitcode = EX_OSERR;
2861
/* Need to check if the handler is SIG_IGN before handling:
2862
| [[info:libc:Initial Signal Actions]] |
2863
| [[info:libc:Basic Signal Handling]] |
2865
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2867
perror_plus("sigaction");
2870
if(old_sigterm_action.sa_handler != SIG_IGN){
2871
ret = sigaction(SIGINT, &sigterm_action, NULL);
2873
perror_plus("sigaction");
2874
exitcode = EX_OSERR;
2878
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2880
perror_plus("sigaction");
2883
if(old_sigterm_action.sa_handler != SIG_IGN){
2884
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2886
perror_plus("sigaction");
2887
exitcode = EX_OSERR;
2891
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2893
perror_plus("sigaction");
2896
if(old_sigterm_action.sa_handler != SIG_IGN){
2897
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2899
perror_plus("sigaction");
2900
exitcode = EX_OSERR;
2905
/* If no interfaces were specified, make a list */
2906
if(mc.interfaces == NULL){
2907
struct dirent **direntries = NULL;
2908
/* Look for any good interfaces */
2909
ret = scandir(sys_class_net, &direntries, good_interface,
2912
/* Add all found interfaces to interfaces list */
2913
for(int i = 0; i < ret; ++i){
2914
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2915
direntries[i]->d_name);
2918
perror_plus("argz_add");
2919
free(direntries[i]);
2923
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2924
direntries[i]->d_name);
2926
free(direntries[i]);
2933
fprintf_plus(stderr, "Could not find a network interface\n");
2934
exitcode = EXIT_FAILURE;
2939
/* Bring up interfaces which are down, and remove any "none"s */
2941
char *interface = NULL;
2942
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2944
/* If interface name is "none", stop bringing up interfaces.
2945
Also remove all instances of "none" from the list */
2946
if(strcmp(interface, "none") == 0){
2947
argz_delete(&mc.interfaces, &mc.interfaces_size,
2950
while((interface = argz_next(mc.interfaces,
2951
mc.interfaces_size, interface))){
2952
if(strcmp(interface, "none") == 0){
2953
argz_delete(&mc.interfaces, &mc.interfaces_size,
2960
bool interface_was_up = interface_is_up(interface);
2961
errno = bring_up_interface(interface, delay);
2962
if(not interface_was_up){
2964
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2965
" %s\n", interface, strerror(errno));
2967
errno = argz_add(&interfaces_to_take_down,
2968
&interfaces_to_take_down_size,
2971
perror_plus("argz_add");
2976
if(debug and (interfaces_to_take_down == NULL)){
2977
fprintf_plus(stderr, "No interfaces were brought up\n");
2981
/* If we only got one interface, explicitly use only that one */
2982
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2984
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2987
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2994
#if GNUTLS_VERSION_NUMBER >= 0x030606
2995
ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
2996
#elif GNUTLS_VERSION_NUMBER < 0x030600
2997
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2999
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
3002
fprintf_plus(stderr, "init_gnutls_global failed\n");
3003
exitcode = EX_UNAVAILABLE;
3006
gnutls_initialized = true;
3013
/* Try /run/tmp before /tmp */
3014
tempdir = mkdtemp(run_tempdir);
3015
if(tempdir == NULL and errno == ENOENT){
3017
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
3018
run_tempdir, old_tempdir);
3020
tempdir = mkdtemp(old_tempdir);
3022
if(tempdir == NULL){
3023
perror_plus("mkdtemp");
3031
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
3032
fprintf_plus(stderr, "init_gpgme failed\n");
3033
exitcode = EX_UNAVAILABLE;
3036
gpgme_initialized = true;
3043
if(connect_to != NULL){
3044
/* Connect directly, do not use Zeroconf */
3045
/* (Mainly meant for debugging) */
3046
char *address = strrchr(connect_to, ':');
3048
if(address == NULL){
3049
fprintf_plus(stderr, "No colon in address\n");
3050
exitcode = EX_USAGE;
3060
tmpmax = strtoimax(address+1, &tmp, 10);
3061
if(errno != 0 or tmp == address+1 or *tmp != '\0'
3062
or tmpmax != (in_port_t)tmpmax){
3063
fprintf_plus(stderr, "Bad port number\n");
3064
exitcode = EX_USAGE;
3072
port = (in_port_t)tmpmax;
3074
/* Colon in address indicates IPv6 */
3076
if(strchr(connect_to, ':') != NULL){
3078
/* Accept [] around IPv6 address - see RFC 5952 */
3079
if(connect_to[0] == '[' and address[-1] == ']')
3087
address = connect_to;
3093
while(not quit_now){
3094
ret = start_mandos_communication(address, port, if_index, af,
3096
if(quit_now or ret == 0){
3100
fprintf_plus(stderr, "Retrying in %d seconds\n",
3101
(int)retry_interval);
3103
sleep((unsigned int)retry_interval);
3107
exitcode = EXIT_SUCCESS;
3118
AvahiServerConfig config;
3119
/* Do not publish any local Zeroconf records */
3120
avahi_server_config_init(&config);
3121
config.publish_hinfo = 0;
3122
config.publish_addresses = 0;
3123
config.publish_workstation = 0;
3124
config.publish_domain = 0;
3126
/* Allocate a new server */
3127
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3128
&config, NULL, NULL, &ret);
3130
/* Free the Avahi configuration data */
3131
avahi_server_config_free(&config);
3134
/* Check if creating the Avahi server object succeeded */
3135
if(mc.server == NULL){
3136
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3137
avahi_strerror(ret));
3138
exitcode = EX_UNAVAILABLE;
3146
/* Create the Avahi service browser */
3147
sb = avahi_s_service_browser_new(mc.server, if_index,
3148
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
3149
NULL, 0, browse_callback,
3152
fprintf_plus(stderr, "Failed to create service browser: %s\n",
3153
avahi_strerror(avahi_server_errno(mc.server)));
3154
exitcode = EX_UNAVAILABLE;
3162
/* Run the main loop */
3165
fprintf_plus(stderr, "Starting Avahi loop search\n");
3168
ret = avahi_loop_with_timeout(simple_poll,
3169
(int)(retry_interval * 1000), &mc);
3171
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
3172
(ret == 0) ? "successfully" : "with error");
3178
if(signal_received){
3179
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3180
argv[0], signal_received,
3181
strsignal(signal_received));
3183
fprintf_plus(stderr, "%s exiting\n", argv[0]);
3187
/* Cleanup things */
3188
free(mc.interfaces);
3191
avahi_s_service_browser_free(sb);
3193
if(mc.server != NULL)
3194
avahi_server_free(mc.server);
3196
if(simple_poll != NULL)
3197
avahi_simple_poll_free(simple_poll);
3199
if(gnutls_initialized){
3200
gnutls_certificate_free_credentials(mc.cred);
3201
gnutls_dh_params_deinit(mc.dh_params);
3204
if(gpgme_initialized){
3205
gpgme_release(mc.ctx);
3208
/* Cleans up the circular linked list of Mandos servers the client
3210
if(mc.current_server != NULL){
3211
mc.current_server->prev->next = NULL;
3212
while(mc.current_server != NULL){
3213
server *next = mc.current_server->next;
3215
#pragma GCC diagnostic push
3216
#pragma GCC diagnostic ignored "-Wcast-qual"
3218
free((char *)(mc.current_server->ip));
3220
#pragma GCC diagnostic pop
3222
free(mc.current_server);
3223
mc.current_server = next;
3227
/* Re-raise privileges */
3229
ret = raise_privileges();
3232
perror_plus("Failed to raise privileges");
3235
/* Run network hooks */
3236
run_network_hooks("stop", interfaces_hooks != NULL ?
3237
interfaces_hooks : "", delay);
3239
/* Take down the network interfaces which were brought up */
3241
char *interface = NULL;
3242
while((interface = argz_next(interfaces_to_take_down,
3243
interfaces_to_take_down_size,
3245
ret = take_down_interface(interface);
3248
perror_plus("Failed to take down interface");
3251
if(debug and (interfaces_to_take_down == NULL)){
3252
fprintf_plus(stderr, "No interfaces needed to be taken"
3258
ret = lower_privileges_permanently();
3261
perror_plus("Failed to lower privileges permanently");
3265
free(interfaces_to_take_down);
3266
free(interfaces_hooks);
3268
void clean_dir_at(int base, const char * const dirname,
3270
struct dirent **direntries = NULL;
3272
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3278
perror_plus("open");
3281
int numentries = scandirat(dir_fd, ".", &direntries,
3282
notdotentries, alphasort);
3283
if(numentries >= 0){
3284
for(int i = 0; i < numentries; i++){
3286
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3287
dirname, direntries[i]->d_name);
3289
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3291
if(errno == EISDIR){
3292
dret = unlinkat(dir_fd, direntries[i]->d_name,
3295
if((dret == -1) and (errno == ENOTEMPTY)
3296
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3297
== 0) and (level == 0)){
3298
/* Recurse only in this special case */
3299
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3302
if((dret == -1) and (errno != ENOENT)){
3303
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3304
direntries[i]->d_name, strerror(errno));
3307
free(direntries[i]);
3310
/* need to clean even if 0 because man page doesn't specify */
3312
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3313
if(dret == -1 and errno != ENOENT){
3314
perror_plus("rmdir");
3317
perror_plus("scandirat");
3322
/* Removes the GPGME temp directory and all files inside */
3323
if(tempdir != NULL){
3324
clean_dir_at(-1, tempdir, 0);
3328
sigemptyset(&old_sigterm_action.sa_mask);
3329
old_sigterm_action.sa_handler = SIG_DFL;
3330
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3331
&old_sigterm_action,
3334
perror_plus("sigaction");
3337
ret = raise(signal_received);
3338
} while(ret != 0 and errno == EINTR);
3340
perror_plus("raise");
3343
TEMP_FAILURE_RETRY(pause());