33
151
#include <avahi-common/malloc.h>
34
152
#include <avahi-common/error.h>
37
#include <sys/types.h> /* socket(), setsockopt(), inet_pton() */
38
#include <sys/socket.h> /* socket(), setsockopt(), struct sockaddr_in6, struct in6_addr, inet_pton() */
39
#include <gnutls/gnutls.h> /* ALL GNUTLS STUFF */
40
#include <gnutls/openpgp.h> /* gnutls with openpgp stuff */
42
#include <unistd.h> /* close() */
43
#include <netinet/in.h>
44
#include <stdbool.h> /* true */
45
#include <string.h> /* memset */
46
#include <arpa/inet.h> /* inet_pton() */
47
#include <iso646.h> /* not */
50
#include <errno.h> /* perror() */
55
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
155
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
156
functions: gnutls_*, GNUTLS_* */
157
#if GNUTLS_VERSION_NUMBER < 0x030600
158
#include <gnutls/openpgp.h>
159
/* gnutls_certificate_set_openpgp_key_file(),
160
GNUTLS_OPENPGP_FMT_BASE64 */
161
#elif GNUTLS_VERSION_NUMBER >= 0x030606
162
#include <gnutls/x509.h> /* GNUTLS_PKCS_PLAIN,
163
GNUTLS_PKCS_NULL_PASSWORD */
57
#define CERTFILE CERT_ROOT "openpgp-client.txt"
58
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
167
#include <gpgme.h> /* All GPGME types, constants and
169
gpgme_*, GPG_ERR_NO_*,
171
GPGME_PROTOCOL_OpenPGP */
59
173
#define BUFFER_SIZE 256
175
#define PATHDIR "/conf/conf.d/mandos"
176
#define SECKEY "seckey.txt"
177
#define PUBKEY "pubkey.txt"
178
#define TLS_PRIVKEY "tls-privkey.pem"
179
#define TLS_PUBKEY "tls-pubkey.pem"
180
#define HOOKDIR "/lib/mandos/network-hooks.d"
62
182
bool debug = false;
183
static const char mandos_protocol_version[] = "1";
184
const char *argp_program_version = "mandos-client " VERSION;
185
const char *argp_program_bug_address = "<mandos@recompile.se>";
186
static const char sys_class_net[] = "/sys/class/net";
187
char *connect_to = NULL;
188
const char *hookdir = HOOKDIR;
193
/* Doubly linked list that need to be circularly linked when used */
194
typedef struct server{
197
AvahiIfIndex if_index;
199
struct timespec last_seen;
204
/* Used for passing in values through the Avahi callback functions */
65
gnutls_session_t session;
66
207
gnutls_certificate_credentials_t cred;
208
unsigned int dh_bits;
67
209
gnutls_dh_params_t dh_params;
71
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
72
gpgme_data_t dh_crypto, dh_plain;
210
const char *priority;
212
server *current_server;
214
size_t interfaces_size;
217
/* global so signal handler can reach it*/
218
AvahiSimplePoll *simple_poll;
220
sig_atomic_t quit_now = 0;
221
int signal_received = 0;
223
/* Function to use when printing errors */
224
void perror_plus(const char *print_text){
226
fprintf(stderr, "Mandos plugin %s: ",
227
program_invocation_short_name);
232
__attribute__((format (gnu_printf, 2, 3), nonnull))
233
int fprintf_plus(FILE *stream, const char *format, ...){
235
va_start (ap, format);
237
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
238
program_invocation_short_name));
239
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
243
* Make additional room in "buffer" for at least BUFFER_SIZE more
244
* bytes. "buffer_capacity" is how much is currently allocated,
245
* "buffer_length" is how much is already used.
247
__attribute__((nonnull, warn_unused_result))
248
size_t incbuffer(char **buffer, size_t buffer_length,
249
size_t buffer_capacity){
250
if(buffer_length + BUFFER_SIZE > buffer_capacity){
251
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
253
int old_errno = errno;
260
buffer_capacity += BUFFER_SIZE;
262
return buffer_capacity;
265
/* Add server to set of servers to retry periodically */
266
__attribute__((nonnull, warn_unused_result))
267
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
268
int af, server **current_server){
270
server *new_server = malloc(sizeof(server));
271
if(new_server == NULL){
272
perror_plus("malloc");
275
*new_server = (server){ .ip = strdup(ip),
277
.if_index = if_index,
279
if(new_server->ip == NULL){
280
perror_plus("strdup");
284
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
286
perror_plus("clock_gettime");
288
#pragma GCC diagnostic push
289
#pragma GCC diagnostic ignored "-Wcast-qual"
291
free((char *)(new_server->ip));
293
#pragma GCC diagnostic pop
298
/* Special case of first server */
299
if(*current_server == NULL){
300
new_server->next = new_server;
301
new_server->prev = new_server;
302
*current_server = new_server;
304
/* Place the new server last in the list */
305
new_server->next = *current_server;
306
new_server->prev = (*current_server)->prev;
307
new_server->prev->next = new_server;
308
(*current_server)->prev = new_server;
313
/* Set effective uid to 0, return errno */
314
__attribute__((warn_unused_result))
315
int raise_privileges(void){
316
int old_errno = errno;
318
if(seteuid(0) == -1){
325
/* Set effective and real user ID to 0. Return errno. */
326
__attribute__((warn_unused_result))
327
int raise_privileges_permanently(void){
328
int old_errno = errno;
329
int ret = raise_privileges();
341
/* Set effective user ID to unprivileged saved user ID */
342
__attribute__((warn_unused_result))
343
int lower_privileges(void){
344
int old_errno = errno;
346
if(seteuid(uid) == -1){
353
/* Lower privileges permanently */
354
__attribute__((warn_unused_result))
355
int lower_privileges_permanently(void){
356
int old_errno = errno;
358
if(setuid(uid) == -1){
368
__attribute__((nonnull, warn_unused_result))
369
static bool init_gpgme(const char * const seckey,
370
const char * const pubkey,
371
const char * const tempdir,
76
size_t new_packet_capacity = 0;
77
size_t new_packet_length = 0;
78
374
gpgme_engine_info_t engine_info;
377
* Helper function to insert pub and seckey to the engine keyring.
379
bool import_key(const char * const filename){
382
gpgme_data_t pgp_data;
384
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
390
/* Workaround for systems without a real-time clock; see also
391
Debian bug #894495: <https://bugs.debian.org/894495> */
394
time_t currtime = time(NULL);
395
if(currtime != (time_t)-1){
397
if(gmtime_r(&currtime, &tm) == NULL) {
398
perror_plus("gmtime_r");
401
if(tm.tm_year != 70 or tm.tm_mon != 0){
405
fprintf_plus(stderr, "System clock is January 1970");
409
fprintf_plus(stderr, "System clock is invalid");
414
ret = fstat(fd, &keystat);
416
perror_plus("fstat");
419
ret = raise_privileges();
422
perror_plus("Failed to raise privileges");
427
"Setting system clock to key file mtime");
429
if(clock_settime(CLOCK_REALTIME, &keystat.st_mtim) != 0){
430
perror_plus("clock_settime");
432
ret = lower_privileges();
435
perror_plus("Failed to lower privileges");
81
fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
439
rc = gpgme_data_new_from_fd(&pgp_data, fd);
440
if(rc != GPG_ERR_NO_ERROR){
441
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
442
gpgme_strsource(rc), gpgme_strerror(rc));
446
rc = gpgme_op_import(mc->ctx, pgp_data);
447
if(rc != GPG_ERR_NO_ERROR){
448
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
449
gpgme_strsource(rc), gpgme_strerror(rc));
453
gpgme_import_result_t import_result
454
= gpgme_op_import_result(mc->ctx);
455
if((import_result->imported < 1
456
or import_result->not_imported > 0)
457
and import_result->unchanged == 0){
458
fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
460
"The total number of considered keys: %d\n",
461
import_result->considered);
463
"The number of keys without user ID: %d\n",
464
import_result->no_user_id);
466
"The total number of imported keys: %d\n",
467
import_result->imported);
468
fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
469
import_result->imported_rsa);
470
fprintf_plus(stderr, "The number of unchanged keys: %d\n",
471
import_result->unchanged);
472
fprintf_plus(stderr, "The number of new user IDs: %d\n",
473
import_result->new_user_ids);
474
fprintf_plus(stderr, "The number of new sub keys: %d\n",
475
import_result->new_sub_keys);
476
fprintf_plus(stderr, "The number of new signatures: %d\n",
477
import_result->new_signatures);
478
fprintf_plus(stderr, "The number of new revocations: %d\n",
479
import_result->new_revocations);
481
"The total number of secret keys read: %d\n",
482
import_result->secret_read);
484
"The number of imported secret keys: %d\n",
485
import_result->secret_imported);
487
"The number of unchanged secret keys: %d\n",
488
import_result->secret_unchanged);
489
fprintf_plus(stderr, "The number of keys not imported: %d\n",
490
import_result->not_imported);
491
for(gpgme_import_status_t import_status
492
= import_result->imports;
493
import_status != NULL;
494
import_status = import_status->next){
495
fprintf_plus(stderr, "Import status for key: %s\n",
497
if(import_status->result != GPG_ERR_NO_ERROR){
498
fprintf_plus(stderr, "Import result: %s: %s\n",
499
gpgme_strsource(import_status->result),
500
gpgme_strerror(import_status->result));
502
fprintf_plus(stderr, "Key status:\n");
504
import_status->status & GPGME_IMPORT_NEW
505
? "The key was new.\n"
506
: "The key was not new.\n");
508
import_status->status & GPGME_IMPORT_UID
509
? "The key contained new user IDs.\n"
510
: "The key did not contain new user IDs.\n");
512
import_status->status & GPGME_IMPORT_SIG
513
? "The key contained new signatures.\n"
514
: "The key did not contain new signatures.\n");
516
import_status->status & GPGME_IMPORT_SUBKEY
517
? "The key contained new sub keys.\n"
518
: "The key did not contain new sub keys.\n");
520
import_status->status & GPGME_IMPORT_SECRET
521
? "The key contained a secret key.\n"
522
: "The key did not contain a secret key.\n");
530
perror_plus("close");
532
gpgme_data_release(pgp_data);
537
fprintf_plus(stderr, "Initializing GPGME\n");
85
541
gpgme_check_version(NULL);
86
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
542
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
543
if(rc != GPG_ERR_NO_ERROR){
544
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
545
gpgme_strsource(rc), gpgme_strerror(rc));
88
/* Set GPGME home directory */
89
rc = gpgme_get_engine_info (&engine_info);
90
if (rc != GPG_ERR_NO_ERROR){
91
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
92
gpgme_strsource(rc), gpgme_strerror(rc));
549
/* Set GPGME home directory for the OpenPGP engine only */
550
rc = gpgme_get_engine_info(&engine_info);
551
if(rc != GPG_ERR_NO_ERROR){
552
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
553
gpgme_strsource(rc), gpgme_strerror(rc));
95
556
while(engine_info != NULL){
96
557
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
97
558
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
98
engine_info->file_name, homedir);
559
engine_info->file_name, tempdir);
101
562
engine_info = engine_info->next;
103
564
if(engine_info == NULL){
104
fprintf(stderr, "Could not set home dir to %s\n", homedir);
108
/* Create new GPGME data buffer from packet buffer */
109
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
110
if (rc != GPG_ERR_NO_ERROR){
111
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
112
gpgme_strsource(rc), gpgme_strerror(rc));
565
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
570
/* Create new GPGME "context" */
571
rc = gpgme_new(&(mc->ctx));
572
if(rc != GPG_ERR_NO_ERROR){
573
fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
574
gpgme_strsource(rc), gpgme_strerror(rc));
578
if(not import_key(pubkey) or not import_key(seckey)){
586
* Decrypt OpenPGP data.
587
* Returns -1 on error
589
__attribute__((nonnull, warn_unused_result))
590
static ssize_t pgp_packet_decrypt(const char *cryptotext,
594
gpgme_data_t dh_crypto, dh_plain;
597
size_t plaintext_capacity = 0;
598
ssize_t plaintext_length = 0;
601
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
604
/* Create new GPGME data buffer from memory cryptotext */
605
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
607
if(rc != GPG_ERR_NO_ERROR){
608
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
609
gpgme_strsource(rc), gpgme_strerror(rc));
116
613
/* Create new empty GPGME data buffer for the plaintext */
117
614
rc = gpgme_data_new(&dh_plain);
118
if (rc != GPG_ERR_NO_ERROR){
119
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
120
gpgme_strsource(rc), gpgme_strerror(rc));
124
/* Create new GPGME "context" */
125
rc = gpgme_new(&ctx);
126
if (rc != GPG_ERR_NO_ERROR){
127
fprintf(stderr, "bad gpgme_new: %s: %s\n",
128
gpgme_strsource(rc), gpgme_strerror(rc));
132
/* Decrypt data from the FILE pointer to the plaintext data buffer */
133
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
134
if (rc != GPG_ERR_NO_ERROR){
135
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
136
gpgme_strsource(rc), gpgme_strerror(rc));
615
if(rc != GPG_ERR_NO_ERROR){
616
fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
617
gpgme_strsource(rc), gpgme_strerror(rc));
618
gpgme_data_release(dh_crypto);
622
/* Decrypt data from the cryptotext data buffer to the plaintext
624
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
625
if(rc != GPG_ERR_NO_ERROR){
626
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
627
gpgme_strsource(rc), gpgme_strerror(rc));
628
plaintext_length = -1;
630
gpgme_decrypt_result_t result;
631
result = gpgme_op_decrypt_result(mc->ctx);
633
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
635
if(result->unsupported_algorithm != NULL) {
636
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
637
result->unsupported_algorithm);
639
fprintf_plus(stderr, "Wrong key usage: %s\n",
640
result->wrong_key_usage ? "Yes" : "No");
641
if(result->file_name != NULL){
642
fprintf_plus(stderr, "File name: %s\n", result->file_name);
645
for(gpgme_recipient_t r = result->recipients; r != NULL;
647
fprintf_plus(stderr, "Public key algorithm: %s\n",
648
gpgme_pubkey_algo_name(r->pubkey_algo));
649
fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
650
fprintf_plus(stderr, "Secret key available: %s\n",
651
r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
141
fprintf(stderr, "decryption of gpg packet succeeded\n");
145
gpgme_decrypt_result_t result;
146
result = gpgme_op_decrypt_result(ctx);
148
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
150
fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm);
151
fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage);
152
if(result->file_name != NULL){
153
fprintf(stderr, "File name: %s\n", result->file_name);
155
gpgme_recipient_t recipient;
156
recipient = result->recipients;
158
while(recipient != NULL){
159
fprintf(stderr, "Public key algorithm: %s\n",
160
gpgme_pubkey_algo_name(recipient->pubkey_algo));
161
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
162
fprintf(stderr, "Secret key available: %s\n",
163
recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
164
recipient = recipient->next;
170
/* Delete the GPGME FILE pointer cryptotext data buffer */
171
gpgme_data_release(dh_crypto);
659
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
173
662
/* Seek back to the beginning of the GPGME plaintext data buffer */
174
gpgme_data_seek(dh_plain, 0, SEEK_SET);
663
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
664
perror_plus("gpgme_data_seek");
665
plaintext_length = -1;
178
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
179
*new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
180
if (*new_packet == NULL){
184
new_packet_capacity += BUFFER_SIZE;
671
plaintext_capacity = incbuffer(plaintext,
672
(size_t)plaintext_length,
674
if(plaintext_capacity == 0){
675
perror_plus("incbuffer");
676
plaintext_length = -1;
187
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
680
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
188
682
/* Print the data, if any */
190
/* If password is empty, then a incorrect error will be printed */
194
perror("gpgme_data_read");
688
perror_plus("gpgme_data_read");
689
plaintext_length = -1;
197
new_packet_length += ret;
692
plaintext_length += ret;
201
fprintf(stderr, "decrypted password is: %s\n", *new_packet);
696
fprintf_plus(stderr, "Decrypted password is: ");
697
for(ssize_t i = 0; i < plaintext_length; i++){
698
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
700
fprintf(stderr, "\n");
204
/* Delete the GPGME plaintext data buffer */
705
/* Delete the GPGME cryptotext data buffer */
706
gpgme_data_release(dh_crypto);
708
/* Delete the GPGME plaintext data buffer */
205
709
gpgme_data_release(dh_plain);
206
return new_packet_length;
209
static const char * safer_gnutls_strerror (int value) {
210
const char *ret = gnutls_strerror (value);
216
void debuggnutls(int level, const char* string){
217
fprintf(stderr, "%s", string);
220
int initgnutls(encrypted_session *es){
710
return plaintext_length;
713
__attribute__((warn_unused_result, const))
714
static const char *safe_string(const char *str){
720
__attribute__((warn_unused_result))
721
static const char *safer_gnutls_strerror(int value){
722
const char *ret = gnutls_strerror(value);
723
return safe_string(ret);
726
/* GnuTLS log function callback */
727
__attribute__((nonnull))
728
static void debuggnutls(__attribute__((unused)) int level,
730
fprintf_plus(stderr, "GnuTLS: %s", string);
733
__attribute__((nonnull(1, 2, 4), warn_unused_result))
734
static int init_gnutls_global(const char *pubkeyfilename,
735
const char *seckeyfilename,
736
const char *dhparamsfilename,
225
fprintf(stderr, "Initializing gnutls\n");
229
if ((ret = gnutls_global_init ())
230
!= GNUTLS_E_SUCCESS) {
231
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
741
fprintf_plus(stderr, "Initializing GnuTLS\n");
745
/* "Use a log level over 10 to enable all debugging options."
236
748
gnutls_global_set_log_level(11);
237
749
gnutls_global_set_log_function(debuggnutls);
241
/* openpgp credentials */
242
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
243
!= GNUTLS_E_SUCCESS) {
244
fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
752
/* OpenPGP credentials */
753
ret = gnutls_certificate_allocate_credentials(&mc->cred);
754
if(ret != GNUTLS_E_SUCCESS){
755
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
756
safer_gnutls_strerror(ret));
249
fprintf(stderr, "Attempting to use openpgp certificate %s"
250
" and keyfile %s as gnutls credentials\n", CERTFILE, KEYFILE);
761
fprintf_plus(stderr, "Attempting to use public key %s and"
762
" private key %s as GnuTLS credentials\n",
767
#if GNUTLS_VERSION_NUMBER >= 0x030606
768
ret = gnutls_certificate_set_rawpk_key_file
769
(mc->cred, pubkeyfilename, seckeyfilename,
770
GNUTLS_X509_FMT_PEM, /* format */
773
GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
775
0, /* names_length */
777
GNUTLS_PKCS_PLAIN | GNUTLS_PKCS_NULL_PASSWORD,
778
0); /* pkcs11_flags */
779
#elif GNUTLS_VERSION_NUMBER < 0x030600
253
780
ret = gnutls_certificate_set_openpgp_key_file
254
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
255
if (ret != GNUTLS_E_SUCCESS) {
257
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
258
ret, CERTFILE, KEYFILE);
259
fprintf(stdout, "The Error is: %s\n",
260
safer_gnutls_strerror(ret));
264
//Gnutls server initialization
265
if ((ret = gnutls_dh_params_init (&es->dh_params))
266
!= GNUTLS_E_SUCCESS) {
267
fprintf (stderr, "Error in dh parameter initialization: %s\n",
268
safer_gnutls_strerror(ret));
272
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
273
!= GNUTLS_E_SUCCESS) {
274
fprintf (stderr, "Error in prime generation: %s\n",
275
safer_gnutls_strerror(ret));
279
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
281
// Gnutls session creation
282
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
283
!= GNUTLS_E_SUCCESS){
284
fprintf(stderr, "Error in gnutls session initialization: %s\n",
285
safer_gnutls_strerror(ret));
288
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
289
!= GNUTLS_E_SUCCESS) {
290
fprintf(stderr, "Syntax error at: %s\n", err);
291
fprintf(stderr, "Gnutls error: %s\n",
292
safer_gnutls_strerror(ret));
296
if ((ret = gnutls_credentials_set
297
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
298
!= GNUTLS_E_SUCCESS) {
299
fprintf(stderr, "Error setting a credentials set: %s\n",
300
safer_gnutls_strerror(ret));
781
(mc->cred, pubkeyfilename, seckeyfilename,
782
GNUTLS_OPENPGP_FMT_BASE64);
784
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
786
if(ret != GNUTLS_E_SUCCESS){
788
"Error[%d] while reading the key pair ('%s',"
789
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
790
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
791
safer_gnutls_strerror(ret));
795
/* GnuTLS server initialization */
796
ret = gnutls_dh_params_init(&mc->dh_params);
797
if(ret != GNUTLS_E_SUCCESS){
798
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
799
" initialization: %s\n",
800
safer_gnutls_strerror(ret));
803
/* If a Diffie-Hellman parameters file was given, try to use it */
804
if(dhparamsfilename != NULL){
805
gnutls_datum_t params = { .data = NULL, .size = 0 };
807
int dhpfile = open(dhparamsfilename, O_RDONLY);
810
dhparamsfilename = NULL;
813
size_t params_capacity = 0;
815
params_capacity = incbuffer((char **)¶ms.data,
817
(size_t)params_capacity);
818
if(params_capacity == 0){
819
perror_plus("incbuffer");
822
dhparamsfilename = NULL;
825
ssize_t bytes_read = read(dhpfile,
826
params.data + params.size,
832
/* check bytes_read for failure */
837
dhparamsfilename = NULL;
840
params.size += (unsigned int)bytes_read;
842
ret = close(dhpfile);
844
perror_plus("close");
846
if(params.data == NULL){
847
dhparamsfilename = NULL;
849
if(dhparamsfilename == NULL){
852
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
853
GNUTLS_X509_FMT_PEM);
854
if(ret != GNUTLS_E_SUCCESS){
855
fprintf_plus(stderr, "Failed to parse DH parameters in file"
856
" \"%s\": %s\n", dhparamsfilename,
857
safer_gnutls_strerror(ret));
858
dhparamsfilename = NULL;
863
if(dhparamsfilename == NULL){
864
if(mc->dh_bits == 0){
865
#if GNUTLS_VERSION_NUMBER < 0x030600
866
/* Find out the optimal number of DH bits */
867
/* Try to read the private key file */
868
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
870
int secfile = open(seckeyfilename, O_RDONLY);
875
size_t buffer_capacity = 0;
877
buffer_capacity = incbuffer((char **)&buffer.data,
879
(size_t)buffer_capacity);
880
if(buffer_capacity == 0){
881
perror_plus("incbuffer");
886
ssize_t bytes_read = read(secfile,
887
buffer.data + buffer.size,
893
/* check bytes_read for failure */
900
buffer.size += (unsigned int)bytes_read;
904
/* If successful, use buffer to parse private key */
905
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
906
if(buffer.data != NULL){
908
gnutls_openpgp_privkey_t privkey = NULL;
909
ret = gnutls_openpgp_privkey_init(&privkey);
910
if(ret != GNUTLS_E_SUCCESS){
911
fprintf_plus(stderr, "Error initializing OpenPGP key"
913
safer_gnutls_strerror(ret));
917
ret = gnutls_openpgp_privkey_import
918
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
919
if(ret != GNUTLS_E_SUCCESS){
920
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
921
safer_gnutls_strerror(ret));
927
/* Use private key to suggest an appropriate
929
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
930
gnutls_openpgp_privkey_deinit(privkey);
932
fprintf_plus(stderr, "This OpenPGP key implies using"
933
" a GnuTLS security parameter \"%s\".\n",
934
safe_string(gnutls_sec_param_get_name
940
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
941
/* Err on the side of caution */
942
sec_param = GNUTLS_SEC_PARAM_ULTRA;
944
fprintf_plus(stderr, "Falling back to security parameter"
946
safe_string(gnutls_sec_param_get_name
951
unsigned int uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
955
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
956
" implies %u DH bits; using that.\n",
957
safe_string(gnutls_sec_param_get_name
962
fprintf_plus(stderr, "Failed to get implied number of DH"
963
" bits for security parameter \"%s\"): %s\n",
964
safe_string(gnutls_sec_param_get_name
966
safer_gnutls_strerror(ret));
970
} else { /* dh_bits != 0 */
972
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
975
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
976
if(ret != GNUTLS_E_SUCCESS){
977
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
978
" bits): %s\n", mc->dh_bits,
979
safer_gnutls_strerror(ret));
982
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
990
gnutls_certificate_free_credentials(mc->cred);
991
gnutls_dh_params_deinit(mc->dh_params);
995
__attribute__((nonnull, warn_unused_result))
996
static int init_gnutls_session(gnutls_session_t *session,
999
/* GnuTLS session creation */
1001
ret = gnutls_init(session, (GNUTLS_SERVER
1002
#if GNUTLS_VERSION_NUMBER >= 0x030506
1005
#if GNUTLS_VERSION_NUMBER >= 0x030606
1006
| GNUTLS_ENABLE_RAWPK
1012
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
1013
if(ret != GNUTLS_E_SUCCESS){
1014
fprintf_plus(stderr,
1015
"Error in GnuTLS session initialization: %s\n",
1016
safer_gnutls_strerror(ret));
1022
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
1024
gnutls_deinit(*session);
1027
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
1028
if(ret != GNUTLS_E_SUCCESS){
1029
fprintf_plus(stderr, "Syntax error at: %s\n", err);
1030
fprintf_plus(stderr, "GnuTLS error: %s\n",
1031
safer_gnutls_strerror(ret));
1032
gnutls_deinit(*session);
1038
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
1041
gnutls_deinit(*session);
1044
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
1045
if(ret != GNUTLS_E_SUCCESS){
1046
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
1047
safer_gnutls_strerror(ret));
1048
gnutls_deinit(*session);
304
1052
/* ignore client certificate if any. */
305
gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
307
gnutls_dh_set_prime_bits (es->session, DH_BITS);
1053
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
312
void empty_log(AvahiLogLevel level, const char *txt){}
314
int start_mandos_communcation(char *ip, uint16_t port){
316
struct sockaddr_in6 to;
317
struct in6_addr ip_addr;
318
encrypted_session es;
1058
/* Avahi log function callback */
1059
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
1060
__attribute__((unused)) const char *txt){}
1062
/* Helper function to add_local_route() and delete_local_route() */
1063
__attribute__((nonnull, warn_unused_result))
1064
static bool add_delete_local_route(const bool add,
1065
const char *address,
1066
AvahiIfIndex if_index){
1068
char helper[] = "mandos-client-iprouteadddel";
1069
char add_arg[] = "add";
1070
char delete_arg[] = "delete";
1071
char debug_flag[] = "--debug";
1072
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
1073
if(pluginhelperdir == NULL){
1075
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
1076
" variable not set; cannot run helper\n");
1081
char interface[IF_NAMESIZE];
1082
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1083
perror_plus("if_indextoname");
1087
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1089
perror_plus("open(\"/dev/null\", O_RDONLY)");
1095
/* Raise privileges */
1096
errno = raise_privileges_permanently();
1098
perror_plus("Failed to raise privileges");
1099
/* _exit(EX_NOPERM); */
1105
perror_plus("setgid");
1109
/* Reset supplementary groups */
1111
ret = setgroups(0, NULL);
1113
perror_plus("setgroups");
1118
ret = dup2(devnull, STDIN_FILENO);
1120
perror_plus("dup2(devnull, STDIN_FILENO)");
1124
ret = close(devnull);
1126
perror_plus("close");
1128
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1130
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1133
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
1138
if(helperdir_fd == -1){
1139
perror_plus("open");
1140
_exit(EX_UNAVAILABLE);
1142
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
1144
if(helper_fd == -1){
1145
perror_plus("openat");
1146
close(helperdir_fd);
1147
_exit(EX_UNAVAILABLE);
1149
close(helperdir_fd);
1151
#pragma GCC diagnostic push
1152
#pragma GCC diagnostic ignored "-Wcast-qual"
1154
if(fexecve(helper_fd, (char *const [])
1155
{ helper, add ? add_arg : delete_arg, (char *)address,
1156
interface, debug ? debug_flag : NULL, NULL },
1159
#pragma GCC diagnostic pop
1161
perror_plus("fexecve");
1162
_exit(EXIT_FAILURE);
1166
perror_plus("fork");
1170
ret = close(devnull);
1172
perror_plus("close");
1178
pret = waitpid(pid, &status, 0);
1179
if(pret == -1 and errno == EINTR and quit_now){
1180
int errno_raising = 0;
1181
if((errno = raise_privileges()) != 0){
1182
errno_raising = errno;
1183
perror_plus("Failed to raise privileges in order to"
1184
" kill helper program");
1186
if(kill(pid, SIGTERM) == -1){
1187
perror_plus("kill");
1189
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
1190
perror_plus("Failed to lower privileges after killing"
1195
} while(pret == -1 and errno == EINTR);
1197
perror_plus("waitpid");
1200
if(WIFEXITED(status)){
1201
if(WEXITSTATUS(status) != 0){
1202
fprintf_plus(stderr, "Error: iprouteadddel exited"
1203
" with status %d\n", WEXITSTATUS(status));
1208
if(WIFSIGNALED(status)){
1209
fprintf_plus(stderr, "Error: iprouteadddel died by"
1210
" signal %d\n", WTERMSIG(status));
1213
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1217
__attribute__((nonnull, warn_unused_result))
1218
static bool add_local_route(const char *address,
1219
AvahiIfIndex if_index){
1221
fprintf_plus(stderr, "Adding route to %s\n", address);
1223
return add_delete_local_route(true, address, if_index);
1226
__attribute__((nonnull, warn_unused_result))
1227
static bool delete_local_route(const char *address,
1228
AvahiIfIndex if_index){
1230
fprintf_plus(stderr, "Removing route to %s\n", address);
1232
return add_delete_local_route(false, address, if_index);
1235
/* Called when a Mandos server is found */
1236
__attribute__((nonnull, warn_unused_result))
1237
static int start_mandos_communication(const char *ip, in_port_t port,
1238
AvahiIfIndex if_index,
1239
int af, mandos_context *mc){
1240
int ret, tcp_sd = -1;
1242
struct sockaddr_storage to;
319
1243
char *buffer = NULL;
320
char *decrypted_buffer;
1244
char *decrypted_buffer = NULL;
321
1245
size_t buffer_length = 0;
322
1246
size_t buffer_capacity = 0;
323
ssize_t decrypted_buffer_size;
325
const char interface[] = "eth0";
328
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
331
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
338
fprintf(stderr, "Binding to interface %s\n", interface);
341
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
343
perror("setsockopt bindtodevice");
347
memset(&to,0,sizeof(to));
348
to.sin6_family = AF_INET6;
349
ret = inet_pton(AF_INET6, ip, &ip_addr);
1249
gnutls_session_t session;
1250
int pf; /* Protocol family */
1251
bool route_added = false;
1268
fprintf_plus(stderr, "Bad address family: %d\n", af);
1273
/* If the interface is specified and we have a list of interfaces */
1274
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1275
/* Check if the interface is one of the interfaces we are using */
1278
char *interface = NULL;
1279
while((interface = argz_next(mc->interfaces,
1280
mc->interfaces_size,
1282
if(if_nametoindex(interface) == (unsigned int)if_index){
1289
/* This interface does not match any in the list, so we don't
1290
connect to the server */
1292
char interface[IF_NAMESIZE];
1293
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1294
perror_plus("if_indextoname");
1296
fprintf_plus(stderr, "Skipping server on non-used interface"
1298
if_indextoname((unsigned int)if_index,
1306
ret = init_gnutls_session(&session, mc);
1312
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1313
PRIuMAX "\n", ip, (uintmax_t)port);
1316
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1319
perror_plus("socket");
1330
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1331
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1332
ret = inet_pton(af, ip, &to6->sin6_addr);
1334
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1335
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1336
ret = inet_pton(af, ip, &to4->sin_addr);
1340
perror_plus("inet_pton");
355
fprintf(stderr, "Bad address: %s\n", ip);
358
to.sin6_port = htons(port);
359
to.sin6_scope_id = if_nametoindex(interface);
362
fprintf(stderr, "Connection to: %s\n", ip);
365
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
371
ret = initgnutls (&es);
378
gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
381
fprintf(stderr, "Establishing tls session with %s\n", ip);
385
ret = gnutls_handshake (es.session);
387
if (ret != GNUTLS_E_SUCCESS){
388
fprintf(stderr, "\n*** Handshake failed ***\n");
394
//Retrieve gpg packet that contains the wanted password
397
fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip);
401
if (buffer_length + BUFFER_SIZE > buffer_capacity){
402
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
407
buffer_capacity += BUFFER_SIZE;
410
ret = gnutls_record_recv
411
(es.session, buffer+buffer_length, BUFFER_SIZE);
1346
fprintf_plus(stderr, "Bad address: %s\n", ip);
1351
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1352
if(IN6_IS_ADDR_LINKLOCAL
1353
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1354
if(if_index == AVAHI_IF_UNSPEC){
1355
fprintf_plus(stderr, "An IPv6 link-local address is"
1356
" incomplete without a network interface\n");
1360
/* Set the network interface number as scope */
1361
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1364
((struct sockaddr_in *)&to)->sin_port = htons(port);
1373
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1374
char interface[IF_NAMESIZE];
1375
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1376
perror_plus("if_indextoname");
1378
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1379
"\n", ip, interface, (uintmax_t)port);
1382
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1383
ip, (uintmax_t)port);
1385
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1386
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1388
ret = getnameinfo((struct sockaddr *)&to,
1389
sizeof(struct sockaddr_in6),
1390
addrstr, sizeof(addrstr), NULL, 0,
1393
ret = getnameinfo((struct sockaddr *)&to,
1394
sizeof(struct sockaddr_in),
1395
addrstr, sizeof(addrstr), NULL, 0,
1398
if(ret == EAI_SYSTEM){
1399
perror_plus("getnameinfo");
1400
} else if(ret != 0) {
1401
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1402
} else if(strcmp(addrstr, ip) != 0){
1403
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1414
ret = connect(tcp_sd, (struct sockaddr *)&to,
1415
sizeof(struct sockaddr_in6));
1417
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1418
sizeof(struct sockaddr_in));
1421
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1422
and if_index != AVAHI_IF_UNSPEC
1423
and connect_to == NULL
1424
and not route_added and
1425
((af == AF_INET6 and not
1426
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1428
or (af == AF_INET and
1429
/* Not a a IPv4LL address */
1430
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1431
& 0xFFFF0000L) != 0xA9FE0000L))){
1432
/* Work around Avahi bug - Avahi does not announce link-local
1433
addresses if it has a global address, so local hosts with
1434
*only* a link-local address (e.g. Mandos clients) cannot
1435
connect to a Mandos server announced by Avahi on a server
1436
host with a global address. Work around this by retrying
1437
with an explicit route added with the server's address.
1439
Avahi bug reference:
1440
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1441
https://bugs.debian.org/587961
1444
fprintf_plus(stderr, "Mandos server unreachable, trying"
1448
route_added = add_local_route(ip, if_index);
1454
if(errno != ECONNREFUSED or debug){
1456
perror_plus("connect");
1469
const char *out = mandos_protocol_version;
1472
size_t out_size = strlen(out);
1473
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1474
out_size - written));
1477
perror_plus("write");
1481
written += (size_t)ret;
1482
if(written < out_size){
1485
if(out == mandos_protocol_version){
1500
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1508
/* This casting via intptr_t is to eliminate warning about casting
1509
an int to a pointer type. This is exactly how the GnuTLS Guile
1510
function "set-session-transport-fd!" does it. */
1511
gnutls_transport_set_ptr(session,
1512
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1520
ret = gnutls_handshake(session);
1525
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1527
if(ret != GNUTLS_E_SUCCESS){
1529
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1536
/* Read OpenPGP packet that contains the wanted password */
1539
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1550
buffer_capacity = incbuffer(&buffer, buffer_length,
1552
if(buffer_capacity == 0){
1554
perror_plus("incbuffer");
1564
sret = gnutls_record_recv(session, buffer+buffer_length,
417
1571
case GNUTLS_E_INTERRUPTED:
418
1572
case GNUTLS_E_AGAIN:
420
1574
case GNUTLS_E_REHANDSHAKE:
421
ret = gnutls_handshake (es.session);
423
fprintf(stderr, "\n*** Handshake failed ***\n");
1576
ret = gnutls_handshake(session);
1582
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1584
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
430
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
432
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
436
buffer_length += ret;
440
if (buffer_length > 0){
441
if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) >= 0){
442
fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
443
free(decrypted_buffer);
1592
fprintf_plus(stderr, "Unknown error while reading data from"
1593
" encrypted session with Mandos server\n");
1594
gnutls_bye(session, GNUTLS_SHUT_RDWR);
1599
buffer_length += (size_t) sret;
1604
fprintf_plus(stderr, "Closing TLS session\n");
1613
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1618
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1620
if(buffer_length > 0){
1621
ssize_t decrypted_buffer_size;
1622
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1623
&decrypted_buffer, mc);
1624
if(decrypted_buffer_size >= 0){
1628
while(written < (size_t) decrypted_buffer_size){
1634
ret = (int)fwrite(decrypted_buffer + written, 1,
1635
(size_t)decrypted_buffer_size - written,
1637
if(ret == 0 and ferror(stdout)){
1640
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1646
written += (size_t)ret;
1648
ret = fflush(stdout);
1652
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1662
/* Shutdown procedure */
1667
if(not delete_local_route(ip, if_index)){
1668
fprintf_plus(stderr, "Failed to delete local route to %s on"
1669
" interface %d", ip, if_index);
1673
free(decrypted_buffer);
1676
ret = close(tcp_sd);
1682
perror_plus("close");
1684
gnutls_deinit(session);
452
fprintf(stderr, "Closing tls session\n");
456
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
459
gnutls_deinit (es.session);
460
gnutls_certificate_free_credentials (es.cred);
461
gnutls_global_deinit ();
465
static AvahiSimplePoll *simple_poll = NULL;
466
static AvahiServer *server = NULL;
468
static void resolve_callback(
469
AvahiSServiceResolver *r,
470
AVAHI_GCC_UNUSED AvahiIfIndex interface,
471
AVAHI_GCC_UNUSED AvahiProtocol protocol,
472
AvahiResolverEvent event,
476
const char *host_name,
477
const AvahiAddress *address,
479
AvahiStringList *txt,
480
AvahiLookupResultFlags flags,
481
AVAHI_GCC_UNUSED void* userdata) {
485
/* Called whenever a service has been resolved successfully or timed out */
488
case AVAHI_RESOLVER_FAILURE:
489
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_server_errno(server)));
492
case AVAHI_RESOLVER_FOUND: {
493
char ip[AVAHI_ADDRESS_STR_MAX];
494
avahi_address_snprint(ip, sizeof(ip), address);
496
fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
498
int ret = start_mandos_communcation(ip, port);
1694
static void resolve_callback(AvahiSServiceResolver *r,
1695
AvahiIfIndex interface,
1696
AvahiProtocol proto,
1697
AvahiResolverEvent event,
1701
const char *host_name,
1702
const AvahiAddress *address,
1704
AVAHI_GCC_UNUSED AvahiStringList *txt,
1705
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1712
/* Called whenever a service has been resolved successfully or
506
1716
avahi_s_service_resolver_free(r);
509
static void browse_callback(
510
AvahiSServiceBrowser *b,
511
AvahiIfIndex interface,
512
AvahiProtocol protocol,
513
AvahiBrowserEvent event,
517
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
520
AvahiServer *s = userdata;
523
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
527
case AVAHI_BROWSER_FAILURE:
529
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
530
avahi_simple_poll_quit(simple_poll);
533
case AVAHI_BROWSER_NEW:
534
/* We ignore the returned resolver object. In the callback
535
function we free it. If the server is terminated before
536
the callback function is called the server will free
537
the resolver for us. */
539
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
540
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
544
case AVAHI_BROWSER_REMOVE:
547
case AVAHI_BROWSER_ALL_FOR_NOW:
548
case AVAHI_BROWSER_CACHE_EXHAUSTED:
553
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1722
case AVAHI_RESOLVER_FAILURE:
1723
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1724
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1726
avahi_strerror(avahi_server_errno
1727
(((mandos_context*)mc)->server)));
1730
case AVAHI_RESOLVER_FOUND:
1732
char ip[AVAHI_ADDRESS_STR_MAX];
1733
avahi_address_snprint(ip, sizeof(ip), address);
1735
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1736
PRIdMAX ") on port %" PRIu16 "\n", name,
1737
host_name, ip, (intmax_t)interface, port);
1739
int ret = start_mandos_communication(ip, (in_port_t)port,
1741
avahi_proto_to_af(proto),
1744
avahi_simple_poll_quit(simple_poll);
1746
if(not add_server(ip, (in_port_t)port, interface,
1747
avahi_proto_to_af(proto),
1748
&((mandos_context*)mc)->current_server)){
1749
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1755
avahi_s_service_resolver_free(r);
1758
static void browse_callback(AvahiSServiceBrowser *b,
1759
AvahiIfIndex interface,
1760
AvahiProtocol protocol,
1761
AvahiBrowserEvent event,
1765
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1772
/* Called whenever a new services becomes available on the LAN or
1773
is removed from the LAN */
1781
case AVAHI_BROWSER_FAILURE:
1783
fprintf_plus(stderr, "(Avahi browser) %s\n",
1784
avahi_strerror(avahi_server_errno
1785
(((mandos_context*)mc)->server)));
1786
avahi_simple_poll_quit(simple_poll);
1789
case AVAHI_BROWSER_NEW:
1790
/* We ignore the returned Avahi resolver object. In the callback
1791
function we free it. If the Avahi server is terminated before
1792
the callback function is called the Avahi server will free the
1795
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1796
interface, protocol, name, type,
1797
domain, protocol, 0,
1798
resolve_callback, mc) == NULL)
1799
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1801
avahi_strerror(avahi_server_errno
1802
(((mandos_context*)mc)->server)));
1805
case AVAHI_BROWSER_REMOVE:
1808
case AVAHI_BROWSER_ALL_FOR_NOW:
1809
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1811
fprintf_plus(stderr, "No Mandos server found, still"
1818
/* Signal handler that stops main loop after SIGTERM */
1819
static void handle_sigterm(int sig){
1824
signal_received = sig;
1825
int old_errno = errno;
1826
/* set main loop to exit */
1827
if(simple_poll != NULL){
1828
avahi_simple_poll_quit(simple_poll);
1833
__attribute__((nonnull, warn_unused_result))
1834
bool get_flags(const char *ifname, struct ifreq *ifr){
1838
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1841
perror_plus("socket");
1845
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1846
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1847
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1851
perror_plus("ioctl SIOCGIFFLAGS");
1854
if((close(s) == -1) and debug){
1856
perror_plus("close");
1861
if((close(s) == -1) and debug){
1863
perror_plus("close");
1869
__attribute__((nonnull, warn_unused_result))
1870
bool good_flags(const char *ifname, const struct ifreq *ifr){
1872
/* Reject the loopback device */
1873
if(ifr->ifr_flags & IFF_LOOPBACK){
1875
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1880
/* Accept point-to-point devices only if connect_to is specified */
1881
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1883
fprintf_plus(stderr, "Accepting point-to-point interface"
1884
" \"%s\"\n", ifname);
1888
/* Otherwise, reject non-broadcast-capable devices */
1889
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1891
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1892
" \"%s\"\n", ifname);
1896
/* Reject non-ARP interfaces (including dummy interfaces) */
1897
if(ifr->ifr_flags & IFF_NOARP){
1899
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1905
/* Accept this device */
1907
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1913
* This function determines if a directory entry in /sys/class/net
1914
* corresponds to an acceptable network device.
1915
* (This function is passed to scandir(3) as a filter function.)
1917
__attribute__((nonnull, warn_unused_result))
1918
int good_interface(const struct dirent *if_entry){
1919
if(if_entry->d_name[0] == '.'){
1924
if(not get_flags(if_entry->d_name, &ifr)){
1926
fprintf_plus(stderr, "Failed to get flags for interface "
1927
"\"%s\"\n", if_entry->d_name);
1932
if(not good_flags(if_entry->d_name, &ifr)){
1939
* This function determines if a network interface is up.
1941
__attribute__((nonnull, warn_unused_result))
1942
bool interface_is_up(const char *interface){
1944
if(not get_flags(interface, &ifr)){
1946
fprintf_plus(stderr, "Failed to get flags for interface "
1947
"\"%s\"\n", interface);
1952
return (bool)(ifr.ifr_flags & IFF_UP);
1956
* This function determines if a network interface is running
1958
__attribute__((nonnull, warn_unused_result))
1959
bool interface_is_running(const char *interface){
1961
if(not get_flags(interface, &ifr)){
1963
fprintf_plus(stderr, "Failed to get flags for interface "
1964
"\"%s\"\n", interface);
1969
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1972
__attribute__((nonnull, pure, warn_unused_result))
1973
int notdotentries(const struct dirent *direntry){
1974
/* Skip "." and ".." */
1975
if(direntry->d_name[0] == '.'
1976
and (direntry->d_name[1] == '\0'
1977
or (direntry->d_name[1] == '.'
1978
and direntry->d_name[2] == '\0'))){
1984
/* Is this directory entry a runnable program? */
1985
__attribute__((nonnull, warn_unused_result))
1986
int runnable_hook(const struct dirent *direntry){
1991
if((direntry->d_name)[0] == '\0'){
1996
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1997
"abcdefghijklmnopqrstuvwxyz"
2000
if((direntry->d_name)[sret] != '\0'){
2001
/* Contains non-allowed characters */
2003
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
2009
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
2012
perror_plus("Could not stat hook");
2016
if(not (S_ISREG(st.st_mode))){
2017
/* Not a regular file */
2019
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
2024
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
2025
/* Not executable */
2027
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
2033
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
2039
__attribute__((nonnull, warn_unused_result))
2040
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
2041
mandos_context *mc){
2043
struct timespec now;
2044
struct timespec waited_time;
2045
intmax_t block_time;
2048
if(mc->current_server == NULL){
2050
fprintf_plus(stderr, "Wait until first server is found."
2053
ret = avahi_simple_poll_iterate(s, -1);
2056
fprintf_plus(stderr, "Check current_server if we should run"
2059
/* the current time */
2060
ret = clock_gettime(CLOCK_MONOTONIC, &now);
2062
perror_plus("clock_gettime");
2065
/* Calculating in ms how long time between now and server
2066
who we visted longest time ago. Now - last seen. */
2067
waited_time.tv_sec = (now.tv_sec
2068
- mc->current_server->last_seen.tv_sec);
2069
waited_time.tv_nsec = (now.tv_nsec
2070
- mc->current_server->last_seen.tv_nsec);
2071
/* total time is 10s/10,000ms.
2072
Converting to s from ms by dividing by 1,000,
2073
and ns to ms by dividing by 1,000,000. */
2074
block_time = ((retry_interval
2075
- ((intmax_t)waited_time.tv_sec * 1000))
2076
- ((intmax_t)waited_time.tv_nsec / 1000000));
2079
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
2083
if(block_time <= 0){
2084
ret = start_mandos_communication(mc->current_server->ip,
2085
mc->current_server->port,
2086
mc->current_server->if_index,
2087
mc->current_server->af, mc);
2089
avahi_simple_poll_quit(s);
2092
ret = clock_gettime(CLOCK_MONOTONIC,
2093
&mc->current_server->last_seen);
2095
perror_plus("clock_gettime");
2098
mc->current_server = mc->current_server->next;
2099
block_time = 0; /* Call avahi to find new Mandos
2100
servers, but don't block */
2103
ret = avahi_simple_poll_iterate(s, (int)block_time);
2106
if(ret > 0 or errno != EINTR){
2107
return (ret != 1) ? ret : 0;
2113
__attribute__((nonnull))
2114
void run_network_hooks(const char *mode, const char *interface,
2116
struct dirent **direntries = NULL;
2117
if(hookdir_fd == -1){
2118
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
2120
if(hookdir_fd == -1){
2121
if(errno == ENOENT){
2123
fprintf_plus(stderr, "Network hook directory \"%s\" not"
2124
" found\n", hookdir);
2127
perror_plus("open");
2132
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2134
perror_plus("open(\"/dev/null\", O_RDONLY)");
2137
int numhooks = scandirat(hookdir_fd, ".", &direntries,
2138
runnable_hook, alphasort);
2140
perror_plus("scandir");
2144
struct dirent *direntry;
2146
for(int i = 0; i < numhooks; i++){
2147
direntry = direntries[i];
2149
fprintf_plus(stderr, "Running network hook \"%s\"\n",
2152
pid_t hook_pid = fork();
2155
/* Raise privileges */
2156
errno = raise_privileges_permanently();
2158
perror_plus("Failed to raise privileges");
2165
perror_plus("setgid");
2168
/* Reset supplementary groups */
2170
ret = setgroups(0, NULL);
2172
perror_plus("setgroups");
2175
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
2177
perror_plus("setenv");
2180
ret = setenv("DEVICE", interface, 1);
2182
perror_plus("setenv");
2185
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
2187
perror_plus("setenv");
2190
ret = setenv("MODE", mode, 1);
2192
perror_plus("setenv");
2196
ret = asprintf(&delaystring, "%f", (double)delay);
2198
perror_plus("asprintf");
2201
ret = setenv("DELAY", delaystring, 1);
2204
perror_plus("setenv");
2208
if(connect_to != NULL){
2209
ret = setenv("CONNECT", connect_to, 1);
2211
perror_plus("setenv");
2215
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2219
perror_plus("openat");
2220
_exit(EXIT_FAILURE);
2222
if(close(hookdir_fd) == -1){
2223
perror_plus("close");
2224
_exit(EXIT_FAILURE);
2226
ret = dup2(devnull, STDIN_FILENO);
2228
perror_plus("dup2(devnull, STDIN_FILENO)");
2231
ret = close(devnull);
2233
perror_plus("close");
2236
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2238
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2241
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2243
perror_plus("fexecve");
2244
_exit(EXIT_FAILURE);
2248
perror_plus("fork");
2253
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2254
perror_plus("waitpid");
2258
if(WIFEXITED(status)){
2259
if(WEXITSTATUS(status) != 0){
2260
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2261
" with status %d\n", direntry->d_name,
2262
WEXITSTATUS(status));
2266
} else if(WIFSIGNALED(status)){
2267
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2268
" signal %d\n", direntry->d_name,
2273
fprintf_plus(stderr, "Warning: network hook \"%s\""
2274
" crashed\n", direntry->d_name);
2280
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2286
if(close(hookdir_fd) == -1){
2287
perror_plus("close");
2294
__attribute__((nonnull, warn_unused_result))
2295
int bring_up_interface(const char *const interface,
2297
int old_errno = errno;
2299
struct ifreq network;
2300
unsigned int if_index = if_nametoindex(interface);
2302
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2312
if(not interface_is_up(interface)){
2314
int ioctl_errno = 0;
2315
if(not get_flags(interface, &network)){
2317
fprintf_plus(stderr, "Failed to get flags for interface "
2318
"\"%s\"\n", interface);
2322
network.ifr_flags |= IFF_UP; /* set flag */
2324
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2327
perror_plus("socket");
2335
perror_plus("close");
2342
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2346
/* Raise privileges */
2347
ret_errno = raise_privileges();
2350
perror_plus("Failed to raise privileges");
2355
bool restore_loglevel = false;
2357
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2358
messages about the network interface to mess up the prompt */
2359
ret_linux = klogctl(8, NULL, 5);
2360
if(ret_linux == -1){
2361
perror_plus("klogctl");
2363
restore_loglevel = true;
2366
#endif /* __linux__ */
2367
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2368
ioctl_errno = errno;
2370
if(restore_loglevel){
2371
ret_linux = klogctl(7, NULL, 0);
2372
if(ret_linux == -1){
2373
perror_plus("klogctl");
2376
#endif /* __linux__ */
2378
/* If raise_privileges() succeeded above */
2380
/* Lower privileges */
2381
ret_errno = lower_privileges();
2384
perror_plus("Failed to lower privileges");
2388
/* Close the socket */
2391
perror_plus("close");
2394
if(ret_setflags == -1){
2395
errno = ioctl_errno;
2396
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2401
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2405
/* Sleep checking until interface is running.
2406
Check every 0.25s, up to total time of delay */
2407
for(int i = 0; i < delay * 4; i++){
2408
if(interface_is_running(interface)){
2411
struct timespec sleeptime = { .tv_nsec = 250000000 };
2412
ret = nanosleep(&sleeptime, NULL);
2413
if(ret == -1 and errno != EINTR){
2414
perror_plus("nanosleep");
2422
__attribute__((nonnull, warn_unused_result))
2423
int take_down_interface(const char *const interface){
2424
int old_errno = errno;
2425
struct ifreq network;
2426
unsigned int if_index = if_nametoindex(interface);
2428
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2432
if(interface_is_up(interface)){
2434
int ioctl_errno = 0;
2435
if(not get_flags(interface, &network) and debug){
2437
fprintf_plus(stderr, "Failed to get flags for interface "
2438
"\"%s\"\n", interface);
2442
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2444
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2447
perror_plus("socket");
2453
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2457
/* Raise privileges */
2458
ret_errno = raise_privileges();
2461
perror_plus("Failed to raise privileges");
2464
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2465
ioctl_errno = errno;
2467
/* If raise_privileges() succeeded above */
2469
/* Lower privileges */
2470
ret_errno = lower_privileges();
2473
perror_plus("Failed to lower privileges");
2477
/* Close the socket */
2478
int ret = close(sd);
2480
perror_plus("close");
2483
if(ret_setflags == -1){
2484
errno = ioctl_errno;
2485
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2490
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2498
int main(int argc, char *argv[]){
2499
mandos_context mc = { .server = NULL, .dh_bits = 0,
2500
#if GNUTLS_VERSION_NUMBER >= 0x030606
2501
.priority = "SECURE128:!CTYPE-X.509"
2502
":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
2504
#elif GNUTLS_VERSION_NUMBER < 0x030600
2505
.priority = "SECURE256:!CTYPE-X.509"
2506
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2508
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
2510
.current_server = NULL, .interfaces = NULL,
2511
.interfaces_size = 0 };
2512
AvahiSServiceBrowser *sb = NULL;
2517
int exitcode = EXIT_SUCCESS;
2518
char *interfaces_to_take_down = NULL;
2519
size_t interfaces_to_take_down_size = 0;
2520
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2521
char old_tempdir[] = "/tmp/mandosXXXXXX";
2522
char *tempdir = NULL;
2523
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2524
const char *seckey = PATHDIR "/" SECKEY;
2525
const char *pubkey = PATHDIR "/" PUBKEY;
2526
#if GNUTLS_VERSION_NUMBER >= 0x030606
2527
const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
2528
const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
2530
const char *dh_params_file = NULL;
2531
char *interfaces_hooks = NULL;
2533
bool gnutls_initialized = false;
2534
bool gpgme_initialized = false;
2536
double retry_interval = 10; /* 10s between trying a server and
2537
retrying the same server again */
2539
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2540
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2545
/* Lower any group privileges we might have, just to be safe */
2549
perror_plus("setgid");
2552
/* Lower user privileges (temporarily) */
2556
perror_plus("seteuid");
2564
struct argp_option options[] = {
2565
{ .name = "debug", .key = 128,
2566
.doc = "Debug mode", .group = 3 },
2567
{ .name = "connect", .key = 'c',
2568
.arg = "ADDRESS:PORT",
2569
.doc = "Connect directly to a specific Mandos server",
2571
{ .name = "interface", .key = 'i',
2573
.doc = "Network interface that will be used to search for"
2576
{ .name = "seckey", .key = 's',
2578
.doc = "OpenPGP secret key file base name",
2580
{ .name = "pubkey", .key = 'p',
2582
.doc = "OpenPGP public key file base name",
2584
{ .name = "tls-privkey", .key = 't',
2586
#if GNUTLS_VERSION_NUMBER >= 0x030606
2587
.doc = "TLS private key file base name",
2589
.doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2592
{ .name = "tls-pubkey", .key = 'T',
2594
#if GNUTLS_VERSION_NUMBER >= 0x030606
2595
.doc = "TLS public key file base name",
2597
.doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2600
{ .name = "dh-bits", .key = 129,
2602
.doc = "Bit length of the prime number used in the"
2603
" Diffie-Hellman key exchange",
2605
{ .name = "dh-params", .key = 134,
2607
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2608
" for the Diffie-Hellman key exchange",
2610
{ .name = "priority", .key = 130,
2612
.doc = "GnuTLS priority string for the TLS handshake",
2614
{ .name = "delay", .key = 131,
2616
.doc = "Maximum delay to wait for interface startup",
2618
{ .name = "retry", .key = 132,
2620
.doc = "Retry interval used when denied by the Mandos server",
2622
{ .name = "network-hook-dir", .key = 133,
2624
.doc = "Directory where network hooks are located",
2627
* These reproduce what we would get without ARGP_NO_HELP
2629
{ .name = "help", .key = '?',
2630
.doc = "Give this help list", .group = -1 },
2631
{ .name = "usage", .key = -3,
2632
.doc = "Give a short usage message", .group = -1 },
2633
{ .name = "version", .key = 'V',
2634
.doc = "Print program version", .group = -1 },
2638
error_t parse_opt(int key, char *arg,
2639
struct argp_state *state){
2642
case 128: /* --debug */
2645
case 'c': /* --connect */
2648
case 'i': /* --interface */
2649
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2652
argp_error(state, "%s", strerror(ret_errno));
2655
case 's': /* --seckey */
2658
case 'p': /* --pubkey */
2661
case 't': /* --tls-privkey */
2662
#if GNUTLS_VERSION_NUMBER >= 0x030606
2666
case 'T': /* --tls-pubkey */
2667
#if GNUTLS_VERSION_NUMBER >= 0x030606
2671
case 129: /* --dh-bits */
2673
tmpmax = strtoimax(arg, &tmp, 10);
2674
if(errno != 0 or tmp == arg or *tmp != '\0'
2675
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2676
argp_error(state, "Bad number of DH bits");
2678
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2680
case 134: /* --dh-params */
2681
dh_params_file = arg;
2683
case 130: /* --priority */
2686
case 131: /* --delay */
2688
delay = strtof(arg, &tmp);
2689
if(errno != 0 or tmp == arg or *tmp != '\0'){
2690
argp_error(state, "Bad delay");
2692
case 132: /* --retry */
2694
retry_interval = strtod(arg, &tmp);
2695
if(errno != 0 or tmp == arg or *tmp != '\0'
2696
or (retry_interval * 1000) > INT_MAX
2697
or retry_interval < 0){
2698
argp_error(state, "Bad retry interval");
2701
case 133: /* --network-hook-dir */
2705
* These reproduce what we would get without ARGP_NO_HELP
2707
case '?': /* --help */
2708
argp_state_help(state, state->out_stream,
2709
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2710
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2711
__builtin_unreachable();
2712
case -3: /* --usage */
2713
argp_state_help(state, state->out_stream,
2714
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2715
__builtin_unreachable();
2716
case 'V': /* --version */
2717
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2718
exit(argp_err_exit_status);
2721
return ARGP_ERR_UNKNOWN;
2726
struct argp argp = { .options = options, .parser = parse_opt,
2728
.doc = "Mandos client -- Get and decrypt"
2729
" passwords from a Mandos server" };
2730
ret_errno = argp_parse(&argp, argc, argv,
2731
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2738
perror_plus("argp_parse");
2739
exitcode = EX_OSERR;
2742
exitcode = EX_USAGE;
2748
/* Re-raise privileges */
2749
ret = raise_privileges();
2752
perror_plus("Failed to raise privileges");
2756
/* Work around Debian bug #633582:
2757
<https://bugs.debian.org/633582> */
2759
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2760
int seckey_fd = open(seckey, O_RDONLY);
2761
if(seckey_fd == -1){
2762
perror_plus("open");
2764
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2766
perror_plus("fstat");
2768
if(S_ISREG(st.st_mode)
2769
and st.st_uid == 0 and st.st_gid == 0){
2770
ret = fchown(seckey_fd, uid, gid);
2772
perror_plus("fchown");
2780
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2781
int pubkey_fd = open(pubkey, O_RDONLY);
2782
if(pubkey_fd == -1){
2783
perror_plus("open");
2785
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2787
perror_plus("fstat");
2789
if(S_ISREG(st.st_mode)
2790
and st.st_uid == 0 and st.st_gid == 0){
2791
ret = fchown(pubkey_fd, uid, gid);
2793
perror_plus("fchown");
2801
if(dh_params_file != NULL
2802
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2803
int dhparams_fd = open(dh_params_file, O_RDONLY);
2804
if(dhparams_fd == -1){
2805
perror_plus("open");
2807
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2809
perror_plus("fstat");
2811
if(S_ISREG(st.st_mode)
2812
and st.st_uid == 0 and st.st_gid == 0){
2813
ret = fchown(dhparams_fd, uid, gid);
2815
perror_plus("fchown");
2823
/* Work around Debian bug #981302
2824
<https://bugs.debian.org/981302> */
2825
if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){
2826
ret = symlink("/proc/self/fd", "/dev/fd");
2828
perror_plus("Failed to create /dev/fd symlink");
2832
/* Lower privileges */
2833
ret = lower_privileges();
2836
perror_plus("Failed to lower privileges");
2841
/* Remove invalid interface names (except "none") */
2843
char *interface = NULL;
2844
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2846
if(strcmp(interface, "none") != 0
2847
and if_nametoindex(interface) == 0){
2848
if(interface[0] != '\0'){
2849
fprintf_plus(stderr, "Not using nonexisting interface"
2850
" \"%s\"\n", interface);
2852
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2858
/* Run network hooks */
2860
if(mc.interfaces != NULL){
2861
interfaces_hooks = malloc(mc.interfaces_size);
2862
if(interfaces_hooks == NULL){
2863
perror_plus("malloc");
2866
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2867
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2869
run_network_hooks("start", interfaces_hooks != NULL ?
2870
interfaces_hooks : "", delay);
2874
avahi_set_log_function(empty_log);
2877
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2878
from the signal handler */
2879
/* Initialize the pseudo-RNG for Avahi */
2880
srand((unsigned int) time(NULL));
2881
simple_poll = avahi_simple_poll_new();
2882
if(simple_poll == NULL){
2883
fprintf_plus(stderr,
2884
"Avahi: Failed to create simple poll object.\n");
2885
exitcode = EX_UNAVAILABLE;
2889
sigemptyset(&sigterm_action.sa_mask);
2890
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2892
perror_plus("sigaddset");
2893
exitcode = EX_OSERR;
2896
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2898
perror_plus("sigaddset");
2899
exitcode = EX_OSERR;
2902
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2904
perror_plus("sigaddset");
2905
exitcode = EX_OSERR;
2908
/* Need to check if the handler is SIG_IGN before handling:
2909
| [[info:libc:Initial Signal Actions]] |
2910
| [[info:libc:Basic Signal Handling]] |
2912
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2914
perror_plus("sigaction");
2917
if(old_sigterm_action.sa_handler != SIG_IGN){
2918
ret = sigaction(SIGINT, &sigterm_action, NULL);
2920
perror_plus("sigaction");
2921
exitcode = EX_OSERR;
2925
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2927
perror_plus("sigaction");
2930
if(old_sigterm_action.sa_handler != SIG_IGN){
2931
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2933
perror_plus("sigaction");
2934
exitcode = EX_OSERR;
2938
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2940
perror_plus("sigaction");
2943
if(old_sigterm_action.sa_handler != SIG_IGN){
2944
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2946
perror_plus("sigaction");
2947
exitcode = EX_OSERR;
2952
/* If no interfaces were specified, make a list */
2953
if(mc.interfaces == NULL){
2954
struct dirent **direntries = NULL;
2955
/* Look for any good interfaces */
2956
ret = scandir(sys_class_net, &direntries, good_interface,
2959
/* Add all found interfaces to interfaces list */
2960
for(int i = 0; i < ret; ++i){
2961
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2962
direntries[i]->d_name);
2965
perror_plus("argz_add");
2966
free(direntries[i]);
2970
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2971
direntries[i]->d_name);
2973
free(direntries[i]);
2980
fprintf_plus(stderr, "Could not find a network interface\n");
2981
exitcode = EXIT_FAILURE;
2986
/* Bring up interfaces which are down, and remove any "none"s */
2988
char *interface = NULL;
2989
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2991
/* If interface name is "none", stop bringing up interfaces.
2992
Also remove all instances of "none" from the list */
2993
if(strcmp(interface, "none") == 0){
2994
argz_delete(&mc.interfaces, &mc.interfaces_size,
2997
while((interface = argz_next(mc.interfaces,
2998
mc.interfaces_size, interface))){
2999
if(strcmp(interface, "none") == 0){
3000
argz_delete(&mc.interfaces, &mc.interfaces_size,
3007
bool interface_was_up = interface_is_up(interface);
3008
errno = bring_up_interface(interface, delay);
3009
if(not interface_was_up){
3011
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
3012
" %s\n", interface, strerror(errno));
3014
errno = argz_add(&interfaces_to_take_down,
3015
&interfaces_to_take_down_size,
3018
perror_plus("argz_add");
3023
if(debug and (interfaces_to_take_down == NULL)){
3024
fprintf_plus(stderr, "No interfaces were brought up\n");
3028
/* If we only got one interface, explicitly use only that one */
3029
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
3031
fprintf_plus(stderr, "Using only interface \"%s\"\n",
3034
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
3041
#if GNUTLS_VERSION_NUMBER >= 0x030606
3042
ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
3043
#elif GNUTLS_VERSION_NUMBER < 0x030600
3044
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
3046
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
3049
fprintf_plus(stderr, "init_gnutls_global failed\n");
3050
exitcode = EX_UNAVAILABLE;
3053
gnutls_initialized = true;
3060
/* Try /run/tmp before /tmp */
3061
tempdir = mkdtemp(run_tempdir);
3062
if(tempdir == NULL and errno == ENOENT){
3064
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
3065
run_tempdir, old_tempdir);
3067
tempdir = mkdtemp(old_tempdir);
3069
if(tempdir == NULL){
3070
perror_plus("mkdtemp");
3078
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
3079
fprintf_plus(stderr, "init_gpgme failed\n");
3080
exitcode = EX_UNAVAILABLE;
3083
gpgme_initialized = true;
3090
if(connect_to != NULL){
3091
/* Connect directly, do not use Zeroconf */
3092
/* (Mainly meant for debugging) */
3093
char *address = strrchr(connect_to, ':');
3095
if(address == NULL){
3096
fprintf_plus(stderr, "No colon in address\n");
3097
exitcode = EX_USAGE;
3107
tmpmax = strtoimax(address+1, &tmp, 10);
3108
if(errno != 0 or tmp == address+1 or *tmp != '\0'
3109
or tmpmax != (in_port_t)tmpmax){
3110
fprintf_plus(stderr, "Bad port number\n");
3111
exitcode = EX_USAGE;
3119
port = (in_port_t)tmpmax;
3121
/* Colon in address indicates IPv6 */
3123
if(strchr(connect_to, ':') != NULL){
3125
/* Accept [] around IPv6 address - see RFC 5952 */
3126
if(connect_to[0] == '[' and address[-1] == ']')
3134
address = connect_to;
3140
while(not quit_now){
3141
ret = start_mandos_communication(address, port, if_index, af,
3143
if(quit_now or ret == 0){
3147
fprintf_plus(stderr, "Retrying in %d seconds\n",
3148
(int)retry_interval);
3150
sleep((unsigned int)retry_interval);
3154
exitcode = EXIT_SUCCESS;
554
3165
AvahiServerConfig config;
555
AvahiSServiceBrowser *sb = NULL;
556
const char db[] = "--debug";
559
int returncode = EXIT_SUCCESS;
560
char *basename = rindex(argv[0], '/');
561
if(basename == NULL){
567
char *program_name = malloc(strlen(basename) + sizeof(db));
569
if (program_name == NULL){
574
program_name[0] = '\0';
576
for (int i = 1; i < argc; i++){
577
if (not strncmp(argv[i], db, 5)){
578
strcat(strcat(strcat(program_name, db ), "="), basename);
579
if(not strcmp(argv[i], db) or not strcmp(argv[i], program_name)){
587
avahi_set_log_function(empty_log);
590
/* Initialize the psuedo-RNG */
593
/* Allocate main loop object */
594
if (!(simple_poll = avahi_simple_poll_new())) {
595
fprintf(stderr, "Failed to create simple poll object.\n");
600
/* Do not publish any local records */
3166
/* Do not publish any local Zeroconf records */
601
3167
avahi_server_config_init(&config);
602
3168
config.publish_hinfo = 0;
603
3169
config.publish_addresses = 0;
604
3170
config.publish_workstation = 0;
605
3171
config.publish_domain = 0;
607
3173
/* Allocate a new server */
608
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
610
/* Free the configuration data */
3174
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3175
&config, NULL, NULL, &ret);
3177
/* Free the Avahi configuration data */
611
3178
avahi_server_config_free(&config);
613
/* Check if creating the server object succeeded */
615
fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
616
returncode = EXIT_FAILURE;
620
/* Create the service browser */
621
if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
622
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
623
returncode = EXIT_FAILURE;
627
/* Run the main loop */
630
fprintf(stderr, "Starting avahi loop search\n");
633
avahi_simple_poll_loop(simple_poll);
638
fprintf(stderr, "%s exiting\n", argv[0]);
643
avahi_s_service_browser_free(sb);
646
avahi_server_free(server);
649
avahi_simple_poll_free(simple_poll);
3181
/* Check if creating the Avahi server object succeeded */
3182
if(mc.server == NULL){
3183
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3184
avahi_strerror(ret));
3185
exitcode = EX_UNAVAILABLE;
3193
/* Create the Avahi service browser */
3194
sb = avahi_s_service_browser_new(mc.server, if_index,
3195
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
3196
NULL, 0, browse_callback,
3199
fprintf_plus(stderr, "Failed to create service browser: %s\n",
3200
avahi_strerror(avahi_server_errno(mc.server)));
3201
exitcode = EX_UNAVAILABLE;
3209
/* Run the main loop */
3212
fprintf_plus(stderr, "Starting Avahi loop search\n");
3215
ret = avahi_loop_with_timeout(simple_poll,
3216
(int)(retry_interval * 1000), &mc);
3218
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
3219
(ret == 0) ? "successfully" : "with error");
3225
if(signal_received){
3226
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3227
argv[0], signal_received,
3228
strsignal(signal_received));
3230
fprintf_plus(stderr, "%s exiting\n", argv[0]);
3234
/* Cleanup things */
3235
free(mc.interfaces);
3238
avahi_s_service_browser_free(sb);
3240
if(mc.server != NULL)
3241
avahi_server_free(mc.server);
3243
if(simple_poll != NULL)
3244
avahi_simple_poll_free(simple_poll);
3246
if(gnutls_initialized){
3247
gnutls_certificate_free_credentials(mc.cred);
3248
gnutls_dh_params_deinit(mc.dh_params);
3251
if(gpgme_initialized){
3252
gpgme_release(mc.ctx);
3255
/* Cleans up the circular linked list of Mandos servers the client
3257
if(mc.current_server != NULL){
3258
mc.current_server->prev->next = NULL;
3259
while(mc.current_server != NULL){
3260
server *next = mc.current_server->next;
3262
#pragma GCC diagnostic push
3263
#pragma GCC diagnostic ignored "-Wcast-qual"
3265
free((char *)(mc.current_server->ip));
3267
#pragma GCC diagnostic pop
3269
free(mc.current_server);
3270
mc.current_server = next;
3274
/* Re-raise privileges */
3276
ret = raise_privileges();
3279
perror_plus("Failed to raise privileges");
3282
/* Run network hooks */
3283
run_network_hooks("stop", interfaces_hooks != NULL ?
3284
interfaces_hooks : "", delay);
3286
/* Take down the network interfaces which were brought up */
3288
char *interface = NULL;
3289
while((interface = argz_next(interfaces_to_take_down,
3290
interfaces_to_take_down_size,
3292
ret = take_down_interface(interface);
3295
perror_plus("Failed to take down interface");
3298
if(debug and (interfaces_to_take_down == NULL)){
3299
fprintf_plus(stderr, "No interfaces needed to be taken"
3305
ret = lower_privileges_permanently();
3308
perror_plus("Failed to lower privileges permanently");
3312
free(interfaces_to_take_down);
3313
free(interfaces_hooks);
3315
void clean_dir_at(int base, const char * const dirname,
3317
struct dirent **direntries = NULL;
3319
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3325
perror_plus("open");
3328
int numentries = scandirat(dir_fd, ".", &direntries,
3329
notdotentries, alphasort);
3330
if(numentries >= 0){
3331
for(int i = 0; i < numentries; i++){
3333
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3334
dirname, direntries[i]->d_name);
3336
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3338
if(errno == EISDIR){
3339
dret = unlinkat(dir_fd, direntries[i]->d_name,
3342
if((dret == -1) and (errno == ENOTEMPTY)
3343
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3344
== 0) and (level == 0)){
3345
/* Recurse only in this special case */
3346
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3349
if((dret == -1) and (errno != ENOENT)){
3350
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3351
direntries[i]->d_name, strerror(errno));
3354
free(direntries[i]);
3357
/* need to clean even if 0 because man page doesn't specify */
3359
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3360
if(dret == -1 and errno != ENOENT){
3361
perror_plus("rmdir");
3364
perror_plus("scandirat");
3369
/* Removes the GPGME temp directory and all files inside */
3370
if(tempdir != NULL){
3371
clean_dir_at(-1, tempdir, 0);
3375
sigemptyset(&old_sigterm_action.sa_mask);
3376
old_sigterm_action.sa_handler = SIG_DFL;
3377
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3378
&old_sigterm_action,
3381
perror_plus("sigaction");
3384
ret = raise(signal_received);
3385
} while(ret != 0 and errno == EINTR);
3387
perror_plus("raise");
3390
TEMP_FAILURE_RETRY(pause());