47
117
#include <avahi-common/malloc.h>
48
118
#include <avahi-common/error.h>
51
#include <sys/types.h> /* socket(), inet_pton() */
52
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
53
struct in6_addr, inet_pton() */
54
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
55
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
57
#include <unistd.h> /* close() */
58
#include <netinet/in.h>
59
#include <stdbool.h> /* true */
60
#include <string.h> /* memset */
61
#include <arpa/inet.h> /* inet_pton() */
62
#include <iso646.h> /* not */
65
#include <errno.h> /* perror() */
72
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
74
#define CERTFILE CERT_ROOT "openpgp-client.txt"
75
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
121
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
124
init_gnutls_session(),
126
#include <gnutls/openpgp.h>
127
/* gnutls_certificate_set_openpgp_key_file(),
128
GNUTLS_OPENPGP_FMT_BASE64 */
131
#include <gpgme.h> /* All GPGME types, constants and
134
GPGME_PROTOCOL_OpenPGP,
76
137
#define BUFFER_SIZE 256
139
#define PATHDIR "/conf/conf.d/mandos"
140
#define SECKEY "seckey.txt"
141
#define PUBKEY "pubkey.txt"
142
#define HOOKDIR "/lib/mandos/network-hooks.d"
79
144
bool debug = false;
145
static const char mandos_protocol_version[] = "1";
146
const char *argp_program_version = "mandos-client " VERSION;
147
const char *argp_program_bug_address = "<mandos@recompile.se>";
148
static const char sys_class_net[] = "/sys/class/net";
149
char *connect_to = NULL;
150
const char *hookdir = HOOKDIR;
155
/* Doubly linked list that need to be circularly linked when used */
156
typedef struct server{
159
AvahiIfIndex if_index;
161
struct timespec last_seen;
166
/* Used for passing in values through the Avahi callback functions */
82
gnutls_session_t session;
83
169
gnutls_certificate_credentials_t cred;
170
unsigned int dh_bits;
84
171
gnutls_dh_params_t dh_params;
88
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
char **new_packet, const char *homedir){
90
gpgme_data_t dh_crypto, dh_plain;
172
const char *priority;
174
server *current_server;
176
size_t interfaces_size;
179
/* global so signal handler can reach it*/
180
AvahiSimplePoll *simple_poll;
182
sig_atomic_t quit_now = 0;
183
int signal_received = 0;
185
/* Function to use when printing errors */
186
void perror_plus(const char *print_text){
188
fprintf(stderr, "Mandos plugin %s: ",
189
program_invocation_short_name);
194
__attribute__((format (gnu_printf, 2, 3), nonnull))
195
int fprintf_plus(FILE *stream, const char *format, ...){
197
va_start (ap, format);
199
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
200
program_invocation_short_name));
201
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
205
* Make additional room in "buffer" for at least BUFFER_SIZE more
206
* bytes. "buffer_capacity" is how much is currently allocated,
207
* "buffer_length" is how much is already used.
209
__attribute__((nonnull, warn_unused_result))
210
size_t incbuffer(char **buffer, size_t buffer_length,
211
size_t buffer_capacity){
212
if(buffer_length + BUFFER_SIZE > buffer_capacity){
213
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
215
int old_errno = errno;
222
buffer_capacity += BUFFER_SIZE;
224
return buffer_capacity;
227
/* Add server to set of servers to retry periodically */
228
__attribute__((nonnull, warn_unused_result))
229
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
230
int af, server **current_server){
232
server *new_server = malloc(sizeof(server));
233
if(new_server == NULL){
234
perror_plus("malloc");
237
*new_server = (server){ .ip = strdup(ip),
239
.if_index = if_index,
241
if(new_server->ip == NULL){
242
perror_plus("strdup");
246
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
248
perror_plus("clock_gettime");
250
#pragma GCC diagnostic push
251
#pragma GCC diagnostic ignored "-Wcast-qual"
253
free((char *)(new_server->ip));
255
#pragma GCC diagnostic pop
260
/* Special case of first server */
261
if(*current_server == NULL){
262
new_server->next = new_server;
263
new_server->prev = new_server;
264
*current_server = new_server;
266
/* Place the new server last in the list */
267
new_server->next = *current_server;
268
new_server->prev = (*current_server)->prev;
269
new_server->prev->next = new_server;
270
(*current_server)->prev = new_server;
278
__attribute__((nonnull, warn_unused_result))
279
static bool init_gpgme(const char * const seckey,
280
const char * const pubkey,
281
const char * const tempdir,
94
ssize_t new_packet_capacity = 0;
95
ssize_t new_packet_length = 0;
96
284
gpgme_engine_info_t engine_info;
99
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
287
* Helper function to insert pub and seckey to the engine keyring.
289
bool import_key(const char * const filename){
292
gpgme_data_t pgp_data;
294
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
300
rc = gpgme_data_new_from_fd(&pgp_data, fd);
301
if(rc != GPG_ERR_NO_ERROR){
302
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
303
gpgme_strsource(rc), gpgme_strerror(rc));
307
rc = gpgme_op_import(mc->ctx, pgp_data);
308
if(rc != GPG_ERR_NO_ERROR){
309
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
310
gpgme_strsource(rc), gpgme_strerror(rc));
316
perror_plus("close");
318
gpgme_data_release(pgp_data);
323
fprintf_plus(stderr, "Initializing GPGME\n");
103
327
gpgme_check_version(NULL);
104
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
328
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
329
if(rc != GPG_ERR_NO_ERROR){
330
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
331
gpgme_strsource(rc), gpgme_strerror(rc));
106
/* Set GPGME home directory */
107
rc = gpgme_get_engine_info (&engine_info);
108
if (rc != GPG_ERR_NO_ERROR){
109
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
110
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));
113
342
while(engine_info != NULL){
114
343
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
115
344
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
116
engine_info->file_name, homedir);
345
engine_info->file_name, tempdir);
119
348
engine_info = engine_info->next;
121
350
if(engine_info == NULL){
122
fprintf(stderr, "Could not set home dir to %s\n", homedir);
126
/* Create new GPGME data buffer from packet buffer */
127
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
128
if (rc != GPG_ERR_NO_ERROR){
129
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
130
gpgme_strsource(rc), gpgme_strerror(rc));
351
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
356
/* Create new GPGME "context" */
357
rc = gpgme_new(&(mc->ctx));
358
if(rc != GPG_ERR_NO_ERROR){
359
fprintf_plus(stderr, "Mandos plugin mandos-client: "
360
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
365
if(not import_key(pubkey) or not import_key(seckey)){
373
* Decrypt OpenPGP data.
374
* Returns -1 on error
376
__attribute__((nonnull, warn_unused_result))
377
static ssize_t pgp_packet_decrypt(const char *cryptotext,
381
gpgme_data_t dh_crypto, dh_plain;
384
size_t plaintext_capacity = 0;
385
ssize_t plaintext_length = 0;
388
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
391
/* Create new GPGME data buffer from memory cryptotext */
392
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
394
if(rc != GPG_ERR_NO_ERROR){
395
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
396
gpgme_strsource(rc), gpgme_strerror(rc));
134
400
/* Create new empty GPGME data buffer for the plaintext */
135
401
rc = gpgme_data_new(&dh_plain);
136
if (rc != GPG_ERR_NO_ERROR){
137
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
138
gpgme_strsource(rc), gpgme_strerror(rc));
142
/* Create new GPGME "context" */
143
rc = gpgme_new(&ctx);
144
if (rc != GPG_ERR_NO_ERROR){
145
fprintf(stderr, "bad gpgme_new: %s: %s\n",
146
gpgme_strsource(rc), gpgme_strerror(rc));
150
/* Decrypt data from the FILE pointer to the plaintext data
152
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
153
if (rc != GPG_ERR_NO_ERROR){
154
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
155
gpgme_strsource(rc), gpgme_strerror(rc));
160
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
164
gpgme_decrypt_result_t result;
165
result = gpgme_op_decrypt_result(ctx);
167
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
169
fprintf(stderr, "Unsupported algorithm: %s\n",
170
result->unsupported_algorithm);
171
fprintf(stderr, "Wrong key usage: %d\n",
172
result->wrong_key_usage);
173
if(result->file_name != NULL){
174
fprintf(stderr, "File name: %s\n", result->file_name);
176
gpgme_recipient_t recipient;
177
recipient = result->recipients;
402
if(rc != GPG_ERR_NO_ERROR){
403
fprintf_plus(stderr, "Mandos plugin mandos-client: "
404
"bad gpgme_data_new: %s: %s\n",
405
gpgme_strsource(rc), gpgme_strerror(rc));
406
gpgme_data_release(dh_crypto);
410
/* Decrypt data from the cryptotext data buffer to the plaintext
412
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
413
if(rc != GPG_ERR_NO_ERROR){
414
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
415
gpgme_strsource(rc), gpgme_strerror(rc));
416
plaintext_length = -1;
418
gpgme_decrypt_result_t result;
419
result = gpgme_op_decrypt_result(mc->ctx);
421
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
423
if(result->unsupported_algorithm != NULL) {
424
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
425
result->unsupported_algorithm);
427
fprintf_plus(stderr, "Wrong key usage: %u\n",
428
result->wrong_key_usage);
429
if(result->file_name != NULL){
430
fprintf_plus(stderr, "File name: %s\n", result->file_name);
432
gpgme_recipient_t recipient;
433
recipient = result->recipients;
179
434
while(recipient != NULL){
180
fprintf(stderr, "Public key algorithm: %s\n",
181
gpgme_pubkey_algo_name(recipient->pubkey_algo));
182
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
183
fprintf(stderr, "Secret key available: %s\n",
184
recipient->status == GPG_ERR_NO_SECKEY
435
fprintf_plus(stderr, "Public key algorithm: %s\n",
436
gpgme_pubkey_algo_name
437
(recipient->pubkey_algo));
438
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
439
fprintf_plus(stderr, "Secret key available: %s\n",
440
recipient->status == GPG_ERR_NO_SECKEY
186
442
recipient = recipient->next;
192
/* Delete the GPGME FILE pointer cryptotext data buffer */
193
gpgme_data_release(dh_crypto);
450
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
195
453
/* Seek back to the beginning of the GPGME plaintext data buffer */
196
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
454
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
455
perror_plus("gpgme_data_seek");
456
plaintext_length = -1;
200
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
201
*new_packet = realloc(*new_packet,
202
(unsigned int)new_packet_capacity
204
if (*new_packet == NULL){
208
new_packet_capacity += BUFFER_SIZE;
462
plaintext_capacity = incbuffer(plaintext,
463
(size_t)plaintext_length,
465
if(plaintext_capacity == 0){
466
perror_plus("incbuffer");
467
plaintext_length = -1;
211
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
471
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
213
473
/* Print the data, if any */
218
perror("gpgme_data_read");
221
new_packet_length += ret;
224
/* FIXME: check characters before printing to screen so to not print
225
terminal control characters */
227
/* fprintf(stderr, "decrypted password is: "); */
228
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
229
/* fprintf(stderr, "\n"); */
479
perror_plus("gpgme_data_read");
480
plaintext_length = -1;
483
plaintext_length += ret;
487
fprintf_plus(stderr, "Decrypted password is: ");
488
for(ssize_t i = 0; i < plaintext_length; i++){
489
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
491
fprintf(stderr, "\n");
496
/* Delete the GPGME cryptotext data buffer */
497
gpgme_data_release(dh_crypto);
232
499
/* Delete the GPGME plaintext data buffer */
233
500
gpgme_data_release(dh_plain);
234
return new_packet_length;
237
static const char * safer_gnutls_strerror (int value) {
238
const char *ret = gnutls_strerror (value);
244
void debuggnutls(__attribute__((unused)) int level,
246
fprintf(stderr, "%s", string);
249
int initgnutls(encrypted_session *es){
501
return plaintext_length;
504
__attribute__((warn_unused_result, const))
505
static const char *safe_string(const char *str){
511
__attribute__((warn_unused_result))
512
static const char *safer_gnutls_strerror(int value){
513
const char *ret = gnutls_strerror(value);
514
return safe_string(ret);
517
/* GnuTLS log function callback */
518
__attribute__((nonnull))
519
static void debuggnutls(__attribute__((unused)) int level,
521
fprintf_plus(stderr, "GnuTLS: %s", string);
524
__attribute__((nonnull(1, 2, 4), warn_unused_result))
525
static int init_gnutls_global(const char *pubkeyfilename,
526
const char *seckeyfilename,
527
const char *dhparamsfilename,
254
fprintf(stderr, "Initializing GnuTLS\n");
257
if ((ret = gnutls_global_init ())
258
!= GNUTLS_E_SUCCESS) {
259
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
533
fprintf_plus(stderr, "Initializing GnuTLS\n");
537
/* "Use a log level over 10 to enable all debugging options."
264
540
gnutls_global_set_log_level(11);
265
541
gnutls_global_set_log_function(debuggnutls);
268
/* openpgp credentials */
269
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
270
!= GNUTLS_E_SUCCESS) {
271
fprintf (stderr, "memory error: %s\n",
272
safer_gnutls_strerror(ret));
544
/* OpenPGP credentials */
545
ret = gnutls_certificate_allocate_credentials(&mc->cred);
546
if(ret != GNUTLS_E_SUCCESS){
547
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
548
safer_gnutls_strerror(ret));
277
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
278
" 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",
282
559
ret = gnutls_certificate_set_openpgp_key_file
283
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
284
if (ret != GNUTLS_E_SUCCESS) {
286
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
288
ret, CERTFILE, KEYFILE);
289
fprintf(stdout, "The Error is: %s\n",
290
safer_gnutls_strerror(ret));
294
//GnuTLS server initialization
295
if ((ret = gnutls_dh_params_init (&es->dh_params))
296
!= GNUTLS_E_SUCCESS) {
297
fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
safer_gnutls_strerror(ret));
302
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
303
!= GNUTLS_E_SUCCESS) {
304
fprintf (stderr, "Error in prime generation: %s\n",
305
safer_gnutls_strerror(ret));
309
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
311
// GnuTLS session creation
312
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
313
!= GNUTLS_E_SUCCESS){
314
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
315
safer_gnutls_strerror(ret));
318
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
319
!= GNUTLS_E_SUCCESS) {
320
fprintf(stderr, "Syntax error at: %s\n", err);
321
fprintf(stderr, "GnuTLS error: %s\n",
322
safer_gnutls_strerror(ret));
326
if ((ret = gnutls_credentials_set
327
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Error setting a credentials set: %s\n",
330
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
ret = close(dhpfile);
620
perror_plus("close");
622
if(params.data == NULL){
623
dhparamsfilename = NULL;
625
if(dhparamsfilename == NULL){
628
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
629
GNUTLS_X509_FMT_PEM);
630
if(ret != GNUTLS_E_SUCCESS){
631
fprintf_plus(stderr, "Failed to parse DH parameters in file"
632
" \"%s\": %s\n", dhparamsfilename,
633
safer_gnutls_strerror(ret));
634
dhparamsfilename = NULL;
639
if(dhparamsfilename == NULL){
640
if(mc->dh_bits == 0){
641
/* Find out the optimal number of DH bits */
642
/* Try to read the private key file */
643
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
645
int secfile = open(seckeyfilename, O_RDONLY);
650
size_t buffer_capacity = 0;
652
buffer_capacity = incbuffer((char **)&buffer.data,
654
(size_t)buffer_capacity);
655
if(buffer_capacity == 0){
656
perror_plus("incbuffer");
661
ssize_t bytes_read = read(secfile,
662
buffer.data + buffer.size,
668
/* check bytes_read for failure */
675
buffer.size += (unsigned int)bytes_read;
679
/* If successful, use buffer to parse private key */
680
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
681
if(buffer.data != NULL){
683
gnutls_openpgp_privkey_t privkey = NULL;
684
ret = gnutls_openpgp_privkey_init(&privkey);
685
if(ret != GNUTLS_E_SUCCESS){
686
fprintf_plus(stderr, "Error initializing OpenPGP key"
688
safer_gnutls_strerror(ret));
692
ret = gnutls_openpgp_privkey_import
693
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
694
if(ret != GNUTLS_E_SUCCESS){
695
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
696
safer_gnutls_strerror(ret));
702
/* Use private key to suggest an appropriate
704
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
705
gnutls_openpgp_privkey_deinit(privkey);
707
fprintf_plus(stderr, "This OpenPGP key implies using"
708
" a GnuTLS security parameter \"%s\".\n",
709
safe_string(gnutls_sec_param_get_name
715
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
716
/* Err on the side of caution */
717
sec_param = GNUTLS_SEC_PARAM_ULTRA;
719
fprintf_plus(stderr, "Falling back to security parameter"
721
safe_string(gnutls_sec_param_get_name
726
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
730
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
731
" implies %u DH bits; using that.\n",
732
safe_string(gnutls_sec_param_get_name
737
fprintf_plus(stderr, "Failed to get implied number of DH"
738
" bits for security parameter \"%s\"): %s\n",
739
safe_string(gnutls_sec_param_get_name
741
safer_gnutls_strerror(ret));
745
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
748
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
749
if(ret != GNUTLS_E_SUCCESS){
750
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
751
" bits): %s\n", mc->dh_bits,
752
safer_gnutls_strerror(ret));
756
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
762
gnutls_certificate_free_credentials(mc->cred);
763
gnutls_dh_params_deinit(mc->dh_params);
767
__attribute__((nonnull, warn_unused_result))
768
static int init_gnutls_session(gnutls_session_t *session,
771
/* GnuTLS session creation */
773
ret = gnutls_init(session, GNUTLS_SERVER);
777
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
778
if(ret != GNUTLS_E_SUCCESS){
780
"Error in GnuTLS session initialization: %s\n",
781
safer_gnutls_strerror(ret));
787
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
789
gnutls_deinit(*session);
792
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
793
if(ret != GNUTLS_E_SUCCESS){
794
fprintf_plus(stderr, "Syntax error at: %s\n", err);
795
fprintf_plus(stderr, "GnuTLS error: %s\n",
796
safer_gnutls_strerror(ret));
797
gnutls_deinit(*session);
803
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
806
gnutls_deinit(*session);
809
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
810
if(ret != GNUTLS_E_SUCCESS){
811
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
812
safer_gnutls_strerror(ret));
813
gnutls_deinit(*session);
334
817
/* ignore client certificate if any. */
335
gnutls_certificate_server_set_request (es->session,
338
gnutls_dh_set_prime_bits (es->session, DH_BITS);
818
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
343
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
__attribute__((unused)) const char *txt){}
346
int start_mandos_communication(const char *ip, uint16_t port,
347
unsigned int if_index){
349
struct sockaddr_in6 to;
350
encrypted_session es;
823
/* Avahi log function callback */
824
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
825
__attribute__((unused)) const char *txt){}
827
/* Set effective uid to 0, return errno */
828
__attribute__((warn_unused_result))
829
int raise_privileges(void){
830
int old_errno = errno;
832
if(seteuid(0) == -1){
839
/* Set effective and real user ID to 0. Return errno. */
840
__attribute__((warn_unused_result))
841
int raise_privileges_permanently(void){
842
int old_errno = errno;
843
int ret = raise_privileges();
855
/* Set effective user ID to unprivileged saved user ID */
856
__attribute__((warn_unused_result))
857
int lower_privileges(void){
858
int old_errno = errno;
860
if(seteuid(uid) == -1){
867
/* Lower privileges permanently */
868
__attribute__((warn_unused_result))
869
int lower_privileges_permanently(void){
870
int old_errno = errno;
872
if(setuid(uid) == -1){
879
/* Helper function to add_local_route() and delete_local_route() */
880
__attribute__((nonnull, warn_unused_result))
881
static bool add_delete_local_route(const bool add,
883
AvahiIfIndex if_index){
885
char helper[] = "mandos-client-iprouteadddel";
886
char add_arg[] = "add";
887
char delete_arg[] = "delete";
888
char debug_flag[] = "--debug";
889
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
890
if(pluginhelperdir == NULL){
892
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
893
" variable not set; cannot run helper\n");
898
char interface[IF_NAMESIZE];
899
if(if_indextoname((unsigned int)if_index, interface) == NULL){
900
perror_plus("if_indextoname");
904
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
906
perror_plus("open(\"/dev/null\", O_RDONLY)");
912
/* Raise privileges */
913
errno = raise_privileges_permanently();
915
perror_plus("Failed to raise privileges");
916
/* _exit(EX_NOPERM); */
922
perror_plus("setgid");
925
/* Reset supplementary groups */
927
ret = setgroups(0, NULL);
929
perror_plus("setgroups");
933
ret = dup2(devnull, STDIN_FILENO);
935
perror_plus("dup2(devnull, STDIN_FILENO)");
938
ret = close(devnull);
940
perror_plus("close");
943
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
945
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
948
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
953
if(helperdir_fd == -1){
955
_exit(EX_UNAVAILABLE);
957
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
960
perror_plus("openat");
962
_exit(EX_UNAVAILABLE);
966
#pragma GCC diagnostic push
967
#pragma GCC diagnostic ignored "-Wcast-qual"
969
if(fexecve(helper_fd, (char *const [])
970
{ helper, add ? add_arg : delete_arg, (char *)address,
971
interface, debug ? debug_flag : NULL, NULL },
974
#pragma GCC diagnostic pop
976
perror_plus("fexecve");
988
pret = waitpid(pid, &status, 0);
989
if(pret == -1 and errno == EINTR and quit_now){
990
int errno_raising = 0;
991
if((errno = raise_privileges()) != 0){
992
errno_raising = errno;
993
perror_plus("Failed to raise privileges in order to"
994
" kill helper program");
996
if(kill(pid, SIGTERM) == -1){
999
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
1000
perror_plus("Failed to lower privileges after killing"
1005
} while(pret == -1 and errno == EINTR);
1007
perror_plus("waitpid");
1010
if(WIFEXITED(status)){
1011
if(WEXITSTATUS(status) != 0){
1012
fprintf_plus(stderr, "Error: iprouteadddel exited"
1013
" with status %d\n", WEXITSTATUS(status));
1018
if(WIFSIGNALED(status)){
1019
fprintf_plus(stderr, "Error: iprouteadddel died by"
1020
" signal %d\n", WTERMSIG(status));
1023
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1027
__attribute__((nonnull, warn_unused_result))
1028
static bool add_local_route(const char *address,
1029
AvahiIfIndex if_index){
1031
fprintf_plus(stderr, "Adding route to %s\n", address);
1033
return add_delete_local_route(true, address, if_index);
1036
__attribute__((nonnull, warn_unused_result))
1037
static bool delete_local_route(const char *address,
1038
AvahiIfIndex if_index){
1040
fprintf_plus(stderr, "Removing route to %s\n", address);
1042
return add_delete_local_route(false, address, if_index);
1045
/* Called when a Mandos server is found */
1046
__attribute__((nonnull, warn_unused_result))
1047
static int start_mandos_communication(const char *ip, in_port_t port,
1048
AvahiIfIndex if_index,
1049
int af, mandos_context *mc){
1050
int ret, tcp_sd = -1;
1052
struct sockaddr_storage to;
351
1053
char *buffer = NULL;
352
char *decrypted_buffer;
1054
char *decrypted_buffer = NULL;
353
1055
size_t buffer_length = 0;
354
1056
size_t buffer_capacity = 0;
355
ssize_t decrypted_buffer_size;
358
char interface[IF_NAMESIZE];
361
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
364
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
370
if(if_indextoname(if_index, interface) == NULL){
372
perror("if_indextoname");
378
fprintf(stderr, "Binding to interface %s\n", interface);
381
memset(&to,0,sizeof(to)); /* Spurious warning */
382
to.sin6_family = AF_INET6;
383
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
1059
gnutls_session_t session;
1060
int pf; /* Protocol family */
1061
bool route_added = false;
1078
fprintf_plus(stderr, "Bad address family: %d\n", af);
1083
/* If the interface is specified and we have a list of interfaces */
1084
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1085
/* Check if the interface is one of the interfaces we are using */
1088
char *interface = NULL;
1089
while((interface = argz_next(mc->interfaces,
1090
mc->interfaces_size,
1092
if(if_nametoindex(interface) == (unsigned int)if_index){
1099
/* This interface does not match any in the list, so we don't
1100
connect to the server */
1102
char interface[IF_NAMESIZE];
1103
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1104
perror_plus("if_indextoname");
1106
fprintf_plus(stderr, "Skipping server on non-used interface"
1108
if_indextoname((unsigned int)if_index,
1116
ret = init_gnutls_session(&session, mc);
1122
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1123
PRIuMAX "\n", ip, (uintmax_t)port);
1126
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1129
perror_plus("socket");
1140
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1141
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1142
ret = inet_pton(af, ip, &to6->sin6_addr);
1144
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1145
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1146
ret = inet_pton(af, ip, &to4->sin_addr);
1150
perror_plus("inet_pton");
389
fprintf(stderr, "Bad address: %s\n", ip);
392
to.sin6_port = htons(port); /* Spurious warning */
394
to.sin6_scope_id = (uint32_t)if_index;
397
fprintf(stderr, "Connection to: %s\n", ip);
400
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
406
ret = initgnutls (&es);
412
gnutls_transport_set_ptr (es.session,
413
(gnutls_transport_ptr_t) tcp_sd);
416
fprintf(stderr, "Establishing TLS session with %s\n", ip);
419
ret = gnutls_handshake (es.session);
421
if (ret != GNUTLS_E_SUCCESS){
1156
fprintf_plus(stderr, "Bad address: %s\n", ip);
1161
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1162
if(IN6_IS_ADDR_LINKLOCAL
1163
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1164
if(if_index == AVAHI_IF_UNSPEC){
1165
fprintf_plus(stderr, "An IPv6 link-local address is"
1166
" incomplete without a network interface\n");
1170
/* Set the network interface number as scope */
1171
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1174
((struct sockaddr_in *)&to)->sin_port = htons(port);
1183
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1184
char interface[IF_NAMESIZE];
1185
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1186
perror_plus("if_indextoname");
1188
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1189
"\n", ip, interface, (uintmax_t)port);
1192
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1193
ip, (uintmax_t)port);
1195
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1196
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1198
ret = getnameinfo((struct sockaddr *)&to,
1199
sizeof(struct sockaddr_in6),
1200
addrstr, sizeof(addrstr), NULL, 0,
1203
ret = getnameinfo((struct sockaddr *)&to,
1204
sizeof(struct sockaddr_in),
1205
addrstr, sizeof(addrstr), NULL, 0,
1208
if(ret == EAI_SYSTEM){
1209
perror_plus("getnameinfo");
1210
} else if(ret != 0) {
1211
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1212
} else if(strcmp(addrstr, ip) != 0){
1213
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1224
ret = connect(tcp_sd, (struct sockaddr *)&to,
1225
sizeof(struct sockaddr_in6));
1227
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1228
sizeof(struct sockaddr_in));
1231
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1232
and if_index != AVAHI_IF_UNSPEC
1233
and connect_to == NULL
1234
and not route_added and
1235
((af == AF_INET6 and not
1236
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1238
or (af == AF_INET and
1239
/* Not a a IPv4LL address */
1240
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1241
& 0xFFFF0000L) != 0xA9FE0000L))){
1242
/* Work around Avahi bug - Avahi does not announce link-local
1243
addresses if it has a global address, so local hosts with
1244
*only* a link-local address (e.g. Mandos clients) cannot
1245
connect to a Mandos server announced by Avahi on a server
1246
host with a global address. Work around this by retrying
1247
with an explicit route added with the server's address.
1249
Avahi bug reference:
1250
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1251
https://bugs.debian.org/587961
1254
fprintf_plus(stderr, "Mandos server unreachable, trying"
1258
route_added = add_local_route(ip, if_index);
1264
if(errno != ECONNREFUSED or debug){
1266
perror_plus("connect");
1279
const char *out = mandos_protocol_version;
1282
size_t out_size = strlen(out);
1283
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1284
out_size - written));
1287
perror_plus("write");
1291
written += (size_t)ret;
1292
if(written < out_size){
1295
if(out == mandos_protocol_version){
1310
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1318
/* This casting via intptr_t is to eliminate warning about casting
1319
an int to a pointer type. This is exactly how the GnuTLS Guile
1320
function "set-session-transport-fd!" does it. */
1321
gnutls_transport_set_ptr(session,
1322
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1330
ret = gnutls_handshake(session);
1335
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1337
if(ret != GNUTLS_E_SUCCESS){
423
fprintf(stderr, "\n*** Handshake failed ***\n");
1339
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
430
//Retrieve OpenPGP packet that contains the wanted password
1346
/* Read OpenPGP packet that contains the wanted password */
433
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1349
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
438
if (buffer_length + BUFFER_SIZE > buffer_capacity){
439
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
444
buffer_capacity += BUFFER_SIZE;
447
ret = gnutls_record_recv
448
(es.session, buffer+buffer_length, BUFFER_SIZE);
1360
buffer_capacity = incbuffer(&buffer, buffer_length,
1362
if(buffer_capacity == 0){
1364
perror_plus("incbuffer");
1374
sret = gnutls_record_recv(session, buffer+buffer_length,
454
1381
case GNUTLS_E_INTERRUPTED:
455
1382
case GNUTLS_E_AGAIN:
457
1384
case GNUTLS_E_REHANDSHAKE:
458
ret = gnutls_handshake (es.session);
460
fprintf(stderr, "\n*** Handshake failed ***\n");
1386
ret = gnutls_handshake(session);
1392
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1394
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
467
fprintf(stderr, "Unknown error while reading data from"
468
" encrypted session with mandos server\n");
470
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1402
fprintf_plus(stderr, "Unknown error while reading data from"
1403
" encrypted session with Mandos server\n");
1404
gnutls_bye(session, GNUTLS_SHUT_RDWR);
474
buffer_length += (size_t) ret;
478
if (buffer_length > 0){
479
decrypted_buffer_size = pgp_packet_decrypt(buffer,
483
if (decrypted_buffer_size >= 0){
484
while(written < decrypted_buffer_size){
485
ret = (int)fwrite (decrypted_buffer + written, 1,
486
(size_t)decrypted_buffer_size - written,
1409
buffer_length += (size_t) sret;
1414
fprintf_plus(stderr, "Closing TLS session\n");
1423
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1428
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1430
if(buffer_length > 0){
1431
ssize_t decrypted_buffer_size;
1432
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1433
&decrypted_buffer, mc);
1434
if(decrypted_buffer_size >= 0){
1438
while(written < (size_t) decrypted_buffer_size){
1444
ret = (int)fwrite(decrypted_buffer + written, 1,
1445
(size_t)decrypted_buffer_size - written,
488
1447
if(ret == 0 and ferror(stdout)){
490
fprintf(stderr, "Error writing encrypted data: %s\n",
1450
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
496
1456
written += (size_t)ret;
498
free(decrypted_buffer);
1458
ret = fflush(stdout);
1462
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1472
/* Shutdown procedure */
1477
if(not delete_local_route(ip, if_index)){
1478
fprintf_plus(stderr, "Failed to delete local route to %s on"
1479
" interface %d", ip, if_index);
1483
free(decrypted_buffer);
1486
ret = close(tcp_sd);
1492
perror_plus("close");
1494
gnutls_deinit(session);
507
fprintf(stderr, "Closing TLS session\n");
511
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
514
gnutls_deinit (es.session);
515
gnutls_certificate_free_credentials (es.cred);
516
gnutls_global_deinit ();
520
static AvahiSimplePoll *simple_poll = NULL;
521
static AvahiServer *server = NULL;
523
static void resolve_callback(
524
AvahiSServiceResolver *r,
525
AvahiIfIndex interface,
526
AVAHI_GCC_UNUSED AvahiProtocol protocol,
527
AvahiResolverEvent event,
531
const char *host_name,
532
const AvahiAddress *address,
534
AVAHI_GCC_UNUSED AvahiStringList *txt,
535
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
536
AVAHI_GCC_UNUSED void* userdata) {
538
assert(r); /* Spurious warning */
1504
static void resolve_callback(AvahiSServiceResolver *r,
1505
AvahiIfIndex interface,
1506
AvahiProtocol proto,
1507
AvahiResolverEvent event,
1511
const char *host_name,
1512
const AvahiAddress *address,
1514
AVAHI_GCC_UNUSED AvahiStringList *txt,
1515
AVAHI_GCC_UNUSED AvahiLookupResultFlags
540
1522
/* Called whenever a service has been resolved successfully or
1526
avahi_s_service_resolver_free(r);
545
1532
case AVAHI_RESOLVER_FAILURE:
546
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
547
" type '%s' in domain '%s': %s\n", name, type, domain,
548
avahi_strerror(avahi_server_errno(server)));
1533
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1534
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1536
avahi_strerror(avahi_server_errno
1537
(((mandos_context*)mc)->server)));
551
1540
case AVAHI_RESOLVER_FOUND:
553
1542
char ip[AVAHI_ADDRESS_STR_MAX];
554
1543
avahi_address_snprint(ip, sizeof(ip), address);
556
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
557
" port %d\n", name, host_name, ip, port);
1545
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1546
PRIdMAX ") on port %" PRIu16 "\n", name,
1547
host_name, ip, (intmax_t)interface, port);
559
int ret = start_mandos_communication(ip, port,
560
(unsigned int) interface);
1549
int ret = start_mandos_communication(ip, (in_port_t)port,
1551
avahi_proto_to_af(proto),
1554
avahi_simple_poll_quit(simple_poll);
1556
if(not add_server(ip, (in_port_t)port, interface,
1557
avahi_proto_to_af(proto),
1558
&((mandos_context*)mc)->current_server)){
1559
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
566
1565
avahi_s_service_resolver_free(r);
569
static void browse_callback(
570
AvahiSServiceBrowser *b,
571
AvahiIfIndex interface,
572
AvahiProtocol protocol,
573
AvahiBrowserEvent event,
577
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
580
AvahiServer *s = userdata;
581
assert(b); /* Spurious warning */
583
/* Called whenever a new services becomes available on the LAN or
584
is removed from the LAN */
1568
static void browse_callback(AvahiSServiceBrowser *b,
1569
AvahiIfIndex interface,
1570
AvahiProtocol protocol,
1571
AvahiBrowserEvent event,
1575
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1582
/* Called whenever a new services becomes available on the LAN or
1583
is removed from the LAN */
1591
case AVAHI_BROWSER_FAILURE:
1593
fprintf_plus(stderr, "(Avahi browser) %s\n",
1594
avahi_strerror(avahi_server_errno
1595
(((mandos_context*)mc)->server)));
1596
avahi_simple_poll_quit(simple_poll);
1599
case AVAHI_BROWSER_NEW:
1600
/* We ignore the returned Avahi resolver object. In the callback
1601
function we free it. If the Avahi server is terminated before
1602
the callback function is called the Avahi server will free the
1605
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1606
interface, protocol, name, type,
1607
domain, protocol, 0,
1608
resolve_callback, mc) == NULL)
1609
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1611
avahi_strerror(avahi_server_errno
1612
(((mandos_context*)mc)->server)));
1615
case AVAHI_BROWSER_REMOVE:
1618
case AVAHI_BROWSER_ALL_FOR_NOW:
1619
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1621
fprintf_plus(stderr, "No Mandos server found, still"
1628
/* Signal handler that stops main loop after SIGTERM */
1629
static void handle_sigterm(int sig){
1634
signal_received = sig;
1635
int old_errno = errno;
1636
/* set main loop to exit */
1637
if(simple_poll != NULL){
1638
avahi_simple_poll_quit(simple_poll);
1643
__attribute__((nonnull, warn_unused_result))
1644
bool get_flags(const char *ifname, struct ifreq *ifr){
1648
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1651
perror_plus("socket");
1655
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1656
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1657
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1661
perror_plus("ioctl SIOCGIFFLAGS");
1664
if((close(s) == -1) and debug){
1666
perror_plus("close");
1671
if((close(s) == -1) and debug){
1673
perror_plus("close");
1679
__attribute__((nonnull, warn_unused_result))
1680
bool good_flags(const char *ifname, const struct ifreq *ifr){
1682
/* Reject the loopback device */
1683
if(ifr->ifr_flags & IFF_LOOPBACK){
1685
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1690
/* Accept point-to-point devices only if connect_to is specified */
1691
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1693
fprintf_plus(stderr, "Accepting point-to-point interface"
1694
" \"%s\"\n", ifname);
1698
/* Otherwise, reject non-broadcast-capable devices */
1699
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1701
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1702
" \"%s\"\n", ifname);
1706
/* Reject non-ARP interfaces (including dummy interfaces) */
1707
if(ifr->ifr_flags & IFF_NOARP){
1709
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1715
/* Accept this device */
1717
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1723
* This function determines if a directory entry in /sys/class/net
1724
* corresponds to an acceptable network device.
1725
* (This function is passed to scandir(3) as a filter function.)
1727
__attribute__((nonnull, warn_unused_result))
1728
int good_interface(const struct dirent *if_entry){
1729
if(if_entry->d_name[0] == '.'){
1734
if(not get_flags(if_entry->d_name, &ifr)){
1736
fprintf_plus(stderr, "Failed to get flags for interface "
1737
"\"%s\"\n", if_entry->d_name);
1742
if(not good_flags(if_entry->d_name, &ifr)){
1749
* This function determines if a network interface is up.
1751
__attribute__((nonnull, warn_unused_result))
1752
bool interface_is_up(const char *interface){
1754
if(not get_flags(interface, &ifr)){
1756
fprintf_plus(stderr, "Failed to get flags for interface "
1757
"\"%s\"\n", interface);
1762
return (bool)(ifr.ifr_flags & IFF_UP);
1766
* This function determines if a network interface is running
1768
__attribute__((nonnull, warn_unused_result))
1769
bool interface_is_running(const char *interface){
1771
if(not get_flags(interface, &ifr)){
1773
fprintf_plus(stderr, "Failed to get flags for interface "
1774
"\"%s\"\n", interface);
1779
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1782
__attribute__((nonnull, pure, warn_unused_result))
1783
int notdotentries(const struct dirent *direntry){
1784
/* Skip "." and ".." */
1785
if(direntry->d_name[0] == '.'
1786
and (direntry->d_name[1] == '\0'
1787
or (direntry->d_name[1] == '.'
1788
and direntry->d_name[2] == '\0'))){
1794
/* Is this directory entry a runnable program? */
1795
__attribute__((nonnull, warn_unused_result))
1796
int runnable_hook(const struct dirent *direntry){
1801
if((direntry->d_name)[0] == '\0'){
1806
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1807
"abcdefghijklmnopqrstuvwxyz"
1810
if((direntry->d_name)[sret] != '\0'){
1811
/* Contains non-allowed characters */
1813
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1819
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1822
perror_plus("Could not stat hook");
1826
if(not (S_ISREG(st.st_mode))){
1827
/* Not a regular file */
1829
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1834
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1835
/* Not executable */
1837
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1843
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1849
__attribute__((nonnull, warn_unused_result))
1850
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1851
mandos_context *mc){
1853
struct timespec now;
1854
struct timespec waited_time;
1855
intmax_t block_time;
1858
if(mc->current_server == NULL){
1860
fprintf_plus(stderr, "Wait until first server is found."
1863
ret = avahi_simple_poll_iterate(s, -1);
1866
fprintf_plus(stderr, "Check current_server if we should run"
1869
/* the current time */
1870
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1872
perror_plus("clock_gettime");
1875
/* Calculating in ms how long time between now and server
1876
who we visted longest time ago. Now - last seen. */
1877
waited_time.tv_sec = (now.tv_sec
1878
- mc->current_server->last_seen.tv_sec);
1879
waited_time.tv_nsec = (now.tv_nsec
1880
- mc->current_server->last_seen.tv_nsec);
1881
/* total time is 10s/10,000ms.
1882
Converting to s from ms by dividing by 1,000,
1883
and ns to ms by dividing by 1,000,000. */
1884
block_time = ((retry_interval
1885
- ((intmax_t)waited_time.tv_sec * 1000))
1886
- ((intmax_t)waited_time.tv_nsec / 1000000));
1889
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1893
if(block_time <= 0){
1894
ret = start_mandos_communication(mc->current_server->ip,
1895
mc->current_server->port,
1896
mc->current_server->if_index,
1897
mc->current_server->af, mc);
1899
avahi_simple_poll_quit(s);
1902
ret = clock_gettime(CLOCK_MONOTONIC,
1903
&mc->current_server->last_seen);
1905
perror_plus("clock_gettime");
1908
mc->current_server = mc->current_server->next;
1909
block_time = 0; /* Call avahi to find new Mandos
1910
servers, but don't block */
1913
ret = avahi_simple_poll_iterate(s, (int)block_time);
1916
if(ret > 0 or errno != EINTR){
1917
return (ret != 1) ? ret : 0;
1923
__attribute__((nonnull))
1924
void run_network_hooks(const char *mode, const char *interface,
1926
struct dirent **direntries = NULL;
1927
if(hookdir_fd == -1){
1928
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1930
if(hookdir_fd == -1){
1931
if(errno == ENOENT){
1933
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1934
" found\n", hookdir);
1937
perror_plus("open");
1942
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1944
perror_plus("open(\"/dev/null\", O_RDONLY)");
1947
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1948
runnable_hook, alphasort);
1950
perror_plus("scandir");
1954
struct dirent *direntry;
1956
for(int i = 0; i < numhooks; i++){
1957
direntry = direntries[i];
1959
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1962
pid_t hook_pid = fork();
1965
/* Raise privileges */
1966
errno = raise_privileges_permanently();
1968
perror_plus("Failed to raise privileges");
1975
perror_plus("setgid");
1978
/* Reset supplementary groups */
1980
ret = setgroups(0, NULL);
1982
perror_plus("setgroups");
1985
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1987
perror_plus("setenv");
1990
ret = setenv("DEVICE", interface, 1);
1992
perror_plus("setenv");
1995
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1997
perror_plus("setenv");
2000
ret = setenv("MODE", mode, 1);
2002
perror_plus("setenv");
2006
ret = asprintf(&delaystring, "%f", (double)delay);
2008
perror_plus("asprintf");
2011
ret = setenv("DELAY", delaystring, 1);
2014
perror_plus("setenv");
2018
if(connect_to != NULL){
2019
ret = setenv("CONNECT", connect_to, 1);
2021
perror_plus("setenv");
2025
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2029
perror_plus("openat");
2030
_exit(EXIT_FAILURE);
2032
if(close(hookdir_fd) == -1){
2033
perror_plus("close");
2034
_exit(EXIT_FAILURE);
2036
ret = dup2(devnull, STDIN_FILENO);
2038
perror_plus("dup2(devnull, STDIN_FILENO)");
2041
ret = close(devnull);
2043
perror_plus("close");
2046
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2048
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2051
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2053
perror_plus("fexecve");
2054
_exit(EXIT_FAILURE);
2058
perror_plus("fork");
2063
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2064
perror_plus("waitpid");
2068
if(WIFEXITED(status)){
2069
if(WEXITSTATUS(status) != 0){
2070
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2071
" with status %d\n", direntry->d_name,
2072
WEXITSTATUS(status));
2076
} else if(WIFSIGNALED(status)){
2077
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2078
" signal %d\n", direntry->d_name,
2083
fprintf_plus(stderr, "Warning: network hook \"%s\""
2084
" crashed\n", direntry->d_name);
2090
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2096
if(close(hookdir_fd) == -1){
2097
perror_plus("close");
2104
__attribute__((nonnull, warn_unused_result))
2105
int bring_up_interface(const char *const interface,
2107
int old_errno = errno;
2109
struct ifreq network;
2110
unsigned int if_index = if_nametoindex(interface);
2112
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2122
if(not interface_is_up(interface)){
2124
int ioctl_errno = 0;
2125
if(not get_flags(interface, &network)){
2127
fprintf_plus(stderr, "Failed to get flags for interface "
2128
"\"%s\"\n", interface);
2132
network.ifr_flags |= IFF_UP; /* set flag */
2134
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2137
perror_plus("socket");
2145
perror_plus("close");
2152
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2156
/* Raise privileges */
2157
ret_errno = raise_privileges();
2160
perror_plus("Failed to raise privileges");
2165
bool restore_loglevel = false;
2167
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2168
messages about the network interface to mess up the prompt */
2169
ret_linux = klogctl(8, NULL, 5);
2170
if(ret_linux == -1){
2171
perror_plus("klogctl");
2173
restore_loglevel = true;
2176
#endif /* __linux__ */
2177
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2178
ioctl_errno = errno;
2180
if(restore_loglevel){
2181
ret_linux = klogctl(7, NULL, 0);
2182
if(ret_linux == -1){
2183
perror_plus("klogctl");
2186
#endif /* __linux__ */
2188
/* If raise_privileges() succeeded above */
2190
/* Lower privileges */
2191
ret_errno = lower_privileges();
2194
perror_plus("Failed to lower privileges");
2198
/* Close the socket */
2201
perror_plus("close");
2204
if(ret_setflags == -1){
2205
errno = ioctl_errno;
2206
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2211
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2215
/* Sleep checking until interface is running.
2216
Check every 0.25s, up to total time of delay */
2217
for(int i = 0; i < delay * 4; i++){
2218
if(interface_is_running(interface)){
2221
struct timespec sleeptime = { .tv_nsec = 250000000 };
2222
ret = nanosleep(&sleeptime, NULL);
2223
if(ret == -1 and errno != EINTR){
2224
perror_plus("nanosleep");
2232
__attribute__((nonnull, warn_unused_result))
2233
int take_down_interface(const char *const interface){
2234
int old_errno = errno;
2235
struct ifreq network;
2236
unsigned int if_index = if_nametoindex(interface);
2238
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2242
if(interface_is_up(interface)){
2244
int ioctl_errno = 0;
2245
if(not get_flags(interface, &network) and debug){
2247
fprintf_plus(stderr, "Failed to get flags for interface "
2248
"\"%s\"\n", interface);
2252
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2254
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2257
perror_plus("socket");
2263
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2267
/* Raise privileges */
2268
ret_errno = raise_privileges();
2271
perror_plus("Failed to raise privileges");
2274
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2275
ioctl_errno = errno;
2277
/* If raise_privileges() succeeded above */
2279
/* Lower privileges */
2280
ret_errno = lower_privileges();
2283
perror_plus("Failed to lower privileges");
2287
/* Close the socket */
2288
int ret = close(sd);
2290
perror_plus("close");
2293
if(ret_setflags == -1){
2294
errno = ioctl_errno;
2295
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2300
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2308
int main(int argc, char *argv[]){
2309
mandos_context mc = { .server = NULL, .dh_bits = 0,
2310
.priority = "SECURE256:!CTYPE-X.509"
2311
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2312
.current_server = NULL, .interfaces = NULL,
2313
.interfaces_size = 0 };
2314
AvahiSServiceBrowser *sb = NULL;
2319
int exitcode = EXIT_SUCCESS;
2320
char *interfaces_to_take_down = NULL;
2321
size_t interfaces_to_take_down_size = 0;
2322
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2323
char old_tempdir[] = "/tmp/mandosXXXXXX";
2324
char *tempdir = NULL;
2325
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2326
const char *seckey = PATHDIR "/" SECKEY;
2327
const char *pubkey = PATHDIR "/" PUBKEY;
2328
const char *dh_params_file = NULL;
2329
char *interfaces_hooks = NULL;
2331
bool gnutls_initialized = false;
2332
bool gpgme_initialized = false;
2334
double retry_interval = 10; /* 10s between trying a server and
2335
retrying the same server again */
2337
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2338
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2343
/* Lower any group privileges we might have, just to be safe */
2347
perror_plus("setgid");
2350
/* Lower user privileges (temporarily) */
2354
perror_plus("seteuid");
2362
struct argp_option options[] = {
2363
{ .name = "debug", .key = 128,
2364
.doc = "Debug mode", .group = 3 },
2365
{ .name = "connect", .key = 'c',
2366
.arg = "ADDRESS:PORT",
2367
.doc = "Connect directly to a specific Mandos server",
2369
{ .name = "interface", .key = 'i',
2371
.doc = "Network interface that will be used to search for"
2374
{ .name = "seckey", .key = 's',
2376
.doc = "OpenPGP secret key file base name",
2378
{ .name = "pubkey", .key = 'p',
2380
.doc = "OpenPGP public key file base name",
2382
{ .name = "dh-bits", .key = 129,
2384
.doc = "Bit length of the prime number used in the"
2385
" Diffie-Hellman key exchange",
2387
{ .name = "dh-params", .key = 134,
2389
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2390
" for the Diffie-Hellman key exchange",
2392
{ .name = "priority", .key = 130,
2394
.doc = "GnuTLS priority string for the TLS handshake",
2396
{ .name = "delay", .key = 131,
2398
.doc = "Maximum delay to wait for interface startup",
2400
{ .name = "retry", .key = 132,
2402
.doc = "Retry interval used when denied by the Mandos server",
2404
{ .name = "network-hook-dir", .key = 133,
2406
.doc = "Directory where network hooks are located",
2409
* These reproduce what we would get without ARGP_NO_HELP
2411
{ .name = "help", .key = '?',
2412
.doc = "Give this help list", .group = -1 },
2413
{ .name = "usage", .key = -3,
2414
.doc = "Give a short usage message", .group = -1 },
2415
{ .name = "version", .key = 'V',
2416
.doc = "Print program version", .group = -1 },
2420
error_t parse_opt(int key, char *arg,
2421
struct argp_state *state){
2424
case 128: /* --debug */
2427
case 'c': /* --connect */
2430
case 'i': /* --interface */
2431
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2434
argp_error(state, "%s", strerror(ret_errno));
2437
case 's': /* --seckey */
2440
case 'p': /* --pubkey */
2443
case 129: /* --dh-bits */
2445
tmpmax = strtoimax(arg, &tmp, 10);
2446
if(errno != 0 or tmp == arg or *tmp != '\0'
2447
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2448
argp_error(state, "Bad number of DH bits");
2450
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2452
case 134: /* --dh-params */
2453
dh_params_file = arg;
2455
case 130: /* --priority */
2458
case 131: /* --delay */
2460
delay = strtof(arg, &tmp);
2461
if(errno != 0 or tmp == arg or *tmp != '\0'){
2462
argp_error(state, "Bad delay");
2464
case 132: /* --retry */
2466
retry_interval = strtod(arg, &tmp);
2467
if(errno != 0 or tmp == arg or *tmp != '\0'
2468
or (retry_interval * 1000) > INT_MAX
2469
or retry_interval < 0){
2470
argp_error(state, "Bad retry interval");
2473
case 133: /* --network-hook-dir */
2477
* These reproduce what we would get without ARGP_NO_HELP
2479
case '?': /* --help */
2480
argp_state_help(state, state->out_stream,
2481
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2482
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2483
case -3: /* --usage */
2484
argp_state_help(state, state->out_stream,
2485
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2486
case 'V': /* --version */
2487
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2488
exit(argp_err_exit_status);
2491
return ARGP_ERR_UNKNOWN;
2496
struct argp argp = { .options = options, .parser = parse_opt,
2498
.doc = "Mandos client -- Get and decrypt"
2499
" passwords from a Mandos server" };
2500
ret_errno = argp_parse(&argp, argc, argv,
2501
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
588
case AVAHI_BROWSER_FAILURE:
590
fprintf(stderr, "(Browser) %s\n",
591
avahi_strerror(avahi_server_errno(server)));
592
avahi_simple_poll_quit(simple_poll);
595
case AVAHI_BROWSER_NEW:
596
/* We ignore the returned resolver object. In the callback
597
function we free it. If the server is terminated before
598
the callback function is called the server will free
599
the resolver for us. */
601
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
603
AVAHI_PROTO_INET6, 0,
604
resolve_callback, s)))
605
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
606
avahi_strerror(avahi_server_errno(s)));
609
case AVAHI_BROWSER_REMOVE:
612
case AVAHI_BROWSER_ALL_FOR_NOW:
613
case AVAHI_BROWSER_CACHE_EXHAUSTED:
618
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
2508
perror_plus("argp_parse");
2509
exitcode = EX_OSERR;
2512
exitcode = EX_USAGE;
2518
/* Work around Debian bug #633582:
2519
<https://bugs.debian.org/633582> */
2521
/* Re-raise privileges */
2522
ret = raise_privileges();
2525
perror_plus("Failed to raise privileges");
2529
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2530
int seckey_fd = open(seckey, O_RDONLY);
2531
if(seckey_fd == -1){
2532
perror_plus("open");
2534
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_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(seckey_fd, uid, gid);
2542
perror_plus("fchown");
2550
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2551
int pubkey_fd = open(pubkey, O_RDONLY);
2552
if(pubkey_fd == -1){
2553
perror_plus("open");
2555
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_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(pubkey_fd, uid, gid);
2563
perror_plus("fchown");
2571
if(dh_params_file != NULL
2572
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2573
int dhparams_fd = open(dh_params_file, O_RDONLY);
2574
if(dhparams_fd == -1){
2575
perror_plus("open");
2577
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2579
perror_plus("fstat");
2581
if(S_ISREG(st.st_mode)
2582
and st.st_uid == 0 and st.st_gid == 0){
2583
ret = fchown(dhparams_fd, uid, gid);
2585
perror_plus("fchown");
2593
/* Lower privileges */
2594
ret = lower_privileges();
2597
perror_plus("Failed to lower privileges");
2602
/* Remove invalid interface names (except "none") */
2604
char *interface = NULL;
2605
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2607
if(strcmp(interface, "none") != 0
2608
and if_nametoindex(interface) == 0){
2609
if(interface[0] != '\0'){
2610
fprintf_plus(stderr, "Not using nonexisting interface"
2611
" \"%s\"\n", interface);
2613
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2619
/* Run network hooks */
2621
if(mc.interfaces != NULL){
2622
interfaces_hooks = malloc(mc.interfaces_size);
2623
if(interfaces_hooks == NULL){
2624
perror_plus("malloc");
2627
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2628
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2630
run_network_hooks("start", interfaces_hooks != NULL ?
2631
interfaces_hooks : "", delay);
2635
avahi_set_log_function(empty_log);
2638
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2639
from the signal handler */
2640
/* Initialize the pseudo-RNG for Avahi */
2641
srand((unsigned int) time(NULL));
2642
simple_poll = avahi_simple_poll_new();
2643
if(simple_poll == NULL){
2644
fprintf_plus(stderr,
2645
"Avahi: Failed to create simple poll object.\n");
2646
exitcode = EX_UNAVAILABLE;
2650
sigemptyset(&sigterm_action.sa_mask);
2651
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2653
perror_plus("sigaddset");
2654
exitcode = EX_OSERR;
2657
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2659
perror_plus("sigaddset");
2660
exitcode = EX_OSERR;
2663
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2665
perror_plus("sigaddset");
2666
exitcode = EX_OSERR;
2669
/* Need to check if the handler is SIG_IGN before handling:
2670
| [[info:libc:Initial Signal Actions]] |
2671
| [[info:libc:Basic Signal Handling]] |
2673
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2675
perror_plus("sigaction");
2678
if(old_sigterm_action.sa_handler != SIG_IGN){
2679
ret = sigaction(SIGINT, &sigterm_action, NULL);
2681
perror_plus("sigaction");
2682
exitcode = EX_OSERR;
2686
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2688
perror_plus("sigaction");
2691
if(old_sigterm_action.sa_handler != SIG_IGN){
2692
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2694
perror_plus("sigaction");
2695
exitcode = EX_OSERR;
2699
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2701
perror_plus("sigaction");
2704
if(old_sigterm_action.sa_handler != SIG_IGN){
2705
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2707
perror_plus("sigaction");
2708
exitcode = EX_OSERR;
2713
/* If no interfaces were specified, make a list */
2714
if(mc.interfaces == NULL){
2715
struct dirent **direntries = NULL;
2716
/* Look for any good interfaces */
2717
ret = scandir(sys_class_net, &direntries, good_interface,
2720
/* Add all found interfaces to interfaces list */
2721
for(int i = 0; i < ret; ++i){
2722
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2723
direntries[i]->d_name);
2726
perror_plus("argz_add");
2727
free(direntries[i]);
2731
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2732
direntries[i]->d_name);
2734
free(direntries[i]);
2741
fprintf_plus(stderr, "Could not find a network interface\n");
2742
exitcode = EXIT_FAILURE;
2747
/* Bring up interfaces which are down, and remove any "none"s */
2749
char *interface = NULL;
2750
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2752
/* If interface name is "none", stop bringing up interfaces.
2753
Also remove all instances of "none" from the list */
2754
if(strcmp(interface, "none") == 0){
2755
argz_delete(&mc.interfaces, &mc.interfaces_size,
2758
while((interface = argz_next(mc.interfaces,
2759
mc.interfaces_size, interface))){
2760
if(strcmp(interface, "none") == 0){
2761
argz_delete(&mc.interfaces, &mc.interfaces_size,
2768
bool interface_was_up = interface_is_up(interface);
2769
errno = bring_up_interface(interface, delay);
2770
if(not interface_was_up){
2772
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2773
" %s\n", interface, strerror(errno));
2775
errno = argz_add(&interfaces_to_take_down,
2776
&interfaces_to_take_down_size,
2779
perror_plus("argz_add");
2784
if(debug and (interfaces_to_take_down == NULL)){
2785
fprintf_plus(stderr, "No interfaces were brought up\n");
2789
/* If we only got one interface, explicitly use only that one */
2790
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2792
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2795
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2802
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2804
fprintf_plus(stderr, "init_gnutls_global failed\n");
2805
exitcode = EX_UNAVAILABLE;
2808
gnutls_initialized = true;
2815
/* Try /run/tmp before /tmp */
2816
tempdir = mkdtemp(run_tempdir);
2817
if(tempdir == NULL and errno == ENOENT){
2819
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2820
run_tempdir, old_tempdir);
2822
tempdir = mkdtemp(old_tempdir);
2824
if(tempdir == NULL){
2825
perror_plus("mkdtemp");
2833
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2834
fprintf_plus(stderr, "init_gpgme failed\n");
2835
exitcode = EX_UNAVAILABLE;
2838
gpgme_initialized = true;
2845
if(connect_to != NULL){
2846
/* Connect directly, do not use Zeroconf */
2847
/* (Mainly meant for debugging) */
2848
char *address = strrchr(connect_to, ':');
2850
if(address == NULL){
2851
fprintf_plus(stderr, "No colon in address\n");
2852
exitcode = EX_USAGE;
2862
tmpmax = strtoimax(address+1, &tmp, 10);
2863
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2864
or tmpmax != (in_port_t)tmpmax){
2865
fprintf_plus(stderr, "Bad port number\n");
2866
exitcode = EX_USAGE;
2874
port = (in_port_t)tmpmax;
2876
/* Colon in address indicates IPv6 */
2878
if(strchr(connect_to, ':') != NULL){
2880
/* Accept [] around IPv6 address - see RFC 5952 */
2881
if(connect_to[0] == '[' and address[-1] == ']')
2889
address = connect_to;
2895
while(not quit_now){
2896
ret = start_mandos_communication(address, port, if_index, af,
2898
if(quit_now or ret == 0){
2902
fprintf_plus(stderr, "Retrying in %d seconds\n",
2903
(int)retry_interval);
2905
sleep((unsigned int)retry_interval);
2909
exitcode = EXIT_SUCCESS;
619
2920
AvahiServerConfig config;
620
AvahiSServiceBrowser *sb = NULL;
623
int returncode = EXIT_SUCCESS;
624
const char *interface = "eth0";
627
static struct option long_options[] = {
628
{"debug", no_argument, (int *)&debug, 1},
629
{"interface", required_argument, 0, 'i'},
632
int option_index = 0;
633
ret = getopt_long (argc, argv, "i:", long_options,
652
avahi_set_log_function(empty_log);
655
/* Initialize the psuedo-RNG */
656
srand((unsigned int) time(NULL));
658
/* Allocate main loop object */
659
if (!(simple_poll = avahi_simple_poll_new())) {
660
fprintf(stderr, "Failed to create simple poll object.\n");
665
/* Do not publish any local records */
2921
/* Do not publish any local Zeroconf records */
666
2922
avahi_server_config_init(&config);
667
2923
config.publish_hinfo = 0;
668
2924
config.publish_addresses = 0;
669
2925
config.publish_workstation = 0;
670
2926
config.publish_domain = 0;
672
2928
/* Allocate a new server */
673
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
&config, NULL, NULL, &error);
676
/* Free the configuration data */
2929
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2930
&config, NULL, NULL, &ret);
2932
/* Free the Avahi configuration data */
677
2933
avahi_server_config_free(&config);
679
/* Check if creating the server object succeeded */
681
fprintf(stderr, "Failed to create server: %s\n",
682
avahi_strerror(error));
683
returncode = EXIT_FAILURE;
687
/* Create the service browser */
688
sb = avahi_s_service_browser_new(server,
690
if_nametoindex(interface),
692
"_mandos._tcp", NULL, 0,
693
browse_callback, server);
695
fprintf(stderr, "Failed to create service browser: %s\n",
696
avahi_strerror(avahi_server_errno(server)));
697
returncode = EXIT_FAILURE;
701
/* Run the main loop */
704
fprintf(stderr, "Starting avahi loop search\n");
707
avahi_simple_poll_loop(simple_poll);
712
fprintf(stderr, "%s exiting\n", argv[0]);
717
avahi_s_service_browser_free(sb);
720
avahi_server_free(server);
723
avahi_simple_poll_free(simple_poll);
2936
/* Check if creating the Avahi server object succeeded */
2937
if(mc.server == NULL){
2938
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2939
avahi_strerror(ret));
2940
exitcode = EX_UNAVAILABLE;
2948
/* Create the Avahi service browser */
2949
sb = avahi_s_service_browser_new(mc.server, if_index,
2950
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2951
NULL, 0, browse_callback,
2954
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2955
avahi_strerror(avahi_server_errno(mc.server)));
2956
exitcode = EX_UNAVAILABLE;
2964
/* Run the main loop */
2967
fprintf_plus(stderr, "Starting Avahi loop search\n");
2970
ret = avahi_loop_with_timeout(simple_poll,
2971
(int)(retry_interval * 1000), &mc);
2973
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2974
(ret == 0) ? "successfully" : "with error");
2980
if(signal_received){
2981
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
2982
argv[0], signal_received,
2983
strsignal(signal_received));
2985
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2989
/* Cleanup things */
2990
free(mc.interfaces);
2993
avahi_s_service_browser_free(sb);
2995
if(mc.server != NULL)
2996
avahi_server_free(mc.server);
2998
if(simple_poll != NULL)
2999
avahi_simple_poll_free(simple_poll);
3001
if(gnutls_initialized){
3002
gnutls_certificate_free_credentials(mc.cred);
3003
gnutls_dh_params_deinit(mc.dh_params);
3006
if(gpgme_initialized){
3007
gpgme_release(mc.ctx);
3010
/* Cleans up the circular linked list of Mandos servers the client
3012
if(mc.current_server != NULL){
3013
mc.current_server->prev->next = NULL;
3014
while(mc.current_server != NULL){
3015
server *next = mc.current_server->next;
3017
#pragma GCC diagnostic push
3018
#pragma GCC diagnostic ignored "-Wcast-qual"
3020
free((char *)(mc.current_server->ip));
3022
#pragma GCC diagnostic pop
3024
free(mc.current_server);
3025
mc.current_server = next;
3029
/* Re-raise privileges */
3031
ret = raise_privileges();
3034
perror_plus("Failed to raise privileges");
3037
/* Run network hooks */
3038
run_network_hooks("stop", interfaces_hooks != NULL ?
3039
interfaces_hooks : "", delay);
3041
/* Take down the network interfaces which were brought up */
3043
char *interface = NULL;
3044
while((interface = argz_next(interfaces_to_take_down,
3045
interfaces_to_take_down_size,
3047
ret = take_down_interface(interface);
3050
perror_plus("Failed to take down interface");
3053
if(debug and (interfaces_to_take_down == NULL)){
3054
fprintf_plus(stderr, "No interfaces needed to be taken"
3060
ret = lower_privileges_permanently();
3063
perror_plus("Failed to lower privileges permanently");
3067
free(interfaces_to_take_down);
3068
free(interfaces_hooks);
3070
void clean_dir_at(int base, const char * const dirname,
3072
struct dirent **direntries = NULL;
3074
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3080
perror_plus("open");
3083
int numentries = scandirat(dir_fd, ".", &direntries,
3084
notdotentries, alphasort);
3085
if(numentries >= 0){
3086
for(int i = 0; i < numentries; i++){
3088
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3089
dirname, direntries[i]->d_name);
3091
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3093
if(errno == EISDIR){
3094
dret = unlinkat(dir_fd, direntries[i]->d_name,
3097
if((dret == -1) and (errno == ENOTEMPTY)
3098
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3099
== 0) and (level == 0)){
3100
/* Recurse only in this special case */
3101
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3104
if((dret == -1) and (errno != ENOENT)){
3105
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3106
direntries[i]->d_name, strerror(errno));
3109
free(direntries[i]);
3112
/* need to clean even if 0 because man page doesn't specify */
3114
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3115
if(dret == -1 and errno != ENOENT){
3116
perror_plus("rmdir");
3119
perror_plus("scandirat");
3124
/* Removes the GPGME temp directory and all files inside */
3125
if(tempdir != NULL){
3126
clean_dir_at(-1, tempdir, 0);
3130
sigemptyset(&old_sigterm_action.sa_mask);
3131
old_sigterm_action.sa_handler = SIG_DFL;
3132
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3133
&old_sigterm_action,
3136
perror_plus("sigaction");
3139
ret = raise(signal_received);
3140
} while(ret != 0 and errno == EINTR);
3142
perror_plus("raise");
3145
TEMP_FAILURE_RETRY(pause());