46
104
#include <avahi-common/malloc.h>
47
105
#include <avahi-common/error.h>
50
#include <sys/types.h> /* socket(), setsockopt(), inet_pton() */
51
#include <sys/socket.h> /* socket(), setsockopt(), struct sockaddr_in6, 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() */
68
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
70
#define CERTFILE CERT_ROOT "openpgp-client.txt"
71
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
108
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
111
init_gnutls_session(),
113
#include <gnutls/openpgp.h>
114
/* gnutls_certificate_set_openpgp_key_file(),
115
GNUTLS_OPENPGP_FMT_BASE64 */
118
#include <gpgme.h> /* All GPGME types, constants and
121
GPGME_PROTOCOL_OpenPGP,
72
124
#define BUFFER_SIZE 256
126
#define PATHDIR "/conf/conf.d/mandos"
127
#define SECKEY "seckey.txt"
128
#define PUBKEY "pubkey.txt"
129
#define HOOKDIR "/lib/mandos/network-hooks.d"
132
static const char mandos_protocol_version[] = "1";
133
const char *argp_program_version = "mandos-client " VERSION;
134
const char *argp_program_bug_address = "<mandos@recompile.se>";
135
static const char sys_class_net[] = "/sys/class/net";
136
char *connect_to = NULL;
137
const char *hookdir = HOOKDIR;
139
/* Doubly linked list that need to be circularly linked when used */
140
typedef struct server{
143
AvahiIfIndex if_index;
145
struct timespec last_seen;
150
/* Used for passing in values through the Avahi callback functions */
76
gnutls_session_t session;
152
AvahiSimplePoll *simple_poll;
77
154
gnutls_certificate_credentials_t cred;
155
unsigned int dh_bits;
78
156
gnutls_dh_params_t dh_params;
82
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
83
gpgme_data_t dh_crypto, dh_plain;
157
const char *priority;
159
server *current_server;
162
/* global context so signal handler can reach it*/
163
mandos_context mc = { .simple_poll = NULL, .server = NULL,
164
.dh_bits = 1024, .priority = "SECURE256"
165
":!CTYPE-X.509:+CTYPE-OPENPGP",
166
.current_server = NULL };
168
sig_atomic_t quit_now = 0;
169
int signal_received = 0;
171
/* Function to use when printing errors */
172
void perror_plus(const char *print_text){
173
fprintf(stderr, "Mandos plugin %s: ",
174
program_invocation_short_name);
178
__attribute__((format (gnu_printf, 2, 3)))
179
int fprintf_plus(FILE *stream, const char *format, ...){
181
va_start (ap, format);
183
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
184
program_invocation_short_name));
185
return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
189
* Make additional room in "buffer" for at least BUFFER_SIZE more
190
* bytes. "buffer_capacity" is how much is currently allocated,
191
* "buffer_length" is how much is already used.
193
size_t incbuffer(char **buffer, size_t buffer_length,
194
size_t buffer_capacity){
195
if(buffer_length + BUFFER_SIZE > buffer_capacity){
196
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
200
buffer_capacity += BUFFER_SIZE;
202
return buffer_capacity;
205
/* Add server to set of servers to retry periodically */
206
bool add_server(const char *ip, uint16_t port, AvahiIfIndex if_index,
209
server *new_server = malloc(sizeof(server));
210
if(new_server == NULL){
211
perror_plus("malloc");
214
*new_server = (server){ .ip = strdup(ip),
216
.if_index = if_index,
218
if(new_server->ip == NULL){
219
perror_plus("strdup");
222
/* Special case of first server */
223
if (mc.current_server == NULL){
224
new_server->next = new_server;
225
new_server->prev = new_server;
226
mc.current_server = new_server;
227
/* Place the new server last in the list */
229
new_server->next = mc.current_server;
230
new_server->prev = mc.current_server->prev;
231
new_server->prev->next = new_server;
232
mc.current_server->prev = new_server;
234
ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
236
perror_plus("clock_gettime");
245
static bool init_gpgme(const char *seckey, const char *pubkey,
246
const char *tempdir){
87
size_t new_packet_capacity = 0;
88
size_t new_packet_length = 0;
89
248
gpgme_engine_info_t engine_info;
252
* Helper function to insert pub and seckey to the engine keyring.
254
bool import_key(const char *filename){
257
gpgme_data_t pgp_data;
259
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
265
rc = gpgme_data_new_from_fd(&pgp_data, fd);
266
if(rc != GPG_ERR_NO_ERROR){
267
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
268
gpgme_strsource(rc), gpgme_strerror(rc));
272
rc = gpgme_op_import(mc.ctx, pgp_data);
273
if(rc != GPG_ERR_NO_ERROR){
274
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
275
gpgme_strsource(rc), gpgme_strerror(rc));
279
ret = (int)TEMP_FAILURE_RETRY(close(fd));
281
perror_plus("close");
283
gpgme_data_release(pgp_data);
288
fprintf_plus(stderr, "Initializing GPGME\n");
92
292
gpgme_check_version(NULL);
93
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
293
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
294
if(rc != GPG_ERR_NO_ERROR){
295
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
296
gpgme_strsource(rc), gpgme_strerror(rc));
95
/* Set GPGME home directory */
96
rc = gpgme_get_engine_info (&engine_info);
97
if (rc != GPG_ERR_NO_ERROR){
98
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
99
gpgme_strsource(rc), gpgme_strerror(rc));
300
/* Set GPGME home directory for the OpenPGP engine only */
301
rc = gpgme_get_engine_info(&engine_info);
302
if(rc != GPG_ERR_NO_ERROR){
303
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
304
gpgme_strsource(rc), gpgme_strerror(rc));
102
307
while(engine_info != NULL){
103
308
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
104
309
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
105
engine_info->file_name, homedir);
310
engine_info->file_name, tempdir);
108
313
engine_info = engine_info->next;
110
315
if(engine_info == NULL){
111
fprintf(stderr, "Could not set home dir to %s\n", homedir);
115
/* Create new GPGME data buffer from packet buffer */
116
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
117
if (rc != GPG_ERR_NO_ERROR){
118
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
119
gpgme_strsource(rc), gpgme_strerror(rc));
316
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
321
/* Create new GPGME "context" */
322
rc = gpgme_new(&(mc.ctx));
323
if(rc != GPG_ERR_NO_ERROR){
324
fprintf_plus(stderr, "Mandos plugin mandos-client: "
325
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
330
if(not import_key(pubkey) or not import_key(seckey)){
338
* Decrypt OpenPGP data.
339
* Returns -1 on error
341
static ssize_t pgp_packet_decrypt(const char *cryptotext,
344
gpgme_data_t dh_crypto, dh_plain;
347
size_t plaintext_capacity = 0;
348
ssize_t plaintext_length = 0;
351
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
354
/* Create new GPGME data buffer from memory cryptotext */
355
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
357
if(rc != GPG_ERR_NO_ERROR){
358
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
359
gpgme_strsource(rc), gpgme_strerror(rc));
123
363
/* Create new empty GPGME data buffer for the plaintext */
124
364
rc = gpgme_data_new(&dh_plain);
125
if (rc != GPG_ERR_NO_ERROR){
126
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
127
gpgme_strsource(rc), gpgme_strerror(rc));
131
/* Create new GPGME "context" */
132
rc = gpgme_new(&ctx);
133
if (rc != GPG_ERR_NO_ERROR){
134
fprintf(stderr, "bad gpgme_new: %s: %s\n",
135
gpgme_strsource(rc), gpgme_strerror(rc));
139
/* Decrypt data from the FILE pointer to the plaintext data buffer */
140
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
141
if (rc != GPG_ERR_NO_ERROR){
142
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
143
gpgme_strsource(rc), gpgme_strerror(rc));
147
/* gpgme_decrypt_result_t result; */
148
/* result = gpgme_op_decrypt_result(ctx); */
149
/* fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm); */
150
/* fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage); */
151
/* if(result->file_name != NULL){ */
152
/* fprintf(stderr, "File name: %s\n", result->file_name); */
154
/* gpgme_recipient_t recipient; */
155
/* recipient = result->recipients; */
157
/* while(recipient != NULL){ */
158
/* fprintf(stderr, "Public key algorithm: %s\n", */
159
/* gpgme_pubkey_algo_name(recipient->pubkey_algo)); */
160
/* fprintf(stderr, "Key ID: %s\n", recipient->keyid); */
161
/* fprintf(stderr, "Secret key available: %s\n", */
162
/* recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes"); */
163
/* recipient = recipient->next; */
167
/* Delete the GPGME FILE pointer cryptotext data buffer */
168
gpgme_data_release(dh_crypto);
365
if(rc != GPG_ERR_NO_ERROR){
366
fprintf_plus(stderr, "Mandos plugin mandos-client: "
367
"bad gpgme_data_new: %s: %s\n",
368
gpgme_strsource(rc), gpgme_strerror(rc));
369
gpgme_data_release(dh_crypto);
373
/* Decrypt data from the cryptotext data buffer to the plaintext
375
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
376
if(rc != GPG_ERR_NO_ERROR){
377
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
378
gpgme_strsource(rc), gpgme_strerror(rc));
379
plaintext_length = -1;
381
gpgme_decrypt_result_t result;
382
result = gpgme_op_decrypt_result(mc.ctx);
384
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
386
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
387
result->unsupported_algorithm);
388
fprintf_plus(stderr, "Wrong key usage: %u\n",
389
result->wrong_key_usage);
390
if(result->file_name != NULL){
391
fprintf_plus(stderr, "File name: %s\n", result->file_name);
393
gpgme_recipient_t recipient;
394
recipient = result->recipients;
395
while(recipient != NULL){
396
fprintf_plus(stderr, "Public key algorithm: %s\n",
397
gpgme_pubkey_algo_name
398
(recipient->pubkey_algo));
399
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
400
fprintf_plus(stderr, "Secret key available: %s\n",
401
recipient->status == GPG_ERR_NO_SECKEY
403
recipient = recipient->next;
411
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
170
414
/* Seek back to the beginning of the GPGME plaintext data buffer */
171
gpgme_data_seek(dh_plain, 0, SEEK_SET);
415
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
416
perror_plus("gpgme_data_seek");
417
plaintext_length = -1;
175
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
176
*new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
177
if (*new_packet == NULL){
181
new_packet_capacity += BUFFER_SIZE;
423
plaintext_capacity = incbuffer(plaintext,
424
(size_t)plaintext_length,
426
if(plaintext_capacity == 0){
427
perror_plus("incbuffer");
428
plaintext_length = -1;
184
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
432
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
185
434
/* Print the data, if any */
187
/* If password is empty, then a incorrect error will be printed */
191
perror("gpgme_data_read");
194
new_packet_length += ret;
197
/* Delete the GPGME plaintext data buffer */
440
perror_plus("gpgme_data_read");
441
plaintext_length = -1;
444
plaintext_length += ret;
448
fprintf_plus(stderr, "Decrypted password is: ");
449
for(ssize_t i = 0; i < plaintext_length; i++){
450
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
452
fprintf(stderr, "\n");
457
/* Delete the GPGME cryptotext data buffer */
458
gpgme_data_release(dh_crypto);
460
/* Delete the GPGME plaintext data buffer */
198
461
gpgme_data_release(dh_plain);
199
return new_packet_length;
462
return plaintext_length;
202
static const char * safer_gnutls_strerror (int value) {
203
const char *ret = gnutls_strerror (value);
465
static const char * safer_gnutls_strerror(int value){
466
const char *ret = gnutls_strerror(value); /* Spurious warning from
467
-Wunreachable-code */
205
469
ret = "(unknown)";
209
void debuggnutls(int level, const char* string){
210
fprintf(stderr, "%s", string);
473
/* GnuTLS log function callback */
474
static void debuggnutls(__attribute__((unused)) int level,
476
fprintf_plus(stderr, "GnuTLS: %s", string);
213
int initgnutls(encrypted_session *es){
479
static int init_gnutls_global(const char *pubkeyfilename,
480
const char *seckeyfilename){
217
if ((ret = gnutls_global_init ())
218
!= GNUTLS_E_SUCCESS) {
219
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
223
/* Uncomment to enable full debuggin on the gnutls library */
224
/* gnutls_global_set_log_level(11); */
225
/* gnutls_global_set_log_function(debuggnutls); */
228
/* openpgp credentials */
229
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
230
!= GNUTLS_E_SUCCESS) {
231
fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
484
fprintf_plus(stderr, "Initializing GnuTLS\n");
487
ret = gnutls_global_init();
488
if(ret != GNUTLS_E_SUCCESS){
489
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
490
safer_gnutls_strerror(ret));
495
/* "Use a log level over 10 to enable all debugging options."
498
gnutls_global_set_log_level(11);
499
gnutls_global_set_log_function(debuggnutls);
502
/* OpenPGP credentials */
503
ret = gnutls_certificate_allocate_credentials(&mc.cred);
504
if(ret != GNUTLS_E_SUCCESS){
505
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
506
safer_gnutls_strerror(ret));
507
gnutls_global_deinit();
512
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
513
" secret key %s as GnuTLS credentials\n",
235
518
ret = gnutls_certificate_set_openpgp_key_file
236
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
237
if (ret != GNUTLS_E_SUCCESS) {
239
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
240
ret, CERTFILE, KEYFILE);
241
fprintf(stdout, "The Error is: %s\n",
242
safer_gnutls_strerror(ret));
246
//Gnutls server initialization
247
if ((ret = gnutls_dh_params_init (&es->dh_params))
248
!= GNUTLS_E_SUCCESS) {
249
fprintf (stderr, "Error in dh parameter initialization: %s\n",
250
safer_gnutls_strerror(ret));
254
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
255
!= GNUTLS_E_SUCCESS) {
256
fprintf (stderr, "Error in prime generation: %s\n",
257
safer_gnutls_strerror(ret));
261
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
263
// Gnutls session creation
264
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
265
!= GNUTLS_E_SUCCESS){
266
fprintf(stderr, "Error in gnutls session initialization: %s\n",
267
safer_gnutls_strerror(ret));
270
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
271
!= GNUTLS_E_SUCCESS) {
272
fprintf(stderr, "Syntax error at: %s\n", err);
273
fprintf(stderr, "Gnutls error: %s\n",
274
safer_gnutls_strerror(ret));
278
if ((ret = gnutls_credentials_set
279
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
280
!= GNUTLS_E_SUCCESS) {
281
fprintf(stderr, "Error setting a credentials set: %s\n",
282
safer_gnutls_strerror(ret));
519
(mc.cred, pubkeyfilename, seckeyfilename,
520
GNUTLS_OPENPGP_FMT_BASE64);
521
if(ret != GNUTLS_E_SUCCESS){
523
"Error[%d] while reading the OpenPGP key pair ('%s',"
524
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
525
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
526
safer_gnutls_strerror(ret));
530
/* GnuTLS server initialization */
531
ret = gnutls_dh_params_init(&mc.dh_params);
532
if(ret != GNUTLS_E_SUCCESS){
533
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
534
" initialization: %s\n",
535
safer_gnutls_strerror(ret));
538
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
539
if(ret != GNUTLS_E_SUCCESS){
540
fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
541
safer_gnutls_strerror(ret));
545
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
551
gnutls_certificate_free_credentials(mc.cred);
552
gnutls_global_deinit();
553
gnutls_dh_params_deinit(mc.dh_params);
557
static int init_gnutls_session(gnutls_session_t *session){
559
/* GnuTLS session creation */
561
ret = gnutls_init(session, GNUTLS_SERVER);
565
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
566
if(ret != GNUTLS_E_SUCCESS){
568
"Error in GnuTLS session initialization: %s\n",
569
safer_gnutls_strerror(ret));
575
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
577
gnutls_deinit(*session);
580
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
581
if(ret != GNUTLS_E_SUCCESS){
582
fprintf_plus(stderr, "Syntax error at: %s\n", err);
583
fprintf_plus(stderr, "GnuTLS error: %s\n",
584
safer_gnutls_strerror(ret));
585
gnutls_deinit(*session);
591
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
594
gnutls_deinit(*session);
597
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
598
if(ret != GNUTLS_E_SUCCESS){
599
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
600
safer_gnutls_strerror(ret));
601
gnutls_deinit(*session);
286
605
/* ignore client certificate if any. */
287
gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
606
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
289
gnutls_dh_set_prime_bits (es->session, DH_BITS);
608
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
294
void empty_log(AvahiLogLevel level, const char *txt){}
613
/* Avahi log function callback */
614
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
615
__attribute__((unused)) const char *txt){}
296
int start_mandos_communcation(char *ip, uint16_t port){
298
struct sockaddr_in6 to;
299
struct in6_addr ip_addr;
300
encrypted_session es;
617
/* Called when a Mandos server is found */
618
static int start_mandos_communication(const char *ip, uint16_t port,
619
AvahiIfIndex if_index,
621
int ret, tcp_sd = -1;
624
struct sockaddr_in in;
625
struct sockaddr_in6 in6;
301
627
char *buffer = NULL;
302
char *decrypted_buffer;
628
char *decrypted_buffer = NULL;
303
629
size_t buffer_length = 0;
304
630
size_t buffer_capacity = 0;
305
ssize_t decrypted_buffer_size;
309
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
315
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5);
317
perror("setsockopt bindtodevice");
321
memset(&to,0,sizeof(to));
322
to.sin6_family = AF_INET6;
323
ret = inet_pton(AF_INET6, ip, &ip_addr);
633
gnutls_session_t session;
634
int pf; /* Protocol family */
651
fprintf_plus(stderr, "Bad address family: %d\n", af);
656
ret = init_gnutls_session(&session);
662
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
663
PRIu16 "\n", ip, port);
666
tcp_sd = socket(pf, SOCK_STREAM, 0);
669
perror_plus("socket");
679
memset(&to, 0, sizeof(to));
681
to.in6.sin6_family = (sa_family_t)af;
682
ret = inet_pton(af, ip, &to.in6.sin6_addr);
684
to.in.sin_family = (sa_family_t)af;
685
ret = inet_pton(af, ip, &to.in.sin_addr);
689
perror_plus("inet_pton");
329
fprintf(stderr, "Bad address: %s\n", ip);
332
to.sin6_port = htons(port);
333
to.sin6_scope_id = if_nametoindex("eth0");
335
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
341
ret = initgnutls (&es);
348
gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
350
ret = gnutls_handshake (es.session);
352
if (ret != GNUTLS_E_SUCCESS){
353
fprintf(stderr, "\n*** Handshake failed ***\n");
361
if (buffer_length + BUFFER_SIZE > buffer_capacity){
362
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
367
buffer_capacity += BUFFER_SIZE;
370
ret = gnutls_record_recv
371
(es.session, buffer+buffer_length, BUFFER_SIZE);
695
fprintf_plus(stderr, "Bad address: %s\n", ip);
700
to.in6.sin6_port = htons(port); /* Spurious warnings from
702
-Wunreachable-code */
704
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
705
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
707
if(if_index == AVAHI_IF_UNSPEC){
708
fprintf_plus(stderr, "An IPv6 link-local address is"
709
" incomplete without a network interface\n");
713
/* Set the network interface number as scope */
714
to.in6.sin6_scope_id = (uint32_t)if_index;
717
to.in.sin_port = htons(port); /* Spurious warnings from
719
-Wunreachable-code */
728
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
729
char interface[IF_NAMESIZE];
730
if(if_indextoname((unsigned int)if_index, interface) == NULL){
731
perror_plus("if_indextoname");
733
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIu16
734
"\n", ip, interface, port);
737
fprintf_plus(stderr, "Connection to: %s, port %" PRIu16 "\n",
740
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
741
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
744
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
747
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
751
perror_plus("inet_ntop");
753
if(strcmp(addrstr, ip) != 0){
754
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
765
ret = connect(tcp_sd, &to.in6, sizeof(to));
767
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
770
if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
772
perror_plus("connect");
783
const char *out = mandos_protocol_version;
786
size_t out_size = strlen(out);
787
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
788
out_size - written));
791
perror_plus("write");
795
written += (size_t)ret;
796
if(written < out_size){
799
if(out == mandos_protocol_version){
814
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
822
/* Spurious warning from -Wint-to-pointer-cast */
823
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
831
ret = gnutls_handshake(session);
836
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
838
if(ret != GNUTLS_E_SUCCESS){
840
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
847
/* Read OpenPGP packet that contains the wanted password */
850
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
861
buffer_capacity = incbuffer(&buffer, buffer_length,
863
if(buffer_capacity == 0){
865
perror_plus("incbuffer");
875
sret = gnutls_record_recv(session, buffer+buffer_length,
377
882
case GNUTLS_E_INTERRUPTED:
378
883
case GNUTLS_E_AGAIN:
380
885
case GNUTLS_E_REHANDSHAKE:
381
ret = gnutls_handshake (es.session);
383
fprintf(stderr, "\n*** Handshake failed ***\n");
887
ret = gnutls_handshake(session);
893
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
895
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
390
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
392
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
903
fprintf_plus(stderr, "Unknown error while reading data from"
904
" encrypted session with Mandos server\n");
905
gnutls_bye(session, GNUTLS_SHUT_RDWR);
396
buffer_length += ret;
400
if (buffer_length > 0){
401
if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) == 0){
910
buffer_length += (size_t) sret;
915
fprintf_plus(stderr, "Closing TLS session\n");
924
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
929
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
931
if(buffer_length > 0){
932
ssize_t decrypted_buffer_size;
933
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
935
if(decrypted_buffer_size >= 0){
938
while(written < (size_t) decrypted_buffer_size){
944
ret = (int)fwrite(decrypted_buffer + written, 1,
945
(size_t)decrypted_buffer_size - written,
947
if(ret == 0 and ferror(stdout)){
950
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
956
written += (size_t)ret;
962
/* Shutdown procedure */
967
free(decrypted_buffer);
970
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
976
perror_plus("close");
978
gnutls_deinit(session);
404
fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
405
free(decrypted_buffer);
412
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
415
gnutls_deinit (es.session);
416
gnutls_certificate_free_credentials (es.cred);
417
gnutls_global_deinit ();
421
static AvahiSimplePoll *simple_poll = NULL;
422
static AvahiServer *server = NULL;
424
static void resolve_callback(
425
AvahiSServiceResolver *r,
426
AVAHI_GCC_UNUSED AvahiIfIndex interface,
427
AVAHI_GCC_UNUSED AvahiProtocol protocol,
428
AvahiResolverEvent event,
432
const char *host_name,
433
const AvahiAddress *address,
435
AvahiStringList *txt,
436
AvahiLookupResultFlags flags,
437
AVAHI_GCC_UNUSED void* userdata) {
441
/* Called whenever a service has been resolved successfully or timed out */
444
case AVAHI_RESOLVER_FAILURE:
445
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_server_errno(server)));
448
case AVAHI_RESOLVER_FOUND: {
449
char ip[AVAHI_ADDRESS_STR_MAX];
450
avahi_address_snprint(ip, sizeof(ip), address);
451
int ret = start_mandos_communcation(ip, port);
459
avahi_s_service_resolver_free(r);
462
static void browse_callback(
463
AvahiSServiceBrowser *b,
464
AvahiIfIndex interface,
465
AvahiProtocol protocol,
466
AvahiBrowserEvent event,
470
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
473
AvahiServer *s = userdata;
476
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
480
case AVAHI_BROWSER_FAILURE:
482
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
483
avahi_simple_poll_quit(simple_poll);
486
case AVAHI_BROWSER_NEW:
487
/* We ignore the returned resolver object. In the callback
488
function we free it. If the server is terminated before
489
the callback function is called the server will free
490
the resolver for us. */
492
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
493
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
497
case AVAHI_BROWSER_REMOVE:
500
case AVAHI_BROWSER_ALL_FOR_NOW:
501
case AVAHI_BROWSER_CACHE_EXHAUSTED:
506
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
988
static void resolve_callback(AvahiSServiceResolver *r,
989
AvahiIfIndex interface,
991
AvahiResolverEvent event,
995
const char *host_name,
996
const AvahiAddress *address,
998
AVAHI_GCC_UNUSED AvahiStringList *txt,
999
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1001
AVAHI_GCC_UNUSED void* userdata){
1004
/* Called whenever a service has been resolved successfully or
1013
case AVAHI_RESOLVER_FAILURE:
1014
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1015
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1017
avahi_strerror(avahi_server_errno(mc.server)));
1020
case AVAHI_RESOLVER_FOUND:
1022
char ip[AVAHI_ADDRESS_STR_MAX];
1023
avahi_address_snprint(ip, sizeof(ip), address);
1025
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1026
PRIdMAX ") on port %" PRIu16 "\n", name,
1027
host_name, ip, (intmax_t)interface, port);
1029
int ret = start_mandos_communication(ip, port, interface,
1030
avahi_proto_to_af(proto));
1032
avahi_simple_poll_quit(mc.simple_poll);
1034
if(not add_server(ip, port, interface,
1035
avahi_proto_to_af(proto))){
1036
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1042
avahi_s_service_resolver_free(r);
1045
static void browse_callback(AvahiSServiceBrowser *b,
1046
AvahiIfIndex interface,
1047
AvahiProtocol protocol,
1048
AvahiBrowserEvent event,
1052
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1054
AVAHI_GCC_UNUSED void* userdata){
1057
/* Called whenever a new services becomes available on the LAN or
1058
is removed from the LAN */
1066
case AVAHI_BROWSER_FAILURE:
1068
fprintf_plus(stderr, "(Avahi browser) %s\n",
1069
avahi_strerror(avahi_server_errno(mc.server)));
1070
avahi_simple_poll_quit(mc.simple_poll);
1073
case AVAHI_BROWSER_NEW:
1074
/* We ignore the returned Avahi resolver object. In the callback
1075
function we free it. If the Avahi server is terminated before
1076
the callback function is called the Avahi server will free the
1079
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1080
name, type, domain, protocol, 0,
1081
resolve_callback, NULL) == NULL)
1082
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1084
avahi_strerror(avahi_server_errno(mc.server)));
1087
case AVAHI_BROWSER_REMOVE:
1090
case AVAHI_BROWSER_ALL_FOR_NOW:
1091
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1093
fprintf_plus(stderr, "No Mandos server found, still"
1100
/* Signal handler that stops main loop after SIGTERM */
1101
static void handle_sigterm(int sig){
1106
signal_received = sig;
1107
int old_errno = errno;
1108
/* set main loop to exit */
1109
if(mc.simple_poll != NULL){
1110
avahi_simple_poll_quit(mc.simple_poll);
1115
bool get_flags(const char *ifname, struct ifreq *ifr){
1118
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1120
perror_plus("socket");
1123
strcpy(ifr->ifr_name, ifname);
1124
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1127
perror_plus("ioctl SIOCGIFFLAGS");
1134
bool good_flags(const char *ifname, const struct ifreq *ifr){
1136
/* Reject the loopback device */
1137
if(ifr->ifr_flags & IFF_LOOPBACK){
1139
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1144
/* Accept point-to-point devices only if connect_to is specified */
1145
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1147
fprintf_plus(stderr, "Accepting point-to-point interface"
1148
" \"%s\"\n", ifname);
1152
/* Otherwise, reject non-broadcast-capable devices */
1153
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1155
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1156
" \"%s\"\n", ifname);
1160
/* Reject non-ARP interfaces (including dummy interfaces) */
1161
if(ifr->ifr_flags & IFF_NOARP){
1163
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1169
/* Accept this device */
1171
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1177
* This function determines if a directory entry in /sys/class/net
1178
* corresponds to an acceptable network device.
1179
* (This function is passed to scandir(3) as a filter function.)
1181
int good_interface(const struct dirent *if_entry){
1182
if(if_entry->d_name[0] == '.'){
1187
if(not get_flags(if_entry->d_name, &ifr)){
1189
fprintf_plus(stderr, "Failed to get flags for interface "
1190
"\"%s\"\n", if_entry->d_name);
1195
if(not good_flags(if_entry->d_name, &ifr)){
1202
* This function determines if a directory entry in /sys/class/net
1203
* corresponds to an acceptable network device which is up.
1204
* (This function is passed to scandir(3) as a filter function.)
1206
int up_interface(const struct dirent *if_entry){
1207
if(if_entry->d_name[0] == '.'){
1212
if(not get_flags(if_entry->d_name, &ifr)){
1214
fprintf_plus(stderr, "Failed to get flags for interface "
1215
"\"%s\"\n", if_entry->d_name);
1220
/* Reject down interfaces */
1221
if(not (ifr.ifr_flags & IFF_UP)){
1223
fprintf_plus(stderr, "Rejecting down interface \"%s\"\n",
1229
/* Reject non-running interfaces */
1230
if(not (ifr.ifr_flags & IFF_RUNNING)){
1232
fprintf_plus(stderr, "Rejecting non-running interface \"%s\"\n",
1238
if(not good_flags(if_entry->d_name, &ifr)){
1244
int notdotentries(const struct dirent *direntry){
1245
/* Skip "." and ".." */
1246
if(direntry->d_name[0] == '.'
1247
and (direntry->d_name[1] == '\0'
1248
or (direntry->d_name[1] == '.'
1249
and direntry->d_name[2] == '\0'))){
1255
/* Is this directory entry a runnable program? */
1256
int runnable_hook(const struct dirent *direntry){
1261
if((direntry->d_name)[0] == '\0'){
1266
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1267
"abcdefghijklmnopqrstuvwxyz"
1270
if((direntry->d_name)[sret] != '\0'){
1271
/* Contains non-allowed characters */
1273
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1279
char *fullname = NULL;
1280
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1282
perror_plus("asprintf");
1286
ret = stat(fullname, &st);
1289
perror_plus("Could not stat hook");
1293
if(not (S_ISREG(st.st_mode))){
1294
/* Not a regular file */
1296
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1301
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1302
/* Not executable */
1304
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1310
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1316
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
1318
struct timespec now;
1319
struct timespec waited_time;
1320
intmax_t block_time;
1323
if(mc.current_server == NULL){
1325
fprintf_plus(stderr, "Wait until first server is found."
1328
ret = avahi_simple_poll_iterate(s, -1);
1331
fprintf_plus(stderr, "Check current_server if we should run"
1334
/* the current time */
1335
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1337
perror_plus("clock_gettime");
1340
/* Calculating in ms how long time between now and server
1341
who we visted longest time ago. Now - last seen. */
1342
waited_time.tv_sec = (now.tv_sec
1343
- mc.current_server->last_seen.tv_sec);
1344
waited_time.tv_nsec = (now.tv_nsec
1345
- mc.current_server->last_seen.tv_nsec);
1346
/* total time is 10s/10,000ms.
1347
Converting to s from ms by dividing by 1,000,
1348
and ns to ms by dividing by 1,000,000. */
1349
block_time = ((retry_interval
1350
- ((intmax_t)waited_time.tv_sec * 1000))
1351
- ((intmax_t)waited_time.tv_nsec / 1000000));
1354
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1358
if(block_time <= 0){
1359
ret = start_mandos_communication(mc.current_server->ip,
1360
mc.current_server->port,
1361
mc.current_server->if_index,
1362
mc.current_server->af);
1364
avahi_simple_poll_quit(mc.simple_poll);
1367
ret = clock_gettime(CLOCK_MONOTONIC,
1368
&mc.current_server->last_seen);
1370
perror_plus("clock_gettime");
1373
mc.current_server = mc.current_server->next;
1374
block_time = 0; /* Call avahi to find new Mandos
1375
servers, but don't block */
1378
ret = avahi_simple_poll_iterate(s, (int)block_time);
1381
if (ret > 0 or errno != EINTR){
1382
return (ret != 1) ? ret : 0;
1388
bool run_network_hooks(const char *mode, const char *interface,
1390
struct dirent **direntries;
1391
struct dirent *direntry;
1393
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1396
perror_plus("scandir");
1398
int devnull = open("/dev/null", O_RDONLY);
1399
for(int i = 0; i < numhooks; i++){
1400
direntry = direntries[i];
1401
char *fullname = NULL;
1402
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1404
perror_plus("asprintf");
1408
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1411
pid_t hook_pid = fork();
1414
/* Raise privileges */
1418
perror_plus("seteuid");
1420
/* Raise privileges even more */
1424
perror_plus("setuid");
1430
perror_plus("setgid");
1432
/* Reset supplementary groups */
1434
ret = setgroups(0, NULL);
1436
perror_plus("setgroups");
1438
dup2(devnull, STDIN_FILENO);
1440
dup2(STDERR_FILENO, STDOUT_FILENO);
1441
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1443
perror_plus("setenv");
1446
ret = setenv("DEVICE", interface, 1);
1448
perror_plus("setenv");
1451
ret = setenv("VERBOSE", debug ? "1" : "0", 1);
1453
perror_plus("setenv");
1456
ret = setenv("MODE", mode, 1);
1458
perror_plus("setenv");
1462
ret = asprintf(&delaystring, "%f", delay);
1464
perror_plus("asprintf");
1467
ret = setenv("DELAY", delaystring, 1);
1470
perror_plus("setenv");
1474
if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1475
perror_plus("execl");
1476
_exit(EXIT_FAILURE);
1480
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1481
perror_plus("waitpid");
1485
if(WIFEXITED(status)){
1486
if(WEXITSTATUS(status) != 0){
1487
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1488
" with status %d\n", direntry->d_name,
1489
WEXITSTATUS(status));
1493
} else if(WIFSIGNALED(status)){
1494
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1495
" signal %d\n", direntry->d_name,
1500
fprintf_plus(stderr, "Warning: network hook \"%s\""
1501
" crashed\n", direntry->d_name);
1508
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1517
int main(int argc, char *argv[]){
1518
AvahiSServiceBrowser *sb = NULL;
1523
int exitcode = EXIT_SUCCESS;
1524
const char *interface = "";
1525
struct ifreq network;
1527
bool take_down_interface = false;
1530
char tempdir[] = "/tmp/mandosXXXXXX";
1531
bool tempdir_created = false;
1532
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1533
const char *seckey = PATHDIR "/" SECKEY;
1534
const char *pubkey = PATHDIR "/" PUBKEY;
1536
bool gnutls_initialized = false;
1537
bool gpgme_initialized = false;
1539
double retry_interval = 10; /* 10s between trying a server and
1540
retrying the same server again */
1542
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1543
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1548
/* Lower any group privileges we might have, just to be safe */
1552
perror_plus("setgid");
1555
/* Lower user privileges (temporarily) */
1559
perror_plus("seteuid");
1567
struct argp_option options[] = {
1568
{ .name = "debug", .key = 128,
1569
.doc = "Debug mode", .group = 3 },
1570
{ .name = "connect", .key = 'c',
1571
.arg = "ADDRESS:PORT",
1572
.doc = "Connect directly to a specific Mandos server",
1574
{ .name = "interface", .key = 'i',
1576
.doc = "Network interface that will be used to search for"
1579
{ .name = "seckey", .key = 's',
1581
.doc = "OpenPGP secret key file base name",
1583
{ .name = "pubkey", .key = 'p',
1585
.doc = "OpenPGP public key file base name",
1587
{ .name = "dh-bits", .key = 129,
1589
.doc = "Bit length of the prime number used in the"
1590
" Diffie-Hellman key exchange",
1592
{ .name = "priority", .key = 130,
1594
.doc = "GnuTLS priority string for the TLS handshake",
1596
{ .name = "delay", .key = 131,
1598
.doc = "Maximum delay to wait for interface startup",
1600
{ .name = "retry", .key = 132,
1602
.doc = "Retry interval used when denied by the mandos server",
1604
{ .name = "network-hook-dir", .key = 133,
1606
.doc = "Directory where network hooks are located",
1609
* These reproduce what we would get without ARGP_NO_HELP
1611
{ .name = "help", .key = '?',
1612
.doc = "Give this help list", .group = -1 },
1613
{ .name = "usage", .key = -3,
1614
.doc = "Give a short usage message", .group = -1 },
1615
{ .name = "version", .key = 'V',
1616
.doc = "Print program version", .group = -1 },
1620
error_t parse_opt(int key, char *arg,
1621
struct argp_state *state){
1624
case 128: /* --debug */
1627
case 'c': /* --connect */
1630
case 'i': /* --interface */
1633
case 's': /* --seckey */
1636
case 'p': /* --pubkey */
1639
case 129: /* --dh-bits */
1641
tmpmax = strtoimax(arg, &tmp, 10);
1642
if(errno != 0 or tmp == arg or *tmp != '\0'
1643
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1644
argp_error(state, "Bad number of DH bits");
1646
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1648
case 130: /* --priority */
1651
case 131: /* --delay */
1653
delay = strtof(arg, &tmp);
1654
if(errno != 0 or tmp == arg or *tmp != '\0'){
1655
argp_error(state, "Bad delay");
1657
case 132: /* --retry */
1659
retry_interval = strtod(arg, &tmp);
1660
if(errno != 0 or tmp == arg or *tmp != '\0'
1661
or (retry_interval * 1000) > INT_MAX
1662
or retry_interval < 0){
1663
argp_error(state, "Bad retry interval");
1666
case 133: /* --network-hook-dir */
1670
* These reproduce what we would get without ARGP_NO_HELP
1672
case '?': /* --help */
1673
argp_state_help(state, state->out_stream,
1674
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1675
& ~(unsigned int)ARGP_HELP_EXIT_OK);
1676
case -3: /* --usage */
1677
argp_state_help(state, state->out_stream,
1678
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1679
case 'V': /* --version */
1680
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
1681
exit(argp_err_exit_status);
1684
return ARGP_ERR_UNKNOWN;
1689
struct argp argp = { .options = options, .parser = parse_opt,
1691
.doc = "Mandos client -- Get and decrypt"
1692
" passwords from a Mandos server" };
1693
ret = argp_parse(&argp, argc, argv,
1694
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
1701
perror_plus("argp_parse");
1702
exitcode = EX_OSERR;
1705
exitcode = EX_USAGE;
1711
/* Work around Debian bug #633582:
1712
<http://bugs.debian.org/633582> */
1714
/* Re-raise priviliges */
1718
perror_plus("seteuid");
1722
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1723
int seckey_fd = open(seckey, O_RDONLY);
1724
if(seckey_fd == -1){
1725
perror_plus("open");
1727
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1729
perror_plus("fstat");
1731
if(S_ISREG(st.st_mode)
1732
and st.st_uid == 0 and st.st_gid == 0){
1733
ret = fchown(seckey_fd, uid, gid);
1735
perror_plus("fchown");
1739
TEMP_FAILURE_RETRY(close(seckey_fd));
1743
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
1744
int pubkey_fd = open(pubkey, O_RDONLY);
1745
if(pubkey_fd == -1){
1746
perror_plus("open");
1748
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1750
perror_plus("fstat");
1752
if(S_ISREG(st.st_mode)
1753
and st.st_uid == 0 and st.st_gid == 0){
1754
ret = fchown(pubkey_fd, uid, gid);
1756
perror_plus("fchown");
1760
TEMP_FAILURE_RETRY(close(pubkey_fd));
1764
/* Lower privileges */
1768
perror_plus("seteuid");
1773
/* Run network hooks */
1774
if(not run_network_hooks("start", interface, delay)){
1779
avahi_set_log_function(empty_log);
1782
if(interface[0] == '\0'){
1783
struct dirent **direntries;
1784
/* First look for interfaces that are up */
1785
ret = scandir(sys_class_net, &direntries, up_interface,
1788
/* No up interfaces, look for any good interfaces */
1790
ret = scandir(sys_class_net, &direntries, good_interface,
1794
/* Pick the first interface returned */
1795
interface = strdup(direntries[0]->d_name);
1797
fprintf_plus(stderr, "Using interface \"%s\"\n", interface);
1799
if(interface == NULL){
1800
perror_plus("malloc");
1802
exitcode = EXIT_FAILURE;
1808
fprintf_plus(stderr, "Could not find a network interface\n");
1809
exitcode = EXIT_FAILURE;
1814
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1815
from the signal handler */
1816
/* Initialize the pseudo-RNG for Avahi */
1817
srand((unsigned int) time(NULL));
1818
mc.simple_poll = avahi_simple_poll_new();
1819
if(mc.simple_poll == NULL){
1820
fprintf_plus(stderr,
1821
"Avahi: Failed to create simple poll object.\n");
1822
exitcode = EX_UNAVAILABLE;
1826
sigemptyset(&sigterm_action.sa_mask);
1827
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1829
perror_plus("sigaddset");
1830
exitcode = EX_OSERR;
1833
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1835
perror_plus("sigaddset");
1836
exitcode = EX_OSERR;
1839
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1841
perror_plus("sigaddset");
1842
exitcode = EX_OSERR;
1845
/* Need to check if the handler is SIG_IGN before handling:
1846
| [[info:libc:Initial Signal Actions]] |
1847
| [[info:libc:Basic Signal Handling]] |
1849
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1851
perror_plus("sigaction");
1854
if(old_sigterm_action.sa_handler != SIG_IGN){
1855
ret = sigaction(SIGINT, &sigterm_action, NULL);
1857
perror_plus("sigaction");
1858
exitcode = EX_OSERR;
1862
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1864
perror_plus("sigaction");
1867
if(old_sigterm_action.sa_handler != SIG_IGN){
1868
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1870
perror_plus("sigaction");
1871
exitcode = EX_OSERR;
1875
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1877
perror_plus("sigaction");
1880
if(old_sigterm_action.sa_handler != SIG_IGN){
1881
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1883
perror_plus("sigaction");
1884
exitcode = EX_OSERR;
1889
/* If the interface is down, bring it up */
1890
if(strcmp(interface, "none") != 0){
1891
if_index = (AvahiIfIndex) if_nametoindex(interface);
1893
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1894
exitcode = EX_UNAVAILABLE;
1902
/* Re-raise priviliges */
1906
perror_plus("seteuid");
1910
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1911
messages about the network interface to mess up the prompt */
1912
ret = klogctl(8, NULL, 5);
1913
bool restore_loglevel = true;
1915
restore_loglevel = false;
1916
perror_plus("klogctl");
1918
#endif /* __linux__ */
1920
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1922
perror_plus("socket");
1923
exitcode = EX_OSERR;
1925
if(restore_loglevel){
1926
ret = klogctl(7, NULL, 0);
1928
perror_plus("klogctl");
1931
#endif /* __linux__ */
1932
/* Lower privileges */
1936
perror_plus("seteuid");
1940
strcpy(network.ifr_name, interface);
1941
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1943
perror_plus("ioctl SIOCGIFFLAGS");
1945
if(restore_loglevel){
1946
ret = klogctl(7, NULL, 0);
1948
perror_plus("klogctl");
1951
#endif /* __linux__ */
1952
exitcode = EX_OSERR;
1953
/* Lower privileges */
1957
perror_plus("seteuid");
1961
if((network.ifr_flags & IFF_UP) == 0){
1962
network.ifr_flags |= IFF_UP;
1963
take_down_interface = true;
1964
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1966
take_down_interface = false;
1967
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1968
exitcode = EX_OSERR;
1970
if(restore_loglevel){
1971
ret = klogctl(7, NULL, 0);
1973
perror_plus("klogctl");
1976
#endif /* __linux__ */
1977
/* Lower privileges */
1981
perror_plus("seteuid");
1986
/* Sleep checking until interface is running.
1987
Check every 0.25s, up to total time of delay */
1988
for(int i=0; i < delay * 4; i++){
1989
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1991
perror_plus("ioctl SIOCGIFFLAGS");
1992
} else if(network.ifr_flags & IFF_RUNNING){
1995
struct timespec sleeptime = { .tv_nsec = 250000000 };
1996
ret = nanosleep(&sleeptime, NULL);
1997
if(ret == -1 and errno != EINTR){
1998
perror_plus("nanosleep");
2001
if(not take_down_interface){
2002
/* We won't need the socket anymore */
2003
ret = (int)TEMP_FAILURE_RETRY(close(sd));
2005
perror_plus("close");
2009
if(restore_loglevel){
2010
/* Restores kernel loglevel to default */
2011
ret = klogctl(7, NULL, 0);
2013
perror_plus("klogctl");
2016
#endif /* __linux__ */
2017
/* Lower privileges */
2019
/* Lower privileges */
2022
perror_plus("seteuid");
2030
ret = init_gnutls_global(pubkey, seckey);
2032
fprintf_plus(stderr, "init_gnutls_global failed\n");
2033
exitcode = EX_UNAVAILABLE;
2036
gnutls_initialized = true;
2043
if(mkdtemp(tempdir) == NULL){
2044
perror_plus("mkdtemp");
2047
tempdir_created = true;
2053
if(not init_gpgme(pubkey, seckey, tempdir)){
2054
fprintf_plus(stderr, "init_gpgme failed\n");
2055
exitcode = EX_UNAVAILABLE;
2058
gpgme_initialized = true;
2065
if(connect_to != NULL){
2066
/* Connect directly, do not use Zeroconf */
2067
/* (Mainly meant for debugging) */
2068
char *address = strrchr(connect_to, ':');
2069
if(address == NULL){
2070
fprintf_plus(stderr, "No colon in address\n");
2071
exitcode = EX_USAGE;
2081
tmpmax = strtoimax(address+1, &tmp, 10);
2082
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2083
or tmpmax != (uint16_t)tmpmax){
2084
fprintf_plus(stderr, "Bad port number\n");
2085
exitcode = EX_USAGE;
2093
port = (uint16_t)tmpmax;
2095
/* Colon in address indicates IPv6 */
2097
if(strchr(connect_to, ':') != NULL){
2099
/* Accept [] around IPv6 address - see RFC 5952 */
2100
if(connect_to[0] == '[' and address[-1] == ']')
2108
address = connect_to;
2114
while(not quit_now){
2115
ret = start_mandos_communication(address, port, if_index, af);
2116
if(quit_now or ret == 0){
2120
fprintf_plus(stderr, "Retrying in %d seconds\n",
2121
(int)retry_interval);
2123
sleep((int)retry_interval);
2127
exitcode = EXIT_SUCCESS;
507
2138
AvahiServerConfig config;
508
AvahiSServiceBrowser *sb = NULL;
512
avahi_set_log_function(empty_log);
514
/* Initialize the psuedo-RNG */
517
/* Allocate main loop object */
518
if (!(simple_poll = avahi_simple_poll_new())) {
519
fprintf(stderr, "Failed to create simple poll object.\n");
523
/* Do not publish any local records */
2139
/* Do not publish any local Zeroconf records */
524
2140
avahi_server_config_init(&config);
525
2141
config.publish_hinfo = 0;
526
2142
config.publish_addresses = 0;
527
2143
config.publish_workstation = 0;
528
2144
config.publish_domain = 0;
530
/* /\* Set a unicast DNS server for wide area DNS-SD *\/ */
531
/* avahi_address_parse("193.11.177.11", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); */
532
/* config.n_wide_area_servers = 1; */
533
/* config.enable_wide_area = 1; */
535
2146
/* Allocate a new server */
536
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
538
/* Free the configuration data */
2147
mc.server = avahi_server_new(avahi_simple_poll_get
2148
(mc.simple_poll), &config, NULL,
2151
/* Free the Avahi configuration data */
539
2152
avahi_server_config_free(&config);
541
/* Check wether creating the server object succeeded */
543
fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
547
/* Create the service browser */
548
if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
549
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
553
/* Run the main loop */
554
avahi_simple_poll_loop(simple_poll);
562
avahi_s_service_browser_free(sb);
565
avahi_server_free(server);
568
avahi_simple_poll_free(simple_poll);
2155
/* Check if creating the Avahi server object succeeded */
2156
if(mc.server == NULL){
2157
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2158
avahi_strerror(error));
2159
exitcode = EX_UNAVAILABLE;
2167
/* Create the Avahi service browser */
2168
sb = avahi_s_service_browser_new(mc.server, if_index,
2169
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2170
NULL, 0, browse_callback, NULL);
2172
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2173
avahi_strerror(avahi_server_errno(mc.server)));
2174
exitcode = EX_UNAVAILABLE;
2182
/* Run the main loop */
2185
fprintf_plus(stderr, "Starting Avahi loop search\n");
2188
ret = avahi_loop_with_timeout(mc.simple_poll,
2189
(int)(retry_interval * 1000));
2191
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2192
(ret == 0) ? "successfully" : "with error");
2198
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2201
/* Cleanup things */
2203
avahi_s_service_browser_free(sb);
2205
if(mc.server != NULL)
2206
avahi_server_free(mc.server);
2208
if(mc.simple_poll != NULL)
2209
avahi_simple_poll_free(mc.simple_poll);
2211
if(gnutls_initialized){
2212
gnutls_certificate_free_credentials(mc.cred);
2213
gnutls_global_deinit();
2214
gnutls_dh_params_deinit(mc.dh_params);
2217
if(gpgme_initialized){
2218
gpgme_release(mc.ctx);
2221
/* Cleans up the circular linked list of Mandos servers the client
2223
if(mc.current_server != NULL){
2224
mc.current_server->prev->next = NULL;
2225
while(mc.current_server != NULL){
2226
server *next = mc.current_server->next;
2227
free(mc.current_server);
2228
mc.current_server = next;
2232
/* Run network hooks */
2233
run_network_hooks("stop", interface, delay);
2235
/* Re-raise priviliges */
2240
perror_plus("seteuid");
2243
/* Take down the network interface */
2244
if(take_down_interface and geteuid() == 0){
2245
ret = ioctl(sd, SIOCGIFFLAGS, &network);
2247
perror_plus("ioctl SIOCGIFFLAGS");
2248
} else if(network.ifr_flags & IFF_UP){
2249
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2250
ret = ioctl(sd, SIOCSIFFLAGS, &network);
2252
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2255
ret = (int)TEMP_FAILURE_RETRY(close(sd));
2257
perror_plus("close");
2261
/* Lower privileges permanently */
2265
perror_plus("setuid");
2268
/* Removes the GPGME temp directory and all files inside */
2269
if(tempdir_created){
2270
struct dirent **direntries = NULL;
2271
struct dirent *direntry = NULL;
2272
int numentries = scandir(tempdir, &direntries, notdotentries,
2274
if (numentries > 0){
2275
for(int i = 0; i < numentries; i++){
2276
direntry = direntries[i];
2277
char *fullname = NULL;
2278
ret = asprintf(&fullname, "%s/%s", tempdir,
2281
perror_plus("asprintf");
2284
ret = remove(fullname);
2286
fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2293
/* need to clean even if 0 because man page doesn't specify */
2295
if (numentries == -1){
2296
perror_plus("scandir");
2298
ret = rmdir(tempdir);
2299
if(ret == -1 and errno != ENOENT){
2300
perror_plus("rmdir");
2305
sigemptyset(&old_sigterm_action.sa_mask);
2306
old_sigterm_action.sa_handler = SIG_DFL;
2307
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2308
&old_sigterm_action,
2311
perror_plus("sigaction");
2314
ret = raise(signal_received);
2315
} while(ret != 0 and errno == EINTR);
2317
perror_plus("raise");
2320
TEMP_FAILURE_RETRY(pause());