45
108
#include <avahi-common/malloc.h>
46
109
#include <avahi-common/error.h>
49
#include <sys/types.h> /* socket(), inet_pton() */
50
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
51
struct in6_addr, inet_pton() */
52
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
53
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
55
#include <unistd.h> /* close() */
56
#include <netinet/in.h>
57
#include <stdbool.h> /* true */
58
#include <string.h> /* memset */
59
#include <arpa/inet.h> /* inet_pton() */
60
#include <iso646.h> /* not */
63
#include <errno.h> /* perror() */
112
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
115
init_gnutls_session(),
117
#include <gnutls/openpgp.h>
118
/* gnutls_certificate_set_openpgp_key_file(),
119
GNUTLS_OPENPGP_FMT_BASE64 */
122
#include <gpgme.h> /* All GPGME types, constants and
125
GPGME_PROTOCOL_OpenPGP,
69
128
#define BUFFER_SIZE 256
72
static const char *certdir = "/conf/conf.d/mandos";
73
static const char *certfile = "openpgp-client.txt";
74
static const char *certkey = "openpgp-client-key.txt";
130
#define PATHDIR "/conf/conf.d/mandos"
131
#define SECKEY "seckey.txt"
132
#define PUBKEY "pubkey.txt"
133
#define HOOKDIR "/lib/mandos/network-hooks.d"
76
135
bool debug = false;
136
static const char mandos_protocol_version[] = "1";
137
const char *argp_program_version = "mandos-client " VERSION;
138
const char *argp_program_bug_address = "<mandos@recompile.se>";
139
static const char sys_class_net[] = "/sys/class/net";
140
char *connect_to = NULL;
141
const char *hookdir = HOOKDIR;
145
/* Doubly linked list that need to be circularly linked when used */
146
typedef struct server{
149
AvahiIfIndex if_index;
151
struct timespec last_seen;
156
/* Used for passing in values through the Avahi callback functions */
79
gnutls_session_t session;
158
AvahiSimplePoll *simple_poll;
80
160
gnutls_certificate_credentials_t cred;
161
unsigned int dh_bits;
81
162
gnutls_dh_params_t dh_params;
85
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
gpgme_data_t dh_crypto, dh_plain;
163
const char *priority;
165
server *current_server;
168
/* global context so signal handler can reach it*/
169
mandos_context mc = { .simple_poll = NULL, .server = NULL,
170
.dh_bits = 1024, .priority = "SECURE256"
171
":!CTYPE-X.509:+CTYPE-OPENPGP",
172
.current_server = NULL };
174
sig_atomic_t quit_now = 0;
175
int signal_received = 0;
177
/* Function to use when printing errors */
178
void perror_plus(const char *print_text){
180
fprintf(stderr, "Mandos plugin %s: ",
181
program_invocation_short_name);
186
__attribute__((format (gnu_printf, 2, 3)))
187
int fprintf_plus(FILE *stream, const char *format, ...){
189
va_start (ap, format);
191
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
192
program_invocation_short_name));
193
return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
197
* Make additional room in "buffer" for at least BUFFER_SIZE more
198
* bytes. "buffer_capacity" is how much is currently allocated,
199
* "buffer_length" is how much is already used.
201
size_t incbuffer(char **buffer, size_t buffer_length,
202
size_t buffer_capacity){
203
if(buffer_length + BUFFER_SIZE > buffer_capacity){
204
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
208
buffer_capacity += BUFFER_SIZE;
210
return buffer_capacity;
213
/* Add server to set of servers to retry periodically */
214
bool add_server(const char *ip, uint16_t port, AvahiIfIndex if_index,
217
server *new_server = malloc(sizeof(server));
218
if(new_server == NULL){
219
perror_plus("malloc");
222
*new_server = (server){ .ip = strdup(ip),
224
.if_index = if_index,
226
if(new_server->ip == NULL){
227
perror_plus("strdup");
230
/* Special case of first server */
231
if (mc.current_server == NULL){
232
new_server->next = new_server;
233
new_server->prev = new_server;
234
mc.current_server = new_server;
235
/* Place the new server last in the list */
237
new_server->next = mc.current_server;
238
new_server->prev = mc.current_server->prev;
239
new_server->prev->next = new_server;
240
mc.current_server->prev = new_server;
242
ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
244
perror_plus("clock_gettime");
253
static bool init_gpgme(const char *seckey, const char *pubkey,
254
const char *tempdir){
92
ssize_t new_packet_capacity = 0;
93
ssize_t new_packet_length = 0;
94
256
gpgme_engine_info_t engine_info;
97
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
260
* Helper function to insert pub and seckey to the engine keyring.
262
bool import_key(const char *filename){
265
gpgme_data_t pgp_data;
267
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
273
rc = gpgme_data_new_from_fd(&pgp_data, fd);
274
if(rc != GPG_ERR_NO_ERROR){
275
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
276
gpgme_strsource(rc), gpgme_strerror(rc));
280
rc = gpgme_op_import(mc.ctx, pgp_data);
281
if(rc != GPG_ERR_NO_ERROR){
282
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
283
gpgme_strsource(rc), gpgme_strerror(rc));
287
ret = (int)TEMP_FAILURE_RETRY(close(fd));
289
perror_plus("close");
291
gpgme_data_release(pgp_data);
296
fprintf_plus(stderr, "Initializing GPGME\n");
101
300
gpgme_check_version(NULL);
102
301
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
103
if (rc != GPG_ERR_NO_ERROR){
104
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
105
gpgme_strsource(rc), gpgme_strerror(rc));
302
if(rc != GPG_ERR_NO_ERROR){
303
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
304
gpgme_strsource(rc), gpgme_strerror(rc));
109
/* Set GPGME home directory */
110
rc = gpgme_get_engine_info (&engine_info);
111
if (rc != GPG_ERR_NO_ERROR){
112
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
113
gpgme_strsource(rc), gpgme_strerror(rc));
308
/* Set GPGME home directory for the OpenPGP engine only */
309
rc = gpgme_get_engine_info(&engine_info);
310
if(rc != GPG_ERR_NO_ERROR){
311
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
312
gpgme_strsource(rc), gpgme_strerror(rc));
116
315
while(engine_info != NULL){
117
316
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
118
317
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
119
engine_info->file_name, homedir);
318
engine_info->file_name, tempdir);
122
321
engine_info = engine_info->next;
124
323
if(engine_info == NULL){
125
fprintf(stderr, "Could not set home dir to %s\n", homedir);
129
/* Create new GPGME data buffer from packet buffer */
130
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
131
if (rc != GPG_ERR_NO_ERROR){
132
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
133
gpgme_strsource(rc), gpgme_strerror(rc));
324
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
329
/* Create new GPGME "context" */
330
rc = gpgme_new(&(mc.ctx));
331
if(rc != GPG_ERR_NO_ERROR){
332
fprintf_plus(stderr, "Mandos plugin mandos-client: "
333
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
338
if(not import_key(pubkey) or not import_key(seckey)){
346
* Decrypt OpenPGP data.
347
* Returns -1 on error
349
static ssize_t pgp_packet_decrypt(const char *cryptotext,
352
gpgme_data_t dh_crypto, dh_plain;
355
size_t plaintext_capacity = 0;
356
ssize_t plaintext_length = 0;
359
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
362
/* Create new GPGME data buffer from memory cryptotext */
363
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
365
if(rc != GPG_ERR_NO_ERROR){
366
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
367
gpgme_strsource(rc), gpgme_strerror(rc));
137
371
/* Create new empty GPGME data buffer for the plaintext */
138
372
rc = gpgme_data_new(&dh_plain);
139
if (rc != GPG_ERR_NO_ERROR){
140
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
141
gpgme_strsource(rc), gpgme_strerror(rc));
145
/* Create new GPGME "context" */
146
rc = gpgme_new(&ctx);
147
if (rc != GPG_ERR_NO_ERROR){
148
fprintf(stderr, "bad gpgme_new: %s: %s\n",
149
gpgme_strsource(rc), gpgme_strerror(rc));
153
/* Decrypt data from the FILE pointer to the plaintext data
155
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
156
if (rc != GPG_ERR_NO_ERROR){
157
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
158
gpgme_strsource(rc), gpgme_strerror(rc));
163
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
167
gpgme_decrypt_result_t result;
168
result = gpgme_op_decrypt_result(ctx);
170
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
172
fprintf(stderr, "Unsupported algorithm: %s\n",
173
result->unsupported_algorithm);
174
fprintf(stderr, "Wrong key usage: %d\n",
175
result->wrong_key_usage);
176
if(result->file_name != NULL){
177
fprintf(stderr, "File name: %s\n", result->file_name);
179
gpgme_recipient_t recipient;
180
recipient = result->recipients;
373
if(rc != GPG_ERR_NO_ERROR){
374
fprintf_plus(stderr, "Mandos plugin mandos-client: "
375
"bad gpgme_data_new: %s: %s\n",
376
gpgme_strsource(rc), gpgme_strerror(rc));
377
gpgme_data_release(dh_crypto);
381
/* Decrypt data from the cryptotext data buffer to the plaintext
383
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
384
if(rc != GPG_ERR_NO_ERROR){
385
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
386
gpgme_strsource(rc), gpgme_strerror(rc));
387
plaintext_length = -1;
389
gpgme_decrypt_result_t result;
390
result = gpgme_op_decrypt_result(mc.ctx);
392
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
394
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
395
result->unsupported_algorithm);
396
fprintf_plus(stderr, "Wrong key usage: %u\n",
397
result->wrong_key_usage);
398
if(result->file_name != NULL){
399
fprintf_plus(stderr, "File name: %s\n", result->file_name);
401
gpgme_recipient_t recipient;
402
recipient = result->recipients;
182
403
while(recipient != NULL){
183
fprintf(stderr, "Public key algorithm: %s\n",
184
gpgme_pubkey_algo_name(recipient->pubkey_algo));
185
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
186
fprintf(stderr, "Secret key available: %s\n",
187
recipient->status == GPG_ERR_NO_SECKEY
404
fprintf_plus(stderr, "Public key algorithm: %s\n",
405
gpgme_pubkey_algo_name
406
(recipient->pubkey_algo));
407
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
408
fprintf_plus(stderr, "Secret key available: %s\n",
409
recipient->status == GPG_ERR_NO_SECKEY
189
411
recipient = recipient->next;
195
/* Delete the GPGME FILE pointer cryptotext data buffer */
196
gpgme_data_release(dh_crypto);
419
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
198
422
/* Seek back to the beginning of the GPGME plaintext data buffer */
199
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
200
perror("pgpme_data_seek");
423
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
424
perror_plus("gpgme_data_seek");
425
plaintext_length = -1;
205
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
206
*new_packet = realloc(*new_packet,
207
(unsigned int)new_packet_capacity
209
if (*new_packet == NULL){
213
new_packet_capacity += BUFFER_SIZE;
431
plaintext_capacity = incbuffer(plaintext,
432
(size_t)plaintext_length,
434
if(plaintext_capacity == 0){
435
perror_plus("incbuffer");
436
plaintext_length = -1;
216
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
440
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
218
442
/* Print the data, if any */
223
perror("gpgme_data_read");
226
new_packet_length += ret;
229
/* FIXME: check characters before printing to screen so to not print
230
terminal control characters */
232
/* fprintf(stderr, "decrypted password is: "); */
233
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
234
/* fprintf(stderr, "\n"); */
448
perror_plus("gpgme_data_read");
449
plaintext_length = -1;
452
plaintext_length += ret;
456
fprintf_plus(stderr, "Decrypted password is: ");
457
for(ssize_t i = 0; i < plaintext_length; i++){
458
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
460
fprintf(stderr, "\n");
465
/* Delete the GPGME cryptotext data buffer */
466
gpgme_data_release(dh_crypto);
237
468
/* Delete the GPGME plaintext data buffer */
238
469
gpgme_data_release(dh_plain);
239
return new_packet_length;
470
return plaintext_length;
242
static const char * safer_gnutls_strerror (int value) {
243
const char *ret = gnutls_strerror (value);
473
static const char * safer_gnutls_strerror(int value){
474
const char *ret = gnutls_strerror(value); /* Spurious warning from
475
-Wunreachable-code */
245
477
ret = "(unknown)";
481
/* GnuTLS log function callback */
249
482
static void debuggnutls(__attribute__((unused)) int level,
250
483
const char* string){
251
fprintf(stderr, "%s", string);
484
fprintf_plus(stderr, "GnuTLS: %s", string);
254
static int initgnutls(encrypted_session *es){
487
static int init_gnutls_global(const char *pubkeyfilename,
488
const char *seckeyfilename){
259
fprintf(stderr, "Initializing GnuTLS\n");
492
fprintf_plus(stderr, "Initializing GnuTLS\n");
262
if ((ret = gnutls_global_init ())
263
!= GNUTLS_E_SUCCESS) {
264
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
495
ret = gnutls_global_init();
496
if(ret != GNUTLS_E_SUCCESS){
497
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
498
safer_gnutls_strerror(ret));
503
/* "Use a log level over 10 to enable all debugging options."
269
506
gnutls_global_set_log_level(11);
270
507
gnutls_global_set_log_function(debuggnutls);
273
/* openpgp credentials */
274
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
275
!= GNUTLS_E_SUCCESS) {
276
fprintf (stderr, "memory error: %s\n",
277
safer_gnutls_strerror(ret));
510
/* OpenPGP credentials */
511
ret = gnutls_certificate_allocate_credentials(&mc.cred);
512
if(ret != GNUTLS_E_SUCCESS){
513
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
514
safer_gnutls_strerror(ret));
515
gnutls_global_deinit();
282
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
283
" and keyfile %s as GnuTLS credentials\n", certfile,
520
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
521
" secret key %s as GnuTLS credentials\n",
287
526
ret = gnutls_certificate_set_openpgp_key_file
288
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
289
if (ret != GNUTLS_E_SUCCESS) {
291
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
293
ret, certfile, certkey);
294
fprintf(stdout, "The Error is: %s\n",
295
safer_gnutls_strerror(ret));
299
//GnuTLS server initialization
300
if ((ret = gnutls_dh_params_init (&es->dh_params))
301
!= GNUTLS_E_SUCCESS) {
302
fprintf (stderr, "Error in dh parameter initialization: %s\n",
303
safer_gnutls_strerror(ret));
307
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
308
!= GNUTLS_E_SUCCESS) {
309
fprintf (stderr, "Error in prime generation: %s\n",
310
safer_gnutls_strerror(ret));
314
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
316
// GnuTLS session creation
317
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
318
!= GNUTLS_E_SUCCESS){
319
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
320
safer_gnutls_strerror(ret));
323
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
324
!= GNUTLS_E_SUCCESS) {
325
fprintf(stderr, "Syntax error at: %s\n", err);
326
fprintf(stderr, "GnuTLS error: %s\n",
327
safer_gnutls_strerror(ret));
331
if ((ret = gnutls_credentials_set
332
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
333
!= GNUTLS_E_SUCCESS) {
334
fprintf(stderr, "Error setting a credentials set: %s\n",
335
safer_gnutls_strerror(ret));
527
(mc.cred, pubkeyfilename, seckeyfilename,
528
GNUTLS_OPENPGP_FMT_BASE64);
529
if(ret != GNUTLS_E_SUCCESS){
531
"Error[%d] while reading the OpenPGP key pair ('%s',"
532
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
533
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
534
safer_gnutls_strerror(ret));
538
/* GnuTLS server initialization */
539
ret = gnutls_dh_params_init(&mc.dh_params);
540
if(ret != GNUTLS_E_SUCCESS){
541
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
542
" initialization: %s\n",
543
safer_gnutls_strerror(ret));
546
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
547
if(ret != GNUTLS_E_SUCCESS){
548
fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
549
safer_gnutls_strerror(ret));
553
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
559
gnutls_certificate_free_credentials(mc.cred);
560
gnutls_global_deinit();
561
gnutls_dh_params_deinit(mc.dh_params);
565
static int init_gnutls_session(gnutls_session_t *session){
567
/* GnuTLS session creation */
569
ret = gnutls_init(session, GNUTLS_SERVER);
573
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
574
if(ret != GNUTLS_E_SUCCESS){
576
"Error in GnuTLS session initialization: %s\n",
577
safer_gnutls_strerror(ret));
583
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
585
gnutls_deinit(*session);
588
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
589
if(ret != GNUTLS_E_SUCCESS){
590
fprintf_plus(stderr, "Syntax error at: %s\n", err);
591
fprintf_plus(stderr, "GnuTLS error: %s\n",
592
safer_gnutls_strerror(ret));
593
gnutls_deinit(*session);
599
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
602
gnutls_deinit(*session);
605
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
606
if(ret != GNUTLS_E_SUCCESS){
607
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
608
safer_gnutls_strerror(ret));
609
gnutls_deinit(*session);
339
613
/* ignore client certificate if any. */
340
gnutls_certificate_server_set_request (es->session,
614
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
343
gnutls_dh_set_prime_bits (es->session, DH_BITS);
616
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
621
/* Avahi log function callback */
348
622
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
349
623
__attribute__((unused)) const char *txt){}
625
/* Called when a Mandos server is found */
351
626
static int start_mandos_communication(const char *ip, uint16_t port,
352
AvahiIfIndex if_index){
354
struct sockaddr_in6 to;
355
encrypted_session es;
627
AvahiIfIndex if_index,
629
int ret, tcp_sd = -1;
632
struct sockaddr_in in;
633
struct sockaddr_in6 in6;
356
635
char *buffer = NULL;
357
char *decrypted_buffer;
636
char *decrypted_buffer = NULL;
358
637
size_t buffer_length = 0;
359
638
size_t buffer_capacity = 0;
360
ssize_t decrypted_buffer_size;
363
char interface[IF_NAMESIZE];
366
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
370
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
376
if(if_indextoname((unsigned int)if_index, interface) == NULL){
378
perror("if_indextoname");
384
fprintf(stderr, "Binding to interface %s\n", interface);
387
memset(&to,0,sizeof(to)); /* Spurious warning */
388
to.sin6_family = AF_INET6;
389
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
641
gnutls_session_t session;
642
int pf; /* Protocol family */
659
fprintf_plus(stderr, "Bad address family: %d\n", af);
664
ret = init_gnutls_session(&session);
670
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
671
PRIu16 "\n", ip, port);
674
tcp_sd = socket(pf, SOCK_STREAM, 0);
677
perror_plus("socket");
687
memset(&to, 0, sizeof(to));
689
to.in6.sin6_family = (sa_family_t)af;
690
ret = inet_pton(af, ip, &to.in6.sin6_addr);
692
to.in.sin_family = (sa_family_t)af;
693
ret = inet_pton(af, ip, &to.in.sin_addr);
697
perror_plus("inet_pton");
395
fprintf(stderr, "Bad address: %s\n", ip);
398
to.sin6_port = htons(port); /* Spurious warning */
400
to.sin6_scope_id = (uint32_t)if_index;
403
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
404
/* char addrstr[INET6_ADDRSTRLEN]; */
405
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
406
/* sizeof(addrstr)) == NULL){ */
407
/* perror("inet_ntop"); */
409
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
410
/* addrstr, ntohs(to.sin6_port)); */
414
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
420
ret = initgnutls (&es);
426
gnutls_transport_set_ptr (es.session,
427
(gnutls_transport_ptr_t) tcp_sd);
430
fprintf(stderr, "Establishing TLS session with %s\n", ip);
433
ret = gnutls_handshake (es.session);
435
if (ret != GNUTLS_E_SUCCESS){
703
fprintf_plus(stderr, "Bad address: %s\n", ip);
708
to.in6.sin6_port = htons(port); /* Spurious warnings from
710
-Wunreachable-code */
712
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
713
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
715
if(if_index == AVAHI_IF_UNSPEC){
716
fprintf_plus(stderr, "An IPv6 link-local address is"
717
" incomplete without a network interface\n");
721
/* Set the network interface number as scope */
722
to.in6.sin6_scope_id = (uint32_t)if_index;
725
to.in.sin_port = htons(port); /* Spurious warnings from
727
-Wunreachable-code */
736
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
737
char interface[IF_NAMESIZE];
738
if(if_indextoname((unsigned int)if_index, interface) == NULL){
739
perror_plus("if_indextoname");
741
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIu16
742
"\n", ip, interface, port);
745
fprintf_plus(stderr, "Connection to: %s, port %" PRIu16 "\n",
748
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
749
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
752
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
755
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
759
perror_plus("inet_ntop");
761
if(strcmp(addrstr, ip) != 0){
762
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
773
ret = connect(tcp_sd, &to.in6, sizeof(to));
775
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
778
if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
780
perror_plus("connect");
791
const char *out = mandos_protocol_version;
794
size_t out_size = strlen(out);
795
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
796
out_size - written));
799
perror_plus("write");
803
written += (size_t)ret;
804
if(written < out_size){
807
if(out == mandos_protocol_version){
822
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
830
/* This casting via intptr_t is to eliminate warning about casting
831
an int to a pointer type. This is exactly how the GnuTLS Guile
832
function "set-session-transport-fd!" does it. */
833
gnutls_transport_set_ptr(session,
834
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
842
ret = gnutls_handshake(session);
847
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
849
if(ret != GNUTLS_E_SUCCESS){
437
fprintf(stderr, "\n*** Handshake failed ***\n");
851
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
444
//Retrieve OpenPGP packet that contains the wanted password
858
/* Read OpenPGP packet that contains the wanted password */
447
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
861
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
452
if (buffer_length + BUFFER_SIZE > buffer_capacity){
453
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
458
buffer_capacity += BUFFER_SIZE;
461
ret = gnutls_record_recv
462
(es.session, buffer+buffer_length, BUFFER_SIZE);
872
buffer_capacity = incbuffer(&buffer, buffer_length,
874
if(buffer_capacity == 0){
876
perror_plus("incbuffer");
886
sret = gnutls_record_recv(session, buffer+buffer_length,
468
893
case GNUTLS_E_INTERRUPTED:
469
894
case GNUTLS_E_AGAIN:
471
896
case GNUTLS_E_REHANDSHAKE:
472
ret = gnutls_handshake (es.session);
474
fprintf(stderr, "\n*** Handshake failed ***\n");
898
ret = gnutls_handshake(session);
904
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
906
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
481
fprintf(stderr, "Unknown error while reading data from"
482
" encrypted session with mandos server\n");
484
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
914
fprintf_plus(stderr, "Unknown error while reading data from"
915
" encrypted session with Mandos server\n");
916
gnutls_bye(session, GNUTLS_SHUT_RDWR);
488
buffer_length += (size_t) ret;
492
if (buffer_length > 0){
493
decrypted_buffer_size = pgp_packet_decrypt(buffer,
497
if (decrypted_buffer_size >= 0){
921
buffer_length += (size_t) sret;
926
fprintf_plus(stderr, "Closing TLS session\n");
935
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
940
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
942
if(buffer_length > 0){
943
ssize_t decrypted_buffer_size;
944
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
946
if(decrypted_buffer_size >= 0){
498
949
while(written < (size_t) decrypted_buffer_size){
499
ret = (int)fwrite (decrypted_buffer + written, 1,
500
(size_t)decrypted_buffer_size - written,
955
ret = (int)fwrite(decrypted_buffer + written, 1,
956
(size_t)decrypted_buffer_size - written,
502
958
if(ret == 0 and ferror(stdout)){
504
fprintf(stderr, "Error writing encrypted data: %s\n",
961
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
510
967
written += (size_t)ret;
512
free(decrypted_buffer);
973
/* Shutdown procedure */
978
free(decrypted_buffer);
981
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
987
perror_plus("close");
989
gnutls_deinit(session);
521
fprintf(stderr, "Closing TLS session\n");
525
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
528
gnutls_deinit (es.session);
529
gnutls_certificate_free_credentials (es.cred);
530
gnutls_global_deinit ();
534
static AvahiSimplePoll *simple_poll = NULL;
535
static AvahiServer *server = NULL;
537
static void resolve_callback(
538
AvahiSServiceResolver *r,
539
AvahiIfIndex interface,
540
AVAHI_GCC_UNUSED AvahiProtocol protocol,
541
AvahiResolverEvent event,
545
const char *host_name,
546
const AvahiAddress *address,
548
AVAHI_GCC_UNUSED AvahiStringList *txt,
549
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
550
AVAHI_GCC_UNUSED void* userdata) {
552
assert(r); /* Spurious warning */
999
static void resolve_callback(AvahiSServiceResolver *r,
1000
AvahiIfIndex interface,
1001
AvahiProtocol proto,
1002
AvahiResolverEvent event,
1006
const char *host_name,
1007
const AvahiAddress *address,
1009
AVAHI_GCC_UNUSED AvahiStringList *txt,
1010
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1012
AVAHI_GCC_UNUSED void* userdata){
554
1015
/* Called whenever a service has been resolved successfully or
559
1024
case AVAHI_RESOLVER_FAILURE:
560
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
561
" type '%s' in domain '%s': %s\n", name, type, domain,
562
avahi_strerror(avahi_server_errno(server)));
1025
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1026
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1028
avahi_strerror(avahi_server_errno(mc.server)));
565
1031
case AVAHI_RESOLVER_FOUND:
567
1033
char ip[AVAHI_ADDRESS_STR_MAX];
568
1034
avahi_address_snprint(ip, sizeof(ip), address);
570
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
" port %d\n", name, host_name, ip, port);
1036
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1037
PRIdMAX ") on port %" PRIu16 "\n", name,
1038
host_name, ip, (intmax_t)interface, port);
573
int ret = start_mandos_communication(ip, port, interface);
1040
int ret = start_mandos_communication(ip, port, interface,
1041
avahi_proto_to_af(proto));
1043
avahi_simple_poll_quit(mc.simple_poll);
1045
if(not add_server(ip, port, interface,
1046
avahi_proto_to_af(proto))){
1047
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
579
1053
avahi_s_service_resolver_free(r);
582
static void browse_callback(
583
AvahiSServiceBrowser *b,
584
AvahiIfIndex interface,
585
AvahiProtocol protocol,
586
AvahiBrowserEvent event,
590
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
593
AvahiServer *s = userdata;
594
assert(b); /* Spurious warning */
596
/* Called whenever a new services becomes available on the LAN or
597
is removed from the LAN */
1056
static void browse_callback(AvahiSServiceBrowser *b,
1057
AvahiIfIndex interface,
1058
AvahiProtocol protocol,
1059
AvahiBrowserEvent event,
1063
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1065
AVAHI_GCC_UNUSED void* userdata){
1068
/* Called whenever a new services becomes available on the LAN or
1069
is removed from the LAN */
1077
case AVAHI_BROWSER_FAILURE:
1079
fprintf_plus(stderr, "(Avahi browser) %s\n",
1080
avahi_strerror(avahi_server_errno(mc.server)));
1081
avahi_simple_poll_quit(mc.simple_poll);
1084
case AVAHI_BROWSER_NEW:
1085
/* We ignore the returned Avahi resolver object. In the callback
1086
function we free it. If the Avahi server is terminated before
1087
the callback function is called the Avahi server will free the
1090
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1091
name, type, domain, protocol, 0,
1092
resolve_callback, NULL) == NULL)
1093
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1095
avahi_strerror(avahi_server_errno(mc.server)));
1098
case AVAHI_BROWSER_REMOVE:
1101
case AVAHI_BROWSER_ALL_FOR_NOW:
1102
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1104
fprintf_plus(stderr, "No Mandos server found, still"
1111
/* Signal handler that stops main loop after SIGTERM */
1112
static void handle_sigterm(int sig){
1117
signal_received = sig;
1118
int old_errno = errno;
1119
/* set main loop to exit */
1120
if(mc.simple_poll != NULL){
1121
avahi_simple_poll_quit(mc.simple_poll);
1126
bool get_flags(const char *ifname, struct ifreq *ifr){
1130
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1133
perror_plus("socket");
1137
strcpy(ifr->ifr_name, ifname);
1138
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1142
perror_plus("ioctl SIOCGIFFLAGS");
1150
bool good_flags(const char *ifname, const struct ifreq *ifr){
1152
/* Reject the loopback device */
1153
if(ifr->ifr_flags & IFF_LOOPBACK){
1155
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1160
/* Accept point-to-point devices only if connect_to is specified */
1161
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1163
fprintf_plus(stderr, "Accepting point-to-point interface"
1164
" \"%s\"\n", ifname);
1168
/* Otherwise, reject non-broadcast-capable devices */
1169
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1171
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1172
" \"%s\"\n", ifname);
1176
/* Reject non-ARP interfaces (including dummy interfaces) */
1177
if(ifr->ifr_flags & IFF_NOARP){
1179
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1185
/* Accept this device */
1187
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1193
* This function determines if a directory entry in /sys/class/net
1194
* corresponds to an acceptable network device.
1195
* (This function is passed to scandir(3) as a filter function.)
1197
int good_interface(const struct dirent *if_entry){
1198
if(if_entry->d_name[0] == '.'){
1203
if(not get_flags(if_entry->d_name, &ifr)){
1205
fprintf_plus(stderr, "Failed to get flags for interface "
1206
"\"%s\"\n", if_entry->d_name);
1211
if(not good_flags(if_entry->d_name, &ifr)){
1218
* This function determines if a network interface is up.
1220
bool interface_is_up(const char *interface){
1222
if(not get_flags(interface, &ifr)){
1224
fprintf_plus(stderr, "Failed to get flags for interface "
1225
"\"%s\"\n", interface);
1230
return (bool)(ifr.ifr_flags & IFF_UP);
1234
* This function determines if a network interface is running
1236
bool interface_is_running(const char *interface){
1238
if(not get_flags(interface, &ifr)){
1240
fprintf_plus(stderr, "Failed to get flags for interface "
1241
"\"%s\"\n", interface);
1246
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1249
int notdotentries(const struct dirent *direntry){
1250
/* Skip "." and ".." */
1251
if(direntry->d_name[0] == '.'
1252
and (direntry->d_name[1] == '\0'
1253
or (direntry->d_name[1] == '.'
1254
and direntry->d_name[2] == '\0'))){
1260
/* Is this directory entry a runnable program? */
1261
int runnable_hook(const struct dirent *direntry){
1266
if((direntry->d_name)[0] == '\0'){
1271
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1272
"abcdefghijklmnopqrstuvwxyz"
1275
if((direntry->d_name)[sret] != '\0'){
1276
/* Contains non-allowed characters */
1278
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1284
char *fullname = NULL;
1285
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1287
perror_plus("asprintf");
1291
ret = stat(fullname, &st);
1294
perror_plus("Could not stat hook");
1298
if(not (S_ISREG(st.st_mode))){
1299
/* Not a regular file */
1301
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1306
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1307
/* Not executable */
1309
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1315
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1321
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
1323
struct timespec now;
1324
struct timespec waited_time;
1325
intmax_t block_time;
1328
if(mc.current_server == NULL){
1330
fprintf_plus(stderr, "Wait until first server is found."
1333
ret = avahi_simple_poll_iterate(s, -1);
1336
fprintf_plus(stderr, "Check current_server if we should run"
1339
/* the current time */
1340
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1342
perror_plus("clock_gettime");
1345
/* Calculating in ms how long time between now and server
1346
who we visted longest time ago. Now - last seen. */
1347
waited_time.tv_sec = (now.tv_sec
1348
- mc.current_server->last_seen.tv_sec);
1349
waited_time.tv_nsec = (now.tv_nsec
1350
- mc.current_server->last_seen.tv_nsec);
1351
/* total time is 10s/10,000ms.
1352
Converting to s from ms by dividing by 1,000,
1353
and ns to ms by dividing by 1,000,000. */
1354
block_time = ((retry_interval
1355
- ((intmax_t)waited_time.tv_sec * 1000))
1356
- ((intmax_t)waited_time.tv_nsec / 1000000));
1359
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1363
if(block_time <= 0){
1364
ret = start_mandos_communication(mc.current_server->ip,
1365
mc.current_server->port,
1366
mc.current_server->if_index,
1367
mc.current_server->af);
1369
avahi_simple_poll_quit(mc.simple_poll);
1372
ret = clock_gettime(CLOCK_MONOTONIC,
1373
&mc.current_server->last_seen);
1375
perror_plus("clock_gettime");
1378
mc.current_server = mc.current_server->next;
1379
block_time = 0; /* Call avahi to find new Mandos
1380
servers, but don't block */
1383
ret = avahi_simple_poll_iterate(s, (int)block_time);
1386
if (ret > 0 or errno != EINTR){
1387
return (ret != 1) ? ret : 0;
1393
/* Set effective uid to 0, return errno */
1394
error_t raise_privileges(void){
1395
error_t old_errno = errno;
1396
error_t ret_errno = 0;
1397
if(seteuid(0) == -1){
1399
perror_plus("seteuid");
1405
/* Set effective and real user ID to 0. Return errno. */
1406
error_t raise_privileges_permanently(void){
1407
error_t old_errno = errno;
1408
error_t ret_errno = raise_privileges();
1413
if(setuid(0) == -1){
1415
perror_plus("seteuid");
1421
/* Set effective user ID to unprivileged saved user ID */
1422
error_t lower_privileges(void){
1423
error_t old_errno = errno;
1424
error_t ret_errno = 0;
1425
if(seteuid(uid) == -1){
1427
perror_plus("seteuid");
1433
/* Lower privileges permanently */
1434
error_t lower_privileges_permanently(void){
1435
error_t old_errno = errno;
1436
error_t ret_errno = 0;
1437
if(setuid(uid) == -1){
1439
perror_plus("setuid");
1445
bool run_network_hooks(const char *mode, const char *interface,
1447
struct dirent **direntries;
1448
struct dirent *direntry;
1450
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1453
perror_plus("scandir");
1455
int devnull = open("/dev/null", O_RDONLY);
1456
for(int i = 0; i < numhooks; i++){
1457
direntry = direntries[i];
1458
char *fullname = NULL;
1459
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1461
perror_plus("asprintf");
1465
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1468
pid_t hook_pid = fork();
1471
/* Raise privileges */
1472
raise_privileges_permanently();
1477
perror_plus("setgid");
1479
/* Reset supplementary groups */
1481
ret = setgroups(0, NULL);
1483
perror_plus("setgroups");
1485
dup2(devnull, STDIN_FILENO);
1487
dup2(STDERR_FILENO, STDOUT_FILENO);
1488
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1490
perror_plus("setenv");
1493
ret = setenv("DEVICE", interface, 1);
1495
perror_plus("setenv");
1498
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1500
perror_plus("setenv");
1503
ret = setenv("MODE", mode, 1);
1505
perror_plus("setenv");
1509
ret = asprintf(&delaystring, "%f", delay);
1511
perror_plus("asprintf");
1514
ret = setenv("DELAY", delaystring, 1);
1517
perror_plus("setenv");
1521
if(connect_to != NULL){
1522
ret = setenv("CONNECT", connect_to, 1);
1524
perror_plus("setenv");
1528
if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1529
perror_plus("execl");
1530
_exit(EXIT_FAILURE);
1534
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1535
perror_plus("waitpid");
1539
if(WIFEXITED(status)){
1540
if(WEXITSTATUS(status) != 0){
1541
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1542
" with status %d\n", direntry->d_name,
1543
WEXITSTATUS(status));
1547
} else if(WIFSIGNALED(status)){
1548
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1549
" signal %d\n", direntry->d_name,
1554
fprintf_plus(stderr, "Warning: network hook \"%s\""
1555
" crashed\n", direntry->d_name);
1562
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1571
error_t bring_up_interface(const char *const interface,
1574
error_t old_errno = errno;
1575
error_t ret_errno = 0;
1576
int ret, ret_setflags;
1577
struct ifreq network;
1578
unsigned int if_index = if_nametoindex(interface);
1580
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1590
if(not interface_is_up(interface)){
1591
if(not get_flags(interface, &network) and debug){
1593
fprintf_plus(stderr, "Failed to get flags for interface "
1594
"\"%s\"\n", interface);
1597
network.ifr_flags |= IFF_UP;
1599
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1602
perror_plus("socket");
1614
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
1618
/* Raise priviliges */
1622
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1623
messages about the network interface to mess up the prompt */
1624
int ret_linux = klogctl(8, NULL, 5);
1625
bool restore_loglevel = true;
1626
if(ret_linux == -1){
1627
restore_loglevel = false;
1628
perror_plus("klogctl");
1630
#endif /* __linux__ */
1631
ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1634
if(restore_loglevel){
1635
ret_linux = klogctl(7, NULL, 0);
1636
if(ret_linux == -1){
1637
perror_plus("klogctl");
1640
#endif /* __linux__ */
1642
/* Lower privileges */
1645
/* Close the socket */
1646
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1648
perror_plus("close");
1651
if(ret_setflags == -1){
1653
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1658
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1662
/* Sleep checking until interface is running.
1663
Check every 0.25s, up to total time of delay */
1664
for(int i=0; i < delay * 4; i++){
1665
if(interface_is_running(interface)){
1668
struct timespec sleeptime = { .tv_nsec = 250000000 };
1669
ret = nanosleep(&sleeptime, NULL);
1670
if(ret == -1 and errno != EINTR){
1671
perror_plus("nanosleep");
1679
error_t take_down_interface(const char *const interface){
1681
error_t old_errno = errno;
1682
error_t ret_errno = 0;
1683
int ret, ret_setflags;
1684
struct ifreq network;
1685
unsigned int if_index = if_nametoindex(interface);
1687
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1691
if(interface_is_up(interface)){
1692
if(not get_flags(interface, &network) and debug){
1694
fprintf_plus(stderr, "Failed to get flags for interface "
1695
"\"%s\"\n", interface);
1698
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1700
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1703
perror_plus("socket");
1709
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
1713
/* Raise priviliges */
1716
ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1719
/* Lower privileges */
1722
/* Close the socket */
1723
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1725
perror_plus("close");
1728
if(ret_setflags == -1){
1730
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1735
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1743
int main(int argc, char *argv[]){
1744
AvahiSServiceBrowser *sb = NULL;
1749
int exitcode = EXIT_SUCCESS;
1750
char *interfaces = NULL;
1751
size_t interfaces_size = 0;
1752
char *interfaces_to_take_down = NULL;
1753
size_t interfaces_to_take_down_size = 0;
1754
char tempdir[] = "/tmp/mandosXXXXXX";
1755
bool tempdir_created = false;
1756
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1757
const char *seckey = PATHDIR "/" SECKEY;
1758
const char *pubkey = PATHDIR "/" PUBKEY;
1759
char *interfaces_hooks = NULL;
1760
size_t interfaces_hooks_size = 0;
1762
bool gnutls_initialized = false;
1763
bool gpgme_initialized = false;
1765
double retry_interval = 10; /* 10s between trying a server and
1766
retrying the same server again */
1768
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1769
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1774
/* Lower any group privileges we might have, just to be safe */
1778
perror_plus("setgid");
1781
/* Lower user privileges (temporarily) */
1785
perror_plus("seteuid");
1793
struct argp_option options[] = {
1794
{ .name = "debug", .key = 128,
1795
.doc = "Debug mode", .group = 3 },
1796
{ .name = "connect", .key = 'c',
1797
.arg = "ADDRESS:PORT",
1798
.doc = "Connect directly to a specific Mandos server",
1800
{ .name = "interface", .key = 'i',
1802
.doc = "Network interface that will be used to search for"
1805
{ .name = "seckey", .key = 's',
1807
.doc = "OpenPGP secret key file base name",
1809
{ .name = "pubkey", .key = 'p',
1811
.doc = "OpenPGP public key file base name",
1813
{ .name = "dh-bits", .key = 129,
1815
.doc = "Bit length of the prime number used in the"
1816
" Diffie-Hellman key exchange",
1818
{ .name = "priority", .key = 130,
1820
.doc = "GnuTLS priority string for the TLS handshake",
1822
{ .name = "delay", .key = 131,
1824
.doc = "Maximum delay to wait for interface startup",
1826
{ .name = "retry", .key = 132,
1828
.doc = "Retry interval used when denied by the Mandos server",
1830
{ .name = "network-hook-dir", .key = 133,
1832
.doc = "Directory where network hooks are located",
1835
* These reproduce what we would get without ARGP_NO_HELP
1837
{ .name = "help", .key = '?',
1838
.doc = "Give this help list", .group = -1 },
1839
{ .name = "usage", .key = -3,
1840
.doc = "Give a short usage message", .group = -1 },
1841
{ .name = "version", .key = 'V',
1842
.doc = "Print program version", .group = -1 },
1846
error_t parse_opt(int key, char *arg,
1847
struct argp_state *state){
1850
case 128: /* --debug */
1853
case 'c': /* --connect */
1856
case 'i': /* --interface */
1857
ret_errno = argz_add_sep(&interfaces, &interfaces_size, arg,
1860
argp_error(state, "%s", strerror(ret_errno));
1863
case 's': /* --seckey */
1866
case 'p': /* --pubkey */
1869
case 129: /* --dh-bits */
1871
tmpmax = strtoimax(arg, &tmp, 10);
1872
if(errno != 0 or tmp == arg or *tmp != '\0'
1873
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1874
argp_error(state, "Bad number of DH bits");
1876
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1878
case 130: /* --priority */
1881
case 131: /* --delay */
1883
delay = strtof(arg, &tmp);
1884
if(errno != 0 or tmp == arg or *tmp != '\0'){
1885
argp_error(state, "Bad delay");
1887
case 132: /* --retry */
1889
retry_interval = strtod(arg, &tmp);
1890
if(errno != 0 or tmp == arg or *tmp != '\0'
1891
or (retry_interval * 1000) > INT_MAX
1892
or retry_interval < 0){
1893
argp_error(state, "Bad retry interval");
1896
case 133: /* --network-hook-dir */
1900
* These reproduce what we would get without ARGP_NO_HELP
1902
case '?': /* --help */
1903
argp_state_help(state, state->out_stream,
1904
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1905
& ~(unsigned int)ARGP_HELP_EXIT_OK);
1906
case -3: /* --usage */
1907
argp_state_help(state, state->out_stream,
1908
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1909
case 'V': /* --version */
1910
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
1911
exit(argp_err_exit_status);
1914
return ARGP_ERR_UNKNOWN;
1919
struct argp argp = { .options = options, .parser = parse_opt,
1921
.doc = "Mandos client -- Get and decrypt"
1922
" passwords from a Mandos server" };
1923
ret = argp_parse(&argp, argc, argv,
1924
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
601
case AVAHI_BROWSER_FAILURE:
603
fprintf(stderr, "(Browser) %s\n",
604
avahi_strerror(avahi_server_errno(server)));
605
avahi_simple_poll_quit(simple_poll);
608
case AVAHI_BROWSER_NEW:
609
/* We ignore the returned resolver object. In the callback
610
function we free it. If the server is terminated before
611
the callback function is called the server will free
612
the resolver for us. */
614
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
616
AVAHI_PROTO_INET6, 0,
617
resolve_callback, s)))
618
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
619
avahi_strerror(avahi_server_errno(s)));
622
case AVAHI_BROWSER_REMOVE:
625
case AVAHI_BROWSER_ALL_FOR_NOW:
626
case AVAHI_BROWSER_CACHE_EXHAUSTED:
631
/* Combines file name and path and returns the malloced new
632
string. some sane checks could/should be added */
633
static const char *combinepath(const char *first, const char *second){
634
size_t f_len = strlen(first);
635
size_t s_len = strlen(second);
636
char *tmp = malloc(f_len + s_len + 2);
641
memcpy(tmp, first, f_len);
645
memcpy(tmp + f_len + 1, second, s_len);
647
tmp[f_len + 1 + s_len] = '\0';
652
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1931
perror_plus("argp_parse");
1932
exitcode = EX_OSERR;
1935
exitcode = EX_USAGE;
1941
/* Work around Debian bug #633582:
1942
<http://bugs.debian.org/633582> */
1944
/* Re-raise priviliges */
1945
if(raise_privileges() == 0){
1948
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1949
int seckey_fd = open(seckey, O_RDONLY);
1950
if(seckey_fd == -1){
1951
perror_plus("open");
1953
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1955
perror_plus("fstat");
1957
if(S_ISREG(st.st_mode)
1958
and st.st_uid == 0 and st.st_gid == 0){
1959
ret = fchown(seckey_fd, uid, gid);
1961
perror_plus("fchown");
1965
TEMP_FAILURE_RETRY(close(seckey_fd));
1969
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
1970
int pubkey_fd = open(pubkey, O_RDONLY);
1971
if(pubkey_fd == -1){
1972
perror_plus("open");
1974
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1976
perror_plus("fstat");
1978
if(S_ISREG(st.st_mode)
1979
and st.st_uid == 0 and st.st_gid == 0){
1980
ret = fchown(pubkey_fd, uid, gid);
1982
perror_plus("fchown");
1986
TEMP_FAILURE_RETRY(close(pubkey_fd));
1990
/* Lower privileges */
1994
perror_plus("seteuid");
1999
/* Remove empty interface names */
2001
char *interface = NULL;
2002
while((interface = argz_next(interfaces, interfaces_size,
2004
if(if_nametoindex(interface) == 0){
2005
if(interface[0] != '\0' and strcmp(interface, "none") != 0){
2006
fprintf_plus(stderr, "Not using nonexisting interface"
2007
" \"%s\"\n", interface);
2009
argz_delete(&interfaces, &interfaces_size, interface);
2015
/* Run network hooks */
2017
ret_errno = argz_append(&interfaces_hooks, &interfaces_hooks_size,
2018
interfaces, interfaces_size);
2021
perror_plus("argz_append");
2024
argz_stringify(interfaces_hooks, interfaces_hooks_size, (int)',');
2025
if(not run_network_hooks("start", interfaces_hooks, delay)){
2031
avahi_set_log_function(empty_log);
2034
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2035
from the signal handler */
2036
/* Initialize the pseudo-RNG for Avahi */
2037
srand((unsigned int) time(NULL));
2038
mc.simple_poll = avahi_simple_poll_new();
2039
if(mc.simple_poll == NULL){
2040
fprintf_plus(stderr,
2041
"Avahi: Failed to create simple poll object.\n");
2042
exitcode = EX_UNAVAILABLE;
2046
sigemptyset(&sigterm_action.sa_mask);
2047
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2049
perror_plus("sigaddset");
2050
exitcode = EX_OSERR;
2053
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2055
perror_plus("sigaddset");
2056
exitcode = EX_OSERR;
2059
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2061
perror_plus("sigaddset");
2062
exitcode = EX_OSERR;
2065
/* Need to check if the handler is SIG_IGN before handling:
2066
| [[info:libc:Initial Signal Actions]] |
2067
| [[info:libc:Basic Signal Handling]] |
2069
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2071
perror_plus("sigaction");
2074
if(old_sigterm_action.sa_handler != SIG_IGN){
2075
ret = sigaction(SIGINT, &sigterm_action, NULL);
2077
perror_plus("sigaction");
2078
exitcode = EX_OSERR;
2082
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2084
perror_plus("sigaction");
2087
if(old_sigterm_action.sa_handler != SIG_IGN){
2088
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2090
perror_plus("sigaction");
2091
exitcode = EX_OSERR;
2095
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2097
perror_plus("sigaction");
2100
if(old_sigterm_action.sa_handler != SIG_IGN){
2101
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2103
perror_plus("sigaction");
2104
exitcode = EX_OSERR;
2109
/* If no interfaces were specified, make a list */
2110
if(interfaces == NULL){
2111
struct dirent **direntries;
2112
/* Look for any good interfaces */
2113
ret = scandir(sys_class_net, &direntries, good_interface,
2116
/* Add all found interfaces to interfaces list */
2117
for(int i = 0; i < ret; ++i){
2118
ret_errno = argz_add(&interfaces, &interfaces_size,
2119
direntries[i]->d_name);
2121
perror_plus("argz_add");
2125
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2126
direntries[i]->d_name);
2132
fprintf_plus(stderr, "Could not find a network interface\n");
2133
exitcode = EXIT_FAILURE;
2138
/* If we only got one interface, explicitly use only that one */
2139
if(argz_count(interfaces, interfaces_size) == 1){
2141
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2144
if_index = (AvahiIfIndex)if_nametoindex(interfaces);
2147
/* Bring up interfaces which are down */
2148
if(not (argz_count(interfaces, interfaces_size) == 1
2149
and strcmp(interfaces, "none") == 0)){
2150
char *interface = NULL;
2151
while((interface = argz_next(interfaces, interfaces_size,
2153
bool interface_was_up = interface_is_up(interface);
2154
ret = bring_up_interface(interface, delay);
2155
if(not interface_was_up){
2158
perror_plus("Failed to bring up interface");
2160
ret_errno = argz_add(&interfaces_to_take_down,
2161
&interfaces_to_take_down_size,
2168
interfaces_size = 0;
2169
if(debug and (interfaces_to_take_down == NULL)){
2170
fprintf_plus(stderr, "No interfaces were brought up\n");
2178
ret = init_gnutls_global(pubkey, seckey);
2180
fprintf_plus(stderr, "init_gnutls_global failed\n");
2181
exitcode = EX_UNAVAILABLE;
2184
gnutls_initialized = true;
2191
if(mkdtemp(tempdir) == NULL){
2192
perror_plus("mkdtemp");
2195
tempdir_created = true;
2201
if(not init_gpgme(pubkey, seckey, tempdir)){
2202
fprintf_plus(stderr, "init_gpgme failed\n");
2203
exitcode = EX_UNAVAILABLE;
2206
gpgme_initialized = true;
2213
if(connect_to != NULL){
2214
/* Connect directly, do not use Zeroconf */
2215
/* (Mainly meant for debugging) */
2216
char *address = strrchr(connect_to, ':');
2218
if(address == NULL){
2219
fprintf_plus(stderr, "No colon in address\n");
2220
exitcode = EX_USAGE;
2230
tmpmax = strtoimax(address+1, &tmp, 10);
2231
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2232
or tmpmax != (uint16_t)tmpmax){
2233
fprintf_plus(stderr, "Bad port number\n");
2234
exitcode = EX_USAGE;
2242
port = (uint16_t)tmpmax;
2244
/* Colon in address indicates IPv6 */
2246
if(strchr(connect_to, ':') != NULL){
2248
/* Accept [] around IPv6 address - see RFC 5952 */
2249
if(connect_to[0] == '[' and address[-1] == ']')
2257
address = connect_to;
2263
while(not quit_now){
2264
ret = start_mandos_communication(address, port, if_index, af);
2265
if(quit_now or ret == 0){
2269
fprintf_plus(stderr, "Retrying in %d seconds\n",
2270
(int)retry_interval);
2272
sleep((int)retry_interval);
2276
exitcode = EXIT_SUCCESS;
653
2287
AvahiServerConfig config;
654
AvahiSServiceBrowser *sb = NULL;
657
int returncode = EXIT_SUCCESS;
658
const char *interface = NULL;
659
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
660
char *connect_to = NULL;
663
static struct option long_options[] = {
664
{"debug", no_argument, (int *)&debug, 1},
665
{"connect", required_argument, 0, 'C'},
666
{"interface", required_argument, 0, 'i'},
667
{"certdir", required_argument, 0, 'd'},
668
{"certkey", required_argument, 0, 'c'},
669
{"certfile", required_argument, 0, 'k'},
672
int option_index = 0;
673
ret = getopt_long (argc, argv, "i:", long_options,
703
certfile = combinepath(certdir, certfile);
704
if (certfile == NULL){
705
perror("combinepath");
709
if(interface != NULL){
710
if_index = (AvahiIfIndex) if_nametoindex(interface);
712
fprintf(stderr, "No such interface: \"%s\"\n", interface);
717
if(connect_to != NULL){
718
/* Connect directly, do not use Zeroconf */
719
/* (Mainly meant for debugging) */
720
char *address = strrchr(connect_to, ':');
722
fprintf(stderr, "No colon in address\n");
726
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
728
perror("Bad port number");
732
address = connect_to;
733
ret = start_mandos_communication(address, port, if_index);
741
certkey = combinepath(certdir, certkey);
742
if (certkey == NULL){
743
perror("combinepath");
748
avahi_set_log_function(empty_log);
751
/* Initialize the psuedo-RNG */
752
srand((unsigned int) time(NULL));
754
/* Allocate main loop object */
755
if (!(simple_poll = avahi_simple_poll_new())) {
756
fprintf(stderr, "Failed to create simple poll object.\n");
761
/* Do not publish any local records */
2288
/* Do not publish any local Zeroconf records */
762
2289
avahi_server_config_init(&config);
763
2290
config.publish_hinfo = 0;
764
2291
config.publish_addresses = 0;
765
2292
config.publish_workstation = 0;
766
2293
config.publish_domain = 0;
768
2295
/* Allocate a new server */
769
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
770
&config, NULL, NULL, &error);
772
/* Free the configuration data */
2296
mc.server = avahi_server_new(avahi_simple_poll_get
2297
(mc.simple_poll), &config, NULL,
2300
/* Free the Avahi configuration data */
773
2301
avahi_server_config_free(&config);
775
/* Check if creating the server object succeeded */
777
fprintf(stderr, "Failed to create server: %s\n",
778
avahi_strerror(error));
779
returncode = EXIT_FAILURE;
783
/* Create the service browser */
784
sb = avahi_s_service_browser_new(server, if_index,
786
"_mandos._tcp", NULL, 0,
787
browse_callback, server);
789
fprintf(stderr, "Failed to create service browser: %s\n",
790
avahi_strerror(avahi_server_errno(server)));
791
returncode = EXIT_FAILURE;
795
/* Run the main loop */
798
fprintf(stderr, "Starting avahi loop search\n");
801
avahi_simple_poll_loop(simple_poll);
806
fprintf(stderr, "%s exiting\n", argv[0]);
811
avahi_s_service_browser_free(sb);
814
avahi_server_free(server);
817
avahi_simple_poll_free(simple_poll);
2304
/* Check if creating the Avahi server object succeeded */
2305
if(mc.server == NULL){
2306
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2307
avahi_strerror(ret_errno));
2308
exitcode = EX_UNAVAILABLE;
2316
/* Create the Avahi service browser */
2317
sb = avahi_s_service_browser_new(mc.server, if_index,
2318
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2319
NULL, 0, browse_callback, NULL);
2321
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2322
avahi_strerror(avahi_server_errno(mc.server)));
2323
exitcode = EX_UNAVAILABLE;
2331
/* Run the main loop */
2334
fprintf_plus(stderr, "Starting Avahi loop search\n");
2337
ret = avahi_loop_with_timeout(mc.simple_poll,
2338
(int)(retry_interval * 1000));
2340
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2341
(ret == 0) ? "successfully" : "with error");
2347
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2350
/* Cleanup things */
2352
avahi_s_service_browser_free(sb);
2354
if(mc.server != NULL)
2355
avahi_server_free(mc.server);
2357
if(mc.simple_poll != NULL)
2358
avahi_simple_poll_free(mc.simple_poll);
2360
if(gnutls_initialized){
2361
gnutls_certificate_free_credentials(mc.cred);
2362
gnutls_global_deinit();
2363
gnutls_dh_params_deinit(mc.dh_params);
2366
if(gpgme_initialized){
2367
gpgme_release(mc.ctx);
2370
/* Cleans up the circular linked list of Mandos servers the client
2372
if(mc.current_server != NULL){
2373
mc.current_server->prev->next = NULL;
2374
while(mc.current_server != NULL){
2375
server *next = mc.current_server->next;
2376
free(mc.current_server);
2377
mc.current_server = next;
2381
/* Re-raise priviliges */
2385
/* Run network hooks */
2386
run_network_hooks("stop", interfaces_hooks, delay);
2388
/* Take down the network interfaces which were brought up */
2390
char *interface = NULL;
2391
while((interface=argz_next(interfaces_to_take_down,
2392
interfaces_to_take_down_size,
2394
ret_errno = take_down_interface(interface);
2397
perror_plus("Failed to take down interface");
2400
if(debug and (interfaces_to_take_down == NULL)){
2401
fprintf_plus(stderr, "No interfaces needed to be taken"
2406
lower_privileges_permanently();
2409
free(interfaces_to_take_down);
2410
free(interfaces_hooks);
2412
/* Removes the GPGME temp directory and all files inside */
2413
if(tempdir_created){
2414
struct dirent **direntries = NULL;
2415
struct dirent *direntry = NULL;
2416
int numentries = scandir(tempdir, &direntries, notdotentries,
2418
if (numentries > 0){
2419
for(int i = 0; i < numentries; i++){
2420
direntry = direntries[i];
2421
char *fullname = NULL;
2422
ret = asprintf(&fullname, "%s/%s", tempdir,
2425
perror_plus("asprintf");
2428
ret = remove(fullname);
2430
fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2437
/* need to clean even if 0 because man page doesn't specify */
2439
if (numentries == -1){
2440
perror_plus("scandir");
2442
ret = rmdir(tempdir);
2443
if(ret == -1 and errno != ENOENT){
2444
perror_plus("rmdir");
2449
sigemptyset(&old_sigterm_action.sa_mask);
2450
old_sigterm_action.sa_handler = SIG_DFL;
2451
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2452
&old_sigterm_action,
2455
perror_plus("sigaction");
2458
ret = raise(signal_received);
2459
} while(ret != 0 and errno == EINTR);
2461
perror_plus("raise");
2464
TEMP_FAILURE_RETRY(pause());