46
117
#include <avahi-common/malloc.h>
47
118
#include <avahi-common/error.h>
50
#include <sys/types.h> /* socket(), inet_pton() */
51
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
52
struct in6_addr, inet_pton() */
53
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
54
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
56
#include <unistd.h> /* close() */
57
#include <netinet/in.h>
58
#include <stdbool.h> /* true */
59
#include <string.h> /* memset */
60
#include <arpa/inet.h> /* inet_pton() */
61
#include <iso646.h> /* not */
64
#include <errno.h> /* perror() */
71
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
73
#define CERTFILE CERT_ROOT "openpgp-client.txt"
74
#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,
75
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"
78
144
bool debug = false;
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 */
81
gnutls_session_t session;
82
169
gnutls_certificate_credentials_t cred;
170
unsigned int dh_bits;
83
171
gnutls_dh_params_t dh_params;
87
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
char **new_packet, const char *homedir){
89
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,
93
ssize_t new_packet_capacity = 0;
94
ssize_t new_packet_length = 0;
95
284
gpgme_engine_info_t engine_info;
98
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
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");
102
327
gpgme_check_version(NULL);
103
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));
105
/* Set GPGME home directory */
106
rc = gpgme_get_engine_info (&engine_info);
107
if (rc != GPG_ERR_NO_ERROR){
108
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
109
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));
112
342
while(engine_info != NULL){
113
343
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
114
344
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
115
engine_info->file_name, homedir);
345
engine_info->file_name, tempdir);
118
348
engine_info = engine_info->next;
120
350
if(engine_info == NULL){
121
fprintf(stderr, "Could not set home dir to %s\n", homedir);
125
/* Create new GPGME data buffer from packet buffer */
126
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
127
if (rc != GPG_ERR_NO_ERROR){
128
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
129
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));
133
400
/* Create new empty GPGME data buffer for the plaintext */
134
401
rc = gpgme_data_new(&dh_plain);
135
if (rc != GPG_ERR_NO_ERROR){
136
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
137
gpgme_strsource(rc), gpgme_strerror(rc));
141
/* Create new GPGME "context" */
142
rc = gpgme_new(&ctx);
143
if (rc != GPG_ERR_NO_ERROR){
144
fprintf(stderr, "bad gpgme_new: %s: %s\n",
145
gpgme_strsource(rc), gpgme_strerror(rc));
149
/* Decrypt data from the FILE pointer to the plaintext data
151
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
152
if (rc != GPG_ERR_NO_ERROR){
153
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
154
gpgme_strsource(rc), gpgme_strerror(rc));
159
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
163
gpgme_decrypt_result_t result;
164
result = gpgme_op_decrypt_result(ctx);
166
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
168
fprintf(stderr, "Unsupported algorithm: %s\n",
169
result->unsupported_algorithm);
170
fprintf(stderr, "Wrong key usage: %d\n",
171
result->wrong_key_usage);
172
if(result->file_name != NULL){
173
fprintf(stderr, "File name: %s\n", result->file_name);
175
gpgme_recipient_t recipient;
176
recipient = result->recipients;
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;
178
432
while(recipient != NULL){
179
fprintf(stderr, "Public key algorithm: %s\n",
180
gpgme_pubkey_algo_name(recipient->pubkey_algo));
181
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
182
fprintf(stderr, "Secret key available: %s\n",
183
recipient->status == GPG_ERR_NO_SECKEY
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
185
440
recipient = recipient->next;
191
/* Delete the GPGME FILE pointer cryptotext data buffer */
192
gpgme_data_release(dh_crypto);
448
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
194
451
/* Seek back to the beginning of the GPGME plaintext data buffer */
195
gpgme_data_seek(dh_plain, (off_t) 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;
199
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
200
*new_packet = realloc(*new_packet,
201
(unsigned int)new_packet_capacity
203
if (*new_packet == NULL){
207
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;
210
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
469
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
212
471
/* Print the data, if any */
217
perror("gpgme_data_read");
220
new_packet_length += ret;
223
/* FIXME: check characters before printing to screen so to not print
224
terminal control characters */
226
/* fprintf(stderr, "decrypted password is: "); */
227
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
228
/* fprintf(stderr, "\n"); */
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);
231
497
/* Delete the GPGME plaintext data buffer */
232
498
gpgme_data_release(dh_plain);
233
return new_packet_length;
236
static const char * safer_gnutls_strerror (int value) {
237
const char *ret = gnutls_strerror (value);
243
void debuggnutls(__attribute__((unused)) int level,
245
fprintf(stderr, "%s", string);
248
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,
253
fprintf(stderr, "Initializing GnuTLS\n");
256
if ((ret = gnutls_global_init ())
257
!= GNUTLS_E_SUCCESS) {
258
fprintf (stderr, "global_init: %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."
263
538
gnutls_global_set_log_level(11);
264
539
gnutls_global_set_log_function(debuggnutls);
267
/* openpgp credentials */
268
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
269
!= GNUTLS_E_SUCCESS) {
270
fprintf (stderr, "memory error: %s\n",
271
safer_gnutls_strerror(ret));
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));
276
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
277
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
551
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
552
" secret key %s as GnuTLS credentials\n",
281
557
ret = gnutls_certificate_set_openpgp_key_file
282
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
283
if (ret != GNUTLS_E_SUCCESS) {
285
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
287
ret, CERTFILE, KEYFILE);
288
fprintf(stdout, "The Error is: %s\n",
289
safer_gnutls_strerror(ret));
293
//GnuTLS server initialization
294
if ((ret = gnutls_dh_params_init (&es->dh_params))
295
!= GNUTLS_E_SUCCESS) {
296
fprintf (stderr, "Error in dh parameter initialization: %s\n",
297
safer_gnutls_strerror(ret));
301
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
302
!= GNUTLS_E_SUCCESS) {
303
fprintf (stderr, "Error in prime generation: %s\n",
304
safer_gnutls_strerror(ret));
308
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
310
// GnuTLS session creation
311
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
312
!= GNUTLS_E_SUCCESS){
313
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
314
safer_gnutls_strerror(ret));
317
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
318
!= GNUTLS_E_SUCCESS) {
319
fprintf(stderr, "Syntax error at: %s\n", err);
320
fprintf(stderr, "GnuTLS error: %s\n",
321
safer_gnutls_strerror(ret));
325
if ((ret = gnutls_credentials_set
326
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
327
!= GNUTLS_E_SUCCESS) {
328
fprintf(stderr, "Error setting a credentials set: %s\n",
329
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;
617
if(params.data == NULL){
618
dhparamsfilename = NULL;
620
if(dhparamsfilename == NULL){
623
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
624
GNUTLS_X509_FMT_PEM);
625
if(ret != GNUTLS_E_SUCCESS){
626
fprintf_plus(stderr, "Failed to parse DH parameters in file"
627
" \"%s\": %s\n", dhparamsfilename,
628
safer_gnutls_strerror(ret));
629
dhparamsfilename = NULL;
634
if(dhparamsfilename == NULL){
635
if(mc->dh_bits == 0){
636
/* Find out the optimal number of DH bits */
637
/* Try to read the private key file */
638
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
640
int secfile = open(seckeyfilename, O_RDONLY);
645
size_t buffer_capacity = 0;
647
buffer_capacity = incbuffer((char **)&buffer.data,
649
(size_t)buffer_capacity);
650
if(buffer_capacity == 0){
651
perror_plus("incbuffer");
656
ssize_t bytes_read = read(secfile,
657
buffer.data + buffer.size,
663
/* check bytes_read for failure */
670
buffer.size += (unsigned int)bytes_read;
674
/* If successful, use buffer to parse private key */
675
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
676
if(buffer.data != NULL){
678
gnutls_openpgp_privkey_t privkey = NULL;
679
ret = gnutls_openpgp_privkey_init(&privkey);
680
if(ret != GNUTLS_E_SUCCESS){
681
fprintf_plus(stderr, "Error initializing OpenPGP key"
683
safer_gnutls_strerror(ret));
687
ret = gnutls_openpgp_privkey_import
688
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
689
if(ret != GNUTLS_E_SUCCESS){
690
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
691
safer_gnutls_strerror(ret));
697
/* Use private key to suggest an appropriate
699
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
700
gnutls_openpgp_privkey_deinit(privkey);
702
fprintf_plus(stderr, "This OpenPGP key implies using"
703
" a GnuTLS security parameter \"%s\".\n",
704
safe_string(gnutls_sec_param_get_name
710
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
711
/* Err on the side of caution */
712
sec_param = GNUTLS_SEC_PARAM_ULTRA;
714
fprintf_plus(stderr, "Falling back to security parameter"
716
safe_string(gnutls_sec_param_get_name
721
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
725
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
726
" implies %u DH bits; using that.\n",
727
safe_string(gnutls_sec_param_get_name
732
fprintf_plus(stderr, "Failed to get implied number of DH"
733
" bits for security parameter \"%s\"): %s\n",
734
safe_string(gnutls_sec_param_get_name
736
safer_gnutls_strerror(ret));
740
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
743
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
744
if(ret != GNUTLS_E_SUCCESS){
745
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
746
" bits): %s\n", mc->dh_bits,
747
safer_gnutls_strerror(ret));
751
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
757
gnutls_certificate_free_credentials(mc->cred);
758
gnutls_dh_params_deinit(mc->dh_params);
762
__attribute__((nonnull, warn_unused_result))
763
static int init_gnutls_session(gnutls_session_t *session,
766
/* GnuTLS session creation */
768
ret = gnutls_init(session, GNUTLS_SERVER);
772
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
773
if(ret != GNUTLS_E_SUCCESS){
775
"Error in GnuTLS session initialization: %s\n",
776
safer_gnutls_strerror(ret));
782
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
784
gnutls_deinit(*session);
787
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
788
if(ret != GNUTLS_E_SUCCESS){
789
fprintf_plus(stderr, "Syntax error at: %s\n", err);
790
fprintf_plus(stderr, "GnuTLS error: %s\n",
791
safer_gnutls_strerror(ret));
792
gnutls_deinit(*session);
798
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
801
gnutls_deinit(*session);
804
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
805
if(ret != GNUTLS_E_SUCCESS){
806
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
807
safer_gnutls_strerror(ret));
808
gnutls_deinit(*session);
333
812
/* ignore client certificate if any. */
334
gnutls_certificate_server_set_request (es->session,
337
gnutls_dh_set_prime_bits (es->session, DH_BITS);
813
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
342
void empty_log(__attribute__((unused)) AvahiLogLevel level,
343
__attribute__((unused)) const char *txt){}
345
int start_mandos_communication(const char *ip, uint16_t port,
346
unsigned int if_index){
348
struct sockaddr_in6 to;
349
encrypted_session es;
818
/* Avahi log function callback */
819
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
820
__attribute__((unused)) const char *txt){}
822
/* Set effective uid to 0, return errno */
823
__attribute__((warn_unused_result))
824
int raise_privileges(void){
825
int old_errno = errno;
827
if(seteuid(0) == -1){
834
/* Set effective and real user ID to 0. Return errno. */
835
__attribute__((warn_unused_result))
836
int raise_privileges_permanently(void){
837
int old_errno = errno;
838
int ret = raise_privileges();
850
/* Set effective user ID to unprivileged saved user ID */
851
__attribute__((warn_unused_result))
852
int lower_privileges(void){
853
int old_errno = errno;
855
if(seteuid(uid) == -1){
862
/* Lower privileges permanently */
863
__attribute__((warn_unused_result))
864
int lower_privileges_permanently(void){
865
int old_errno = errno;
867
if(setuid(uid) == -1){
874
/* Helper function to add_local_route() and delete_local_route() */
875
__attribute__((nonnull, warn_unused_result))
876
static bool add_delete_local_route(const bool add,
878
AvahiIfIndex if_index){
880
char helper[] = "mandos-client-iprouteadddel";
881
char add_arg[] = "add";
882
char delete_arg[] = "delete";
883
char debug_flag[] = "--debug";
884
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
885
if(pluginhelperdir == NULL){
887
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
888
" variable not set; cannot run helper\n");
893
char interface[IF_NAMESIZE];
894
if(if_indextoname((unsigned int)if_index, interface) == NULL){
895
perror_plus("if_indextoname");
899
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
901
perror_plus("open(\"/dev/null\", O_RDONLY)");
907
/* Raise privileges */
908
errno = raise_privileges_permanently();
910
perror_plus("Failed to raise privileges");
911
/* _exit(EX_NOPERM); */
917
perror_plus("setgid");
920
/* Reset supplementary groups */
922
ret = setgroups(0, NULL);
924
perror_plus("setgroups");
928
ret = dup2(devnull, STDIN_FILENO);
930
perror_plus("dup2(devnull, STDIN_FILENO)");
933
ret = close(devnull);
935
perror_plus("close");
938
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
940
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
943
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
948
if(helperdir_fd == -1){
950
_exit(EX_UNAVAILABLE);
952
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
955
perror_plus("openat");
957
_exit(EX_UNAVAILABLE);
961
#pragma GCC diagnostic push
962
#pragma GCC diagnostic ignored "-Wcast-qual"
964
if(fexecve(helper_fd, (char *const [])
965
{ helper, add ? add_arg : delete_arg, (char *)address,
966
interface, debug ? debug_flag : NULL, NULL },
969
#pragma GCC diagnostic pop
971
perror_plus("fexecve");
983
pret = waitpid(pid, &status, 0);
984
if(pret == -1 and errno == EINTR and quit_now){
985
int errno_raising = 0;
986
if((errno = raise_privileges()) != 0){
987
errno_raising = errno;
988
perror_plus("Failed to raise privileges in order to"
989
" kill helper program");
991
if(kill(pid, SIGTERM) == -1){
994
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
995
perror_plus("Failed to lower privileges after killing"
1000
} while(pret == -1 and errno == EINTR);
1002
perror_plus("waitpid");
1005
if(WIFEXITED(status)){
1006
if(WEXITSTATUS(status) != 0){
1007
fprintf_plus(stderr, "Error: iprouteadddel exited"
1008
" with status %d\n", WEXITSTATUS(status));
1013
if(WIFSIGNALED(status)){
1014
fprintf_plus(stderr, "Error: iprouteadddel died by"
1015
" signal %d\n", WTERMSIG(status));
1018
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1022
__attribute__((nonnull, warn_unused_result))
1023
static bool add_local_route(const char *address,
1024
AvahiIfIndex if_index){
1026
fprintf_plus(stderr, "Adding route to %s\n", address);
1028
return add_delete_local_route(true, address, if_index);
1031
__attribute__((nonnull, warn_unused_result))
1032
static bool delete_local_route(const char *address,
1033
AvahiIfIndex if_index){
1035
fprintf_plus(stderr, "Removing route to %s\n", address);
1037
return add_delete_local_route(false, address, if_index);
1040
/* Called when a Mandos server is found */
1041
__attribute__((nonnull, warn_unused_result))
1042
static int start_mandos_communication(const char *ip, in_port_t port,
1043
AvahiIfIndex if_index,
1044
int af, mandos_context *mc){
1045
int ret, tcp_sd = -1;
1047
struct sockaddr_storage to;
350
1048
char *buffer = NULL;
351
char *decrypted_buffer;
1049
char *decrypted_buffer = NULL;
352
1050
size_t buffer_length = 0;
353
1051
size_t buffer_capacity = 0;
354
ssize_t decrypted_buffer_size;
357
char interface[IF_NAMESIZE];
360
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
364
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
370
if(if_indextoname(if_index, interface) == NULL){
372
perror("if_indextoname");
378
fprintf(stderr, "Binding to interface %s\n", interface);
381
memset(&to,0,sizeof(to)); /* Spurious warning */
382
to.sin6_family = AF_INET6;
383
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
1054
gnutls_session_t session;
1055
int pf; /* Protocol family */
1056
bool route_added = false;
1073
fprintf_plus(stderr, "Bad address family: %d\n", af);
1078
/* If the interface is specified and we have a list of interfaces */
1079
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1080
/* Check if the interface is one of the interfaces we are using */
1083
char *interface = NULL;
1084
while((interface = argz_next(mc->interfaces,
1085
mc->interfaces_size,
1087
if(if_nametoindex(interface) == (unsigned int)if_index){
1094
/* This interface does not match any in the list, so we don't
1095
connect to the server */
1097
char interface[IF_NAMESIZE];
1098
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1099
perror_plus("if_indextoname");
1101
fprintf_plus(stderr, "Skipping server on non-used interface"
1103
if_indextoname((unsigned int)if_index,
1111
ret = init_gnutls_session(&session, mc);
1117
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1118
PRIuMAX "\n", ip, (uintmax_t)port);
1121
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1124
perror_plus("socket");
1135
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1136
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1137
ret = inet_pton(af, ip, &to6->sin6_addr);
1139
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1140
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1141
ret = inet_pton(af, ip, &to4->sin_addr);
1145
perror_plus("inet_pton");
389
fprintf(stderr, "Bad address: %s\n", ip);
392
to.sin6_port = htons(port); /* Spurious warning */
394
to.sin6_scope_id = (uint32_t)if_index;
397
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
398
/* char addrstr[INET6_ADDRSTRLEN]; */
399
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
400
/* sizeof(addrstr)) == NULL){ */
401
/* perror("inet_ntop"); */
403
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
404
/* addrstr, ntohs(to.sin6_port)); */
408
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
414
ret = initgnutls (&es);
420
gnutls_transport_set_ptr (es.session,
421
(gnutls_transport_ptr_t) tcp_sd);
424
fprintf(stderr, "Establishing TLS session with %s\n", ip);
427
ret = gnutls_handshake (es.session);
429
if (ret != GNUTLS_E_SUCCESS){
1151
fprintf_plus(stderr, "Bad address: %s\n", ip);
1156
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1157
if(IN6_IS_ADDR_LINKLOCAL
1158
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1159
if(if_index == AVAHI_IF_UNSPEC){
1160
fprintf_plus(stderr, "An IPv6 link-local address is"
1161
" incomplete without a network interface\n");
1165
/* Set the network interface number as scope */
1166
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1169
((struct sockaddr_in *)&to)->sin_port = htons(port);
1178
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1179
char interface[IF_NAMESIZE];
1180
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1181
perror_plus("if_indextoname");
1183
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1184
"\n", ip, interface, (uintmax_t)port);
1187
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1188
ip, (uintmax_t)port);
1190
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1191
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1193
ret = getnameinfo((struct sockaddr *)&to,
1194
sizeof(struct sockaddr_in6),
1195
addrstr, sizeof(addrstr), NULL, 0,
1198
ret = getnameinfo((struct sockaddr *)&to,
1199
sizeof(struct sockaddr_in),
1200
addrstr, sizeof(addrstr), NULL, 0,
1203
if(ret == EAI_SYSTEM){
1204
perror_plus("getnameinfo");
1205
} else if(ret != 0) {
1206
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1207
} else if(strcmp(addrstr, ip) != 0){
1208
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1219
ret = connect(tcp_sd, (struct sockaddr *)&to,
1220
sizeof(struct sockaddr_in6));
1222
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1223
sizeof(struct sockaddr_in));
1226
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1227
and if_index != AVAHI_IF_UNSPEC
1228
and connect_to == NULL
1229
and not route_added and
1230
((af == AF_INET6 and not
1231
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1233
or (af == AF_INET and
1234
/* Not a a IPv4LL address */
1235
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1236
& 0xFFFF0000L) != 0xA9FE0000L))){
1237
/* Work around Avahi bug - Avahi does not announce link-local
1238
addresses if it has a global address, so local hosts with
1239
*only* a link-local address (e.g. Mandos clients) cannot
1240
connect to a Mandos server announced by Avahi on a server
1241
host with a global address. Work around this by retrying
1242
with an explicit route added with the server's address.
1244
Avahi bug reference:
1245
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1246
https://bugs.debian.org/587961
1249
fprintf_plus(stderr, "Mandos server unreachable, trying"
1253
route_added = add_local_route(ip, if_index);
1259
if(errno != ECONNREFUSED or debug){
1261
perror_plus("connect");
1274
const char *out = mandos_protocol_version;
1277
size_t out_size = strlen(out);
1278
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1279
out_size - written));
1282
perror_plus("write");
1286
written += (size_t)ret;
1287
if(written < out_size){
1290
if(out == mandos_protocol_version){
1305
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1313
/* This casting via intptr_t is to eliminate warning about casting
1314
an int to a pointer type. This is exactly how the GnuTLS Guile
1315
function "set-session-transport-fd!" does it. */
1316
gnutls_transport_set_ptr(session,
1317
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1325
ret = gnutls_handshake(session);
1330
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1332
if(ret != GNUTLS_E_SUCCESS){
431
fprintf(stderr, "\n*** Handshake failed ***\n");
1334
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
438
//Retrieve OpenPGP packet that contains the wanted password
1341
/* Read OpenPGP packet that contains the wanted password */
441
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1344
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
446
if (buffer_length + BUFFER_SIZE > buffer_capacity){
447
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
452
buffer_capacity += BUFFER_SIZE;
455
ret = gnutls_record_recv
456
(es.session, buffer+buffer_length, BUFFER_SIZE);
1355
buffer_capacity = incbuffer(&buffer, buffer_length,
1357
if(buffer_capacity == 0){
1359
perror_plus("incbuffer");
1369
sret = gnutls_record_recv(session, buffer+buffer_length,
462
1376
case GNUTLS_E_INTERRUPTED:
463
1377
case GNUTLS_E_AGAIN:
465
1379
case GNUTLS_E_REHANDSHAKE:
466
ret = gnutls_handshake (es.session);
468
fprintf(stderr, "\n*** Handshake failed ***\n");
1381
ret = gnutls_handshake(session);
1387
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1389
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
475
fprintf(stderr, "Unknown error while reading data from"
476
" encrypted session with mandos server\n");
478
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1397
fprintf_plus(stderr, "Unknown error while reading data from"
1398
" encrypted session with Mandos server\n");
1399
gnutls_bye(session, GNUTLS_SHUT_RDWR);
482
buffer_length += (size_t) ret;
486
if (buffer_length > 0){
487
decrypted_buffer_size = pgp_packet_decrypt(buffer,
491
if (decrypted_buffer_size >= 0){
1404
buffer_length += (size_t) sret;
1409
fprintf_plus(stderr, "Closing TLS session\n");
1418
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1423
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1425
if(buffer_length > 0){
1426
ssize_t decrypted_buffer_size;
1427
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1428
&decrypted_buffer, mc);
1429
if(decrypted_buffer_size >= 0){
492
1433
while(written < (size_t) decrypted_buffer_size){
493
ret = (int)fwrite (decrypted_buffer + written, 1,
494
(size_t)decrypted_buffer_size - written,
1439
ret = (int)fwrite(decrypted_buffer + written, 1,
1440
(size_t)decrypted_buffer_size - written,
496
1442
if(ret == 0 and ferror(stdout)){
498
fprintf(stderr, "Error writing encrypted data: %s\n",
1445
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
504
1451
written += (size_t)ret;
506
free(decrypted_buffer);
1453
ret = fflush(stdout);
1457
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1467
/* Shutdown procedure */
1472
if(not delete_local_route(ip, if_index)){
1473
fprintf_plus(stderr, "Failed to delete local route to %s on"
1474
" interface %d", ip, if_index);
1478
free(decrypted_buffer);
1481
ret = close(tcp_sd);
1487
perror_plus("close");
1489
gnutls_deinit(session);
515
fprintf(stderr, "Closing TLS session\n");
519
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
522
gnutls_deinit (es.session);
523
gnutls_certificate_free_credentials (es.cred);
524
gnutls_global_deinit ();
528
static AvahiSimplePoll *simple_poll = NULL;
529
static AvahiServer *server = NULL;
531
static void resolve_callback(
532
AvahiSServiceResolver *r,
533
AvahiIfIndex interface,
534
AVAHI_GCC_UNUSED AvahiProtocol protocol,
535
AvahiResolverEvent event,
539
const char *host_name,
540
const AvahiAddress *address,
542
AVAHI_GCC_UNUSED AvahiStringList *txt,
543
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
544
AVAHI_GCC_UNUSED void* userdata) {
546
assert(r); /* Spurious warning */
1499
static void resolve_callback(AvahiSServiceResolver *r,
1500
AvahiIfIndex interface,
1501
AvahiProtocol proto,
1502
AvahiResolverEvent event,
1506
const char *host_name,
1507
const AvahiAddress *address,
1509
AVAHI_GCC_UNUSED AvahiStringList *txt,
1510
AVAHI_GCC_UNUSED AvahiLookupResultFlags
548
1517
/* Called whenever a service has been resolved successfully or
1521
avahi_s_service_resolver_free(r);
553
1527
case AVAHI_RESOLVER_FAILURE:
554
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
555
" type '%s' in domain '%s': %s\n", name, type, domain,
556
avahi_strerror(avahi_server_errno(server)));
1528
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1529
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1531
avahi_strerror(avahi_server_errno
1532
(((mandos_context*)mc)->server)));
559
1535
case AVAHI_RESOLVER_FOUND:
561
1537
char ip[AVAHI_ADDRESS_STR_MAX];
562
1538
avahi_address_snprint(ip, sizeof(ip), address);
564
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
565
" port %d\n", name, host_name, ip, port);
1540
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1541
PRIdMAX ") on port %" PRIu16 "\n", name,
1542
host_name, ip, (intmax_t)interface, port);
567
int ret = start_mandos_communication(ip, port,
568
(unsigned int) interface);
1544
int ret = start_mandos_communication(ip, (in_port_t)port,
1546
avahi_proto_to_af(proto),
1549
avahi_simple_poll_quit(simple_poll);
1551
if(not add_server(ip, (in_port_t)port, interface,
1552
avahi_proto_to_af(proto),
1553
&((mandos_context*)mc)->current_server)){
1554
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
574
1560
avahi_s_service_resolver_free(r);
577
static void browse_callback(
578
AvahiSServiceBrowser *b,
579
AvahiIfIndex interface,
580
AvahiProtocol protocol,
581
AvahiBrowserEvent event,
585
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
588
AvahiServer *s = userdata;
589
assert(b); /* Spurious warning */
591
/* Called whenever a new services becomes available on the LAN or
592
is removed from the LAN */
1563
static void browse_callback(AvahiSServiceBrowser *b,
1564
AvahiIfIndex interface,
1565
AvahiProtocol protocol,
1566
AvahiBrowserEvent event,
1570
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1577
/* Called whenever a new services becomes available on the LAN or
1578
is removed from the LAN */
1586
case AVAHI_BROWSER_FAILURE:
1588
fprintf_plus(stderr, "(Avahi browser) %s\n",
1589
avahi_strerror(avahi_server_errno
1590
(((mandos_context*)mc)->server)));
1591
avahi_simple_poll_quit(simple_poll);
1594
case AVAHI_BROWSER_NEW:
1595
/* We ignore the returned Avahi resolver object. In the callback
1596
function we free it. If the Avahi server is terminated before
1597
the callback function is called the Avahi server will free the
1600
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1601
interface, protocol, name, type,
1602
domain, protocol, 0,
1603
resolve_callback, mc) == NULL)
1604
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1606
avahi_strerror(avahi_server_errno
1607
(((mandos_context*)mc)->server)));
1610
case AVAHI_BROWSER_REMOVE:
1613
case AVAHI_BROWSER_ALL_FOR_NOW:
1614
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1616
fprintf_plus(stderr, "No Mandos server found, still"
1623
/* Signal handler that stops main loop after SIGTERM */
1624
static void handle_sigterm(int sig){
1629
signal_received = sig;
1630
int old_errno = errno;
1631
/* set main loop to exit */
1632
if(simple_poll != NULL){
1633
avahi_simple_poll_quit(simple_poll);
1638
__attribute__((nonnull, warn_unused_result))
1639
bool get_flags(const char *ifname, struct ifreq *ifr){
1643
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1646
perror_plus("socket");
1650
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1651
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1652
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1656
perror_plus("ioctl SIOCGIFFLAGS");
1666
__attribute__((nonnull, warn_unused_result))
1667
bool good_flags(const char *ifname, const struct ifreq *ifr){
1669
/* Reject the loopback device */
1670
if(ifr->ifr_flags & IFF_LOOPBACK){
1672
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1677
/* Accept point-to-point devices only if connect_to is specified */
1678
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1680
fprintf_plus(stderr, "Accepting point-to-point interface"
1681
" \"%s\"\n", ifname);
1685
/* Otherwise, reject non-broadcast-capable devices */
1686
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1688
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1689
" \"%s\"\n", ifname);
1693
/* Reject non-ARP interfaces (including dummy interfaces) */
1694
if(ifr->ifr_flags & IFF_NOARP){
1696
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1702
/* Accept this device */
1704
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1710
* This function determines if a directory entry in /sys/class/net
1711
* corresponds to an acceptable network device.
1712
* (This function is passed to scandir(3) as a filter function.)
1714
__attribute__((nonnull, warn_unused_result))
1715
int good_interface(const struct dirent *if_entry){
1716
if(if_entry->d_name[0] == '.'){
1721
if(not get_flags(if_entry->d_name, &ifr)){
1723
fprintf_plus(stderr, "Failed to get flags for interface "
1724
"\"%s\"\n", if_entry->d_name);
1729
if(not good_flags(if_entry->d_name, &ifr)){
1736
* This function determines if a network interface is up.
1738
__attribute__((nonnull, warn_unused_result))
1739
bool interface_is_up(const char *interface){
1741
if(not get_flags(interface, &ifr)){
1743
fprintf_plus(stderr, "Failed to get flags for interface "
1744
"\"%s\"\n", interface);
1749
return (bool)(ifr.ifr_flags & IFF_UP);
1753
* This function determines if a network interface is running
1755
__attribute__((nonnull, warn_unused_result))
1756
bool interface_is_running(const char *interface){
1758
if(not get_flags(interface, &ifr)){
1760
fprintf_plus(stderr, "Failed to get flags for interface "
1761
"\"%s\"\n", interface);
1766
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1769
__attribute__((nonnull, pure, warn_unused_result))
1770
int notdotentries(const struct dirent *direntry){
1771
/* Skip "." and ".." */
1772
if(direntry->d_name[0] == '.'
1773
and (direntry->d_name[1] == '\0'
1774
or (direntry->d_name[1] == '.'
1775
and direntry->d_name[2] == '\0'))){
1781
/* Is this directory entry a runnable program? */
1782
__attribute__((nonnull, warn_unused_result))
1783
int runnable_hook(const struct dirent *direntry){
1788
if((direntry->d_name)[0] == '\0'){
1793
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1794
"abcdefghijklmnopqrstuvwxyz"
1797
if((direntry->d_name)[sret] != '\0'){
1798
/* Contains non-allowed characters */
1800
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1806
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1809
perror_plus("Could not stat hook");
1813
if(not (S_ISREG(st.st_mode))){
1814
/* Not a regular file */
1816
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1821
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1822
/* Not executable */
1824
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1830
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1836
__attribute__((nonnull, warn_unused_result))
1837
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1838
mandos_context *mc){
1840
struct timespec now;
1841
struct timespec waited_time;
1842
intmax_t block_time;
1845
if(mc->current_server == NULL){
1847
fprintf_plus(stderr, "Wait until first server is found."
1850
ret = avahi_simple_poll_iterate(s, -1);
1853
fprintf_plus(stderr, "Check current_server if we should run"
1856
/* the current time */
1857
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1859
perror_plus("clock_gettime");
1862
/* Calculating in ms how long time between now and server
1863
who we visted longest time ago. Now - last seen. */
1864
waited_time.tv_sec = (now.tv_sec
1865
- mc->current_server->last_seen.tv_sec);
1866
waited_time.tv_nsec = (now.tv_nsec
1867
- mc->current_server->last_seen.tv_nsec);
1868
/* total time is 10s/10,000ms.
1869
Converting to s from ms by dividing by 1,000,
1870
and ns to ms by dividing by 1,000,000. */
1871
block_time = ((retry_interval
1872
- ((intmax_t)waited_time.tv_sec * 1000))
1873
- ((intmax_t)waited_time.tv_nsec / 1000000));
1876
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1880
if(block_time <= 0){
1881
ret = start_mandos_communication(mc->current_server->ip,
1882
mc->current_server->port,
1883
mc->current_server->if_index,
1884
mc->current_server->af, mc);
1886
avahi_simple_poll_quit(s);
1889
ret = clock_gettime(CLOCK_MONOTONIC,
1890
&mc->current_server->last_seen);
1892
perror_plus("clock_gettime");
1895
mc->current_server = mc->current_server->next;
1896
block_time = 0; /* Call avahi to find new Mandos
1897
servers, but don't block */
1900
ret = avahi_simple_poll_iterate(s, (int)block_time);
1903
if(ret > 0 or errno != EINTR){
1904
return (ret != 1) ? ret : 0;
1910
__attribute__((nonnull))
1911
void run_network_hooks(const char *mode, const char *interface,
1913
struct dirent **direntries = NULL;
1914
if(hookdir_fd == -1){
1915
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1917
if(hookdir_fd == -1){
1918
if(errno == ENOENT){
1920
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1921
" found\n", hookdir);
1924
perror_plus("open");
1929
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1931
perror_plus("open(\"/dev/null\", O_RDONLY)");
1934
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1935
runnable_hook, alphasort);
1937
perror_plus("scandir");
1941
struct dirent *direntry;
1943
for(int i = 0; i < numhooks; i++){
1944
direntry = direntries[i];
1946
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1949
pid_t hook_pid = fork();
1952
/* Raise privileges */
1953
errno = raise_privileges_permanently();
1955
perror_plus("Failed to raise privileges");
1962
perror_plus("setgid");
1965
/* Reset supplementary groups */
1967
ret = setgroups(0, NULL);
1969
perror_plus("setgroups");
1972
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1974
perror_plus("setenv");
1977
ret = setenv("DEVICE", interface, 1);
1979
perror_plus("setenv");
1982
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1984
perror_plus("setenv");
1987
ret = setenv("MODE", mode, 1);
1989
perror_plus("setenv");
1993
ret = asprintf(&delaystring, "%f", (double)delay);
1995
perror_plus("asprintf");
1998
ret = setenv("DELAY", delaystring, 1);
2001
perror_plus("setenv");
2005
if(connect_to != NULL){
2006
ret = setenv("CONNECT", connect_to, 1);
2008
perror_plus("setenv");
2012
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2016
perror_plus("openat");
2017
_exit(EXIT_FAILURE);
2019
if(close(hookdir_fd) == -1){
2020
perror_plus("close");
2021
_exit(EXIT_FAILURE);
2023
ret = dup2(devnull, STDIN_FILENO);
2025
perror_plus("dup2(devnull, STDIN_FILENO)");
2028
ret = close(devnull);
2030
perror_plus("close");
2033
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2035
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2038
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2040
perror_plus("fexecve");
2041
_exit(EXIT_FAILURE);
2045
perror_plus("fork");
2050
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2051
perror_plus("waitpid");
2055
if(WIFEXITED(status)){
2056
if(WEXITSTATUS(status) != 0){
2057
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2058
" with status %d\n", direntry->d_name,
2059
WEXITSTATUS(status));
2063
} else if(WIFSIGNALED(status)){
2064
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2065
" signal %d\n", direntry->d_name,
2070
fprintf_plus(stderr, "Warning: network hook \"%s\""
2071
" crashed\n", direntry->d_name);
2077
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2083
if(close(hookdir_fd) == -1){
2084
perror_plus("close");
2091
__attribute__((nonnull, warn_unused_result))
2092
int bring_up_interface(const char *const interface,
2094
int old_errno = errno;
2096
struct ifreq network;
2097
unsigned int if_index = if_nametoindex(interface);
2099
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2109
if(not interface_is_up(interface)){
2111
int ioctl_errno = 0;
2112
if(not get_flags(interface, &network)){
2114
fprintf_plus(stderr, "Failed to get flags for interface "
2115
"\"%s\"\n", interface);
2119
network.ifr_flags |= IFF_UP; /* set flag */
2121
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2124
perror_plus("socket");
2132
perror_plus("close");
2139
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2143
/* Raise privileges */
2144
ret_errno = raise_privileges();
2147
perror_plus("Failed to raise privileges");
2152
bool restore_loglevel = false;
2154
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2155
messages about the network interface to mess up the prompt */
2156
ret_linux = klogctl(8, NULL, 5);
2157
if(ret_linux == -1){
2158
perror_plus("klogctl");
2160
restore_loglevel = true;
2163
#endif /* __linux__ */
2164
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2165
ioctl_errno = errno;
2167
if(restore_loglevel){
2168
ret_linux = klogctl(7, NULL, 0);
2169
if(ret_linux == -1){
2170
perror_plus("klogctl");
2173
#endif /* __linux__ */
2175
/* If raise_privileges() succeeded above */
2177
/* Lower privileges */
2178
ret_errno = lower_privileges();
2181
perror_plus("Failed to lower privileges");
2185
/* Close the socket */
2188
perror_plus("close");
2191
if(ret_setflags == -1){
2192
errno = ioctl_errno;
2193
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2198
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2202
/* Sleep checking until interface is running.
2203
Check every 0.25s, up to total time of delay */
2204
for(int i = 0; i < delay * 4; i++){
2205
if(interface_is_running(interface)){
2208
struct timespec sleeptime = { .tv_nsec = 250000000 };
2209
ret = nanosleep(&sleeptime, NULL);
2210
if(ret == -1 and errno != EINTR){
2211
perror_plus("nanosleep");
2219
__attribute__((nonnull, warn_unused_result))
2220
int take_down_interface(const char *const interface){
2221
int old_errno = errno;
2222
struct ifreq network;
2223
unsigned int if_index = if_nametoindex(interface);
2225
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2229
if(interface_is_up(interface)){
2231
int ioctl_errno = 0;
2232
if(not get_flags(interface, &network) and debug){
2234
fprintf_plus(stderr, "Failed to get flags for interface "
2235
"\"%s\"\n", interface);
2239
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2241
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2244
perror_plus("socket");
2250
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2254
/* Raise privileges */
2255
ret_errno = raise_privileges();
2258
perror_plus("Failed to raise privileges");
2261
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2262
ioctl_errno = errno;
2264
/* If raise_privileges() succeeded above */
2266
/* Lower privileges */
2267
ret_errno = lower_privileges();
2270
perror_plus("Failed to lower privileges");
2274
/* Close the socket */
2275
int ret = close(sd);
2277
perror_plus("close");
2280
if(ret_setflags == -1){
2281
errno = ioctl_errno;
2282
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2287
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2295
int main(int argc, char *argv[]){
2296
mandos_context mc = { .server = NULL, .dh_bits = 0,
2297
.priority = "SECURE256:!CTYPE-X.509"
2298
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2299
.current_server = NULL, .interfaces = NULL,
2300
.interfaces_size = 0 };
2301
AvahiSServiceBrowser *sb = NULL;
2306
int exitcode = EXIT_SUCCESS;
2307
char *interfaces_to_take_down = NULL;
2308
size_t interfaces_to_take_down_size = 0;
2309
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2310
char old_tempdir[] = "/tmp/mandosXXXXXX";
2311
char *tempdir = NULL;
2312
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2313
const char *seckey = PATHDIR "/" SECKEY;
2314
const char *pubkey = PATHDIR "/" PUBKEY;
2315
const char *dh_params_file = NULL;
2316
char *interfaces_hooks = NULL;
2318
bool gnutls_initialized = false;
2319
bool gpgme_initialized = false;
2321
double retry_interval = 10; /* 10s between trying a server and
2322
retrying the same server again */
2324
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2325
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2330
/* Lower any group privileges we might have, just to be safe */
2334
perror_plus("setgid");
2337
/* Lower user privileges (temporarily) */
2341
perror_plus("seteuid");
2349
struct argp_option options[] = {
2350
{ .name = "debug", .key = 128,
2351
.doc = "Debug mode", .group = 3 },
2352
{ .name = "connect", .key = 'c',
2353
.arg = "ADDRESS:PORT",
2354
.doc = "Connect directly to a specific Mandos server",
2356
{ .name = "interface", .key = 'i',
2358
.doc = "Network interface that will be used to search for"
2361
{ .name = "seckey", .key = 's',
2363
.doc = "OpenPGP secret key file base name",
2365
{ .name = "pubkey", .key = 'p',
2367
.doc = "OpenPGP public key file base name",
2369
{ .name = "dh-bits", .key = 129,
2371
.doc = "Bit length of the prime number used in the"
2372
" Diffie-Hellman key exchange",
2374
{ .name = "dh-params", .key = 134,
2376
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2377
" for the Diffie-Hellman key exchange",
2379
{ .name = "priority", .key = 130,
2381
.doc = "GnuTLS priority string for the TLS handshake",
2383
{ .name = "delay", .key = 131,
2385
.doc = "Maximum delay to wait for interface startup",
2387
{ .name = "retry", .key = 132,
2389
.doc = "Retry interval used when denied by the Mandos server",
2391
{ .name = "network-hook-dir", .key = 133,
2393
.doc = "Directory where network hooks are located",
2396
* These reproduce what we would get without ARGP_NO_HELP
2398
{ .name = "help", .key = '?',
2399
.doc = "Give this help list", .group = -1 },
2400
{ .name = "usage", .key = -3,
2401
.doc = "Give a short usage message", .group = -1 },
2402
{ .name = "version", .key = 'V',
2403
.doc = "Print program version", .group = -1 },
2407
error_t parse_opt(int key, char *arg,
2408
struct argp_state *state){
2411
case 128: /* --debug */
2414
case 'c': /* --connect */
2417
case 'i': /* --interface */
2418
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2421
argp_error(state, "%s", strerror(ret_errno));
2424
case 's': /* --seckey */
2427
case 'p': /* --pubkey */
2430
case 129: /* --dh-bits */
2432
tmpmax = strtoimax(arg, &tmp, 10);
2433
if(errno != 0 or tmp == arg or *tmp != '\0'
2434
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2435
argp_error(state, "Bad number of DH bits");
2437
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2439
case 134: /* --dh-params */
2440
dh_params_file = arg;
2442
case 130: /* --priority */
2445
case 131: /* --delay */
2447
delay = strtof(arg, &tmp);
2448
if(errno != 0 or tmp == arg or *tmp != '\0'){
2449
argp_error(state, "Bad delay");
2451
case 132: /* --retry */
2453
retry_interval = strtod(arg, &tmp);
2454
if(errno != 0 or tmp == arg or *tmp != '\0'
2455
or (retry_interval * 1000) > INT_MAX
2456
or retry_interval < 0){
2457
argp_error(state, "Bad retry interval");
2460
case 133: /* --network-hook-dir */
2464
* These reproduce what we would get without ARGP_NO_HELP
2466
case '?': /* --help */
2467
argp_state_help(state, state->out_stream,
2468
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2469
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2470
case -3: /* --usage */
2471
argp_state_help(state, state->out_stream,
2472
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2473
case 'V': /* --version */
2474
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2475
exit(argp_err_exit_status);
2478
return ARGP_ERR_UNKNOWN;
2483
struct argp argp = { .options = options, .parser = parse_opt,
2485
.doc = "Mandos client -- Get and decrypt"
2486
" passwords from a Mandos server" };
2487
ret_errno = argp_parse(&argp, argc, argv,
2488
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
596
case AVAHI_BROWSER_FAILURE:
598
fprintf(stderr, "(Browser) %s\n",
599
avahi_strerror(avahi_server_errno(server)));
600
avahi_simple_poll_quit(simple_poll);
603
case AVAHI_BROWSER_NEW:
604
/* We ignore the returned resolver object. In the callback
605
function we free it. If the server is terminated before
606
the callback function is called the server will free
607
the resolver for us. */
609
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
611
AVAHI_PROTO_INET6, 0,
612
resolve_callback, s)))
613
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
614
avahi_strerror(avahi_server_errno(s)));
617
case AVAHI_BROWSER_REMOVE:
620
case AVAHI_BROWSER_ALL_FOR_NOW:
621
case AVAHI_BROWSER_CACHE_EXHAUSTED:
626
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
2495
perror_plus("argp_parse");
2496
exitcode = EX_OSERR;
2499
exitcode = EX_USAGE;
2505
/* Work around Debian bug #633582:
2506
<https://bugs.debian.org/633582> */
2508
/* Re-raise privileges */
2509
ret = raise_privileges();
2512
perror_plus("Failed to raise privileges");
2516
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2517
int seckey_fd = open(seckey, O_RDONLY);
2518
if(seckey_fd == -1){
2519
perror_plus("open");
2521
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2523
perror_plus("fstat");
2525
if(S_ISREG(st.st_mode)
2526
and st.st_uid == 0 and st.st_gid == 0){
2527
ret = fchown(seckey_fd, uid, gid);
2529
perror_plus("fchown");
2537
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2538
int pubkey_fd = open(pubkey, O_RDONLY);
2539
if(pubkey_fd == -1){
2540
perror_plus("open");
2542
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2544
perror_plus("fstat");
2546
if(S_ISREG(st.st_mode)
2547
and st.st_uid == 0 and st.st_gid == 0){
2548
ret = fchown(pubkey_fd, uid, gid);
2550
perror_plus("fchown");
2558
if(dh_params_file != NULL
2559
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2560
int dhparams_fd = open(dh_params_file, O_RDONLY);
2561
if(dhparams_fd == -1){
2562
perror_plus("open");
2564
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2566
perror_plus("fstat");
2568
if(S_ISREG(st.st_mode)
2569
and st.st_uid == 0 and st.st_gid == 0){
2570
ret = fchown(dhparams_fd, uid, gid);
2572
perror_plus("fchown");
2580
/* Lower privileges */
2581
ret = lower_privileges();
2584
perror_plus("Failed to lower privileges");
2589
/* Remove invalid interface names (except "none") */
2591
char *interface = NULL;
2592
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2594
if(strcmp(interface, "none") != 0
2595
and if_nametoindex(interface) == 0){
2596
if(interface[0] != '\0'){
2597
fprintf_plus(stderr, "Not using nonexisting interface"
2598
" \"%s\"\n", interface);
2600
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2606
/* Run network hooks */
2608
if(mc.interfaces != NULL){
2609
interfaces_hooks = malloc(mc.interfaces_size);
2610
if(interfaces_hooks == NULL){
2611
perror_plus("malloc");
2614
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2615
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2617
run_network_hooks("start", interfaces_hooks != NULL ?
2618
interfaces_hooks : "", delay);
2622
avahi_set_log_function(empty_log);
2625
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2626
from the signal handler */
2627
/* Initialize the pseudo-RNG for Avahi */
2628
srand((unsigned int) time(NULL));
2629
simple_poll = avahi_simple_poll_new();
2630
if(simple_poll == NULL){
2631
fprintf_plus(stderr,
2632
"Avahi: Failed to create simple poll object.\n");
2633
exitcode = EX_UNAVAILABLE;
2637
sigemptyset(&sigterm_action.sa_mask);
2638
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2640
perror_plus("sigaddset");
2641
exitcode = EX_OSERR;
2644
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2646
perror_plus("sigaddset");
2647
exitcode = EX_OSERR;
2650
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2652
perror_plus("sigaddset");
2653
exitcode = EX_OSERR;
2656
/* Need to check if the handler is SIG_IGN before handling:
2657
| [[info:libc:Initial Signal Actions]] |
2658
| [[info:libc:Basic Signal Handling]] |
2660
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2662
perror_plus("sigaction");
2665
if(old_sigterm_action.sa_handler != SIG_IGN){
2666
ret = sigaction(SIGINT, &sigterm_action, NULL);
2668
perror_plus("sigaction");
2669
exitcode = EX_OSERR;
2673
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2675
perror_plus("sigaction");
2678
if(old_sigterm_action.sa_handler != SIG_IGN){
2679
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2681
perror_plus("sigaction");
2682
exitcode = EX_OSERR;
2686
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2688
perror_plus("sigaction");
2691
if(old_sigterm_action.sa_handler != SIG_IGN){
2692
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2694
perror_plus("sigaction");
2695
exitcode = EX_OSERR;
2700
/* If no interfaces were specified, make a list */
2701
if(mc.interfaces == NULL){
2702
struct dirent **direntries = NULL;
2703
/* Look for any good interfaces */
2704
ret = scandir(sys_class_net, &direntries, good_interface,
2707
/* Add all found interfaces to interfaces list */
2708
for(int i = 0; i < ret; ++i){
2709
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2710
direntries[i]->d_name);
2713
perror_plus("argz_add");
2714
free(direntries[i]);
2718
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2719
direntries[i]->d_name);
2721
free(direntries[i]);
2728
fprintf_plus(stderr, "Could not find a network interface\n");
2729
exitcode = EXIT_FAILURE;
2734
/* Bring up interfaces which are down, and remove any "none"s */
2736
char *interface = NULL;
2737
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2739
/* If interface name is "none", stop bringing up interfaces.
2740
Also remove all instances of "none" from the list */
2741
if(strcmp(interface, "none") == 0){
2742
argz_delete(&mc.interfaces, &mc.interfaces_size,
2745
while((interface = argz_next(mc.interfaces,
2746
mc.interfaces_size, interface))){
2747
if(strcmp(interface, "none") == 0){
2748
argz_delete(&mc.interfaces, &mc.interfaces_size,
2755
bool interface_was_up = interface_is_up(interface);
2756
errno = bring_up_interface(interface, delay);
2757
if(not interface_was_up){
2759
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2760
" %s\n", interface, strerror(errno));
2762
errno = argz_add(&interfaces_to_take_down,
2763
&interfaces_to_take_down_size,
2766
perror_plus("argz_add");
2771
if(debug and (interfaces_to_take_down == NULL)){
2772
fprintf_plus(stderr, "No interfaces were brought up\n");
2776
/* If we only got one interface, explicitly use only that one */
2777
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2779
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2782
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2789
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2791
fprintf_plus(stderr, "init_gnutls_global failed\n");
2792
exitcode = EX_UNAVAILABLE;
2795
gnutls_initialized = true;
2802
/* Try /run/tmp before /tmp */
2803
tempdir = mkdtemp(run_tempdir);
2804
if(tempdir == NULL and errno == ENOENT){
2806
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2807
run_tempdir, old_tempdir);
2809
tempdir = mkdtemp(old_tempdir);
2811
if(tempdir == NULL){
2812
perror_plus("mkdtemp");
2820
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2821
fprintf_plus(stderr, "init_gpgme failed\n");
2822
exitcode = EX_UNAVAILABLE;
2825
gpgme_initialized = true;
2832
if(connect_to != NULL){
2833
/* Connect directly, do not use Zeroconf */
2834
/* (Mainly meant for debugging) */
2835
char *address = strrchr(connect_to, ':');
2837
if(address == NULL){
2838
fprintf_plus(stderr, "No colon in address\n");
2839
exitcode = EX_USAGE;
2849
tmpmax = strtoimax(address+1, &tmp, 10);
2850
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2851
or tmpmax != (in_port_t)tmpmax){
2852
fprintf_plus(stderr, "Bad port number\n");
2853
exitcode = EX_USAGE;
2861
port = (in_port_t)tmpmax;
2863
/* Colon in address indicates IPv6 */
2865
if(strchr(connect_to, ':') != NULL){
2867
/* Accept [] around IPv6 address - see RFC 5952 */
2868
if(connect_to[0] == '[' and address[-1] == ']')
2876
address = connect_to;
2882
while(not quit_now){
2883
ret = start_mandos_communication(address, port, if_index, af,
2885
if(quit_now or ret == 0){
2889
fprintf_plus(stderr, "Retrying in %d seconds\n",
2890
(int)retry_interval);
2892
sleep((unsigned int)retry_interval);
2896
exitcode = EXIT_SUCCESS;
627
2907
AvahiServerConfig config;
628
AvahiSServiceBrowser *sb = NULL;
631
int returncode = EXIT_SUCCESS;
632
const char *interface = "eth0";
633
unsigned int if_index;
634
char *connect_to = NULL;
637
static struct option long_options[] = {
638
{"debug", no_argument, (int *)&debug, 1},
639
{"connect", required_argument, 0, 'c'},
640
{"interface", required_argument, 0, 'i'},
643
int option_index = 0;
644
ret = getopt_long (argc, argv, "i:", long_options,
665
if_index = if_nametoindex(interface);
667
fprintf(stderr, "No such interface: \"%s\"\n", interface);
671
if(connect_to != NULL){
672
/* Connect directly, do not use Zeroconf */
673
/* (Mainly meant for debugging) */
674
char *address = strrchr(connect_to, ':');
676
fprintf(stderr, "No colon in address\n");
680
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
682
perror("Bad port number");
686
address = connect_to;
687
ret = start_mandos_communication(address, port, if_index);
696
avahi_set_log_function(empty_log);
699
/* Initialize the psuedo-RNG */
700
srand((unsigned int) time(NULL));
702
/* Allocate main loop object */
703
if (!(simple_poll = avahi_simple_poll_new())) {
704
fprintf(stderr, "Failed to create simple poll object.\n");
709
/* Do not publish any local records */
2908
/* Do not publish any local Zeroconf records */
710
2909
avahi_server_config_init(&config);
711
2910
config.publish_hinfo = 0;
712
2911
config.publish_addresses = 0;
713
2912
config.publish_workstation = 0;
714
2913
config.publish_domain = 0;
716
2915
/* Allocate a new server */
717
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
718
&config, NULL, NULL, &error);
720
/* Free the configuration data */
2916
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2917
&config, NULL, NULL, &ret);
2919
/* Free the Avahi configuration data */
721
2920
avahi_server_config_free(&config);
723
/* Check if creating the server object succeeded */
725
fprintf(stderr, "Failed to create server: %s\n",
726
avahi_strerror(error));
727
returncode = EXIT_FAILURE;
731
/* Create the service browser */
732
sb = avahi_s_service_browser_new(server, (AvahiIfIndex)if_index,
734
"_mandos._tcp", NULL, 0,
735
browse_callback, server);
737
fprintf(stderr, "Failed to create service browser: %s\n",
738
avahi_strerror(avahi_server_errno(server)));
739
returncode = EXIT_FAILURE;
743
/* Run the main loop */
746
fprintf(stderr, "Starting avahi loop search\n");
749
avahi_simple_poll_loop(simple_poll);
754
fprintf(stderr, "%s exiting\n", argv[0]);
759
avahi_s_service_browser_free(sb);
762
avahi_server_free(server);
765
avahi_simple_poll_free(simple_poll);
2923
/* Check if creating the Avahi server object succeeded */
2924
if(mc.server == NULL){
2925
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2926
avahi_strerror(ret));
2927
exitcode = EX_UNAVAILABLE;
2935
/* Create the Avahi service browser */
2936
sb = avahi_s_service_browser_new(mc.server, if_index,
2937
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2938
NULL, 0, browse_callback,
2941
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2942
avahi_strerror(avahi_server_errno(mc.server)));
2943
exitcode = EX_UNAVAILABLE;
2951
/* Run the main loop */
2954
fprintf_plus(stderr, "Starting Avahi loop search\n");
2957
ret = avahi_loop_with_timeout(simple_poll,
2958
(int)(retry_interval * 1000), &mc);
2960
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2961
(ret == 0) ? "successfully" : "with error");
2967
if(signal_received){
2968
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
2969
argv[0], signal_received,
2970
strsignal(signal_received));
2972
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2976
/* Cleanup things */
2977
free(mc.interfaces);
2980
avahi_s_service_browser_free(sb);
2982
if(mc.server != NULL)
2983
avahi_server_free(mc.server);
2985
if(simple_poll != NULL)
2986
avahi_simple_poll_free(simple_poll);
2988
if(gnutls_initialized){
2989
gnutls_certificate_free_credentials(mc.cred);
2990
gnutls_dh_params_deinit(mc.dh_params);
2993
if(gpgme_initialized){
2994
gpgme_release(mc.ctx);
2997
/* Cleans up the circular linked list of Mandos servers the client
2999
if(mc.current_server != NULL){
3000
mc.current_server->prev->next = NULL;
3001
while(mc.current_server != NULL){
3002
server *next = mc.current_server->next;
3004
#pragma GCC diagnostic push
3005
#pragma GCC diagnostic ignored "-Wcast-qual"
3007
free((char *)(mc.current_server->ip));
3009
#pragma GCC diagnostic pop
3011
free(mc.current_server);
3012
mc.current_server = next;
3016
/* Re-raise privileges */
3018
ret = raise_privileges();
3021
perror_plus("Failed to raise privileges");
3024
/* Run network hooks */
3025
run_network_hooks("stop", interfaces_hooks != NULL ?
3026
interfaces_hooks : "", delay);
3028
/* Take down the network interfaces which were brought up */
3030
char *interface = NULL;
3031
while((interface = argz_next(interfaces_to_take_down,
3032
interfaces_to_take_down_size,
3034
ret = take_down_interface(interface);
3037
perror_plus("Failed to take down interface");
3040
if(debug and (interfaces_to_take_down == NULL)){
3041
fprintf_plus(stderr, "No interfaces needed to be taken"
3047
ret = lower_privileges_permanently();
3050
perror_plus("Failed to lower privileges permanently");
3054
free(interfaces_to_take_down);
3055
free(interfaces_hooks);
3057
void clean_dir_at(int base, const char * const dirname,
3059
struct dirent **direntries = NULL;
3061
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3067
perror_plus("open");
3070
int numentries = scandirat(dir_fd, ".", &direntries,
3071
notdotentries, alphasort);
3072
if(numentries >= 0){
3073
for(int i = 0; i < numentries; i++){
3075
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3076
dirname, direntries[i]->d_name);
3078
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3080
if(errno == EISDIR){
3081
dret = unlinkat(dir_fd, direntries[i]->d_name,
3084
if((dret == -1) and (errno == ENOTEMPTY)
3085
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3086
== 0) and (level == 0)){
3087
/* Recurse only in this special case */
3088
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3091
if((dret == -1) and (errno != ENOENT)){
3092
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3093
direntries[i]->d_name, strerror(errno));
3096
free(direntries[i]);
3099
/* need to clean even if 0 because man page doesn't specify */
3101
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3102
if(dret == -1 and errno != ENOENT){
3103
perror_plus("rmdir");
3106
perror_plus("scandirat");
3111
/* Removes the GPGME temp directory and all files inside */
3112
if(tempdir != NULL){
3113
clean_dir_at(-1, tempdir, 0);
3117
sigemptyset(&old_sigterm_action.sa_mask);
3118
old_sigterm_action.sa_handler = SIG_DFL;
3119
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3120
&old_sigterm_action,
3123
perror_plus("sigaction");
3126
ret = raise(signal_received);
3127
} while(ret != 0 and errno == EINTR);
3129
perror_plus("raise");
3132
TEMP_FAILURE_RETRY(pause());