117
46
#include <avahi-common/malloc.h>
118
47
#include <avahi-common/error.h>
121
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
124
init_gnutls_session(),
126
#include <gnutls/openpgp.h>
127
/* gnutls_certificate_set_openpgp_key_file(),
128
GNUTLS_OPENPGP_FMT_BASE64 */
131
#include <gpgme.h> /* All GPGME types, constants and
134
GPGME_PROTOCOL_OpenPGP,
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"
137
72
#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;
169
77
gnutls_certificate_credentials_t cred;
170
unsigned int dh_bits;
171
78
gnutls_dh_params_t dh_params;
172
const char *priority;
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;
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;
284
89
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));
314
gpgme_import_result_t import_result
315
= gpgme_op_import_result(mc->ctx);
316
if((import_result->imported < 1
317
or import_result->not_imported > 0)
318
and import_result->unchanged == 0){
319
fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
321
"The total number of considered keys: %d\n",
322
import_result->considered);
324
"The number of keys without user ID: %d\n",
325
import_result->no_user_id);
327
"The total number of imported keys: %d\n",
328
import_result->imported);
329
fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
330
import_result->imported_rsa);
331
fprintf_plus(stderr, "The number of unchanged keys: %d\n",
332
import_result->unchanged);
333
fprintf_plus(stderr, "The number of new user IDs: %d\n",
334
import_result->new_user_ids);
335
fprintf_plus(stderr, "The number of new sub keys: %d\n",
336
import_result->new_sub_keys);
337
fprintf_plus(stderr, "The number of new signatures: %d\n",
338
import_result->new_signatures);
339
fprintf_plus(stderr, "The number of new revocations: %d\n",
340
import_result->new_revocations);
342
"The total number of secret keys read: %d\n",
343
import_result->secret_read);
345
"The number of imported secret keys: %d\n",
346
import_result->secret_imported);
348
"The number of unchanged secret keys: %d\n",
349
import_result->secret_unchanged);
350
fprintf_plus(stderr, "The number of keys not imported: %d\n",
351
import_result->not_imported);
352
for(gpgme_import_status_t import_status
353
= import_result->imports;
354
import_status != NULL;
355
import_status = import_status->next){
356
fprintf_plus(stderr, "Import status for key: %s\n",
358
if(import_status->result != GPG_ERR_NO_ERROR){
359
fprintf_plus(stderr, "Import result: %s: %s\n",
360
gpgme_strsource(import_status->result),
361
gpgme_strerror(import_status->result));
363
fprintf_plus(stderr, "Key status:\n");
365
import_status->status & GPGME_IMPORT_NEW
366
? "The key was new.\n"
367
: "The key was not new.\n");
369
import_status->status & GPGME_IMPORT_UID
370
? "The key contained new user IDs.\n"
371
: "The key did not contain new user IDs.\n");
373
import_status->status & GPGME_IMPORT_SIG
374
? "The key contained new signatures.\n"
375
: "The key did not contain new signatures.\n");
377
import_status->status & GPGME_IMPORT_SUBKEY
378
? "The key contained new sub keys.\n"
379
: "The key did not contain new sub keys.\n");
381
import_status->status & GPGME_IMPORT_SECRET
382
? "The key contained a secret key.\n"
383
: "The key did not contain a secret key.\n");
391
perror_plus("close");
393
gpgme_data_release(pgp_data);
398
fprintf_plus(stderr, "Initializing GPGME\n");
402
92
gpgme_check_version(NULL);
403
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
404
if(rc != GPG_ERR_NO_ERROR){
405
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
406
gpgme_strsource(rc), gpgme_strerror(rc));
93
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
410
/* Set GPGME home directory for the OpenPGP engine only */
411
rc = gpgme_get_engine_info(&engine_info);
412
if(rc != GPG_ERR_NO_ERROR){
413
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
414
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));
417
102
while(engine_info != NULL){
418
103
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
419
104
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
420
engine_info->file_name, tempdir);
105
engine_info->file_name, homedir);
423
108
engine_info = engine_info->next;
425
110
if(engine_info == NULL){
426
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
431
/* Create new GPGME "context" */
432
rc = gpgme_new(&(mc->ctx));
433
if(rc != GPG_ERR_NO_ERROR){
434
fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
435
gpgme_strsource(rc), gpgme_strerror(rc));
439
if(not import_key(pubkey) or not import_key(seckey)){
447
* Decrypt OpenPGP data.
448
* Returns -1 on error
450
__attribute__((nonnull, warn_unused_result))
451
static ssize_t pgp_packet_decrypt(const char *cryptotext,
455
gpgme_data_t dh_crypto, dh_plain;
458
size_t plaintext_capacity = 0;
459
ssize_t plaintext_length = 0;
462
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
465
/* Create new GPGME data buffer from memory cryptotext */
466
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
468
if(rc != GPG_ERR_NO_ERROR){
469
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
470
gpgme_strsource(rc), gpgme_strerror(rc));
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));
474
123
/* Create new empty GPGME data buffer for the plaintext */
475
124
rc = gpgme_data_new(&dh_plain);
476
if(rc != GPG_ERR_NO_ERROR){
477
fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
478
gpgme_strsource(rc), gpgme_strerror(rc));
479
gpgme_data_release(dh_crypto);
483
/* Decrypt data from the cryptotext data buffer to the plaintext
485
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
486
if(rc != GPG_ERR_NO_ERROR){
487
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
488
gpgme_strsource(rc), gpgme_strerror(rc));
489
plaintext_length = -1;
491
gpgme_decrypt_result_t result;
492
result = gpgme_op_decrypt_result(mc->ctx);
494
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
496
if(result->unsupported_algorithm != NULL) {
497
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
498
result->unsupported_algorithm);
500
fprintf_plus(stderr, "Wrong key usage: %s\n",
501
result->wrong_key_usage ? "Yes" : "No");
502
if(result->file_name != NULL){
503
fprintf_plus(stderr, "File name: %s\n", result->file_name);
505
gpgme_recipient_t recipient;
506
recipient = result->recipients;
507
while(recipient != NULL){
508
fprintf_plus(stderr, "Public key algorithm: %s\n",
509
gpgme_pubkey_algo_name
510
(recipient->pubkey_algo));
511
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
512
fprintf_plus(stderr, "Secret key available: %s\n",
513
recipient->status == GPG_ERR_NO_SECKEY
515
recipient = recipient->next;
523
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
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);
526
170
/* Seek back to the beginning of the GPGME plaintext data buffer */
527
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
528
perror_plus("gpgme_data_seek");
529
plaintext_length = -1;
171
gpgme_data_seek(dh_plain, 0, SEEK_SET);
535
plaintext_capacity = incbuffer(plaintext,
536
(size_t)plaintext_length,
538
if(plaintext_capacity == 0){
539
perror_plus("incbuffer");
540
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;
544
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
184
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
546
185
/* Print the data, if any */
187
/* If password is empty, then a incorrect error will be printed */
552
perror_plus("gpgme_data_read");
553
plaintext_length = -1;
556
plaintext_length += ret;
560
fprintf_plus(stderr, "Decrypted password is: ");
561
for(ssize_t i = 0; i < plaintext_length; i++){
562
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
564
fprintf(stderr, "\n");
569
/* Delete the GPGME cryptotext data buffer */
570
gpgme_data_release(dh_crypto);
572
/* Delete the GPGME plaintext data buffer */
191
perror("gpgme_data_read");
194
new_packet_length += ret;
197
/* Delete the GPGME plaintext data buffer */
573
198
gpgme_data_release(dh_plain);
574
return plaintext_length;
577
__attribute__((warn_unused_result, const))
578
static const char *safe_string(const char *str){
584
__attribute__((warn_unused_result))
585
static const char *safer_gnutls_strerror(int value){
586
const char *ret = gnutls_strerror(value);
587
return safe_string(ret);
590
/* GnuTLS log function callback */
591
__attribute__((nonnull))
592
static void debuggnutls(__attribute__((unused)) int level,
594
fprintf_plus(stderr, "GnuTLS: %s", string);
597
__attribute__((nonnull(1, 2, 4), warn_unused_result))
598
static int init_gnutls_global(const char *pubkeyfilename,
599
const char *seckeyfilename,
600
const char *dhparamsfilename,
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){
606
fprintf_plus(stderr, "Initializing GnuTLS\n");
610
/* "Use a log level over 10 to enable all debugging options."
613
gnutls_global_set_log_level(11);
614
gnutls_global_set_log_function(debuggnutls);
617
/* OpenPGP credentials */
618
ret = gnutls_certificate_allocate_credentials(&mc->cred);
619
if(ret != GNUTLS_E_SUCCESS){
620
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
621
safer_gnutls_strerror(ret));
626
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
627
" secret key %s as GnuTLS credentials\n",
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));
632
235
ret = gnutls_certificate_set_openpgp_key_file
633
(mc->cred, pubkeyfilename, seckeyfilename,
634
GNUTLS_OPENPGP_FMT_BASE64);
635
if(ret != GNUTLS_E_SUCCESS){
637
"Error[%d] while reading the OpenPGP key pair ('%s',"
638
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
639
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
640
safer_gnutls_strerror(ret));
644
/* GnuTLS server initialization */
645
ret = gnutls_dh_params_init(&mc->dh_params);
646
if(ret != GNUTLS_E_SUCCESS){
647
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
648
" initialization: %s\n",
649
safer_gnutls_strerror(ret));
652
/* If a Diffie-Hellman parameters file was given, try to use it */
653
if(dhparamsfilename != NULL){
654
gnutls_datum_t params = { .data = NULL, .size = 0 };
656
int dhpfile = open(dhparamsfilename, O_RDONLY);
659
dhparamsfilename = NULL;
662
size_t params_capacity = 0;
664
params_capacity = incbuffer((char **)¶ms.data,
666
(size_t)params_capacity);
667
if(params_capacity == 0){
668
perror_plus("incbuffer");
671
dhparamsfilename = NULL;
674
ssize_t bytes_read = read(dhpfile,
675
params.data + params.size,
681
/* check bytes_read for failure */
686
dhparamsfilename = NULL;
689
params.size += (unsigned int)bytes_read;
691
ret = close(dhpfile);
693
perror_plus("close");
695
if(params.data == NULL){
696
dhparamsfilename = NULL;
698
if(dhparamsfilename == NULL){
701
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
702
GNUTLS_X509_FMT_PEM);
703
if(ret != GNUTLS_E_SUCCESS){
704
fprintf_plus(stderr, "Failed to parse DH parameters in file"
705
" \"%s\": %s\n", dhparamsfilename,
706
safer_gnutls_strerror(ret));
707
dhparamsfilename = NULL;
712
if(dhparamsfilename == NULL){
713
if(mc->dh_bits == 0){
714
/* Find out the optimal number of DH bits */
715
/* Try to read the private key file */
716
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
718
int secfile = open(seckeyfilename, O_RDONLY);
723
size_t buffer_capacity = 0;
725
buffer_capacity = incbuffer((char **)&buffer.data,
727
(size_t)buffer_capacity);
728
if(buffer_capacity == 0){
729
perror_plus("incbuffer");
734
ssize_t bytes_read = read(secfile,
735
buffer.data + buffer.size,
741
/* check bytes_read for failure */
748
buffer.size += (unsigned int)bytes_read;
752
/* If successful, use buffer to parse private key */
753
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
754
if(buffer.data != NULL){
756
gnutls_openpgp_privkey_t privkey = NULL;
757
ret = gnutls_openpgp_privkey_init(&privkey);
758
if(ret != GNUTLS_E_SUCCESS){
759
fprintf_plus(stderr, "Error initializing OpenPGP key"
761
safer_gnutls_strerror(ret));
765
ret = gnutls_openpgp_privkey_import
766
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
767
if(ret != GNUTLS_E_SUCCESS){
768
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
769
safer_gnutls_strerror(ret));
775
/* Use private key to suggest an appropriate
777
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
778
gnutls_openpgp_privkey_deinit(privkey);
780
fprintf_plus(stderr, "This OpenPGP key implies using"
781
" a GnuTLS security parameter \"%s\".\n",
782
safe_string(gnutls_sec_param_get_name
788
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
789
/* Err on the side of caution */
790
sec_param = GNUTLS_SEC_PARAM_ULTRA;
792
fprintf_plus(stderr, "Falling back to security parameter"
794
safe_string(gnutls_sec_param_get_name
799
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
803
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
804
" implies %u DH bits; using that.\n",
805
safe_string(gnutls_sec_param_get_name
810
fprintf_plus(stderr, "Failed to get implied number of DH"
811
" bits for security parameter \"%s\"): %s\n",
812
safe_string(gnutls_sec_param_get_name
814
safer_gnutls_strerror(ret));
818
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
821
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
822
if(ret != GNUTLS_E_SUCCESS){
823
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
824
" bits): %s\n", mc->dh_bits,
825
safer_gnutls_strerror(ret));
829
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
835
gnutls_certificate_free_credentials(mc->cred);
836
gnutls_dh_params_deinit(mc->dh_params);
840
__attribute__((nonnull, warn_unused_result))
841
static int init_gnutls_session(gnutls_session_t *session,
844
/* GnuTLS session creation */
846
ret = gnutls_init(session, GNUTLS_SERVER);
850
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
851
if(ret != GNUTLS_E_SUCCESS){
853
"Error in GnuTLS session initialization: %s\n",
854
safer_gnutls_strerror(ret));
860
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
862
gnutls_deinit(*session);
865
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
866
if(ret != GNUTLS_E_SUCCESS){
867
fprintf_plus(stderr, "Syntax error at: %s\n", err);
868
fprintf_plus(stderr, "GnuTLS error: %s\n",
869
safer_gnutls_strerror(ret));
870
gnutls_deinit(*session);
876
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
879
gnutls_deinit(*session);
882
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
883
if(ret != GNUTLS_E_SUCCESS){
884
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
885
safer_gnutls_strerror(ret));
886
gnutls_deinit(*session);
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));
890
286
/* ignore client certificate if any. */
891
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
287
gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
289
gnutls_dh_set_prime_bits (es->session, DH_BITS);
896
/* Avahi log function callback */
897
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
898
__attribute__((unused)) const char *txt){}
900
/* Set effective uid to 0, return errno */
901
__attribute__((warn_unused_result))
902
int raise_privileges(void){
903
int old_errno = errno;
905
if(seteuid(0) == -1){
912
/* Set effective and real user ID to 0. Return errno. */
913
__attribute__((warn_unused_result))
914
int raise_privileges_permanently(void){
915
int old_errno = errno;
916
int ret = raise_privileges();
928
/* Set effective user ID to unprivileged saved user ID */
929
__attribute__((warn_unused_result))
930
int lower_privileges(void){
931
int old_errno = errno;
933
if(seteuid(uid) == -1){
940
/* Lower privileges permanently */
941
__attribute__((warn_unused_result))
942
int lower_privileges_permanently(void){
943
int old_errno = errno;
945
if(setuid(uid) == -1){
952
/* Helper function to add_local_route() and delete_local_route() */
953
__attribute__((nonnull, warn_unused_result))
954
static bool add_delete_local_route(const bool add,
956
AvahiIfIndex if_index){
958
char helper[] = "mandos-client-iprouteadddel";
959
char add_arg[] = "add";
960
char delete_arg[] = "delete";
961
char debug_flag[] = "--debug";
962
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
963
if(pluginhelperdir == NULL){
965
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
966
" variable not set; cannot run helper\n");
971
char interface[IF_NAMESIZE];
972
if(if_indextoname((unsigned int)if_index, interface) == NULL){
973
perror_plus("if_indextoname");
977
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
979
perror_plus("open(\"/dev/null\", O_RDONLY)");
985
/* Raise privileges */
986
errno = raise_privileges_permanently();
988
perror_plus("Failed to raise privileges");
989
/* _exit(EX_NOPERM); */
995
perror_plus("setgid");
998
/* Reset supplementary groups */
1000
ret = setgroups(0, NULL);
1002
perror_plus("setgroups");
1006
ret = dup2(devnull, STDIN_FILENO);
1008
perror_plus("dup2(devnull, STDIN_FILENO)");
1011
ret = close(devnull);
1013
perror_plus("close");
1016
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1018
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1021
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
1026
if(helperdir_fd == -1){
1027
perror_plus("open");
1028
_exit(EX_UNAVAILABLE);
1030
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
1032
if(helper_fd == -1){
1033
perror_plus("openat");
1034
close(helperdir_fd);
1035
_exit(EX_UNAVAILABLE);
1037
close(helperdir_fd);
1039
#pragma GCC diagnostic push
1040
#pragma GCC diagnostic ignored "-Wcast-qual"
1042
if(fexecve(helper_fd, (char *const [])
1043
{ helper, add ? add_arg : delete_arg, (char *)address,
1044
interface, debug ? debug_flag : NULL, NULL },
1047
#pragma GCC diagnostic pop
1049
perror_plus("fexecve");
1050
_exit(EXIT_FAILURE);
1054
perror_plus("fork");
1061
pret = waitpid(pid, &status, 0);
1062
if(pret == -1 and errno == EINTR and quit_now){
1063
int errno_raising = 0;
1064
if((errno = raise_privileges()) != 0){
1065
errno_raising = errno;
1066
perror_plus("Failed to raise privileges in order to"
1067
" kill helper program");
1069
if(kill(pid, SIGTERM) == -1){
1070
perror_plus("kill");
1072
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
1073
perror_plus("Failed to lower privileges after killing"
1078
} while(pret == -1 and errno == EINTR);
1080
perror_plus("waitpid");
1083
if(WIFEXITED(status)){
1084
if(WEXITSTATUS(status) != 0){
1085
fprintf_plus(stderr, "Error: iprouteadddel exited"
1086
" with status %d\n", WEXITSTATUS(status));
1091
if(WIFSIGNALED(status)){
1092
fprintf_plus(stderr, "Error: iprouteadddel died by"
1093
" signal %d\n", WTERMSIG(status));
1096
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1100
__attribute__((nonnull, warn_unused_result))
1101
static bool add_local_route(const char *address,
1102
AvahiIfIndex if_index){
1104
fprintf_plus(stderr, "Adding route to %s\n", address);
1106
return add_delete_local_route(true, address, if_index);
1109
__attribute__((nonnull, warn_unused_result))
1110
static bool delete_local_route(const char *address,
1111
AvahiIfIndex if_index){
1113
fprintf_plus(stderr, "Removing route to %s\n", address);
1115
return add_delete_local_route(false, address, if_index);
1118
/* Called when a Mandos server is found */
1119
__attribute__((nonnull, warn_unused_result))
1120
static int start_mandos_communication(const char *ip, in_port_t port,
1121
AvahiIfIndex if_index,
1122
int af, mandos_context *mc){
1123
int ret, tcp_sd = -1;
1125
struct sockaddr_storage to;
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;
1126
301
char *buffer = NULL;
1127
char *decrypted_buffer = NULL;
302
char *decrypted_buffer;
1128
303
size_t buffer_length = 0;
1129
304
size_t buffer_capacity = 0;
1132
gnutls_session_t session;
1133
int pf; /* Protocol family */
1134
bool route_added = false;
1151
fprintf_plus(stderr, "Bad address family: %d\n", af);
1156
/* If the interface is specified and we have a list of interfaces */
1157
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1158
/* Check if the interface is one of the interfaces we are using */
1161
char *interface = NULL;
1162
while((interface = argz_next(mc->interfaces,
1163
mc->interfaces_size,
1165
if(if_nametoindex(interface) == (unsigned int)if_index){
1172
/* This interface does not match any in the list, so we don't
1173
connect to the server */
1175
char interface[IF_NAMESIZE];
1176
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1177
perror_plus("if_indextoname");
1179
fprintf_plus(stderr, "Skipping server on non-used interface"
1181
if_indextoname((unsigned int)if_index,
1189
ret = init_gnutls_session(&session, mc);
1195
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1196
PRIuMAX "\n", ip, (uintmax_t)port);
1199
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1202
perror_plus("socket");
1213
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1214
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1215
ret = inet_pton(af, ip, &to6->sin6_addr);
1217
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1218
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1219
ret = inet_pton(af, ip, &to4->sin_addr);
1223
perror_plus("inet_pton");
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);
1229
fprintf_plus(stderr, "Bad address: %s\n", ip);
1234
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1235
if(IN6_IS_ADDR_LINKLOCAL
1236
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1237
if(if_index == AVAHI_IF_UNSPEC){
1238
fprintf_plus(stderr, "An IPv6 link-local address is"
1239
" incomplete without a network interface\n");
1243
/* Set the network interface number as scope */
1244
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1247
((struct sockaddr_in *)&to)->sin_port = htons(port);
1256
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1257
char interface[IF_NAMESIZE];
1258
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1259
perror_plus("if_indextoname");
1261
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1262
"\n", ip, interface, (uintmax_t)port);
1265
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1266
ip, (uintmax_t)port);
1268
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1269
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1271
ret = getnameinfo((struct sockaddr *)&to,
1272
sizeof(struct sockaddr_in6),
1273
addrstr, sizeof(addrstr), NULL, 0,
1276
ret = getnameinfo((struct sockaddr *)&to,
1277
sizeof(struct sockaddr_in),
1278
addrstr, sizeof(addrstr), NULL, 0,
1281
if(ret == EAI_SYSTEM){
1282
perror_plus("getnameinfo");
1283
} else if(ret != 0) {
1284
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1285
} else if(strcmp(addrstr, ip) != 0){
1286
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1297
ret = connect(tcp_sd, (struct sockaddr *)&to,
1298
sizeof(struct sockaddr_in6));
1300
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1301
sizeof(struct sockaddr_in));
1304
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1305
and if_index != AVAHI_IF_UNSPEC
1306
and connect_to == NULL
1307
and not route_added and
1308
((af == AF_INET6 and not
1309
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1311
or (af == AF_INET and
1312
/* Not a a IPv4LL address */
1313
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1314
& 0xFFFF0000L) != 0xA9FE0000L))){
1315
/* Work around Avahi bug - Avahi does not announce link-local
1316
addresses if it has a global address, so local hosts with
1317
*only* a link-local address (e.g. Mandos clients) cannot
1318
connect to a Mandos server announced by Avahi on a server
1319
host with a global address. Work around this by retrying
1320
with an explicit route added with the server's address.
1322
Avahi bug reference:
1323
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1324
https://bugs.debian.org/587961
1327
fprintf_plus(stderr, "Mandos server unreachable, trying"
1331
route_added = add_local_route(ip, if_index);
1337
if(errno != ECONNREFUSED or debug){
1339
perror_plus("connect");
1352
const char *out = mandos_protocol_version;
1355
size_t out_size = strlen(out);
1356
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1357
out_size - written));
1360
perror_plus("write");
1364
written += (size_t)ret;
1365
if(written < out_size){
1368
if(out == mandos_protocol_version){
1383
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1391
/* This casting via intptr_t is to eliminate warning about casting
1392
an int to a pointer type. This is exactly how the GnuTLS Guile
1393
function "set-session-transport-fd!" does it. */
1394
gnutls_transport_set_ptr(session,
1395
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1403
ret = gnutls_handshake(session);
1408
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1410
if(ret != GNUTLS_E_SUCCESS){
1412
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1419
/* Read OpenPGP packet that contains the wanted password */
1422
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1433
buffer_capacity = incbuffer(&buffer, buffer_length,
1435
if(buffer_capacity == 0){
1437
perror_plus("incbuffer");
1447
sret = gnutls_record_recv(session, buffer+buffer_length,
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);
1454
377
case GNUTLS_E_INTERRUPTED:
1455
378
case GNUTLS_E_AGAIN:
1457
380
case GNUTLS_E_REHANDSHAKE:
1459
ret = gnutls_handshake(session);
1465
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1467
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
381
ret = gnutls_handshake (es.session);
383
fprintf(stderr, "\n*** Handshake failed ***\n");
1475
fprintf_plus(stderr, "Unknown error while reading data from"
1476
" encrypted session with Mandos server\n");
1477
gnutls_bye(session, GNUTLS_SHUT_RDWR);
390
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
392
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1482
buffer_length += (size_t) sret;
1487
fprintf_plus(stderr, "Closing TLS session\n");
1496
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1501
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1503
if(buffer_length > 0){
1504
ssize_t decrypted_buffer_size;
1505
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1506
&decrypted_buffer, mc);
1507
if(decrypted_buffer_size >= 0){
1511
while(written < (size_t) decrypted_buffer_size){
1517
ret = (int)fwrite(decrypted_buffer + written, 1,
1518
(size_t)decrypted_buffer_size - written,
1520
if(ret == 0 and ferror(stdout)){
1523
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1529
written += (size_t)ret;
1531
ret = fflush(stdout);
1535
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1545
/* Shutdown procedure */
1550
if(not delete_local_route(ip, if_index)){
1551
fprintf_plus(stderr, "Failed to delete local route to %s on"
1552
" interface %d", ip, if_index);
1556
free(decrypted_buffer);
1559
ret = close(tcp_sd);
1565
perror_plus("close");
1567
gnutls_deinit(session);
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){
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 ();
1577
static void resolve_callback(AvahiSServiceResolver *r,
1578
AvahiIfIndex interface,
1579
AvahiProtocol proto,
1580
AvahiResolverEvent event,
1584
const char *host_name,
1585
const AvahiAddress *address,
1587
AVAHI_GCC_UNUSED AvahiStringList *txt,
1588
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1595
/* Called whenever a service has been resolved successfully or
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);
1599
459
avahi_s_service_resolver_free(r);
1605
case AVAHI_RESOLVER_FAILURE:
1606
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1607
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1609
avahi_strerror(avahi_server_errno
1610
(((mandos_context*)mc)->server)));
1613
case AVAHI_RESOLVER_FOUND:
1615
char ip[AVAHI_ADDRESS_STR_MAX];
1616
avahi_address_snprint(ip, sizeof(ip), address);
1618
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1619
PRIdMAX ") on port %" PRIu16 "\n", name,
1620
host_name, ip, (intmax_t)interface, port);
1622
int ret = start_mandos_communication(ip, (in_port_t)port,
1624
avahi_proto_to_af(proto),
1627
avahi_simple_poll_quit(simple_poll);
1629
if(not add_server(ip, (in_port_t)port, interface,
1630
avahi_proto_to_af(proto),
1631
&((mandos_context*)mc)->current_server)){
1632
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1638
avahi_s_service_resolver_free(r);
1641
static void browse_callback(AvahiSServiceBrowser *b,
1642
AvahiIfIndex interface,
1643
AvahiProtocol protocol,
1644
AvahiBrowserEvent event,
1648
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1655
/* Called whenever a new services becomes available on the LAN or
1656
is removed from the LAN */
1664
case AVAHI_BROWSER_FAILURE:
1666
fprintf_plus(stderr, "(Avahi browser) %s\n",
1667
avahi_strerror(avahi_server_errno
1668
(((mandos_context*)mc)->server)));
1669
avahi_simple_poll_quit(simple_poll);
1672
case AVAHI_BROWSER_NEW:
1673
/* We ignore the returned Avahi resolver object. In the callback
1674
function we free it. If the Avahi server is terminated before
1675
the callback function is called the Avahi server will free the
1678
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1679
interface, protocol, name, type,
1680
domain, protocol, 0,
1681
resolve_callback, mc) == NULL)
1682
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1684
avahi_strerror(avahi_server_errno
1685
(((mandos_context*)mc)->server)));
1688
case AVAHI_BROWSER_REMOVE:
1691
case AVAHI_BROWSER_ALL_FOR_NOW:
1692
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1694
fprintf_plus(stderr, "No Mandos server found, still"
1701
/* Signal handler that stops main loop after SIGTERM */
1702
static void handle_sigterm(int sig){
1707
signal_received = sig;
1708
int old_errno = errno;
1709
/* set main loop to exit */
1710
if(simple_poll != NULL){
1711
avahi_simple_poll_quit(simple_poll);
1716
__attribute__((nonnull, warn_unused_result))
1717
bool get_flags(const char *ifname, struct ifreq *ifr){
1721
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1724
perror_plus("socket");
1728
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1729
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1730
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1734
perror_plus("ioctl SIOCGIFFLAGS");
1737
if((close(s) == -1) and debug){
1739
perror_plus("close");
1744
if((close(s) == -1) and debug){
1746
perror_plus("close");
1752
__attribute__((nonnull, warn_unused_result))
1753
bool good_flags(const char *ifname, const struct ifreq *ifr){
1755
/* Reject the loopback device */
1756
if(ifr->ifr_flags & IFF_LOOPBACK){
1758
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1763
/* Accept point-to-point devices only if connect_to is specified */
1764
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1766
fprintf_plus(stderr, "Accepting point-to-point interface"
1767
" \"%s\"\n", ifname);
1771
/* Otherwise, reject non-broadcast-capable devices */
1772
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1774
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1775
" \"%s\"\n", ifname);
1779
/* Reject non-ARP interfaces (including dummy interfaces) */
1780
if(ifr->ifr_flags & IFF_NOARP){
1782
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1788
/* Accept this device */
1790
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1796
* This function determines if a directory entry in /sys/class/net
1797
* corresponds to an acceptable network device.
1798
* (This function is passed to scandir(3) as a filter function.)
1800
__attribute__((nonnull, warn_unused_result))
1801
int good_interface(const struct dirent *if_entry){
1802
if(if_entry->d_name[0] == '.'){
1807
if(not get_flags(if_entry->d_name, &ifr)){
1809
fprintf_plus(stderr, "Failed to get flags for interface "
1810
"\"%s\"\n", if_entry->d_name);
1815
if(not good_flags(if_entry->d_name, &ifr)){
1822
* This function determines if a network interface is up.
1824
__attribute__((nonnull, warn_unused_result))
1825
bool interface_is_up(const char *interface){
1827
if(not get_flags(interface, &ifr)){
1829
fprintf_plus(stderr, "Failed to get flags for interface "
1830
"\"%s\"\n", interface);
1835
return (bool)(ifr.ifr_flags & IFF_UP);
1839
* This function determines if a network interface is running
1841
__attribute__((nonnull, warn_unused_result))
1842
bool interface_is_running(const char *interface){
1844
if(not get_flags(interface, &ifr)){
1846
fprintf_plus(stderr, "Failed to get flags for interface "
1847
"\"%s\"\n", interface);
1852
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1855
__attribute__((nonnull, pure, warn_unused_result))
1856
int notdotentries(const struct dirent *direntry){
1857
/* Skip "." and ".." */
1858
if(direntry->d_name[0] == '.'
1859
and (direntry->d_name[1] == '\0'
1860
or (direntry->d_name[1] == '.'
1861
and direntry->d_name[2] == '\0'))){
1867
/* Is this directory entry a runnable program? */
1868
__attribute__((nonnull, warn_unused_result))
1869
int runnable_hook(const struct dirent *direntry){
1874
if((direntry->d_name)[0] == '\0'){
1879
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1880
"abcdefghijklmnopqrstuvwxyz"
1883
if((direntry->d_name)[sret] != '\0'){
1884
/* Contains non-allowed characters */
1886
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1892
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1895
perror_plus("Could not stat hook");
1899
if(not (S_ISREG(st.st_mode))){
1900
/* Not a regular file */
1902
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1907
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1908
/* Not executable */
1910
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1916
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1922
__attribute__((nonnull, warn_unused_result))
1923
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1924
mandos_context *mc){
1926
struct timespec now;
1927
struct timespec waited_time;
1928
intmax_t block_time;
1931
if(mc->current_server == NULL){
1933
fprintf_plus(stderr, "Wait until first server is found."
1936
ret = avahi_simple_poll_iterate(s, -1);
1939
fprintf_plus(stderr, "Check current_server if we should run"
1942
/* the current time */
1943
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1945
perror_plus("clock_gettime");
1948
/* Calculating in ms how long time between now and server
1949
who we visted longest time ago. Now - last seen. */
1950
waited_time.tv_sec = (now.tv_sec
1951
- mc->current_server->last_seen.tv_sec);
1952
waited_time.tv_nsec = (now.tv_nsec
1953
- mc->current_server->last_seen.tv_nsec);
1954
/* total time is 10s/10,000ms.
1955
Converting to s from ms by dividing by 1,000,
1956
and ns to ms by dividing by 1,000,000. */
1957
block_time = ((retry_interval
1958
- ((intmax_t)waited_time.tv_sec * 1000))
1959
- ((intmax_t)waited_time.tv_nsec / 1000000));
1962
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1966
if(block_time <= 0){
1967
ret = start_mandos_communication(mc->current_server->ip,
1968
mc->current_server->port,
1969
mc->current_server->if_index,
1970
mc->current_server->af, mc);
1972
avahi_simple_poll_quit(s);
1975
ret = clock_gettime(CLOCK_MONOTONIC,
1976
&mc->current_server->last_seen);
1978
perror_plus("clock_gettime");
1981
mc->current_server = mc->current_server->next;
1982
block_time = 0; /* Call avahi to find new Mandos
1983
servers, but don't block */
1986
ret = avahi_simple_poll_iterate(s, (int)block_time);
1989
if(ret > 0 or errno != EINTR){
1990
return (ret != 1) ? ret : 0;
1996
__attribute__((nonnull))
1997
void run_network_hooks(const char *mode, const char *interface,
1999
struct dirent **direntries = NULL;
2000
if(hookdir_fd == -1){
2001
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
2003
if(hookdir_fd == -1){
2004
if(errno == ENOENT){
2006
fprintf_plus(stderr, "Network hook directory \"%s\" not"
2007
" found\n", hookdir);
2010
perror_plus("open");
2015
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2017
perror_plus("open(\"/dev/null\", O_RDONLY)");
2020
int numhooks = scandirat(hookdir_fd, ".", &direntries,
2021
runnable_hook, alphasort);
2023
perror_plus("scandir");
2027
struct dirent *direntry;
2029
for(int i = 0; i < numhooks; i++){
2030
direntry = direntries[i];
2032
fprintf_plus(stderr, "Running network hook \"%s\"\n",
2035
pid_t hook_pid = fork();
2038
/* Raise privileges */
2039
errno = raise_privileges_permanently();
2041
perror_plus("Failed to raise privileges");
2048
perror_plus("setgid");
2051
/* Reset supplementary groups */
2053
ret = setgroups(0, NULL);
2055
perror_plus("setgroups");
2058
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
2060
perror_plus("setenv");
2063
ret = setenv("DEVICE", interface, 1);
2065
perror_plus("setenv");
2068
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
2070
perror_plus("setenv");
2073
ret = setenv("MODE", mode, 1);
2075
perror_plus("setenv");
2079
ret = asprintf(&delaystring, "%f", (double)delay);
2081
perror_plus("asprintf");
2084
ret = setenv("DELAY", delaystring, 1);
2087
perror_plus("setenv");
2091
if(connect_to != NULL){
2092
ret = setenv("CONNECT", connect_to, 1);
2094
perror_plus("setenv");
2098
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2102
perror_plus("openat");
2103
_exit(EXIT_FAILURE);
2105
if(close(hookdir_fd) == -1){
2106
perror_plus("close");
2107
_exit(EXIT_FAILURE);
2109
ret = dup2(devnull, STDIN_FILENO);
2111
perror_plus("dup2(devnull, STDIN_FILENO)");
2114
ret = close(devnull);
2116
perror_plus("close");
2119
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2121
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2124
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2126
perror_plus("fexecve");
2127
_exit(EXIT_FAILURE);
2131
perror_plus("fork");
2136
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2137
perror_plus("waitpid");
2141
if(WIFEXITED(status)){
2142
if(WEXITSTATUS(status) != 0){
2143
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2144
" with status %d\n", direntry->d_name,
2145
WEXITSTATUS(status));
2149
} else if(WIFSIGNALED(status)){
2150
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2151
" signal %d\n", direntry->d_name,
2156
fprintf_plus(stderr, "Warning: network hook \"%s\""
2157
" crashed\n", direntry->d_name);
2163
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2169
if(close(hookdir_fd) == -1){
2170
perror_plus("close");
2177
__attribute__((nonnull, warn_unused_result))
2178
int bring_up_interface(const char *const interface,
2180
int old_errno = errno;
2182
struct ifreq network;
2183
unsigned int if_index = if_nametoindex(interface);
2185
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2195
if(not interface_is_up(interface)){
2197
int ioctl_errno = 0;
2198
if(not get_flags(interface, &network)){
2200
fprintf_plus(stderr, "Failed to get flags for interface "
2201
"\"%s\"\n", interface);
2205
network.ifr_flags |= IFF_UP; /* set flag */
2207
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2210
perror_plus("socket");
2218
perror_plus("close");
2225
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2229
/* Raise privileges */
2230
ret_errno = raise_privileges();
2233
perror_plus("Failed to raise privileges");
2238
bool restore_loglevel = false;
2240
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2241
messages about the network interface to mess up the prompt */
2242
ret_linux = klogctl(8, NULL, 5);
2243
if(ret_linux == -1){
2244
perror_plus("klogctl");
2246
restore_loglevel = true;
2249
#endif /* __linux__ */
2250
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2251
ioctl_errno = errno;
2253
if(restore_loglevel){
2254
ret_linux = klogctl(7, NULL, 0);
2255
if(ret_linux == -1){
2256
perror_plus("klogctl");
2259
#endif /* __linux__ */
2261
/* If raise_privileges() succeeded above */
2263
/* Lower privileges */
2264
ret_errno = lower_privileges();
2267
perror_plus("Failed to lower privileges");
2271
/* Close the socket */
2274
perror_plus("close");
2277
if(ret_setflags == -1){
2278
errno = ioctl_errno;
2279
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2284
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2288
/* Sleep checking until interface is running.
2289
Check every 0.25s, up to total time of delay */
2290
for(int i = 0; i < delay * 4; i++){
2291
if(interface_is_running(interface)){
2294
struct timespec sleeptime = { .tv_nsec = 250000000 };
2295
ret = nanosleep(&sleeptime, NULL);
2296
if(ret == -1 and errno != EINTR){
2297
perror_plus("nanosleep");
2305
__attribute__((nonnull, warn_unused_result))
2306
int take_down_interface(const char *const interface){
2307
int old_errno = errno;
2308
struct ifreq network;
2309
unsigned int if_index = if_nametoindex(interface);
2311
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2315
if(interface_is_up(interface)){
2317
int ioctl_errno = 0;
2318
if(not get_flags(interface, &network) and debug){
2320
fprintf_plus(stderr, "Failed to get flags for interface "
2321
"\"%s\"\n", interface);
2325
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2327
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2330
perror_plus("socket");
2336
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2340
/* Raise privileges */
2341
ret_errno = raise_privileges();
2344
perror_plus("Failed to raise privileges");
2347
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2348
ioctl_errno = errno;
2350
/* If raise_privileges() succeeded above */
2352
/* Lower privileges */
2353
ret_errno = lower_privileges();
2356
perror_plus("Failed to lower privileges");
2360
/* Close the socket */
2361
int ret = close(sd);
2363
perror_plus("close");
2366
if(ret_setflags == -1){
2367
errno = ioctl_errno;
2368
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2373
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2381
int main(int argc, char *argv[]){
2382
mandos_context mc = { .server = NULL, .dh_bits = 0,
2383
.priority = "SECURE256:!CTYPE-X.509"
2384
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2385
.current_server = NULL, .interfaces = NULL,
2386
.interfaces_size = 0 };
2387
AvahiSServiceBrowser *sb = NULL;
2392
int exitcode = EXIT_SUCCESS;
2393
char *interfaces_to_take_down = NULL;
2394
size_t interfaces_to_take_down_size = 0;
2395
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2396
char old_tempdir[] = "/tmp/mandosXXXXXX";
2397
char *tempdir = NULL;
2398
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2399
const char *seckey = PATHDIR "/" SECKEY;
2400
const char *pubkey = PATHDIR "/" PUBKEY;
2401
const char *dh_params_file = NULL;
2402
char *interfaces_hooks = NULL;
2404
bool gnutls_initialized = false;
2405
bool gpgme_initialized = false;
2407
double retry_interval = 10; /* 10s between trying a server and
2408
retrying the same server again */
2410
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2411
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2416
/* Lower any group privileges we might have, just to be safe */
2420
perror_plus("setgid");
2423
/* Lower user privileges (temporarily) */
2427
perror_plus("seteuid");
2435
struct argp_option options[] = {
2436
{ .name = "debug", .key = 128,
2437
.doc = "Debug mode", .group = 3 },
2438
{ .name = "connect", .key = 'c',
2439
.arg = "ADDRESS:PORT",
2440
.doc = "Connect directly to a specific Mandos server",
2442
{ .name = "interface", .key = 'i',
2444
.doc = "Network interface that will be used to search for"
2447
{ .name = "seckey", .key = 's',
2449
.doc = "OpenPGP secret key file base name",
2451
{ .name = "pubkey", .key = 'p',
2453
.doc = "OpenPGP public key file base name",
2455
{ .name = "dh-bits", .key = 129,
2457
.doc = "Bit length of the prime number used in the"
2458
" Diffie-Hellman key exchange",
2460
{ .name = "dh-params", .key = 134,
2462
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2463
" for the Diffie-Hellman key exchange",
2465
{ .name = "priority", .key = 130,
2467
.doc = "GnuTLS priority string for the TLS handshake",
2469
{ .name = "delay", .key = 131,
2471
.doc = "Maximum delay to wait for interface startup",
2473
{ .name = "retry", .key = 132,
2475
.doc = "Retry interval used when denied by the Mandos server",
2477
{ .name = "network-hook-dir", .key = 133,
2479
.doc = "Directory where network hooks are located",
2482
* These reproduce what we would get without ARGP_NO_HELP
2484
{ .name = "help", .key = '?',
2485
.doc = "Give this help list", .group = -1 },
2486
{ .name = "usage", .key = -3,
2487
.doc = "Give a short usage message", .group = -1 },
2488
{ .name = "version", .key = 'V',
2489
.doc = "Print program version", .group = -1 },
2493
error_t parse_opt(int key, char *arg,
2494
struct argp_state *state){
2497
case 128: /* --debug */
2500
case 'c': /* --connect */
2503
case 'i': /* --interface */
2504
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2507
argp_error(state, "%s", strerror(ret_errno));
2510
case 's': /* --seckey */
2513
case 'p': /* --pubkey */
2516
case 129: /* --dh-bits */
2518
tmpmax = strtoimax(arg, &tmp, 10);
2519
if(errno != 0 or tmp == arg or *tmp != '\0'
2520
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2521
argp_error(state, "Bad number of DH bits");
2523
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2525
case 134: /* --dh-params */
2526
dh_params_file = arg;
2528
case 130: /* --priority */
2531
case 131: /* --delay */
2533
delay = strtof(arg, &tmp);
2534
if(errno != 0 or tmp == arg or *tmp != '\0'){
2535
argp_error(state, "Bad delay");
2537
case 132: /* --retry */
2539
retry_interval = strtod(arg, &tmp);
2540
if(errno != 0 or tmp == arg or *tmp != '\0'
2541
or (retry_interval * 1000) > INT_MAX
2542
or retry_interval < 0){
2543
argp_error(state, "Bad retry interval");
2546
case 133: /* --network-hook-dir */
2550
* These reproduce what we would get without ARGP_NO_HELP
2552
case '?': /* --help */
2553
argp_state_help(state, state->out_stream,
2554
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2555
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2556
case -3: /* --usage */
2557
argp_state_help(state, state->out_stream,
2558
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2559
case 'V': /* --version */
2560
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2561
exit(argp_err_exit_status);
2564
return ARGP_ERR_UNKNOWN;
2569
struct argp argp = { .options = options, .parser = parse_opt,
2571
.doc = "Mandos client -- Get and decrypt"
2572
" passwords from a Mandos server" };
2573
ret_errno = argp_parse(&argp, argc, argv,
2574
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2581
perror_plus("argp_parse");
2582
exitcode = EX_OSERR;
2585
exitcode = EX_USAGE;
2591
/* Work around Debian bug #633582:
2592
<https://bugs.debian.org/633582> */
2594
/* Re-raise privileges */
2595
ret = raise_privileges();
2598
perror_plus("Failed to raise privileges");
2602
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2603
int seckey_fd = open(seckey, O_RDONLY);
2604
if(seckey_fd == -1){
2605
perror_plus("open");
2607
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2609
perror_plus("fstat");
2611
if(S_ISREG(st.st_mode)
2612
and st.st_uid == 0 and st.st_gid == 0){
2613
ret = fchown(seckey_fd, uid, gid);
2615
perror_plus("fchown");
2623
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2624
int pubkey_fd = open(pubkey, O_RDONLY);
2625
if(pubkey_fd == -1){
2626
perror_plus("open");
2628
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2630
perror_plus("fstat");
2632
if(S_ISREG(st.st_mode)
2633
and st.st_uid == 0 and st.st_gid == 0){
2634
ret = fchown(pubkey_fd, uid, gid);
2636
perror_plus("fchown");
2644
if(dh_params_file != NULL
2645
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2646
int dhparams_fd = open(dh_params_file, O_RDONLY);
2647
if(dhparams_fd == -1){
2648
perror_plus("open");
2650
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2652
perror_plus("fstat");
2654
if(S_ISREG(st.st_mode)
2655
and st.st_uid == 0 and st.st_gid == 0){
2656
ret = fchown(dhparams_fd, uid, gid);
2658
perror_plus("fchown");
2666
/* Lower privileges */
2667
ret = lower_privileges();
2670
perror_plus("Failed to lower privileges");
2675
/* Remove invalid interface names (except "none") */
2677
char *interface = NULL;
2678
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2680
if(strcmp(interface, "none") != 0
2681
and if_nametoindex(interface) == 0){
2682
if(interface[0] != '\0'){
2683
fprintf_plus(stderr, "Not using nonexisting interface"
2684
" \"%s\"\n", interface);
2686
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2692
/* Run network hooks */
2694
if(mc.interfaces != NULL){
2695
interfaces_hooks = malloc(mc.interfaces_size);
2696
if(interfaces_hooks == NULL){
2697
perror_plus("malloc");
2700
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2701
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2703
run_network_hooks("start", interfaces_hooks != NULL ?
2704
interfaces_hooks : "", delay);
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[]) {
507
AvahiServerConfig config;
508
AvahiSServiceBrowser *sb = NULL;
2708
512
avahi_set_log_function(empty_log);
2711
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2712
from the signal handler */
2713
/* Initialize the pseudo-RNG for Avahi */
2714
srand((unsigned int) time(NULL));
2715
simple_poll = avahi_simple_poll_new();
2716
if(simple_poll == NULL){
2717
fprintf_plus(stderr,
2718
"Avahi: Failed to create simple poll object.\n");
2719
exitcode = EX_UNAVAILABLE;
2723
sigemptyset(&sigterm_action.sa_mask);
2724
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2726
perror_plus("sigaddset");
2727
exitcode = EX_OSERR;
2730
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2732
perror_plus("sigaddset");
2733
exitcode = EX_OSERR;
2736
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2738
perror_plus("sigaddset");
2739
exitcode = EX_OSERR;
2742
/* Need to check if the handler is SIG_IGN before handling:
2743
| [[info:libc:Initial Signal Actions]] |
2744
| [[info:libc:Basic Signal Handling]] |
2746
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2748
perror_plus("sigaction");
2751
if(old_sigterm_action.sa_handler != SIG_IGN){
2752
ret = sigaction(SIGINT, &sigterm_action, NULL);
2754
perror_plus("sigaction");
2755
exitcode = EX_OSERR;
2759
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2761
perror_plus("sigaction");
2764
if(old_sigterm_action.sa_handler != SIG_IGN){
2765
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2767
perror_plus("sigaction");
2768
exitcode = EX_OSERR;
2772
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2774
perror_plus("sigaction");
2777
if(old_sigterm_action.sa_handler != SIG_IGN){
2778
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2780
perror_plus("sigaction");
2781
exitcode = EX_OSERR;
2786
/* If no interfaces were specified, make a list */
2787
if(mc.interfaces == NULL){
2788
struct dirent **direntries = NULL;
2789
/* Look for any good interfaces */
2790
ret = scandir(sys_class_net, &direntries, good_interface,
2793
/* Add all found interfaces to interfaces list */
2794
for(int i = 0; i < ret; ++i){
2795
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2796
direntries[i]->d_name);
2799
perror_plus("argz_add");
2800
free(direntries[i]);
2804
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2805
direntries[i]->d_name);
2807
free(direntries[i]);
2814
fprintf_plus(stderr, "Could not find a network interface\n");
2815
exitcode = EXIT_FAILURE;
2820
/* Bring up interfaces which are down, and remove any "none"s */
2822
char *interface = NULL;
2823
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2825
/* If interface name is "none", stop bringing up interfaces.
2826
Also remove all instances of "none" from the list */
2827
if(strcmp(interface, "none") == 0){
2828
argz_delete(&mc.interfaces, &mc.interfaces_size,
2831
while((interface = argz_next(mc.interfaces,
2832
mc.interfaces_size, interface))){
2833
if(strcmp(interface, "none") == 0){
2834
argz_delete(&mc.interfaces, &mc.interfaces_size,
2841
bool interface_was_up = interface_is_up(interface);
2842
errno = bring_up_interface(interface, delay);
2843
if(not interface_was_up){
2845
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2846
" %s\n", interface, strerror(errno));
2848
errno = argz_add(&interfaces_to_take_down,
2849
&interfaces_to_take_down_size,
2852
perror_plus("argz_add");
2857
if(debug and (interfaces_to_take_down == NULL)){
2858
fprintf_plus(stderr, "No interfaces were brought up\n");
2862
/* If we only got one interface, explicitly use only that one */
2863
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2865
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2868
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2875
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2877
fprintf_plus(stderr, "init_gnutls_global failed\n");
2878
exitcode = EX_UNAVAILABLE;
2881
gnutls_initialized = true;
2888
/* Try /run/tmp before /tmp */
2889
tempdir = mkdtemp(run_tempdir);
2890
if(tempdir == NULL and errno == ENOENT){
2892
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2893
run_tempdir, old_tempdir);
2895
tempdir = mkdtemp(old_tempdir);
2897
if(tempdir == NULL){
2898
perror_plus("mkdtemp");
2906
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2907
fprintf_plus(stderr, "init_gpgme failed\n");
2908
exitcode = EX_UNAVAILABLE;
2911
gpgme_initialized = true;
2918
if(connect_to != NULL){
2919
/* Connect directly, do not use Zeroconf */
2920
/* (Mainly meant for debugging) */
2921
char *address = strrchr(connect_to, ':');
2923
if(address == NULL){
2924
fprintf_plus(stderr, "No colon in address\n");
2925
exitcode = EX_USAGE;
2935
tmpmax = strtoimax(address+1, &tmp, 10);
2936
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2937
or tmpmax != (in_port_t)tmpmax){
2938
fprintf_plus(stderr, "Bad port number\n");
2939
exitcode = EX_USAGE;
2947
port = (in_port_t)tmpmax;
2949
/* Colon in address indicates IPv6 */
2951
if(strchr(connect_to, ':') != NULL){
2953
/* Accept [] around IPv6 address - see RFC 5952 */
2954
if(connect_to[0] == '[' and address[-1] == ']')
2962
address = connect_to;
2968
while(not quit_now){
2969
ret = start_mandos_communication(address, port, if_index, af,
2971
if(quit_now or ret == 0){
2975
fprintf_plus(stderr, "Retrying in %d seconds\n",
2976
(int)retry_interval);
2978
sleep((unsigned int)retry_interval);
2982
exitcode = EXIT_SUCCESS;
2993
AvahiServerConfig config;
2994
/* Do not publish any local Zeroconf records */
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 */
2995
524
avahi_server_config_init(&config);
2996
525
config.publish_hinfo = 0;
2997
526
config.publish_addresses = 0;
2998
527
config.publish_workstation = 0;
2999
528
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; */
3001
535
/* Allocate a new server */
3002
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3003
&config, NULL, NULL, &ret);
3005
/* Free the Avahi configuration data */
536
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
538
/* Free the configuration data */
3006
539
avahi_server_config_free(&config);
3009
/* Check if creating the Avahi server object succeeded */
3010
if(mc.server == NULL){
3011
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3012
avahi_strerror(ret));
3013
exitcode = EX_UNAVAILABLE;
3021
/* Create the Avahi service browser */
3022
sb = avahi_s_service_browser_new(mc.server, if_index,
3023
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
3024
NULL, 0, browse_callback,
3027
fprintf_plus(stderr, "Failed to create service browser: %s\n",
3028
avahi_strerror(avahi_server_errno(mc.server)));
3029
exitcode = EX_UNAVAILABLE;
3037
/* Run the main loop */
3040
fprintf_plus(stderr, "Starting Avahi loop search\n");
3043
ret = avahi_loop_with_timeout(simple_poll,
3044
(int)(retry_interval * 1000), &mc);
3046
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
3047
(ret == 0) ? "successfully" : "with error");
3053
if(signal_received){
3054
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3055
argv[0], signal_received,
3056
strsignal(signal_received));
3058
fprintf_plus(stderr, "%s exiting\n", argv[0]);
3062
/* Cleanup things */
3063
free(mc.interfaces);
3066
avahi_s_service_browser_free(sb);
3068
if(mc.server != NULL)
3069
avahi_server_free(mc.server);
3071
if(simple_poll != NULL)
3072
avahi_simple_poll_free(simple_poll);
3074
if(gnutls_initialized){
3075
gnutls_certificate_free_credentials(mc.cred);
3076
gnutls_dh_params_deinit(mc.dh_params);
3079
if(gpgme_initialized){
3080
gpgme_release(mc.ctx);
3083
/* Cleans up the circular linked list of Mandos servers the client
3085
if(mc.current_server != NULL){
3086
mc.current_server->prev->next = NULL;
3087
while(mc.current_server != NULL){
3088
server *next = mc.current_server->next;
3090
#pragma GCC diagnostic push
3091
#pragma GCC diagnostic ignored "-Wcast-qual"
3093
free((char *)(mc.current_server->ip));
3095
#pragma GCC diagnostic pop
3097
free(mc.current_server);
3098
mc.current_server = next;
3102
/* Re-raise privileges */
3104
ret = raise_privileges();
3107
perror_plus("Failed to raise privileges");
3110
/* Run network hooks */
3111
run_network_hooks("stop", interfaces_hooks != NULL ?
3112
interfaces_hooks : "", delay);
3114
/* Take down the network interfaces which were brought up */
3116
char *interface = NULL;
3117
while((interface = argz_next(interfaces_to_take_down,
3118
interfaces_to_take_down_size,
3120
ret = take_down_interface(interface);
3123
perror_plus("Failed to take down interface");
3126
if(debug and (interfaces_to_take_down == NULL)){
3127
fprintf_plus(stderr, "No interfaces needed to be taken"
3133
ret = lower_privileges_permanently();
3136
perror_plus("Failed to lower privileges permanently");
3140
free(interfaces_to_take_down);
3141
free(interfaces_hooks);
3143
void clean_dir_at(int base, const char * const dirname,
3145
struct dirent **direntries = NULL;
3147
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3153
perror_plus("open");
3156
int numentries = scandirat(dir_fd, ".", &direntries,
3157
notdotentries, alphasort);
3158
if(numentries >= 0){
3159
for(int i = 0; i < numentries; i++){
3161
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3162
dirname, direntries[i]->d_name);
3164
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3166
if(errno == EISDIR){
3167
dret = unlinkat(dir_fd, direntries[i]->d_name,
3170
if((dret == -1) and (errno == ENOTEMPTY)
3171
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3172
== 0) and (level == 0)){
3173
/* Recurse only in this special case */
3174
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3177
if((dret == -1) and (errno != ENOENT)){
3178
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3179
direntries[i]->d_name, strerror(errno));
3182
free(direntries[i]);
3185
/* need to clean even if 0 because man page doesn't specify */
3187
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3188
if(dret == -1 and errno != ENOENT){
3189
perror_plus("rmdir");
3192
perror_plus("scandirat");
3197
/* Removes the GPGME temp directory and all files inside */
3198
if(tempdir != NULL){
3199
clean_dir_at(-1, tempdir, 0);
3203
sigemptyset(&old_sigterm_action.sa_mask);
3204
old_sigterm_action.sa_handler = SIG_DFL;
3205
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3206
&old_sigterm_action,
3209
perror_plus("sigaction");
3212
ret = raise(signal_received);
3213
} while(ret != 0 and errno == EINTR);
3215
perror_plus("raise");
3218
TEMP_FAILURE_RETRY(pause());
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);