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-2015 Teddy Hogeborn
13
* Copyright © 2008-2015 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(), strcpy() */
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, 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");
528
ret = gnutls_global_init();
529
if(ret != GNUTLS_E_SUCCESS){
530
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
531
safer_gnutls_strerror(ret));
536
/* "Use a log level over 10 to enable all debugging options."
539
gnutls_global_set_log_level(11);
540
gnutls_global_set_log_function(debuggnutls);
543
/* OpenPGP credentials */
544
ret = gnutls_certificate_allocate_credentials(&mc->cred);
545
if(ret != GNUTLS_E_SUCCESS){
546
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
547
safer_gnutls_strerror(ret));
548
gnutls_global_deinit();
553
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
554
" secret key %s as GnuTLS credentials\n",
559
ret = gnutls_certificate_set_openpgp_key_file
560
(mc->cred, pubkeyfilename, seckeyfilename,
561
GNUTLS_OPENPGP_FMT_BASE64);
562
if(ret != GNUTLS_E_SUCCESS){
564
"Error[%d] while reading the OpenPGP key pair ('%s',"
565
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
566
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
567
safer_gnutls_strerror(ret));
571
/* GnuTLS server initialization */
572
ret = gnutls_dh_params_init(&mc->dh_params);
573
if(ret != GNUTLS_E_SUCCESS){
574
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
575
" initialization: %s\n",
576
safer_gnutls_strerror(ret));
579
/* If a Diffie-Hellman parameters file was given, try to use it */
580
if(dhparamsfilename != NULL){
581
gnutls_datum_t params = { .data = NULL, .size = 0 };
583
int dhpfile = open(dhparamsfilename, O_RDONLY);
586
dhparamsfilename = NULL;
589
size_t params_capacity = 0;
591
params_capacity = incbuffer((char **)¶ms.data,
593
(size_t)params_capacity);
594
if(params_capacity == 0){
595
perror_plus("incbuffer");
598
dhparamsfilename = NULL;
601
ssize_t bytes_read = read(dhpfile,
602
params.data + params.size,
608
/* check bytes_read for failure */
613
dhparamsfilename = NULL;
616
params.size += (unsigned int)bytes_read;
618
if(params.data == NULL){
619
dhparamsfilename = NULL;
621
if(dhparamsfilename == NULL){
624
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
625
GNUTLS_X509_FMT_PEM);
626
if(ret != GNUTLS_E_SUCCESS){
627
fprintf_plus(stderr, "Failed to parse DH parameters in file"
628
" \"%s\": %s\n", dhparamsfilename,
629
safer_gnutls_strerror(ret));
630
dhparamsfilename = NULL;
634
if(dhparamsfilename == NULL){
635
if(mc->dh_bits == 0){
636
/* Find out the optimal number of DH bits */
637
/* Try to read the private key file */
638
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
640
int secfile = open(seckeyfilename, O_RDONLY);
645
size_t buffer_capacity = 0;
647
buffer_capacity = incbuffer((char **)&buffer.data,
649
(size_t)buffer_capacity);
650
if(buffer_capacity == 0){
651
perror_plus("incbuffer");
656
ssize_t bytes_read = read(secfile,
657
buffer.data + buffer.size,
663
/* check bytes_read for failure */
670
buffer.size += (unsigned int)bytes_read;
674
/* If successful, use buffer to parse private key */
675
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
676
if(buffer.data != NULL){
678
gnutls_openpgp_privkey_t privkey = NULL;
679
ret = gnutls_openpgp_privkey_init(&privkey);
680
if(ret != GNUTLS_E_SUCCESS){
681
fprintf_plus(stderr, "Error initializing OpenPGP key"
683
safer_gnutls_strerror(ret));
687
ret = gnutls_openpgp_privkey_import
688
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
689
if(ret != GNUTLS_E_SUCCESS){
690
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
691
safer_gnutls_strerror(ret));
697
/* Use private key to suggest an appropriate
699
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
700
gnutls_openpgp_privkey_deinit(privkey);
702
fprintf_plus(stderr, "This OpenPGP key implies using"
703
" a GnuTLS security parameter \"%s\".\n",
704
safe_string(gnutls_sec_param_get_name
710
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
711
/* Err on the side of caution */
712
sec_param = GNUTLS_SEC_PARAM_ULTRA;
714
fprintf_plus(stderr, "Falling back to security parameter"
716
safe_string(gnutls_sec_param_get_name
721
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
725
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
726
" implies %u DH bits; using that.\n",
727
safe_string(gnutls_sec_param_get_name
732
fprintf_plus(stderr, "Failed to get implied number of DH"
733
" bits for security parameter \"%s\"): %s\n",
734
safe_string(gnutls_sec_param_get_name
736
safer_gnutls_strerror(ret));
740
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
743
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
744
if(ret != GNUTLS_E_SUCCESS){
745
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
746
" bits): %s\n", mc->dh_bits,
747
safer_gnutls_strerror(ret));
751
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
757
gnutls_certificate_free_credentials(mc->cred);
758
gnutls_global_deinit();
759
gnutls_dh_params_deinit(mc->dh_params);
763
__attribute__((nonnull, warn_unused_result))
764
static int init_gnutls_session(gnutls_session_t *session,
767
/* GnuTLS session creation */
769
ret = gnutls_init(session, GNUTLS_SERVER);
773
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
774
if(ret != GNUTLS_E_SUCCESS){
776
"Error in GnuTLS session initialization: %s\n",
777
safer_gnutls_strerror(ret));
783
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
785
gnutls_deinit(*session);
788
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
789
if(ret != GNUTLS_E_SUCCESS){
790
fprintf_plus(stderr, "Syntax error at: %s\n", err);
791
fprintf_plus(stderr, "GnuTLS error: %s\n",
792
safer_gnutls_strerror(ret));
793
gnutls_deinit(*session);
799
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
802
gnutls_deinit(*session);
805
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
806
if(ret != GNUTLS_E_SUCCESS){
807
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
808
safer_gnutls_strerror(ret));
809
gnutls_deinit(*session);
813
/* ignore client certificate if any. */
814
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
819
/* Avahi log function callback */
820
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
821
__attribute__((unused)) const char *txt){}
823
/* Set effective uid to 0, return errno */
824
__attribute__((warn_unused_result))
825
error_t raise_privileges(void){
826
error_t old_errno = errno;
827
error_t ret_errno = 0;
828
if(seteuid(0) == -1){
835
/* Set effective and real user ID to 0. Return errno. */
836
__attribute__((warn_unused_result))
837
error_t raise_privileges_permanently(void){
838
error_t old_errno = errno;
839
error_t ret_errno = raise_privileges();
851
/* Set effective user ID to unprivileged saved user ID */
852
__attribute__((warn_unused_result))
853
error_t lower_privileges(void){
854
error_t old_errno = errno;
855
error_t ret_errno = 0;
856
if(seteuid(uid) == -1){
863
/* Lower privileges permanently */
864
__attribute__((warn_unused_result))
865
error_t lower_privileges_permanently(void){
866
error_t old_errno = errno;
867
error_t ret_errno = 0;
868
if(setuid(uid) == -1){
875
/* Helper function to add_local_route() and delete_local_route() */
876
__attribute__((nonnull, warn_unused_result))
877
static bool add_delete_local_route(const bool add,
879
AvahiIfIndex if_index){
881
char helper[] = "mandos-client-iprouteadddel";
882
char add_arg[] = "add";
883
char delete_arg[] = "delete";
884
char debug_flag[] = "--debug";
885
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
886
if(pluginhelperdir == NULL){
888
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
889
" variable not set; cannot run helper\n");
894
char interface[IF_NAMESIZE];
895
if(if_indextoname((unsigned int)if_index, interface) == NULL){
896
perror_plus("if_indextoname");
900
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
902
perror_plus("open(\"/dev/null\", O_RDONLY)");
908
/* Raise privileges */
909
errno = raise_privileges_permanently();
911
perror_plus("Failed to raise privileges");
912
/* _exit(EX_NOPERM); */
918
perror_plus("setgid");
921
/* Reset supplementary groups */
923
ret = setgroups(0, NULL);
925
perror_plus("setgroups");
929
ret = dup2(devnull, STDIN_FILENO);
931
perror_plus("dup2(devnull, STDIN_FILENO)");
934
ret = close(devnull);
936
perror_plus("close");
939
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
941
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
944
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
949
if(helperdir_fd == -1){
951
_exit(EX_UNAVAILABLE);
953
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
956
perror_plus("openat");
957
_exit(EX_UNAVAILABLE);
961
#pragma GCC diagnostic push
962
#pragma GCC diagnostic ignored "-Wcast-qual"
964
if(fexecve(helper_fd, (char *const [])
965
{ helper, add ? add_arg : delete_arg, (char *)address,
966
interface, debug ? debug_flag : NULL, NULL },
969
#pragma GCC diagnostic pop
971
perror_plus("fexecve");
983
pret = waitpid(pid, &status, 0);
984
if(pret == -1 and errno == EINTR and quit_now){
985
int errno_raising = 0;
986
if((errno = raise_privileges()) != 0){
987
errno_raising = errno;
988
perror_plus("Failed to raise privileges in order to"
989
" kill helper program");
991
if(kill(pid, SIGTERM) == -1){
994
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
995
perror_plus("Failed to lower privileges after killing"
1000
} while(pret == -1 and errno == EINTR);
1002
perror_plus("waitpid");
1005
if(WIFEXITED(status)){
1006
if(WEXITSTATUS(status) != 0){
1007
fprintf_plus(stderr, "Error: iprouteadddel exited"
1008
" with status %d\n", WEXITSTATUS(status));
1013
if(WIFSIGNALED(status)){
1014
fprintf_plus(stderr, "Error: iprouteadddel died by"
1015
" signal %d\n", WTERMSIG(status));
1018
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1022
__attribute__((nonnull, warn_unused_result))
1023
static bool add_local_route(const char *address,
1024
AvahiIfIndex if_index){
1026
fprintf_plus(stderr, "Adding route to %s\n", address);
1028
return add_delete_local_route(true, address, if_index);
1031
__attribute__((nonnull, warn_unused_result))
1032
static bool delete_local_route(const char *address,
1033
AvahiIfIndex if_index){
1035
fprintf_plus(stderr, "Removing route to %s\n", address);
1037
return add_delete_local_route(false, address, if_index);
1040
/* Called when a Mandos server is found */
1041
__attribute__((nonnull, warn_unused_result))
1042
static int start_mandos_communication(const char *ip, in_port_t port,
1043
AvahiIfIndex if_index,
1044
int af, mandos_context *mc){
1045
int ret, tcp_sd = -1;
1047
struct sockaddr_storage to;
1048
char *buffer = NULL;
1049
char *decrypted_buffer = NULL;
1050
size_t buffer_length = 0;
1051
size_t buffer_capacity = 0;
1054
gnutls_session_t session;
1055
int pf; /* Protocol family */
1056
bool route_added = false;
1073
fprintf_plus(stderr, "Bad address family: %d\n", af);
1078
/* If the interface is specified and we have a list of interfaces */
1079
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1080
/* Check if the interface is one of the interfaces we are using */
1083
char *interface = NULL;
1084
while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1086
if(if_nametoindex(interface) == (unsigned int)if_index){
1093
/* This interface does not match any in the list, so we don't
1094
connect to the server */
1096
char interface[IF_NAMESIZE];
1097
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1098
perror_plus("if_indextoname");
1100
fprintf_plus(stderr, "Skipping server on non-used interface"
1102
if_indextoname((unsigned int)if_index,
1110
ret = init_gnutls_session(&session, mc);
1116
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1117
PRIuMAX "\n", ip, (uintmax_t)port);
1120
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1123
perror_plus("socket");
1134
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1135
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1136
ret = inet_pton(af, ip, &to6->sin6_addr);
1138
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1139
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1140
ret = inet_pton(af, ip, &to4->sin_addr);
1144
perror_plus("inet_pton");
1150
fprintf_plus(stderr, "Bad address: %s\n", ip);
1155
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1156
if(IN6_IS_ADDR_LINKLOCAL
1157
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1158
if(if_index == AVAHI_IF_UNSPEC){
1159
fprintf_plus(stderr, "An IPv6 link-local address is"
1160
" incomplete without a network interface\n");
1164
/* Set the network interface number as scope */
1165
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1168
((struct sockaddr_in *)&to)->sin_port = htons(port);
1177
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1178
char interface[IF_NAMESIZE];
1179
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1180
perror_plus("if_indextoname");
1182
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1183
"\n", ip, interface, (uintmax_t)port);
1186
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1187
ip, (uintmax_t)port);
1189
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1190
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1192
ret = getnameinfo((struct sockaddr *)&to,
1193
sizeof(struct sockaddr_in6),
1194
addrstr, sizeof(addrstr), NULL, 0,
1197
ret = getnameinfo((struct sockaddr *)&to,
1198
sizeof(struct sockaddr_in),
1199
addrstr, sizeof(addrstr), NULL, 0,
1202
if(ret == EAI_SYSTEM){
1203
perror_plus("getnameinfo");
1204
} else if(ret != 0) {
1205
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1206
} else if(strcmp(addrstr, ip) != 0){
1207
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1218
ret = connect(tcp_sd, (struct sockaddr *)&to,
1219
sizeof(struct sockaddr_in6));
1221
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1222
sizeof(struct sockaddr_in));
1225
if(errno == ENETUNREACH
1226
and if_index != AVAHI_IF_UNSPEC
1227
and connect_to == NULL
1228
and not route_added and
1229
((af == AF_INET6 and not
1230
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1232
or (af == AF_INET and
1233
/* Not a a IPv4LL address */
1234
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1235
& 0xFFFF0000L) != 0xA9FE0000L))){
1236
/* Work around Avahi bug - Avahi does not announce link-local
1237
addresses if it has a global address, so local hosts with
1238
*only* a link-local address (e.g. Mandos clients) cannot
1239
connect to a Mandos server announced by Avahi on a server
1240
host with a global address. Work around this by retrying
1241
with an explicit route added with the server's address.
1243
Avahi bug reference:
1244
http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1245
https://bugs.debian.org/587961
1248
fprintf_plus(stderr, "Mandos server unreachable, trying"
1252
route_added = add_local_route(ip, if_index);
1258
if(errno != ECONNREFUSED or debug){
1260
perror_plus("connect");
1273
const char *out = mandos_protocol_version;
1276
size_t out_size = strlen(out);
1277
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1278
out_size - written));
1281
perror_plus("write");
1285
written += (size_t)ret;
1286
if(written < out_size){
1289
if(out == mandos_protocol_version){
1304
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1312
/* This casting via intptr_t is to eliminate warning about casting
1313
an int to a pointer type. This is exactly how the GnuTLS Guile
1314
function "set-session-transport-fd!" does it. */
1315
gnutls_transport_set_ptr(session,
1316
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1324
ret = gnutls_handshake(session);
1329
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1331
if(ret != GNUTLS_E_SUCCESS){
1333
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1340
/* Read OpenPGP packet that contains the wanted password */
1343
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1354
buffer_capacity = incbuffer(&buffer, buffer_length,
1356
if(buffer_capacity == 0){
1358
perror_plus("incbuffer");
1368
sret = gnutls_record_recv(session, buffer+buffer_length,
1375
case GNUTLS_E_INTERRUPTED:
1376
case GNUTLS_E_AGAIN:
1378
case GNUTLS_E_REHANDSHAKE:
1380
ret = gnutls_handshake(session);
1386
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1388
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
1396
fprintf_plus(stderr, "Unknown error while reading data from"
1397
" encrypted session with Mandos server\n");
1398
gnutls_bye(session, GNUTLS_SHUT_RDWR);
1403
buffer_length += (size_t) sret;
1408
fprintf_plus(stderr, "Closing TLS session\n");
1417
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1422
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1424
if(buffer_length > 0){
1425
ssize_t decrypted_buffer_size;
1426
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1427
&decrypted_buffer, mc);
1428
if(decrypted_buffer_size >= 0){
1431
while(written < (size_t) decrypted_buffer_size){
1437
ret = (int)fwrite(decrypted_buffer + written, 1,
1438
(size_t)decrypted_buffer_size - written,
1440
if(ret == 0 and ferror(stdout)){
1443
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1449
written += (size_t)ret;
1455
/* Shutdown procedure */
1460
if(not delete_local_route(ip, if_index)){
1461
fprintf_plus(stderr, "Failed to delete local route to %s on"
1462
" interface %d", ip, if_index);
1466
free(decrypted_buffer);
1469
ret = close(tcp_sd);
1475
perror_plus("close");
1477
gnutls_deinit(session);
1487
__attribute__((nonnull))
1488
static void resolve_callback(AvahiSServiceResolver *r,
1489
AvahiIfIndex interface,
1490
AvahiProtocol proto,
1491
AvahiResolverEvent event,
1495
const char *host_name,
1496
const AvahiAddress *address,
1498
AVAHI_GCC_UNUSED AvahiStringList *txt,
1499
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1506
/* Called whenever a service has been resolved successfully or
1510
avahi_s_service_resolver_free(r);
1516
case AVAHI_RESOLVER_FAILURE:
1517
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1518
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1520
avahi_strerror(avahi_server_errno
1521
(((mandos_context*)mc)->server)));
1524
case AVAHI_RESOLVER_FOUND:
1526
char ip[AVAHI_ADDRESS_STR_MAX];
1527
avahi_address_snprint(ip, sizeof(ip), address);
1529
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1530
PRIdMAX ") on port %" PRIu16 "\n", name,
1531
host_name, ip, (intmax_t)interface, port);
1533
int ret = start_mandos_communication(ip, (in_port_t)port,
1535
avahi_proto_to_af(proto),
1538
avahi_simple_poll_quit(simple_poll);
1540
if(not add_server(ip, (in_port_t)port, interface,
1541
avahi_proto_to_af(proto),
1542
&((mandos_context*)mc)->current_server)){
1543
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1549
avahi_s_service_resolver_free(r);
1552
static void browse_callback(AvahiSServiceBrowser *b,
1553
AvahiIfIndex interface,
1554
AvahiProtocol protocol,
1555
AvahiBrowserEvent event,
1559
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1566
/* Called whenever a new services becomes available on the LAN or
1567
is removed from the LAN */
1575
case AVAHI_BROWSER_FAILURE:
1577
fprintf_plus(stderr, "(Avahi browser) %s\n",
1578
avahi_strerror(avahi_server_errno
1579
(((mandos_context*)mc)->server)));
1580
avahi_simple_poll_quit(simple_poll);
1583
case AVAHI_BROWSER_NEW:
1584
/* We ignore the returned Avahi resolver object. In the callback
1585
function we free it. If the Avahi server is terminated before
1586
the callback function is called the Avahi server will free the
1589
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1590
interface, protocol, name, type,
1591
domain, protocol, 0,
1592
resolve_callback, mc) == NULL)
1593
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1595
avahi_strerror(avahi_server_errno
1596
(((mandos_context*)mc)->server)));
1599
case AVAHI_BROWSER_REMOVE:
1602
case AVAHI_BROWSER_ALL_FOR_NOW:
1603
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1605
fprintf_plus(stderr, "No Mandos server found, still"
1612
/* Signal handler that stops main loop after SIGTERM */
1613
static void handle_sigterm(int sig){
1618
signal_received = sig;
1619
int old_errno = errno;
1620
/* set main loop to exit */
1621
if(simple_poll != NULL){
1622
avahi_simple_poll_quit(simple_poll);
1627
__attribute__((nonnull, warn_unused_result))
1628
bool get_flags(const char *ifname, struct ifreq *ifr){
1632
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1635
perror_plus("socket");
1639
strcpy(ifr->ifr_name, ifname);
1640
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1644
perror_plus("ioctl SIOCGIFFLAGS");
1652
__attribute__((nonnull, warn_unused_result))
1653
bool good_flags(const char *ifname, const struct ifreq *ifr){
1655
/* Reject the loopback device */
1656
if(ifr->ifr_flags & IFF_LOOPBACK){
1658
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1663
/* Accept point-to-point devices only if connect_to is specified */
1664
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1666
fprintf_plus(stderr, "Accepting point-to-point interface"
1667
" \"%s\"\n", ifname);
1671
/* Otherwise, reject non-broadcast-capable devices */
1672
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1674
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1675
" \"%s\"\n", ifname);
1679
/* Reject non-ARP interfaces (including dummy interfaces) */
1680
if(ifr->ifr_flags & IFF_NOARP){
1682
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1688
/* Accept this device */
1690
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1696
* This function determines if a directory entry in /sys/class/net
1697
* corresponds to an acceptable network device.
1698
* (This function is passed to scandir(3) as a filter function.)
1700
__attribute__((nonnull, warn_unused_result))
1701
int good_interface(const struct dirent *if_entry){
1702
if(if_entry->d_name[0] == '.'){
1707
if(not get_flags(if_entry->d_name, &ifr)){
1709
fprintf_plus(stderr, "Failed to get flags for interface "
1710
"\"%s\"\n", if_entry->d_name);
1715
if(not good_flags(if_entry->d_name, &ifr)){
1722
* This function determines if a network interface is up.
1724
__attribute__((nonnull, warn_unused_result))
1725
bool interface_is_up(const char *interface){
1727
if(not get_flags(interface, &ifr)){
1729
fprintf_plus(stderr, "Failed to get flags for interface "
1730
"\"%s\"\n", interface);
1735
return (bool)(ifr.ifr_flags & IFF_UP);
1739
* This function determines if a network interface is running
1741
__attribute__((nonnull, warn_unused_result))
1742
bool interface_is_running(const char *interface){
1744
if(not get_flags(interface, &ifr)){
1746
fprintf_plus(stderr, "Failed to get flags for interface "
1747
"\"%s\"\n", interface);
1752
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1755
__attribute__((nonnull, pure, warn_unused_result))
1756
int notdotentries(const struct dirent *direntry){
1757
/* Skip "." and ".." */
1758
if(direntry->d_name[0] == '.'
1759
and (direntry->d_name[1] == '\0'
1760
or (direntry->d_name[1] == '.'
1761
and direntry->d_name[2] == '\0'))){
1767
/* Is this directory entry a runnable program? */
1768
__attribute__((nonnull, warn_unused_result))
1769
int runnable_hook(const struct dirent *direntry){
1774
if((direntry->d_name)[0] == '\0'){
1779
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1780
"abcdefghijklmnopqrstuvwxyz"
1783
if((direntry->d_name)[sret] != '\0'){
1784
/* Contains non-allowed characters */
1786
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1792
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1795
perror_plus("Could not stat hook");
1799
if(not (S_ISREG(st.st_mode))){
1800
/* Not a regular file */
1802
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1807
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1808
/* Not executable */
1810
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1816
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1822
__attribute__((nonnull, warn_unused_result))
1823
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1824
mandos_context *mc){
1826
struct timespec now;
1827
struct timespec waited_time;
1828
intmax_t block_time;
1831
if(mc->current_server == NULL){
1833
fprintf_plus(stderr, "Wait until first server is found."
1836
ret = avahi_simple_poll_iterate(s, -1);
1839
fprintf_plus(stderr, "Check current_server if we should run"
1842
/* the current time */
1843
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1845
perror_plus("clock_gettime");
1848
/* Calculating in ms how long time between now and server
1849
who we visted longest time ago. Now - last seen. */
1850
waited_time.tv_sec = (now.tv_sec
1851
- mc->current_server->last_seen.tv_sec);
1852
waited_time.tv_nsec = (now.tv_nsec
1853
- mc->current_server->last_seen.tv_nsec);
1854
/* total time is 10s/10,000ms.
1855
Converting to s from ms by dividing by 1,000,
1856
and ns to ms by dividing by 1,000,000. */
1857
block_time = ((retry_interval
1858
- ((intmax_t)waited_time.tv_sec * 1000))
1859
- ((intmax_t)waited_time.tv_nsec / 1000000));
1862
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1866
if(block_time <= 0){
1867
ret = start_mandos_communication(mc->current_server->ip,
1868
mc->current_server->port,
1869
mc->current_server->if_index,
1870
mc->current_server->af, mc);
1872
avahi_simple_poll_quit(s);
1875
ret = clock_gettime(CLOCK_MONOTONIC,
1876
&mc->current_server->last_seen);
1878
perror_plus("clock_gettime");
1881
mc->current_server = mc->current_server->next;
1882
block_time = 0; /* Call avahi to find new Mandos
1883
servers, but don't block */
1886
ret = avahi_simple_poll_iterate(s, (int)block_time);
1889
if(ret > 0 or errno != EINTR){
1890
return (ret != 1) ? ret : 0;
1896
__attribute__((nonnull))
1897
void run_network_hooks(const char *mode, const char *interface,
1899
struct dirent **direntries = NULL;
1900
if(hookdir_fd == -1){
1901
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1903
if(hookdir_fd == -1){
1904
if(errno == ENOENT){
1906
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1907
" found\n", hookdir);
1910
perror_plus("open");
1916
#if __GLIBC_PREREQ(2, 15)
1917
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1918
runnable_hook, alphasort);
1919
#else /* not __GLIBC_PREREQ(2, 15) */
1920
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1922
#endif /* not __GLIBC_PREREQ(2, 15) */
1923
#else /* not __GLIBC__ */
1924
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1926
#endif /* not __GLIBC__ */
1928
perror_plus("scandir");
1931
struct dirent *direntry;
1933
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1935
perror_plus("open(\"/dev/null\", O_RDONLY)");
1938
for(int i = 0; i < numhooks; i++){
1939
direntry = direntries[i];
1941
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1944
pid_t hook_pid = fork();
1947
/* Raise privileges */
1948
errno = raise_privileges_permanently();
1950
perror_plus("Failed to raise privileges");
1957
perror_plus("setgid");
1960
/* Reset supplementary groups */
1962
ret = setgroups(0, NULL);
1964
perror_plus("setgroups");
1967
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1969
perror_plus("setenv");
1972
ret = setenv("DEVICE", interface, 1);
1974
perror_plus("setenv");
1977
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1979
perror_plus("setenv");
1982
ret = setenv("MODE", mode, 1);
1984
perror_plus("setenv");
1988
ret = asprintf(&delaystring, "%f", (double)delay);
1990
perror_plus("asprintf");
1993
ret = setenv("DELAY", delaystring, 1);
1996
perror_plus("setenv");
2000
if(connect_to != NULL){
2001
ret = setenv("CONNECT", connect_to, 1);
2003
perror_plus("setenv");
2007
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2011
perror_plus("openat");
2012
_exit(EXIT_FAILURE);
2014
if(close(hookdir_fd) == -1){
2015
perror_plus("close");
2016
_exit(EXIT_FAILURE);
2018
ret = dup2(devnull, STDIN_FILENO);
2020
perror_plus("dup2(devnull, STDIN_FILENO)");
2023
ret = close(devnull);
2025
perror_plus("close");
2028
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2030
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2033
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2035
perror_plus("fexecve");
2036
_exit(EXIT_FAILURE);
2040
perror_plus("fork");
2045
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2046
perror_plus("waitpid");
2050
if(WIFEXITED(status)){
2051
if(WEXITSTATUS(status) != 0){
2052
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2053
" with status %d\n", direntry->d_name,
2054
WEXITSTATUS(status));
2058
} else if(WIFSIGNALED(status)){
2059
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2060
" signal %d\n", direntry->d_name,
2065
fprintf_plus(stderr, "Warning: network hook \"%s\""
2066
" crashed\n", direntry->d_name);
2072
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2078
if(close(hookdir_fd) == -1){
2079
perror_plus("close");
2086
__attribute__((nonnull, warn_unused_result))
2087
error_t bring_up_interface(const char *const interface,
2089
error_t old_errno = errno;
2091
struct ifreq network;
2092
unsigned int if_index = if_nametoindex(interface);
2094
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2104
if(not interface_is_up(interface)){
2105
error_t ret_errno = 0, ioctl_errno = 0;
2106
if(not get_flags(interface, &network)){
2108
fprintf_plus(stderr, "Failed to get flags for interface "
2109
"\"%s\"\n", interface);
2113
network.ifr_flags |= IFF_UP; /* set flag */
2115
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2118
perror_plus("socket");
2126
perror_plus("close");
2133
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2137
/* Raise privileges */
2138
ret_errno = raise_privileges();
2141
perror_plus("Failed to raise privileges");
2146
bool restore_loglevel = false;
2148
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2149
messages about the network interface to mess up the prompt */
2150
ret_linux = klogctl(8, NULL, 5);
2151
if(ret_linux == -1){
2152
perror_plus("klogctl");
2154
restore_loglevel = true;
2157
#endif /* __linux__ */
2158
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2159
ioctl_errno = errno;
2161
if(restore_loglevel){
2162
ret_linux = klogctl(7, NULL, 0);
2163
if(ret_linux == -1){
2164
perror_plus("klogctl");
2167
#endif /* __linux__ */
2169
/* If raise_privileges() succeeded above */
2171
/* Lower privileges */
2172
ret_errno = lower_privileges();
2175
perror_plus("Failed to lower privileges");
2179
/* Close the socket */
2182
perror_plus("close");
2185
if(ret_setflags == -1){
2186
errno = ioctl_errno;
2187
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2192
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2196
/* Sleep checking until interface is running.
2197
Check every 0.25s, up to total time of delay */
2198
for(int i=0; i < delay * 4; i++){
2199
if(interface_is_running(interface)){
2202
struct timespec sleeptime = { .tv_nsec = 250000000 };
2203
ret = nanosleep(&sleeptime, NULL);
2204
if(ret == -1 and errno != EINTR){
2205
perror_plus("nanosleep");
2213
__attribute__((nonnull, warn_unused_result))
2214
error_t take_down_interface(const char *const interface){
2215
error_t old_errno = errno;
2216
struct ifreq network;
2217
unsigned int if_index = if_nametoindex(interface);
2219
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2223
if(interface_is_up(interface)){
2224
error_t ret_errno = 0, ioctl_errno = 0;
2225
if(not get_flags(interface, &network) and debug){
2227
fprintf_plus(stderr, "Failed to get flags for interface "
2228
"\"%s\"\n", interface);
2232
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2234
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2237
perror_plus("socket");
2243
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2247
/* Raise privileges */
2248
ret_errno = raise_privileges();
2251
perror_plus("Failed to raise privileges");
2254
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2255
ioctl_errno = errno;
2257
/* If raise_privileges() succeeded above */
2259
/* Lower privileges */
2260
ret_errno = lower_privileges();
2263
perror_plus("Failed to lower privileges");
2267
/* Close the socket */
2268
int ret = close(sd);
2270
perror_plus("close");
2273
if(ret_setflags == -1){
2274
errno = ioctl_errno;
2275
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2280
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2288
int main(int argc, char *argv[]){
2289
mandos_context mc = { .server = NULL, .dh_bits = 0,
2290
.priority = "SECURE256:!CTYPE-X.509:"
2291
"+CTYPE-OPENPGP:!RSA", .current_server = NULL,
2292
.interfaces = NULL, .interfaces_size = 0 };
2293
AvahiSServiceBrowser *sb = NULL;
2298
int exitcode = EXIT_SUCCESS;
2299
char *interfaces_to_take_down = NULL;
2300
size_t interfaces_to_take_down_size = 0;
2301
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2302
char old_tempdir[] = "/tmp/mandosXXXXXX";
2303
char *tempdir = NULL;
2304
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2305
const char *seckey = PATHDIR "/" SECKEY;
2306
const char *pubkey = PATHDIR "/" PUBKEY;
2307
const char *dh_params_file = NULL;
2308
char *interfaces_hooks = NULL;
2310
bool gnutls_initialized = false;
2311
bool gpgme_initialized = false;
2313
double retry_interval = 10; /* 10s between trying a server and
2314
retrying the same server again */
2316
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2317
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2322
/* Lower any group privileges we might have, just to be safe */
2326
perror_plus("setgid");
2329
/* Lower user privileges (temporarily) */
2333
perror_plus("seteuid");
2341
struct argp_option options[] = {
2342
{ .name = "debug", .key = 128,
2343
.doc = "Debug mode", .group = 3 },
2344
{ .name = "connect", .key = 'c',
2345
.arg = "ADDRESS:PORT",
2346
.doc = "Connect directly to a specific Mandos server",
2348
{ .name = "interface", .key = 'i',
2350
.doc = "Network interface that will be used to search for"
2353
{ .name = "seckey", .key = 's',
2355
.doc = "OpenPGP secret key file base name",
2357
{ .name = "pubkey", .key = 'p',
2359
.doc = "OpenPGP public key file base name",
2361
{ .name = "dh-bits", .key = 129,
2363
.doc = "Bit length of the prime number used in the"
2364
" Diffie-Hellman key exchange",
2366
{ .name = "dh-params", .key = 134,
2368
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2369
" for the Diffie-Hellman key exchange",
2371
{ .name = "priority", .key = 130,
2373
.doc = "GnuTLS priority string for the TLS handshake",
2375
{ .name = "delay", .key = 131,
2377
.doc = "Maximum delay to wait for interface startup",
2379
{ .name = "retry", .key = 132,
2381
.doc = "Retry interval used when denied by the Mandos server",
2383
{ .name = "network-hook-dir", .key = 133,
2385
.doc = "Directory where network hooks are located",
2388
* These reproduce what we would get without ARGP_NO_HELP
2390
{ .name = "help", .key = '?',
2391
.doc = "Give this help list", .group = -1 },
2392
{ .name = "usage", .key = -3,
2393
.doc = "Give a short usage message", .group = -1 },
2394
{ .name = "version", .key = 'V',
2395
.doc = "Print program version", .group = -1 },
2399
error_t parse_opt(int key, char *arg,
2400
struct argp_state *state){
2403
case 128: /* --debug */
2406
case 'c': /* --connect */
2409
case 'i': /* --interface */
2410
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2413
argp_error(state, "%s", strerror(ret_errno));
2416
case 's': /* --seckey */
2419
case 'p': /* --pubkey */
2422
case 129: /* --dh-bits */
2424
tmpmax = strtoimax(arg, &tmp, 10);
2425
if(errno != 0 or tmp == arg or *tmp != '\0'
2426
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2427
argp_error(state, "Bad number of DH bits");
2429
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2431
case 134: /* --dh-params */
2432
dh_params_file = arg;
2434
case 130: /* --priority */
2437
case 131: /* --delay */
2439
delay = strtof(arg, &tmp);
2440
if(errno != 0 or tmp == arg or *tmp != '\0'){
2441
argp_error(state, "Bad delay");
2443
case 132: /* --retry */
2445
retry_interval = strtod(arg, &tmp);
2446
if(errno != 0 or tmp == arg or *tmp != '\0'
2447
or (retry_interval * 1000) > INT_MAX
2448
or retry_interval < 0){
2449
argp_error(state, "Bad retry interval");
2452
case 133: /* --network-hook-dir */
2456
* These reproduce what we would get without ARGP_NO_HELP
2458
case '?': /* --help */
2459
argp_state_help(state, state->out_stream,
2460
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2461
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2462
case -3: /* --usage */
2463
argp_state_help(state, state->out_stream,
2464
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2465
case 'V': /* --version */
2466
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2467
exit(argp_err_exit_status);
2470
return ARGP_ERR_UNKNOWN;
2475
struct argp argp = { .options = options, .parser = parse_opt,
2477
.doc = "Mandos client -- Get and decrypt"
2478
" passwords from a Mandos server" };
2479
ret = argp_parse(&argp, argc, argv,
2480
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2487
perror_plus("argp_parse");
2488
exitcode = EX_OSERR;
2491
exitcode = EX_USAGE;
2497
/* Work around Debian bug #633582:
2498
<http://bugs.debian.org/633582> */
2500
/* Re-raise privileges */
2501
ret_errno = raise_privileges();
2504
perror_plus("Failed to raise privileges");
2508
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2509
int seckey_fd = open(seckey, O_RDONLY);
2510
if(seckey_fd == -1){
2511
perror_plus("open");
2513
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2515
perror_plus("fstat");
2517
if(S_ISREG(st.st_mode)
2518
and st.st_uid == 0 and st.st_gid == 0){
2519
ret = fchown(seckey_fd, uid, gid);
2521
perror_plus("fchown");
2529
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2530
int pubkey_fd = open(pubkey, O_RDONLY);
2531
if(pubkey_fd == -1){
2532
perror_plus("open");
2534
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2536
perror_plus("fstat");
2538
if(S_ISREG(st.st_mode)
2539
and st.st_uid == 0 and st.st_gid == 0){
2540
ret = fchown(pubkey_fd, uid, gid);
2542
perror_plus("fchown");
2550
if(strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2551
int dhparams_fd = open(dh_params_file, O_RDONLY);
2552
if(dhparams_fd == -1){
2553
perror_plus("open");
2555
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2557
perror_plus("fstat");
2559
if(S_ISREG(st.st_mode)
2560
and st.st_uid == 0 and st.st_gid == 0){
2561
ret = fchown(dhparams_fd, uid, gid);
2563
perror_plus("fchown");
2571
/* Lower privileges */
2572
ret_errno = lower_privileges();
2575
perror_plus("Failed to lower privileges");
2580
/* Remove invalid interface names (except "none") */
2582
char *interface = NULL;
2583
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2585
if(strcmp(interface, "none") != 0
2586
and if_nametoindex(interface) == 0){
2587
if(interface[0] != '\0'){
2588
fprintf_plus(stderr, "Not using nonexisting interface"
2589
" \"%s\"\n", interface);
2591
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2597
/* Run network hooks */
2599
if(mc.interfaces != NULL){
2600
interfaces_hooks = malloc(mc.interfaces_size);
2601
if(interfaces_hooks == NULL){
2602
perror_plus("malloc");
2605
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2606
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2608
run_network_hooks("start", interfaces_hooks != NULL ?
2609
interfaces_hooks : "", delay);
2613
avahi_set_log_function(empty_log);
2616
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2617
from the signal handler */
2618
/* Initialize the pseudo-RNG for Avahi */
2619
srand((unsigned int) time(NULL));
2620
simple_poll = avahi_simple_poll_new();
2621
if(simple_poll == NULL){
2622
fprintf_plus(stderr,
2623
"Avahi: Failed to create simple poll object.\n");
2624
exitcode = EX_UNAVAILABLE;
2628
sigemptyset(&sigterm_action.sa_mask);
2629
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2631
perror_plus("sigaddset");
2632
exitcode = EX_OSERR;
2635
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2637
perror_plus("sigaddset");
2638
exitcode = EX_OSERR;
2641
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2643
perror_plus("sigaddset");
2644
exitcode = EX_OSERR;
2647
/* Need to check if the handler is SIG_IGN before handling:
2648
| [[info:libc:Initial Signal Actions]] |
2649
| [[info:libc:Basic Signal Handling]] |
2651
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2653
perror_plus("sigaction");
2656
if(old_sigterm_action.sa_handler != SIG_IGN){
2657
ret = sigaction(SIGINT, &sigterm_action, NULL);
2659
perror_plus("sigaction");
2660
exitcode = EX_OSERR;
2664
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2666
perror_plus("sigaction");
2669
if(old_sigterm_action.sa_handler != SIG_IGN){
2670
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2672
perror_plus("sigaction");
2673
exitcode = EX_OSERR;
2677
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2679
perror_plus("sigaction");
2682
if(old_sigterm_action.sa_handler != SIG_IGN){
2683
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2685
perror_plus("sigaction");
2686
exitcode = EX_OSERR;
2691
/* If no interfaces were specified, make a list */
2692
if(mc.interfaces == NULL){
2693
struct dirent **direntries = NULL;
2694
/* Look for any good interfaces */
2695
ret = scandir(sys_class_net, &direntries, good_interface,
2698
/* Add all found interfaces to interfaces list */
2699
for(int i = 0; i < ret; ++i){
2700
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2701
direntries[i]->d_name);
2704
perror_plus("argz_add");
2705
free(direntries[i]);
2709
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2710
direntries[i]->d_name);
2712
free(direntries[i]);
2719
fprintf_plus(stderr, "Could not find a network interface\n");
2720
exitcode = EXIT_FAILURE;
2725
/* Bring up interfaces which are down, and remove any "none"s */
2727
char *interface = NULL;
2728
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2730
/* If interface name is "none", stop bringing up interfaces.
2731
Also remove all instances of "none" from the list */
2732
if(strcmp(interface, "none") == 0){
2733
argz_delete(&mc.interfaces, &mc.interfaces_size,
2736
while((interface = argz_next(mc.interfaces,
2737
mc.interfaces_size, interface))){
2738
if(strcmp(interface, "none") == 0){
2739
argz_delete(&mc.interfaces, &mc.interfaces_size,
2746
bool interface_was_up = interface_is_up(interface);
2747
errno = bring_up_interface(interface, delay);
2748
if(not interface_was_up){
2750
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2751
" %s\n", interface, strerror(errno));
2753
errno = argz_add(&interfaces_to_take_down,
2754
&interfaces_to_take_down_size,
2757
perror_plus("argz_add");
2762
if(debug and (interfaces_to_take_down == NULL)){
2763
fprintf_plus(stderr, "No interfaces were brought up\n");
2767
/* If we only got one interface, explicitly use only that one */
2768
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2770
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2773
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2780
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2782
fprintf_plus(stderr, "init_gnutls_global failed\n");
2783
exitcode = EX_UNAVAILABLE;
2786
gnutls_initialized = true;
2793
/* Try /run/tmp before /tmp */
2794
tempdir = mkdtemp(run_tempdir);
2795
if(tempdir == NULL and errno == ENOENT){
2797
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2798
run_tempdir, old_tempdir);
2800
tempdir = mkdtemp(old_tempdir);
2802
if(tempdir == NULL){
2803
perror_plus("mkdtemp");
2811
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2812
fprintf_plus(stderr, "init_gpgme failed\n");
2813
exitcode = EX_UNAVAILABLE;
2816
gpgme_initialized = true;
2823
if(connect_to != NULL){
2824
/* Connect directly, do not use Zeroconf */
2825
/* (Mainly meant for debugging) */
2826
char *address = strrchr(connect_to, ':');
2828
if(address == NULL){
2829
fprintf_plus(stderr, "No colon in address\n");
2830
exitcode = EX_USAGE;
2840
tmpmax = strtoimax(address+1, &tmp, 10);
2841
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2842
or tmpmax != (in_port_t)tmpmax){
2843
fprintf_plus(stderr, "Bad port number\n");
2844
exitcode = EX_USAGE;
2852
port = (in_port_t)tmpmax;
2854
/* Colon in address indicates IPv6 */
2856
if(strchr(connect_to, ':') != NULL){
2858
/* Accept [] around IPv6 address - see RFC 5952 */
2859
if(connect_to[0] == '[' and address[-1] == ']')
2867
address = connect_to;
2873
while(not quit_now){
2874
ret = start_mandos_communication(address, port, if_index, af,
2876
if(quit_now or ret == 0){
2880
fprintf_plus(stderr, "Retrying in %d seconds\n",
2881
(int)retry_interval);
2883
sleep((unsigned int)retry_interval);
2887
exitcode = EXIT_SUCCESS;
2898
AvahiServerConfig config;
2899
/* Do not publish any local Zeroconf records */
2900
avahi_server_config_init(&config);
2901
config.publish_hinfo = 0;
2902
config.publish_addresses = 0;
2903
config.publish_workstation = 0;
2904
config.publish_domain = 0;
2906
/* Allocate a new server */
2907
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2908
&config, NULL, NULL, &ret_errno);
2910
/* Free the Avahi configuration data */
2911
avahi_server_config_free(&config);
2914
/* Check if creating the Avahi server object succeeded */
2915
if(mc.server == NULL){
2916
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2917
avahi_strerror(ret_errno));
2918
exitcode = EX_UNAVAILABLE;
2926
/* Create the Avahi service browser */
2927
sb = avahi_s_service_browser_new(mc.server, if_index,
2928
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2929
NULL, 0, browse_callback,
2932
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2933
avahi_strerror(avahi_server_errno(mc.server)));
2934
exitcode = EX_UNAVAILABLE;
2942
/* Run the main loop */
2945
fprintf_plus(stderr, "Starting Avahi loop search\n");
2948
ret = avahi_loop_with_timeout(simple_poll,
2949
(int)(retry_interval * 1000), &mc);
2951
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2952
(ret == 0) ? "successfully" : "with error");
2958
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2961
/* Cleanup things */
2962
free(mc.interfaces);
2965
avahi_s_service_browser_free(sb);
2967
if(mc.server != NULL)
2968
avahi_server_free(mc.server);
2970
if(simple_poll != NULL)
2971
avahi_simple_poll_free(simple_poll);
2973
if(gnutls_initialized){
2974
gnutls_certificate_free_credentials(mc.cred);
2975
gnutls_global_deinit();
2976
gnutls_dh_params_deinit(mc.dh_params);
2979
if(gpgme_initialized){
2980
gpgme_release(mc.ctx);
2983
/* Cleans up the circular linked list of Mandos servers the client
2985
if(mc.current_server != NULL){
2986
mc.current_server->prev->next = NULL;
2987
while(mc.current_server != NULL){
2988
server *next = mc.current_server->next;
2990
#pragma GCC diagnostic push
2991
#pragma GCC diagnostic ignored "-Wcast-qual"
2993
free((char *)(mc.current_server->ip));
2995
#pragma GCC diagnostic pop
2997
free(mc.current_server);
2998
mc.current_server = next;
3002
/* Re-raise privileges */
3004
ret_errno = raise_privileges();
3007
perror_plus("Failed to raise privileges");
3010
/* Run network hooks */
3011
run_network_hooks("stop", interfaces_hooks != NULL ?
3012
interfaces_hooks : "", delay);
3014
/* Take down the network interfaces which were brought up */
3016
char *interface = NULL;
3017
while((interface=argz_next(interfaces_to_take_down,
3018
interfaces_to_take_down_size,
3020
ret_errno = take_down_interface(interface);
3023
perror_plus("Failed to take down interface");
3026
if(debug and (interfaces_to_take_down == NULL)){
3027
fprintf_plus(stderr, "No interfaces needed to be taken"
3033
ret_errno = lower_privileges_permanently();
3036
perror_plus("Failed to lower privileges permanently");
3040
free(interfaces_to_take_down);
3041
free(interfaces_hooks);
3043
/* Removes the GPGME temp directory and all files inside */
3044
if(tempdir != NULL){
3045
struct dirent **direntries = NULL;
3046
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
3050
if(tempdir_fd == -1){
3051
perror_plus("open");
3054
#if __GLIBC_PREREQ(2, 15)
3055
int numentries = scandirat(tempdir_fd, ".", &direntries,
3056
notdotentries, alphasort);
3057
#else /* not __GLIBC_PREREQ(2, 15) */
3058
int numentries = scandir(tempdir, &direntries, notdotentries,
3060
#endif /* not __GLIBC_PREREQ(2, 15) */
3061
#else /* not __GLIBC__ */
3062
int numentries = scandir(tempdir, &direntries, notdotentries,
3064
#endif /* not __GLIBC__ */
3065
if(numentries >= 0){
3066
for(int i = 0; i < numentries; i++){
3067
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
3069
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
3070
" \"%s\", 0): %s\n", tempdir,
3071
direntries[i]->d_name, strerror(errno));
3073
free(direntries[i]);
3076
/* need to clean even if 0 because man page doesn't specify */
3078
if(numentries == -1){
3079
perror_plus("scandir");
3081
ret = rmdir(tempdir);
3082
if(ret == -1 and errno != ENOENT){
3083
perror_plus("rmdir");
3091
sigemptyset(&old_sigterm_action.sa_mask);
3092
old_sigterm_action.sa_handler = SIG_DFL;
3093
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3094
&old_sigterm_action,
3097
perror_plus("sigaction");
3100
ret = raise(signal_received);
3101
} while(ret != 0 and errno == EINTR);
3103
perror_plus("raise");
3106
TEMP_FAILURE_RETRY(pause());