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-2016 Teddy Hogeborn
13
* Copyright © 2008-2016 Björn Påhlsson
15
* This program is free software: you can redistribute it and/or
16
* modify it under the terms of the GNU General Public License as
17
* published by the Free Software Foundation, either version 3 of the
18
* License, or (at your option) any later version.
20
* This program is distributed in the hope that it will be useful, but
21
* WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23
* General Public License for more details.
25
* You should have received a copy of the GNU General Public License
26
* along with this program. If not, see
27
* <http://www.gnu.org/licenses/>.
29
* Contact the authors at <mandos@recompile.se>.
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#ifndef _LARGEFILE_SOURCE
34
#define _LARGEFILE_SOURCE
35
#endif /* not _LARGEFILE_SOURCE */
36
#ifndef _FILE_OFFSET_BITS
37
#define _FILE_OFFSET_BITS 64
38
#endif /* not _FILE_OFFSET_BITS */
40
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
42
#include <stdio.h> /* fprintf(), stderr, fwrite(),
44
#include <stdint.h> /* uint16_t, uint32_t, intptr_t */
45
#include <stddef.h> /* NULL, size_t, ssize_t */
46
#include <stdlib.h> /* free(), EXIT_SUCCESS, srand(),
48
#include <stdbool.h> /* bool, false, true */
49
#include <string.h> /* strcmp(), strlen(), strerror(),
50
asprintf(), strncpy() */
51
#include <sys/ioctl.h> /* ioctl */
52
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
53
sockaddr_in6, PF_INET6,
54
SOCK_STREAM, uid_t, gid_t, open(),
56
#include <sys/stat.h> /* open(), S_ISREG */
57
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
58
inet_pton(), connect(),
60
#include <fcntl.h> /* open(), unlinkat() */
61
#include <dirent.h> /* opendir(), struct dirent, readdir()
63
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
65
#include <errno.h> /* perror(), errno,
66
program_invocation_short_name */
67
#include <time.h> /* nanosleep(), time(), sleep() */
68
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
69
SIOCSIFFLAGS, if_indextoname(),
70
if_nametoindex(), IF_NAMESIZE */
71
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
72
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
74
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
75
getuid(), getgid(), seteuid(),
76
setgid(), pause(), _exit(),
78
#include <arpa/inet.h> /* inet_pton(), htons() */
79
#include <iso646.h> /* not, or, and */
80
#include <argp.h> /* struct argp_option, error_t, struct
81
argp_state, struct argp,
82
argp_parse(), ARGP_KEY_ARG,
83
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
84
#include <signal.h> /* sigemptyset(), sigaddset(),
85
sigaction(), SIGTERM, sig_atomic_t,
87
#include <sysexits.h> /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
88
EX_NOHOST, EX_IOERR, EX_PROTOCOL */
89
#include <sys/wait.h> /* waitpid(), WIFEXITED(),
90
WEXITSTATUS(), WTERMSIG() */
91
#include <grp.h> /* setgroups() */
92
#include <argz.h> /* argz_add_sep(), argz_next(),
93
argz_delete(), argz_append(),
94
argz_stringify(), argz_add(),
96
#include <netdb.h> /* getnameinfo(), NI_NUMERICHOST,
97
EAI_SYSTEM, gai_strerror() */
100
#include <sys/klog.h> /* klogctl() */
101
#endif /* __linux__ */
104
/* All Avahi types, constants and functions
107
#include <avahi-core/core.h>
108
#include <avahi-core/lookup.h>
109
#include <avahi-core/log.h>
110
#include <avahi-common/simple-watch.h>
111
#include <avahi-common/malloc.h>
112
#include <avahi-common/error.h>
115
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
118
init_gnutls_session(),
120
#include <gnutls/openpgp.h>
121
/* gnutls_certificate_set_openpgp_key_file(),
122
GNUTLS_OPENPGP_FMT_BASE64 */
125
#include <gpgme.h> /* All GPGME types, constants and
128
GPGME_PROTOCOL_OpenPGP,
131
#define BUFFER_SIZE 256
133
#define PATHDIR "/conf/conf.d/mandos"
134
#define SECKEY "seckey.txt"
135
#define PUBKEY "pubkey.txt"
136
#define HOOKDIR "/lib/mandos/network-hooks.d"
139
static const char mandos_protocol_version[] = "1";
140
const char *argp_program_version = "mandos-client " VERSION;
141
const char *argp_program_bug_address = "<mandos@recompile.se>";
142
static const char sys_class_net[] = "/sys/class/net";
143
char *connect_to = NULL;
144
const char *hookdir = HOOKDIR;
149
/* Doubly linked list that need to be circularly linked when used */
150
typedef struct server{
153
AvahiIfIndex if_index;
155
struct timespec last_seen;
160
/* Used for passing in values through the Avahi callback functions */
163
gnutls_certificate_credentials_t cred;
164
unsigned int dh_bits;
165
gnutls_dh_params_t dh_params;
166
const char *priority;
168
server *current_server;
170
size_t interfaces_size;
173
/* global so signal handler can reach it*/
174
AvahiSimplePoll *simple_poll;
176
sig_atomic_t quit_now = 0;
177
int signal_received = 0;
179
/* Function to use when printing errors */
180
void perror_plus(const char *print_text){
182
fprintf(stderr, "Mandos plugin %s: ",
183
program_invocation_short_name);
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
189
int fprintf_plus(FILE *stream, const char *format, ...){
191
va_start (ap, format);
193
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
194
program_invocation_short_name));
195
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
199
* Make additional room in "buffer" for at least BUFFER_SIZE more
200
* bytes. "buffer_capacity" is how much is currently allocated,
201
* "buffer_length" is how much is already used.
203
__attribute__((nonnull, warn_unused_result))
204
size_t incbuffer(char **buffer, size_t buffer_length,
205
size_t buffer_capacity){
206
if(buffer_length + BUFFER_SIZE > buffer_capacity){
207
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
209
int old_errno = errno;
216
buffer_capacity += BUFFER_SIZE;
218
return buffer_capacity;
221
/* Add server to set of servers to retry periodically */
222
__attribute__((nonnull, warn_unused_result))
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
224
int af, server **current_server){
226
server *new_server = malloc(sizeof(server));
227
if(new_server == NULL){
228
perror_plus("malloc");
231
*new_server = (server){ .ip = strdup(ip),
233
.if_index = if_index,
235
if(new_server->ip == NULL){
236
perror_plus("strdup");
240
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
242
perror_plus("clock_gettime");
244
#pragma GCC diagnostic push
245
#pragma GCC diagnostic ignored "-Wcast-qual"
247
free((char *)(new_server->ip));
249
#pragma GCC diagnostic pop
254
/* Special case of first server */
255
if(*current_server == NULL){
256
new_server->next = new_server;
257
new_server->prev = new_server;
258
*current_server = new_server;
260
/* Place the new server last in the list */
261
new_server->next = *current_server;
262
new_server->prev = (*current_server)->prev;
263
new_server->prev->next = new_server;
264
(*current_server)->prev = new_server;
272
__attribute__((nonnull, warn_unused_result))
273
static bool init_gpgme(const char * const seckey,
274
const char * const pubkey,
275
const char * const tempdir,
278
gpgme_engine_info_t engine_info;
281
* Helper function to insert pub and seckey to the engine keyring.
283
bool import_key(const char * const filename){
286
gpgme_data_t pgp_data;
288
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
294
rc = gpgme_data_new_from_fd(&pgp_data, fd);
295
if(rc != GPG_ERR_NO_ERROR){
296
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
297
gpgme_strsource(rc), gpgme_strerror(rc));
301
rc = gpgme_op_import(mc->ctx, pgp_data);
302
if(rc != GPG_ERR_NO_ERROR){
303
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
304
gpgme_strsource(rc), gpgme_strerror(rc));
310
perror_plus("close");
312
gpgme_data_release(pgp_data);
317
fprintf_plus(stderr, "Initializing GPGME\n");
321
gpgme_check_version(NULL);
322
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
323
if(rc != GPG_ERR_NO_ERROR){
324
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
325
gpgme_strsource(rc), gpgme_strerror(rc));
329
/* Set GPGME home directory for the OpenPGP engine only */
330
rc = gpgme_get_engine_info(&engine_info);
331
if(rc != GPG_ERR_NO_ERROR){
332
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
333
gpgme_strsource(rc), gpgme_strerror(rc));
336
while(engine_info != NULL){
337
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
338
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
339
engine_info->file_name, tempdir);
342
engine_info = engine_info->next;
344
if(engine_info == NULL){
345
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
350
/* Create new GPGME "context" */
351
rc = gpgme_new(&(mc->ctx));
352
if(rc != GPG_ERR_NO_ERROR){
353
fprintf_plus(stderr, "Mandos plugin mandos-client: "
354
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
359
if(not import_key(pubkey) or not import_key(seckey)){
367
* Decrypt OpenPGP data.
368
* Returns -1 on error
370
__attribute__((nonnull, warn_unused_result))
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
375
gpgme_data_t dh_crypto, dh_plain;
378
size_t plaintext_capacity = 0;
379
ssize_t plaintext_length = 0;
382
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
385
/* Create new GPGME data buffer from memory cryptotext */
386
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
388
if(rc != GPG_ERR_NO_ERROR){
389
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
390
gpgme_strsource(rc), gpgme_strerror(rc));
394
/* Create new empty GPGME data buffer for the plaintext */
395
rc = gpgme_data_new(&dh_plain);
396
if(rc != GPG_ERR_NO_ERROR){
397
fprintf_plus(stderr, "Mandos plugin mandos-client: "
398
"bad gpgme_data_new: %s: %s\n",
399
gpgme_strsource(rc), gpgme_strerror(rc));
400
gpgme_data_release(dh_crypto);
404
/* Decrypt data from the cryptotext data buffer to the plaintext
406
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
407
if(rc != GPG_ERR_NO_ERROR){
408
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
409
gpgme_strsource(rc), gpgme_strerror(rc));
410
plaintext_length = -1;
412
gpgme_decrypt_result_t result;
413
result = gpgme_op_decrypt_result(mc->ctx);
415
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
417
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
418
result->unsupported_algorithm);
419
fprintf_plus(stderr, "Wrong key usage: %u\n",
420
result->wrong_key_usage);
421
if(result->file_name != NULL){
422
fprintf_plus(stderr, "File name: %s\n", result->file_name);
424
gpgme_recipient_t recipient;
425
recipient = result->recipients;
426
while(recipient != NULL){
427
fprintf_plus(stderr, "Public key algorithm: %s\n",
428
gpgme_pubkey_algo_name
429
(recipient->pubkey_algo));
430
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
431
fprintf_plus(stderr, "Secret key available: %s\n",
432
recipient->status == GPG_ERR_NO_SECKEY
434
recipient = recipient->next;
442
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
445
/* Seek back to the beginning of the GPGME plaintext data buffer */
446
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
447
perror_plus("gpgme_data_seek");
448
plaintext_length = -1;
454
plaintext_capacity = incbuffer(plaintext,
455
(size_t)plaintext_length,
457
if(plaintext_capacity == 0){
458
perror_plus("incbuffer");
459
plaintext_length = -1;
463
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
465
/* Print the data, if any */
471
perror_plus("gpgme_data_read");
472
plaintext_length = -1;
475
plaintext_length += ret;
479
fprintf_plus(stderr, "Decrypted password is: ");
480
for(ssize_t i = 0; i < plaintext_length; i++){
481
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
483
fprintf(stderr, "\n");
488
/* Delete the GPGME cryptotext data buffer */
489
gpgme_data_release(dh_crypto);
491
/* Delete the GPGME plaintext data buffer */
492
gpgme_data_release(dh_plain);
493
return plaintext_length;
496
__attribute__((warn_unused_result, const))
497
static const char *safe_string(const char *str){
503
__attribute__((warn_unused_result))
504
static const char *safer_gnutls_strerror(int value){
505
const char *ret = gnutls_strerror(value);
506
return safe_string(ret);
509
/* GnuTLS log function callback */
510
__attribute__((nonnull))
511
static void debuggnutls(__attribute__((unused)) int level,
513
fprintf_plus(stderr, "GnuTLS: %s", string);
516
__attribute__((nonnull(1, 2, 4), warn_unused_result))
517
static int init_gnutls_global(const char *pubkeyfilename,
518
const char *seckeyfilename,
519
const char *dhparamsfilename,
525
fprintf_plus(stderr, "Initializing GnuTLS\n");
529
/* "Use a log level over 10 to enable all debugging options."
532
gnutls_global_set_log_level(11);
533
gnutls_global_set_log_function(debuggnutls);
536
/* OpenPGP credentials */
537
ret = gnutls_certificate_allocate_credentials(&mc->cred);
538
if(ret != GNUTLS_E_SUCCESS){
539
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
540
safer_gnutls_strerror(ret));
545
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
546
" secret key %s as GnuTLS credentials\n",
551
ret = gnutls_certificate_set_openpgp_key_file
552
(mc->cred, pubkeyfilename, seckeyfilename,
553
GNUTLS_OPENPGP_FMT_BASE64);
554
if(ret != GNUTLS_E_SUCCESS){
556
"Error[%d] while reading the OpenPGP key pair ('%s',"
557
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
558
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
559
safer_gnutls_strerror(ret));
563
/* GnuTLS server initialization */
564
ret = gnutls_dh_params_init(&mc->dh_params);
565
if(ret != GNUTLS_E_SUCCESS){
566
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
567
" initialization: %s\n",
568
safer_gnutls_strerror(ret));
571
/* If a Diffie-Hellman parameters file was given, try to use it */
572
if(dhparamsfilename != NULL){
573
gnutls_datum_t params = { .data = NULL, .size = 0 };
575
int dhpfile = open(dhparamsfilename, O_RDONLY);
578
dhparamsfilename = NULL;
581
size_t params_capacity = 0;
583
params_capacity = incbuffer((char **)¶ms.data,
585
(size_t)params_capacity);
586
if(params_capacity == 0){
587
perror_plus("incbuffer");
590
dhparamsfilename = NULL;
593
ssize_t bytes_read = read(dhpfile,
594
params.data + params.size,
600
/* check bytes_read for failure */
605
dhparamsfilename = NULL;
608
params.size += (unsigned int)bytes_read;
610
if(params.data == NULL){
611
dhparamsfilename = NULL;
613
if(dhparamsfilename == NULL){
616
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
617
GNUTLS_X509_FMT_PEM);
618
if(ret != GNUTLS_E_SUCCESS){
619
fprintf_plus(stderr, "Failed to parse DH parameters in file"
620
" \"%s\": %s\n", dhparamsfilename,
621
safer_gnutls_strerror(ret));
622
dhparamsfilename = NULL;
626
if(dhparamsfilename == NULL){
627
if(mc->dh_bits == 0){
628
/* Find out the optimal number of DH bits */
629
/* Try to read the private key file */
630
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
632
int secfile = open(seckeyfilename, O_RDONLY);
637
size_t buffer_capacity = 0;
639
buffer_capacity = incbuffer((char **)&buffer.data,
641
(size_t)buffer_capacity);
642
if(buffer_capacity == 0){
643
perror_plus("incbuffer");
648
ssize_t bytes_read = read(secfile,
649
buffer.data + buffer.size,
655
/* check bytes_read for failure */
662
buffer.size += (unsigned int)bytes_read;
666
/* If successful, use buffer to parse private key */
667
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
668
if(buffer.data != NULL){
670
gnutls_openpgp_privkey_t privkey = NULL;
671
ret = gnutls_openpgp_privkey_init(&privkey);
672
if(ret != GNUTLS_E_SUCCESS){
673
fprintf_plus(stderr, "Error initializing OpenPGP key"
675
safer_gnutls_strerror(ret));
679
ret = gnutls_openpgp_privkey_import
680
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
681
if(ret != GNUTLS_E_SUCCESS){
682
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
683
safer_gnutls_strerror(ret));
689
/* Use private key to suggest an appropriate
691
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
692
gnutls_openpgp_privkey_deinit(privkey);
694
fprintf_plus(stderr, "This OpenPGP key implies using"
695
" a GnuTLS security parameter \"%s\".\n",
696
safe_string(gnutls_sec_param_get_name
702
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
703
/* Err on the side of caution */
704
sec_param = GNUTLS_SEC_PARAM_ULTRA;
706
fprintf_plus(stderr, "Falling back to security parameter"
708
safe_string(gnutls_sec_param_get_name
713
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
717
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
718
" implies %u DH bits; using that.\n",
719
safe_string(gnutls_sec_param_get_name
724
fprintf_plus(stderr, "Failed to get implied number of DH"
725
" bits for security parameter \"%s\"): %s\n",
726
safe_string(gnutls_sec_param_get_name
728
safer_gnutls_strerror(ret));
732
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
735
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
736
if(ret != GNUTLS_E_SUCCESS){
737
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
738
" bits): %s\n", mc->dh_bits,
739
safer_gnutls_strerror(ret));
743
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
749
gnutls_certificate_free_credentials(mc->cred);
750
gnutls_dh_params_deinit(mc->dh_params);
754
__attribute__((nonnull, warn_unused_result))
755
static int init_gnutls_session(gnutls_session_t *session,
758
/* GnuTLS session creation */
760
ret = gnutls_init(session, GNUTLS_SERVER);
764
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
765
if(ret != GNUTLS_E_SUCCESS){
767
"Error in GnuTLS session initialization: %s\n",
768
safer_gnutls_strerror(ret));
774
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
776
gnutls_deinit(*session);
779
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
780
if(ret != GNUTLS_E_SUCCESS){
781
fprintf_plus(stderr, "Syntax error at: %s\n", err);
782
fprintf_plus(stderr, "GnuTLS error: %s\n",
783
safer_gnutls_strerror(ret));
784
gnutls_deinit(*session);
790
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
793
gnutls_deinit(*session);
796
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
797
if(ret != GNUTLS_E_SUCCESS){
798
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
799
safer_gnutls_strerror(ret));
800
gnutls_deinit(*session);
804
/* ignore client certificate if any. */
805
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
810
/* Avahi log function callback */
811
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
812
__attribute__((unused)) const char *txt){}
814
/* Set effective uid to 0, return errno */
815
__attribute__((warn_unused_result))
816
error_t raise_privileges(void){
817
error_t old_errno = errno;
818
error_t ret_errno = 0;
819
if(seteuid(0) == -1){
826
/* Set effective and real user ID to 0. Return errno. */
827
__attribute__((warn_unused_result))
828
error_t raise_privileges_permanently(void){
829
error_t old_errno = errno;
830
error_t ret_errno = raise_privileges();
842
/* Set effective user ID to unprivileged saved user ID */
843
__attribute__((warn_unused_result))
844
error_t lower_privileges(void){
845
error_t old_errno = errno;
846
error_t ret_errno = 0;
847
if(seteuid(uid) == -1){
854
/* Lower privileges permanently */
855
__attribute__((warn_unused_result))
856
error_t lower_privileges_permanently(void){
857
error_t old_errno = errno;
858
error_t ret_errno = 0;
859
if(setuid(uid) == -1){
866
/* Helper function to add_local_route() and delete_local_route() */
867
__attribute__((nonnull, warn_unused_result))
868
static bool add_delete_local_route(const bool add,
870
AvahiIfIndex if_index){
872
char helper[] = "mandos-client-iprouteadddel";
873
char add_arg[] = "add";
874
char delete_arg[] = "delete";
875
char debug_flag[] = "--debug";
876
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
877
if(pluginhelperdir == NULL){
879
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
880
" variable not set; cannot run helper\n");
885
char interface[IF_NAMESIZE];
886
if(if_indextoname((unsigned int)if_index, interface) == NULL){
887
perror_plus("if_indextoname");
891
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
893
perror_plus("open(\"/dev/null\", O_RDONLY)");
899
/* Raise privileges */
900
errno = raise_privileges_permanently();
902
perror_plus("Failed to raise privileges");
903
/* _exit(EX_NOPERM); */
909
perror_plus("setgid");
912
/* Reset supplementary groups */
914
ret = setgroups(0, NULL);
916
perror_plus("setgroups");
920
ret = dup2(devnull, STDIN_FILENO);
922
perror_plus("dup2(devnull, STDIN_FILENO)");
925
ret = close(devnull);
927
perror_plus("close");
930
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
932
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
935
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
940
if(helperdir_fd == -1){
942
_exit(EX_UNAVAILABLE);
944
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
947
perror_plus("openat");
949
_exit(EX_UNAVAILABLE);
953
#pragma GCC diagnostic push
954
#pragma GCC diagnostic ignored "-Wcast-qual"
956
if(fexecve(helper_fd, (char *const [])
957
{ helper, add ? add_arg : delete_arg, (char *)address,
958
interface, debug ? debug_flag : NULL, NULL },
961
#pragma GCC diagnostic pop
963
perror_plus("fexecve");
975
pret = waitpid(pid, &status, 0);
976
if(pret == -1 and errno == EINTR and quit_now){
977
int errno_raising = 0;
978
if((errno = raise_privileges()) != 0){
979
errno_raising = errno;
980
perror_plus("Failed to raise privileges in order to"
981
" kill helper program");
983
if(kill(pid, SIGTERM) == -1){
986
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
987
perror_plus("Failed to lower privileges after killing"
992
} while(pret == -1 and errno == EINTR);
994
perror_plus("waitpid");
997
if(WIFEXITED(status)){
998
if(WEXITSTATUS(status) != 0){
999
fprintf_plus(stderr, "Error: iprouteadddel exited"
1000
" with status %d\n", WEXITSTATUS(status));
1005
if(WIFSIGNALED(status)){
1006
fprintf_plus(stderr, "Error: iprouteadddel died by"
1007
" signal %d\n", WTERMSIG(status));
1010
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1014
__attribute__((nonnull, warn_unused_result))
1015
static bool add_local_route(const char *address,
1016
AvahiIfIndex if_index){
1018
fprintf_plus(stderr, "Adding route to %s\n", address);
1020
return add_delete_local_route(true, address, if_index);
1023
__attribute__((nonnull, warn_unused_result))
1024
static bool delete_local_route(const char *address,
1025
AvahiIfIndex if_index){
1027
fprintf_plus(stderr, "Removing route to %s\n", address);
1029
return add_delete_local_route(false, address, if_index);
1032
/* Called when a Mandos server is found */
1033
__attribute__((nonnull, warn_unused_result))
1034
static int start_mandos_communication(const char *ip, in_port_t port,
1035
AvahiIfIndex if_index,
1036
int af, mandos_context *mc){
1037
int ret, tcp_sd = -1;
1039
struct sockaddr_storage to;
1040
char *buffer = NULL;
1041
char *decrypted_buffer = NULL;
1042
size_t buffer_length = 0;
1043
size_t buffer_capacity = 0;
1046
gnutls_session_t session;
1047
int pf; /* Protocol family */
1048
bool route_added = false;
1065
fprintf_plus(stderr, "Bad address family: %d\n", af);
1070
/* If the interface is specified and we have a list of interfaces */
1071
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1072
/* Check if the interface is one of the interfaces we are using */
1075
char *interface = NULL;
1076
while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1078
if(if_nametoindex(interface) == (unsigned int)if_index){
1085
/* This interface does not match any in the list, so we don't
1086
connect to the server */
1088
char interface[IF_NAMESIZE];
1089
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1090
perror_plus("if_indextoname");
1092
fprintf_plus(stderr, "Skipping server on non-used interface"
1094
if_indextoname((unsigned int)if_index,
1102
ret = init_gnutls_session(&session, mc);
1108
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1109
PRIuMAX "\n", ip, (uintmax_t)port);
1112
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1115
perror_plus("socket");
1126
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1127
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1128
ret = inet_pton(af, ip, &to6->sin6_addr);
1130
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1131
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1132
ret = inet_pton(af, ip, &to4->sin_addr);
1136
perror_plus("inet_pton");
1142
fprintf_plus(stderr, "Bad address: %s\n", ip);
1147
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1148
if(IN6_IS_ADDR_LINKLOCAL
1149
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1150
if(if_index == AVAHI_IF_UNSPEC){
1151
fprintf_plus(stderr, "An IPv6 link-local address is"
1152
" incomplete without a network interface\n");
1156
/* Set the network interface number as scope */
1157
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1160
((struct sockaddr_in *)&to)->sin_port = htons(port);
1169
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1170
char interface[IF_NAMESIZE];
1171
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1172
perror_plus("if_indextoname");
1174
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1175
"\n", ip, interface, (uintmax_t)port);
1178
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1179
ip, (uintmax_t)port);
1181
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1182
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1184
ret = getnameinfo((struct sockaddr *)&to,
1185
sizeof(struct sockaddr_in6),
1186
addrstr, sizeof(addrstr), NULL, 0,
1189
ret = getnameinfo((struct sockaddr *)&to,
1190
sizeof(struct sockaddr_in),
1191
addrstr, sizeof(addrstr), NULL, 0,
1194
if(ret == EAI_SYSTEM){
1195
perror_plus("getnameinfo");
1196
} else if(ret != 0) {
1197
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1198
} else if(strcmp(addrstr, ip) != 0){
1199
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1210
ret = connect(tcp_sd, (struct sockaddr *)&to,
1211
sizeof(struct sockaddr_in6));
1213
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1214
sizeof(struct sockaddr_in));
1217
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1218
and if_index != AVAHI_IF_UNSPEC
1219
and connect_to == NULL
1220
and not route_added and
1221
((af == AF_INET6 and not
1222
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1224
or (af == AF_INET and
1225
/* Not a a IPv4LL address */
1226
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1227
& 0xFFFF0000L) != 0xA9FE0000L))){
1228
/* Work around Avahi bug - Avahi does not announce link-local
1229
addresses if it has a global address, so local hosts with
1230
*only* a link-local address (e.g. Mandos clients) cannot
1231
connect to a Mandos server announced by Avahi on a server
1232
host with a global address. Work around this by retrying
1233
with an explicit route added with the server's address.
1235
Avahi bug reference:
1236
http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1237
https://bugs.debian.org/587961
1240
fprintf_plus(stderr, "Mandos server unreachable, trying"
1244
route_added = add_local_route(ip, if_index);
1250
if(errno != ECONNREFUSED or debug){
1252
perror_plus("connect");
1265
const char *out = mandos_protocol_version;
1268
size_t out_size = strlen(out);
1269
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1270
out_size - written));
1273
perror_plus("write");
1277
written += (size_t)ret;
1278
if(written < out_size){
1281
if(out == mandos_protocol_version){
1296
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1304
/* This casting via intptr_t is to eliminate warning about casting
1305
an int to a pointer type. This is exactly how the GnuTLS Guile
1306
function "set-session-transport-fd!" does it. */
1307
gnutls_transport_set_ptr(session,
1308
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1316
ret = gnutls_handshake(session);
1321
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1323
if(ret != GNUTLS_E_SUCCESS){
1325
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1332
/* Read OpenPGP packet that contains the wanted password */
1335
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1346
buffer_capacity = incbuffer(&buffer, buffer_length,
1348
if(buffer_capacity == 0){
1350
perror_plus("incbuffer");
1360
sret = gnutls_record_recv(session, buffer+buffer_length,
1367
case GNUTLS_E_INTERRUPTED:
1368
case GNUTLS_E_AGAIN:
1370
case GNUTLS_E_REHANDSHAKE:
1372
ret = gnutls_handshake(session);
1378
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1380
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
1388
fprintf_plus(stderr, "Unknown error while reading data from"
1389
" encrypted session with Mandos server\n");
1390
gnutls_bye(session, GNUTLS_SHUT_RDWR);
1395
buffer_length += (size_t) sret;
1400
fprintf_plus(stderr, "Closing TLS session\n");
1409
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1414
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1416
if(buffer_length > 0){
1417
ssize_t decrypted_buffer_size;
1418
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1419
&decrypted_buffer, mc);
1420
if(decrypted_buffer_size >= 0){
1423
while(written < (size_t) decrypted_buffer_size){
1429
ret = (int)fwrite(decrypted_buffer + written, 1,
1430
(size_t)decrypted_buffer_size - written,
1432
if(ret == 0 and ferror(stdout)){
1435
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1441
written += (size_t)ret;
1447
/* Shutdown procedure */
1452
if(not delete_local_route(ip, if_index)){
1453
fprintf_plus(stderr, "Failed to delete local route to %s on"
1454
" interface %d", ip, if_index);
1458
free(decrypted_buffer);
1461
ret = close(tcp_sd);
1467
perror_plus("close");
1469
gnutls_deinit(session);
1479
__attribute__((nonnull))
1480
static void resolve_callback(AvahiSServiceResolver *r,
1481
AvahiIfIndex interface,
1482
AvahiProtocol proto,
1483
AvahiResolverEvent event,
1487
const char *host_name,
1488
const AvahiAddress *address,
1490
AVAHI_GCC_UNUSED AvahiStringList *txt,
1491
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1498
/* Called whenever a service has been resolved successfully or
1502
avahi_s_service_resolver_free(r);
1508
case AVAHI_RESOLVER_FAILURE:
1509
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1510
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1512
avahi_strerror(avahi_server_errno
1513
(((mandos_context*)mc)->server)));
1516
case AVAHI_RESOLVER_FOUND:
1518
char ip[AVAHI_ADDRESS_STR_MAX];
1519
avahi_address_snprint(ip, sizeof(ip), address);
1521
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1522
PRIdMAX ") on port %" PRIu16 "\n", name,
1523
host_name, ip, (intmax_t)interface, port);
1525
int ret = start_mandos_communication(ip, (in_port_t)port,
1527
avahi_proto_to_af(proto),
1530
avahi_simple_poll_quit(simple_poll);
1532
if(not add_server(ip, (in_port_t)port, interface,
1533
avahi_proto_to_af(proto),
1534
&((mandos_context*)mc)->current_server)){
1535
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1541
avahi_s_service_resolver_free(r);
1544
static void browse_callback(AvahiSServiceBrowser *b,
1545
AvahiIfIndex interface,
1546
AvahiProtocol protocol,
1547
AvahiBrowserEvent event,
1551
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1558
/* Called whenever a new services becomes available on the LAN or
1559
is removed from the LAN */
1567
case AVAHI_BROWSER_FAILURE:
1569
fprintf_plus(stderr, "(Avahi browser) %s\n",
1570
avahi_strerror(avahi_server_errno
1571
(((mandos_context*)mc)->server)));
1572
avahi_simple_poll_quit(simple_poll);
1575
case AVAHI_BROWSER_NEW:
1576
/* We ignore the returned Avahi resolver object. In the callback
1577
function we free it. If the Avahi server is terminated before
1578
the callback function is called the Avahi server will free the
1581
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1582
interface, protocol, name, type,
1583
domain, protocol, 0,
1584
resolve_callback, mc) == NULL)
1585
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1587
avahi_strerror(avahi_server_errno
1588
(((mandos_context*)mc)->server)));
1591
case AVAHI_BROWSER_REMOVE:
1594
case AVAHI_BROWSER_ALL_FOR_NOW:
1595
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1597
fprintf_plus(stderr, "No Mandos server found, still"
1604
/* Signal handler that stops main loop after SIGTERM */
1605
static void handle_sigterm(int sig){
1610
signal_received = sig;
1611
int old_errno = errno;
1612
/* set main loop to exit */
1613
if(simple_poll != NULL){
1614
avahi_simple_poll_quit(simple_poll);
1619
__attribute__((nonnull, warn_unused_result))
1620
bool get_flags(const char *ifname, struct ifreq *ifr){
1624
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1627
perror_plus("socket");
1631
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1632
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1633
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1637
perror_plus("ioctl SIOCGIFFLAGS");
1645
__attribute__((nonnull, warn_unused_result))
1646
bool good_flags(const char *ifname, const struct ifreq *ifr){
1648
/* Reject the loopback device */
1649
if(ifr->ifr_flags & IFF_LOOPBACK){
1651
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1656
/* Accept point-to-point devices only if connect_to is specified */
1657
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1659
fprintf_plus(stderr, "Accepting point-to-point interface"
1660
" \"%s\"\n", ifname);
1664
/* Otherwise, reject non-broadcast-capable devices */
1665
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1667
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1668
" \"%s\"\n", ifname);
1672
/* Reject non-ARP interfaces (including dummy interfaces) */
1673
if(ifr->ifr_flags & IFF_NOARP){
1675
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1681
/* Accept this device */
1683
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1689
* This function determines if a directory entry in /sys/class/net
1690
* corresponds to an acceptable network device.
1691
* (This function is passed to scandir(3) as a filter function.)
1693
__attribute__((nonnull, warn_unused_result))
1694
int good_interface(const struct dirent *if_entry){
1695
if(if_entry->d_name[0] == '.'){
1700
if(not get_flags(if_entry->d_name, &ifr)){
1702
fprintf_plus(stderr, "Failed to get flags for interface "
1703
"\"%s\"\n", if_entry->d_name);
1708
if(not good_flags(if_entry->d_name, &ifr)){
1715
* This function determines if a network interface is up.
1717
__attribute__((nonnull, warn_unused_result))
1718
bool interface_is_up(const char *interface){
1720
if(not get_flags(interface, &ifr)){
1722
fprintf_plus(stderr, "Failed to get flags for interface "
1723
"\"%s\"\n", interface);
1728
return (bool)(ifr.ifr_flags & IFF_UP);
1732
* This function determines if a network interface is running
1734
__attribute__((nonnull, warn_unused_result))
1735
bool interface_is_running(const char *interface){
1737
if(not get_flags(interface, &ifr)){
1739
fprintf_plus(stderr, "Failed to get flags for interface "
1740
"\"%s\"\n", interface);
1745
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1748
__attribute__((nonnull, pure, warn_unused_result))
1749
int notdotentries(const struct dirent *direntry){
1750
/* Skip "." and ".." */
1751
if(direntry->d_name[0] == '.'
1752
and (direntry->d_name[1] == '\0'
1753
or (direntry->d_name[1] == '.'
1754
and direntry->d_name[2] == '\0'))){
1760
/* Is this directory entry a runnable program? */
1761
__attribute__((nonnull, warn_unused_result))
1762
int runnable_hook(const struct dirent *direntry){
1767
if((direntry->d_name)[0] == '\0'){
1772
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1773
"abcdefghijklmnopqrstuvwxyz"
1776
if((direntry->d_name)[sret] != '\0'){
1777
/* Contains non-allowed characters */
1779
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1785
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1788
perror_plus("Could not stat hook");
1792
if(not (S_ISREG(st.st_mode))){
1793
/* Not a regular file */
1795
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1800
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1801
/* Not executable */
1803
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1809
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1815
__attribute__((nonnull, warn_unused_result))
1816
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1817
mandos_context *mc){
1819
struct timespec now;
1820
struct timespec waited_time;
1821
intmax_t block_time;
1824
if(mc->current_server == NULL){
1826
fprintf_plus(stderr, "Wait until first server is found."
1829
ret = avahi_simple_poll_iterate(s, -1);
1832
fprintf_plus(stderr, "Check current_server if we should run"
1835
/* the current time */
1836
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1838
perror_plus("clock_gettime");
1841
/* Calculating in ms how long time between now and server
1842
who we visted longest time ago. Now - last seen. */
1843
waited_time.tv_sec = (now.tv_sec
1844
- mc->current_server->last_seen.tv_sec);
1845
waited_time.tv_nsec = (now.tv_nsec
1846
- mc->current_server->last_seen.tv_nsec);
1847
/* total time is 10s/10,000ms.
1848
Converting to s from ms by dividing by 1,000,
1849
and ns to ms by dividing by 1,000,000. */
1850
block_time = ((retry_interval
1851
- ((intmax_t)waited_time.tv_sec * 1000))
1852
- ((intmax_t)waited_time.tv_nsec / 1000000));
1855
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1859
if(block_time <= 0){
1860
ret = start_mandos_communication(mc->current_server->ip,
1861
mc->current_server->port,
1862
mc->current_server->if_index,
1863
mc->current_server->af, mc);
1865
avahi_simple_poll_quit(s);
1868
ret = clock_gettime(CLOCK_MONOTONIC,
1869
&mc->current_server->last_seen);
1871
perror_plus("clock_gettime");
1874
mc->current_server = mc->current_server->next;
1875
block_time = 0; /* Call avahi to find new Mandos
1876
servers, but don't block */
1879
ret = avahi_simple_poll_iterate(s, (int)block_time);
1882
if(ret > 0 or errno != EINTR){
1883
return (ret != 1) ? ret : 0;
1889
__attribute__((nonnull))
1890
void run_network_hooks(const char *mode, const char *interface,
1892
struct dirent **direntries = NULL;
1893
if(hookdir_fd == -1){
1894
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1896
if(hookdir_fd == -1){
1897
if(errno == ENOENT){
1899
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1900
" found\n", hookdir);
1903
perror_plus("open");
1908
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1909
runnable_hook, alphasort);
1911
perror_plus("scandir");
1914
struct dirent *direntry;
1916
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1918
perror_plus("open(\"/dev/null\", O_RDONLY)");
1921
for(int i = 0; i < numhooks; i++){
1922
direntry = direntries[i];
1924
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1927
pid_t hook_pid = fork();
1930
/* Raise privileges */
1931
errno = raise_privileges_permanently();
1933
perror_plus("Failed to raise privileges");
1940
perror_plus("setgid");
1943
/* Reset supplementary groups */
1945
ret = setgroups(0, NULL);
1947
perror_plus("setgroups");
1950
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1952
perror_plus("setenv");
1955
ret = setenv("DEVICE", interface, 1);
1957
perror_plus("setenv");
1960
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1962
perror_plus("setenv");
1965
ret = setenv("MODE", mode, 1);
1967
perror_plus("setenv");
1971
ret = asprintf(&delaystring, "%f", (double)delay);
1973
perror_plus("asprintf");
1976
ret = setenv("DELAY", delaystring, 1);
1979
perror_plus("setenv");
1983
if(connect_to != NULL){
1984
ret = setenv("CONNECT", connect_to, 1);
1986
perror_plus("setenv");
1990
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
1994
perror_plus("openat");
1995
_exit(EXIT_FAILURE);
1997
if(close(hookdir_fd) == -1){
1998
perror_plus("close");
1999
_exit(EXIT_FAILURE);
2001
ret = dup2(devnull, STDIN_FILENO);
2003
perror_plus("dup2(devnull, STDIN_FILENO)");
2006
ret = close(devnull);
2008
perror_plus("close");
2011
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2013
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2016
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2018
perror_plus("fexecve");
2019
_exit(EXIT_FAILURE);
2023
perror_plus("fork");
2028
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2029
perror_plus("waitpid");
2033
if(WIFEXITED(status)){
2034
if(WEXITSTATUS(status) != 0){
2035
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2036
" with status %d\n", direntry->d_name,
2037
WEXITSTATUS(status));
2041
} else if(WIFSIGNALED(status)){
2042
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2043
" signal %d\n", direntry->d_name,
2048
fprintf_plus(stderr, "Warning: network hook \"%s\""
2049
" crashed\n", direntry->d_name);
2055
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2061
if(close(hookdir_fd) == -1){
2062
perror_plus("close");
2069
__attribute__((nonnull, warn_unused_result))
2070
error_t bring_up_interface(const char *const interface,
2072
error_t old_errno = errno;
2074
struct ifreq network;
2075
unsigned int if_index = if_nametoindex(interface);
2077
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2087
if(not interface_is_up(interface)){
2088
error_t ret_errno = 0, ioctl_errno = 0;
2089
if(not get_flags(interface, &network)){
2091
fprintf_plus(stderr, "Failed to get flags for interface "
2092
"\"%s\"\n", interface);
2096
network.ifr_flags |= IFF_UP; /* set flag */
2098
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2101
perror_plus("socket");
2109
perror_plus("close");
2116
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2120
/* Raise privileges */
2121
ret_errno = raise_privileges();
2124
perror_plus("Failed to raise privileges");
2129
bool restore_loglevel = false;
2131
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2132
messages about the network interface to mess up the prompt */
2133
ret_linux = klogctl(8, NULL, 5);
2134
if(ret_linux == -1){
2135
perror_plus("klogctl");
2137
restore_loglevel = true;
2140
#endif /* __linux__ */
2141
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2142
ioctl_errno = errno;
2144
if(restore_loglevel){
2145
ret_linux = klogctl(7, NULL, 0);
2146
if(ret_linux == -1){
2147
perror_plus("klogctl");
2150
#endif /* __linux__ */
2152
/* If raise_privileges() succeeded above */
2154
/* Lower privileges */
2155
ret_errno = lower_privileges();
2158
perror_plus("Failed to lower privileges");
2162
/* Close the socket */
2165
perror_plus("close");
2168
if(ret_setflags == -1){
2169
errno = ioctl_errno;
2170
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2175
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2179
/* Sleep checking until interface is running.
2180
Check every 0.25s, up to total time of delay */
2181
for(int i=0; i < delay * 4; i++){
2182
if(interface_is_running(interface)){
2185
struct timespec sleeptime = { .tv_nsec = 250000000 };
2186
ret = nanosleep(&sleeptime, NULL);
2187
if(ret == -1 and errno != EINTR){
2188
perror_plus("nanosleep");
2196
__attribute__((nonnull, warn_unused_result))
2197
error_t take_down_interface(const char *const interface){
2198
error_t old_errno = errno;
2199
struct ifreq network;
2200
unsigned int if_index = if_nametoindex(interface);
2202
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2206
if(interface_is_up(interface)){
2207
error_t ret_errno = 0, ioctl_errno = 0;
2208
if(not get_flags(interface, &network) and debug){
2210
fprintf_plus(stderr, "Failed to get flags for interface "
2211
"\"%s\"\n", interface);
2215
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2217
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2220
perror_plus("socket");
2226
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2230
/* Raise privileges */
2231
ret_errno = raise_privileges();
2234
perror_plus("Failed to raise privileges");
2237
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2238
ioctl_errno = errno;
2240
/* If raise_privileges() succeeded above */
2242
/* Lower privileges */
2243
ret_errno = lower_privileges();
2246
perror_plus("Failed to lower privileges");
2250
/* Close the socket */
2251
int ret = close(sd);
2253
perror_plus("close");
2256
if(ret_setflags == -1){
2257
errno = ioctl_errno;
2258
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2263
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2271
int main(int argc, char *argv[]){
2272
mandos_context mc = { .server = NULL, .dh_bits = 0,
2273
.priority = "SECURE256:!CTYPE-X.509"
2274
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2275
.current_server = NULL, .interfaces = NULL,
2276
.interfaces_size = 0 };
2277
AvahiSServiceBrowser *sb = NULL;
2282
int exitcode = EXIT_SUCCESS;
2283
char *interfaces_to_take_down = NULL;
2284
size_t interfaces_to_take_down_size = 0;
2285
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2286
char old_tempdir[] = "/tmp/mandosXXXXXX";
2287
char *tempdir = NULL;
2288
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2289
const char *seckey = PATHDIR "/" SECKEY;
2290
const char *pubkey = PATHDIR "/" PUBKEY;
2291
const char *dh_params_file = NULL;
2292
char *interfaces_hooks = NULL;
2294
bool gnutls_initialized = false;
2295
bool gpgme_initialized = false;
2297
double retry_interval = 10; /* 10s between trying a server and
2298
retrying the same server again */
2300
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2301
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2306
/* Lower any group privileges we might have, just to be safe */
2310
perror_plus("setgid");
2313
/* Lower user privileges (temporarily) */
2317
perror_plus("seteuid");
2325
struct argp_option options[] = {
2326
{ .name = "debug", .key = 128,
2327
.doc = "Debug mode", .group = 3 },
2328
{ .name = "connect", .key = 'c',
2329
.arg = "ADDRESS:PORT",
2330
.doc = "Connect directly to a specific Mandos server",
2332
{ .name = "interface", .key = 'i',
2334
.doc = "Network interface that will be used to search for"
2337
{ .name = "seckey", .key = 's',
2339
.doc = "OpenPGP secret key file base name",
2341
{ .name = "pubkey", .key = 'p',
2343
.doc = "OpenPGP public key file base name",
2345
{ .name = "dh-bits", .key = 129,
2347
.doc = "Bit length of the prime number used in the"
2348
" Diffie-Hellman key exchange",
2350
{ .name = "dh-params", .key = 134,
2352
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2353
" for the Diffie-Hellman key exchange",
2355
{ .name = "priority", .key = 130,
2357
.doc = "GnuTLS priority string for the TLS handshake",
2359
{ .name = "delay", .key = 131,
2361
.doc = "Maximum delay to wait for interface startup",
2363
{ .name = "retry", .key = 132,
2365
.doc = "Retry interval used when denied by the Mandos server",
2367
{ .name = "network-hook-dir", .key = 133,
2369
.doc = "Directory where network hooks are located",
2372
* These reproduce what we would get without ARGP_NO_HELP
2374
{ .name = "help", .key = '?',
2375
.doc = "Give this help list", .group = -1 },
2376
{ .name = "usage", .key = -3,
2377
.doc = "Give a short usage message", .group = -1 },
2378
{ .name = "version", .key = 'V',
2379
.doc = "Print program version", .group = -1 },
2383
error_t parse_opt(int key, char *arg,
2384
struct argp_state *state){
2387
case 128: /* --debug */
2390
case 'c': /* --connect */
2393
case 'i': /* --interface */
2394
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2397
argp_error(state, "%s", strerror(ret_errno));
2400
case 's': /* --seckey */
2403
case 'p': /* --pubkey */
2406
case 129: /* --dh-bits */
2408
tmpmax = strtoimax(arg, &tmp, 10);
2409
if(errno != 0 or tmp == arg or *tmp != '\0'
2410
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2411
argp_error(state, "Bad number of DH bits");
2413
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2415
case 134: /* --dh-params */
2416
dh_params_file = arg;
2418
case 130: /* --priority */
2421
case 131: /* --delay */
2423
delay = strtof(arg, &tmp);
2424
if(errno != 0 or tmp == arg or *tmp != '\0'){
2425
argp_error(state, "Bad delay");
2427
case 132: /* --retry */
2429
retry_interval = strtod(arg, &tmp);
2430
if(errno != 0 or tmp == arg or *tmp != '\0'
2431
or (retry_interval * 1000) > INT_MAX
2432
or retry_interval < 0){
2433
argp_error(state, "Bad retry interval");
2436
case 133: /* --network-hook-dir */
2440
* These reproduce what we would get without ARGP_NO_HELP
2442
case '?': /* --help */
2443
argp_state_help(state, state->out_stream,
2444
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2445
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2446
case -3: /* --usage */
2447
argp_state_help(state, state->out_stream,
2448
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2449
case 'V': /* --version */
2450
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2451
exit(argp_err_exit_status);
2454
return ARGP_ERR_UNKNOWN;
2459
struct argp argp = { .options = options, .parser = parse_opt,
2461
.doc = "Mandos client -- Get and decrypt"
2462
" passwords from a Mandos server" };
2463
ret = argp_parse(&argp, argc, argv,
2464
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2471
perror_plus("argp_parse");
2472
exitcode = EX_OSERR;
2475
exitcode = EX_USAGE;
2481
/* Work around Debian bug #633582:
2482
<http://bugs.debian.org/633582> */
2484
/* Re-raise privileges */
2485
ret_errno = raise_privileges();
2488
perror_plus("Failed to raise privileges");
2492
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2493
int seckey_fd = open(seckey, O_RDONLY);
2494
if(seckey_fd == -1){
2495
perror_plus("open");
2497
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2499
perror_plus("fstat");
2501
if(S_ISREG(st.st_mode)
2502
and st.st_uid == 0 and st.st_gid == 0){
2503
ret = fchown(seckey_fd, uid, gid);
2505
perror_plus("fchown");
2513
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2514
int pubkey_fd = open(pubkey, O_RDONLY);
2515
if(pubkey_fd == -1){
2516
perror_plus("open");
2518
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2520
perror_plus("fstat");
2522
if(S_ISREG(st.st_mode)
2523
and st.st_uid == 0 and st.st_gid == 0){
2524
ret = fchown(pubkey_fd, uid, gid);
2526
perror_plus("fchown");
2534
if(dh_params_file != NULL
2535
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2536
int dhparams_fd = open(dh_params_file, O_RDONLY);
2537
if(dhparams_fd == -1){
2538
perror_plus("open");
2540
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2542
perror_plus("fstat");
2544
if(S_ISREG(st.st_mode)
2545
and st.st_uid == 0 and st.st_gid == 0){
2546
ret = fchown(dhparams_fd, uid, gid);
2548
perror_plus("fchown");
2556
/* Lower privileges */
2557
ret_errno = lower_privileges();
2560
perror_plus("Failed to lower privileges");
2565
/* Remove invalid interface names (except "none") */
2567
char *interface = NULL;
2568
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2570
if(strcmp(interface, "none") != 0
2571
and if_nametoindex(interface) == 0){
2572
if(interface[0] != '\0'){
2573
fprintf_plus(stderr, "Not using nonexisting interface"
2574
" \"%s\"\n", interface);
2576
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2582
/* Run network hooks */
2584
if(mc.interfaces != NULL){
2585
interfaces_hooks = malloc(mc.interfaces_size);
2586
if(interfaces_hooks == NULL){
2587
perror_plus("malloc");
2590
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2591
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2593
run_network_hooks("start", interfaces_hooks != NULL ?
2594
interfaces_hooks : "", delay);
2598
avahi_set_log_function(empty_log);
2601
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2602
from the signal handler */
2603
/* Initialize the pseudo-RNG for Avahi */
2604
srand((unsigned int) time(NULL));
2605
simple_poll = avahi_simple_poll_new();
2606
if(simple_poll == NULL){
2607
fprintf_plus(stderr,
2608
"Avahi: Failed to create simple poll object.\n");
2609
exitcode = EX_UNAVAILABLE;
2613
sigemptyset(&sigterm_action.sa_mask);
2614
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2616
perror_plus("sigaddset");
2617
exitcode = EX_OSERR;
2620
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2622
perror_plus("sigaddset");
2623
exitcode = EX_OSERR;
2626
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2628
perror_plus("sigaddset");
2629
exitcode = EX_OSERR;
2632
/* Need to check if the handler is SIG_IGN before handling:
2633
| [[info:libc:Initial Signal Actions]] |
2634
| [[info:libc:Basic Signal Handling]] |
2636
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2638
perror_plus("sigaction");
2641
if(old_sigterm_action.sa_handler != SIG_IGN){
2642
ret = sigaction(SIGINT, &sigterm_action, NULL);
2644
perror_plus("sigaction");
2645
exitcode = EX_OSERR;
2649
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2651
perror_plus("sigaction");
2654
if(old_sigterm_action.sa_handler != SIG_IGN){
2655
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2657
perror_plus("sigaction");
2658
exitcode = EX_OSERR;
2662
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2664
perror_plus("sigaction");
2667
if(old_sigterm_action.sa_handler != SIG_IGN){
2668
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2670
perror_plus("sigaction");
2671
exitcode = EX_OSERR;
2676
/* If no interfaces were specified, make a list */
2677
if(mc.interfaces == NULL){
2678
struct dirent **direntries = NULL;
2679
/* Look for any good interfaces */
2680
ret = scandir(sys_class_net, &direntries, good_interface,
2683
/* Add all found interfaces to interfaces list */
2684
for(int i = 0; i < ret; ++i){
2685
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2686
direntries[i]->d_name);
2689
perror_plus("argz_add");
2690
free(direntries[i]);
2694
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2695
direntries[i]->d_name);
2697
free(direntries[i]);
2704
fprintf_plus(stderr, "Could not find a network interface\n");
2705
exitcode = EXIT_FAILURE;
2710
/* Bring up interfaces which are down, and remove any "none"s */
2712
char *interface = NULL;
2713
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2715
/* If interface name is "none", stop bringing up interfaces.
2716
Also remove all instances of "none" from the list */
2717
if(strcmp(interface, "none") == 0){
2718
argz_delete(&mc.interfaces, &mc.interfaces_size,
2721
while((interface = argz_next(mc.interfaces,
2722
mc.interfaces_size, interface))){
2723
if(strcmp(interface, "none") == 0){
2724
argz_delete(&mc.interfaces, &mc.interfaces_size,
2731
bool interface_was_up = interface_is_up(interface);
2732
errno = bring_up_interface(interface, delay);
2733
if(not interface_was_up){
2735
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2736
" %s\n", interface, strerror(errno));
2738
errno = argz_add(&interfaces_to_take_down,
2739
&interfaces_to_take_down_size,
2742
perror_plus("argz_add");
2747
if(debug and (interfaces_to_take_down == NULL)){
2748
fprintf_plus(stderr, "No interfaces were brought up\n");
2752
/* If we only got one interface, explicitly use only that one */
2753
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2755
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2758
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2765
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2767
fprintf_plus(stderr, "init_gnutls_global failed\n");
2768
exitcode = EX_UNAVAILABLE;
2771
gnutls_initialized = true;
2778
/* Try /run/tmp before /tmp */
2779
tempdir = mkdtemp(run_tempdir);
2780
if(tempdir == NULL and errno == ENOENT){
2782
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2783
run_tempdir, old_tempdir);
2785
tempdir = mkdtemp(old_tempdir);
2787
if(tempdir == NULL){
2788
perror_plus("mkdtemp");
2796
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2797
fprintf_plus(stderr, "init_gpgme failed\n");
2798
exitcode = EX_UNAVAILABLE;
2801
gpgme_initialized = true;
2808
if(connect_to != NULL){
2809
/* Connect directly, do not use Zeroconf */
2810
/* (Mainly meant for debugging) */
2811
char *address = strrchr(connect_to, ':');
2813
if(address == NULL){
2814
fprintf_plus(stderr, "No colon in address\n");
2815
exitcode = EX_USAGE;
2825
tmpmax = strtoimax(address+1, &tmp, 10);
2826
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2827
or tmpmax != (in_port_t)tmpmax){
2828
fprintf_plus(stderr, "Bad port number\n");
2829
exitcode = EX_USAGE;
2837
port = (in_port_t)tmpmax;
2839
/* Colon in address indicates IPv6 */
2841
if(strchr(connect_to, ':') != NULL){
2843
/* Accept [] around IPv6 address - see RFC 5952 */
2844
if(connect_to[0] == '[' and address[-1] == ']')
2852
address = connect_to;
2858
while(not quit_now){
2859
ret = start_mandos_communication(address, port, if_index, af,
2861
if(quit_now or ret == 0){
2865
fprintf_plus(stderr, "Retrying in %d seconds\n",
2866
(int)retry_interval);
2868
sleep((unsigned int)retry_interval);
2872
exitcode = EXIT_SUCCESS;
2883
AvahiServerConfig config;
2884
/* Do not publish any local Zeroconf records */
2885
avahi_server_config_init(&config);
2886
config.publish_hinfo = 0;
2887
config.publish_addresses = 0;
2888
config.publish_workstation = 0;
2889
config.publish_domain = 0;
2891
/* Allocate a new server */
2892
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2893
&config, NULL, NULL, &ret_errno);
2895
/* Free the Avahi configuration data */
2896
avahi_server_config_free(&config);
2899
/* Check if creating the Avahi server object succeeded */
2900
if(mc.server == NULL){
2901
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2902
avahi_strerror(ret_errno));
2903
exitcode = EX_UNAVAILABLE;
2911
/* Create the Avahi service browser */
2912
sb = avahi_s_service_browser_new(mc.server, if_index,
2913
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2914
NULL, 0, browse_callback,
2917
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2918
avahi_strerror(avahi_server_errno(mc.server)));
2919
exitcode = EX_UNAVAILABLE;
2927
/* Run the main loop */
2930
fprintf_plus(stderr, "Starting Avahi loop search\n");
2933
ret = avahi_loop_with_timeout(simple_poll,
2934
(int)(retry_interval * 1000), &mc);
2936
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2937
(ret == 0) ? "successfully" : "with error");
2943
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2946
/* Cleanup things */
2947
free(mc.interfaces);
2950
avahi_s_service_browser_free(sb);
2952
if(mc.server != NULL)
2953
avahi_server_free(mc.server);
2955
if(simple_poll != NULL)
2956
avahi_simple_poll_free(simple_poll);
2958
if(gnutls_initialized){
2959
gnutls_certificate_free_credentials(mc.cred);
2960
gnutls_dh_params_deinit(mc.dh_params);
2963
if(gpgme_initialized){
2964
gpgme_release(mc.ctx);
2967
/* Cleans up the circular linked list of Mandos servers the client
2969
if(mc.current_server != NULL){
2970
mc.current_server->prev->next = NULL;
2971
while(mc.current_server != NULL){
2972
server *next = mc.current_server->next;
2974
#pragma GCC diagnostic push
2975
#pragma GCC diagnostic ignored "-Wcast-qual"
2977
free((char *)(mc.current_server->ip));
2979
#pragma GCC diagnostic pop
2981
free(mc.current_server);
2982
mc.current_server = next;
2986
/* Re-raise privileges */
2988
ret_errno = raise_privileges();
2991
perror_plus("Failed to raise privileges");
2994
/* Run network hooks */
2995
run_network_hooks("stop", interfaces_hooks != NULL ?
2996
interfaces_hooks : "", delay);
2998
/* Take down the network interfaces which were brought up */
3000
char *interface = NULL;
3001
while((interface=argz_next(interfaces_to_take_down,
3002
interfaces_to_take_down_size,
3004
ret_errno = take_down_interface(interface);
3007
perror_plus("Failed to take down interface");
3010
if(debug and (interfaces_to_take_down == NULL)){
3011
fprintf_plus(stderr, "No interfaces needed to be taken"
3017
ret_errno = lower_privileges_permanently();
3020
perror_plus("Failed to lower privileges permanently");
3024
free(interfaces_to_take_down);
3025
free(interfaces_hooks);
3027
/* Removes the GPGME temp directory and all files inside */
3028
if(tempdir != NULL){
3029
struct dirent **direntries = NULL;
3030
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
3034
if(tempdir_fd == -1){
3035
perror_plus("open");
3037
int numentries = scandirat(tempdir_fd, ".", &direntries,
3038
notdotentries, alphasort);
3039
if(numentries >= 0){
3040
for(int i = 0; i < numentries; i++){
3041
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
3043
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
3044
" \"%s\", 0): %s\n", tempdir,
3045
direntries[i]->d_name, strerror(errno));
3047
free(direntries[i]);
3050
/* need to clean even if 0 because man page doesn't specify */
3052
if(numentries == -1){
3053
perror_plus("scandir");
3055
ret = rmdir(tempdir);
3056
if(ret == -1 and errno != ENOENT){
3057
perror_plus("rmdir");
3065
sigemptyset(&old_sigterm_action.sa_mask);
3066
old_sigterm_action.sa_handler = SIG_DFL;
3067
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3068
&old_sigterm_action,
3071
perror_plus("sigaction");
3074
ret = raise(signal_received);
3075
} while(ret != 0 and errno == EINTR);
3077
perror_plus("raise");
3080
TEMP_FAILURE_RETRY(pause());