49
117
#include <avahi-common/malloc.h>
50
118
#include <avahi-common/error.h>
53
#include <sys/types.h> /* socket(), inet_pton() */
54
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
55
struct in6_addr, inet_pton() */
56
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
57
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
59
#include <unistd.h> /* close() */
60
#include <netinet/in.h>
61
#include <stdbool.h> /* true */
62
#include <string.h> /* memset */
63
#include <arpa/inet.h> /* inet_pton() */
64
#include <iso646.h> /* not */
67
#include <errno.h> /* perror() */
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,
73
137
#define BUFFER_SIZE 256
76
static const char *certdir = "/conf/conf.d/mandos";
77
static const char *certfile = "openpgp-client.txt";
78
static const char *certkey = "openpgp-client-key.txt";
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"
80
144
bool debug = false;
82
const char mandos_protocol_version[] = "1";
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 */
85
AvahiSimplePoll *simple_poll;
86
168
AvahiServer *server;
87
169
gnutls_certificate_credentials_t cred;
88
170
unsigned int dh_bits;
171
gnutls_dh_params_t dh_params;
89
172
const char *priority;
174
server *current_server;
176
size_t interfaces_size;
92
size_t adjustbuffer(char *buffer, size_t buffer_length,
93
size_t buffer_capacity){
94
if (buffer_length + BUFFER_SIZE > buffer_capacity){
95
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
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;
99
222
buffer_capacity += BUFFER_SIZE;
101
224
return buffer_capacity;
104
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
106
const char *homedir){
107
gpgme_data_t dh_crypto, dh_plain;
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,
109
283
gpgme_error_t rc;
111
size_t new_packet_capacity = 0;
112
ssize_t new_packet_length = 0;
113
284
gpgme_engine_info_t engine_info;
116
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");
120
327
gpgme_check_version(NULL);
121
328
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
122
if (rc != GPG_ERR_NO_ERROR){
123
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
124
gpgme_strsource(rc), gpgme_strerror(rc));
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));
128
/* Set GPGME home directory */
129
rc = gpgme_get_engine_info (&engine_info);
130
if (rc != GPG_ERR_NO_ERROR){
131
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
132
gpgme_strsource(rc), gpgme_strerror(rc));
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));
135
342
while(engine_info != NULL){
136
343
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
137
344
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
138
engine_info->file_name, homedir);
345
engine_info->file_name, tempdir);
141
348
engine_info = engine_info->next;
143
350
if(engine_info == NULL){
144
fprintf(stderr, "Could not set home dir to %s\n", homedir);
148
/* Create new GPGME data buffer from packet buffer */
149
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
150
if (rc != GPG_ERR_NO_ERROR){
151
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
152
gpgme_strsource(rc), gpgme_strerror(rc));
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));
156
400
/* Create new empty GPGME data buffer for the plaintext */
157
401
rc = gpgme_data_new(&dh_plain);
158
if (rc != GPG_ERR_NO_ERROR){
159
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
160
gpgme_strsource(rc), gpgme_strerror(rc));
164
/* Create new GPGME "context" */
165
rc = gpgme_new(&ctx);
166
if (rc != GPG_ERR_NO_ERROR){
167
fprintf(stderr, "bad gpgme_new: %s: %s\n",
168
gpgme_strsource(rc), gpgme_strerror(rc));
172
/* Decrypt data from the FILE pointer to the plaintext data
174
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
175
if (rc != GPG_ERR_NO_ERROR){
176
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
177
gpgme_strsource(rc), gpgme_strerror(rc));
182
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
186
gpgme_decrypt_result_t result;
187
result = gpgme_op_decrypt_result(ctx);
189
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
191
fprintf(stderr, "Unsupported algorithm: %s\n",
192
result->unsupported_algorithm);
193
fprintf(stderr, "Wrong key usage: %d\n",
194
result->wrong_key_usage);
195
if(result->file_name != NULL){
196
fprintf(stderr, "File name: %s\n", result->file_name);
198
gpgme_recipient_t recipient;
199
recipient = result->recipients;
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;
201
432
while(recipient != NULL){
202
fprintf(stderr, "Public key algorithm: %s\n",
203
gpgme_pubkey_algo_name(recipient->pubkey_algo));
204
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
205
fprintf(stderr, "Secret key available: %s\n",
206
recipient->status == GPG_ERR_NO_SECKEY
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
208
440
recipient = recipient->next;
214
/* Delete the GPGME FILE pointer cryptotext data buffer */
215
gpgme_data_release(dh_crypto);
448
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
217
451
/* Seek back to the beginning of the GPGME plaintext data buffer */
218
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
219
perror("pgpme_data_seek");
452
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
453
perror_plus("gpgme_data_seek");
454
plaintext_length = -1;
224
new_packet_capacity = adjustbuffer(*new_packet, new_packet_length,
225
new_packet_capacity);
226
if (new_packet_capacity == 0){
227
perror("adjustbuffer");
230
new_packet_capacity += BUFFER_SIZE;
460
plaintext_capacity = incbuffer(plaintext,
461
(size_t)plaintext_length,
463
if(plaintext_capacity == 0){
464
perror_plus("incbuffer");
465
plaintext_length = -1;
233
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
469
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
235
471
/* Print the data, if any */
240
perror("gpgme_data_read");
243
new_packet_length += ret;
246
/* FIXME: check characters before printing to screen so to not print
247
terminal control characters */
249
/* fprintf(stderr, "decrypted password is: "); */
250
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
251
/* fprintf(stderr, "\n"); */
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);
254
497
/* Delete the GPGME plaintext data buffer */
255
498
gpgme_data_release(dh_plain);
256
return new_packet_length;
259
static const char * safer_gnutls_strerror (int value) {
260
const char *ret = gnutls_strerror (value);
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))
266
517
static void debuggnutls(__attribute__((unused)) int level,
267
518
const char* string){
268
fprintf(stderr, "%s", string);
519
fprintf_plus(stderr, "GnuTLS: %s", string);
271
static int initgnutls(mandos_context *mc){
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,
276
fprintf(stderr, "Initializing GnuTLS\n");
279
if ((ret = gnutls_global_init ())
280
!= GNUTLS_E_SUCCESS) {
281
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
531
fprintf_plus(stderr, "Initializing GnuTLS\n");
535
/* "Use a log level over 10 to enable all debugging options."
286
538
gnutls_global_set_log_level(11);
287
539
gnutls_global_set_log_function(debuggnutls);
290
/* openpgp credentials */
291
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
292
!= GNUTLS_E_SUCCESS) {
293
fprintf (stderr, "memory error: %s\n",
294
safer_gnutls_strerror(ret));
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));
299
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
300
" and keyfile %s as GnuTLS credentials\n", certfile,
551
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
552
" secret key %s as GnuTLS credentials\n",
304
557
ret = gnutls_certificate_set_openpgp_key_file
305
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
306
if (ret != GNUTLS_E_SUCCESS) {
308
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
310
ret, certfile, certkey);
311
fprintf(stdout, "The Error is: %s\n",
312
safer_gnutls_strerror(ret));
316
//GnuTLS server initialization
317
if ((ret = gnutls_dh_params_init (&es->dh_params))
318
!= GNUTLS_E_SUCCESS) {
319
fprintf (stderr, "Error in dh parameter initialization: %s\n",
320
safer_gnutls_strerror(ret));
324
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
325
!= GNUTLS_E_SUCCESS) {
326
fprintf (stderr, "Error in prime generation: %s\n",
327
safer_gnutls_strerror(ret));
331
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
333
// GnuTLS session creation
334
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
335
!= GNUTLS_E_SUCCESS){
336
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
337
safer_gnutls_strerror(ret));
340
if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
341
!= GNUTLS_E_SUCCESS) {
342
fprintf(stderr, "Syntax error at: %s\n", err);
343
fprintf(stderr, "GnuTLS error: %s\n",
344
safer_gnutls_strerror(ret));
348
if ((ret = gnutls_credentials_set
349
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
350
!= GNUTLS_E_SUCCESS) {
351
fprintf(stderr, "Error setting a credentials set: %s\n",
352
safer_gnutls_strerror(ret));
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;
616
if(params.data == NULL){
617
dhparamsfilename = NULL;
619
if(dhparamsfilename == NULL){
622
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
623
GNUTLS_X509_FMT_PEM);
624
if(ret != GNUTLS_E_SUCCESS){
625
fprintf_plus(stderr, "Failed to parse DH parameters in file"
626
" \"%s\": %s\n", dhparamsfilename,
627
safer_gnutls_strerror(ret));
628
dhparamsfilename = NULL;
633
if(dhparamsfilename == NULL){
634
if(mc->dh_bits == 0){
635
/* Find out the optimal number of DH bits */
636
/* Try to read the private key file */
637
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
639
int secfile = open(seckeyfilename, O_RDONLY);
644
size_t buffer_capacity = 0;
646
buffer_capacity = incbuffer((char **)&buffer.data,
648
(size_t)buffer_capacity);
649
if(buffer_capacity == 0){
650
perror_plus("incbuffer");
655
ssize_t bytes_read = read(secfile,
656
buffer.data + buffer.size,
662
/* check bytes_read for failure */
669
buffer.size += (unsigned int)bytes_read;
673
/* If successful, use buffer to parse private key */
674
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
675
if(buffer.data != NULL){
677
gnutls_openpgp_privkey_t privkey = NULL;
678
ret = gnutls_openpgp_privkey_init(&privkey);
679
if(ret != GNUTLS_E_SUCCESS){
680
fprintf_plus(stderr, "Error initializing OpenPGP key"
682
safer_gnutls_strerror(ret));
686
ret = gnutls_openpgp_privkey_import
687
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
688
if(ret != GNUTLS_E_SUCCESS){
689
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
690
safer_gnutls_strerror(ret));
696
/* Use private key to suggest an appropriate
698
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
699
gnutls_openpgp_privkey_deinit(privkey);
701
fprintf_plus(stderr, "This OpenPGP key implies using"
702
" a GnuTLS security parameter \"%s\".\n",
703
safe_string(gnutls_sec_param_get_name
709
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
710
/* Err on the side of caution */
711
sec_param = GNUTLS_SEC_PARAM_ULTRA;
713
fprintf_plus(stderr, "Falling back to security parameter"
715
safe_string(gnutls_sec_param_get_name
720
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
724
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
725
" implies %u DH bits; using that.\n",
726
safe_string(gnutls_sec_param_get_name
731
fprintf_plus(stderr, "Failed to get implied number of DH"
732
" bits for security parameter \"%s\"): %s\n",
733
safe_string(gnutls_sec_param_get_name
735
safer_gnutls_strerror(ret));
739
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
742
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
743
if(ret != GNUTLS_E_SUCCESS){
744
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
745
" bits): %s\n", mc->dh_bits,
746
safer_gnutls_strerror(ret));
750
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
756
gnutls_certificate_free_credentials(mc->cred);
757
gnutls_dh_params_deinit(mc->dh_params);
761
__attribute__((nonnull, warn_unused_result))
762
static int init_gnutls_session(gnutls_session_t *session,
765
/* GnuTLS session creation */
767
ret = gnutls_init(session, GNUTLS_SERVER);
771
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
772
if(ret != GNUTLS_E_SUCCESS){
774
"Error in GnuTLS session initialization: %s\n",
775
safer_gnutls_strerror(ret));
781
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
783
gnutls_deinit(*session);
786
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
787
if(ret != GNUTLS_E_SUCCESS){
788
fprintf_plus(stderr, "Syntax error at: %s\n", err);
789
fprintf_plus(stderr, "GnuTLS error: %s\n",
790
safer_gnutls_strerror(ret));
791
gnutls_deinit(*session);
797
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
800
gnutls_deinit(*session);
803
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
804
if(ret != GNUTLS_E_SUCCESS){
805
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
806
safer_gnutls_strerror(ret));
807
gnutls_deinit(*session);
356
811
/* ignore client certificate if any. */
357
gnutls_certificate_server_set_request (es->session,
360
gnutls_dh_set_prime_bits (es->session, DH_BITS);
812
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
817
/* Avahi log function callback */
365
818
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
366
819
__attribute__((unused)) const char *txt){}
368
static int start_mandos_communication(const char *ip, uint16_t port,
821
/* Set effective uid to 0, return errno */
822
__attribute__((warn_unused_result))
823
int raise_privileges(void){
824
int old_errno = errno;
826
if(seteuid(0) == -1){
833
/* Set effective and real user ID to 0. Return errno. */
834
__attribute__((warn_unused_result))
835
int raise_privileges_permanently(void){
836
int old_errno = errno;
837
int ret = raise_privileges();
849
/* Set effective user ID to unprivileged saved user ID */
850
__attribute__((warn_unused_result))
851
int lower_privileges(void){
852
int old_errno = errno;
854
if(seteuid(uid) == -1){
861
/* Lower privileges permanently */
862
__attribute__((warn_unused_result))
863
int lower_privileges_permanently(void){
864
int old_errno = errno;
866
if(setuid(uid) == -1){
873
/* Helper function to add_local_route() and delete_local_route() */
874
__attribute__((nonnull, warn_unused_result))
875
static bool add_delete_local_route(const bool add,
877
AvahiIfIndex if_index){
879
char helper[] = "mandos-client-iprouteadddel";
880
char add_arg[] = "add";
881
char delete_arg[] = "delete";
882
char debug_flag[] = "--debug";
883
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
884
if(pluginhelperdir == NULL){
886
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
887
" variable not set; cannot run helper\n");
892
char interface[IF_NAMESIZE];
893
if(if_indextoname((unsigned int)if_index, interface) == NULL){
894
perror_plus("if_indextoname");
898
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
900
perror_plus("open(\"/dev/null\", O_RDONLY)");
906
/* Raise privileges */
907
errno = raise_privileges_permanently();
909
perror_plus("Failed to raise privileges");
910
/* _exit(EX_NOPERM); */
916
perror_plus("setgid");
919
/* Reset supplementary groups */
921
ret = setgroups(0, NULL);
923
perror_plus("setgroups");
927
ret = dup2(devnull, STDIN_FILENO);
929
perror_plus("dup2(devnull, STDIN_FILENO)");
932
ret = close(devnull);
934
perror_plus("close");
937
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
939
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
942
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
947
if(helperdir_fd == -1){
949
_exit(EX_UNAVAILABLE);
951
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
954
perror_plus("openat");
956
_exit(EX_UNAVAILABLE);
960
#pragma GCC diagnostic push
961
#pragma GCC diagnostic ignored "-Wcast-qual"
963
if(fexecve(helper_fd, (char *const [])
964
{ helper, add ? add_arg : delete_arg, (char *)address,
965
interface, debug ? debug_flag : NULL, NULL },
968
#pragma GCC diagnostic pop
970
perror_plus("fexecve");
982
pret = waitpid(pid, &status, 0);
983
if(pret == -1 and errno == EINTR and quit_now){
984
int errno_raising = 0;
985
if((errno = raise_privileges()) != 0){
986
errno_raising = errno;
987
perror_plus("Failed to raise privileges in order to"
988
" kill helper program");
990
if(kill(pid, SIGTERM) == -1){
993
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
994
perror_plus("Failed to lower privileges after killing"
999
} while(pret == -1 and errno == EINTR);
1001
perror_plus("waitpid");
1004
if(WIFEXITED(status)){
1005
if(WEXITSTATUS(status) != 0){
1006
fprintf_plus(stderr, "Error: iprouteadddel exited"
1007
" with status %d\n", WEXITSTATUS(status));
1012
if(WIFSIGNALED(status)){
1013
fprintf_plus(stderr, "Error: iprouteadddel died by"
1014
" signal %d\n", WTERMSIG(status));
1017
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1021
__attribute__((nonnull, warn_unused_result))
1022
static bool add_local_route(const char *address,
1023
AvahiIfIndex if_index){
1025
fprintf_plus(stderr, "Adding route to %s\n", address);
1027
return add_delete_local_route(true, address, if_index);
1030
__attribute__((nonnull, warn_unused_result))
1031
static bool delete_local_route(const char *address,
1032
AvahiIfIndex if_index){
1034
fprintf_plus(stderr, "Removing route to %s\n", address);
1036
return add_delete_local_route(false, address, if_index);
1039
/* Called when a Mandos server is found */
1040
__attribute__((nonnull, warn_unused_result))
1041
static int start_mandos_communication(const char *ip, in_port_t port,
369
1042
AvahiIfIndex if_index,
372
struct sockaddr_in6 to;
373
encrypted_session es;
1043
int af, mandos_context *mc){
1044
int ret, tcp_sd = -1;
1046
struct sockaddr_storage to;
374
1047
char *buffer = NULL;
375
char *decrypted_buffer;
1048
char *decrypted_buffer = NULL;
376
1049
size_t buffer_length = 0;
377
1050
size_t buffer_capacity = 0;
378
ssize_t decrypted_buffer_size;
381
char interface[IF_NAMESIZE];
384
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
388
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
395
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1053
gnutls_session_t session;
1054
int pf; /* Protocol family */
1055
bool route_added = false;
1072
fprintf_plus(stderr, "Bad address family: %d\n", af);
1077
/* If the interface is specified and we have a list of interfaces */
1078
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1079
/* Check if the interface is one of the interfaces we are using */
1082
char *interface = NULL;
1083
while((interface = argz_next(mc->interfaces,
1084
mc->interfaces_size,
1086
if(if_nametoindex(interface) == (unsigned int)if_index){
1093
/* This interface does not match any in the list, so we don't
1094
connect to the server */
397
perror("if_indextoname");
1096
char interface[IF_NAMESIZE];
1097
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1098
perror_plus("if_indextoname");
1100
fprintf_plus(stderr, "Skipping server on non-used interface"
1102
if_indextoname((unsigned int)if_index,
402
fprintf(stderr, "Binding to interface %s\n", interface);
405
memset(&to,0,sizeof(to)); /* Spurious warning */
406
to.sin6_family = AF_INET6;
407
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
1110
ret = init_gnutls_session(&session, mc);
1116
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1117
PRIuMAX "\n", ip, (uintmax_t)port);
1120
tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1123
perror_plus("socket");
1134
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1135
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1136
ret = inet_pton(af, ip, &to6->sin6_addr);
1138
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1139
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1140
ret = inet_pton(af, ip, &to4->sin_addr);
1144
perror_plus("inet_pton");
413
fprintf(stderr, "Bad address: %s\n", ip);
416
to.sin6_port = htons(port); /* Spurious warning */
1150
fprintf_plus(stderr, "Bad address: %s\n", ip);
1155
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
1156
if(IN6_IS_ADDR_LINKLOCAL
1157
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
1158
if(if_index == AVAHI_IF_UNSPEC){
1159
fprintf_plus(stderr, "An IPv6 link-local address is"
1160
" incomplete without a network interface\n");
1164
/* Set the network interface number as scope */
1165
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
1168
((struct sockaddr_in *)&to)->sin_port = htons(port);
418
to.sin6_scope_id = (uint32_t)if_index;
421
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
422
/* char addrstr[INET6_ADDRSTRLEN]; */
423
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
424
/* sizeof(addrstr)) == NULL){ */
425
/* perror("inet_ntop"); */
427
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
428
/* addrstr, ntohs(to.sin6_port)); */
432
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
438
char *out = mandos_protocol_version;
1177
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1178
char interface[IF_NAMESIZE];
1179
if(if_indextoname((unsigned int)if_index, interface) == NULL){
1180
perror_plus("if_indextoname");
1182
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
1183
"\n", ip, interface, (uintmax_t)port);
1186
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
1187
ip, (uintmax_t)port);
1189
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
1190
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
1192
ret = getnameinfo((struct sockaddr *)&to,
1193
sizeof(struct sockaddr_in6),
1194
addrstr, sizeof(addrstr), NULL, 0,
1197
ret = getnameinfo((struct sockaddr *)&to,
1198
sizeof(struct sockaddr_in),
1199
addrstr, sizeof(addrstr), NULL, 0,
1202
if(ret == EAI_SYSTEM){
1203
perror_plus("getnameinfo");
1204
} else if(ret != 0) {
1205
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
1206
} else if(strcmp(addrstr, ip) != 0){
1207
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1218
ret = connect(tcp_sd, (struct sockaddr *)&to,
1219
sizeof(struct sockaddr_in6));
1221
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1222
sizeof(struct sockaddr_in));
1225
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1226
and if_index != AVAHI_IF_UNSPEC
1227
and connect_to == NULL
1228
and not route_added and
1229
((af == AF_INET6 and not
1230
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1232
or (af == AF_INET and
1233
/* Not a a IPv4LL address */
1234
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1235
& 0xFFFF0000L) != 0xA9FE0000L))){
1236
/* Work around Avahi bug - Avahi does not announce link-local
1237
addresses if it has a global address, so local hosts with
1238
*only* a link-local address (e.g. Mandos clients) cannot
1239
connect to a Mandos server announced by Avahi on a server
1240
host with a global address. Work around this by retrying
1241
with an explicit route added with the server's address.
1243
Avahi bug reference:
1244
https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1245
https://bugs.debian.org/587961
1248
fprintf_plus(stderr, "Mandos server unreachable, trying"
1252
route_added = add_local_route(ip, if_index);
1258
if(errno != ECONNREFUSED or debug){
1260
perror_plus("connect");
1273
const char *out = mandos_protocol_version;
441
1276
size_t out_size = strlen(out);
442
ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
443
out_size - written));
1277
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
1278
out_size - written));
1281
perror_plus("write");
1285
written += (size_t)ret;
450
1286
if(written < out_size){
453
if (out == mandos_protocol_version){
1289
if(out == mandos_protocol_version){
462
ret = initgnutls (&es);
468
gnutls_transport_set_ptr (es.session,
469
(gnutls_transport_ptr_t) tcp_sd);
472
fprintf(stderr, "Establishing TLS session with %s\n", ip);
475
ret = gnutls_handshake (es.session);
477
if (ret != GNUTLS_E_SUCCESS){
1304
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1312
/* This casting via intptr_t is to eliminate warning about casting
1313
an int to a pointer type. This is exactly how the GnuTLS Guile
1314
function "set-session-transport-fd!" does it. */
1315
gnutls_transport_set_ptr(session,
1316
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1324
ret = gnutls_handshake(session);
1329
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1331
if(ret != GNUTLS_E_SUCCESS){
479
fprintf(stderr, "\n*** Handshake failed ***\n");
1333
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
486
//Retrieve OpenPGP packet that contains the wanted password
1340
/* Read OpenPGP packet that contains the wanted password */
489
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
1343
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
494
buffer_capacity = adjustbuffer(buffer, buffer_length, buffer_capacity);
495
if (buffer_capacity == 0){
496
perror("adjustbuffer");
501
ret = gnutls_record_recv
502
(es.session, buffer+buffer_length, BUFFER_SIZE);
1354
buffer_capacity = incbuffer(&buffer, buffer_length,
1356
if(buffer_capacity == 0){
1358
perror_plus("incbuffer");
1368
sret = gnutls_record_recv(session, buffer+buffer_length,
508
1375
case GNUTLS_E_INTERRUPTED:
509
1376
case GNUTLS_E_AGAIN:
511
1378
case GNUTLS_E_REHANDSHAKE:
512
ret = gnutls_handshake (es.session);
514
fprintf(stderr, "\n*** Handshake failed ***\n");
1380
ret = gnutls_handshake(session);
1386
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1388
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
521
fprintf(stderr, "Unknown error while reading data from"
522
" encrypted session with mandos server\n");
524
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
1396
fprintf_plus(stderr, "Unknown error while reading data from"
1397
" encrypted session with Mandos server\n");
1398
gnutls_bye(session, GNUTLS_SHUT_RDWR);
528
buffer_length += (size_t) ret;
532
if (buffer_length > 0){
533
decrypted_buffer_size = pgp_packet_decrypt(buffer,
537
if (decrypted_buffer_size >= 0){
1403
buffer_length += (size_t) sret;
1408
fprintf_plus(stderr, "Closing TLS session\n");
1417
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1422
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1424
if(buffer_length > 0){
1425
ssize_t decrypted_buffer_size;
1426
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1427
&decrypted_buffer, mc);
1428
if(decrypted_buffer_size >= 0){
539
1432
while(written < (size_t) decrypted_buffer_size){
540
ret = (int)fwrite (decrypted_buffer + written, 1,
541
(size_t)decrypted_buffer_size - written,
1438
ret = (int)fwrite(decrypted_buffer + written, 1,
1439
(size_t)decrypted_buffer_size - written,
543
1441
if(ret == 0 and ferror(stdout)){
545
fprintf(stderr, "Error writing encrypted data: %s\n",
1444
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
551
1450
written += (size_t)ret;
553
free(decrypted_buffer);
1452
ret = fflush(stdout);
1456
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1466
/* Shutdown procedure */
1471
if(not delete_local_route(ip, if_index)){
1472
fprintf_plus(stderr, "Failed to delete local route to %s on"
1473
" interface %d", ip, if_index);
1477
free(decrypted_buffer);
1480
ret = close(tcp_sd);
1486
perror_plus("close");
1488
gnutls_deinit(session);
562
fprintf(stderr, "Closing TLS session\n");
566
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
569
gnutls_deinit (es.session);
570
gnutls_certificate_free_credentials (es.cred);
571
gnutls_global_deinit ();
575
static void resolve_callback( AvahiSServiceResolver *r,
576
AvahiIfIndex interface,
577
AVAHI_GCC_UNUSED AvahiProtocol protocol,
578
AvahiResolverEvent event,
582
const char *host_name,
583
const AvahiAddress *address,
585
AVAHI_GCC_UNUSED AvahiStringList *txt,
586
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
587
AVAHI_GCC_UNUSED void* userdata) {
588
mandos_context *mc = userdata;
589
assert(r); /* Spurious warning */
1498
static void resolve_callback(AvahiSServiceResolver *r,
1499
AvahiIfIndex interface,
1500
AvahiProtocol proto,
1501
AvahiResolverEvent event,
1505
const char *host_name,
1506
const AvahiAddress *address,
1508
AVAHI_GCC_UNUSED AvahiStringList *txt,
1509
AVAHI_GCC_UNUSED AvahiLookupResultFlags
591
1516
/* Called whenever a service has been resolved successfully or
1520
avahi_s_service_resolver_free(r);
596
1526
case AVAHI_RESOLVER_FAILURE:
597
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
598
" type '%s' in domain '%s': %s\n", name, type, domain,
599
avahi_strerror(avahi_server_errno(mc->server)));
1527
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1528
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1530
avahi_strerror(avahi_server_errno
1531
(((mandos_context*)mc)->server)));
602
1534
case AVAHI_RESOLVER_FOUND:
604
1536
char ip[AVAHI_ADDRESS_STR_MAX];
605
1537
avahi_address_snprint(ip, sizeof(ip), address);
607
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
608
" port %d\n", name, host_name, ip, port);
1539
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1540
PRIdMAX ") on port %" PRIu16 "\n", name,
1541
host_name, ip, (intmax_t)interface, port);
610
int ret = start_mandos_communication(ip, port, interface, mc);
1543
int ret = start_mandos_communication(ip, (in_port_t)port,
1545
avahi_proto_to_af(proto),
1548
avahi_simple_poll_quit(simple_poll);
1550
if(not add_server(ip, (in_port_t)port, interface,
1551
avahi_proto_to_af(proto),
1552
&((mandos_context*)mc)->current_server)){
1553
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
616
1559
avahi_s_service_resolver_free(r);
619
static void browse_callback( AvahiSServiceBrowser *b,
620
AvahiIfIndex interface,
621
AvahiProtocol protocol,
622
AvahiBrowserEvent event,
626
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
628
mandos_context *mc = userdata;
629
assert(b); /* Spurious warning */
1562
static void browse_callback(AvahiSServiceBrowser *b,
1563
AvahiIfIndex interface,
1564
AvahiProtocol protocol,
1565
AvahiBrowserEvent event,
1569
AVAHI_GCC_UNUSED AvahiLookupResultFlags
631
1576
/* Called whenever a new services becomes available on the LAN or
632
1577
is removed from the LAN */
636
1585
case AVAHI_BROWSER_FAILURE:
638
fprintf(stderr, "(Browser) %s\n",
639
avahi_strerror(avahi_server_errno(mc->server)));
640
avahi_simple_poll_quit(mc->simple_poll);
1587
fprintf_plus(stderr, "(Avahi browser) %s\n",
1588
avahi_strerror(avahi_server_errno
1589
(((mandos_context*)mc)->server)));
1590
avahi_simple_poll_quit(simple_poll);
643
1593
case AVAHI_BROWSER_NEW:
644
/* We ignore the returned resolver object. In the callback
645
function we free it. If the server is terminated before
646
the callback function is called the server will free
647
the resolver for us. */
649
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
651
AVAHI_PROTO_INET6, 0,
652
resolve_callback, mc)))
653
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
654
avahi_strerror(avahi_server_errno(s)));
1594
/* We ignore the returned Avahi resolver object. In the callback
1595
function we free it. If the Avahi server is terminated before
1596
the callback function is called the Avahi server will free the
1599
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1600
interface, protocol, name, type,
1601
domain, protocol, 0,
1602
resolve_callback, mc) == NULL)
1603
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1605
avahi_strerror(avahi_server_errno
1606
(((mandos_context*)mc)->server)));
657
1609
case AVAHI_BROWSER_REMOVE:
660
1612
case AVAHI_BROWSER_ALL_FOR_NOW:
661
1613
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1615
fprintf_plus(stderr, "No Mandos server found, still"
666
/* Combines file name and path and returns the malloced new
667
string. some sane checks could/should be added */
668
static const char *combinepath(const char *first, const char *second){
669
size_t f_len = strlen(first);
670
size_t s_len = strlen(second);
671
char *tmp = malloc(f_len + s_len + 2);
676
memcpy(tmp, first, f_len);
680
memcpy(tmp + f_len + 1, second, s_len);
682
tmp[f_len + 1 + s_len] = '\0';
687
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1622
/* Signal handler that stops main loop after SIGTERM */
1623
static void handle_sigterm(int sig){
1628
signal_received = sig;
1629
int old_errno = errno;
1630
/* set main loop to exit */
1631
if(simple_poll != NULL){
1632
avahi_simple_poll_quit(simple_poll);
1637
__attribute__((nonnull, warn_unused_result))
1638
bool get_flags(const char *ifname, struct ifreq *ifr){
1642
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1645
perror_plus("socket");
1649
strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1650
ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1651
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1655
perror_plus("ioctl SIOCGIFFLAGS");
1663
__attribute__((nonnull, warn_unused_result))
1664
bool good_flags(const char *ifname, const struct ifreq *ifr){
1666
/* Reject the loopback device */
1667
if(ifr->ifr_flags & IFF_LOOPBACK){
1669
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1674
/* Accept point-to-point devices only if connect_to is specified */
1675
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1677
fprintf_plus(stderr, "Accepting point-to-point interface"
1678
" \"%s\"\n", ifname);
1682
/* Otherwise, reject non-broadcast-capable devices */
1683
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1685
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1686
" \"%s\"\n", ifname);
1690
/* Reject non-ARP interfaces (including dummy interfaces) */
1691
if(ifr->ifr_flags & IFF_NOARP){
1693
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1699
/* Accept this device */
1701
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1707
* This function determines if a directory entry in /sys/class/net
1708
* corresponds to an acceptable network device.
1709
* (This function is passed to scandir(3) as a filter function.)
1711
__attribute__((nonnull, warn_unused_result))
1712
int good_interface(const struct dirent *if_entry){
1713
if(if_entry->d_name[0] == '.'){
1718
if(not get_flags(if_entry->d_name, &ifr)){
1720
fprintf_plus(stderr, "Failed to get flags for interface "
1721
"\"%s\"\n", if_entry->d_name);
1726
if(not good_flags(if_entry->d_name, &ifr)){
1733
* This function determines if a network interface is up.
1735
__attribute__((nonnull, warn_unused_result))
1736
bool interface_is_up(const char *interface){
1738
if(not get_flags(interface, &ifr)){
1740
fprintf_plus(stderr, "Failed to get flags for interface "
1741
"\"%s\"\n", interface);
1746
return (bool)(ifr.ifr_flags & IFF_UP);
1750
* This function determines if a network interface is running
1752
__attribute__((nonnull, warn_unused_result))
1753
bool interface_is_running(const char *interface){
1755
if(not get_flags(interface, &ifr)){
1757
fprintf_plus(stderr, "Failed to get flags for interface "
1758
"\"%s\"\n", interface);
1763
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1766
__attribute__((nonnull, pure, warn_unused_result))
1767
int notdotentries(const struct dirent *direntry){
1768
/* Skip "." and ".." */
1769
if(direntry->d_name[0] == '.'
1770
and (direntry->d_name[1] == '\0'
1771
or (direntry->d_name[1] == '.'
1772
and direntry->d_name[2] == '\0'))){
1778
/* Is this directory entry a runnable program? */
1779
__attribute__((nonnull, warn_unused_result))
1780
int runnable_hook(const struct dirent *direntry){
1785
if((direntry->d_name)[0] == '\0'){
1790
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1791
"abcdefghijklmnopqrstuvwxyz"
1794
if((direntry->d_name)[sret] != '\0'){
1795
/* Contains non-allowed characters */
1797
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1803
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1806
perror_plus("Could not stat hook");
1810
if(not (S_ISREG(st.st_mode))){
1811
/* Not a regular file */
1813
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1818
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1819
/* Not executable */
1821
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1827
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1833
__attribute__((nonnull, warn_unused_result))
1834
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1835
mandos_context *mc){
1837
struct timespec now;
1838
struct timespec waited_time;
1839
intmax_t block_time;
1842
if(mc->current_server == NULL){
1844
fprintf_plus(stderr, "Wait until first server is found."
1847
ret = avahi_simple_poll_iterate(s, -1);
1850
fprintf_plus(stderr, "Check current_server if we should run"
1853
/* the current time */
1854
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1856
perror_plus("clock_gettime");
1859
/* Calculating in ms how long time between now and server
1860
who we visted longest time ago. Now - last seen. */
1861
waited_time.tv_sec = (now.tv_sec
1862
- mc->current_server->last_seen.tv_sec);
1863
waited_time.tv_nsec = (now.tv_nsec
1864
- mc->current_server->last_seen.tv_nsec);
1865
/* total time is 10s/10,000ms.
1866
Converting to s from ms by dividing by 1,000,
1867
and ns to ms by dividing by 1,000,000. */
1868
block_time = ((retry_interval
1869
- ((intmax_t)waited_time.tv_sec * 1000))
1870
- ((intmax_t)waited_time.tv_nsec / 1000000));
1873
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1877
if(block_time <= 0){
1878
ret = start_mandos_communication(mc->current_server->ip,
1879
mc->current_server->port,
1880
mc->current_server->if_index,
1881
mc->current_server->af, mc);
1883
avahi_simple_poll_quit(s);
1886
ret = clock_gettime(CLOCK_MONOTONIC,
1887
&mc->current_server->last_seen);
1889
perror_plus("clock_gettime");
1892
mc->current_server = mc->current_server->next;
1893
block_time = 0; /* Call avahi to find new Mandos
1894
servers, but don't block */
1897
ret = avahi_simple_poll_iterate(s, (int)block_time);
1900
if(ret > 0 or errno != EINTR){
1901
return (ret != 1) ? ret : 0;
1907
__attribute__((nonnull))
1908
void run_network_hooks(const char *mode, const char *interface,
1910
struct dirent **direntries = NULL;
1911
if(hookdir_fd == -1){
1912
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1914
if(hookdir_fd == -1){
1915
if(errno == ENOENT){
1917
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1918
" found\n", hookdir);
1921
perror_plus("open");
1926
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1927
runnable_hook, alphasort);
1929
perror_plus("scandir");
1932
struct dirent *direntry;
1934
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1936
perror_plus("open(\"/dev/null\", O_RDONLY)");
1939
for(int i = 0; i < numhooks; i++){
1940
direntry = direntries[i];
1942
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1945
pid_t hook_pid = fork();
1948
/* Raise privileges */
1949
errno = raise_privileges_permanently();
1951
perror_plus("Failed to raise privileges");
1958
perror_plus("setgid");
1961
/* Reset supplementary groups */
1963
ret = setgroups(0, NULL);
1965
perror_plus("setgroups");
1968
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1970
perror_plus("setenv");
1973
ret = setenv("DEVICE", interface, 1);
1975
perror_plus("setenv");
1978
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1980
perror_plus("setenv");
1983
ret = setenv("MODE", mode, 1);
1985
perror_plus("setenv");
1989
ret = asprintf(&delaystring, "%f", (double)delay);
1991
perror_plus("asprintf");
1994
ret = setenv("DELAY", delaystring, 1);
1997
perror_plus("setenv");
2001
if(connect_to != NULL){
2002
ret = setenv("CONNECT", connect_to, 1);
2004
perror_plus("setenv");
2008
int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
2012
perror_plus("openat");
2013
_exit(EXIT_FAILURE);
2015
if(close(hookdir_fd) == -1){
2016
perror_plus("close");
2017
_exit(EXIT_FAILURE);
2019
ret = dup2(devnull, STDIN_FILENO);
2021
perror_plus("dup2(devnull, STDIN_FILENO)");
2024
ret = close(devnull);
2026
perror_plus("close");
2029
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
2031
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
2034
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
2036
perror_plus("fexecve");
2037
_exit(EXIT_FAILURE);
2041
perror_plus("fork");
2046
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
2047
perror_plus("waitpid");
2051
if(WIFEXITED(status)){
2052
if(WEXITSTATUS(status) != 0){
2053
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
2054
" with status %d\n", direntry->d_name,
2055
WEXITSTATUS(status));
2059
} else if(WIFSIGNALED(status)){
2060
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
2061
" signal %d\n", direntry->d_name,
2066
fprintf_plus(stderr, "Warning: network hook \"%s\""
2067
" crashed\n", direntry->d_name);
2073
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
2079
if(close(hookdir_fd) == -1){
2080
perror_plus("close");
2087
__attribute__((nonnull, warn_unused_result))
2088
int bring_up_interface(const char *const interface,
2090
int old_errno = errno;
2092
struct ifreq network;
2093
unsigned int if_index = if_nametoindex(interface);
2095
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2105
if(not interface_is_up(interface)){
2107
int ioctl_errno = 0;
2108
if(not get_flags(interface, &network)){
2110
fprintf_plus(stderr, "Failed to get flags for interface "
2111
"\"%s\"\n", interface);
2115
network.ifr_flags |= IFF_UP; /* set flag */
2117
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2120
perror_plus("socket");
2128
perror_plus("close");
2135
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2139
/* Raise privileges */
2140
ret_errno = raise_privileges();
2143
perror_plus("Failed to raise privileges");
2148
bool restore_loglevel = false;
2150
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
2151
messages about the network interface to mess up the prompt */
2152
ret_linux = klogctl(8, NULL, 5);
2153
if(ret_linux == -1){
2154
perror_plus("klogctl");
2156
restore_loglevel = true;
2159
#endif /* __linux__ */
2160
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2161
ioctl_errno = errno;
2163
if(restore_loglevel){
2164
ret_linux = klogctl(7, NULL, 0);
2165
if(ret_linux == -1){
2166
perror_plus("klogctl");
2169
#endif /* __linux__ */
2171
/* If raise_privileges() succeeded above */
2173
/* Lower privileges */
2174
ret_errno = lower_privileges();
2177
perror_plus("Failed to lower privileges");
2181
/* Close the socket */
2184
perror_plus("close");
2187
if(ret_setflags == -1){
2188
errno = ioctl_errno;
2189
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2194
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2198
/* Sleep checking until interface is running.
2199
Check every 0.25s, up to total time of delay */
2200
for(int i = 0; i < delay * 4; i++){
2201
if(interface_is_running(interface)){
2204
struct timespec sleeptime = { .tv_nsec = 250000000 };
2205
ret = nanosleep(&sleeptime, NULL);
2206
if(ret == -1 and errno != EINTR){
2207
perror_plus("nanosleep");
2215
__attribute__((nonnull, warn_unused_result))
2216
int take_down_interface(const char *const interface){
2217
int old_errno = errno;
2218
struct ifreq network;
2219
unsigned int if_index = if_nametoindex(interface);
2221
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
2225
if(interface_is_up(interface)){
2227
int ioctl_errno = 0;
2228
if(not get_flags(interface, &network) and debug){
2230
fprintf_plus(stderr, "Failed to get flags for interface "
2231
"\"%s\"\n", interface);
2235
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2237
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2240
perror_plus("socket");
2246
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2250
/* Raise privileges */
2251
ret_errno = raise_privileges();
2254
perror_plus("Failed to raise privileges");
2257
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2258
ioctl_errno = errno;
2260
/* If raise_privileges() succeeded above */
2262
/* Lower privileges */
2263
ret_errno = lower_privileges();
2266
perror_plus("Failed to lower privileges");
2270
/* Close the socket */
2271
int ret = close(sd);
2273
perror_plus("close");
2276
if(ret_setflags == -1){
2277
errno = ioctl_errno;
2278
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2283
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2291
int main(int argc, char *argv[]){
2292
mandos_context mc = { .server = NULL, .dh_bits = 0,
2293
.priority = "SECURE256:!CTYPE-X.509"
2294
":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2295
.current_server = NULL, .interfaces = NULL,
2296
.interfaces_size = 0 };
2297
AvahiSServiceBrowser *sb = NULL;
2302
int exitcode = EXIT_SUCCESS;
2303
char *interfaces_to_take_down = NULL;
2304
size_t interfaces_to_take_down_size = 0;
2305
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
2306
char old_tempdir[] = "/tmp/mandosXXXXXX";
2307
char *tempdir = NULL;
2308
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2309
const char *seckey = PATHDIR "/" SECKEY;
2310
const char *pubkey = PATHDIR "/" PUBKEY;
2311
const char *dh_params_file = NULL;
2312
char *interfaces_hooks = NULL;
2314
bool gnutls_initialized = false;
2315
bool gpgme_initialized = false;
2317
double retry_interval = 10; /* 10s between trying a server and
2318
retrying the same server again */
2320
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2321
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2326
/* Lower any group privileges we might have, just to be safe */
2330
perror_plus("setgid");
2333
/* Lower user privileges (temporarily) */
2337
perror_plus("seteuid");
2345
struct argp_option options[] = {
2346
{ .name = "debug", .key = 128,
2347
.doc = "Debug mode", .group = 3 },
2348
{ .name = "connect", .key = 'c',
2349
.arg = "ADDRESS:PORT",
2350
.doc = "Connect directly to a specific Mandos server",
2352
{ .name = "interface", .key = 'i',
2354
.doc = "Network interface that will be used to search for"
2357
{ .name = "seckey", .key = 's',
2359
.doc = "OpenPGP secret key file base name",
2361
{ .name = "pubkey", .key = 'p',
2363
.doc = "OpenPGP public key file base name",
2365
{ .name = "dh-bits", .key = 129,
2367
.doc = "Bit length of the prime number used in the"
2368
" Diffie-Hellman key exchange",
2370
{ .name = "dh-params", .key = 134,
2372
.doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
2373
" for the Diffie-Hellman key exchange",
2375
{ .name = "priority", .key = 130,
2377
.doc = "GnuTLS priority string for the TLS handshake",
2379
{ .name = "delay", .key = 131,
2381
.doc = "Maximum delay to wait for interface startup",
2383
{ .name = "retry", .key = 132,
2385
.doc = "Retry interval used when denied by the Mandos server",
2387
{ .name = "network-hook-dir", .key = 133,
2389
.doc = "Directory where network hooks are located",
2392
* These reproduce what we would get without ARGP_NO_HELP
2394
{ .name = "help", .key = '?',
2395
.doc = "Give this help list", .group = -1 },
2396
{ .name = "usage", .key = -3,
2397
.doc = "Give a short usage message", .group = -1 },
2398
{ .name = "version", .key = 'V',
2399
.doc = "Print program version", .group = -1 },
2403
error_t parse_opt(int key, char *arg,
2404
struct argp_state *state){
2407
case 128: /* --debug */
2410
case 'c': /* --connect */
2413
case 'i': /* --interface */
2414
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2417
argp_error(state, "%s", strerror(ret_errno));
2420
case 's': /* --seckey */
2423
case 'p': /* --pubkey */
2426
case 129: /* --dh-bits */
2428
tmpmax = strtoimax(arg, &tmp, 10);
2429
if(errno != 0 or tmp == arg or *tmp != '\0'
2430
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2431
argp_error(state, "Bad number of DH bits");
2433
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2435
case 134: /* --dh-params */
2436
dh_params_file = arg;
2438
case 130: /* --priority */
2441
case 131: /* --delay */
2443
delay = strtof(arg, &tmp);
2444
if(errno != 0 or tmp == arg or *tmp != '\0'){
2445
argp_error(state, "Bad delay");
2447
case 132: /* --retry */
2449
retry_interval = strtod(arg, &tmp);
2450
if(errno != 0 or tmp == arg or *tmp != '\0'
2451
or (retry_interval * 1000) > INT_MAX
2452
or retry_interval < 0){
2453
argp_error(state, "Bad retry interval");
2456
case 133: /* --network-hook-dir */
2460
* These reproduce what we would get without ARGP_NO_HELP
2462
case '?': /* --help */
2463
argp_state_help(state, state->out_stream,
2464
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2465
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2466
case -3: /* --usage */
2467
argp_state_help(state, state->out_stream,
2468
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2469
case 'V': /* --version */
2470
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2471
exit(argp_err_exit_status);
2474
return ARGP_ERR_UNKNOWN;
2479
struct argp argp = { .options = options, .parser = parse_opt,
2481
.doc = "Mandos client -- Get and decrypt"
2482
" passwords from a Mandos server" };
2483
ret_errno = argp_parse(&argp, argc, argv,
2484
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2491
perror_plus("argp_parse");
2492
exitcode = EX_OSERR;
2495
exitcode = EX_USAGE;
2501
/* Work around Debian bug #633582:
2502
<https://bugs.debian.org/633582> */
2504
/* Re-raise privileges */
2505
ret = raise_privileges();
2508
perror_plus("Failed to raise privileges");
2512
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2513
int seckey_fd = open(seckey, O_RDONLY);
2514
if(seckey_fd == -1){
2515
perror_plus("open");
2517
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2519
perror_plus("fstat");
2521
if(S_ISREG(st.st_mode)
2522
and st.st_uid == 0 and st.st_gid == 0){
2523
ret = fchown(seckey_fd, uid, gid);
2525
perror_plus("fchown");
2533
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2534
int pubkey_fd = open(pubkey, O_RDONLY);
2535
if(pubkey_fd == -1){
2536
perror_plus("open");
2538
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2540
perror_plus("fstat");
2542
if(S_ISREG(st.st_mode)
2543
and st.st_uid == 0 and st.st_gid == 0){
2544
ret = fchown(pubkey_fd, uid, gid);
2546
perror_plus("fchown");
2554
if(dh_params_file != NULL
2555
and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2556
int dhparams_fd = open(dh_params_file, O_RDONLY);
2557
if(dhparams_fd == -1){
2558
perror_plus("open");
2560
ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
2562
perror_plus("fstat");
2564
if(S_ISREG(st.st_mode)
2565
and st.st_uid == 0 and st.st_gid == 0){
2566
ret = fchown(dhparams_fd, uid, gid);
2568
perror_plus("fchown");
2576
/* Lower privileges */
2577
ret = lower_privileges();
2580
perror_plus("Failed to lower privileges");
2585
/* Remove invalid interface names (except "none") */
2587
char *interface = NULL;
2588
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2590
if(strcmp(interface, "none") != 0
2591
and if_nametoindex(interface) == 0){
2592
if(interface[0] != '\0'){
2593
fprintf_plus(stderr, "Not using nonexisting interface"
2594
" \"%s\"\n", interface);
2596
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2602
/* Run network hooks */
2604
if(mc.interfaces != NULL){
2605
interfaces_hooks = malloc(mc.interfaces_size);
2606
if(interfaces_hooks == NULL){
2607
perror_plus("malloc");
2610
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2611
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2613
run_network_hooks("start", interfaces_hooks != NULL ?
2614
interfaces_hooks : "", delay);
2618
avahi_set_log_function(empty_log);
2621
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2622
from the signal handler */
2623
/* Initialize the pseudo-RNG for Avahi */
2624
srand((unsigned int) time(NULL));
2625
simple_poll = avahi_simple_poll_new();
2626
if(simple_poll == NULL){
2627
fprintf_plus(stderr,
2628
"Avahi: Failed to create simple poll object.\n");
2629
exitcode = EX_UNAVAILABLE;
2633
sigemptyset(&sigterm_action.sa_mask);
2634
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2636
perror_plus("sigaddset");
2637
exitcode = EX_OSERR;
2640
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2642
perror_plus("sigaddset");
2643
exitcode = EX_OSERR;
2646
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2648
perror_plus("sigaddset");
2649
exitcode = EX_OSERR;
2652
/* Need to check if the handler is SIG_IGN before handling:
2653
| [[info:libc:Initial Signal Actions]] |
2654
| [[info:libc:Basic Signal Handling]] |
2656
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2658
perror_plus("sigaction");
2661
if(old_sigterm_action.sa_handler != SIG_IGN){
2662
ret = sigaction(SIGINT, &sigterm_action, NULL);
2664
perror_plus("sigaction");
2665
exitcode = EX_OSERR;
2669
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2671
perror_plus("sigaction");
2674
if(old_sigterm_action.sa_handler != SIG_IGN){
2675
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2677
perror_plus("sigaction");
2678
exitcode = EX_OSERR;
2682
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2684
perror_plus("sigaction");
2687
if(old_sigterm_action.sa_handler != SIG_IGN){
2688
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2690
perror_plus("sigaction");
2691
exitcode = EX_OSERR;
2696
/* If no interfaces were specified, make a list */
2697
if(mc.interfaces == NULL){
2698
struct dirent **direntries = NULL;
2699
/* Look for any good interfaces */
2700
ret = scandir(sys_class_net, &direntries, good_interface,
2703
/* Add all found interfaces to interfaces list */
2704
for(int i = 0; i < ret; ++i){
2705
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2706
direntries[i]->d_name);
2709
perror_plus("argz_add");
2710
free(direntries[i]);
2714
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2715
direntries[i]->d_name);
2717
free(direntries[i]);
2724
fprintf_plus(stderr, "Could not find a network interface\n");
2725
exitcode = EXIT_FAILURE;
2730
/* Bring up interfaces which are down, and remove any "none"s */
2732
char *interface = NULL;
2733
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2735
/* If interface name is "none", stop bringing up interfaces.
2736
Also remove all instances of "none" from the list */
2737
if(strcmp(interface, "none") == 0){
2738
argz_delete(&mc.interfaces, &mc.interfaces_size,
2741
while((interface = argz_next(mc.interfaces,
2742
mc.interfaces_size, interface))){
2743
if(strcmp(interface, "none") == 0){
2744
argz_delete(&mc.interfaces, &mc.interfaces_size,
2751
bool interface_was_up = interface_is_up(interface);
2752
errno = bring_up_interface(interface, delay);
2753
if(not interface_was_up){
2755
fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
2756
" %s\n", interface, strerror(errno));
2758
errno = argz_add(&interfaces_to_take_down,
2759
&interfaces_to_take_down_size,
2762
perror_plus("argz_add");
2767
if(debug and (interfaces_to_take_down == NULL)){
2768
fprintf_plus(stderr, "No interfaces were brought up\n");
2772
/* If we only got one interface, explicitly use only that one */
2773
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2775
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2778
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2785
ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2787
fprintf_plus(stderr, "init_gnutls_global failed\n");
2788
exitcode = EX_UNAVAILABLE;
2791
gnutls_initialized = true;
2798
/* Try /run/tmp before /tmp */
2799
tempdir = mkdtemp(run_tempdir);
2800
if(tempdir == NULL and errno == ENOENT){
2802
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2803
run_tempdir, old_tempdir);
2805
tempdir = mkdtemp(old_tempdir);
2807
if(tempdir == NULL){
2808
perror_plus("mkdtemp");
2816
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2817
fprintf_plus(stderr, "init_gpgme failed\n");
2818
exitcode = EX_UNAVAILABLE;
2821
gpgme_initialized = true;
2828
if(connect_to != NULL){
2829
/* Connect directly, do not use Zeroconf */
2830
/* (Mainly meant for debugging) */
2831
char *address = strrchr(connect_to, ':');
2833
if(address == NULL){
2834
fprintf_plus(stderr, "No colon in address\n");
2835
exitcode = EX_USAGE;
2845
tmpmax = strtoimax(address+1, &tmp, 10);
2846
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2847
or tmpmax != (in_port_t)tmpmax){
2848
fprintf_plus(stderr, "Bad port number\n");
2849
exitcode = EX_USAGE;
2857
port = (in_port_t)tmpmax;
2859
/* Colon in address indicates IPv6 */
2861
if(strchr(connect_to, ':') != NULL){
2863
/* Accept [] around IPv6 address - see RFC 5952 */
2864
if(connect_to[0] == '[' and address[-1] == ']')
2872
address = connect_to;
2878
while(not quit_now){
2879
ret = start_mandos_communication(address, port, if_index, af,
2881
if(quit_now or ret == 0){
2885
fprintf_plus(stderr, "Retrying in %d seconds\n",
2886
(int)retry_interval);
2888
sleep((unsigned int)retry_interval);
2892
exitcode = EXIT_SUCCESS;
688
2903
AvahiServerConfig config;
689
AvahiSServiceBrowser *sb = NULL;
692
int returncode = EXIT_SUCCESS;
693
const char *interface = "eth0";
694
struct ifreq network;
696
char *connect_to = NULL;
697
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
698
mandos_context mc = { .simple_poll = NULL, .server = NULL,
699
.dh_bits = 2048, .priority = "SECURE256"};
702
static struct option long_options[] = {
703
{"debug", no_argument, (int *)&debug, 1},
704
{"connect", required_argument, 0, 'C'},
705
{"interface", required_argument, 0, 'i'},
706
{"certdir", required_argument, 0, 'd'},
707
{"certkey", required_argument, 0, 'c'},
708
{"certfile", required_argument, 0, 'k'},
709
{"dh_bits", required_argument, 0, 'D'},
710
{"priority", required_argument, 0, 'p'},
713
int option_index = 0;
714
ret = getopt_long (argc, argv, "i:", long_options,
743
tmp = strtol(optarg, NULL, 10);
744
if (errno == ERANGE){
752
mc.priority = optarg;
759
certfile = combinepath(certdir, certfile);
760
if (certfile == NULL){
761
perror("combinepath");
762
returncode = EXIT_FAILURE;
766
certkey = combinepath(certdir, certkey);
767
if (certkey == NULL){
768
perror("combinepath");
769
returncode = EXIT_FAILURE;
773
if_index = (AvahiIfIndex) if_nametoindex(interface);
775
fprintf(stderr, "No such interface: \"%s\"\n", interface);
779
if(connect_to != NULL){
780
/* Connect directly, do not use Zeroconf */
781
/* (Mainly meant for debugging) */
782
char *address = strrchr(connect_to, ':');
784
fprintf(stderr, "No colon in address\n");
788
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
790
perror("Bad port number");
794
address = connect_to;
795
ret = start_mandos_communication(address, port, if_index);
803
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
806
returncode = EXIT_FAILURE;
809
strcpy(network.ifr_name, interface);
810
ret = ioctl(sd, SIOCGIFFLAGS, &network);
813
perror("ioctl SIOCGIFFLAGS");
814
returncode = EXIT_FAILURE;
817
if((network.ifr_flags & IFF_UP) == 0){
818
network.ifr_flags |= IFF_UP;
819
ret = ioctl(sd, SIOCSIFFLAGS, &network);
821
perror("ioctl SIOCSIFFLAGS");
822
returncode = EXIT_FAILURE;
829
avahi_set_log_function(empty_log);
832
/* Initialize the psuedo-RNG */
833
srand((unsigned int) time(NULL));
835
/* Allocate main loop object */
836
if (!(mc.simple_poll = avahi_simple_poll_new())) {
837
fprintf(stderr, "Failed to create simple poll object.\n");
838
returncode = EXIT_FAILURE;
842
/* Do not publish any local records */
2904
/* Do not publish any local Zeroconf records */
843
2905
avahi_server_config_init(&config);
844
2906
config.publish_hinfo = 0;
845
2907
config.publish_addresses = 0;
846
2908
config.publish_workstation = 0;
847
2909
config.publish_domain = 0;
849
2911
/* Allocate a new server */
850
2912
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
851
&config, NULL, NULL, &error);
853
/* Free the configuration data */
2913
&config, NULL, NULL, &ret);
2915
/* Free the Avahi configuration data */
854
2916
avahi_server_config_free(&config);
856
/* Check if creating the server object succeeded */
858
fprintf(stderr, "Failed to create server: %s\n",
859
avahi_strerror(error));
860
returncode = EXIT_FAILURE;
864
/* Create the service browser */
865
sb = avahi_s_service_browser_new(mc.server, if_index,
867
"_mandos._tcp", NULL, 0,
868
browse_callback, &mc);
870
fprintf(stderr, "Failed to create service browser: %s\n",
871
avahi_strerror(avahi_server_errno(mc.server)));
872
returncode = EXIT_FAILURE;
876
/* Run the main loop */
879
fprintf(stderr, "Starting avahi loop search\n");
882
avahi_simple_poll_loop(simple_poll);
887
fprintf(stderr, "%s exiting\n", argv[0]);
892
avahi_s_service_browser_free(sb);
895
avahi_server_free(mc.server);
898
avahi_simple_poll_free(simple_poll);
2919
/* Check if creating the Avahi server object succeeded */
2920
if(mc.server == NULL){
2921
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2922
avahi_strerror(ret));
2923
exitcode = EX_UNAVAILABLE;
2931
/* Create the Avahi service browser */
2932
sb = avahi_s_service_browser_new(mc.server, if_index,
2933
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2934
NULL, 0, browse_callback,
2937
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2938
avahi_strerror(avahi_server_errno(mc.server)));
2939
exitcode = EX_UNAVAILABLE;
2947
/* Run the main loop */
2950
fprintf_plus(stderr, "Starting Avahi loop search\n");
2953
ret = avahi_loop_with_timeout(simple_poll,
2954
(int)(retry_interval * 1000), &mc);
2956
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2957
(ret == 0) ? "successfully" : "with error");
2963
if(signal_received){
2964
fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
2965
argv[0], signal_received,
2966
strsignal(signal_received));
2968
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2972
/* Cleanup things */
2973
free(mc.interfaces);
2976
avahi_s_service_browser_free(sb);
2978
if(mc.server != NULL)
2979
avahi_server_free(mc.server);
2981
if(simple_poll != NULL)
2982
avahi_simple_poll_free(simple_poll);
2984
if(gnutls_initialized){
2985
gnutls_certificate_free_credentials(mc.cred);
2986
gnutls_dh_params_deinit(mc.dh_params);
2989
if(gpgme_initialized){
2990
gpgme_release(mc.ctx);
2993
/* Cleans up the circular linked list of Mandos servers the client
2995
if(mc.current_server != NULL){
2996
mc.current_server->prev->next = NULL;
2997
while(mc.current_server != NULL){
2998
server *next = mc.current_server->next;
3000
#pragma GCC diagnostic push
3001
#pragma GCC diagnostic ignored "-Wcast-qual"
3003
free((char *)(mc.current_server->ip));
3005
#pragma GCC diagnostic pop
3007
free(mc.current_server);
3008
mc.current_server = next;
3012
/* Re-raise privileges */
3014
ret = raise_privileges();
3017
perror_plus("Failed to raise privileges");
3020
/* Run network hooks */
3021
run_network_hooks("stop", interfaces_hooks != NULL ?
3022
interfaces_hooks : "", delay);
3024
/* Take down the network interfaces which were brought up */
3026
char *interface = NULL;
3027
while((interface = argz_next(interfaces_to_take_down,
3028
interfaces_to_take_down_size,
3030
ret = take_down_interface(interface);
3033
perror_plus("Failed to take down interface");
3036
if(debug and (interfaces_to_take_down == NULL)){
3037
fprintf_plus(stderr, "No interfaces needed to be taken"
3043
ret = lower_privileges_permanently();
3046
perror_plus("Failed to lower privileges permanently");
3050
free(interfaces_to_take_down);
3051
free(interfaces_hooks);
3053
void clean_dir_at(int base, const char * const dirname,
3055
struct dirent **direntries = NULL;
3057
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3063
perror_plus("open");
3065
int numentries = scandirat(dir_fd, ".", &direntries,
3066
notdotentries, alphasort);
3067
if(numentries >= 0){
3068
for(int i = 0; i < numentries; i++){
3070
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3071
dirname, direntries[i]->d_name);
3073
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3075
if(errno == EISDIR){
3076
dret = unlinkat(dir_fd, direntries[i]->d_name,
3079
if((dret == -1) and (errno == ENOTEMPTY)
3080
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3081
== 0) and (level == 0)){
3082
/* Recurse only in this special case */
3083
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3087
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3088
direntries[i]->d_name, strerror(errno));
3091
free(direntries[i]);
3094
/* need to clean even if 0 because man page doesn't specify */
3096
if(numentries == -1){
3097
perror_plus("scandirat");
3099
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3100
if(dret == -1 and errno != ENOENT){
3101
perror_plus("rmdir");
3104
perror_plus("scandirat");
3109
/* Removes the GPGME temp directory and all files inside */
3110
if(tempdir != NULL){
3111
clean_dir_at(-1, tempdir, 0);
3115
sigemptyset(&old_sigterm_action.sa_mask);
3116
old_sigterm_action.sa_handler = SIG_DFL;
3117
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
3118
&old_sigterm_action,
3121
perror_plus("sigaction");
3124
ret = raise(signal_received);
3125
} while(ret != 0 and errno == EINTR);
3127
perror_plus("raise");
3130
TEMP_FAILURE_RETRY(pause());