1
/* -*- coding: utf-8 -*- */
3
* Mandos-client - get and decrypt data from a Mandos server
5
* This program is partly derived from an example program for an Avahi
6
* service browser, downloaded from
7
* <http://avahi.org/browser/examples/core-browse-services.c>. This
8
* includes the following functions: "resolve_callback",
9
* "browse_callback", and parts of "main".
12
* Copyright © 2008-2018 Teddy Hogeborn
13
* Copyright © 2008-2018 Björn Påhlsson
15
* This file is part of Mandos.
17
* Mandos is free software: you can redistribute it and/or modify it
18
* under the terms of the GNU General Public License as published by
19
* the Free Software Foundation, either version 3 of the License, or
20
* (at your option) any later version.
22
* Mandos is distributed in the hope that it will be useful, but
23
* WITHOUT ANY WARRANTY; without even the implied warranty of
24
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25
* General Public License for more details.
27
* You should have received a copy of the GNU General Public License
28
* along with Mandos. If not, see <http://www.gnu.org/licenses/>.
30
* Contact the authors at <mandos@recompile.se>.
33
/* Needed by GPGME, specifically gpgme_data_seek() */
34
#ifndef _LARGEFILE_SOURCE
35
#define _LARGEFILE_SOURCE
36
#endif /* not _LARGEFILE_SOURCE */
37
#ifndef _FILE_OFFSET_BITS
38
#define _FILE_OFFSET_BITS 64
39
#endif /* not _FILE_OFFSET_BITS */
41
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
43
#include <stdio.h> /* fprintf(), stderr, fwrite(),
45
#include <stdint.h> /* uint16_t, uint32_t, intptr_t */
46
#include <stddef.h> /* NULL, size_t, ssize_t */
47
#include <stdlib.h> /* free(), EXIT_SUCCESS, srand(),
49
#include <stdbool.h> /* bool, false, true */
50
#include <string.h> /* strcmp(), strlen(), strerror(),
51
asprintf(), strncpy(), strsignal()
53
#include <sys/ioctl.h> /* ioctl */
54
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
55
sockaddr_in6, PF_INET6,
56
SOCK_STREAM, uid_t, gid_t, open(),
58
#include <sys/stat.h> /* open(), S_ISREG */
59
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
60
inet_pton(), connect(),
62
#include <fcntl.h> /* open(), unlinkat(), AT_REMOVEDIR */
63
#include <dirent.h> /* opendir(), struct dirent, readdir()
65
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
67
#include <errno.h> /* perror(), errno, EINTR, EINVAL,
68
EAI_SYSTEM, ENETUNREACH,
69
EHOSTUNREACH, ECONNREFUSED, EPROTO,
70
EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
72
program_invocation_short_name */
73
#include <time.h> /* nanosleep(), time(), sleep() */
74
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
75
SIOCSIFFLAGS, if_indextoname(),
76
if_nametoindex(), IF_NAMESIZE */
77
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
78
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
80
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
81
getuid(), getgid(), seteuid(),
82
setgid(), pause(), _exit(),
84
#include <arpa/inet.h> /* inet_pton(), htons() */
85
#include <iso646.h> /* not, or, and */
86
#include <argp.h> /* struct argp_option, error_t, struct
87
argp_state, struct argp,
88
argp_parse(), ARGP_KEY_ARG,
89
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
90
#include <signal.h> /* sigemptyset(), sigaddset(),
91
sigaction(), SIGTERM, sig_atomic_t,
93
#include <sysexits.h> /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
94
EX_NOHOST, EX_IOERR, EX_PROTOCOL */
95
#include <sys/wait.h> /* waitpid(), WIFEXITED(),
96
WEXITSTATUS(), WTERMSIG() */
97
#include <grp.h> /* setgroups() */
98
#include <argz.h> /* argz_add_sep(), argz_next(),
99
argz_delete(), argz_append(),
100
argz_stringify(), argz_add(),
102
#include <netdb.h> /* getnameinfo(), NI_NUMERICHOST,
103
EAI_SYSTEM, gai_strerror() */
106
#include <sys/klog.h> /* klogctl() */
107
#endif /* __linux__ */
110
/* All Avahi types, constants and functions
113
#include <avahi-core/core.h>
114
#include <avahi-core/lookup.h>
115
#include <avahi-core/log.h>
116
#include <avahi-common/simple-watch.h>
117
#include <avahi-common/malloc.h>
118
#include <avahi-common/error.h>
121
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
124
init_gnutls_session(),
126
#include <gnutls/openpgp.h>
127
/* gnutls_certificate_set_openpgp_key_file(),
128
GNUTLS_OPENPGP_FMT_BASE64 */
131
#include <gpgme.h> /* All GPGME types, constants and
134
GPGME_PROTOCOL_OpenPGP,
137
#define BUFFER_SIZE 256
139
#define PATHDIR "/conf/conf.d/mandos"
140
#define SECKEY "seckey.txt"
141
#define PUBKEY "pubkey.txt"
142
#define HOOKDIR "/lib/mandos/network-hooks.d"
145
static const char mandos_protocol_version[] = "1";
146
const char *argp_program_version = "mandos-client " VERSION;
147
const char *argp_program_bug_address = "<mandos@recompile.se>";
148
static const char sys_class_net[] = "/sys/class/net";
149
char *connect_to = NULL;
150
const char *hookdir = HOOKDIR;
155
/* Doubly linked list that need to be circularly linked when used */
156
typedef struct server{
159
AvahiIfIndex if_index;
161
struct timespec last_seen;
166
/* Used for passing in values through the Avahi callback functions */
169
gnutls_certificate_credentials_t cred;
170
unsigned int dh_bits;
171
gnutls_dh_params_t dh_params;
172
const char *priority;
174
server *current_server;
176
size_t interfaces_size;
179
/* global so signal handler can reach it*/
180
AvahiSimplePoll *simple_poll;
182
sig_atomic_t quit_now = 0;
183
int signal_received = 0;
185
/* Function to use when printing errors */
186
void perror_plus(const char *print_text){
188
fprintf(stderr, "Mandos plugin %s: ",
189
program_invocation_short_name);
194
__attribute__((format (gnu_printf, 2, 3), nonnull))
195
int fprintf_plus(FILE *stream, const char *format, ...){
197
va_start (ap, format);
199
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
200
program_invocation_short_name));
201
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
205
* Make additional room in "buffer" for at least BUFFER_SIZE more
206
* bytes. "buffer_capacity" is how much is currently allocated,
207
* "buffer_length" is how much is already used.
209
__attribute__((nonnull, warn_unused_result))
210
size_t incbuffer(char **buffer, size_t buffer_length,
211
size_t buffer_capacity){
212
if(buffer_length + BUFFER_SIZE > buffer_capacity){
213
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
215
int old_errno = errno;
222
buffer_capacity += BUFFER_SIZE;
224
return buffer_capacity;
227
/* Add server to set of servers to retry periodically */
228
__attribute__((nonnull, warn_unused_result))
229
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
230
int af, server **current_server){
232
server *new_server = malloc(sizeof(server));
233
if(new_server == NULL){
234
perror_plus("malloc");
237
*new_server = (server){ .ip = strdup(ip),
239
.if_index = if_index,
241
if(new_server->ip == NULL){
242
perror_plus("strdup");
246
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
248
perror_plus("clock_gettime");
250
#pragma GCC diagnostic push
251
#pragma GCC diagnostic ignored "-Wcast-qual"
253
free((char *)(new_server->ip));
255
#pragma GCC diagnostic pop
260
/* Special case of first server */
261
if(*current_server == NULL){
262
new_server->next = new_server;
263
new_server->prev = new_server;
264
*current_server = new_server;
266
/* Place the new server last in the list */
267
new_server->next = *current_server;
268
new_server->prev = (*current_server)->prev;
269
new_server->prev->next = new_server;
270
(*current_server)->prev = new_server;
275
/* Set effective uid to 0, return errno */
276
__attribute__((warn_unused_result))
277
int raise_privileges(void){
278
int old_errno = errno;
280
if(seteuid(0) == -1){
287
/* Set effective and real user ID to 0. Return errno. */
288
__attribute__((warn_unused_result))
289
int raise_privileges_permanently(void){
290
int old_errno = errno;
291
int ret = raise_privileges();
303
/* Set effective user ID to unprivileged saved user ID */
304
__attribute__((warn_unused_result))
305
int lower_privileges(void){
306
int old_errno = errno;
308
if(seteuid(uid) == -1){
315
/* Lower privileges permanently */
316
__attribute__((warn_unused_result))
317
int lower_privileges_permanently(void){
318
int old_errno = errno;
320
if(setuid(uid) == -1){
330
__attribute__((nonnull, warn_unused_result))
331
static bool init_gpgme(const char * const seckey,
332
const char * const pubkey,
333
const char * const tempdir,
336
gpgme_engine_info_t engine_info;
339
* Helper function to insert pub and seckey to the engine keyring.
341
bool import_key(const char * const filename){
344
gpgme_data_t pgp_data;
346
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
352
/* Workaround for systems without a real-time clock; see also
353
Debian bug #894495: <https://bugs.debian.org/894495> */
356
time_t currtime = time(NULL);
357
if(currtime != (time_t)-1){
359
if(gmtime_r(&currtime, &tm) == NULL) {
360
perror_plus("gmtime_r");
363
if(tm.tm_year != 70 or tm.tm_mon != 0){
367
fprintf_plus(stderr, "System clock is January 1970");
371
fprintf_plus(stderr, "System clock is invalid");
376
ret = fstat(fd, &keystat);
378
perror_plus("fstat");
381
ret = raise_privileges();
384
perror_plus("Failed to raise privileges");
389
"Setting system clock to key file mtime");
391
time_t keytime = keystat.st_mtim.tv_sec;
392
if(stime(&keytime) != 0){
393
perror_plus("stime");
395
ret = lower_privileges();
398
perror_plus("Failed to lower privileges");
402
rc = gpgme_data_new_from_fd(&pgp_data, fd);
403
if(rc != GPG_ERR_NO_ERROR){
404
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
405
gpgme_strsource(rc), gpgme_strerror(rc));
409
rc = gpgme_op_import(mc->ctx, pgp_data);
410
if(rc != GPG_ERR_NO_ERROR){
411
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
412
gpgme_strsource(rc), gpgme_strerror(rc));
416
gpgme_import_result_t import_result
417
= gpgme_op_import_result(mc->ctx);
418
if((import_result->imported < 1
419
or import_result->not_imported > 0)
420
and import_result->unchanged == 0){
421
fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
423
"The total number of considered keys: %d\n",
424
import_result->considered);
426
"The number of keys without user ID: %d\n",
427
import_result->no_user_id);
429
"The total number of imported keys: %d\n",
430
import_result->imported);
431
fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
432
import_result->imported_rsa);
433
fprintf_plus(stderr, "The number of unchanged keys: %d\n",
434
import_result->unchanged);
435
fprintf_plus(stderr, "The number of new user IDs: %d\n",
436
import_result->new_user_ids);
437
fprintf_plus(stderr, "The number of new sub keys: %d\n",
438
import_result->new_sub_keys);
439
fprintf_plus(stderr, "The number of new signatures: %d\n",
440
import_result->new_signatures);
441
fprintf_plus(stderr, "The number of new revocations: %d\n",
442
import_result->new_revocations);
444
"The total number of secret keys read: %d\n",
445
import_result->secret_read);
447
"The number of imported secret keys: %d\n",
448
import_result->secret_imported);
450
"The number of unchanged secret keys: %d\n",
451
import_result->secret_unchanged);
452
fprintf_plus(stderr, "The number of keys not imported: %d\n",
453
import_result->not_imported);
454
for(gpgme_import_status_t import_status
455
= import_result->imports;
456
import_status != NULL;
457
import_status = import_status->next){
458
fprintf_plus(stderr, "Import status for key: %s\n",
460
if(import_status->result != GPG_ERR_NO_ERROR){
461
fprintf_plus(stderr, "Import result: %s: %s\n",
462
gpgme_strsource(import_status->result),
463
gpgme_strerror(import_status->result));
465
fprintf_plus(stderr, "Key status:\n");
467
import_status->status & GPGME_IMPORT_NEW
468
? "The key was new.\n"
469
: "The key was not new.\n");
471
import_status->status & GPGME_IMPORT_UID
472
? "The key contained new user IDs.\n"
473
: "The key did not contain new user IDs.\n");
475
import_status->status & GPGME_IMPORT_SIG
476
? "The key contained new signatures.\n"
477
: "The key did not contain new signatures.\n");
479
import_status->status & GPGME_IMPORT_SUBKEY
480
? "The key contained new sub keys.\n"
481
: "The key did not contain new sub keys.\n");
483
import_status->status & GPGME_IMPORT_SECRET
484
? "The key contained a secret key.\n"
485
: "The key did not contain a secret key.\n");
493
perror_plus("close");
495
gpgme_data_release(pgp_data);
500
fprintf_plus(stderr, "Initializing GPGME\n");
504
gpgme_check_version(NULL);
505
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
506
if(rc != GPG_ERR_NO_ERROR){
507
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
508
gpgme_strsource(rc), gpgme_strerror(rc));
512
/* Set GPGME home directory for the OpenPGP engine only */
513
rc = gpgme_get_engine_info(&engine_info);
514
if(rc != GPG_ERR_NO_ERROR){
515
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
516
gpgme_strsource(rc), gpgme_strerror(rc));
519
while(engine_info != NULL){
520
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
521
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
522
engine_info->file_name, tempdir);
525
engine_info = engine_info->next;
527
if(engine_info == NULL){
528
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
533
/* Create new GPGME "context" */
534
rc = gpgme_new(&(mc->ctx));
535
if(rc != GPG_ERR_NO_ERROR){
536
fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
537
gpgme_strsource(rc), gpgme_strerror(rc));
541
if(not import_key(pubkey) or not import_key(seckey)){
549
* Decrypt OpenPGP data.
550
* Returns -1 on error
552
__attribute__((nonnull, warn_unused_result))
553
static ssize_t pgp_packet_decrypt(const char *cryptotext,
557
gpgme_data_t dh_crypto, dh_plain;
560
size_t plaintext_capacity = 0;
561
ssize_t plaintext_length = 0;
564
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
567
/* Create new GPGME data buffer from memory cryptotext */
568
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
570
if(rc != GPG_ERR_NO_ERROR){
571
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
572
gpgme_strsource(rc), gpgme_strerror(rc));
576
/* Create new empty GPGME data buffer for the plaintext */
577
rc = gpgme_data_new(&dh_plain);
578
if(rc != GPG_ERR_NO_ERROR){
579
fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
580
gpgme_strsource(rc), gpgme_strerror(rc));
581
gpgme_data_release(dh_crypto);
585
/* Decrypt data from the cryptotext data buffer to the plaintext
587
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
588
if(rc != GPG_ERR_NO_ERROR){
589
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
590
gpgme_strsource(rc), gpgme_strerror(rc));
591
plaintext_length = -1;
593
gpgme_decrypt_result_t result;
594
result = gpgme_op_decrypt_result(mc->ctx);
596
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
598
if(result->unsupported_algorithm != NULL) {
599
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
600
result->unsupported_algorithm);
602
fprintf_plus(stderr, "Wrong key usage: %s\n",
603
result->wrong_key_usage ? "Yes" : "No");
604
if(result->file_name != NULL){
605
fprintf_plus(stderr, "File name: %s\n", result->file_name);
608
for(gpgme_recipient_t r = result->recipients; r != NULL;
610
fprintf_plus(stderr, "Public key algorithm: %s\n",
611
gpgme_pubkey_algo_name(r->pubkey_algo));
612
fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
613
fprintf_plus(stderr, "Secret key available: %s\n",
614
r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
622
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
625
/* Seek back to the beginning of the GPGME plaintext data buffer */
626
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
627
perror_plus("gpgme_data_seek");
628
plaintext_length = -1;
634
plaintext_capacity = incbuffer(plaintext,
635
(size_t)plaintext_length,
637
if(plaintext_capacity == 0){
638
perror_plus("incbuffer");
639
plaintext_length = -1;
643
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
645
/* Print the data, if any */
651
perror_plus("gpgme_data_read");
652
plaintext_length = -1;
655
plaintext_length += ret;
659
fprintf_plus(stderr, "Decrypted password is: ");
660
for(ssize_t i = 0; i < plaintext_length; i++){
661
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
663
fprintf(stderr, "\n");
668
/* Delete the GPGME cryptotext data buffer */
669
gpgme_data_release(dh_crypto);
671
/* Delete the GPGME plaintext data buffer */
672
gpgme_data_release(dh_plain);
673
return plaintext_length;
676
__attribute__((warn_unused_result, const))
677
static const char *safe_string(const char *str){
683
__attribute__((warn_unused_result))
684
static const char *safer_gnutls_strerror(int value){
685
const char *ret = gnutls_strerror(value);
686
return safe_string(ret);
689
/* GnuTLS log function callback */
690
__attribute__((nonnull))
691
static void debuggnutls(__attribute__((unused)) int level,
693
fprintf_plus(stderr, "GnuTLS: %s", string);
696
__attribute__((nonnull(1, 2, 4), warn_unused_result))
697
static int init_gnutls_global(const char *pubkeyfilename,
698
const char *seckeyfilename,
699
const char *dhparamsfilename,
705
fprintf_plus(stderr, "Initializing GnuTLS\n");
709
/* "Use a log level over 10 to enable all debugging options."
712
gnutls_global_set_log_level(11);
713
gnutls_global_set_log_function(debuggnutls);
716
/* OpenPGP credentials */
717
ret = gnutls_certificate_allocate_credentials(&mc->cred);
718
if(ret != GNUTLS_E_SUCCESS){
719
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
720
safer_gnutls_strerror(ret));
725
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
726
" secret key %s as GnuTLS credentials\n",
731
ret = gnutls_certificate_set_openpgp_key_file
732
(mc->cred, pubkeyfilename, seckeyfilename,
733
GNUTLS_OPENPGP_FMT_BASE64);
734
if(ret != GNUTLS_E_SUCCESS){
736
"Error[%d] while reading the OpenPGP key pair ('%s',"
737
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
738
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
739
safer_gnutls_strerror(ret));
743
/* GnuTLS server initialization */
744
ret = gnutls_dh_params_init(&mc->dh_params);
745
if(ret != GNUTLS_E_SUCCESS){
746
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
747
" initialization: %s\n",
748
safer_gnutls_strerror(ret));
751
/* If a Diffie-Hellman parameters file was given, try to use it */
752
if(dhparamsfilename != NULL){
753
gnutls_datum_t params = { .data = NULL, .size = 0 };
755
int dhpfile = open(dhparamsfilename, O_RDONLY);
758
dhparamsfilename = NULL;
761
size_t params_capacity = 0;
763
params_capacity = incbuffer((char **)¶ms.data,
765
(size_t)params_capacity);
766
if(params_capacity == 0){
767
perror_plus("incbuffer");
770
dhparamsfilename = NULL;
773
ssize_t bytes_read = read(dhpfile,
774
params.data + params.size,
780
/* check bytes_read for failure */
785
dhparamsfilename = NULL;
788
params.size += (unsigned int)bytes_read;
790
ret = close(dhpfile);
792
perror_plus("close");
794
if(params.data == NULL){
795
dhparamsfilename = NULL;
797
if(dhparamsfilename == NULL){
800
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
801
GNUTLS_X509_FMT_PEM);
802
if(ret != GNUTLS_E_SUCCESS){
803
fprintf_plus(stderr, "Failed to parse DH parameters in file"
804
" \"%s\": %s\n", dhparamsfilename,
805
safer_gnutls_strerror(ret));
806
dhparamsfilename = NULL;
811
if(dhparamsfilename == NULL){
812
if(mc->dh_bits == 0){
813
/* Find out the optimal number of DH bits */
814
/* Try to read the private key file */
815
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
817
int secfile = open(seckeyfilename, O_RDONLY);
822
size_t buffer_capacity = 0;
824
buffer_capacity = incbuffer((char **)&buffer.data,
826
(size_t)buffer_capacity);
827
if(buffer_capacity == 0){
828
perror_plus("incbuffer");
833
ssize_t bytes_read = read(secfile,
834
buffer.data + buffer.size,
840
/* check bytes_read for failure */
847
buffer.size += (unsigned int)bytes_read;
851
/* If successful, use buffer to parse private key */
852
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
853
if(buffer.data != NULL){
855
gnutls_openpgp_privkey_t privkey = NULL;
856
ret = gnutls_openpgp_privkey_init(&privkey);
857
if(ret != GNUTLS_E_SUCCESS){
858
fprintf_plus(stderr, "Error initializing OpenPGP key"
860
safer_gnutls_strerror(ret));
864
ret = gnutls_openpgp_privkey_import
865
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
866
if(ret != GNUTLS_E_SUCCESS){
867
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
868
safer_gnutls_strerror(ret));
874
/* Use private key to suggest an appropriate
876
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
877
gnutls_openpgp_privkey_deinit(privkey);
879
fprintf_plus(stderr, "This OpenPGP key implies using"
880
" a GnuTLS security parameter \"%s\".\n",
881
safe_string(gnutls_sec_param_get_name
887
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
888
/* Err on the side of caution */
889
sec_param = GNUTLS_SEC_PARAM_ULTRA;
891
fprintf_plus(stderr, "Falling back to security parameter"
893
safe_string(gnutls_sec_param_get_name
898
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
902
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
903
" implies %u DH bits; using that.\n",
904
safe_string(gnutls_sec_param_get_name
909
fprintf_plus(stderr, "Failed to get implied number of DH"
910
" bits for security parameter \"%s\"): %s\n",
911
safe_string(gnutls_sec_param_get_name
913
safer_gnutls_strerror(ret));
917
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
920
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
921
if(ret != GNUTLS_E_SUCCESS){
922
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
923
" bits): %s\n", mc->dh_bits,
924
safer_gnutls_strerror(ret));
928
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
934
gnutls_certificate_free_credentials(mc->cred);
935
gnutls_dh_params_deinit(mc->dh_params);
939
__attribute__((nonnull, warn_unused_result))
940
static int init_gnutls_session(gnutls_session_t *session,
943
/* GnuTLS session creation */
945
ret = gnutls_init(session, GNUTLS_SERVER);
949
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
950
if(ret != GNUTLS_E_SUCCESS){
952
"Error in GnuTLS session initialization: %s\n",
953
safer_gnutls_strerror(ret));
959
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
961
gnutls_deinit(*session);
964
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
965
if(ret != GNUTLS_E_SUCCESS){
966
fprintf_plus(stderr, "Syntax error at: %s\n", err);
967
fprintf_plus(stderr, "GnuTLS error: %s\n",
968
safer_gnutls_strerror(ret));
969
gnutls_deinit(*session);
975
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
978
gnutls_deinit(*session);
981
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
982
if(ret != GNUTLS_E_SUCCESS){
983
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
984
safer_gnutls_strerror(ret));
985
gnutls_deinit(*session);
989
/* ignore client certificate if any. */
990
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
995
/* Avahi log function callback */
996
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
997
__attribute__((unused)) const char *txt){}
999
/* Helper function to add_local_route() and delete_local_route() */
1000
__attribute__((nonnull, warn_unused_result))
1001
static bool add_delete_local_route(const bool add,
1002
const char *address,
1003
AvahiIfIndex if_index){
1005
char helper[] = "mandos-client-iprouteadddel";
1006
char add_arg[] = "add";
1007
char delete_arg[] = "delete";
1008
char debug_flag[] = "--debug";
1009
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
1010
if(pluginhelperdir == NULL){
1012
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
1013
" variable not set; cannot run helper\n");
1018
char interface[IF_NAMESIZE];
1019
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1020
perror_plus("if_indextoname");
1024
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1026
perror_plus("open(\"/dev/null\", O_RDONLY)");
1032
/* Raise privileges */
1033
errno = raise_privileges_permanently();
1035
perror_plus("Failed to raise privileges");
1036
/* _exit(EX_NOPERM); */
1042
perror_plus("setgid");
1045
/* Reset supplementary groups */
1047
ret = setgroups(0, NULL);
1049
perror_plus("setgroups");
1053
ret = dup2(devnull, STDIN_FILENO);
1055
perror_plus("dup2(devnull, STDIN_FILENO)");
1058
ret = close(devnull);
1060
perror_plus("close");
1063
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1065
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1068
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
1073
if(helperdir_fd == -1){
1074
perror_plus("open");
1075
_exit(EX_UNAVAILABLE);
1077
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
1079
if(helper_fd == -1){
1080
perror_plus("openat");
1081
close(helperdir_fd);
1082
_exit(EX_UNAVAILABLE);
1084
close(helperdir_fd);
1086
#pragma GCC diagnostic push
1087
#pragma GCC diagnostic ignored "-Wcast-qual"
1089
if(fexecve(helper_fd, (char *const [])
1090
{ helper, add ? add_arg : delete_arg, (char *)address,
1091
interface, debug ? debug_flag : NULL, NULL },
1094
#pragma GCC diagnostic pop
1096
perror_plus("fexecve");
1097
_exit(EXIT_FAILURE);
1101
perror_plus("fork");
1108
pret = waitpid(pid, &status, 0);
1109
if(pret == -1 and errno == EINTR and quit_now){
1110
int errno_raising = 0;
1111
if((errno = raise_privileges()) != 0){
1112
errno_raising = errno;
1113
perror_plus("Failed to raise privileges in order to"
1114
" kill helper program");
1116
if(kill(pid, SIGTERM) == -1){
1117
perror_plus("kill");
1119
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
1120
perror_plus("Failed to lower privileges after killing"
1125
} while(pret == -1 and errno == EINTR);
1127
perror_plus("waitpid");
1130
if(WIFEXITED(status)){
1131
if(WEXITSTATUS(status) != 0){
1132
fprintf_plus(stderr, "Error: iprouteadddel exited"
1133
" with status %d\n", WEXITSTATUS(status));
1138
if(WIFSIGNALED(status)){
1139
fprintf_plus(stderr, "Error: iprouteadddel died by"
1140
" signal %d\n", WTERMSIG(status));
1143
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1147
__attribute__((nonnull, warn_unused_result))
1148
static bool add_local_route(const char *address,
1149
AvahiIfIndex if_index){
1151
fprintf_plus(stderr, "Adding route to %s\n", address);
1153
return add_delete_local_route(true, address, if_index);
1156
__attribute__((nonnull, warn_unused_result))
1157
static bool delete_local_route(const char *address,
1158
AvahiIfIndex if_index){
1160
fprintf_plus(stderr, "Removing route to %s\n", address);
1162
return add_delete_local_route(false, address, if_index);
1165
/* Called when a Mandos server is found */
1166
__attribute__((nonnull, warn_unused_result))
1167
static int start_mandos_communication(const char *ip, in_port_t port,
1168
AvahiIfIndex if_index,
1169
int af, mandos_context *mc){
1170
int ret, tcp_sd = -1;
1172
struct sockaddr_storage to;
1173
char *buffer = NULL;
1174
char *decrypted_buffer = NULL;
1175
size_t buffer_length = 0;
1176
size_t buffer_capacity = 0;
1179
gnutls_session_t session;
1180
int pf; /* Protocol family */
1181
bool route_added = false;
1198
fprintf_plus(stderr, "Bad address family: %d\n", af);
1203
/* If the interface is specified and we have a list of interfaces */
1204
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1205
/* Check if the interface is one of the interfaces we are using */
1208
char *interface = NULL;
1209
while((interface = argz_next(mc->interfaces,
1210
mc->interfaces_size,
1212
if(if_nametoindex(interface) == (unsigned int)if_index){
1219
/* This interface does not match any in the list, so we don't
1220
connect to the server */
1222
char interface[IF_NAMESIZE];
1223
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1224
perror_plus("if_indextoname");
1226
fprintf_plus(stderr, "Skipping server on non-used interface"
1228
if_indextoname((unsigned int)if_index,
1236
ret = init_gnutls_session(&session, mc);
1242
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1243
PRIuMAX "\n", ip, (uintmax_t)port);
1246
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1249
perror_plus("socket");
1260
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1261
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1262
ret = inet_pton(af, ip, &to6->sin6_addr);
1264
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1265
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1266
ret = inet_pton(af, ip, &to4->sin_addr);
1270
perror_plus("inet_pton");
1276
fprintf_plus(stderr, "Bad address: %s\n", ip);
1281
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1282
if(IN6_IS_ADDR_LINKLOCAL
1283
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1284
if(if_index == AVAHI_IF_UNSPEC){
1285
fprintf_plus(stderr, "An IPv6 link-local address is"
1286
" incomplete without a network interface\n");
1290
/* Set the network interface number as scope */
1291
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1294
((struct sockaddr_in *)&to)->sin_port = htons(port);
1303
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1304
char interface[IF_NAMESIZE];
1305
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1306
perror_plus("if_indextoname");
1308
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1309
"\n", ip, interface, (uintmax_t)port);
1312
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1313
ip, (uintmax_t)port);
1315
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1316
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1318
ret = getnameinfo((struct sockaddr *)&to,
1319
sizeof(struct sockaddr_in6),
1320
addrstr, sizeof(addrstr), NULL, 0,
1323
ret = getnameinfo((struct sockaddr *)&to,
1324
sizeof(struct sockaddr_in),
1325
addrstr, sizeof(addrstr), NULL, 0,
1328
if(ret == EAI_SYSTEM){
1329
perror_plus("getnameinfo");
1330
} else if(ret != 0) {
1331
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1332
} else if(strcmp(addrstr, ip) != 0){
1333
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1344
ret = connect(tcp_sd, (struct sockaddr *)&to,
1345
sizeof(struct sockaddr_in6));
1347
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1348
sizeof(struct sockaddr_in));
1351
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1352
and if_index != AVAHI_IF_UNSPEC
1353
and connect_to == NULL
1354
and not route_added and
1355
((af == AF_INET6 and not
1356
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1358
or (af == AF_INET and
1359
/* Not a a IPv4LL address */
1360
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1361
& 0xFFFF0000L) != 0xA9FE0000L))){
1362
/* Work around Avahi bug - Avahi does not announce link-local
1363
addresses if it has a global address, so local hosts with
1364
*only* a link-local address (e.g. Mandos clients) cannot
1365
connect to a Mandos server announced by Avahi on a server
1366
host with a global address. Work around this by retrying
1367
with an explicit route added with the server's address.
1369
Avahi bug reference:
1370
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1371
https://bugs.debian.org/587961
1374
fprintf_plus(stderr, "Mandos server unreachable, trying"
1378
route_added = add_local_route(ip, if_index);
1384
if(errno != ECONNREFUSED or debug){
1386
perror_plus("connect");
1399
const char *out = mandos_protocol_version;
1402
size_t out_size = strlen(out);
1403
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1404
out_size - written));
1407
perror_plus("write");
1411
written += (size_t)ret;
1412
if(written < out_size){
1415
if(out == mandos_protocol_version){
1430
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1438
/* This casting via intptr_t is to eliminate warning about casting
1439
an int to a pointer type. This is exactly how the GnuTLS Guile
1440
function "set-session-transport-fd!" does it. */
1441
gnutls_transport_set_ptr(session,
1442
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1450
ret = gnutls_handshake(session);
1455
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1457
if(ret != GNUTLS_E_SUCCESS){
1459
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1466
/* Read OpenPGP packet that contains the wanted password */
1469
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1480
buffer_capacity = incbuffer(&buffer, buffer_length,
1482
if(buffer_capacity == 0){
1484
perror_plus("incbuffer");
1494
sret = gnutls_record_recv(session, buffer+buffer_length,
1501
case GNUTLS_E_INTERRUPTED:
1502
case GNUTLS_E_AGAIN:
1504
case GNUTLS_E_REHANDSHAKE:
1506
ret = gnutls_handshake(session);
1512
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1514
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
1522
fprintf_plus(stderr, "Unknown error while reading data from"
1523
" encrypted session with Mandos server\n");
1524
gnutls_bye(session, GNUTLS_SHUT_RDWR);
1529
buffer_length += (size_t) sret;
1534
fprintf_plus(stderr, "Closing TLS session\n");
1543
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1548
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1550
if(buffer_length > 0){
1551
ssize_t decrypted_buffer_size;
1552
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1553
&decrypted_buffer, mc);
1554
if(decrypted_buffer_size >= 0){
1558
while(written < (size_t) decrypted_buffer_size){
1564
ret = (int)fwrite(decrypted_buffer + written, 1,
1565
(size_t)decrypted_buffer_size - written,
1567
if(ret == 0 and ferror(stdout)){
1570
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1576
written += (size_t)ret;
1578
ret = fflush(stdout);
1582
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1592
/* Shutdown procedure */
1597
if(not delete_local_route(ip, if_index)){
1598
fprintf_plus(stderr, "Failed to delete local route to %s on"
1599
" interface %d", ip, if_index);
1603
free(decrypted_buffer);
1606
ret = close(tcp_sd);
1612
perror_plus("close");
1614
gnutls_deinit(session);
1624
static void resolve_callback(AvahiSServiceResolver *r,
1625
AvahiIfIndex interface,
1626
AvahiProtocol proto,
1627
AvahiResolverEvent event,
1631
const char *host_name,
1632
const AvahiAddress *address,
1634
AVAHI_GCC_UNUSED AvahiStringList *txt,
1635
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1642
/* Called whenever a service has been resolved successfully or
1646
avahi_s_service_resolver_free(r);
1652
case AVAHI_RESOLVER_FAILURE:
1653
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1654
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1656
avahi_strerror(avahi_server_errno
1657
(((mandos_context*)mc)->server)));
1660
case AVAHI_RESOLVER_FOUND:
1662
char ip[AVAHI_ADDRESS_STR_MAX];
1663
avahi_address_snprint(ip, sizeof(ip), address);
1665
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1666
PRIdMAX ") on port %" PRIu16 "\n", name,
1667
host_name, ip, (intmax_t)interface, port);
1669
int ret = start_mandos_communication(ip, (in_port_t)port,
1671
avahi_proto_to_af(proto),
1674
avahi_simple_poll_quit(simple_poll);
1676
if(not add_server(ip, (in_port_t)port, interface,
1677
avahi_proto_to_af(proto),
1678
&((mandos_context*)mc)->current_server)){
1679
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1685
avahi_s_service_resolver_free(r);
1688
static void browse_callback(AvahiSServiceBrowser *b,
1689
AvahiIfIndex interface,
1690
AvahiProtocol protocol,
1691
AvahiBrowserEvent event,
1695
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1702
/* Called whenever a new services becomes available on the LAN or
1703
is removed from the LAN */
1711
case AVAHI_BROWSER_FAILURE:
1713
fprintf_plus(stderr, "(Avahi browser) %s\n",
1714
avahi_strerror(avahi_server_errno
1715
(((mandos_context*)mc)->server)));
1716
avahi_simple_poll_quit(simple_poll);
1719
case AVAHI_BROWSER_NEW:
1720
/* We ignore the returned Avahi resolver object. In the callback
1721
function we free it. If the Avahi server is terminated before
1722
the callback function is called the Avahi server will free the
1725
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1726
interface, protocol, name, type,
1727
domain, protocol, 0,
1728
resolve_callback, mc) == NULL)
1729
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1731
avahi_strerror(avahi_server_errno
1732
(((mandos_context*)mc)->server)));
1735
case AVAHI_BROWSER_REMOVE:
1738
case AVAHI_BROWSER_ALL_FOR_NOW:
1739
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1741
fprintf_plus(stderr, "No Mandos server found, still"
1748
/* Signal handler that stops main loop after SIGTERM */
1749
static void handle_sigterm(int sig){
1754
signal_received = sig;
1755
int old_errno = errno;
1756
/* set main loop to exit */
1757
if(simple_poll != NULL){
1758
avahi_simple_poll_quit(simple_poll);
1763
__attribute__((nonnull, warn_unused_result))
1764
bool get_flags(const char *ifname, struct ifreq *ifr){
1768
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1771
perror_plus("socket");
1775
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1776
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1777
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1781
perror_plus("ioctl SIOCGIFFLAGS");
1784
if((close(s) == -1) and debug){
1786
perror_plus("close");
1791
if((close(s) == -1) and debug){
1793
perror_plus("close");
1799
__attribute__((nonnull, warn_unused_result))
1800
bool good_flags(const char *ifname, const struct ifreq *ifr){
1802
/* Reject the loopback device */
1803
if(ifr->ifr_flags & IFF_LOOPBACK){
1805
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1810
/* Accept point-to-point devices only if connect_to is specified */
1811
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1813
fprintf_plus(stderr, "Accepting point-to-point interface"
1814
" \"%s\"\n", ifname);
1818
/* Otherwise, reject non-broadcast-capable devices */
1819
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1821
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1822
" \"%s\"\n", ifname);
1826
/* Reject non-ARP interfaces (including dummy interfaces) */
1827
if(ifr->ifr_flags & IFF_NOARP){
1829
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1835
/* Accept this device */
1837
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1843
* This function determines if a directory entry in /sys/class/net
1844
* corresponds to an acceptable network device.
1845
* (This function is passed to scandir(3) as a filter function.)
1847
__attribute__((nonnull, warn_unused_result))
1848
int good_interface(const struct dirent *if_entry){
1849
if(if_entry->d_name[0] == '.'){
1854
if(not get_flags(if_entry->d_name, &ifr)){
1856
fprintf_plus(stderr, "Failed to get flags for interface "
1857
"\"%s\"\n", if_entry->d_name);
1862
if(not good_flags(if_entry->d_name, &ifr)){
1869
* This function determines if a network interface is up.
1871
__attribute__((nonnull, warn_unused_result))
1872
bool interface_is_up(const char *interface){
1874
if(not get_flags(interface, &ifr)){
1876
fprintf_plus(stderr, "Failed to get flags for interface "
1877
"\"%s\"\n", interface);
1882
return (bool)(ifr.ifr_flags & IFF_UP);
1886
* This function determines if a network interface is running
1888
__attribute__((nonnull, warn_unused_result))
1889
bool interface_is_running(const char *interface){
1891
if(not get_flags(interface, &ifr)){
1893
fprintf_plus(stderr, "Failed to get flags for interface "
1894
"\"%s\"\n", interface);
1899
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1902
__attribute__((nonnull, pure, warn_unused_result))
1903
int notdotentries(const struct dirent *direntry){
1904
/* Skip "." and ".." */
1905
if(direntry->d_name[0] == '.'
1906
and (direntry->d_name[1] == '\0'
1907
or (direntry->d_name[1] == '.'
1908
and direntry->d_name[2] == '\0'))){
1914
/* Is this directory entry a runnable program? */
1915
__attribute__((nonnull, warn_unused_result))
1916
int runnable_hook(const struct dirent *direntry){
1921
if((direntry->d_name)[0] == '\0'){
1926
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1927
"abcdefghijklmnopqrstuvwxyz"
1930
if((direntry->d_name)[sret] != '\0'){
1931
/* Contains non-allowed characters */
1933
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1939
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1942
perror_plus("Could not stat hook");
1946
if(not (S_ISREG(st.st_mode))){
1947
/* Not a regular file */
1949
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1954
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1955
/* Not executable */
1957
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1963
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1969
__attribute__((nonnull, warn_unused_result))
1970
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1971
mandos_context *mc){
1973
struct timespec now;
1974
struct timespec waited_time;
1975
intmax_t block_time;
1978
if(mc->current_server == NULL){
1980
fprintf_plus(stderr, "Wait until first server is found."
1983
ret = avahi_simple_poll_iterate(s, -1);
1986
fprintf_plus(stderr, "Check current_server if we should run"
1989
/* the current time */
1990
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1992
perror_plus("clock_gettime");
1995
/* Calculating in ms how long time between now and server
1996
who we visted longest time ago. Now - last seen. */
1997
waited_time.tv_sec = (now.tv_sec
1998
- mc->current_server->last_seen.tv_sec);
1999
waited_time.tv_nsec = (now.tv_nsec
2000
- mc->current_server->last_seen.tv_nsec);
2001
/* total time is 10s/10,000ms.
2002
Converting to s from ms by dividing by 1,000,
2003
and ns to ms by dividing by 1,000,000. */
2004
block_time = ((retry_interval
2005
- ((intmax_t)waited_time.tv_sec * 1000))
2006
- ((intmax_t)waited_time.tv_nsec / 1000000));
2009
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
2013
if(block_time <= 0){
2014
ret = start_mandos_communication(mc->current_server->ip,
2015
mc->current_server->port,
2016
mc->current_server->if_index,
2017
mc->current_server->af, mc);
2019
avahi_simple_poll_quit(s);
2022
ret = clock_gettime(CLOCK_MONOTONIC,
2023
&mc->current_server->last_seen);
2025
perror_plus("clock_gettime");
2028
mc->current_server = mc->current_server->next;
2029
block_time = 0; /* Call avahi to find new Mandos
2030
servers, but don't block */
2033
ret = avahi_simple_poll_iterate(s, (int)block_time);
2036
if(ret > 0 or errno != EINTR){
2037
return (ret != 1) ? ret : 0;
2043
__attribute__((nonnull))
2044
void run_network_hooks(const char *mode, const char *interface,
2046
struct dirent **direntries = NULL;
2047
if(hookdir_fd == -1){
2048
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
2050
if(hookdir_fd == -1){
2051
if(errno == ENOENT){
2053
fprintf_plus(stderr, "Network hook directory \"%s\" not"
2054
" found\n", hookdir);
2057
perror_plus("open");
2062
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2064
perror_plus("open(\"/dev/null\", O_RDONLY)");
2067
int numhooks = scandirat(hookdir_fd, ".", &direntries,
2068
runnable_hook, alphasort);
2070
perror_plus("scandir");
2074
struct dirent *direntry;
2076
for(int i = 0; i < numhooks; i++){
2077
direntry = direntries[i];
2079
fprintf_plus(stderr, "Running network hook \"%s\"\n",
2082
pid_t hook_pid = fork();
2085
/* Raise privileges */
2086
errno = raise_privileges_permanently();
2088
perror_plus("Failed to raise privileges");
2095
perror_plus("setgid");
2098
/* Reset supplementary groups */
2100
ret = setgroups(0, NULL);
2102
perror_plus("setgroups");
2105
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
2107
perror_plus("setenv");
2110
ret = setenv("DEVICE", interface, 1);
2112
perror_plus("setenv");
2115
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
2117
perror_plus("setenv");
2120
ret = setenv("MODE", mode, 1);
2122
perror_plus("setenv");
2126
ret = asprintf(&delaystring, "%f", (double)delay);
2128
perror_plus("asprintf");
2131
ret = setenv("DELAY", delaystring, 1);
2134
perror_plus("setenv");
2138
if(connect_to != NULL){
2139
ret = setenv("CONNECT", connect_to, 1);
2141
perror_plus("setenv");
2145
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2149
perror_plus("openat");
2150
_exit(EXIT_FAILURE);
2152
if(close(hookdir_fd) == -1){
2153
perror_plus("close");
2154
_exit(EXIT_FAILURE);
2156
ret = dup2(devnull, STDIN_FILENO);
2158
perror_plus("dup2(devnull, STDIN_FILENO)");
2161
ret = close(devnull);
2163
perror_plus("close");
2166
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2168
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2171
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2173
perror_plus("fexecve");
2174
_exit(EXIT_FAILURE);
2178
perror_plus("fork");
2183
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2184
perror_plus("waitpid");
2188
if(WIFEXITED(status)){
2189
if(WEXITSTATUS(status) != 0){
2190
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2191
" with status %d\n", direntry->d_name,
2192
WEXITSTATUS(status));
2196
} else if(WIFSIGNALED(status)){
2197
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2198
" signal %d\n", direntry->d_name,
2203
fprintf_plus(stderr, "Warning: network hook \"%s\""
2204
" crashed\n", direntry->d_name);
2210
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2216
if(close(hookdir_fd) == -1){
2217
perror_plus("close");
2224
__attribute__((nonnull, warn_unused_result))
2225
int bring_up_interface(const char *const interface,
2227
int old_errno = errno;
2229
struct ifreq network;
2230
unsigned int if_index = if_nametoindex(interface);
2232
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2242
if(not interface_is_up(interface)){
2244
int ioctl_errno = 0;
2245
if(not get_flags(interface, &network)){
2247
fprintf_plus(stderr, "Failed to get flags for interface "
2248
"\"%s\"\n", interface);
2252
network.ifr_flags |= IFF_UP; /* set flag */
2254
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2257
perror_plus("socket");
2265
perror_plus("close");
2272
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2276
/* Raise privileges */
2277
ret_errno = raise_privileges();
2280
perror_plus("Failed to raise privileges");
2285
bool restore_loglevel = false;
2287
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2288
messages about the network interface to mess up the prompt */
2289
ret_linux = klogctl(8, NULL, 5);
2290
if(ret_linux == -1){
2291
perror_plus("klogctl");
2293
restore_loglevel = true;
2296
#endif /* __linux__ */
2297
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2298
ioctl_errno = errno;
2300
if(restore_loglevel){
2301
ret_linux = klogctl(7, NULL, 0);
2302
if(ret_linux == -1){
2303
perror_plus("klogctl");
2306
#endif /* __linux__ */
2308
/* If raise_privileges() succeeded above */
2310
/* Lower privileges */
2311
ret_errno = lower_privileges();
2314
perror_plus("Failed to lower privileges");
2318
/* Close the socket */
2321
perror_plus("close");
2324
if(ret_setflags == -1){
2325
errno = ioctl_errno;
2326
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2331
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2335
/* Sleep checking until interface is running.
2336
Check every 0.25s, up to total time of delay */
2337
for(int i = 0; i < delay * 4; i++){
2338
if(interface_is_running(interface)){
2341
struct timespec sleeptime = { .tv_nsec = 250000000 };
2342
ret = nanosleep(&sleeptime, NULL);
2343
if(ret == -1 and errno != EINTR){
2344
perror_plus("nanosleep");
2352
__attribute__((nonnull, warn_unused_result))
2353
int take_down_interface(const char *const interface){
2354
int old_errno = errno;
2355
struct ifreq network;
2356
unsigned int if_index = if_nametoindex(interface);
2358
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2362
if(interface_is_up(interface)){
2364
int ioctl_errno = 0;
2365
if(not get_flags(interface, &network) and debug){
2367
fprintf_plus(stderr, "Failed to get flags for interface "
2368
"\"%s\"\n", interface);
2372
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2374
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2377
perror_plus("socket");
2383
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2387
/* Raise privileges */
2388
ret_errno = raise_privileges();
2391
perror_plus("Failed to raise privileges");
2394
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2395
ioctl_errno = errno;
2397
/* If raise_privileges() succeeded above */
2399
/* Lower privileges */
2400
ret_errno = lower_privileges();
2403
perror_plus("Failed to lower privileges");
2407
/* Close the socket */
2408
int ret = close(sd);
2410
perror_plus("close");
2413
if(ret_setflags == -1){
2414
errno = ioctl_errno;
2415
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2420
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2428
int main(int argc, char *argv[]){
2429
mandos_context mc = { .server = NULL, .dh_bits = 0,
2430
.priority = "SECURE256:!CTYPE-X.509"
2431
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2432
.current_server = NULL, .interfaces = NULL,
2433
.interfaces_size = 0 };
2434
AvahiSServiceBrowser *sb = NULL;
2439
int exitcode = EXIT_SUCCESS;
2440
char *interfaces_to_take_down = NULL;
2441
size_t interfaces_to_take_down_size = 0;
2442
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2443
char old_tempdir[] = "/tmp/mandosXXXXXX";
2444
char *tempdir = NULL;
2445
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2446
const char *seckey = PATHDIR "/" SECKEY;
2447
const char *pubkey = PATHDIR "/" PUBKEY;
2448
const char *dh_params_file = NULL;
2449
char *interfaces_hooks = NULL;
2451
bool gnutls_initialized = false;
2452
bool gpgme_initialized = false;
2454
double retry_interval = 10; /* 10s between trying a server and
2455
retrying the same server again */
2457
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2458
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2463
/* Lower any group privileges we might have, just to be safe */
2467
perror_plus("setgid");
2470
/* Lower user privileges (temporarily) */
2474
perror_plus("seteuid");
2482
struct argp_option options[] = {
2483
{ .name = "debug", .key = 128,
2484
.doc = "Debug mode", .group = 3 },
2485
{ .name = "connect", .key = 'c',
2486
.arg = "ADDRESS:PORT",
2487
.doc = "Connect directly to a specific Mandos server",
2489
{ .name = "interface", .key = 'i',
2491
.doc = "Network interface that will be used to search for"
2494
{ .name = "seckey", .key = 's',
2496
.doc = "OpenPGP secret key file base name",
2498
{ .name = "pubkey", .key = 'p',
2500
.doc = "OpenPGP public key file base name",
2502
{ .name = "dh-bits", .key = 129,
2504
.doc = "Bit length of the prime number used in the"
2505
" Diffie-Hellman key exchange",
2507
{ .name = "dh-params", .key = 134,
2509
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2510
" for the Diffie-Hellman key exchange",
2512
{ .name = "priority", .key = 130,
2514
.doc = "GnuTLS priority string for the TLS handshake",
2516
{ .name = "delay", .key = 131,
2518
.doc = "Maximum delay to wait for interface startup",
2520
{ .name = "retry", .key = 132,
2522
.doc = "Retry interval used when denied by the Mandos server",
2524
{ .name = "network-hook-dir", .key = 133,
2526
.doc = "Directory where network hooks are located",
2529
* These reproduce what we would get without ARGP_NO_HELP
2531
{ .name = "help", .key = '?',
2532
.doc = "Give this help list", .group = -1 },
2533
{ .name = "usage", .key = -3,
2534
.doc = "Give a short usage message", .group = -1 },
2535
{ .name = "version", .key = 'V',
2536
.doc = "Print program version", .group = -1 },
2540
error_t parse_opt(int key, char *arg,
2541
struct argp_state *state){
2544
case 128: /* --debug */
2547
case 'c': /* --connect */
2550
case 'i': /* --interface */
2551
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2554
argp_error(state, "%s", strerror(ret_errno));
2557
case 's': /* --seckey */
2560
case 'p': /* --pubkey */
2563
case 129: /* --dh-bits */
2565
tmpmax = strtoimax(arg, &tmp, 10);
2566
if(errno != 0 or tmp == arg or *tmp != '\0'
2567
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2568
argp_error(state, "Bad number of DH bits");
2570
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2572
case 134: /* --dh-params */
2573
dh_params_file = arg;
2575
case 130: /* --priority */
2578
case 131: /* --delay */
2580
delay = strtof(arg, &tmp);
2581
if(errno != 0 or tmp == arg or *tmp != '\0'){
2582
argp_error(state, "Bad delay");
2584
case 132: /* --retry */
2586
retry_interval = strtod(arg, &tmp);
2587
if(errno != 0 or tmp == arg or *tmp != '\0'
2588
or (retry_interval * 1000) > INT_MAX
2589
or retry_interval < 0){
2590
argp_error(state, "Bad retry interval");
2593
case 133: /* --network-hook-dir */
2597
* These reproduce what we would get without ARGP_NO_HELP
2599
case '?': /* --help */
2600
argp_state_help(state, state->out_stream,
2601
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2602
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2603
case -3: /* --usage */
2604
argp_state_help(state, state->out_stream,
2605
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2606
case 'V': /* --version */
2607
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2608
exit(argp_err_exit_status);
2611
return ARGP_ERR_UNKNOWN;
2616
struct argp argp = { .options = options, .parser = parse_opt,
2618
.doc = "Mandos client -- Get and decrypt"
2619
" passwords from a Mandos server" };
2620
ret_errno = argp_parse(&argp, argc, argv,
2621
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2628
perror_plus("argp_parse");
2629
exitcode = EX_OSERR;
2632
exitcode = EX_USAGE;
2638
/* Work around Debian bug #633582:
2639
<https://bugs.debian.org/633582> */
2641
/* Re-raise privileges */
2642
ret = raise_privileges();
2645
perror_plus("Failed to raise privileges");
2649
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2650
int seckey_fd = open(seckey, O_RDONLY);
2651
if(seckey_fd == -1){
2652
perror_plus("open");
2654
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2656
perror_plus("fstat");
2658
if(S_ISREG(st.st_mode)
2659
and st.st_uid == 0 and st.st_gid == 0){
2660
ret = fchown(seckey_fd, uid, gid);
2662
perror_plus("fchown");
2670
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2671
int pubkey_fd = open(pubkey, O_RDONLY);
2672
if(pubkey_fd == -1){
2673
perror_plus("open");
2675
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2677
perror_plus("fstat");
2679
if(S_ISREG(st.st_mode)
2680
and st.st_uid == 0 and st.st_gid == 0){
2681
ret = fchown(pubkey_fd, uid, gid);
2683
perror_plus("fchown");
2691
if(dh_params_file != NULL
2692
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2693
int dhparams_fd = open(dh_params_file, O_RDONLY);
2694
if(dhparams_fd == -1){
2695
perror_plus("open");
2697
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2699
perror_plus("fstat");
2701
if(S_ISREG(st.st_mode)
2702
and st.st_uid == 0 and st.st_gid == 0){
2703
ret = fchown(dhparams_fd, uid, gid);
2705
perror_plus("fchown");
2713
/* Lower privileges */
2714
ret = lower_privileges();
2717
perror_plus("Failed to lower privileges");
2722
/* Remove invalid interface names (except "none") */
2724
char *interface = NULL;
2725
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2727
if(strcmp(interface, "none") != 0
2728
and if_nametoindex(interface) == 0){
2729
if(interface[0] != '\0'){
2730
fprintf_plus(stderr, "Not using nonexisting interface"
2731
" \"%s\"\n", interface);
2733
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2739
/* Run network hooks */
2741
if(mc.interfaces != NULL){
2742
interfaces_hooks = malloc(mc.interfaces_size);
2743
if(interfaces_hooks == NULL){
2744
perror_plus("malloc");
2747
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2748
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2750
run_network_hooks("start", interfaces_hooks != NULL ?
2751
interfaces_hooks : "", delay);
2755
avahi_set_log_function(empty_log);
2758
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2759
from the signal handler */
2760
/* Initialize the pseudo-RNG for Avahi */
2761
srand((unsigned int) time(NULL));
2762
simple_poll = avahi_simple_poll_new();
2763
if(simple_poll == NULL){
2764
fprintf_plus(stderr,
2765
"Avahi: Failed to create simple poll object.\n");
2766
exitcode = EX_UNAVAILABLE;
2770
sigemptyset(&sigterm_action.sa_mask);
2771
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2773
perror_plus("sigaddset");
2774
exitcode = EX_OSERR;
2777
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2779
perror_plus("sigaddset");
2780
exitcode = EX_OSERR;
2783
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2785
perror_plus("sigaddset");
2786
exitcode = EX_OSERR;
2789
/* Need to check if the handler is SIG_IGN before handling:
2790
| [[info:libc:Initial Signal Actions]] |
2791
| [[info:libc:Basic Signal Handling]] |
2793
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2795
perror_plus("sigaction");
2798
if(old_sigterm_action.sa_handler != SIG_IGN){
2799
ret = sigaction(SIGINT, &sigterm_action, NULL);
2801
perror_plus("sigaction");
2802
exitcode = EX_OSERR;
2806
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2808
perror_plus("sigaction");
2811
if(old_sigterm_action.sa_handler != SIG_IGN){
2812
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2814
perror_plus("sigaction");
2815
exitcode = EX_OSERR;
2819
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2821
perror_plus("sigaction");
2824
if(old_sigterm_action.sa_handler != SIG_IGN){
2825
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2827
perror_plus("sigaction");
2828
exitcode = EX_OSERR;
2833
/* If no interfaces were specified, make a list */
2834
if(mc.interfaces == NULL){
2835
struct dirent **direntries = NULL;
2836
/* Look for any good interfaces */
2837
ret = scandir(sys_class_net, &direntries, good_interface,
2840
/* Add all found interfaces to interfaces list */
2841
for(int i = 0; i < ret; ++i){
2842
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2843
direntries[i]->d_name);
2846
perror_plus("argz_add");
2847
free(direntries[i]);
2851
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2852
direntries[i]->d_name);
2854
free(direntries[i]);
2861
fprintf_plus(stderr, "Could not find a network interface\n");
2862
exitcode = EXIT_FAILURE;
2867
/* Bring up interfaces which are down, and remove any "none"s */
2869
char *interface = NULL;
2870
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2872
/* If interface name is "none", stop bringing up interfaces.
2873
Also remove all instances of "none" from the list */
2874
if(strcmp(interface, "none") == 0){
2875
argz_delete(&mc.interfaces, &mc.interfaces_size,
2878
while((interface = argz_next(mc.interfaces,
2879
mc.interfaces_size, interface))){
2880
if(strcmp(interface, "none") == 0){
2881
argz_delete(&mc.interfaces, &mc.interfaces_size,
2888
bool interface_was_up = interface_is_up(interface);
2889
errno = bring_up_interface(interface, delay);
2890
if(not interface_was_up){
2892
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2893
" %s\n", interface, strerror(errno));
2895
errno = argz_add(&interfaces_to_take_down,
2896
&interfaces_to_take_down_size,
2899
perror_plus("argz_add");
2904
if(debug and (interfaces_to_take_down == NULL)){
2905
fprintf_plus(stderr, "No interfaces were brought up\n");
2909
/* If we only got one interface, explicitly use only that one */
2910
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2912
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2915
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2922
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2924
fprintf_plus(stderr, "init_gnutls_global failed\n");
2925
exitcode = EX_UNAVAILABLE;
2928
gnutls_initialized = true;
2935
/* Try /run/tmp before /tmp */
2936
tempdir = mkdtemp(run_tempdir);
2937
if(tempdir == NULL and errno == ENOENT){
2939
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2940
run_tempdir, old_tempdir);
2942
tempdir = mkdtemp(old_tempdir);
2944
if(tempdir == NULL){
2945
perror_plus("mkdtemp");
2953
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2954
fprintf_plus(stderr, "init_gpgme failed\n");
2955
exitcode = EX_UNAVAILABLE;
2958
gpgme_initialized = true;
2965
if(connect_to != NULL){
2966
/* Connect directly, do not use Zeroconf */
2967
/* (Mainly meant for debugging) */
2968
char *address = strrchr(connect_to, ':');
2970
if(address == NULL){
2971
fprintf_plus(stderr, "No colon in address\n");
2972
exitcode = EX_USAGE;
2982
tmpmax = strtoimax(address+1, &tmp, 10);
2983
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2984
or tmpmax != (in_port_t)tmpmax){
2985
fprintf_plus(stderr, "Bad port number\n");
2986
exitcode = EX_USAGE;
2994
port = (in_port_t)tmpmax;
2996
/* Colon in address indicates IPv6 */
2998
if(strchr(connect_to, ':') != NULL){
3000
/* Accept [] around IPv6 address - see RFC 5952 */
3001
if(connect_to[0] == '[' and address[-1] == ']')
3009
address = connect_to;
3015
while(not quit_now){
3016
ret = start_mandos_communication(address, port, if_index, af,
3018
if(quit_now or ret == 0){
3022
fprintf_plus(stderr, "Retrying in %d seconds\n",
3023
(int)retry_interval);
3025
sleep((unsigned int)retry_interval);
3029
exitcode = EXIT_SUCCESS;
3040
AvahiServerConfig config;
3041
/* Do not publish any local Zeroconf records */
3042
avahi_server_config_init(&config);
3043
config.publish_hinfo = 0;
3044
config.publish_addresses = 0;
3045
config.publish_workstation = 0;
3046
config.publish_domain = 0;
3048
/* Allocate a new server */
3049
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3050
&config, NULL, NULL, &ret);
3052
/* Free the Avahi configuration data */
3053
avahi_server_config_free(&config);
3056
/* Check if creating the Avahi server object succeeded */
3057
if(mc.server == NULL){
3058
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3059
avahi_strerror(ret));
3060
exitcode = EX_UNAVAILABLE;
3068
/* Create the Avahi service browser */
3069
sb = avahi_s_service_browser_new(mc.server, if_index,
3070
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
3071
NULL, 0, browse_callback,
3074
fprintf_plus(stderr, "Failed to create service browser: %s\n",
3075
avahi_strerror(avahi_server_errno(mc.server)));
3076
exitcode = EX_UNAVAILABLE;
3084
/* Run the main loop */
3087
fprintf_plus(stderr, "Starting Avahi loop search\n");
3090
ret = avahi_loop_with_timeout(simple_poll,
3091
(int)(retry_interval * 1000), &mc);
3093
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
3094
(ret == 0) ? "successfully" : "with error");
3100
if(signal_received){
3101
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3102
argv[0], signal_received,
3103
strsignal(signal_received));
3105
fprintf_plus(stderr, "%s exiting\n", argv[0]);
3109
/* Cleanup things */
3110
free(mc.interfaces);
3113
avahi_s_service_browser_free(sb);
3115
if(mc.server != NULL)
3116
avahi_server_free(mc.server);
3118
if(simple_poll != NULL)
3119
avahi_simple_poll_free(simple_poll);
3121
if(gnutls_initialized){
3122
gnutls_certificate_free_credentials(mc.cred);
3123
gnutls_dh_params_deinit(mc.dh_params);
3126
if(gpgme_initialized){
3127
gpgme_release(mc.ctx);
3130
/* Cleans up the circular linked list of Mandos servers the client
3132
if(mc.current_server != NULL){
3133
mc.current_server->prev->next = NULL;
3134
while(mc.current_server != NULL){
3135
server *next = mc.current_server->next;
3137
#pragma GCC diagnostic push
3138
#pragma GCC diagnostic ignored "-Wcast-qual"
3140
free((char *)(mc.current_server->ip));
3142
#pragma GCC diagnostic pop
3144
free(mc.current_server);
3145
mc.current_server = next;
3149
/* Re-raise privileges */
3151
ret = raise_privileges();
3154
perror_plus("Failed to raise privileges");
3157
/* Run network hooks */
3158
run_network_hooks("stop", interfaces_hooks != NULL ?
3159
interfaces_hooks : "", delay);
3161
/* Take down the network interfaces which were brought up */
3163
char *interface = NULL;
3164
while((interface = argz_next(interfaces_to_take_down,
3165
interfaces_to_take_down_size,
3167
ret = take_down_interface(interface);
3170
perror_plus("Failed to take down interface");
3173
if(debug and (interfaces_to_take_down == NULL)){
3174
fprintf_plus(stderr, "No interfaces needed to be taken"
3180
ret = lower_privileges_permanently();
3183
perror_plus("Failed to lower privileges permanently");
3187
free(interfaces_to_take_down);
3188
free(interfaces_hooks);
3190
void clean_dir_at(int base, const char * const dirname,
3192
struct dirent **direntries = NULL;
3194
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3200
perror_plus("open");
3203
int numentries = scandirat(dir_fd, ".", &direntries,
3204
notdotentries, alphasort);
3205
if(numentries >= 0){
3206
for(int i = 0; i < numentries; i++){
3208
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3209
dirname, direntries[i]->d_name);
3211
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3213
if(errno == EISDIR){
3214
dret = unlinkat(dir_fd, direntries[i]->d_name,
3217
if((dret == -1) and (errno == ENOTEMPTY)
3218
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3219
== 0) and (level == 0)){
3220
/* Recurse only in this special case */
3221
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3224
if((dret == -1) and (errno != ENOENT)){
3225
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3226
direntries[i]->d_name, strerror(errno));
3229
free(direntries[i]);
3232
/* need to clean even if 0 because man page doesn't specify */
3234
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3235
if(dret == -1 and errno != ENOENT){
3236
perror_plus("rmdir");
3239
perror_plus("scandirat");
3244
/* Removes the GPGME temp directory and all files inside */
3245
if(tempdir != NULL){
3246
clean_dir_at(-1, tempdir, 0);
3250
sigemptyset(&old_sigterm_action.sa_mask);
3251
old_sigterm_action.sa_handler = SIG_DFL;
3252
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3253
&old_sigterm_action,
3256
perror_plus("sigaction");
3259
ret = raise(signal_received);
3260
} while(ret != 0 and errno == EINTR);
3262
perror_plus("raise");
3265
TEMP_FAILURE_RETRY(pause());