49
111
#include <avahi-common/malloc.h>
50
112
#include <avahi-common/error.h>
53
#include <sys/types.h> /* socket(), inet_pton() */
54
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
55
struct in6_addr, inet_pton() */
56
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
57
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
59
#include <unistd.h> /* close() */
60
#include <netinet/in.h>
61
#include <stdbool.h> /* true */
62
#include <string.h> /* memset */
63
#include <arpa/inet.h> /* inet_pton() */
64
#include <iso646.h> /* not */
67
#include <errno.h> /* perror() */
115
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
118
init_gnutls_session(),
120
#include <gnutls/openpgp.h>
121
/* gnutls_certificate_set_openpgp_key_file(),
122
GNUTLS_OPENPGP_FMT_BASE64 */
125
#include <gpgme.h> /* All GPGME types, constants and
128
GPGME_PROTOCOL_OpenPGP,
73
131
#define BUFFER_SIZE 256
76
static const char *certdir = "/conf/conf.d/mandos";
77
static const char *certfile = "openpgp-client.txt";
78
static const char *certkey = "openpgp-client-key.txt";
133
#define PATHDIR "/conf/conf.d/mandos"
134
#define SECKEY "seckey.txt"
135
#define PUBKEY "pubkey.txt"
136
#define HOOKDIR "/lib/mandos/network-hooks.d"
80
138
bool debug = false;
82
const char mandos_protocol_version[] = "1";
139
static const char mandos_protocol_version[] = "1";
140
const char *argp_program_version = "mandos-client " VERSION;
141
const char *argp_program_bug_address = "<mandos@recompile.se>";
142
static const char sys_class_net[] = "/sys/class/net";
143
char *connect_to = NULL;
144
const char *hookdir = HOOKDIR;
149
/* Doubly linked list that need to be circularly linked when used */
150
typedef struct server{
153
AvahiIfIndex if_index;
155
struct timespec last_seen;
160
/* Used for passing in values through the Avahi callback functions */
85
AvahiSimplePoll *simple_poll;
86
162
AvahiServer *server;
87
163
gnutls_certificate_credentials_t cred;
88
164
unsigned int dh_bits;
165
gnutls_dh_params_t dh_params;
89
166
const char *priority;
168
server *current_server;
170
size_t interfaces_size;
92
size_t adjustbuffer(char *buffer, size_t buffer_length,
93
size_t buffer_capacity){
94
if (buffer_length + BUFFER_SIZE > buffer_capacity){
95
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
173
/* global so signal handler can reach it*/
174
AvahiSimplePoll *simple_poll;
176
sig_atomic_t quit_now = 0;
177
int signal_received = 0;
179
/* Function to use when printing errors */
180
void perror_plus(const char *print_text){
182
fprintf(stderr, "Mandos plugin %s: ",
183
program_invocation_short_name);
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
189
int fprintf_plus(FILE *stream, const char *format, ...){
191
va_start (ap, format);
193
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
194
program_invocation_short_name));
195
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
199
* Make additional room in "buffer" for at least BUFFER_SIZE more
200
* bytes. "buffer_capacity" is how much is currently allocated,
201
* "buffer_length" is how much is already used.
203
__attribute__((nonnull, warn_unused_result))
204
size_t incbuffer(char **buffer, size_t buffer_length,
205
size_t buffer_capacity){
206
if(buffer_length + BUFFER_SIZE > buffer_capacity){
207
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
209
int old_errno = errno;
99
216
buffer_capacity += BUFFER_SIZE;
101
218
return buffer_capacity;
104
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
106
const char *homedir){
107
gpgme_data_t dh_crypto, dh_plain;
221
/* Add server to set of servers to retry periodically */
222
__attribute__((nonnull, warn_unused_result))
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
224
int af, server **current_server){
226
server *new_server = malloc(sizeof(server));
227
if(new_server == NULL){
228
perror_plus("malloc");
231
*new_server = (server){ .ip = strdup(ip),
233
.if_index = if_index,
235
if(new_server->ip == NULL){
236
perror_plus("strdup");
240
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
242
perror_plus("clock_gettime");
244
#pragma GCC diagnostic push
245
#pragma GCC diagnostic ignored "-Wcast-qual"
247
free((char *)(new_server->ip));
249
#pragma GCC diagnostic pop
254
/* Special case of first server */
255
if(*current_server == NULL){
256
new_server->next = new_server;
257
new_server->prev = new_server;
258
*current_server = new_server;
260
/* Place the new server last in the list */
261
new_server->next = *current_server;
262
new_server->prev = (*current_server)->prev;
263
new_server->prev->next = new_server;
264
(*current_server)->prev = new_server;
272
__attribute__((nonnull, warn_unused_result))
273
static bool init_gpgme(const char * const seckey,
274
const char * const pubkey,
275
const char * const tempdir,
109
277
gpgme_error_t rc;
111
size_t new_packet_capacity = 0;
112
ssize_t new_packet_length = 0;
113
278
gpgme_engine_info_t engine_info;
116
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
281
* Helper function to insert pub and seckey to the engine keyring.
283
bool import_key(const char * const filename){
286
gpgme_data_t pgp_data;
288
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
294
rc = gpgme_data_new_from_fd(&pgp_data, fd);
295
if(rc != GPG_ERR_NO_ERROR){
296
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
297
gpgme_strsource(rc), gpgme_strerror(rc));
301
rc = gpgme_op_import(mc->ctx, pgp_data);
302
if(rc != GPG_ERR_NO_ERROR){
303
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
304
gpgme_strsource(rc), gpgme_strerror(rc));
308
ret = (int)TEMP_FAILURE_RETRY(close(fd));
310
perror_plus("close");
312
gpgme_data_release(pgp_data);
317
fprintf_plus(stderr, "Initializing GPGME\n");
120
321
gpgme_check_version(NULL);
121
322
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
122
if (rc != GPG_ERR_NO_ERROR){
123
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
124
gpgme_strsource(rc), gpgme_strerror(rc));
323
if(rc != GPG_ERR_NO_ERROR){
324
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
325
gpgme_strsource(rc), gpgme_strerror(rc));
128
/* Set GPGME home directory */
129
rc = gpgme_get_engine_info (&engine_info);
130
if (rc != GPG_ERR_NO_ERROR){
131
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
132
gpgme_strsource(rc), gpgme_strerror(rc));
329
/* Set GPGME home directory for the OpenPGP engine only */
330
rc = gpgme_get_engine_info(&engine_info);
331
if(rc != GPG_ERR_NO_ERROR){
332
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
333
gpgme_strsource(rc), gpgme_strerror(rc));
135
336
while(engine_info != NULL){
136
337
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
137
338
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
138
engine_info->file_name, homedir);
339
engine_info->file_name, tempdir);
141
342
engine_info = engine_info->next;
143
344
if(engine_info == NULL){
144
fprintf(stderr, "Could not set home dir to %s\n", homedir);
148
/* Create new GPGME data buffer from packet buffer */
149
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
150
if (rc != GPG_ERR_NO_ERROR){
151
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
152
gpgme_strsource(rc), gpgme_strerror(rc));
345
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
350
/* Create new GPGME "context" */
351
rc = gpgme_new(&(mc->ctx));
352
if(rc != GPG_ERR_NO_ERROR){
353
fprintf_plus(stderr, "Mandos plugin mandos-client: "
354
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
359
if(not import_key(pubkey) or not import_key(seckey)){
367
* Decrypt OpenPGP data.
368
* Returns -1 on error
370
__attribute__((nonnull, warn_unused_result))
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
375
gpgme_data_t dh_crypto, dh_plain;
378
size_t plaintext_capacity = 0;
379
ssize_t plaintext_length = 0;
382
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
385
/* Create new GPGME data buffer from memory cryptotext */
386
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
388
if(rc != GPG_ERR_NO_ERROR){
389
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
390
gpgme_strsource(rc), gpgme_strerror(rc));
156
394
/* Create new empty GPGME data buffer for the plaintext */
157
395
rc = gpgme_data_new(&dh_plain);
158
if (rc != GPG_ERR_NO_ERROR){
159
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
160
gpgme_strsource(rc), gpgme_strerror(rc));
164
/* Create new GPGME "context" */
165
rc = gpgme_new(&ctx);
166
if (rc != GPG_ERR_NO_ERROR){
167
fprintf(stderr, "bad gpgme_new: %s: %s\n",
168
gpgme_strsource(rc), gpgme_strerror(rc));
172
/* Decrypt data from the FILE pointer to the plaintext data
174
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
175
if (rc != GPG_ERR_NO_ERROR){
176
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
177
gpgme_strsource(rc), gpgme_strerror(rc));
182
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
186
gpgme_decrypt_result_t result;
187
result = gpgme_op_decrypt_result(ctx);
189
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
191
fprintf(stderr, "Unsupported algorithm: %s\n",
192
result->unsupported_algorithm);
193
fprintf(stderr, "Wrong key usage: %d\n",
194
result->wrong_key_usage);
195
if(result->file_name != NULL){
196
fprintf(stderr, "File name: %s\n", result->file_name);
198
gpgme_recipient_t recipient;
199
recipient = result->recipients;
396
if(rc != GPG_ERR_NO_ERROR){
397
fprintf_plus(stderr, "Mandos plugin mandos-client: "
398
"bad gpgme_data_new: %s: %s\n",
399
gpgme_strsource(rc), gpgme_strerror(rc));
400
gpgme_data_release(dh_crypto);
404
/* Decrypt data from the cryptotext data buffer to the plaintext
406
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
407
if(rc != GPG_ERR_NO_ERROR){
408
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
409
gpgme_strsource(rc), gpgme_strerror(rc));
410
plaintext_length = -1;
412
gpgme_decrypt_result_t result;
413
result = gpgme_op_decrypt_result(mc->ctx);
415
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
417
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
418
result->unsupported_algorithm);
419
fprintf_plus(stderr, "Wrong key usage: %u\n",
420
result->wrong_key_usage);
421
if(result->file_name != NULL){
422
fprintf_plus(stderr, "File name: %s\n", result->file_name);
424
gpgme_recipient_t recipient;
425
recipient = result->recipients;
201
426
while(recipient != NULL){
202
fprintf(stderr, "Public key algorithm: %s\n",
203
gpgme_pubkey_algo_name(recipient->pubkey_algo));
204
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
205
fprintf(stderr, "Secret key available: %s\n",
206
recipient->status == GPG_ERR_NO_SECKEY
427
fprintf_plus(stderr, "Public key algorithm: %s\n",
428
gpgme_pubkey_algo_name
429
(recipient->pubkey_algo));
430
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
431
fprintf_plus(stderr, "Secret key available: %s\n",
432
recipient->status == GPG_ERR_NO_SECKEY
208
434
recipient = recipient->next;
214
/* Delete the GPGME FILE pointer cryptotext data buffer */
215
gpgme_data_release(dh_crypto);
442
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
217
445
/* Seek back to the beginning of the GPGME plaintext data buffer */
218
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
219
perror("pgpme_data_seek");
446
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
447
perror_plus("gpgme_data_seek");
448
plaintext_length = -1;
224
new_packet_capacity = adjustbuffer(*new_packet, new_packet_length,
225
new_packet_capacity);
226
if (new_packet_capacity == 0){
227
perror("adjustbuffer");
230
new_packet_capacity += BUFFER_SIZE;
454
plaintext_capacity = incbuffer(plaintext,
455
(size_t)plaintext_length,
457
if(plaintext_capacity == 0){
458
perror_plus("incbuffer");
459
plaintext_length = -1;
233
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
463
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
235
465
/* Print the data, if any */
240
perror("gpgme_data_read");
243
new_packet_length += ret;
246
/* FIXME: check characters before printing to screen so to not print
247
terminal control characters */
249
/* fprintf(stderr, "decrypted password is: "); */
250
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
251
/* fprintf(stderr, "\n"); */
471
perror_plus("gpgme_data_read");
472
plaintext_length = -1;
475
plaintext_length += ret;
479
fprintf_plus(stderr, "Decrypted password is: ");
480
for(ssize_t i = 0; i < plaintext_length; i++){
481
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
483
fprintf(stderr, "\n");
488
/* Delete the GPGME cryptotext data buffer */
489
gpgme_data_release(dh_crypto);
254
491
/* Delete the GPGME plaintext data buffer */
255
492
gpgme_data_release(dh_plain);
256
return new_packet_length;
259
static const char * safer_gnutls_strerror (int value) {
260
const char *ret = gnutls_strerror (value);
493
return plaintext_length;
496
__attribute__((warn_unused_result, const))
497
static const char *safe_string(const char *str){
503
__attribute__((warn_unused_result))
504
static const char *safer_gnutls_strerror(int value){
505
const char *ret = gnutls_strerror(value);
506
return safe_string(ret);
509
/* GnuTLS log function callback */
510
__attribute__((nonnull))
266
511
static void debuggnutls(__attribute__((unused)) int level,
267
512
const char* string){
268
fprintf(stderr, "%s", string);
513
fprintf_plus(stderr, "GnuTLS: %s", string);
271
static int initgnutls(mandos_context *mc){
516
__attribute__((nonnull, warn_unused_result))
517
static int init_gnutls_global(const char *pubkeyfilename,
518
const char *seckeyfilename,
519
const char *dhparamsfilename,
276
fprintf(stderr, "Initializing GnuTLS\n");
525
fprintf_plus(stderr, "Initializing GnuTLS\n");
279
if ((ret = gnutls_global_init ())
280
!= GNUTLS_E_SUCCESS) {
281
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
528
ret = gnutls_global_init();
529
if(ret != GNUTLS_E_SUCCESS){
530
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
531
safer_gnutls_strerror(ret));
536
/* "Use a log level over 10 to enable all debugging options."
286
539
gnutls_global_set_log_level(11);
287
540
gnutls_global_set_log_function(debuggnutls);
290
/* openpgp credentials */
291
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
292
!= GNUTLS_E_SUCCESS) {
293
fprintf (stderr, "memory error: %s\n",
294
safer_gnutls_strerror(ret));
543
/* OpenPGP credentials */
544
ret = gnutls_certificate_allocate_credentials(&mc->cred);
545
if(ret != GNUTLS_E_SUCCESS){
546
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
547
safer_gnutls_strerror(ret));
548
gnutls_global_deinit();
299
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
300
" and keyfile %s as GnuTLS credentials\n", certfile,
553
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
554
" secret key %s as GnuTLS credentials\n",
304
559
ret = gnutls_certificate_set_openpgp_key_file
305
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
306
if (ret != GNUTLS_E_SUCCESS) {
308
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
310
ret, certfile, certkey);
311
fprintf(stdout, "The Error is: %s\n",
312
safer_gnutls_strerror(ret));
316
//GnuTLS server initialization
317
if ((ret = gnutls_dh_params_init (&es->dh_params))
318
!= GNUTLS_E_SUCCESS) {
319
fprintf (stderr, "Error in dh parameter initialization: %s\n",
320
safer_gnutls_strerror(ret));
324
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
325
!= GNUTLS_E_SUCCESS) {
326
fprintf (stderr, "Error in prime generation: %s\n",
327
safer_gnutls_strerror(ret));
331
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
333
// GnuTLS session creation
334
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
335
!= GNUTLS_E_SUCCESS){
336
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
337
safer_gnutls_strerror(ret));
340
if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
341
!= GNUTLS_E_SUCCESS) {
342
fprintf(stderr, "Syntax error at: %s\n", err);
343
fprintf(stderr, "GnuTLS error: %s\n",
344
safer_gnutls_strerror(ret));
348
if ((ret = gnutls_credentials_set
349
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
350
!= GNUTLS_E_SUCCESS) {
351
fprintf(stderr, "Error setting a credentials set: %s\n",
352
safer_gnutls_strerror(ret));
560
(mc->cred, pubkeyfilename, seckeyfilename,
561
GNUTLS_OPENPGP_FMT_BASE64);
562
if(ret != GNUTLS_E_SUCCESS){
564
"Error[%d] while reading the OpenPGP key pair ('%s',"
565
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
566
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
567
safer_gnutls_strerror(ret));
571
/* GnuTLS server initialization */
572
ret = gnutls_dh_params_init(&mc->dh_params);
573
if(ret != GNUTLS_E_SUCCESS){
574
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
575
" initialization: %s\n",
576
safer_gnutls_strerror(ret));
579
/* If a Diffie-Hellman parameters file was given, try to use it */
580
if(dhparamsfilename != NULL){
581
gnutls_datum_t params = { .data = NULL, .size = 0 };
583
int dhpfile = open(dhparamsfilename, O_RDONLY);
586
dhparamsfilename = NULL;
589
size_t params_capacity = 0;
591
params_capacity = incbuffer((char **)¶ms.data,
593
(size_t)params_capacity);
594
if(params_capacity == 0){
595
perror_plus("incbuffer");
598
dhparamsfilename = NULL;
601
ssize_t bytes_read = read(dhpfile,
602
params.data + params.size,
608
/* check bytes_read for failure */
613
dhparamsfilename = NULL;
616
params.size += (unsigned int)bytes_read;
618
if(params.data == NULL){
619
dhparamsfilename = NULL;
621
if(dhparamsfilename == NULL){
624
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
625
GNUTLS_X509_FMT_PEM);
626
if(ret != GNUTLS_E_SUCCESS){
627
fprintf_plus(stderr, "Failed to parse DH parameters in file"
628
" \"%s\": %s\n", dhparamsfilename,
629
safer_gnutls_strerror(ret));
630
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_global_deinit();
759
gnutls_dh_params_deinit(mc->dh_params);
763
__attribute__((nonnull, warn_unused_result))
764
static int init_gnutls_session(gnutls_session_t *session,
767
/* GnuTLS session creation */
769
ret = gnutls_init(session, GNUTLS_SERVER);
773
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
774
if(ret != GNUTLS_E_SUCCESS){
776
"Error in GnuTLS session initialization: %s\n",
777
safer_gnutls_strerror(ret));
783
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
785
gnutls_deinit(*session);
788
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
789
if(ret != GNUTLS_E_SUCCESS){
790
fprintf_plus(stderr, "Syntax error at: %s\n", err);
791
fprintf_plus(stderr, "GnuTLS error: %s\n",
792
safer_gnutls_strerror(ret));
793
gnutls_deinit(*session);
799
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
802
gnutls_deinit(*session);
805
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
806
if(ret != GNUTLS_E_SUCCESS){
807
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
808
safer_gnutls_strerror(ret));
809
gnutls_deinit(*session);
356
813
/* ignore client certificate if any. */
357
gnutls_certificate_server_set_request (es->session,
360
gnutls_dh_set_prime_bits (es->session, DH_BITS);
814
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
819
/* Avahi log function callback */
365
820
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
366
821
__attribute__((unused)) const char *txt){}
368
static int start_mandos_communication(const char *ip, uint16_t port,
823
/* Set effective uid to 0, return errno */
824
__attribute__((warn_unused_result))
825
error_t raise_privileges(void){
826
error_t old_errno = errno;
827
error_t ret_errno = 0;
828
if(seteuid(0) == -1){
835
/* Set effective and real user ID to 0. Return errno. */
836
__attribute__((warn_unused_result))
837
error_t raise_privileges_permanently(void){
838
error_t old_errno = errno;
839
error_t ret_errno = raise_privileges();
851
/* Set effective user ID to unprivileged saved user ID */
852
__attribute__((warn_unused_result))
853
error_t lower_privileges(void){
854
error_t old_errno = errno;
855
error_t ret_errno = 0;
856
if(seteuid(uid) == -1){
863
/* Lower privileges permanently */
864
__attribute__((warn_unused_result))
865
error_t lower_privileges_permanently(void){
866
error_t old_errno = errno;
867
error_t ret_errno = 0;
868
if(setuid(uid) == -1){
875
/* Helper function to add_local_route() and delete_local_route() */
876
__attribute__((nonnull, warn_unused_result))
877
static bool add_delete_local_route(const bool add,
879
AvahiIfIndex if_index){
881
char helper[] = "mandos-client-iprouteadddel";
882
char add_arg[] = "add";
883
char delete_arg[] = "delete";
884
char debug_flag[] = "--debug";
885
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
886
if(pluginhelperdir == NULL){
888
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
889
" variable not set; cannot run helper\n");
894
char interface[IF_NAMESIZE];
895
if(if_indextoname((unsigned int)if_index, interface) == NULL){
896
perror_plus("if_indextoname");
900
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
902
perror_plus("open(\"/dev/null\", O_RDONLY)");
908
/* Raise privileges */
909
errno = raise_privileges_permanently();
911
perror_plus("Failed to raise privileges");
912
/* _exit(EX_NOPERM); */
918
perror_plus("setgid");
921
/* Reset supplementary groups */
923
ret = setgroups(0, NULL);
925
perror_plus("setgroups");
929
ret = dup2(devnull, STDIN_FILENO);
931
perror_plus("dup2(devnull, STDIN_FILENO)");
934
ret = (int)TEMP_FAILURE_RETRY(close(devnull));
936
perror_plus("close");
939
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
941
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
944
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
949
if(helperdir_fd == -1){
951
_exit(EX_UNAVAILABLE);
953
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
956
perror_plus("openat");
957
_exit(EX_UNAVAILABLE);
959
TEMP_FAILURE_RETRY(close(helperdir_fd));
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,
369
1043
AvahiIfIndex if_index,
372
struct sockaddr_in6 to;
373
encrypted_session es;
1044
int af, mandos_context *mc){
1045
int ret, tcp_sd = -1;
1047
struct sockaddr_storage to;
374
1048
char *buffer = NULL;
375
char *decrypted_buffer;
1049
char *decrypted_buffer = NULL;
376
1050
size_t buffer_length = 0;
377
1051
size_t buffer_capacity = 0;
378
ssize_t decrypted_buffer_size;
381
char interface[IF_NAMESIZE];
384
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
388
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
395
if(if_indextoname((unsigned int)if_index, interface) == NULL){
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, mc->interfaces_size,
1086
if(if_nametoindex(interface) == (unsigned int)if_index){
1093
/* This interface does not match any in the list, so we don't
1094
connect to the server */
397
perror("if_indextoname");
1096
char interface[IF_NAMESIZE];
1097
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1098
perror_plus("if_indextoname");
1100
fprintf_plus(stderr, "Skipping server on non-used interface"
1102
if_indextoname((unsigned int)if_index,
402
fprintf(stderr, "Binding to interface %s\n", interface);
405
memset(&to,0,sizeof(to)); /* Spurious warning */
406
to.sin6_family = AF_INET6;
407
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
1110
ret = init_gnutls_session(&session, mc);
1116
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1117
PRIuMAX "\n", ip, (uintmax_t)port);
1120
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1123
perror_plus("socket");
1134
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1135
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1136
ret = inet_pton(af, ip, &to6->sin6_addr);
1138
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1139
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1140
ret = inet_pton(af, ip, &to4->sin_addr);
1144
perror_plus("inet_pton");
413
fprintf(stderr, "Bad address: %s\n", ip);
416
to.sin6_port = htons(port); /* Spurious warning */
1150
fprintf_plus(stderr, "Bad address: %s\n", ip);
1155
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1156
if(IN6_IS_ADDR_LINKLOCAL
1157
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1158
if(if_index == AVAHI_IF_UNSPEC){
1159
fprintf_plus(stderr, "An IPv6 link-local address is"
1160
" incomplete without a network interface\n");
1164
/* Set the network interface number as scope */
1165
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1168
((struct sockaddr_in *)&to)->sin_port = htons(port);
418
to.sin6_scope_id = (uint32_t)if_index;
421
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
422
/* char addrstr[INET6_ADDRSTRLEN]; */
423
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
424
/* sizeof(addrstr)) == NULL){ */
425
/* perror("inet_ntop"); */
427
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
428
/* addrstr, ntohs(to.sin6_port)); */
432
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
438
char *out = mandos_protocol_version;
1177
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1178
char interface[IF_NAMESIZE];
1179
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1180
perror_plus("if_indextoname");
1182
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1183
"\n", ip, interface, (uintmax_t)port);
1186
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1187
ip, (uintmax_t)port);
1189
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1190
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1192
ret = getnameinfo((struct sockaddr *)&to,
1193
sizeof(struct sockaddr_in6),
1194
addrstr, sizeof(addrstr), NULL, 0,
1197
ret = getnameinfo((struct sockaddr *)&to,
1198
sizeof(struct sockaddr_in),
1199
addrstr, sizeof(addrstr), NULL, 0,
1202
if(ret == EAI_SYSTEM){
1203
perror_plus("getnameinfo");
1204
} else if(ret != 0) {
1205
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1206
} else if(strcmp(addrstr, ip) != 0){
1207
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1218
ret = connect(tcp_sd, (struct sockaddr *)&to,
1219
sizeof(struct sockaddr_in6));
1221
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1222
sizeof(struct sockaddr_in));
1225
if(errno == ENETUNREACH
1226
and if_index != AVAHI_IF_UNSPEC
1227
and connect_to == NULL
1228
and not route_added and
1229
((af == AF_INET6 and not
1230
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1232
or (af == AF_INET and
1233
/* Not a a IPv4LL address */
1234
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1235
& 0xFFFF0000L) != 0xA9FE0000L))){
1236
/* Work around Avahi bug - Avahi does not announce link-local
1237
addresses if it has a global address, so local hosts with
1238
*only* a link-local address (e.g. Mandos clients) cannot
1239
connect to a Mandos server announced by Avahi on a server
1240
host with a global address. Work around this by retrying
1241
with an explicit route added with the server's address.
1243
Avahi bug reference:
1244
http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1245
https://bugs.debian.org/587961
1248
fprintf_plus(stderr, "Mandos server unreachable, trying"
1252
route_added = add_local_route(ip, if_index);
1258
if(errno != ECONNREFUSED or debug){
1260
perror_plus("connect");
1273
const char *out = mandos_protocol_version;
441
1276
size_t out_size = strlen(out);
442
ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
443
out_size - written));
1277
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1278
out_size - written));
1281
perror_plus("write");
1285
written += (size_t)ret;
450
1286
if(written < out_size){
453
if (out == mandos_protocol_version){
1289
if(out == mandos_protocol_version){
462
ret = initgnutls (&es);
468
gnutls_transport_set_ptr (es.session,
469
(gnutls_transport_ptr_t) tcp_sd);
472
fprintf(stderr, "Establishing TLS session with %s\n", ip);
475
ret = gnutls_handshake (es.session);
477
if (ret != GNUTLS_E_SUCCESS){
1304
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1312
/* This casting via intptr_t is to eliminate warning about casting
1313
an int to a pointer type. This is exactly how the GnuTLS Guile
1314
function "set-session-transport-fd!" does it. */
1315
gnutls_transport_set_ptr(session,
1316
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1324
ret = gnutls_handshake(session);
1329
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1331
if(ret != GNUTLS_E_SUCCESS){
479
fprintf(stderr, "\n*** Handshake failed ***\n");
1333
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
486
//Retrieve OpenPGP packet that contains the wanted password
1340
/* Read OpenPGP packet that contains the wanted password */
489
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1343
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
494
buffer_capacity = adjustbuffer(buffer, buffer_length, buffer_capacity);
495
if (buffer_capacity == 0){
496
perror("adjustbuffer");
501
ret = gnutls_record_recv
502
(es.session, buffer+buffer_length, BUFFER_SIZE);
1354
buffer_capacity = incbuffer(&buffer, buffer_length,
1356
if(buffer_capacity == 0){
1358
perror_plus("incbuffer");
1368
sret = gnutls_record_recv(session, buffer+buffer_length,
508
1375
case GNUTLS_E_INTERRUPTED:
509
1376
case GNUTLS_E_AGAIN:
511
1378
case GNUTLS_E_REHANDSHAKE:
512
ret = gnutls_handshake (es.session);
514
fprintf(stderr, "\n*** Handshake failed ***\n");
1380
ret = gnutls_handshake(session);
1386
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1388
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
521
fprintf(stderr, "Unknown error while reading data from"
522
" encrypted session with mandos server\n");
524
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1396
fprintf_plus(stderr, "Unknown error while reading data from"
1397
" encrypted session with Mandos server\n");
1398
gnutls_bye(session, GNUTLS_SHUT_RDWR);
528
buffer_length += (size_t) ret;
532
if (buffer_length > 0){
533
decrypted_buffer_size = pgp_packet_decrypt(buffer,
537
if (decrypted_buffer_size >= 0){
1403
buffer_length += (size_t) sret;
1408
fprintf_plus(stderr, "Closing TLS session\n");
1417
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1422
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1424
if(buffer_length > 0){
1425
ssize_t decrypted_buffer_size;
1426
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1427
&decrypted_buffer, mc);
1428
if(decrypted_buffer_size >= 0){
539
1431
while(written < (size_t) decrypted_buffer_size){
540
ret = (int)fwrite (decrypted_buffer + written, 1,
541
(size_t)decrypted_buffer_size - written,
1437
ret = (int)fwrite(decrypted_buffer + written, 1,
1438
(size_t)decrypted_buffer_size - written,
543
1440
if(ret == 0 and ferror(stdout)){
545
fprintf(stderr, "Error writing encrypted data: %s\n",
1443
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
551
1449
written += (size_t)ret;
553
free(decrypted_buffer);
1455
/* Shutdown procedure */
1460
if(not delete_local_route(ip, if_index)){
1461
fprintf_plus(stderr, "Failed to delete local route to %s on"
1462
" interface %d", ip, if_index);
1466
free(decrypted_buffer);
1469
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1475
perror_plus("close");
1477
gnutls_deinit(session);
562
fprintf(stderr, "Closing TLS session\n");
566
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
569
gnutls_deinit (es.session);
570
gnutls_certificate_free_credentials (es.cred);
571
gnutls_global_deinit ();
575
static void resolve_callback( AvahiSServiceResolver *r,
576
AvahiIfIndex interface,
577
AVAHI_GCC_UNUSED AvahiProtocol protocol,
578
AvahiResolverEvent event,
582
const char *host_name,
583
const AvahiAddress *address,
585
AVAHI_GCC_UNUSED AvahiStringList *txt,
586
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
587
AVAHI_GCC_UNUSED void* userdata) {
588
mandos_context *mc = userdata;
589
assert(r); /* Spurious warning */
1487
__attribute__((nonnull))
1488
static void resolve_callback(AvahiSServiceResolver *r,
1489
AvahiIfIndex interface,
1490
AvahiProtocol proto,
1491
AvahiResolverEvent event,
1495
const char *host_name,
1496
const AvahiAddress *address,
1498
AVAHI_GCC_UNUSED AvahiStringList *txt,
1499
AVAHI_GCC_UNUSED AvahiLookupResultFlags
591
1506
/* Called whenever a service has been resolved successfully or
1510
avahi_s_service_resolver_free(r);
596
1516
case AVAHI_RESOLVER_FAILURE:
597
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
598
" type '%s' in domain '%s': %s\n", name, type, domain,
599
avahi_strerror(avahi_server_errno(mc->server)));
1517
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1518
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1520
avahi_strerror(avahi_server_errno
1521
(((mandos_context*)mc)->server)));
602
1524
case AVAHI_RESOLVER_FOUND:
604
1526
char ip[AVAHI_ADDRESS_STR_MAX];
605
1527
avahi_address_snprint(ip, sizeof(ip), address);
607
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
608
" port %d\n", name, host_name, ip, port);
1529
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1530
PRIdMAX ") on port %" PRIu16 "\n", name,
1531
host_name, ip, (intmax_t)interface, port);
610
int ret = start_mandos_communication(ip, port, interface, mc);
1533
int ret = start_mandos_communication(ip, (in_port_t)port,
1535
avahi_proto_to_af(proto),
1538
avahi_simple_poll_quit(simple_poll);
1540
if(not add_server(ip, (in_port_t)port, interface,
1541
avahi_proto_to_af(proto),
1542
&((mandos_context*)mc)->current_server)){
1543
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
616
1549
avahi_s_service_resolver_free(r);
619
static void browse_callback( AvahiSServiceBrowser *b,
620
AvahiIfIndex interface,
621
AvahiProtocol protocol,
622
AvahiBrowserEvent event,
626
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
628
mandos_context *mc = userdata;
629
assert(b); /* Spurious warning */
1552
static void browse_callback(AvahiSServiceBrowser *b,
1553
AvahiIfIndex interface,
1554
AvahiProtocol protocol,
1555
AvahiBrowserEvent event,
1559
AVAHI_GCC_UNUSED AvahiLookupResultFlags
631
1566
/* Called whenever a new services becomes available on the LAN or
632
1567
is removed from the LAN */
636
1575
case AVAHI_BROWSER_FAILURE:
638
fprintf(stderr, "(Browser) %s\n",
639
avahi_strerror(avahi_server_errno(mc->server)));
640
avahi_simple_poll_quit(mc->simple_poll);
1577
fprintf_plus(stderr, "(Avahi browser) %s\n",
1578
avahi_strerror(avahi_server_errno
1579
(((mandos_context*)mc)->server)));
1580
avahi_simple_poll_quit(simple_poll);
643
1583
case AVAHI_BROWSER_NEW:
644
/* We ignore the returned resolver object. In the callback
645
function we free it. If the server is terminated before
646
the callback function is called the server will free
647
the resolver for us. */
649
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
651
AVAHI_PROTO_INET6, 0,
652
resolve_callback, mc)))
653
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
654
avahi_strerror(avahi_server_errno(s)));
1584
/* We ignore the returned Avahi resolver object. In the callback
1585
function we free it. If the Avahi server is terminated before
1586
the callback function is called the Avahi server will free the
1589
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1590
interface, protocol, name, type,
1591
domain, protocol, 0,
1592
resolve_callback, mc) == NULL)
1593
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1595
avahi_strerror(avahi_server_errno
1596
(((mandos_context*)mc)->server)));
657
1599
case AVAHI_BROWSER_REMOVE:
660
1602
case AVAHI_BROWSER_ALL_FOR_NOW:
661
1603
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1605
fprintf_plus(stderr, "No Mandos server found, still"
666
/* Combines file name and path and returns the malloced new
667
string. some sane checks could/should be added */
668
static const char *combinepath(const char *first, const char *second){
669
size_t f_len = strlen(first);
670
size_t s_len = strlen(second);
671
char *tmp = malloc(f_len + s_len + 2);
676
memcpy(tmp, first, f_len);
680
memcpy(tmp + f_len + 1, second, s_len);
682
tmp[f_len + 1 + s_len] = '\0';
687
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1612
/* Signal handler that stops main loop after SIGTERM */
1613
static void handle_sigterm(int sig){
1618
signal_received = sig;
1619
int old_errno = errno;
1620
/* set main loop to exit */
1621
if(simple_poll != NULL){
1622
avahi_simple_poll_quit(simple_poll);
1627
__attribute__((nonnull, warn_unused_result))
1628
bool get_flags(const char *ifname, struct ifreq *ifr){
1632
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1635
perror_plus("socket");
1639
strcpy(ifr->ifr_name, ifname);
1640
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1644
perror_plus("ioctl SIOCGIFFLAGS");
1652
__attribute__((nonnull, warn_unused_result))
1653
bool good_flags(const char *ifname, const struct ifreq *ifr){
1655
/* Reject the loopback device */
1656
if(ifr->ifr_flags & IFF_LOOPBACK){
1658
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1663
/* Accept point-to-point devices only if connect_to is specified */
1664
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1666
fprintf_plus(stderr, "Accepting point-to-point interface"
1667
" \"%s\"\n", ifname);
1671
/* Otherwise, reject non-broadcast-capable devices */
1672
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1674
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1675
" \"%s\"\n", ifname);
1679
/* Reject non-ARP interfaces (including dummy interfaces) */
1680
if(ifr->ifr_flags & IFF_NOARP){
1682
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1688
/* Accept this device */
1690
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1696
* This function determines if a directory entry in /sys/class/net
1697
* corresponds to an acceptable network device.
1698
* (This function is passed to scandir(3) as a filter function.)
1700
__attribute__((nonnull, warn_unused_result))
1701
int good_interface(const struct dirent *if_entry){
1702
if(if_entry->d_name[0] == '.'){
1707
if(not get_flags(if_entry->d_name, &ifr)){
1709
fprintf_plus(stderr, "Failed to get flags for interface "
1710
"\"%s\"\n", if_entry->d_name);
1715
if(not good_flags(if_entry->d_name, &ifr)){
1722
* This function determines if a network interface is up.
1724
__attribute__((nonnull, warn_unused_result))
1725
bool interface_is_up(const char *interface){
1727
if(not get_flags(interface, &ifr)){
1729
fprintf_plus(stderr, "Failed to get flags for interface "
1730
"\"%s\"\n", interface);
1735
return (bool)(ifr.ifr_flags & IFF_UP);
1739
* This function determines if a network interface is running
1741
__attribute__((nonnull, warn_unused_result))
1742
bool interface_is_running(const char *interface){
1744
if(not get_flags(interface, &ifr)){
1746
fprintf_plus(stderr, "Failed to get flags for interface "
1747
"\"%s\"\n", interface);
1752
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1755
__attribute__((nonnull, pure, warn_unused_result))
1756
int notdotentries(const struct dirent *direntry){
1757
/* Skip "." and ".." */
1758
if(direntry->d_name[0] == '.'
1759
and (direntry->d_name[1] == '\0'
1760
or (direntry->d_name[1] == '.'
1761
and direntry->d_name[2] == '\0'))){
1767
/* Is this directory entry a runnable program? */
1768
__attribute__((nonnull, warn_unused_result))
1769
int runnable_hook(const struct dirent *direntry){
1774
if((direntry->d_name)[0] == '\0'){
1779
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1780
"abcdefghijklmnopqrstuvwxyz"
1783
if((direntry->d_name)[sret] != '\0'){
1784
/* Contains non-allowed characters */
1786
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1792
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1795
perror_plus("Could not stat hook");
1799
if(not (S_ISREG(st.st_mode))){
1800
/* Not a regular file */
1802
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1807
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1808
/* Not executable */
1810
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1816
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1822
__attribute__((nonnull, warn_unused_result))
1823
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1824
mandos_context *mc){
1826
struct timespec now;
1827
struct timespec waited_time;
1828
intmax_t block_time;
1831
if(mc->current_server == NULL){
1833
fprintf_plus(stderr, "Wait until first server is found."
1836
ret = avahi_simple_poll_iterate(s, -1);
1839
fprintf_plus(stderr, "Check current_server if we should run"
1842
/* the current time */
1843
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1845
perror_plus("clock_gettime");
1848
/* Calculating in ms how long time between now and server
1849
who we visted longest time ago. Now - last seen. */
1850
waited_time.tv_sec = (now.tv_sec
1851
- mc->current_server->last_seen.tv_sec);
1852
waited_time.tv_nsec = (now.tv_nsec
1853
- mc->current_server->last_seen.tv_nsec);
1854
/* total time is 10s/10,000ms.
1855
Converting to s from ms by dividing by 1,000,
1856
and ns to ms by dividing by 1,000,000. */
1857
block_time = ((retry_interval
1858
- ((intmax_t)waited_time.tv_sec * 1000))
1859
- ((intmax_t)waited_time.tv_nsec / 1000000));
1862
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1866
if(block_time <= 0){
1867
ret = start_mandos_communication(mc->current_server->ip,
1868
mc->current_server->port,
1869
mc->current_server->if_index,
1870
mc->current_server->af, mc);
1872
avahi_simple_poll_quit(s);
1875
ret = clock_gettime(CLOCK_MONOTONIC,
1876
&mc->current_server->last_seen);
1878
perror_plus("clock_gettime");
1881
mc->current_server = mc->current_server->next;
1882
block_time = 0; /* Call avahi to find new Mandos
1883
servers, but don't block */
1886
ret = avahi_simple_poll_iterate(s, (int)block_time);
1889
if(ret > 0 or errno != EINTR){
1890
return (ret != 1) ? ret : 0;
1896
__attribute__((nonnull))
1897
void run_network_hooks(const char *mode, const char *interface,
1899
struct dirent **direntries = NULL;
1900
if(hookdir_fd == -1){
1901
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1903
if(hookdir_fd == -1){
1904
if(errno == ENOENT){
1906
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1907
" found\n", hookdir);
1910
perror_plus("open");
1916
#if __GLIBC_PREREQ(2, 15)
1917
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1918
runnable_hook, alphasort);
1919
#else /* not __GLIBC_PREREQ(2, 15) */
1920
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1922
#endif /* not __GLIBC_PREREQ(2, 15) */
1923
#else /* not __GLIBC__ */
1924
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1926
#endif /* not __GLIBC__ */
1928
perror_plus("scandir");
1931
struct dirent *direntry;
1933
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1935
perror_plus("open(\"/dev/null\", O_RDONLY)");
1938
for(int i = 0; i < numhooks; i++){
1939
direntry = direntries[i];
1941
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1944
pid_t hook_pid = fork();
1947
/* Raise privileges */
1948
errno = raise_privileges_permanently();
1950
perror_plus("Failed to raise privileges");
1957
perror_plus("setgid");
1960
/* Reset supplementary groups */
1962
ret = setgroups(0, NULL);
1964
perror_plus("setgroups");
1967
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1969
perror_plus("setenv");
1972
ret = setenv("DEVICE", interface, 1);
1974
perror_plus("setenv");
1977
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1979
perror_plus("setenv");
1982
ret = setenv("MODE", mode, 1);
1984
perror_plus("setenv");
1988
ret = asprintf(&delaystring, "%f", (double)delay);
1990
perror_plus("asprintf");
1993
ret = setenv("DELAY", delaystring, 1);
1996
perror_plus("setenv");
2000
if(connect_to != NULL){
2001
ret = setenv("CONNECT", connect_to, 1);
2003
perror_plus("setenv");
2007
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2011
perror_plus("openat");
2012
_exit(EXIT_FAILURE);
2014
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2015
perror_plus("close");
2016
_exit(EXIT_FAILURE);
2018
ret = dup2(devnull, STDIN_FILENO);
2020
perror_plus("dup2(devnull, STDIN_FILENO)");
2023
ret = (int)TEMP_FAILURE_RETRY(close(devnull));
2025
perror_plus("close");
2028
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2030
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2033
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2035
perror_plus("fexecve");
2036
_exit(EXIT_FAILURE);
2040
perror_plus("fork");
2045
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2046
perror_plus("waitpid");
2050
if(WIFEXITED(status)){
2051
if(WEXITSTATUS(status) != 0){
2052
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2053
" with status %d\n", direntry->d_name,
2054
WEXITSTATUS(status));
2058
} else if(WIFSIGNALED(status)){
2059
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2060
" signal %d\n", direntry->d_name,
2065
fprintf_plus(stderr, "Warning: network hook \"%s\""
2066
" crashed\n", direntry->d_name);
2072
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2078
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
2079
perror_plus("close");
2086
__attribute__((nonnull, warn_unused_result))
2087
error_t bring_up_interface(const char *const interface,
2089
error_t old_errno = errno;
2091
struct ifreq network;
2092
unsigned int if_index = if_nametoindex(interface);
2094
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2104
if(not interface_is_up(interface)){
2105
error_t ret_errno = 0, ioctl_errno = 0;
2106
if(not get_flags(interface, &network)){
2108
fprintf_plus(stderr, "Failed to get flags for interface "
2109
"\"%s\"\n", interface);
2113
network.ifr_flags |= IFF_UP; /* set flag */
2115
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2118
perror_plus("socket");
2124
ret = (int)TEMP_FAILURE_RETRY(close(sd));
2126
perror_plus("close");
2133
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2137
/* Raise privileges */
2138
ret_errno = raise_privileges();
2141
perror_plus("Failed to raise privileges");
2146
bool restore_loglevel = false;
2148
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2149
messages about the network interface to mess up the prompt */
2150
ret_linux = klogctl(8, NULL, 5);
2151
if(ret_linux == -1){
2152
perror_plus("klogctl");
2154
restore_loglevel = true;
2157
#endif /* __linux__ */
2158
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2159
ioctl_errno = errno;
2161
if(restore_loglevel){
2162
ret_linux = klogctl(7, NULL, 0);
2163
if(ret_linux == -1){
2164
perror_plus("klogctl");
2167
#endif /* __linux__ */
2169
/* If raise_privileges() succeeded above */
2171
/* Lower privileges */
2172
ret_errno = lower_privileges();
2175
perror_plus("Failed to lower privileges");
2179
/* Close the socket */
2180
ret = (int)TEMP_FAILURE_RETRY(close(sd));
2182
perror_plus("close");
2185
if(ret_setflags == -1){
2186
errno = ioctl_errno;
2187
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2192
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2196
/* Sleep checking until interface is running.
2197
Check every 0.25s, up to total time of delay */
2198
for(int i=0; i < delay * 4; i++){
2199
if(interface_is_running(interface)){
2202
struct timespec sleeptime = { .tv_nsec = 250000000 };
2203
ret = nanosleep(&sleeptime, NULL);
2204
if(ret == -1 and errno != EINTR){
2205
perror_plus("nanosleep");
2213
__attribute__((nonnull, warn_unused_result))
2214
error_t take_down_interface(const char *const interface){
2215
error_t old_errno = errno;
2216
struct ifreq network;
2217
unsigned int if_index = if_nametoindex(interface);
2219
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2223
if(interface_is_up(interface)){
2224
error_t ret_errno = 0, ioctl_errno = 0;
2225
if(not get_flags(interface, &network) and debug){
2227
fprintf_plus(stderr, "Failed to get flags for interface "
2228
"\"%s\"\n", interface);
2232
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2234
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2237
perror_plus("socket");
2243
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2247
/* Raise privileges */
2248
ret_errno = raise_privileges();
2251
perror_plus("Failed to raise privileges");
2254
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2255
ioctl_errno = errno;
2257
/* If raise_privileges() succeeded above */
2259
/* Lower privileges */
2260
ret_errno = lower_privileges();
2263
perror_plus("Failed to lower privileges");
2267
/* Close the socket */
2268
int ret = (int)TEMP_FAILURE_RETRY(close(sd));
2270
perror_plus("close");
2273
if(ret_setflags == -1){
2274
errno = ioctl_errno;
2275
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2280
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2288
int main(int argc, char *argv[]){
2289
mandos_context mc = { .server = NULL, .dh_bits = 0,
2290
.priority = "SECURE256:!CTYPE-X.509:"
2291
"+CTYPE-OPENPGP:!RSA", .current_server = NULL,
2292
.interfaces = NULL, .interfaces_size = 0 };
2293
AvahiSServiceBrowser *sb = NULL;
2298
int exitcode = EXIT_SUCCESS;
2299
char *interfaces_to_take_down = NULL;
2300
size_t interfaces_to_take_down_size = 0;
2301
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2302
char old_tempdir[] = "/tmp/mandosXXXXXX";
2303
char *tempdir = NULL;
2304
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2305
const char *seckey = PATHDIR "/" SECKEY;
2306
const char *pubkey = PATHDIR "/" PUBKEY;
2307
const char *dh_params_file = NULL;
2308
char *interfaces_hooks = NULL;
2310
bool gnutls_initialized = false;
2311
bool gpgme_initialized = false;
2313
double retry_interval = 10; /* 10s between trying a server and
2314
retrying the same server again */
2316
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2317
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2322
/* Lower any group privileges we might have, just to be safe */
2326
perror_plus("setgid");
2329
/* Lower user privileges (temporarily) */
2333
perror_plus("seteuid");
2341
struct argp_option options[] = {
2342
{ .name = "debug", .key = 128,
2343
.doc = "Debug mode", .group = 3 },
2344
{ .name = "connect", .key = 'c',
2345
.arg = "ADDRESS:PORT",
2346
.doc = "Connect directly to a specific Mandos server",
2348
{ .name = "interface", .key = 'i',
2350
.doc = "Network interface that will be used to search for"
2353
{ .name = "seckey", .key = 's',
2355
.doc = "OpenPGP secret key file base name",
2357
{ .name = "pubkey", .key = 'p',
2359
.doc = "OpenPGP public key file base name",
2361
{ .name = "dh-bits", .key = 129,
2363
.doc = "Bit length of the prime number used in the"
2364
" Diffie-Hellman key exchange",
2366
{ .name = "dh-params", .key = 134,
2368
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2369
" for the Diffie-Hellman key exchange",
2371
{ .name = "priority", .key = 130,
2373
.doc = "GnuTLS priority string for the TLS handshake",
2375
{ .name = "delay", .key = 131,
2377
.doc = "Maximum delay to wait for interface startup",
2379
{ .name = "retry", .key = 132,
2381
.doc = "Retry interval used when denied by the Mandos server",
2383
{ .name = "network-hook-dir", .key = 133,
2385
.doc = "Directory where network hooks are located",
2388
* These reproduce what we would get without ARGP_NO_HELP
2390
{ .name = "help", .key = '?',
2391
.doc = "Give this help list", .group = -1 },
2392
{ .name = "usage", .key = -3,
2393
.doc = "Give a short usage message", .group = -1 },
2394
{ .name = "version", .key = 'V',
2395
.doc = "Print program version", .group = -1 },
2399
error_t parse_opt(int key, char *arg,
2400
struct argp_state *state){
2403
case 128: /* --debug */
2406
case 'c': /* --connect */
2409
case 'i': /* --interface */
2410
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2413
argp_error(state, "%s", strerror(ret_errno));
2416
case 's': /* --seckey */
2419
case 'p': /* --pubkey */
2422
case 129: /* --dh-bits */
2424
tmpmax = strtoimax(arg, &tmp, 10);
2425
if(errno != 0 or tmp == arg or *tmp != '\0'
2426
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2427
argp_error(state, "Bad number of DH bits");
2429
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2431
case 134: /* --dh-params */
2432
dh_params_file = arg;
2434
case 130: /* --priority */
2437
case 131: /* --delay */
2439
delay = strtof(arg, &tmp);
2440
if(errno != 0 or tmp == arg or *tmp != '\0'){
2441
argp_error(state, "Bad delay");
2443
case 132: /* --retry */
2445
retry_interval = strtod(arg, &tmp);
2446
if(errno != 0 or tmp == arg or *tmp != '\0'
2447
or (retry_interval * 1000) > INT_MAX
2448
or retry_interval < 0){
2449
argp_error(state, "Bad retry interval");
2452
case 133: /* --network-hook-dir */
2456
* These reproduce what we would get without ARGP_NO_HELP
2458
case '?': /* --help */
2459
argp_state_help(state, state->out_stream,
2460
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2461
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2462
case -3: /* --usage */
2463
argp_state_help(state, state->out_stream,
2464
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2465
case 'V': /* --version */
2466
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2467
exit(argp_err_exit_status);
2470
return ARGP_ERR_UNKNOWN;
2475
struct argp argp = { .options = options, .parser = parse_opt,
2477
.doc = "Mandos client -- Get and decrypt"
2478
" passwords from a Mandos server" };
2479
ret = argp_parse(&argp, argc, argv,
2480
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2487
perror_plus("argp_parse");
2488
exitcode = EX_OSERR;
2491
exitcode = EX_USAGE;
2497
/* Work around Debian bug #633582:
2498
<http://bugs.debian.org/633582> */
2500
/* Re-raise privileges */
2501
ret_errno = raise_privileges();
2504
perror_plus("Failed to raise privileges");
2508
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2509
int seckey_fd = open(seckey, O_RDONLY);
2510
if(seckey_fd == -1){
2511
perror_plus("open");
2513
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2515
perror_plus("fstat");
2517
if(S_ISREG(st.st_mode)
2518
and st.st_uid == 0 and st.st_gid == 0){
2519
ret = fchown(seckey_fd, uid, gid);
2521
perror_plus("fchown");
2525
TEMP_FAILURE_RETRY(close(seckey_fd));
2529
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2530
int pubkey_fd = open(pubkey, O_RDONLY);
2531
if(pubkey_fd == -1){
2532
perror_plus("open");
2534
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2536
perror_plus("fstat");
2538
if(S_ISREG(st.st_mode)
2539
and st.st_uid == 0 and st.st_gid == 0){
2540
ret = fchown(pubkey_fd, uid, gid);
2542
perror_plus("fchown");
2546
TEMP_FAILURE_RETRY(close(pubkey_fd));
2550
if(strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2551
int dhparams_fd = open(dh_params_file, O_RDONLY);
2552
if(dhparams_fd == -1){
2553
perror_plus("open");
2555
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2557
perror_plus("fstat");
2559
if(S_ISREG(st.st_mode)
2560
and st.st_uid == 0 and st.st_gid == 0){
2561
ret = fchown(dhparams_fd, uid, gid);
2563
perror_plus("fchown");
2567
TEMP_FAILURE_RETRY(close(dhparams_fd));
2571
/* Lower privileges */
2572
ret_errno = lower_privileges();
2575
perror_plus("Failed to lower privileges");
2580
/* Remove invalid interface names (except "none") */
2582
char *interface = NULL;
2583
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2585
if(strcmp(interface, "none") != 0
2586
and if_nametoindex(interface) == 0){
2587
if(interface[0] != '\0'){
2588
fprintf_plus(stderr, "Not using nonexisting interface"
2589
" \"%s\"\n", interface);
2591
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2597
/* Run network hooks */
2599
if(mc.interfaces != NULL){
2600
interfaces_hooks = malloc(mc.interfaces_size);
2601
if(interfaces_hooks == NULL){
2602
perror_plus("malloc");
2605
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2606
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2608
run_network_hooks("start", interfaces_hooks != NULL ?
2609
interfaces_hooks : "", delay);
2613
avahi_set_log_function(empty_log);
2616
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2617
from the signal handler */
2618
/* Initialize the pseudo-RNG for Avahi */
2619
srand((unsigned int) time(NULL));
2620
simple_poll = avahi_simple_poll_new();
2621
if(simple_poll == NULL){
2622
fprintf_plus(stderr,
2623
"Avahi: Failed to create simple poll object.\n");
2624
exitcode = EX_UNAVAILABLE;
2628
sigemptyset(&sigterm_action.sa_mask);
2629
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2631
perror_plus("sigaddset");
2632
exitcode = EX_OSERR;
2635
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2637
perror_plus("sigaddset");
2638
exitcode = EX_OSERR;
2641
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2643
perror_plus("sigaddset");
2644
exitcode = EX_OSERR;
2647
/* Need to check if the handler is SIG_IGN before handling:
2648
| [[info:libc:Initial Signal Actions]] |
2649
| [[info:libc:Basic Signal Handling]] |
2651
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2653
perror_plus("sigaction");
2656
if(old_sigterm_action.sa_handler != SIG_IGN){
2657
ret = sigaction(SIGINT, &sigterm_action, NULL);
2659
perror_plus("sigaction");
2660
exitcode = EX_OSERR;
2664
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2666
perror_plus("sigaction");
2669
if(old_sigterm_action.sa_handler != SIG_IGN){
2670
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2672
perror_plus("sigaction");
2673
exitcode = EX_OSERR;
2677
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2679
perror_plus("sigaction");
2682
if(old_sigterm_action.sa_handler != SIG_IGN){
2683
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2685
perror_plus("sigaction");
2686
exitcode = EX_OSERR;
2691
/* If no interfaces were specified, make a list */
2692
if(mc.interfaces == NULL){
2693
struct dirent **direntries = NULL;
2694
/* Look for any good interfaces */
2695
ret = scandir(sys_class_net, &direntries, good_interface,
2698
/* Add all found interfaces to interfaces list */
2699
for(int i = 0; i < ret; ++i){
2700
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2701
direntries[i]->d_name);
2704
perror_plus("argz_add");
2705
free(direntries[i]);
2709
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2710
direntries[i]->d_name);
2712
free(direntries[i]);
2719
fprintf_plus(stderr, "Could not find a network interface\n");
2720
exitcode = EXIT_FAILURE;
2725
/* Bring up interfaces which are down, and remove any "none"s */
2727
char *interface = NULL;
2728
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2730
/* If interface name is "none", stop bringing up interfaces.
2731
Also remove all instances of "none" from the list */
2732
if(strcmp(interface, "none") == 0){
2733
argz_delete(&mc.interfaces, &mc.interfaces_size,
2736
while((interface = argz_next(mc.interfaces,
2737
mc.interfaces_size, interface))){
2738
if(strcmp(interface, "none") == 0){
2739
argz_delete(&mc.interfaces, &mc.interfaces_size,
2746
bool interface_was_up = interface_is_up(interface);
2747
errno = bring_up_interface(interface, delay);
2748
if(not interface_was_up){
2750
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2751
" %s\n", interface, strerror(errno));
2753
errno = argz_add(&interfaces_to_take_down,
2754
&interfaces_to_take_down_size,
2757
perror_plus("argz_add");
2762
if(debug and (interfaces_to_take_down == NULL)){
2763
fprintf_plus(stderr, "No interfaces were brought up\n");
2767
/* If we only got one interface, explicitly use only that one */
2768
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2770
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2773
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2780
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2782
fprintf_plus(stderr, "init_gnutls_global failed\n");
2783
exitcode = EX_UNAVAILABLE;
2786
gnutls_initialized = true;
2793
/* Try /run/tmp before /tmp */
2794
tempdir = mkdtemp(run_tempdir);
2795
if(tempdir == NULL and errno == ENOENT){
2797
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2798
run_tempdir, old_tempdir);
2800
tempdir = mkdtemp(old_tempdir);
2802
if(tempdir == NULL){
2803
perror_plus("mkdtemp");
2811
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2812
fprintf_plus(stderr, "init_gpgme failed\n");
2813
exitcode = EX_UNAVAILABLE;
2816
gpgme_initialized = true;
2823
if(connect_to != NULL){
2824
/* Connect directly, do not use Zeroconf */
2825
/* (Mainly meant for debugging) */
2826
char *address = strrchr(connect_to, ':');
2828
if(address == NULL){
2829
fprintf_plus(stderr, "No colon in address\n");
2830
exitcode = EX_USAGE;
2840
tmpmax = strtoimax(address+1, &tmp, 10);
2841
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2842
or tmpmax != (in_port_t)tmpmax){
2843
fprintf_plus(stderr, "Bad port number\n");
2844
exitcode = EX_USAGE;
2852
port = (in_port_t)tmpmax;
2854
/* Colon in address indicates IPv6 */
2856
if(strchr(connect_to, ':') != NULL){
2858
/* Accept [] around IPv6 address - see RFC 5952 */
2859
if(connect_to[0] == '[' and address[-1] == ']')
2867
address = connect_to;
2873
while(not quit_now){
2874
ret = start_mandos_communication(address, port, if_index, af,
2876
if(quit_now or ret == 0){
2880
fprintf_plus(stderr, "Retrying in %d seconds\n",
2881
(int)retry_interval);
2883
sleep((unsigned int)retry_interval);
2887
exitcode = EXIT_SUCCESS;
688
2898
AvahiServerConfig config;
689
AvahiSServiceBrowser *sb = NULL;
692
int returncode = EXIT_SUCCESS;
693
const char *interface = "eth0";
694
struct ifreq network;
696
char *connect_to = NULL;
697
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
698
mandos_context mc = { .simple_poll = NULL, .server = NULL,
699
.dh_bits = 2048, .priority = "SECURE256"};
702
static struct option long_options[] = {
703
{"debug", no_argument, (int *)&debug, 1},
704
{"connect", required_argument, 0, 'C'},
705
{"interface", required_argument, 0, 'i'},
706
{"certdir", required_argument, 0, 'd'},
707
{"certkey", required_argument, 0, 'c'},
708
{"certfile", required_argument, 0, 'k'},
709
{"dh_bits", required_argument, 0, 'D'},
710
{"priority", required_argument, 0, 'p'},
713
int option_index = 0;
714
ret = getopt_long (argc, argv, "i:", long_options,
743
tmp = strtol(optarg, NULL, 10);
744
if (errno == ERANGE){
752
mc.priority = optarg;
759
certfile = combinepath(certdir, certfile);
760
if (certfile == NULL){
761
perror("combinepath");
762
returncode = EXIT_FAILURE;
766
certkey = combinepath(certdir, certkey);
767
if (certkey == NULL){
768
perror("combinepath");
769
returncode = EXIT_FAILURE;
773
if_index = (AvahiIfIndex) if_nametoindex(interface);
775
fprintf(stderr, "No such interface: \"%s\"\n", interface);
779
if(connect_to != NULL){
780
/* Connect directly, do not use Zeroconf */
781
/* (Mainly meant for debugging) */
782
char *address = strrchr(connect_to, ':');
784
fprintf(stderr, "No colon in address\n");
788
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
790
perror("Bad port number");
794
address = connect_to;
795
ret = start_mandos_communication(address, port, if_index);
803
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
806
returncode = EXIT_FAILURE;
809
strcpy(network.ifr_name, interface);
810
ret = ioctl(sd, SIOCGIFFLAGS, &network);
813
perror("ioctl SIOCGIFFLAGS");
814
returncode = EXIT_FAILURE;
817
if((network.ifr_flags & IFF_UP) == 0){
818
network.ifr_flags |= IFF_UP;
819
ret = ioctl(sd, SIOCSIFFLAGS, &network);
821
perror("ioctl SIOCSIFFLAGS");
822
returncode = EXIT_FAILURE;
829
avahi_set_log_function(empty_log);
832
/* Initialize the psuedo-RNG */
833
srand((unsigned int) time(NULL));
835
/* Allocate main loop object */
836
if (!(mc.simple_poll = avahi_simple_poll_new())) {
837
fprintf(stderr, "Failed to create simple poll object.\n");
838
returncode = EXIT_FAILURE;
842
/* Do not publish any local records */
2899
/* Do not publish any local Zeroconf records */
843
2900
avahi_server_config_init(&config);
844
2901
config.publish_hinfo = 0;
845
2902
config.publish_addresses = 0;
846
2903
config.publish_workstation = 0;
847
2904
config.publish_domain = 0;
849
2906
/* Allocate a new server */
850
2907
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
851
&config, NULL, NULL, &error);
853
/* Free the configuration data */
2908
&config, NULL, NULL, &ret_errno);
2910
/* Free the Avahi configuration data */
854
2911
avahi_server_config_free(&config);
856
/* Check if creating the server object succeeded */
858
fprintf(stderr, "Failed to create server: %s\n",
859
avahi_strerror(error));
860
returncode = EXIT_FAILURE;
864
/* Create the service browser */
865
sb = avahi_s_service_browser_new(mc.server, if_index,
867
"_mandos._tcp", NULL, 0,
868
browse_callback, &mc);
870
fprintf(stderr, "Failed to create service browser: %s\n",
871
avahi_strerror(avahi_server_errno(mc.server)));
872
returncode = EXIT_FAILURE;
876
/* Run the main loop */
879
fprintf(stderr, "Starting avahi loop search\n");
882
avahi_simple_poll_loop(simple_poll);
887
fprintf(stderr, "%s exiting\n", argv[0]);
892
avahi_s_service_browser_free(sb);
895
avahi_server_free(mc.server);
898
avahi_simple_poll_free(simple_poll);
2914
/* Check if creating the Avahi server object succeeded */
2915
if(mc.server == NULL){
2916
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2917
avahi_strerror(ret_errno));
2918
exitcode = EX_UNAVAILABLE;
2926
/* Create the Avahi service browser */
2927
sb = avahi_s_service_browser_new(mc.server, if_index,
2928
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2929
NULL, 0, browse_callback,
2932
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2933
avahi_strerror(avahi_server_errno(mc.server)));
2934
exitcode = EX_UNAVAILABLE;
2942
/* Run the main loop */
2945
fprintf_plus(stderr, "Starting Avahi loop search\n");
2948
ret = avahi_loop_with_timeout(simple_poll,
2949
(int)(retry_interval * 1000), &mc);
2951
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2952
(ret == 0) ? "successfully" : "with error");
2958
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2961
/* Cleanup things */
2962
free(mc.interfaces);
2965
avahi_s_service_browser_free(sb);
2967
if(mc.server != NULL)
2968
avahi_server_free(mc.server);
2970
if(simple_poll != NULL)
2971
avahi_simple_poll_free(simple_poll);
2973
if(gnutls_initialized){
2974
gnutls_certificate_free_credentials(mc.cred);
2975
gnutls_global_deinit();
2976
gnutls_dh_params_deinit(mc.dh_params);
2979
if(gpgme_initialized){
2980
gpgme_release(mc.ctx);
2983
/* Cleans up the circular linked list of Mandos servers the client
2985
if(mc.current_server != NULL){
2986
mc.current_server->prev->next = NULL;
2987
while(mc.current_server != NULL){
2988
server *next = mc.current_server->next;
2990
#pragma GCC diagnostic push
2991
#pragma GCC diagnostic ignored "-Wcast-qual"
2993
free((char *)(mc.current_server->ip));
2995
#pragma GCC diagnostic pop
2997
free(mc.current_server);
2998
mc.current_server = next;
3002
/* Re-raise privileges */
3004
ret_errno = raise_privileges();
3007
perror_plus("Failed to raise privileges");
3010
/* Run network hooks */
3011
run_network_hooks("stop", interfaces_hooks != NULL ?
3012
interfaces_hooks : "", delay);
3014
/* Take down the network interfaces which were brought up */
3016
char *interface = NULL;
3017
while((interface=argz_next(interfaces_to_take_down,
3018
interfaces_to_take_down_size,
3020
ret_errno = take_down_interface(interface);
3023
perror_plus("Failed to take down interface");
3026
if(debug and (interfaces_to_take_down == NULL)){
3027
fprintf_plus(stderr, "No interfaces needed to be taken"
3033
ret_errno = lower_privileges_permanently();
3036
perror_plus("Failed to lower privileges permanently");
3040
free(interfaces_to_take_down);
3041
free(interfaces_hooks);
3043
/* Removes the GPGME temp directory and all files inside */
3044
if(tempdir != NULL){
3045
struct dirent **direntries = NULL;
3046
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
3050
if(tempdir_fd == -1){
3051
perror_plus("open");
3054
#if __GLIBC_PREREQ(2, 15)
3055
int numentries = scandirat(tempdir_fd, ".", &direntries,
3056
notdotentries, alphasort);
3057
#else /* not __GLIBC_PREREQ(2, 15) */
3058
int numentries = scandir(tempdir, &direntries, notdotentries,
3060
#endif /* not __GLIBC_PREREQ(2, 15) */
3061
#else /* not __GLIBC__ */
3062
int numentries = scandir(tempdir, &direntries, notdotentries,
3064
#endif /* not __GLIBC__ */
3065
if(numentries >= 0){
3066
for(int i = 0; i < numentries; i++){
3067
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
3069
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
3070
" \"%s\", 0): %s\n", tempdir,
3071
direntries[i]->d_name, strerror(errno));
3073
free(direntries[i]);
3076
/* need to clean even if 0 because man page doesn't specify */
3078
if(numentries == -1){
3079
perror_plus("scandir");
3081
ret = rmdir(tempdir);
3082
if(ret == -1 and errno != ENOENT){
3083
perror_plus("rmdir");
3086
TEMP_FAILURE_RETRY(close(tempdir_fd));
3091
sigemptyset(&old_sigterm_action.sa_mask);
3092
old_sigterm_action.sa_handler = SIG_DFL;
3093
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3094
&old_sigterm_action,
3097
perror_plus("sigaction");
3100
ret = raise(signal_received);
3101
} while(ret != 0 and errno == EINTR);
3103
perror_plus("raise");
3106
TEMP_FAILURE_RETRY(pause());