117
46
#include <avahi-common/malloc.h>
118
47
#include <avahi-common/error.h>
121
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
124
init_gnutls_session(),
126
#include <gnutls/openpgp.h>
127
/* gnutls_certificate_set_openpgp_key_file(),
128
GNUTLS_OPENPGP_FMT_BASE64 */
131
#include <gpgme.h> /* All GPGME types, constants and
134
GPGME_PROTOCOL_OpenPGP,
50
#include <sys/types.h> /* socket(), 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() */
137
70
#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"
73
const char *certdir = "/conf/conf.d/cryptkeyreq/";
74
const char *certfile = "openpgp-client.txt";
75
const char *certkey = "openpgp-client-key.txt";
144
77
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 */
80
gnutls_session_t session;
169
81
gnutls_certificate_credentials_t cred;
170
unsigned int dh_bits;
171
82
gnutls_dh_params_t dh_params;
172
const char *priority;
86
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
87
char **new_packet, const char *homedir){
88
gpgme_data_t dh_crypto, dh_plain;
174
server *current_server;
176
size_t interfaces_size;
179
/* global so signal handler can reach it*/
180
AvahiSimplePoll *simple_poll;
182
sig_atomic_t quit_now = 0;
183
int signal_received = 0;
185
/* Function to use when printing errors */
186
void perror_plus(const char *print_text){
188
fprintf(stderr, "Mandos plugin %s: ",
189
program_invocation_short_name);
194
__attribute__((format (gnu_printf, 2, 3), nonnull))
195
int fprintf_plus(FILE *stream, const char *format, ...){
197
va_start (ap, format);
199
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
200
program_invocation_short_name));
201
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
205
* Make additional room in "buffer" for at least BUFFER_SIZE more
206
* bytes. "buffer_capacity" is how much is currently allocated,
207
* "buffer_length" is how much is already used.
209
__attribute__((nonnull, warn_unused_result))
210
size_t incbuffer(char **buffer, size_t buffer_length,
211
size_t buffer_capacity){
212
if(buffer_length + BUFFER_SIZE > buffer_capacity){
213
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
215
int old_errno = errno;
222
buffer_capacity += BUFFER_SIZE;
224
return buffer_capacity;
227
/* Add server to set of servers to retry periodically */
228
__attribute__((nonnull, warn_unused_result))
229
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
230
int af, server **current_server){
232
server *new_server = malloc(sizeof(server));
233
if(new_server == NULL){
234
perror_plus("malloc");
237
*new_server = (server){ .ip = strdup(ip),
239
.if_index = if_index,
241
if(new_server->ip == NULL){
242
perror_plus("strdup");
246
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
248
perror_plus("clock_gettime");
250
#pragma GCC diagnostic push
251
#pragma GCC diagnostic ignored "-Wcast-qual"
253
free((char *)(new_server->ip));
255
#pragma GCC diagnostic pop
260
/* Special case of first server */
261
if(*current_server == NULL){
262
new_server->next = new_server;
263
new_server->prev = new_server;
264
*current_server = new_server;
266
/* Place the new server last in the list */
267
new_server->next = *current_server;
268
new_server->prev = (*current_server)->prev;
269
new_server->prev->next = new_server;
270
(*current_server)->prev = new_server;
278
__attribute__((nonnull, warn_unused_result))
279
static bool init_gpgme(const char * const seckey,
280
const char * const pubkey,
281
const char * const tempdir,
92
ssize_t new_packet_capacity = 0;
93
ssize_t new_packet_length = 0;
284
94
gpgme_engine_info_t engine_info;
287
* Helper function to insert pub and seckey to the engine keyring.
289
bool import_key(const char * const filename){
292
gpgme_data_t pgp_data;
294
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
300
rc = gpgme_data_new_from_fd(&pgp_data, fd);
301
if(rc != GPG_ERR_NO_ERROR){
302
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
303
gpgme_strsource(rc), gpgme_strerror(rc));
307
rc = gpgme_op_import(mc->ctx, pgp_data);
308
if(rc != GPG_ERR_NO_ERROR){
309
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
310
gpgme_strsource(rc), gpgme_strerror(rc));
316
perror_plus("close");
318
gpgme_data_release(pgp_data);
323
fprintf_plus(stderr, "Initializing GPGME\n");
97
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
327
101
gpgme_check_version(NULL);
328
102
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));
103
if (rc != GPG_ERR_NO_ERROR){
104
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
105
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));
109
/* Set GPGME home directory */
110
rc = gpgme_get_engine_info (&engine_info);
111
if (rc != GPG_ERR_NO_ERROR){
112
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
113
gpgme_strsource(rc), gpgme_strerror(rc));
342
116
while(engine_info != NULL){
343
117
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
344
118
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
345
engine_info->file_name, tempdir);
119
engine_info->file_name, homedir);
348
122
engine_info = engine_info->next;
350
124
if(engine_info == NULL){
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));
125
fprintf(stderr, "Could not set home dir to %s\n", homedir);
129
/* Create new GPGME data buffer from packet buffer */
130
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
131
if (rc != GPG_ERR_NO_ERROR){
132
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
133
gpgme_strsource(rc), gpgme_strerror(rc));
400
137
/* Create new empty GPGME data buffer for the plaintext */
401
138
rc = gpgme_data_new(&dh_plain);
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;
139
if (rc != GPG_ERR_NO_ERROR){
140
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
141
gpgme_strsource(rc), gpgme_strerror(rc));
145
/* Create new GPGME "context" */
146
rc = gpgme_new(&ctx);
147
if (rc != GPG_ERR_NO_ERROR){
148
fprintf(stderr, "bad gpgme_new: %s: %s\n",
149
gpgme_strsource(rc), gpgme_strerror(rc));
153
/* Decrypt data from the FILE pointer to the plaintext data
155
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
156
if (rc != GPG_ERR_NO_ERROR){
157
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
158
gpgme_strsource(rc), gpgme_strerror(rc));
163
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
167
gpgme_decrypt_result_t result;
168
result = gpgme_op_decrypt_result(ctx);
170
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
172
fprintf(stderr, "Unsupported algorithm: %s\n",
173
result->unsupported_algorithm);
174
fprintf(stderr, "Wrong key usage: %d\n",
175
result->wrong_key_usage);
176
if(result->file_name != NULL){
177
fprintf(stderr, "File name: %s\n", result->file_name);
179
gpgme_recipient_t recipient;
180
recipient = result->recipients;
432
182
while(recipient != NULL){
433
fprintf_plus(stderr, "Public key algorithm: %s\n",
434
gpgme_pubkey_algo_name
435
(recipient->pubkey_algo));
436
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
437
fprintf_plus(stderr, "Secret key available: %s\n",
438
recipient->status == GPG_ERR_NO_SECKEY
183
fprintf(stderr, "Public key algorithm: %s\n",
184
gpgme_pubkey_algo_name(recipient->pubkey_algo));
185
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
186
fprintf(stderr, "Secret key available: %s\n",
187
recipient->status == GPG_ERR_NO_SECKEY
440
189
recipient = recipient->next;
448
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
195
/* Delete the GPGME FILE pointer cryptotext data buffer */
196
gpgme_data_release(dh_crypto);
451
198
/* Seek back to the beginning of the GPGME plaintext data buffer */
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 (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
200
perror("pgpme_data_seek");
460
plaintext_capacity = incbuffer(plaintext,
461
(size_t)plaintext_length,
463
if(plaintext_capacity == 0){
464
perror_plus("incbuffer");
465
plaintext_length = -1;
205
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
206
*new_packet = realloc(*new_packet,
207
(unsigned int)new_packet_capacity
209
if (*new_packet == NULL){
213
new_packet_capacity += BUFFER_SIZE;
469
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
216
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
471
218
/* Print the data, if any */
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);
223
perror("gpgme_data_read");
226
new_packet_length += ret;
229
/* FIXME: check characters before printing to screen so to not print
230
terminal control characters */
232
/* fprintf(stderr, "decrypted password is: "); */
233
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
234
/* fprintf(stderr, "\n"); */
497
237
/* Delete the GPGME plaintext data buffer */
498
238
gpgme_data_release(dh_plain);
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,
239
return new_packet_length;
242
static const char * safer_gnutls_strerror (int value) {
243
const char *ret = gnutls_strerror (value);
249
void debuggnutls(__attribute__((unused)) int level,
251
fprintf(stderr, "%s", string);
254
int initgnutls(encrypted_session *es){
531
fprintf_plus(stderr, "Initializing GnuTLS\n");
535
/* "Use a log level over 10 to enable all debugging options."
259
fprintf(stderr, "Initializing GnuTLS\n");
262
if ((ret = gnutls_global_init ())
263
!= GNUTLS_E_SUCCESS) {
264
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
538
269
gnutls_global_set_log_level(11);
539
270
gnutls_global_set_log_function(debuggnutls);
542
/* OpenPGP credentials */
543
ret = gnutls_certificate_allocate_credentials(&mc->cred);
544
if(ret != GNUTLS_E_SUCCESS){
545
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
546
safer_gnutls_strerror(ret));
273
/* openpgp credentials */
274
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
275
!= GNUTLS_E_SUCCESS) {
276
fprintf (stderr, "memory error: %s\n",
277
safer_gnutls_strerror(ret));
551
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
552
" secret key %s as GnuTLS credentials\n",
282
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
283
" and keyfile %s as GnuTLS credentials\n", certfile,
557
287
ret = gnutls_certificate_set_openpgp_key_file
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);
288
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
289
if (ret != GNUTLS_E_SUCCESS) {
291
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
293
ret, certfile, certkey);
294
fprintf(stdout, "The Error is: %s\n",
295
safer_gnutls_strerror(ret));
299
//GnuTLS server initialization
300
if ((ret = gnutls_dh_params_init (&es->dh_params))
301
!= GNUTLS_E_SUCCESS) {
302
fprintf (stderr, "Error in dh parameter initialization: %s\n",
303
safer_gnutls_strerror(ret));
307
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
308
!= GNUTLS_E_SUCCESS) {
309
fprintf (stderr, "Error in prime generation: %s\n",
310
safer_gnutls_strerror(ret));
314
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
316
// GnuTLS session creation
317
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
318
!= GNUTLS_E_SUCCESS){
319
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
320
safer_gnutls_strerror(ret));
323
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
324
!= GNUTLS_E_SUCCESS) {
325
fprintf(stderr, "Syntax error at: %s\n", err);
326
fprintf(stderr, "GnuTLS error: %s\n",
327
safer_gnutls_strerror(ret));
331
if ((ret = gnutls_credentials_set
332
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
333
!= GNUTLS_E_SUCCESS) {
334
fprintf(stderr, "Error setting a credentials set: %s\n",
335
safer_gnutls_strerror(ret));
812
339
/* ignore client certificate if any. */
813
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
340
gnutls_certificate_server_set_request (es->session,
343
gnutls_dh_set_prime_bits (es->session, DH_BITS);
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,
348
void empty_log(__attribute__((unused)) AvahiLogLevel level,
349
__attribute__((unused)) const char *txt){}
351
int start_mandos_communication(const char *ip, uint16_t port,
1033
352
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;
354
struct sockaddr_in6 to;
355
encrypted_session es;
1048
356
char *buffer = NULL;
1049
char *decrypted_buffer = NULL;
357
char *decrypted_buffer;
1050
358
size_t buffer_length = 0;
1051
359
size_t buffer_capacity = 0;
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");
360
ssize_t decrypted_buffer_size;
363
char interface[IF_NAMESIZE];
366
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
370
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
376
if(if_indextoname((unsigned int)if_index, interface) == NULL){
378
perror("if_indextoname");
384
fprintf(stderr, "Binding to interface %s\n", interface);
387
memset(&to,0,sizeof(to)); /* Spurious warning */
388
to.sin6_family = AF_INET6;
389
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
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){
395
fprintf(stderr, "Bad address: %s\n", ip);
398
to.sin6_port = htons(port); /* Spurious warning */
400
to.sin6_scope_id = (uint32_t)if_index;
403
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
404
/* char addrstr[INET6_ADDRSTRLEN]; */
405
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
406
/* sizeof(addrstr)) == NULL){ */
407
/* perror("inet_ntop"); */
409
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
410
/* addrstr, ntohs(to.sin6_port)); */
414
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
420
ret = initgnutls (&es);
426
gnutls_transport_set_ptr (es.session,
427
(gnutls_transport_ptr_t) tcp_sd);
430
fprintf(stderr, "Establishing TLS session with %s\n", ip);
433
ret = gnutls_handshake (es.session);
435
if (ret != GNUTLS_E_SUCCESS){
1334
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
437
fprintf(stderr, "\n*** Handshake failed ***\n");
1341
/* Read OpenPGP packet that contains the wanted password */
444
//Retrieve OpenPGP packet that contains the wanted password
1344
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
447
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
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,
452
if (buffer_length + BUFFER_SIZE > buffer_capacity){
453
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
458
buffer_capacity += BUFFER_SIZE;
461
ret = gnutls_record_recv
462
(es.session, buffer+buffer_length, BUFFER_SIZE);
1376
468
case GNUTLS_E_INTERRUPTED:
1377
469
case GNUTLS_E_AGAIN:
1379
471
case GNUTLS_E_REHANDSHAKE:
1381
ret = gnutls_handshake(session);
1387
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1389
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
472
ret = gnutls_handshake (es.session);
474
fprintf(stderr, "\n*** Handshake failed ***\n");
1397
fprintf_plus(stderr, "Unknown error while reading data from"
1398
" encrypted session with Mandos server\n");
1399
gnutls_bye(session, GNUTLS_SHUT_RDWR);
481
fprintf(stderr, "Unknown error while reading data from"
482
" encrypted session with mandos server\n");
484
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
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){
488
buffer_length += (size_t) ret;
492
if (buffer_length > 0){
493
decrypted_buffer_size = pgp_packet_decrypt(buffer,
497
if (decrypted_buffer_size >= 0){
1433
498
while(written < (size_t) decrypted_buffer_size){
1439
ret = (int)fwrite(decrypted_buffer + written, 1,
1440
(size_t)decrypted_buffer_size - written,
499
ret = (int)fwrite (decrypted_buffer + written, 1,
500
(size_t)decrypted_buffer_size - written,
1442
502
if(ret == 0 and ferror(stdout)){
1445
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
504
fprintf(stderr, "Error writing encrypted data: %s\n",
1451
510
written += (size_t)ret;
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);
512
free(decrypted_buffer);
521
fprintf(stderr, "Closing TLS session\n");
525
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
528
gnutls_deinit (es.session);
529
gnutls_certificate_free_credentials (es.cred);
530
gnutls_global_deinit ();
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
534
static AvahiSimplePoll *simple_poll = NULL;
535
static AvahiServer *server = NULL;
537
static void resolve_callback(
538
AvahiSServiceResolver *r,
539
AvahiIfIndex interface,
540
AVAHI_GCC_UNUSED AvahiProtocol protocol,
541
AvahiResolverEvent event,
545
const char *host_name,
546
const AvahiAddress *address,
548
AVAHI_GCC_UNUSED AvahiStringList *txt,
549
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
550
AVAHI_GCC_UNUSED void* userdata) {
552
assert(r); /* Spurious warning */
1517
554
/* Called whenever a service has been resolved successfully or
1521
avahi_s_service_resolver_free(r);
1527
559
case AVAHI_RESOLVER_FAILURE:
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)));
560
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
561
" type '%s' in domain '%s': %s\n", name, type, domain,
562
avahi_strerror(avahi_server_errno(server)));
1535
565
case AVAHI_RESOLVER_FOUND:
1537
567
char ip[AVAHI_ADDRESS_STR_MAX];
1538
568
avahi_address_snprint(ip, sizeof(ip), address);
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);
570
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
" port %d\n", name, host_name, ip, port);
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"
573
int ret = start_mandos_communication(ip, port, interface);
1560
579
avahi_s_service_resolver_free(r);
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");
582
static void browse_callback(
583
AvahiSServiceBrowser *b,
584
AvahiIfIndex interface,
585
AvahiProtocol protocol,
586
AvahiBrowserEvent event,
590
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
593
AvahiServer *s = userdata;
594
assert(b); /* Spurious warning */
596
/* Called whenever a new services becomes available on the LAN or
597
is removed from the LAN */
601
case AVAHI_BROWSER_FAILURE:
603
fprintf(stderr, "(Browser) %s\n",
604
avahi_strerror(avahi_server_errno(server)));
605
avahi_simple_poll_quit(simple_poll);
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;
608
case AVAHI_BROWSER_NEW:
609
/* We ignore the returned resolver object. In the callback
610
function we free it. If the server is terminated before
611
the callback function is called the server will free
612
the resolver for us. */
614
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
616
AVAHI_PROTO_INET6, 0,
617
resolve_callback, s)))
618
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
619
avahi_strerror(avahi_server_errno(s)));
622
case AVAHI_BROWSER_REMOVE:
625
case AVAHI_BROWSER_ALL_FOR_NOW:
626
case AVAHI_BROWSER_CACHE_EXHAUSTED:
631
/* combinds file name and path and returns the malloced new string. som sane checks could/should be added */
632
const char *combinepath(const char *first, const char *second){
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);
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;
634
tmp = malloc(strlen(first) + strlen(second) + 2);
640
if (first[0] != '\0' and first[strlen(first) - 1] != '/'){
648
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
2907
649
AvahiServerConfig config;
2908
/* Do not publish any local Zeroconf records */
650
AvahiSServiceBrowser *sb = NULL;
653
int returncode = EXIT_SUCCESS;
654
const char *interface = NULL;
655
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
656
char *connect_to = NULL;
659
static struct option long_options[] = {
660
{"debug", no_argument, (int *)&debug, 1},
661
{"connect", required_argument, 0, 'C'},
662
{"interface", required_argument, 0, 'i'},
663
{"certdir", required_argument, 0, 'd'},
664
{"certkey", required_argument, 0, 'c'},
665
{"certfile", required_argument, 0, 'k'},
668
int option_index = 0;
669
ret = getopt_long (argc, argv, "i:", long_options,
699
certfile = combinepath(certdir, certfile);
700
if (certfile == NULL){
704
if(interface != NULL){
705
if_index = (AvahiIfIndex) if_nametoindex(interface);
707
fprintf(stderr, "No such interface: \"%s\"\n", interface);
712
if(connect_to != NULL){
713
/* Connect directly, do not use Zeroconf */
714
/* (Mainly meant for debugging) */
715
char *address = strrchr(connect_to, ':');
717
fprintf(stderr, "No colon in address\n");
721
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
723
perror("Bad port number");
727
address = connect_to;
728
ret = start_mandos_communication(address, port, if_index);
736
certkey = combinepath(certdir, certkey);
737
if (certkey == NULL){
742
avahi_set_log_function(empty_log);
745
/* Initialize the psuedo-RNG */
746
srand((unsigned int) time(NULL));
748
/* Allocate main loop object */
749
if (!(simple_poll = avahi_simple_poll_new())) {
750
fprintf(stderr, "Failed to create simple poll object.\n");
755
/* Do not publish any local records */
2909
756
avahi_server_config_init(&config);
2910
757
config.publish_hinfo = 0;
2911
758
config.publish_addresses = 0;
2912
759
config.publish_workstation = 0;
2913
760
config.publish_domain = 0;
2915
762
/* Allocate a new server */
2916
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2917
&config, NULL, NULL, &ret);
2919
/* Free the Avahi configuration data */
763
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
764
&config, NULL, NULL, &error);
766
/* Free the configuration data */
2920
767
avahi_server_config_free(&config);
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());
769
/* Check if creating the server object succeeded */
771
fprintf(stderr, "Failed to create server: %s\n",
772
avahi_strerror(error));
773
returncode = EXIT_FAILURE;
777
/* Create the service browser */
778
sb = avahi_s_service_browser_new(server, if_index,
780
"_mandos._tcp", NULL, 0,
781
browse_callback, server);
783
fprintf(stderr, "Failed to create service browser: %s\n",
784
avahi_strerror(avahi_server_errno(server)));
785
returncode = EXIT_FAILURE;
789
/* Run the main loop */
792
fprintf(stderr, "Starting avahi loop search\n");
795
avahi_simple_poll_loop(simple_poll);
800
fprintf(stderr, "%s exiting\n", argv[0]);
805
avahi_s_service_browser_free(sb);
808
avahi_server_free(server);
811
avahi_simple_poll_free(simple_poll);