47
107
#include <avahi-common/malloc.h>
48
108
#include <avahi-common/error.h>
51
#include <sys/types.h> /* socket(), inet_pton() */
52
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
53
struct in6_addr, inet_pton() */
54
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
55
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
57
#include <unistd.h> /* close() */
58
#include <netinet/in.h>
59
#include <stdbool.h> /* true */
60
#include <string.h> /* memset */
61
#include <arpa/inet.h> /* inet_pton() */
62
#include <iso646.h> /* not */
65
#include <errno.h> /* perror() */
72
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
74
#define CERTFILE CERT_ROOT "openpgp-client.txt"
75
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
111
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
114
init_gnutls_session(),
116
#include <gnutls/openpgp.h>
117
/* gnutls_certificate_set_openpgp_key_file(),
118
GNUTLS_OPENPGP_FMT_BASE64 */
121
#include <gpgme.h> /* All GPGME types, constants and
124
GPGME_PROTOCOL_OpenPGP,
76
127
#define BUFFER_SIZE 256
129
#define PATHDIR "/conf/conf.d/mandos"
130
#define SECKEY "seckey.txt"
131
#define PUBKEY "pubkey.txt"
132
#define HOOKDIR "/lib/mandos/network-hooks.d"
79
134
bool debug = false;
135
static const char mandos_protocol_version[] = "1";
136
const char *argp_program_version = "mandos-client " VERSION;
137
const char *argp_program_bug_address = "<mandos@recompile.se>";
138
static const char sys_class_net[] = "/sys/class/net";
139
char *connect_to = NULL;
140
const char *hookdir = HOOKDIR;
144
/* Doubly linked list that need to be circularly linked when used */
145
typedef struct server{
148
AvahiIfIndex if_index;
150
struct timespec last_seen;
155
/* Used for passing in values through the Avahi callback functions */
82
gnutls_session_t session;
83
158
gnutls_certificate_credentials_t cred;
159
unsigned int dh_bits;
84
160
gnutls_dh_params_t dh_params;
88
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
char **new_packet, const char *homedir){
90
gpgme_data_t dh_crypto, dh_plain;
161
const char *priority;
163
server *current_server;
165
size_t interfaces_size;
168
/* global so signal handler can reach it*/
169
AvahiSimplePoll *simple_poll;
171
sig_atomic_t quit_now = 0;
172
int signal_received = 0;
174
/* Function to use when printing errors */
175
void perror_plus(const char *print_text){
177
fprintf(stderr, "Mandos plugin %s: ",
178
program_invocation_short_name);
183
__attribute__((format (gnu_printf, 2, 3)))
184
int fprintf_plus(FILE *stream, const char *format, ...){
186
va_start (ap, format);
188
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
189
program_invocation_short_name));
190
return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
194
* Make additional room in "buffer" for at least BUFFER_SIZE more
195
* bytes. "buffer_capacity" is how much is currently allocated,
196
* "buffer_length" is how much is already used.
198
size_t incbuffer(char **buffer, size_t buffer_length,
199
size_t buffer_capacity){
200
if(buffer_length + BUFFER_SIZE > buffer_capacity){
201
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
205
buffer_capacity += BUFFER_SIZE;
207
return buffer_capacity;
210
/* Add server to set of servers to retry periodically */
211
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
212
int af, server **current_server){
214
server *new_server = malloc(sizeof(server));
215
if(new_server == NULL){
216
perror_plus("malloc");
219
*new_server = (server){ .ip = strdup(ip),
221
.if_index = if_index,
223
if(new_server->ip == NULL){
224
perror_plus("strdup");
227
/* Special case of first server */
228
if(*current_server == NULL){
229
new_server->next = new_server;
230
new_server->prev = new_server;
231
*current_server = new_server;
232
/* Place the new server last in the list */
234
new_server->next = *current_server;
235
new_server->prev = (*current_server)->prev;
236
new_server->prev->next = new_server;
237
(*current_server)->prev = new_server;
239
ret = clock_gettime(CLOCK_MONOTONIC, &(*current_server)->last_seen);
241
perror_plus("clock_gettime");
250
static bool init_gpgme(const char *seckey, const char *pubkey,
251
const char *tempdir, mandos_context *mc){
94
ssize_t new_packet_capacity = 0;
95
ssize_t new_packet_length = 0;
96
253
gpgme_engine_info_t engine_info;
99
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
256
* Helper function to insert pub and seckey to the engine keyring.
258
bool import_key(const char *filename){
261
gpgme_data_t pgp_data;
263
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
269
rc = gpgme_data_new_from_fd(&pgp_data, fd);
270
if(rc != GPG_ERR_NO_ERROR){
271
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
272
gpgme_strsource(rc), gpgme_strerror(rc));
276
rc = gpgme_op_import(mc->ctx, pgp_data);
277
if(rc != GPG_ERR_NO_ERROR){
278
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
279
gpgme_strsource(rc), gpgme_strerror(rc));
283
ret = (int)TEMP_FAILURE_RETRY(close(fd));
285
perror_plus("close");
287
gpgme_data_release(pgp_data);
292
fprintf_plus(stderr, "Initializing GPGME\n");
103
296
gpgme_check_version(NULL);
104
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
297
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
298
if(rc != GPG_ERR_NO_ERROR){
299
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
300
gpgme_strsource(rc), gpgme_strerror(rc));
106
/* Set GPGME home directory */
107
rc = gpgme_get_engine_info (&engine_info);
108
if (rc != GPG_ERR_NO_ERROR){
109
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
110
gpgme_strsource(rc), gpgme_strerror(rc));
304
/* Set GPGME home directory for the OpenPGP engine only */
305
rc = gpgme_get_engine_info(&engine_info);
306
if(rc != GPG_ERR_NO_ERROR){
307
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
308
gpgme_strsource(rc), gpgme_strerror(rc));
113
311
while(engine_info != NULL){
114
312
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
115
313
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
116
engine_info->file_name, homedir);
314
engine_info->file_name, tempdir);
119
317
engine_info = engine_info->next;
121
319
if(engine_info == NULL){
122
fprintf(stderr, "Could not set home dir to %s\n", homedir);
126
/* Create new GPGME data buffer from packet buffer */
127
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
128
if (rc != GPG_ERR_NO_ERROR){
129
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
130
gpgme_strsource(rc), gpgme_strerror(rc));
320
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
325
/* Create new GPGME "context" */
326
rc = gpgme_new(&(mc->ctx));
327
if(rc != GPG_ERR_NO_ERROR){
328
fprintf_plus(stderr, "Mandos plugin mandos-client: "
329
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
334
if(not import_key(pubkey) or not import_key(seckey)){
342
* Decrypt OpenPGP data.
343
* Returns -1 on error
345
static ssize_t pgp_packet_decrypt(const char *cryptotext,
349
gpgme_data_t dh_crypto, dh_plain;
352
size_t plaintext_capacity = 0;
353
ssize_t plaintext_length = 0;
356
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
359
/* Create new GPGME data buffer from memory cryptotext */
360
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
362
if(rc != GPG_ERR_NO_ERROR){
363
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
364
gpgme_strsource(rc), gpgme_strerror(rc));
134
368
/* Create new empty GPGME data buffer for the plaintext */
135
369
rc = gpgme_data_new(&dh_plain);
136
if (rc != GPG_ERR_NO_ERROR){
137
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
138
gpgme_strsource(rc), gpgme_strerror(rc));
142
/* Create new GPGME "context" */
143
rc = gpgme_new(&ctx);
144
if (rc != GPG_ERR_NO_ERROR){
145
fprintf(stderr, "bad gpgme_new: %s: %s\n",
146
gpgme_strsource(rc), gpgme_strerror(rc));
150
/* Decrypt data from the FILE pointer to the plaintext data
152
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
153
if (rc != GPG_ERR_NO_ERROR){
154
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
155
gpgme_strsource(rc), gpgme_strerror(rc));
160
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
164
gpgme_decrypt_result_t result;
165
result = gpgme_op_decrypt_result(ctx);
167
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
169
fprintf(stderr, "Unsupported algorithm: %s\n",
170
result->unsupported_algorithm);
171
fprintf(stderr, "Wrong key usage: %d\n",
172
result->wrong_key_usage);
173
if(result->file_name != NULL){
174
fprintf(stderr, "File name: %s\n", result->file_name);
176
gpgme_recipient_t recipient;
177
recipient = result->recipients;
370
if(rc != GPG_ERR_NO_ERROR){
371
fprintf_plus(stderr, "Mandos plugin mandos-client: "
372
"bad gpgme_data_new: %s: %s\n",
373
gpgme_strsource(rc), gpgme_strerror(rc));
374
gpgme_data_release(dh_crypto);
378
/* Decrypt data from the cryptotext data buffer to the plaintext
380
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
381
if(rc != GPG_ERR_NO_ERROR){
382
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
383
gpgme_strsource(rc), gpgme_strerror(rc));
384
plaintext_length = -1;
386
gpgme_decrypt_result_t result;
387
result = gpgme_op_decrypt_result(mc->ctx);
389
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
391
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
392
result->unsupported_algorithm);
393
fprintf_plus(stderr, "Wrong key usage: %u\n",
394
result->wrong_key_usage);
395
if(result->file_name != NULL){
396
fprintf_plus(stderr, "File name: %s\n", result->file_name);
398
gpgme_recipient_t recipient;
399
recipient = result->recipients;
179
400
while(recipient != NULL){
180
fprintf(stderr, "Public key algorithm: %s\n",
181
gpgme_pubkey_algo_name(recipient->pubkey_algo));
182
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
183
fprintf(stderr, "Secret key available: %s\n",
184
recipient->status == GPG_ERR_NO_SECKEY
401
fprintf_plus(stderr, "Public key algorithm: %s\n",
402
gpgme_pubkey_algo_name
403
(recipient->pubkey_algo));
404
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
405
fprintf_plus(stderr, "Secret key available: %s\n",
406
recipient->status == GPG_ERR_NO_SECKEY
186
408
recipient = recipient->next;
192
/* Delete the GPGME FILE pointer cryptotext data buffer */
193
gpgme_data_release(dh_crypto);
416
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
195
419
/* Seek back to the beginning of the GPGME plaintext data buffer */
196
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
420
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
421
perror_plus("gpgme_data_seek");
422
plaintext_length = -1;
200
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
201
*new_packet = realloc(*new_packet,
202
(unsigned int)new_packet_capacity
204
if (*new_packet == NULL){
208
new_packet_capacity += BUFFER_SIZE;
428
plaintext_capacity = incbuffer(plaintext,
429
(size_t)plaintext_length,
431
if(plaintext_capacity == 0){
432
perror_plus("incbuffer");
433
plaintext_length = -1;
211
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
437
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
213
439
/* Print the data, if any */
218
perror("gpgme_data_read");
221
new_packet_length += ret;
224
/* FIXME: check characters before printing to screen so to not print
225
terminal control characters */
227
/* fprintf(stderr, "decrypted password is: "); */
228
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
229
/* fprintf(stderr, "\n"); */
445
perror_plus("gpgme_data_read");
446
plaintext_length = -1;
449
plaintext_length += ret;
453
fprintf_plus(stderr, "Decrypted password is: ");
454
for(ssize_t i = 0; i < plaintext_length; i++){
455
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
457
fprintf(stderr, "\n");
462
/* Delete the GPGME cryptotext data buffer */
463
gpgme_data_release(dh_crypto);
232
465
/* Delete the GPGME plaintext data buffer */
233
466
gpgme_data_release(dh_plain);
234
return new_packet_length;
467
return plaintext_length;
237
static const char * safer_gnutls_strerror (int value) {
238
const char *ret = gnutls_strerror (value);
470
static const char * safer_gnutls_strerror(int value){
471
const char *ret = gnutls_strerror(value);
240
473
ret = "(unknown)";
244
void debuggnutls(__attribute__((unused)) int level,
246
fprintf(stderr, "%s", string);
477
/* GnuTLS log function callback */
478
static void debuggnutls(__attribute__((unused)) int level,
480
fprintf_plus(stderr, "GnuTLS: %s", string);
249
int initgnutls(encrypted_session *es){
483
static int init_gnutls_global(const char *pubkeyfilename,
484
const char *seckeyfilename,
254
fprintf(stderr, "Initializing GnuTLS\n");
489
fprintf_plus(stderr, "Initializing GnuTLS\n");
257
if ((ret = gnutls_global_init ())
258
!= GNUTLS_E_SUCCESS) {
259
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
492
ret = gnutls_global_init();
493
if(ret != GNUTLS_E_SUCCESS){
494
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
495
safer_gnutls_strerror(ret));
500
/* "Use a log level over 10 to enable all debugging options."
264
503
gnutls_global_set_log_level(11);
265
504
gnutls_global_set_log_function(debuggnutls);
268
/* openpgp credentials */
269
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
270
!= GNUTLS_E_SUCCESS) {
271
fprintf (stderr, "memory error: %s\n",
272
safer_gnutls_strerror(ret));
507
/* OpenPGP credentials */
508
ret = gnutls_certificate_allocate_credentials(&mc->cred);
509
if(ret != GNUTLS_E_SUCCESS){
510
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
511
safer_gnutls_strerror(ret));
512
gnutls_global_deinit();
277
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
278
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
517
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
518
" secret key %s as GnuTLS credentials\n",
282
523
ret = gnutls_certificate_set_openpgp_key_file
283
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
284
if (ret != GNUTLS_E_SUCCESS) {
286
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
288
ret, CERTFILE, KEYFILE);
289
fprintf(stdout, "The Error is: %s\n",
290
safer_gnutls_strerror(ret));
294
//GnuTLS server initialization
295
if ((ret = gnutls_dh_params_init (&es->dh_params))
296
!= GNUTLS_E_SUCCESS) {
297
fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
safer_gnutls_strerror(ret));
302
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
303
!= GNUTLS_E_SUCCESS) {
304
fprintf (stderr, "Error in prime generation: %s\n",
305
safer_gnutls_strerror(ret));
309
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
311
// GnuTLS session creation
312
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
313
!= GNUTLS_E_SUCCESS){
314
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
315
safer_gnutls_strerror(ret));
318
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
319
!= GNUTLS_E_SUCCESS) {
320
fprintf(stderr, "Syntax error at: %s\n", err);
321
fprintf(stderr, "GnuTLS error: %s\n",
322
safer_gnutls_strerror(ret));
326
if ((ret = gnutls_credentials_set
327
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Error setting a credentials set: %s\n",
330
safer_gnutls_strerror(ret));
524
(mc->cred, pubkeyfilename, seckeyfilename,
525
GNUTLS_OPENPGP_FMT_BASE64);
526
if(ret != GNUTLS_E_SUCCESS){
528
"Error[%d] while reading the OpenPGP key pair ('%s',"
529
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
530
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
531
safer_gnutls_strerror(ret));
535
/* GnuTLS server initialization */
536
ret = gnutls_dh_params_init(&mc->dh_params);
537
if(ret != GNUTLS_E_SUCCESS){
538
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
539
" initialization: %s\n",
540
safer_gnutls_strerror(ret));
543
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
544
if(ret != GNUTLS_E_SUCCESS){
545
fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
546
safer_gnutls_strerror(ret));
550
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
556
gnutls_certificate_free_credentials(mc->cred);
557
gnutls_global_deinit();
558
gnutls_dh_params_deinit(mc->dh_params);
562
static int init_gnutls_session(gnutls_session_t *session,
565
/* GnuTLS session creation */
567
ret = gnutls_init(session, GNUTLS_SERVER);
571
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
572
if(ret != GNUTLS_E_SUCCESS){
574
"Error in GnuTLS session initialization: %s\n",
575
safer_gnutls_strerror(ret));
581
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
583
gnutls_deinit(*session);
586
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
587
if(ret != GNUTLS_E_SUCCESS){
588
fprintf_plus(stderr, "Syntax error at: %s\n", err);
589
fprintf_plus(stderr, "GnuTLS error: %s\n",
590
safer_gnutls_strerror(ret));
591
gnutls_deinit(*session);
597
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
600
gnutls_deinit(*session);
603
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
604
if(ret != GNUTLS_E_SUCCESS){
605
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
606
safer_gnutls_strerror(ret));
607
gnutls_deinit(*session);
334
611
/* ignore client certificate if any. */
335
gnutls_certificate_server_set_request (es->session,
612
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
338
gnutls_dh_set_prime_bits (es->session, DH_BITS);
614
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
343
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
__attribute__((unused)) const char *txt){}
619
/* Avahi log function callback */
620
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
621
__attribute__((unused)) const char *txt){}
346
int start_mandos_communication(const char *ip, uint16_t port,
347
unsigned int if_index){
349
struct sockaddr_in6 to;
350
encrypted_session es;
623
/* Called when a Mandos server is found */
624
static int start_mandos_communication(const char *ip, in_port_t port,
625
AvahiIfIndex if_index,
626
int af, mandos_context *mc){
627
int ret, tcp_sd = -1;
630
struct sockaddr_in in;
631
struct sockaddr_in6 in6;
351
633
char *buffer = NULL;
352
char *decrypted_buffer;
634
char *decrypted_buffer = NULL;
353
635
size_t buffer_length = 0;
354
636
size_t buffer_capacity = 0;
355
ssize_t decrypted_buffer_size;
358
char interface[IF_NAMESIZE];
361
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
364
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
370
if(if_indextoname(if_index, interface) == NULL){
372
perror("if_indextoname");
378
fprintf(stderr, "Binding to interface %s\n", interface);
381
memset(&to,0,sizeof(to)); /* Spurious warning */
382
to.sin6_family = AF_INET6;
383
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
639
gnutls_session_t session;
640
int pf; /* Protocol family */
657
fprintf_plus(stderr, "Bad address family: %d\n", af);
662
ret = init_gnutls_session(&session, mc);
668
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
669
PRIuMAX "\n", ip, (uintmax_t)port);
672
tcp_sd = socket(pf, SOCK_STREAM, 0);
675
perror_plus("socket");
685
memset(&to, 0, sizeof(to));
687
to.in6.sin6_family = (sa_family_t)af;
688
ret = inet_pton(af, ip, &to.in6.sin6_addr);
690
to.in.sin_family = (sa_family_t)af;
691
ret = inet_pton(af, ip, &to.in.sin_addr);
695
perror_plus("inet_pton");
389
fprintf(stderr, "Bad address: %s\n", ip);
392
to.sin6_port = htons(port); /* Spurious warning */
394
to.sin6_scope_id = (uint32_t)if_index;
397
fprintf(stderr, "Connection to: %s\n", ip);
400
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
406
ret = initgnutls (&es);
412
gnutls_transport_set_ptr (es.session,
413
(gnutls_transport_ptr_t) tcp_sd);
416
fprintf(stderr, "Establishing TLS session with %s\n", ip);
419
ret = gnutls_handshake (es.session);
421
if (ret != GNUTLS_E_SUCCESS){
701
fprintf_plus(stderr, "Bad address: %s\n", ip);
706
to.in6.sin6_port = htons(port);
707
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
708
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
710
if(if_index == AVAHI_IF_UNSPEC){
711
fprintf_plus(stderr, "An IPv6 link-local address is"
712
" incomplete without a network interface\n");
716
/* Set the network interface number as scope */
717
to.in6.sin6_scope_id = (uint32_t)if_index;
720
to.in.sin_port = htons(port); /* Spurious warnings from
722
-Wunreachable-code */
731
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
732
char interface[IF_NAMESIZE];
733
if(if_indextoname((unsigned int)if_index, interface) == NULL){
734
perror_plus("if_indextoname");
736
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
737
"\n", ip, interface, (uintmax_t)port);
740
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
741
ip, (uintmax_t)port);
743
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
744
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
747
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
750
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
754
perror_plus("inet_ntop");
756
if(strcmp(addrstr, ip) != 0){
757
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
768
ret = connect(tcp_sd, &to.in6, sizeof(to));
770
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
773
if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
775
perror_plus("connect");
786
const char *out = mandos_protocol_version;
789
size_t out_size = strlen(out);
790
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
791
out_size - written));
794
perror_plus("write");
798
written += (size_t)ret;
799
if(written < out_size){
802
if(out == mandos_protocol_version){
817
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
825
/* This casting via intptr_t is to eliminate warning about casting
826
an int to a pointer type. This is exactly how the GnuTLS Guile
827
function "set-session-transport-fd!" does it. */
828
gnutls_transport_set_ptr(session,
829
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
837
ret = gnutls_handshake(session);
842
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
844
if(ret != GNUTLS_E_SUCCESS){
423
fprintf(stderr, "\n*** Handshake failed ***\n");
846
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
430
//Retrieve OpenPGP packet that contains the wanted password
853
/* Read OpenPGP packet that contains the wanted password */
433
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
856
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
438
if (buffer_length + BUFFER_SIZE > buffer_capacity){
439
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
444
buffer_capacity += BUFFER_SIZE;
447
ret = gnutls_record_recv
448
(es.session, buffer+buffer_length, BUFFER_SIZE);
867
buffer_capacity = incbuffer(&buffer, buffer_length,
869
if(buffer_capacity == 0){
871
perror_plus("incbuffer");
881
sret = gnutls_record_recv(session, buffer+buffer_length,
454
888
case GNUTLS_E_INTERRUPTED:
455
889
case GNUTLS_E_AGAIN:
457
891
case GNUTLS_E_REHANDSHAKE:
458
ret = gnutls_handshake (es.session);
460
fprintf(stderr, "\n*** Handshake failed ***\n");
893
ret = gnutls_handshake(session);
899
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
901
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
467
fprintf(stderr, "Unknown error while reading data from"
468
" encrypted session with mandos server\n");
470
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
909
fprintf_plus(stderr, "Unknown error while reading data from"
910
" encrypted session with Mandos server\n");
911
gnutls_bye(session, GNUTLS_SHUT_RDWR);
474
buffer_length += (size_t) ret;
478
if (buffer_length > 0){
479
decrypted_buffer_size = pgp_packet_decrypt(buffer,
483
if (decrypted_buffer_size >= 0){
484
while(written < decrypted_buffer_size){
485
ret = (int)fwrite (decrypted_buffer + written, 1,
486
(size_t)decrypted_buffer_size - written,
916
buffer_length += (size_t) sret;
921
fprintf_plus(stderr, "Closing TLS session\n");
930
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
935
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
937
if(buffer_length > 0){
938
ssize_t decrypted_buffer_size;
939
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
940
&decrypted_buffer, mc);
941
if(decrypted_buffer_size >= 0){
944
while(written < (size_t) decrypted_buffer_size){
950
ret = (int)fwrite(decrypted_buffer + written, 1,
951
(size_t)decrypted_buffer_size - written,
488
953
if(ret == 0 and ferror(stdout)){
490
fprintf(stderr, "Error writing encrypted data: %s\n",
956
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
496
962
written += (size_t)ret;
498
free(decrypted_buffer);
968
/* Shutdown procedure */
973
free(decrypted_buffer);
976
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
982
perror_plus("close");
984
gnutls_deinit(session);
507
fprintf(stderr, "Closing TLS session\n");
511
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
514
gnutls_deinit (es.session);
515
gnutls_certificate_free_credentials (es.cred);
516
gnutls_global_deinit ();
520
static AvahiSimplePoll *simple_poll = NULL;
521
static AvahiServer *server = NULL;
523
static void resolve_callback(
524
AvahiSServiceResolver *r,
525
AvahiIfIndex interface,
526
AVAHI_GCC_UNUSED AvahiProtocol protocol,
527
AvahiResolverEvent event,
531
const char *host_name,
532
const AvahiAddress *address,
534
AVAHI_GCC_UNUSED AvahiStringList *txt,
535
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
536
AVAHI_GCC_UNUSED void* userdata) {
538
assert(r); /* Spurious warning */
994
static void resolve_callback(AvahiSServiceResolver *r,
995
AvahiIfIndex interface,
997
AvahiResolverEvent event,
1001
const char *host_name,
1002
const AvahiAddress *address,
1004
AVAHI_GCC_UNUSED AvahiStringList *txt,
1005
AVAHI_GCC_UNUSED AvahiLookupResultFlags
540
1012
/* Called whenever a service has been resolved successfully or
545
1021
case AVAHI_RESOLVER_FAILURE:
546
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
547
" type '%s' in domain '%s': %s\n", name, type, domain,
548
avahi_strerror(avahi_server_errno(server)));
1022
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1023
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1025
avahi_strerror(avahi_server_errno
1026
(((mandos_context*)mc)->server)));
551
1029
case AVAHI_RESOLVER_FOUND:
553
1031
char ip[AVAHI_ADDRESS_STR_MAX];
554
1032
avahi_address_snprint(ip, sizeof(ip), address);
556
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
557
" port %d\n", name, host_name, ip, port);
1034
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1035
PRIdMAX ") on port %" PRIu16 "\n", name,
1036
host_name, ip, (intmax_t)interface, port);
559
int ret = start_mandos_communication(ip, port,
560
(unsigned int) interface);
1038
int ret = start_mandos_communication(ip, (in_port_t)port,
1040
avahi_proto_to_af(proto),
1043
avahi_simple_poll_quit(simple_poll);
1045
if(not add_server(ip, (in_port_t)port, interface,
1046
avahi_proto_to_af(proto),
1047
&((mandos_context*)mc)->current_server)){
1048
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
566
1054
avahi_s_service_resolver_free(r);
569
static void browse_callback(
570
AvahiSServiceBrowser *b,
571
AvahiIfIndex interface,
572
AvahiProtocol protocol,
573
AvahiBrowserEvent event,
577
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
580
AvahiServer *s = userdata;
581
assert(b); /* Spurious warning */
583
/* Called whenever a new services becomes available on the LAN or
584
is removed from the LAN */
1057
static void browse_callback(AvahiSServiceBrowser *b,
1058
AvahiIfIndex interface,
1059
AvahiProtocol protocol,
1060
AvahiBrowserEvent event,
1064
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1071
/* Called whenever a new services becomes available on the LAN or
1072
is removed from the LAN */
1080
case AVAHI_BROWSER_FAILURE:
1082
fprintf_plus(stderr, "(Avahi browser) %s\n",
1083
avahi_strerror(avahi_server_errno
1084
(((mandos_context*)mc)->server)));
1085
avahi_simple_poll_quit(simple_poll);
1088
case AVAHI_BROWSER_NEW:
1089
/* We ignore the returned Avahi resolver object. In the callback
1090
function we free it. If the Avahi server is terminated before
1091
the callback function is called the Avahi server will free the
1094
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1095
interface, protocol, name, type,
1096
domain, protocol, 0,
1097
resolve_callback, mc) == NULL)
1098
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1100
avahi_strerror(avahi_server_errno
1101
(((mandos_context*)mc)->server)));
1104
case AVAHI_BROWSER_REMOVE:
1107
case AVAHI_BROWSER_ALL_FOR_NOW:
1108
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1110
fprintf_plus(stderr, "No Mandos server found, still"
1117
/* Signal handler that stops main loop after SIGTERM */
1118
static void handle_sigterm(int sig){
1123
signal_received = sig;
1124
int old_errno = errno;
1125
/* set main loop to exit */
1126
if(simple_poll != NULL){
1127
avahi_simple_poll_quit(simple_poll);
1132
bool get_flags(const char *ifname, struct ifreq *ifr){
1136
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1139
perror_plus("socket");
1143
strcpy(ifr->ifr_name, ifname);
1144
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1148
perror_plus("ioctl SIOCGIFFLAGS");
1156
bool good_flags(const char *ifname, const struct ifreq *ifr){
1158
/* Reject the loopback device */
1159
if(ifr->ifr_flags & IFF_LOOPBACK){
1161
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1166
/* Accept point-to-point devices only if connect_to is specified */
1167
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1169
fprintf_plus(stderr, "Accepting point-to-point interface"
1170
" \"%s\"\n", ifname);
1174
/* Otherwise, reject non-broadcast-capable devices */
1175
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1177
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1178
" \"%s\"\n", ifname);
1182
/* Reject non-ARP interfaces (including dummy interfaces) */
1183
if(ifr->ifr_flags & IFF_NOARP){
1185
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1191
/* Accept this device */
1193
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1199
* This function determines if a directory entry in /sys/class/net
1200
* corresponds to an acceptable network device.
1201
* (This function is passed to scandir(3) as a filter function.)
1203
int good_interface(const struct dirent *if_entry){
1204
if(if_entry->d_name[0] == '.'){
1209
if(not get_flags(if_entry->d_name, &ifr)){
1211
fprintf_plus(stderr, "Failed to get flags for interface "
1212
"\"%s\"\n", if_entry->d_name);
1217
if(not good_flags(if_entry->d_name, &ifr)){
1224
* This function determines if a network interface is up.
1226
bool interface_is_up(const char *interface){
1228
if(not get_flags(interface, &ifr)){
1230
fprintf_plus(stderr, "Failed to get flags for interface "
1231
"\"%s\"\n", interface);
1236
return (bool)(ifr.ifr_flags & IFF_UP);
1240
* This function determines if a network interface is running
1242
bool interface_is_running(const char *interface){
1244
if(not get_flags(interface, &ifr)){
1246
fprintf_plus(stderr, "Failed to get flags for interface "
1247
"\"%s\"\n", interface);
1252
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1255
int notdotentries(const struct dirent *direntry){
1256
/* Skip "." and ".." */
1257
if(direntry->d_name[0] == '.'
1258
and (direntry->d_name[1] == '\0'
1259
or (direntry->d_name[1] == '.'
1260
and direntry->d_name[2] == '\0'))){
1266
/* Is this directory entry a runnable program? */
1267
int runnable_hook(const struct dirent *direntry){
1272
if((direntry->d_name)[0] == '\0'){
1277
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1278
"abcdefghijklmnopqrstuvwxyz"
1281
if((direntry->d_name)[sret] != '\0'){
1282
/* Contains non-allowed characters */
1284
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1290
char *fullname = NULL;
1291
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1293
perror_plus("asprintf");
1297
ret = stat(fullname, &st);
1300
perror_plus("Could not stat hook");
1304
if(not (S_ISREG(st.st_mode))){
1305
/* Not a regular file */
1307
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1312
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1313
/* Not executable */
1315
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1321
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1327
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1328
mandos_context *mc){
1330
struct timespec now;
1331
struct timespec waited_time;
1332
intmax_t block_time;
1335
if(mc->current_server == NULL){
1337
fprintf_plus(stderr, "Wait until first server is found."
1340
ret = avahi_simple_poll_iterate(s, -1);
1343
fprintf_plus(stderr, "Check current_server if we should run"
1346
/* the current time */
1347
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1349
perror_plus("clock_gettime");
1352
/* Calculating in ms how long time between now and server
1353
who we visted longest time ago. Now - last seen. */
1354
waited_time.tv_sec = (now.tv_sec
1355
- mc->current_server->last_seen.tv_sec);
1356
waited_time.tv_nsec = (now.tv_nsec
1357
- mc->current_server->last_seen.tv_nsec);
1358
/* total time is 10s/10,000ms.
1359
Converting to s from ms by dividing by 1,000,
1360
and ns to ms by dividing by 1,000,000. */
1361
block_time = ((retry_interval
1362
- ((intmax_t)waited_time.tv_sec * 1000))
1363
- ((intmax_t)waited_time.tv_nsec / 1000000));
1366
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1370
if(block_time <= 0){
1371
ret = start_mandos_communication(mc->current_server->ip,
1372
mc->current_server->port,
1373
mc->current_server->if_index,
1374
mc->current_server->af, mc);
1376
avahi_simple_poll_quit(s);
1379
ret = clock_gettime(CLOCK_MONOTONIC,
1380
&mc->current_server->last_seen);
1382
perror_plus("clock_gettime");
1385
mc->current_server = mc->current_server->next;
1386
block_time = 0; /* Call avahi to find new Mandos
1387
servers, but don't block */
1390
ret = avahi_simple_poll_iterate(s, (int)block_time);
1393
if (ret > 0 or errno != EINTR){
1394
return (ret != 1) ? ret : 0;
1400
/* Set effective uid to 0, return errno */
1401
error_t raise_privileges(void){
1402
error_t old_errno = errno;
1403
error_t ret_errno = 0;
1404
if(seteuid(0) == -1){
1406
perror_plus("seteuid");
1412
/* Set effective and real user ID to 0. Return errno. */
1413
error_t raise_privileges_permanently(void){
1414
error_t old_errno = errno;
1415
error_t ret_errno = raise_privileges();
1420
if(setuid(0) == -1){
1422
perror_plus("seteuid");
1428
/* Set effective user ID to unprivileged saved user ID */
1429
error_t lower_privileges(void){
1430
error_t old_errno = errno;
1431
error_t ret_errno = 0;
1432
if(seteuid(uid) == -1){
1434
perror_plus("seteuid");
1440
/* Lower privileges permanently */
1441
error_t lower_privileges_permanently(void){
1442
error_t old_errno = errno;
1443
error_t ret_errno = 0;
1444
if(setuid(uid) == -1){
1446
perror_plus("setuid");
1452
bool run_network_hooks(const char *mode, const char *interface,
1454
struct dirent **direntries;
1455
struct dirent *direntry;
1457
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1460
if(errno == ENOENT){
1462
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1463
" found\n", hookdir);
1466
perror_plus("scandir");
1469
int devnull = open("/dev/null", O_RDONLY);
1470
for(int i = 0; i < numhooks; i++){
1471
direntry = direntries[i];
1472
char *fullname = NULL;
1473
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1475
perror_plus("asprintf");
1479
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1482
pid_t hook_pid = fork();
1485
/* Raise privileges */
1486
raise_privileges_permanently();
1491
perror_plus("setgid");
1493
/* Reset supplementary groups */
1495
ret = setgroups(0, NULL);
1497
perror_plus("setgroups");
1499
dup2(devnull, STDIN_FILENO);
1501
dup2(STDERR_FILENO, STDOUT_FILENO);
1502
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1504
perror_plus("setenv");
1507
ret = setenv("DEVICE", interface, 1);
1509
perror_plus("setenv");
1512
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1514
perror_plus("setenv");
1517
ret = setenv("MODE", mode, 1);
1519
perror_plus("setenv");
1523
ret = asprintf(&delaystring, "%f", delay);
1525
perror_plus("asprintf");
1528
ret = setenv("DELAY", delaystring, 1);
1531
perror_plus("setenv");
1535
if(connect_to != NULL){
1536
ret = setenv("CONNECT", connect_to, 1);
1538
perror_plus("setenv");
1542
if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1543
perror_plus("execl");
1544
_exit(EXIT_FAILURE);
1548
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1549
perror_plus("waitpid");
1553
if(WIFEXITED(status)){
1554
if(WEXITSTATUS(status) != 0){
1555
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1556
" with status %d\n", direntry->d_name,
1557
WEXITSTATUS(status));
1561
} else if(WIFSIGNALED(status)){
1562
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1563
" signal %d\n", direntry->d_name,
1568
fprintf_plus(stderr, "Warning: network hook \"%s\""
1569
" crashed\n", direntry->d_name);
1576
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1585
error_t bring_up_interface(const char *const interface,
1588
error_t old_errno = errno;
1589
error_t ret_errno = 0;
1590
int ret, ret_setflags;
1591
struct ifreq network;
1592
unsigned int if_index = if_nametoindex(interface);
1594
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1604
if(not interface_is_up(interface)){
1605
if(not get_flags(interface, &network) and debug){
1607
fprintf_plus(stderr, "Failed to get flags for interface "
1608
"\"%s\"\n", interface);
1611
network.ifr_flags |= IFF_UP;
1613
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1616
perror_plus("socket");
1628
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
1632
/* Raise priviliges */
1636
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1637
messages about the network interface to mess up the prompt */
1638
int ret_linux = klogctl(8, NULL, 5);
1639
bool restore_loglevel = true;
1640
if(ret_linux == -1){
1641
restore_loglevel = false;
1642
perror_plus("klogctl");
1644
#endif /* __linux__ */
1645
ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1648
if(restore_loglevel){
1649
ret_linux = klogctl(7, NULL, 0);
1650
if(ret_linux == -1){
1651
perror_plus("klogctl");
1654
#endif /* __linux__ */
1656
/* Lower privileges */
1659
/* Close the socket */
1660
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1662
perror_plus("close");
1665
if(ret_setflags == -1){
1667
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1672
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1676
/* Sleep checking until interface is running.
1677
Check every 0.25s, up to total time of delay */
1678
for(int i=0; i < delay * 4; i++){
1679
if(interface_is_running(interface)){
1682
struct timespec sleeptime = { .tv_nsec = 250000000 };
1683
ret = nanosleep(&sleeptime, NULL);
1684
if(ret == -1 and errno != EINTR){
1685
perror_plus("nanosleep");
1693
error_t take_down_interface(const char *const interface){
1695
error_t old_errno = errno;
1696
error_t ret_errno = 0;
1697
int ret, ret_setflags;
1698
struct ifreq network;
1699
unsigned int if_index = if_nametoindex(interface);
1701
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1705
if(interface_is_up(interface)){
1706
if(not get_flags(interface, &network) and debug){
1708
fprintf_plus(stderr, "Failed to get flags for interface "
1709
"\"%s\"\n", interface);
1712
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1714
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1717
perror_plus("socket");
1723
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
1727
/* Raise priviliges */
1730
ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1733
/* Lower privileges */
1736
/* Close the socket */
1737
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1739
perror_plus("close");
1742
if(ret_setflags == -1){
1744
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1749
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1757
int main(int argc, char *argv[]){
1758
mandos_context mc = { .server = NULL, .dh_bits = 1024,
1759
.priority = "SECURE256:!CTYPE-X.509:"
1760
"+CTYPE-OPENPGP", .current_server = NULL,
1761
.interfaces = NULL, .interfaces_size = 0 };
1762
AvahiSServiceBrowser *sb = NULL;
1767
int exitcode = EXIT_SUCCESS;
1768
char *interfaces_to_take_down = NULL;
1769
size_t interfaces_to_take_down_size = 0;
1770
char tempdir[] = "/tmp/mandosXXXXXX";
1771
bool tempdir_created = false;
1772
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1773
const char *seckey = PATHDIR "/" SECKEY;
1774
const char *pubkey = PATHDIR "/" PUBKEY;
1775
char *interfaces_hooks = NULL;
1776
size_t interfaces_hooks_size = 0;
1778
bool gnutls_initialized = false;
1779
bool gpgme_initialized = false;
1781
double retry_interval = 10; /* 10s between trying a server and
1782
retrying the same server again */
1784
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1785
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1790
/* Lower any group privileges we might have, just to be safe */
1794
perror_plus("setgid");
1797
/* Lower user privileges (temporarily) */
1801
perror_plus("seteuid");
1809
struct argp_option options[] = {
1810
{ .name = "debug", .key = 128,
1811
.doc = "Debug mode", .group = 3 },
1812
{ .name = "connect", .key = 'c',
1813
.arg = "ADDRESS:PORT",
1814
.doc = "Connect directly to a specific Mandos server",
1816
{ .name = "interface", .key = 'i',
1818
.doc = "Network interface that will be used to search for"
1821
{ .name = "seckey", .key = 's',
1823
.doc = "OpenPGP secret key file base name",
1825
{ .name = "pubkey", .key = 'p',
1827
.doc = "OpenPGP public key file base name",
1829
{ .name = "dh-bits", .key = 129,
1831
.doc = "Bit length of the prime number used in the"
1832
" Diffie-Hellman key exchange",
1834
{ .name = "priority", .key = 130,
1836
.doc = "GnuTLS priority string for the TLS handshake",
1838
{ .name = "delay", .key = 131,
1840
.doc = "Maximum delay to wait for interface startup",
1842
{ .name = "retry", .key = 132,
1844
.doc = "Retry interval used when denied by the Mandos server",
1846
{ .name = "network-hook-dir", .key = 133,
1848
.doc = "Directory where network hooks are located",
1851
* These reproduce what we would get without ARGP_NO_HELP
1853
{ .name = "help", .key = '?',
1854
.doc = "Give this help list", .group = -1 },
1855
{ .name = "usage", .key = -3,
1856
.doc = "Give a short usage message", .group = -1 },
1857
{ .name = "version", .key = 'V',
1858
.doc = "Print program version", .group = -1 },
1862
error_t parse_opt(int key, char *arg,
1863
struct argp_state *state){
1866
case 128: /* --debug */
1869
case 'c': /* --connect */
1872
case 'i': /* --interface */
1873
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
1876
argp_error(state, "%s", strerror(ret_errno));
1879
case 's': /* --seckey */
1882
case 'p': /* --pubkey */
1885
case 129: /* --dh-bits */
1887
tmpmax = strtoimax(arg, &tmp, 10);
1888
if(errno != 0 or tmp == arg or *tmp != '\0'
1889
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1890
argp_error(state, "Bad number of DH bits");
1892
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1894
case 130: /* --priority */
1897
case 131: /* --delay */
1899
delay = strtof(arg, &tmp);
1900
if(errno != 0 or tmp == arg or *tmp != '\0'){
1901
argp_error(state, "Bad delay");
1903
case 132: /* --retry */
1905
retry_interval = strtod(arg, &tmp);
1906
if(errno != 0 or tmp == arg or *tmp != '\0'
1907
or (retry_interval * 1000) > INT_MAX
1908
or retry_interval < 0){
1909
argp_error(state, "Bad retry interval");
1912
case 133: /* --network-hook-dir */
1916
* These reproduce what we would get without ARGP_NO_HELP
1918
case '?': /* --help */
1919
argp_state_help(state, state->out_stream,
1920
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1921
& ~(unsigned int)ARGP_HELP_EXIT_OK);
1922
case -3: /* --usage */
1923
argp_state_help(state, state->out_stream,
1924
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1925
case 'V': /* --version */
1926
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
1927
exit(argp_err_exit_status);
1930
return ARGP_ERR_UNKNOWN;
1935
struct argp argp = { .options = options, .parser = parse_opt,
1937
.doc = "Mandos client -- Get and decrypt"
1938
" passwords from a Mandos server" };
1939
ret = argp_parse(&argp, argc, argv,
1940
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
588
case AVAHI_BROWSER_FAILURE:
590
fprintf(stderr, "(Browser) %s\n",
591
avahi_strerror(avahi_server_errno(server)));
592
avahi_simple_poll_quit(simple_poll);
595
case AVAHI_BROWSER_NEW:
596
/* We ignore the returned resolver object. In the callback
597
function we free it. If the server is terminated before
598
the callback function is called the server will free
599
the resolver for us. */
601
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
603
AVAHI_PROTO_INET6, 0,
604
resolve_callback, s)))
605
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
606
avahi_strerror(avahi_server_errno(s)));
609
case AVAHI_BROWSER_REMOVE:
612
case AVAHI_BROWSER_ALL_FOR_NOW:
613
case AVAHI_BROWSER_CACHE_EXHAUSTED:
618
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1947
perror_plus("argp_parse");
1948
exitcode = EX_OSERR;
1951
exitcode = EX_USAGE;
1957
/* Work around Debian bug #633582:
1958
<http://bugs.debian.org/633582> */
1960
/* Re-raise priviliges */
1961
if(raise_privileges() == 0){
1964
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1965
int seckey_fd = open(seckey, O_RDONLY);
1966
if(seckey_fd == -1){
1967
perror_plus("open");
1969
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1971
perror_plus("fstat");
1973
if(S_ISREG(st.st_mode)
1974
and st.st_uid == 0 and st.st_gid == 0){
1975
ret = fchown(seckey_fd, uid, gid);
1977
perror_plus("fchown");
1981
TEMP_FAILURE_RETRY(close(seckey_fd));
1985
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
1986
int pubkey_fd = open(pubkey, O_RDONLY);
1987
if(pubkey_fd == -1){
1988
perror_plus("open");
1990
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1992
perror_plus("fstat");
1994
if(S_ISREG(st.st_mode)
1995
and st.st_uid == 0 and st.st_gid == 0){
1996
ret = fchown(pubkey_fd, uid, gid);
1998
perror_plus("fchown");
2002
TEMP_FAILURE_RETRY(close(pubkey_fd));
2006
/* Lower privileges */
2010
perror_plus("seteuid");
2015
/* Remove empty interface names */
2017
char *interface = NULL;
2018
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2020
if(if_nametoindex(interface) == 0){
2021
if(interface[0] != '\0' and strcmp(interface, "none") != 0){
2022
fprintf_plus(stderr, "Not using nonexisting interface"
2023
" \"%s\"\n", interface);
2025
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2031
/* Run network hooks */
2034
if(mc.interfaces != NULL){
2035
interfaces_hooks = malloc(mc.interfaces_size);
2036
if(interfaces_hooks == NULL){
2037
perror_plus("malloc");
2040
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2041
interfaces_hooks_size = mc.interfaces_size;
2042
argz_stringify(interfaces_hooks, interfaces_hooks_size,
2045
if(not run_network_hooks("start", interfaces_hooks != NULL ?
2046
interfaces_hooks : "", delay)){
2052
avahi_set_log_function(empty_log);
2055
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2056
from the signal handler */
2057
/* Initialize the pseudo-RNG for Avahi */
2058
srand((unsigned int) time(NULL));
2059
simple_poll = avahi_simple_poll_new();
2060
if(simple_poll == NULL){
2061
fprintf_plus(stderr,
2062
"Avahi: Failed to create simple poll object.\n");
2063
exitcode = EX_UNAVAILABLE;
2067
sigemptyset(&sigterm_action.sa_mask);
2068
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2070
perror_plus("sigaddset");
2071
exitcode = EX_OSERR;
2074
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2076
perror_plus("sigaddset");
2077
exitcode = EX_OSERR;
2080
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2082
perror_plus("sigaddset");
2083
exitcode = EX_OSERR;
2086
/* Need to check if the handler is SIG_IGN before handling:
2087
| [[info:libc:Initial Signal Actions]] |
2088
| [[info:libc:Basic Signal Handling]] |
2090
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2092
perror_plus("sigaction");
2095
if(old_sigterm_action.sa_handler != SIG_IGN){
2096
ret = sigaction(SIGINT, &sigterm_action, NULL);
2098
perror_plus("sigaction");
2099
exitcode = EX_OSERR;
2103
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2105
perror_plus("sigaction");
2108
if(old_sigterm_action.sa_handler != SIG_IGN){
2109
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2111
perror_plus("sigaction");
2112
exitcode = EX_OSERR;
2116
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2118
perror_plus("sigaction");
2121
if(old_sigterm_action.sa_handler != SIG_IGN){
2122
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2124
perror_plus("sigaction");
2125
exitcode = EX_OSERR;
2130
/* If no interfaces were specified, make a list */
2131
if(mc.interfaces == NULL){
2132
struct dirent **direntries;
2133
/* Look for any good interfaces */
2134
ret = scandir(sys_class_net, &direntries, good_interface,
2137
/* Add all found interfaces to interfaces list */
2138
for(int i = 0; i < ret; ++i){
2139
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2140
direntries[i]->d_name);
2142
perror_plus("argz_add");
2146
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2147
direntries[i]->d_name);
2153
fprintf_plus(stderr, "Could not find a network interface\n");
2154
exitcode = EXIT_FAILURE;
2159
/* If we only got one interface, explicitly use only that one */
2160
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2162
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2165
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2168
/* Bring up interfaces which are down */
2169
if(not (argz_count(mc.interfaces, mc.interfaces_size) == 1
2170
and strcmp(mc.interfaces, "none") == 0)){
2171
char *interface = NULL;
2172
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2174
bool interface_was_up = interface_is_up(interface);
2175
ret = bring_up_interface(interface, delay);
2176
if(not interface_was_up){
2179
perror_plus("Failed to bring up interface");
2181
ret_errno = argz_add(&interfaces_to_take_down,
2182
&interfaces_to_take_down_size,
2187
free(mc.interfaces);
2188
mc.interfaces = NULL;
2189
mc.interfaces_size = 0;
2190
if(debug and (interfaces_to_take_down == NULL)){
2191
fprintf_plus(stderr, "No interfaces were brought up\n");
2199
ret = init_gnutls_global(pubkey, seckey, &mc);
2201
fprintf_plus(stderr, "init_gnutls_global failed\n");
2202
exitcode = EX_UNAVAILABLE;
2205
gnutls_initialized = true;
2212
if(mkdtemp(tempdir) == NULL){
2213
perror_plus("mkdtemp");
2216
tempdir_created = true;
2222
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2223
fprintf_plus(stderr, "init_gpgme failed\n");
2224
exitcode = EX_UNAVAILABLE;
2227
gpgme_initialized = true;
2234
if(connect_to != NULL){
2235
/* Connect directly, do not use Zeroconf */
2236
/* (Mainly meant for debugging) */
2237
char *address = strrchr(connect_to, ':');
2239
if(address == NULL){
2240
fprintf_plus(stderr, "No colon in address\n");
2241
exitcode = EX_USAGE;
2251
tmpmax = strtoimax(address+1, &tmp, 10);
2252
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2253
or tmpmax != (in_port_t)tmpmax){
2254
fprintf_plus(stderr, "Bad port number\n");
2255
exitcode = EX_USAGE;
2263
port = (in_port_t)tmpmax;
2265
/* Colon in address indicates IPv6 */
2267
if(strchr(connect_to, ':') != NULL){
2269
/* Accept [] around IPv6 address - see RFC 5952 */
2270
if(connect_to[0] == '[' and address[-1] == ']')
2278
address = connect_to;
2284
while(not quit_now){
2285
ret = start_mandos_communication(address, port, if_index, af,
2287
if(quit_now or ret == 0){
2291
fprintf_plus(stderr, "Retrying in %d seconds\n",
2292
(int)retry_interval);
2294
sleep((int)retry_interval);
2298
exitcode = EXIT_SUCCESS;
619
2309
AvahiServerConfig config;
620
AvahiSServiceBrowser *sb = NULL;
623
int returncode = EXIT_SUCCESS;
624
const char *interface = "eth0";
627
static struct option long_options[] = {
628
{"debug", no_argument, (int *)&debug, 1},
629
{"interface", required_argument, 0, 'i'},
632
int option_index = 0;
633
ret = getopt_long (argc, argv, "i:", long_options,
652
avahi_set_log_function(empty_log);
655
/* Initialize the psuedo-RNG */
656
srand((unsigned int) time(NULL));
658
/* Allocate main loop object */
659
if (!(simple_poll = avahi_simple_poll_new())) {
660
fprintf(stderr, "Failed to create simple poll object.\n");
665
/* Do not publish any local records */
2310
/* Do not publish any local Zeroconf records */
666
2311
avahi_server_config_init(&config);
667
2312
config.publish_hinfo = 0;
668
2313
config.publish_addresses = 0;
669
2314
config.publish_workstation = 0;
670
2315
config.publish_domain = 0;
672
2317
/* Allocate a new server */
673
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
&config, NULL, NULL, &error);
676
/* Free the configuration data */
2318
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2319
&config, NULL, NULL, &ret_errno);
2321
/* Free the Avahi configuration data */
677
2322
avahi_server_config_free(&config);
679
/* Check if creating the server object succeeded */
681
fprintf(stderr, "Failed to create server: %s\n",
682
avahi_strerror(error));
683
returncode = EXIT_FAILURE;
687
/* Create the service browser */
688
sb = avahi_s_service_browser_new(server,
690
if_nametoindex(interface),
692
"_mandos._tcp", NULL, 0,
693
browse_callback, server);
695
fprintf(stderr, "Failed to create service browser: %s\n",
696
avahi_strerror(avahi_server_errno(server)));
697
returncode = EXIT_FAILURE;
701
/* Run the main loop */
704
fprintf(stderr, "Starting avahi loop search\n");
707
avahi_simple_poll_loop(simple_poll);
712
fprintf(stderr, "%s exiting\n", argv[0]);
717
avahi_s_service_browser_free(sb);
720
avahi_server_free(server);
723
avahi_simple_poll_free(simple_poll);
2325
/* Check if creating the Avahi server object succeeded */
2326
if(mc.server == NULL){
2327
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2328
avahi_strerror(ret_errno));
2329
exitcode = EX_UNAVAILABLE;
2337
/* Create the Avahi service browser */
2338
sb = avahi_s_service_browser_new(mc.server, if_index,
2339
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2340
NULL, 0, browse_callback,
2343
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2344
avahi_strerror(avahi_server_errno(mc.server)));
2345
exitcode = EX_UNAVAILABLE;
2353
/* Run the main loop */
2356
fprintf_plus(stderr, "Starting Avahi loop search\n");
2359
ret = avahi_loop_with_timeout(simple_poll,
2360
(int)(retry_interval * 1000), &mc);
2362
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2363
(ret == 0) ? "successfully" : "with error");
2369
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2372
/* Cleanup things */
2374
avahi_s_service_browser_free(sb);
2376
if(mc.server != NULL)
2377
avahi_server_free(mc.server);
2379
if(simple_poll != NULL)
2380
avahi_simple_poll_free(simple_poll);
2382
if(gnutls_initialized){
2383
gnutls_certificate_free_credentials(mc.cred);
2384
gnutls_global_deinit();
2385
gnutls_dh_params_deinit(mc.dh_params);
2388
if(gpgme_initialized){
2389
gpgme_release(mc.ctx);
2392
/* Cleans up the circular linked list of Mandos servers the client
2394
if(mc.current_server != NULL){
2395
mc.current_server->prev->next = NULL;
2396
while(mc.current_server != NULL){
2397
server *next = mc.current_server->next;
2398
free(mc.current_server);
2399
mc.current_server = next;
2403
/* Re-raise priviliges */
2407
/* Run network hooks */
2408
run_network_hooks("stop", interfaces_hooks != NULL ?
2409
interfaces_hooks : "", delay);
2411
/* Take down the network interfaces which were brought up */
2413
char *interface = NULL;
2414
while((interface=argz_next(interfaces_to_take_down,
2415
interfaces_to_take_down_size,
2417
ret_errno = take_down_interface(interface);
2420
perror_plus("Failed to take down interface");
2423
if(debug and (interfaces_to_take_down == NULL)){
2424
fprintf_plus(stderr, "No interfaces needed to be taken"
2429
lower_privileges_permanently();
2432
free(interfaces_to_take_down);
2433
free(interfaces_hooks);
2435
/* Removes the GPGME temp directory and all files inside */
2436
if(tempdir_created){
2437
struct dirent **direntries = NULL;
2438
struct dirent *direntry = NULL;
2439
int numentries = scandir(tempdir, &direntries, notdotentries,
2441
if (numentries > 0){
2442
for(int i = 0; i < numentries; i++){
2443
direntry = direntries[i];
2444
char *fullname = NULL;
2445
ret = asprintf(&fullname, "%s/%s", tempdir,
2448
perror_plus("asprintf");
2451
ret = remove(fullname);
2453
fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2460
/* need to clean even if 0 because man page doesn't specify */
2462
if (numentries == -1){
2463
perror_plus("scandir");
2465
ret = rmdir(tempdir);
2466
if(ret == -1 and errno != ENOENT){
2467
perror_plus("rmdir");
2472
sigemptyset(&old_sigterm_action.sa_mask);
2473
old_sigterm_action.sa_handler = SIG_DFL;
2474
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2475
&old_sigterm_action,
2478
perror_plus("sigaction");
2481
ret = raise(signal_received);
2482
} while(ret != 0 and errno == EINTR);
2484
perror_plus("raise");
2487
TEMP_FAILURE_RETRY(pause());