46
117
#include <avahi-common/malloc.h>
47
118
#include <avahi-common/error.h>
50
#include <sys/types.h> /* socket(), setsockopt(), inet_pton() */
51
#include <sys/socket.h> /* socket(), setsockopt(), struct sockaddr_in6, struct in6_addr, inet_pton() */
52
#include <gnutls/gnutls.h> /* ALL GNUTLS STUFF */
53
#include <gnutls/openpgp.h> /* gnutls with openpgp stuff */
55
#include <unistd.h> /* close() */
56
#include <netinet/in.h>
57
#include <stdbool.h> /* true */
58
#include <string.h> /* memset */
59
#include <arpa/inet.h> /* inet_pton() */
60
#include <iso646.h> /* not */
63
#include <errno.h> /* perror() */
68
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
70
#define CERTFILE CERT_ROOT "openpgp-client.txt"
71
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
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,
72
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 */
76
gnutls_session_t session;
77
169
gnutls_certificate_credentials_t cred;
170
unsigned int dh_bits;
78
171
gnutls_dh_params_t dh_params;
82
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
83
gpgme_data_t dh_crypto, dh_plain;
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;
278
__attribute__((nonnull, warn_unused_result))
279
static bool init_gpgme(const char * const seckey,
280
const char * const pubkey,
281
const char * const tempdir,
87
size_t new_packet_capacity = 0;
88
size_t new_packet_length = 0;
89
284
gpgme_engine_info_t engine_info;
287
* Helper function to insert pub and seckey to the engine keyring.
289
bool import_key(const char * const filename){
292
gpgme_data_t pgp_data;
294
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
300
rc = gpgme_data_new_from_fd(&pgp_data, fd);
301
if(rc != GPG_ERR_NO_ERROR){
302
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
303
gpgme_strsource(rc), gpgme_strerror(rc));
307
rc = gpgme_op_import(mc->ctx, pgp_data);
308
if(rc != GPG_ERR_NO_ERROR){
309
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
310
gpgme_strsource(rc), gpgme_strerror(rc));
316
perror_plus("close");
318
gpgme_data_release(pgp_data);
323
fprintf_plus(stderr, "Initializing GPGME\n");
92
327
gpgme_check_version(NULL);
93
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
328
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
329
if(rc != GPG_ERR_NO_ERROR){
330
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
331
gpgme_strsource(rc), gpgme_strerror(rc));
95
/* Set GPGME home directory */
96
rc = gpgme_get_engine_info (&engine_info);
97
if (rc != GPG_ERR_NO_ERROR){
98
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
99
gpgme_strsource(rc), gpgme_strerror(rc));
335
/* Set GPGME home directory for the OpenPGP engine only */
336
rc = gpgme_get_engine_info(&engine_info);
337
if(rc != GPG_ERR_NO_ERROR){
338
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
339
gpgme_strsource(rc), gpgme_strerror(rc));
102
342
while(engine_info != NULL){
103
343
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
104
344
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
105
engine_info->file_name, homedir);
345
engine_info->file_name, tempdir);
108
348
engine_info = engine_info->next;
110
350
if(engine_info == NULL){
111
fprintf(stderr, "Could not set home dir to %s\n", homedir);
115
/* Create new GPGME data buffer from packet buffer */
116
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
117
if (rc != GPG_ERR_NO_ERROR){
118
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
119
gpgme_strsource(rc), gpgme_strerror(rc));
351
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
356
/* Create new GPGME "context" */
357
rc = gpgme_new(&(mc->ctx));
358
if(rc != GPG_ERR_NO_ERROR){
359
fprintf_plus(stderr, "Mandos plugin mandos-client: "
360
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
365
if(not import_key(pubkey) or not import_key(seckey)){
373
* Decrypt OpenPGP data.
374
* Returns -1 on error
376
__attribute__((nonnull, warn_unused_result))
377
static ssize_t pgp_packet_decrypt(const char *cryptotext,
381
gpgme_data_t dh_crypto, dh_plain;
384
size_t plaintext_capacity = 0;
385
ssize_t plaintext_length = 0;
388
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
391
/* Create new GPGME data buffer from memory cryptotext */
392
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
394
if(rc != GPG_ERR_NO_ERROR){
395
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
396
gpgme_strsource(rc), gpgme_strerror(rc));
123
400
/* Create new empty GPGME data buffer for the plaintext */
124
401
rc = gpgme_data_new(&dh_plain);
125
if (rc != GPG_ERR_NO_ERROR){
126
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
127
gpgme_strsource(rc), gpgme_strerror(rc));
131
/* Create new GPGME "context" */
132
rc = gpgme_new(&ctx);
133
if (rc != GPG_ERR_NO_ERROR){
134
fprintf(stderr, "bad gpgme_new: %s: %s\n",
135
gpgme_strsource(rc), gpgme_strerror(rc));
139
/* Decrypt data from the FILE pointer to the plaintext data buffer */
140
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
141
if (rc != GPG_ERR_NO_ERROR){
142
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
143
gpgme_strsource(rc), gpgme_strerror(rc));
147
/* gpgme_decrypt_result_t result; */
148
/* result = gpgme_op_decrypt_result(ctx); */
149
/* fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm); */
150
/* fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage); */
151
/* if(result->file_name != NULL){ */
152
/* fprintf(stderr, "File name: %s\n", result->file_name); */
154
/* gpgme_recipient_t recipient; */
155
/* recipient = result->recipients; */
157
/* while(recipient != NULL){ */
158
/* fprintf(stderr, "Public key algorithm: %s\n", */
159
/* gpgme_pubkey_algo_name(recipient->pubkey_algo)); */
160
/* fprintf(stderr, "Key ID: %s\n", recipient->keyid); */
161
/* fprintf(stderr, "Secret key available: %s\n", */
162
/* recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes"); */
163
/* recipient = recipient->next; */
167
/* Delete the GPGME FILE pointer cryptotext data buffer */
168
gpgme_data_release(dh_crypto);
402
if(rc != GPG_ERR_NO_ERROR){
403
fprintf_plus(stderr, "Mandos plugin mandos-client: "
404
"bad gpgme_data_new: %s: %s\n",
405
gpgme_strsource(rc), gpgme_strerror(rc));
406
gpgme_data_release(dh_crypto);
410
/* Decrypt data from the cryptotext data buffer to the plaintext
412
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
413
if(rc != GPG_ERR_NO_ERROR){
414
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
415
gpgme_strsource(rc), gpgme_strerror(rc));
416
plaintext_length = -1;
418
gpgme_decrypt_result_t result;
419
result = gpgme_op_decrypt_result(mc->ctx);
421
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
423
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
424
result->unsupported_algorithm);
425
fprintf_plus(stderr, "Wrong key usage: %u\n",
426
result->wrong_key_usage);
427
if(result->file_name != NULL){
428
fprintf_plus(stderr, "File name: %s\n", result->file_name);
430
gpgme_recipient_t recipient;
431
recipient = result->recipients;
432
while(recipient != NULL){
433
fprintf_plus(stderr, "Public key algorithm: %s\n",
434
gpgme_pubkey_algo_name
435
(recipient->pubkey_algo));
436
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
437
fprintf_plus(stderr, "Secret key available: %s\n",
438
recipient->status == GPG_ERR_NO_SECKEY
440
recipient = recipient->next;
448
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
170
451
/* Seek back to the beginning of the GPGME plaintext data buffer */
171
gpgme_data_seek(dh_plain, 0, SEEK_SET);
452
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
453
perror_plus("gpgme_data_seek");
454
plaintext_length = -1;
175
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
176
*new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
177
if (*new_packet == NULL){
181
new_packet_capacity += BUFFER_SIZE;
460
plaintext_capacity = incbuffer(plaintext,
461
(size_t)plaintext_length,
463
if(plaintext_capacity == 0){
464
perror_plus("incbuffer");
465
plaintext_length = -1;
184
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
469
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
185
471
/* Print the data, if any */
187
/* If password is empty, then a incorrect error will be printed */
191
perror("gpgme_data_read");
194
new_packet_length += ret;
197
/* Delete the GPGME plaintext data buffer */
477
perror_plus("gpgme_data_read");
478
plaintext_length = -1;
481
plaintext_length += ret;
485
fprintf_plus(stderr, "Decrypted password is: ");
486
for(ssize_t i = 0; i < plaintext_length; i++){
487
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
489
fprintf(stderr, "\n");
494
/* Delete the GPGME cryptotext data buffer */
495
gpgme_data_release(dh_crypto);
497
/* Delete the GPGME plaintext data buffer */
198
498
gpgme_data_release(dh_plain);
199
return new_packet_length;
202
static const char * safer_gnutls_strerror (int value) {
203
const char *ret = gnutls_strerror (value);
209
void debuggnutls(int level, const char* string){
210
fprintf(stderr, "%s", string);
213
int initgnutls(encrypted_session *es){
499
return plaintext_length;
502
__attribute__((warn_unused_result, const))
503
static const char *safe_string(const char *str){
509
__attribute__((warn_unused_result))
510
static const char *safer_gnutls_strerror(int value){
511
const char *ret = gnutls_strerror(value);
512
return safe_string(ret);
515
/* GnuTLS log function callback */
516
__attribute__((nonnull))
517
static void debuggnutls(__attribute__((unused)) int level,
519
fprintf_plus(stderr, "GnuTLS: %s", string);
522
__attribute__((nonnull(1, 2, 4), warn_unused_result))
523
static int init_gnutls_global(const char *pubkeyfilename,
524
const char *seckeyfilename,
525
const char *dhparamsfilename,
217
if ((ret = gnutls_global_init ())
218
!= GNUTLS_E_SUCCESS) {
219
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
223
/* Uncomment to enable full debuggin on the gnutls library */
224
/* gnutls_global_set_log_level(11); */
225
/* gnutls_global_set_log_function(debuggnutls); */
228
/* openpgp credentials */
229
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
230
!= GNUTLS_E_SUCCESS) {
231
fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
531
fprintf_plus(stderr, "Initializing GnuTLS\n");
535
/* "Use a log level over 10 to enable all debugging options."
538
gnutls_global_set_log_level(11);
539
gnutls_global_set_log_function(debuggnutls);
542
/* OpenPGP credentials */
543
ret = gnutls_certificate_allocate_credentials(&mc->cred);
544
if(ret != GNUTLS_E_SUCCESS){
545
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
546
safer_gnutls_strerror(ret));
551
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
552
" secret key %s as GnuTLS credentials\n",
235
557
ret = gnutls_certificate_set_openpgp_key_file
236
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
237
if (ret != GNUTLS_E_SUCCESS) {
239
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
240
ret, CERTFILE, KEYFILE);
241
fprintf(stdout, "The Error is: %s\n",
242
safer_gnutls_strerror(ret));
246
//Gnutls server initialization
247
if ((ret = gnutls_dh_params_init (&es->dh_params))
248
!= GNUTLS_E_SUCCESS) {
249
fprintf (stderr, "Error in dh parameter initialization: %s\n",
250
safer_gnutls_strerror(ret));
254
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
255
!= GNUTLS_E_SUCCESS) {
256
fprintf (stderr, "Error in prime generation: %s\n",
257
safer_gnutls_strerror(ret));
261
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
263
// Gnutls session creation
264
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
265
!= GNUTLS_E_SUCCESS){
266
fprintf(stderr, "Error in gnutls session initialization: %s\n",
267
safer_gnutls_strerror(ret));
270
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
271
!= GNUTLS_E_SUCCESS) {
272
fprintf(stderr, "Syntax error at: %s\n", err);
273
fprintf(stderr, "Gnutls error: %s\n",
274
safer_gnutls_strerror(ret));
278
if ((ret = gnutls_credentials_set
279
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
280
!= GNUTLS_E_SUCCESS) {
281
fprintf(stderr, "Error setting a credentials set: %s\n",
282
safer_gnutls_strerror(ret));
558
(mc->cred, pubkeyfilename, seckeyfilename,
559
GNUTLS_OPENPGP_FMT_BASE64);
560
if(ret != GNUTLS_E_SUCCESS){
562
"Error[%d] while reading the OpenPGP key pair ('%s',"
563
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
564
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
565
safer_gnutls_strerror(ret));
569
/* GnuTLS server initialization */
570
ret = gnutls_dh_params_init(&mc->dh_params);
571
if(ret != GNUTLS_E_SUCCESS){
572
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
573
" initialization: %s\n",
574
safer_gnutls_strerror(ret));
577
/* If a Diffie-Hellman parameters file was given, try to use it */
578
if(dhparamsfilename != NULL){
579
gnutls_datum_t params = { .data = NULL, .size = 0 };
581
int dhpfile = open(dhparamsfilename, O_RDONLY);
584
dhparamsfilename = NULL;
587
size_t params_capacity = 0;
589
params_capacity = incbuffer((char **)¶ms.data,
591
(size_t)params_capacity);
592
if(params_capacity == 0){
593
perror_plus("incbuffer");
596
dhparamsfilename = NULL;
599
ssize_t bytes_read = read(dhpfile,
600
params.data + params.size,
606
/* check bytes_read for failure */
611
dhparamsfilename = NULL;
614
params.size += (unsigned int)bytes_read;
616
ret = close(dhpfile);
618
perror_plus("close");
620
if(params.data == NULL){
621
dhparamsfilename = NULL;
623
if(dhparamsfilename == NULL){
626
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
627
GNUTLS_X509_FMT_PEM);
628
if(ret != GNUTLS_E_SUCCESS){
629
fprintf_plus(stderr, "Failed to parse DH parameters in file"
630
" \"%s\": %s\n", dhparamsfilename,
631
safer_gnutls_strerror(ret));
632
dhparamsfilename = NULL;
637
if(dhparamsfilename == NULL){
638
if(mc->dh_bits == 0){
639
/* Find out the optimal number of DH bits */
640
/* Try to read the private key file */
641
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
643
int secfile = open(seckeyfilename, O_RDONLY);
648
size_t buffer_capacity = 0;
650
buffer_capacity = incbuffer((char **)&buffer.data,
652
(size_t)buffer_capacity);
653
if(buffer_capacity == 0){
654
perror_plus("incbuffer");
659
ssize_t bytes_read = read(secfile,
660
buffer.data + buffer.size,
666
/* check bytes_read for failure */
673
buffer.size += (unsigned int)bytes_read;
677
/* If successful, use buffer to parse private key */
678
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
679
if(buffer.data != NULL){
681
gnutls_openpgp_privkey_t privkey = NULL;
682
ret = gnutls_openpgp_privkey_init(&privkey);
683
if(ret != GNUTLS_E_SUCCESS){
684
fprintf_plus(stderr, "Error initializing OpenPGP key"
686
safer_gnutls_strerror(ret));
690
ret = gnutls_openpgp_privkey_import
691
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
692
if(ret != GNUTLS_E_SUCCESS){
693
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
694
safer_gnutls_strerror(ret));
700
/* Use private key to suggest an appropriate
702
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
703
gnutls_openpgp_privkey_deinit(privkey);
705
fprintf_plus(stderr, "This OpenPGP key implies using"
706
" a GnuTLS security parameter \"%s\".\n",
707
safe_string(gnutls_sec_param_get_name
713
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
714
/* Err on the side of caution */
715
sec_param = GNUTLS_SEC_PARAM_ULTRA;
717
fprintf_plus(stderr, "Falling back to security parameter"
719
safe_string(gnutls_sec_param_get_name
724
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
728
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
729
" implies %u DH bits; using that.\n",
730
safe_string(gnutls_sec_param_get_name
735
fprintf_plus(stderr, "Failed to get implied number of DH"
736
" bits for security parameter \"%s\"): %s\n",
737
safe_string(gnutls_sec_param_get_name
739
safer_gnutls_strerror(ret));
743
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
746
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
747
if(ret != GNUTLS_E_SUCCESS){
748
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
749
" bits): %s\n", mc->dh_bits,
750
safer_gnutls_strerror(ret));
754
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
760
gnutls_certificate_free_credentials(mc->cred);
761
gnutls_dh_params_deinit(mc->dh_params);
765
__attribute__((nonnull, warn_unused_result))
766
static int init_gnutls_session(gnutls_session_t *session,
769
/* GnuTLS session creation */
771
ret = gnutls_init(session, GNUTLS_SERVER);
775
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
776
if(ret != GNUTLS_E_SUCCESS){
778
"Error in GnuTLS session initialization: %s\n",
779
safer_gnutls_strerror(ret));
785
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
787
gnutls_deinit(*session);
790
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
791
if(ret != GNUTLS_E_SUCCESS){
792
fprintf_plus(stderr, "Syntax error at: %s\n", err);
793
fprintf_plus(stderr, "GnuTLS error: %s\n",
794
safer_gnutls_strerror(ret));
795
gnutls_deinit(*session);
801
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
804
gnutls_deinit(*session);
807
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
808
if(ret != GNUTLS_E_SUCCESS){
809
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
810
safer_gnutls_strerror(ret));
811
gnutls_deinit(*session);
286
815
/* ignore client certificate if any. */
287
gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
289
gnutls_dh_set_prime_bits (es->session, DH_BITS);
816
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
294
void empty_log(AvahiLogLevel level, const char *txt){}
296
int start_mandos_communcation(char *ip, uint16_t port){
298
struct sockaddr_in6 to;
299
struct in6_addr ip_addr;
300
encrypted_session es;
821
/* Avahi log function callback */
822
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
823
__attribute__((unused)) const char *txt){}
825
/* Set effective uid to 0, return errno */
826
__attribute__((warn_unused_result))
827
int raise_privileges(void){
828
int old_errno = errno;
830
if(seteuid(0) == -1){
837
/* Set effective and real user ID to 0. Return errno. */
838
__attribute__((warn_unused_result))
839
int raise_privileges_permanently(void){
840
int old_errno = errno;
841
int ret = raise_privileges();
853
/* Set effective user ID to unprivileged saved user ID */
854
__attribute__((warn_unused_result))
855
int lower_privileges(void){
856
int old_errno = errno;
858
if(seteuid(uid) == -1){
865
/* Lower privileges permanently */
866
__attribute__((warn_unused_result))
867
int lower_privileges_permanently(void){
868
int old_errno = errno;
870
if(setuid(uid) == -1){
877
/* Helper function to add_local_route() and delete_local_route() */
878
__attribute__((nonnull, warn_unused_result))
879
static bool add_delete_local_route(const bool add,
881
AvahiIfIndex if_index){
883
char helper[] = "mandos-client-iprouteadddel";
884
char add_arg[] = "add";
885
char delete_arg[] = "delete";
886
char debug_flag[] = "--debug";
887
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
888
if(pluginhelperdir == NULL){
890
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
891
" variable not set; cannot run helper\n");
896
char interface[IF_NAMESIZE];
897
if(if_indextoname((unsigned int)if_index, interface) == NULL){
898
perror_plus("if_indextoname");
902
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
904
perror_plus("open(\"/dev/null\", O_RDONLY)");
910
/* Raise privileges */
911
errno = raise_privileges_permanently();
913
perror_plus("Failed to raise privileges");
914
/* _exit(EX_NOPERM); */
920
perror_plus("setgid");
923
/* Reset supplementary groups */
925
ret = setgroups(0, NULL);
927
perror_plus("setgroups");
931
ret = dup2(devnull, STDIN_FILENO);
933
perror_plus("dup2(devnull, STDIN_FILENO)");
936
ret = close(devnull);
938
perror_plus("close");
941
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
943
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
946
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
951
if(helperdir_fd == -1){
953
_exit(EX_UNAVAILABLE);
955
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
958
perror_plus("openat");
960
_exit(EX_UNAVAILABLE);
964
#pragma GCC diagnostic push
965
#pragma GCC diagnostic ignored "-Wcast-qual"
967
if(fexecve(helper_fd, (char *const [])
968
{ helper, add ? add_arg : delete_arg, (char *)address,
969
interface, debug ? debug_flag : NULL, NULL },
972
#pragma GCC diagnostic pop
974
perror_plus("fexecve");
986
pret = waitpid(pid, &status, 0);
987
if(pret == -1 and errno == EINTR and quit_now){
988
int errno_raising = 0;
989
if((errno = raise_privileges()) != 0){
990
errno_raising = errno;
991
perror_plus("Failed to raise privileges in order to"
992
" kill helper program");
994
if(kill(pid, SIGTERM) == -1){
997
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
998
perror_plus("Failed to lower privileges after killing"
1003
} while(pret == -1 and errno == EINTR);
1005
perror_plus("waitpid");
1008
if(WIFEXITED(status)){
1009
if(WEXITSTATUS(status) != 0){
1010
fprintf_plus(stderr, "Error: iprouteadddel exited"
1011
" with status %d\n", WEXITSTATUS(status));
1016
if(WIFSIGNALED(status)){
1017
fprintf_plus(stderr, "Error: iprouteadddel died by"
1018
" signal %d\n", WTERMSIG(status));
1021
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1025
__attribute__((nonnull, warn_unused_result))
1026
static bool add_local_route(const char *address,
1027
AvahiIfIndex if_index){
1029
fprintf_plus(stderr, "Adding route to %s\n", address);
1031
return add_delete_local_route(true, address, if_index);
1034
__attribute__((nonnull, warn_unused_result))
1035
static bool delete_local_route(const char *address,
1036
AvahiIfIndex if_index){
1038
fprintf_plus(stderr, "Removing route to %s\n", address);
1040
return add_delete_local_route(false, address, if_index);
1043
/* Called when a Mandos server is found */
1044
__attribute__((nonnull, warn_unused_result))
1045
static int start_mandos_communication(const char *ip, in_port_t port,
1046
AvahiIfIndex if_index,
1047
int af, mandos_context *mc){
1048
int ret, tcp_sd = -1;
1050
struct sockaddr_storage to;
301
1051
char *buffer = NULL;
302
char *decrypted_buffer;
1052
char *decrypted_buffer = NULL;
303
1053
size_t buffer_length = 0;
304
1054
size_t buffer_capacity = 0;
305
ssize_t decrypted_buffer_size;
309
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
315
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5);
317
perror("setsockopt bindtodevice");
321
memset(&to,0,sizeof(to));
322
to.sin6_family = AF_INET6;
323
ret = inet_pton(AF_INET6, ip, &ip_addr);
1057
gnutls_session_t session;
1058
int pf; /* Protocol family */
1059
bool route_added = false;
1076
fprintf_plus(stderr, "Bad address family: %d\n", af);
1081
/* If the interface is specified and we have a list of interfaces */
1082
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1083
/* Check if the interface is one of the interfaces we are using */
1086
char *interface = NULL;
1087
while((interface = argz_next(mc->interfaces,
1088
mc->interfaces_size,
1090
if(if_nametoindex(interface) == (unsigned int)if_index){
1097
/* This interface does not match any in the list, so we don't
1098
connect to the server */
1100
char interface[IF_NAMESIZE];
1101
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1102
perror_plus("if_indextoname");
1104
fprintf_plus(stderr, "Skipping server on non-used interface"
1106
if_indextoname((unsigned int)if_index,
1114
ret = init_gnutls_session(&session, mc);
1120
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1121
PRIuMAX "\n", ip, (uintmax_t)port);
1124
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1127
perror_plus("socket");
1138
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1139
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1140
ret = inet_pton(af, ip, &to6->sin6_addr);
1142
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1143
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1144
ret = inet_pton(af, ip, &to4->sin_addr);
1148
perror_plus("inet_pton");
329
fprintf(stderr, "Bad address: %s\n", ip);
332
to.sin6_port = htons(port);
333
to.sin6_scope_id = if_nametoindex("eth0");
335
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
341
ret = initgnutls (&es);
348
gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
350
ret = gnutls_handshake (es.session);
352
if (ret != GNUTLS_E_SUCCESS){
353
fprintf(stderr, "\n*** Handshake failed ***\n");
361
if (buffer_length + BUFFER_SIZE > buffer_capacity){
362
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
367
buffer_capacity += BUFFER_SIZE;
370
ret = gnutls_record_recv
371
(es.session, buffer+buffer_length, BUFFER_SIZE);
1154
fprintf_plus(stderr, "Bad address: %s\n", ip);
1159
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1160
if(IN6_IS_ADDR_LINKLOCAL
1161
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1162
if(if_index == AVAHI_IF_UNSPEC){
1163
fprintf_plus(stderr, "An IPv6 link-local address is"
1164
" incomplete without a network interface\n");
1168
/* Set the network interface number as scope */
1169
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1172
((struct sockaddr_in *)&to)->sin_port = htons(port);
1181
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1182
char interface[IF_NAMESIZE];
1183
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1184
perror_plus("if_indextoname");
1186
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1187
"\n", ip, interface, (uintmax_t)port);
1190
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1191
ip, (uintmax_t)port);
1193
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1194
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1196
ret = getnameinfo((struct sockaddr *)&to,
1197
sizeof(struct sockaddr_in6),
1198
addrstr, sizeof(addrstr), NULL, 0,
1201
ret = getnameinfo((struct sockaddr *)&to,
1202
sizeof(struct sockaddr_in),
1203
addrstr, sizeof(addrstr), NULL, 0,
1206
if(ret == EAI_SYSTEM){
1207
perror_plus("getnameinfo");
1208
} else if(ret != 0) {
1209
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1210
} else if(strcmp(addrstr, ip) != 0){
1211
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1222
ret = connect(tcp_sd, (struct sockaddr *)&to,
1223
sizeof(struct sockaddr_in6));
1225
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1226
sizeof(struct sockaddr_in));
1229
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1230
and if_index != AVAHI_IF_UNSPEC
1231
and connect_to == NULL
1232
and not route_added and
1233
((af == AF_INET6 and not
1234
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1236
or (af == AF_INET and
1237
/* Not a a IPv4LL address */
1238
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1239
& 0xFFFF0000L) != 0xA9FE0000L))){
1240
/* Work around Avahi bug - Avahi does not announce link-local
1241
addresses if it has a global address, so local hosts with
1242
*only* a link-local address (e.g. Mandos clients) cannot
1243
connect to a Mandos server announced by Avahi on a server
1244
host with a global address. Work around this by retrying
1245
with an explicit route added with the server's address.
1247
Avahi bug reference:
1248
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1249
https://bugs.debian.org/587961
1252
fprintf_plus(stderr, "Mandos server unreachable, trying"
1256
route_added = add_local_route(ip, if_index);
1262
if(errno != ECONNREFUSED or debug){
1264
perror_plus("connect");
1277
const char *out = mandos_protocol_version;
1280
size_t out_size = strlen(out);
1281
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1282
out_size - written));
1285
perror_plus("write");
1289
written += (size_t)ret;
1290
if(written < out_size){
1293
if(out == mandos_protocol_version){
1308
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1316
/* This casting via intptr_t is to eliminate warning about casting
1317
an int to a pointer type. This is exactly how the GnuTLS Guile
1318
function "set-session-transport-fd!" does it. */
1319
gnutls_transport_set_ptr(session,
1320
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1328
ret = gnutls_handshake(session);
1333
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1335
if(ret != GNUTLS_E_SUCCESS){
1337
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1344
/* Read OpenPGP packet that contains the wanted password */
1347
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1358
buffer_capacity = incbuffer(&buffer, buffer_length,
1360
if(buffer_capacity == 0){
1362
perror_plus("incbuffer");
1372
sret = gnutls_record_recv(session, buffer+buffer_length,
377
1379
case GNUTLS_E_INTERRUPTED:
378
1380
case GNUTLS_E_AGAIN:
380
1382
case GNUTLS_E_REHANDSHAKE:
381
ret = gnutls_handshake (es.session);
383
fprintf(stderr, "\n*** Handshake failed ***\n");
1384
ret = gnutls_handshake(session);
1390
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1392
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
390
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
392
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1400
fprintf_plus(stderr, "Unknown error while reading data from"
1401
" encrypted session with Mandos server\n");
1402
gnutls_bye(session, GNUTLS_SHUT_RDWR);
396
buffer_length += ret;
400
if (buffer_length > 0){
401
if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) == 0){
1407
buffer_length += (size_t) sret;
1412
fprintf_plus(stderr, "Closing TLS session\n");
1421
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1426
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1428
if(buffer_length > 0){
1429
ssize_t decrypted_buffer_size;
1430
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1431
&decrypted_buffer, mc);
1432
if(decrypted_buffer_size >= 0){
1436
while(written < (size_t) decrypted_buffer_size){
1442
ret = (int)fwrite(decrypted_buffer + written, 1,
1443
(size_t)decrypted_buffer_size - written,
1445
if(ret == 0 and ferror(stdout)){
1448
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1454
written += (size_t)ret;
1456
ret = fflush(stdout);
1460
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1470
/* Shutdown procedure */
1475
if(not delete_local_route(ip, if_index)){
1476
fprintf_plus(stderr, "Failed to delete local route to %s on"
1477
" interface %d", ip, if_index);
1481
free(decrypted_buffer);
1484
ret = close(tcp_sd);
1490
perror_plus("close");
1492
gnutls_deinit(session);
404
fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
405
free(decrypted_buffer);
412
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
415
gnutls_deinit (es.session);
416
gnutls_certificate_free_credentials (es.cred);
417
gnutls_global_deinit ();
421
static AvahiSimplePoll *simple_poll = NULL;
422
static AvahiServer *server = NULL;
424
static void resolve_callback(
425
AvahiSServiceResolver *r,
426
AVAHI_GCC_UNUSED AvahiIfIndex interface,
427
AVAHI_GCC_UNUSED AvahiProtocol protocol,
428
AvahiResolverEvent event,
432
const char *host_name,
433
const AvahiAddress *address,
435
AvahiStringList *txt,
436
AvahiLookupResultFlags flags,
437
AVAHI_GCC_UNUSED void* userdata) {
441
/* Called whenever a service has been resolved successfully or timed out */
444
case AVAHI_RESOLVER_FAILURE:
445
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)));
448
case AVAHI_RESOLVER_FOUND: {
449
char ip[AVAHI_ADDRESS_STR_MAX];
450
avahi_address_snprint(ip, sizeof(ip), address);
451
int ret = start_mandos_communcation(ip, port);
1502
static void resolve_callback(AvahiSServiceResolver *r,
1503
AvahiIfIndex interface,
1504
AvahiProtocol proto,
1505
AvahiResolverEvent event,
1509
const char *host_name,
1510
const AvahiAddress *address,
1512
AVAHI_GCC_UNUSED AvahiStringList *txt,
1513
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1520
/* Called whenever a service has been resolved successfully or
459
1524
avahi_s_service_resolver_free(r);
462
static void browse_callback(
463
AvahiSServiceBrowser *b,
464
AvahiIfIndex interface,
465
AvahiProtocol protocol,
466
AvahiBrowserEvent event,
470
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
473
AvahiServer *s = userdata;
476
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
480
case AVAHI_BROWSER_FAILURE:
482
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
483
avahi_simple_poll_quit(simple_poll);
486
case AVAHI_BROWSER_NEW:
487
/* We ignore the returned resolver object. In the callback
488
function we free it. If the server is terminated before
489
the callback function is called the server will free
490
the resolver for us. */
492
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
493
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
497
case AVAHI_BROWSER_REMOVE:
500
case AVAHI_BROWSER_ALL_FOR_NOW:
501
case AVAHI_BROWSER_CACHE_EXHAUSTED:
506
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1530
case AVAHI_RESOLVER_FAILURE:
1531
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1532
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1534
avahi_strerror(avahi_server_errno
1535
(((mandos_context*)mc)->server)));
1538
case AVAHI_RESOLVER_FOUND:
1540
char ip[AVAHI_ADDRESS_STR_MAX];
1541
avahi_address_snprint(ip, sizeof(ip), address);
1543
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1544
PRIdMAX ") on port %" PRIu16 "\n", name,
1545
host_name, ip, (intmax_t)interface, port);
1547
int ret = start_mandos_communication(ip, (in_port_t)port,
1549
avahi_proto_to_af(proto),
1552
avahi_simple_poll_quit(simple_poll);
1554
if(not add_server(ip, (in_port_t)port, interface,
1555
avahi_proto_to_af(proto),
1556
&((mandos_context*)mc)->current_server)){
1557
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1563
avahi_s_service_resolver_free(r);
1566
static void browse_callback(AvahiSServiceBrowser *b,
1567
AvahiIfIndex interface,
1568
AvahiProtocol protocol,
1569
AvahiBrowserEvent event,
1573
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1580
/* Called whenever a new services becomes available on the LAN or
1581
is removed from the LAN */
1589
case AVAHI_BROWSER_FAILURE:
1591
fprintf_plus(stderr, "(Avahi browser) %s\n",
1592
avahi_strerror(avahi_server_errno
1593
(((mandos_context*)mc)->server)));
1594
avahi_simple_poll_quit(simple_poll);
1597
case AVAHI_BROWSER_NEW:
1598
/* We ignore the returned Avahi resolver object. In the callback
1599
function we free it. If the Avahi server is terminated before
1600
the callback function is called the Avahi server will free the
1603
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1604
interface, protocol, name, type,
1605
domain, protocol, 0,
1606
resolve_callback, mc) == NULL)
1607
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1609
avahi_strerror(avahi_server_errno
1610
(((mandos_context*)mc)->server)));
1613
case AVAHI_BROWSER_REMOVE:
1616
case AVAHI_BROWSER_ALL_FOR_NOW:
1617
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1619
fprintf_plus(stderr, "No Mandos server found, still"
1626
/* Signal handler that stops main loop after SIGTERM */
1627
static void handle_sigterm(int sig){
1632
signal_received = sig;
1633
int old_errno = errno;
1634
/* set main loop to exit */
1635
if(simple_poll != NULL){
1636
avahi_simple_poll_quit(simple_poll);
1641
__attribute__((nonnull, warn_unused_result))
1642
bool get_flags(const char *ifname, struct ifreq *ifr){
1646
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1649
perror_plus("socket");
1653
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1654
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1655
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1659
perror_plus("ioctl SIOCGIFFLAGS");
1662
if((close(s) == -1) and debug){
1664
perror_plus("close");
1669
if((close(s) == -1) and debug){
1671
perror_plus("close");
1677
__attribute__((nonnull, warn_unused_result))
1678
bool good_flags(const char *ifname, const struct ifreq *ifr){
1680
/* Reject the loopback device */
1681
if(ifr->ifr_flags & IFF_LOOPBACK){
1683
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1688
/* Accept point-to-point devices only if connect_to is specified */
1689
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1691
fprintf_plus(stderr, "Accepting point-to-point interface"
1692
" \"%s\"\n", ifname);
1696
/* Otherwise, reject non-broadcast-capable devices */
1697
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1699
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1700
" \"%s\"\n", ifname);
1704
/* Reject non-ARP interfaces (including dummy interfaces) */
1705
if(ifr->ifr_flags & IFF_NOARP){
1707
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1713
/* Accept this device */
1715
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1721
* This function determines if a directory entry in /sys/class/net
1722
* corresponds to an acceptable network device.
1723
* (This function is passed to scandir(3) as a filter function.)
1725
__attribute__((nonnull, warn_unused_result))
1726
int good_interface(const struct dirent *if_entry){
1727
if(if_entry->d_name[0] == '.'){
1732
if(not get_flags(if_entry->d_name, &ifr)){
1734
fprintf_plus(stderr, "Failed to get flags for interface "
1735
"\"%s\"\n", if_entry->d_name);
1740
if(not good_flags(if_entry->d_name, &ifr)){
1747
* This function determines if a network interface is up.
1749
__attribute__((nonnull, warn_unused_result))
1750
bool interface_is_up(const char *interface){
1752
if(not get_flags(interface, &ifr)){
1754
fprintf_plus(stderr, "Failed to get flags for interface "
1755
"\"%s\"\n", interface);
1760
return (bool)(ifr.ifr_flags & IFF_UP);
1764
* This function determines if a network interface is running
1766
__attribute__((nonnull, warn_unused_result))
1767
bool interface_is_running(const char *interface){
1769
if(not get_flags(interface, &ifr)){
1771
fprintf_plus(stderr, "Failed to get flags for interface "
1772
"\"%s\"\n", interface);
1777
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1780
__attribute__((nonnull, pure, warn_unused_result))
1781
int notdotentries(const struct dirent *direntry){
1782
/* Skip "." and ".." */
1783
if(direntry->d_name[0] == '.'
1784
and (direntry->d_name[1] == '\0'
1785
or (direntry->d_name[1] == '.'
1786
and direntry->d_name[2] == '\0'))){
1792
/* Is this directory entry a runnable program? */
1793
__attribute__((nonnull, warn_unused_result))
1794
int runnable_hook(const struct dirent *direntry){
1799
if((direntry->d_name)[0] == '\0'){
1804
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1805
"abcdefghijklmnopqrstuvwxyz"
1808
if((direntry->d_name)[sret] != '\0'){
1809
/* Contains non-allowed characters */
1811
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1817
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1820
perror_plus("Could not stat hook");
1824
if(not (S_ISREG(st.st_mode))){
1825
/* Not a regular file */
1827
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1832
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1833
/* Not executable */
1835
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1841
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1847
__attribute__((nonnull, warn_unused_result))
1848
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1849
mandos_context *mc){
1851
struct timespec now;
1852
struct timespec waited_time;
1853
intmax_t block_time;
1856
if(mc->current_server == NULL){
1858
fprintf_plus(stderr, "Wait until first server is found."
1861
ret = avahi_simple_poll_iterate(s, -1);
1864
fprintf_plus(stderr, "Check current_server if we should run"
1867
/* the current time */
1868
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1870
perror_plus("clock_gettime");
1873
/* Calculating in ms how long time between now and server
1874
who we visted longest time ago. Now - last seen. */
1875
waited_time.tv_sec = (now.tv_sec
1876
- mc->current_server->last_seen.tv_sec);
1877
waited_time.tv_nsec = (now.tv_nsec
1878
- mc->current_server->last_seen.tv_nsec);
1879
/* total time is 10s/10,000ms.
1880
Converting to s from ms by dividing by 1,000,
1881
and ns to ms by dividing by 1,000,000. */
1882
block_time = ((retry_interval
1883
- ((intmax_t)waited_time.tv_sec * 1000))
1884
- ((intmax_t)waited_time.tv_nsec / 1000000));
1887
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1891
if(block_time <= 0){
1892
ret = start_mandos_communication(mc->current_server->ip,
1893
mc->current_server->port,
1894
mc->current_server->if_index,
1895
mc->current_server->af, mc);
1897
avahi_simple_poll_quit(s);
1900
ret = clock_gettime(CLOCK_MONOTONIC,
1901
&mc->current_server->last_seen);
1903
perror_plus("clock_gettime");
1906
mc->current_server = mc->current_server->next;
1907
block_time = 0; /* Call avahi to find new Mandos
1908
servers, but don't block */
1911
ret = avahi_simple_poll_iterate(s, (int)block_time);
1914
if(ret > 0 or errno != EINTR){
1915
return (ret != 1) ? ret : 0;
1921
__attribute__((nonnull))
1922
void run_network_hooks(const char *mode, const char *interface,
1924
struct dirent **direntries = NULL;
1925
if(hookdir_fd == -1){
1926
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1928
if(hookdir_fd == -1){
1929
if(errno == ENOENT){
1931
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1932
" found\n", hookdir);
1935
perror_plus("open");
1940
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1942
perror_plus("open(\"/dev/null\", O_RDONLY)");
1945
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1946
runnable_hook, alphasort);
1948
perror_plus("scandir");
1952
struct dirent *direntry;
1954
for(int i = 0; i < numhooks; i++){
1955
direntry = direntries[i];
1957
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1960
pid_t hook_pid = fork();
1963
/* Raise privileges */
1964
errno = raise_privileges_permanently();
1966
perror_plus("Failed to raise privileges");
1973
perror_plus("setgid");
1976
/* Reset supplementary groups */
1978
ret = setgroups(0, NULL);
1980
perror_plus("setgroups");
1983
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1985
perror_plus("setenv");
1988
ret = setenv("DEVICE", interface, 1);
1990
perror_plus("setenv");
1993
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1995
perror_plus("setenv");
1998
ret = setenv("MODE", mode, 1);
2000
perror_plus("setenv");
2004
ret = asprintf(&delaystring, "%f", (double)delay);
2006
perror_plus("asprintf");
2009
ret = setenv("DELAY", delaystring, 1);
2012
perror_plus("setenv");
2016
if(connect_to != NULL){
2017
ret = setenv("CONNECT", connect_to, 1);
2019
perror_plus("setenv");
2023
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2027
perror_plus("openat");
2028
_exit(EXIT_FAILURE);
2030
if(close(hookdir_fd) == -1){
2031
perror_plus("close");
2032
_exit(EXIT_FAILURE);
2034
ret = dup2(devnull, STDIN_FILENO);
2036
perror_plus("dup2(devnull, STDIN_FILENO)");
2039
ret = close(devnull);
2041
perror_plus("close");
2044
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2046
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2049
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2051
perror_plus("fexecve");
2052
_exit(EXIT_FAILURE);
2056
perror_plus("fork");
2061
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2062
perror_plus("waitpid");
2066
if(WIFEXITED(status)){
2067
if(WEXITSTATUS(status) != 0){
2068
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2069
" with status %d\n", direntry->d_name,
2070
WEXITSTATUS(status));
2074
} else if(WIFSIGNALED(status)){
2075
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2076
" signal %d\n", direntry->d_name,
2081
fprintf_plus(stderr, "Warning: network hook \"%s\""
2082
" crashed\n", direntry->d_name);
2088
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2094
if(close(hookdir_fd) == -1){
2095
perror_plus("close");
2102
__attribute__((nonnull, warn_unused_result))
2103
int bring_up_interface(const char *const interface,
2105
int old_errno = errno;
2107
struct ifreq network;
2108
unsigned int if_index = if_nametoindex(interface);
2110
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2120
if(not interface_is_up(interface)){
2122
int ioctl_errno = 0;
2123
if(not get_flags(interface, &network)){
2125
fprintf_plus(stderr, "Failed to get flags for interface "
2126
"\"%s\"\n", interface);
2130
network.ifr_flags |= IFF_UP; /* set flag */
2132
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2135
perror_plus("socket");
2143
perror_plus("close");
2150
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2154
/* Raise privileges */
2155
ret_errno = raise_privileges();
2158
perror_plus("Failed to raise privileges");
2163
bool restore_loglevel = false;
2165
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2166
messages about the network interface to mess up the prompt */
2167
ret_linux = klogctl(8, NULL, 5);
2168
if(ret_linux == -1){
2169
perror_plus("klogctl");
2171
restore_loglevel = true;
2174
#endif /* __linux__ */
2175
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2176
ioctl_errno = errno;
2178
if(restore_loglevel){
2179
ret_linux = klogctl(7, NULL, 0);
2180
if(ret_linux == -1){
2181
perror_plus("klogctl");
2184
#endif /* __linux__ */
2186
/* If raise_privileges() succeeded above */
2188
/* Lower privileges */
2189
ret_errno = lower_privileges();
2192
perror_plus("Failed to lower privileges");
2196
/* Close the socket */
2199
perror_plus("close");
2202
if(ret_setflags == -1){
2203
errno = ioctl_errno;
2204
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2209
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2213
/* Sleep checking until interface is running.
2214
Check every 0.25s, up to total time of delay */
2215
for(int i = 0; i < delay * 4; i++){
2216
if(interface_is_running(interface)){
2219
struct timespec sleeptime = { .tv_nsec = 250000000 };
2220
ret = nanosleep(&sleeptime, NULL);
2221
if(ret == -1 and errno != EINTR){
2222
perror_plus("nanosleep");
2230
__attribute__((nonnull, warn_unused_result))
2231
int take_down_interface(const char *const interface){
2232
int old_errno = errno;
2233
struct ifreq network;
2234
unsigned int if_index = if_nametoindex(interface);
2236
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2240
if(interface_is_up(interface)){
2242
int ioctl_errno = 0;
2243
if(not get_flags(interface, &network) and debug){
2245
fprintf_plus(stderr, "Failed to get flags for interface "
2246
"\"%s\"\n", interface);
2250
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2252
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2255
perror_plus("socket");
2261
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2265
/* Raise privileges */
2266
ret_errno = raise_privileges();
2269
perror_plus("Failed to raise privileges");
2272
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2273
ioctl_errno = errno;
2275
/* If raise_privileges() succeeded above */
2277
/* Lower privileges */
2278
ret_errno = lower_privileges();
2281
perror_plus("Failed to lower privileges");
2285
/* Close the socket */
2286
int ret = close(sd);
2288
perror_plus("close");
2291
if(ret_setflags == -1){
2292
errno = ioctl_errno;
2293
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2298
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2306
int main(int argc, char *argv[]){
2307
mandos_context mc = { .server = NULL, .dh_bits = 0,
2308
.priority = "SECURE256:!CTYPE-X.509"
2309
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2310
.current_server = NULL, .interfaces = NULL,
2311
.interfaces_size = 0 };
2312
AvahiSServiceBrowser *sb = NULL;
2317
int exitcode = EXIT_SUCCESS;
2318
char *interfaces_to_take_down = NULL;
2319
size_t interfaces_to_take_down_size = 0;
2320
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2321
char old_tempdir[] = "/tmp/mandosXXXXXX";
2322
char *tempdir = NULL;
2323
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2324
const char *seckey = PATHDIR "/" SECKEY;
2325
const char *pubkey = PATHDIR "/" PUBKEY;
2326
const char *dh_params_file = NULL;
2327
char *interfaces_hooks = NULL;
2329
bool gnutls_initialized = false;
2330
bool gpgme_initialized = false;
2332
double retry_interval = 10; /* 10s between trying a server and
2333
retrying the same server again */
2335
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2336
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2341
/* Lower any group privileges we might have, just to be safe */
2345
perror_plus("setgid");
2348
/* Lower user privileges (temporarily) */
2352
perror_plus("seteuid");
2360
struct argp_option options[] = {
2361
{ .name = "debug", .key = 128,
2362
.doc = "Debug mode", .group = 3 },
2363
{ .name = "connect", .key = 'c',
2364
.arg = "ADDRESS:PORT",
2365
.doc = "Connect directly to a specific Mandos server",
2367
{ .name = "interface", .key = 'i',
2369
.doc = "Network interface that will be used to search for"
2372
{ .name = "seckey", .key = 's',
2374
.doc = "OpenPGP secret key file base name",
2376
{ .name = "pubkey", .key = 'p',
2378
.doc = "OpenPGP public key file base name",
2380
{ .name = "dh-bits", .key = 129,
2382
.doc = "Bit length of the prime number used in the"
2383
" Diffie-Hellman key exchange",
2385
{ .name = "dh-params", .key = 134,
2387
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2388
" for the Diffie-Hellman key exchange",
2390
{ .name = "priority", .key = 130,
2392
.doc = "GnuTLS priority string for the TLS handshake",
2394
{ .name = "delay", .key = 131,
2396
.doc = "Maximum delay to wait for interface startup",
2398
{ .name = "retry", .key = 132,
2400
.doc = "Retry interval used when denied by the Mandos server",
2402
{ .name = "network-hook-dir", .key = 133,
2404
.doc = "Directory where network hooks are located",
2407
* These reproduce what we would get without ARGP_NO_HELP
2409
{ .name = "help", .key = '?',
2410
.doc = "Give this help list", .group = -1 },
2411
{ .name = "usage", .key = -3,
2412
.doc = "Give a short usage message", .group = -1 },
2413
{ .name = "version", .key = 'V',
2414
.doc = "Print program version", .group = -1 },
2418
error_t parse_opt(int key, char *arg,
2419
struct argp_state *state){
2422
case 128: /* --debug */
2425
case 'c': /* --connect */
2428
case 'i': /* --interface */
2429
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2432
argp_error(state, "%s", strerror(ret_errno));
2435
case 's': /* --seckey */
2438
case 'p': /* --pubkey */
2441
case 129: /* --dh-bits */
2443
tmpmax = strtoimax(arg, &tmp, 10);
2444
if(errno != 0 or tmp == arg or *tmp != '\0'
2445
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2446
argp_error(state, "Bad number of DH bits");
2448
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2450
case 134: /* --dh-params */
2451
dh_params_file = arg;
2453
case 130: /* --priority */
2456
case 131: /* --delay */
2458
delay = strtof(arg, &tmp);
2459
if(errno != 0 or tmp == arg or *tmp != '\0'){
2460
argp_error(state, "Bad delay");
2462
case 132: /* --retry */
2464
retry_interval = strtod(arg, &tmp);
2465
if(errno != 0 or tmp == arg or *tmp != '\0'
2466
or (retry_interval * 1000) > INT_MAX
2467
or retry_interval < 0){
2468
argp_error(state, "Bad retry interval");
2471
case 133: /* --network-hook-dir */
2475
* These reproduce what we would get without ARGP_NO_HELP
2477
case '?': /* --help */
2478
argp_state_help(state, state->out_stream,
2479
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2480
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2481
case -3: /* --usage */
2482
argp_state_help(state, state->out_stream,
2483
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2484
case 'V': /* --version */
2485
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2486
exit(argp_err_exit_status);
2489
return ARGP_ERR_UNKNOWN;
2494
struct argp argp = { .options = options, .parser = parse_opt,
2496
.doc = "Mandos client -- Get and decrypt"
2497
" passwords from a Mandos server" };
2498
ret_errno = argp_parse(&argp, argc, argv,
2499
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2506
perror_plus("argp_parse");
2507
exitcode = EX_OSERR;
2510
exitcode = EX_USAGE;
2516
/* Work around Debian bug #633582:
2517
<https://bugs.debian.org/633582> */
2519
/* Re-raise privileges */
2520
ret = raise_privileges();
2523
perror_plus("Failed to raise privileges");
2527
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2528
int seckey_fd = open(seckey, O_RDONLY);
2529
if(seckey_fd == -1){
2530
perror_plus("open");
2532
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2534
perror_plus("fstat");
2536
if(S_ISREG(st.st_mode)
2537
and st.st_uid == 0 and st.st_gid == 0){
2538
ret = fchown(seckey_fd, uid, gid);
2540
perror_plus("fchown");
2548
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2549
int pubkey_fd = open(pubkey, O_RDONLY);
2550
if(pubkey_fd == -1){
2551
perror_plus("open");
2553
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2555
perror_plus("fstat");
2557
if(S_ISREG(st.st_mode)
2558
and st.st_uid == 0 and st.st_gid == 0){
2559
ret = fchown(pubkey_fd, uid, gid);
2561
perror_plus("fchown");
2569
if(dh_params_file != NULL
2570
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2571
int dhparams_fd = open(dh_params_file, O_RDONLY);
2572
if(dhparams_fd == -1){
2573
perror_plus("open");
2575
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2577
perror_plus("fstat");
2579
if(S_ISREG(st.st_mode)
2580
and st.st_uid == 0 and st.st_gid == 0){
2581
ret = fchown(dhparams_fd, uid, gid);
2583
perror_plus("fchown");
2591
/* Lower privileges */
2592
ret = lower_privileges();
2595
perror_plus("Failed to lower privileges");
2600
/* Remove invalid interface names (except "none") */
2602
char *interface = NULL;
2603
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2605
if(strcmp(interface, "none") != 0
2606
and if_nametoindex(interface) == 0){
2607
if(interface[0] != '\0'){
2608
fprintf_plus(stderr, "Not using nonexisting interface"
2609
" \"%s\"\n", interface);
2611
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2617
/* Run network hooks */
2619
if(mc.interfaces != NULL){
2620
interfaces_hooks = malloc(mc.interfaces_size);
2621
if(interfaces_hooks == NULL){
2622
perror_plus("malloc");
2625
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2626
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2628
run_network_hooks("start", interfaces_hooks != NULL ?
2629
interfaces_hooks : "", delay);
2633
avahi_set_log_function(empty_log);
2636
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2637
from the signal handler */
2638
/* Initialize the pseudo-RNG for Avahi */
2639
srand((unsigned int) time(NULL));
2640
simple_poll = avahi_simple_poll_new();
2641
if(simple_poll == NULL){
2642
fprintf_plus(stderr,
2643
"Avahi: Failed to create simple poll object.\n");
2644
exitcode = EX_UNAVAILABLE;
2648
sigemptyset(&sigterm_action.sa_mask);
2649
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2651
perror_plus("sigaddset");
2652
exitcode = EX_OSERR;
2655
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2657
perror_plus("sigaddset");
2658
exitcode = EX_OSERR;
2661
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2663
perror_plus("sigaddset");
2664
exitcode = EX_OSERR;
2667
/* Need to check if the handler is SIG_IGN before handling:
2668
| [[info:libc:Initial Signal Actions]] |
2669
| [[info:libc:Basic Signal Handling]] |
2671
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2673
perror_plus("sigaction");
2676
if(old_sigterm_action.sa_handler != SIG_IGN){
2677
ret = sigaction(SIGINT, &sigterm_action, NULL);
2679
perror_plus("sigaction");
2680
exitcode = EX_OSERR;
2684
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2686
perror_plus("sigaction");
2689
if(old_sigterm_action.sa_handler != SIG_IGN){
2690
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2692
perror_plus("sigaction");
2693
exitcode = EX_OSERR;
2697
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2699
perror_plus("sigaction");
2702
if(old_sigterm_action.sa_handler != SIG_IGN){
2703
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2705
perror_plus("sigaction");
2706
exitcode = EX_OSERR;
2711
/* If no interfaces were specified, make a list */
2712
if(mc.interfaces == NULL){
2713
struct dirent **direntries = NULL;
2714
/* Look for any good interfaces */
2715
ret = scandir(sys_class_net, &direntries, good_interface,
2718
/* Add all found interfaces to interfaces list */
2719
for(int i = 0; i < ret; ++i){
2720
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2721
direntries[i]->d_name);
2724
perror_plus("argz_add");
2725
free(direntries[i]);
2729
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2730
direntries[i]->d_name);
2732
free(direntries[i]);
2739
fprintf_plus(stderr, "Could not find a network interface\n");
2740
exitcode = EXIT_FAILURE;
2745
/* Bring up interfaces which are down, and remove any "none"s */
2747
char *interface = NULL;
2748
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2750
/* If interface name is "none", stop bringing up interfaces.
2751
Also remove all instances of "none" from the list */
2752
if(strcmp(interface, "none") == 0){
2753
argz_delete(&mc.interfaces, &mc.interfaces_size,
2756
while((interface = argz_next(mc.interfaces,
2757
mc.interfaces_size, interface))){
2758
if(strcmp(interface, "none") == 0){
2759
argz_delete(&mc.interfaces, &mc.interfaces_size,
2766
bool interface_was_up = interface_is_up(interface);
2767
errno = bring_up_interface(interface, delay);
2768
if(not interface_was_up){
2770
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2771
" %s\n", interface, strerror(errno));
2773
errno = argz_add(&interfaces_to_take_down,
2774
&interfaces_to_take_down_size,
2777
perror_plus("argz_add");
2782
if(debug and (interfaces_to_take_down == NULL)){
2783
fprintf_plus(stderr, "No interfaces were brought up\n");
2787
/* If we only got one interface, explicitly use only that one */
2788
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2790
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2793
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2800
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2802
fprintf_plus(stderr, "init_gnutls_global failed\n");
2803
exitcode = EX_UNAVAILABLE;
2806
gnutls_initialized = true;
2813
/* Try /run/tmp before /tmp */
2814
tempdir = mkdtemp(run_tempdir);
2815
if(tempdir == NULL and errno == ENOENT){
2817
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2818
run_tempdir, old_tempdir);
2820
tempdir = mkdtemp(old_tempdir);
2822
if(tempdir == NULL){
2823
perror_plus("mkdtemp");
2831
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2832
fprintf_plus(stderr, "init_gpgme failed\n");
2833
exitcode = EX_UNAVAILABLE;
2836
gpgme_initialized = true;
2843
if(connect_to != NULL){
2844
/* Connect directly, do not use Zeroconf */
2845
/* (Mainly meant for debugging) */
2846
char *address = strrchr(connect_to, ':');
2848
if(address == NULL){
2849
fprintf_plus(stderr, "No colon in address\n");
2850
exitcode = EX_USAGE;
2860
tmpmax = strtoimax(address+1, &tmp, 10);
2861
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2862
or tmpmax != (in_port_t)tmpmax){
2863
fprintf_plus(stderr, "Bad port number\n");
2864
exitcode = EX_USAGE;
2872
port = (in_port_t)tmpmax;
2874
/* Colon in address indicates IPv6 */
2876
if(strchr(connect_to, ':') != NULL){
2878
/* Accept [] around IPv6 address - see RFC 5952 */
2879
if(connect_to[0] == '[' and address[-1] == ']')
2887
address = connect_to;
2893
while(not quit_now){
2894
ret = start_mandos_communication(address, port, if_index, af,
2896
if(quit_now or ret == 0){
2900
fprintf_plus(stderr, "Retrying in %d seconds\n",
2901
(int)retry_interval);
2903
sleep((unsigned int)retry_interval);
2907
exitcode = EXIT_SUCCESS;
507
2918
AvahiServerConfig config;
508
AvahiSServiceBrowser *sb = NULL;
512
avahi_set_log_function(empty_log);
514
/* Initialize the psuedo-RNG */
517
/* Allocate main loop object */
518
if (!(simple_poll = avahi_simple_poll_new())) {
519
fprintf(stderr, "Failed to create simple poll object.\n");
523
/* Do not publish any local records */
2919
/* Do not publish any local Zeroconf records */
524
2920
avahi_server_config_init(&config);
525
2921
config.publish_hinfo = 0;
526
2922
config.publish_addresses = 0;
527
2923
config.publish_workstation = 0;
528
2924
config.publish_domain = 0;
530
/* /\* Set a unicast DNS server for wide area DNS-SD *\/ */
531
/* avahi_address_parse("193.11.177.11", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); */
532
/* config.n_wide_area_servers = 1; */
533
/* config.enable_wide_area = 1; */
535
2926
/* Allocate a new server */
536
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
538
/* Free the configuration data */
2927
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2928
&config, NULL, NULL, &ret);
2930
/* Free the Avahi configuration data */
539
2931
avahi_server_config_free(&config);
541
/* Check wether creating the server object succeeded */
543
fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
547
/* Create the service browser */
548
if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
549
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
553
/* Run the main loop */
554
avahi_simple_poll_loop(simple_poll);
562
avahi_s_service_browser_free(sb);
565
avahi_server_free(server);
568
avahi_simple_poll_free(simple_poll);
2934
/* Check if creating the Avahi server object succeeded */
2935
if(mc.server == NULL){
2936
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2937
avahi_strerror(ret));
2938
exitcode = EX_UNAVAILABLE;
2946
/* Create the Avahi service browser */
2947
sb = avahi_s_service_browser_new(mc.server, if_index,
2948
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2949
NULL, 0, browse_callback,
2952
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2953
avahi_strerror(avahi_server_errno(mc.server)));
2954
exitcode = EX_UNAVAILABLE;
2962
/* Run the main loop */
2965
fprintf_plus(stderr, "Starting Avahi loop search\n");
2968
ret = avahi_loop_with_timeout(simple_poll,
2969
(int)(retry_interval * 1000), &mc);
2971
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2972
(ret == 0) ? "successfully" : "with error");
2978
if(signal_received){
2979
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
2980
argv[0], signal_received,
2981
strsignal(signal_received));
2983
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2987
/* Cleanup things */
2988
free(mc.interfaces);
2991
avahi_s_service_browser_free(sb);
2993
if(mc.server != NULL)
2994
avahi_server_free(mc.server);
2996
if(simple_poll != NULL)
2997
avahi_simple_poll_free(simple_poll);
2999
if(gnutls_initialized){
3000
gnutls_certificate_free_credentials(mc.cred);
3001
gnutls_dh_params_deinit(mc.dh_params);
3004
if(gpgme_initialized){
3005
gpgme_release(mc.ctx);
3008
/* Cleans up the circular linked list of Mandos servers the client
3010
if(mc.current_server != NULL){
3011
mc.current_server->prev->next = NULL;
3012
while(mc.current_server != NULL){
3013
server *next = mc.current_server->next;
3015
#pragma GCC diagnostic push
3016
#pragma GCC diagnostic ignored "-Wcast-qual"
3018
free((char *)(mc.current_server->ip));
3020
#pragma GCC diagnostic pop
3022
free(mc.current_server);
3023
mc.current_server = next;
3027
/* Re-raise privileges */
3029
ret = raise_privileges();
3032
perror_plus("Failed to raise privileges");
3035
/* Run network hooks */
3036
run_network_hooks("stop", interfaces_hooks != NULL ?
3037
interfaces_hooks : "", delay);
3039
/* Take down the network interfaces which were brought up */
3041
char *interface = NULL;
3042
while((interface = argz_next(interfaces_to_take_down,
3043
interfaces_to_take_down_size,
3045
ret = take_down_interface(interface);
3048
perror_plus("Failed to take down interface");
3051
if(debug and (interfaces_to_take_down == NULL)){
3052
fprintf_plus(stderr, "No interfaces needed to be taken"
3058
ret = lower_privileges_permanently();
3061
perror_plus("Failed to lower privileges permanently");
3065
free(interfaces_to_take_down);
3066
free(interfaces_hooks);
3068
void clean_dir_at(int base, const char * const dirname,
3070
struct dirent **direntries = NULL;
3072
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3078
perror_plus("open");
3081
int numentries = scandirat(dir_fd, ".", &direntries,
3082
notdotentries, alphasort);
3083
if(numentries >= 0){
3084
for(int i = 0; i < numentries; i++){
3086
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3087
dirname, direntries[i]->d_name);
3089
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3091
if(errno == EISDIR){
3092
dret = unlinkat(dir_fd, direntries[i]->d_name,
3095
if((dret == -1) and (errno == ENOTEMPTY)
3096
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3097
== 0) and (level == 0)){
3098
/* Recurse only in this special case */
3099
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3102
if((dret == -1) and (errno != ENOENT)){
3103
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3104
direntries[i]->d_name, strerror(errno));
3107
free(direntries[i]);
3110
/* need to clean even if 0 because man page doesn't specify */
3112
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3113
if(dret == -1 and errno != ENOENT){
3114
perror_plus("rmdir");
3117
perror_plus("scandirat");
3122
/* Removes the GPGME temp directory and all files inside */
3123
if(tempdir != NULL){
3124
clean_dir_at(-1, tempdir, 0);
3128
sigemptyset(&old_sigterm_action.sa_mask);
3129
old_sigterm_action.sa_handler = SIG_DFL;
3130
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3131
&old_sigterm_action,
3134
perror_plus("sigaction");
3137
ret = raise(signal_received);
3138
} while(ret != 0 and errno == EINTR);
3140
perror_plus("raise");
3143
TEMP_FAILURE_RETRY(pause());