110
33
#include <avahi-common/malloc.h>
111
34
#include <avahi-common/error.h>
114
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
117
init_gnutls_session(),
119
#include <gnutls/openpgp.h>
120
/* gnutls_certificate_set_openpgp_key_file(),
121
GNUTLS_OPENPGP_FMT_BASE64 */
124
#include <gpgme.h> /* All GPGME types, constants and
127
GPGME_PROTOCOL_OpenPGP,
37
#include <sys/types.h> /* socket(), setsockopt(), inet_pton() */
38
#include <sys/socket.h> /* socket(), setsockopt(), struct sockaddr_in6, struct in6_addr, inet_pton() */
39
#include <gnutls/gnutls.h> /* ALL GNUTLS STUFF */
40
#include <gnutls/openpgp.h> /* gnutls with openpgp stuff */
42
#include <unistd.h> /* close() */
43
#include <netinet/in.h>
44
#include <stdbool.h> /* true */
45
#include <string.h> /* memset */
46
#include <arpa/inet.h> /* inet_pton() */
47
#include <iso646.h> /* not */
50
#include <errno.h> /* perror() */
55
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
57
#define CERTFILE CERT_ROOT "openpgp-client.txt"
58
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
130
59
#define BUFFER_SIZE 256
132
#define PATHDIR "/conf/conf.d/mandos"
133
#define SECKEY "seckey.txt"
134
#define PUBKEY "pubkey.txt"
135
#define HOOKDIR "/lib/mandos/network-hooks.d"
137
62
bool debug = false;
138
static const char mandos_protocol_version[] = "1";
139
const char *argp_program_version = "mandos-client " VERSION;
140
const char *argp_program_bug_address = "<mandos@recompile.se>";
141
static const char sys_class_net[] = "/sys/class/net";
142
char *connect_to = NULL;
143
const char *hookdir = HOOKDIR;
147
/* Doubly linked list that need to be circularly linked when used */
148
typedef struct server{
151
AvahiIfIndex if_index;
153
struct timespec last_seen;
158
/* Used for passing in values through the Avahi callback functions */
65
gnutls_session_t session;
161
66
gnutls_certificate_credentials_t cred;
162
unsigned int dh_bits;
163
67
gnutls_dh_params_t dh_params;
164
const char *priority;
71
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
72
gpgme_data_t dh_crypto, dh_plain;
166
server *current_server;
168
size_t interfaces_size;
171
/* global so signal handler can reach it*/
172
AvahiSimplePoll *simple_poll;
174
sig_atomic_t quit_now = 0;
175
int signal_received = 0;
177
/* Function to use when printing errors */
178
void perror_plus(const char *print_text){
180
fprintf(stderr, "Mandos plugin %s: ",
181
program_invocation_short_name);
186
__attribute__((format (gnu_printf, 2, 3)))
187
int fprintf_plus(FILE *stream, const char *format, ...){
189
va_start (ap, format);
191
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
192
program_invocation_short_name));
193
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
197
* Make additional room in "buffer" for at least BUFFER_SIZE more
198
* bytes. "buffer_capacity" is how much is currently allocated,
199
* "buffer_length" is how much is already used.
201
size_t incbuffer(char **buffer, size_t buffer_length,
202
size_t buffer_capacity){
203
if(buffer_length + BUFFER_SIZE > buffer_capacity){
204
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
206
int old_errno = errno;
213
buffer_capacity += BUFFER_SIZE;
215
return buffer_capacity;
218
/* Add server to set of servers to retry periodically */
219
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
220
int af, server **current_server){
222
server *new_server = malloc(sizeof(server));
223
if(new_server == NULL){
224
perror_plus("malloc");
227
*new_server = (server){ .ip = strdup(ip),
229
.if_index = if_index,
231
if(new_server->ip == NULL){
232
perror_plus("strdup");
235
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
237
perror_plus("clock_gettime");
240
/* Special case of first server */
241
if(*current_server == NULL){
242
new_server->next = new_server;
243
new_server->prev = new_server;
244
*current_server = new_server;
245
/* Place the new server last in the list */
247
new_server->next = *current_server;
248
new_server->prev = (*current_server)->prev;
249
new_server->prev->next = new_server;
250
(*current_server)->prev = new_server;
258
static bool init_gpgme(const char *seckey, const char *pubkey,
259
const char *tempdir, mandos_context *mc){
76
size_t new_packet_capacity = 0;
77
size_t new_packet_length = 0;
261
78
gpgme_engine_info_t engine_info;
264
* Helper function to insert pub and seckey to the engine keyring.
266
bool import_key(const char *filename){
269
gpgme_data_t pgp_data;
271
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
277
rc = gpgme_data_new_from_fd(&pgp_data, fd);
278
if(rc != GPG_ERR_NO_ERROR){
279
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
280
gpgme_strsource(rc), gpgme_strerror(rc));
284
rc = gpgme_op_import(mc->ctx, pgp_data);
285
if(rc != GPG_ERR_NO_ERROR){
286
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
287
gpgme_strsource(rc), gpgme_strerror(rc));
291
ret = (int)TEMP_FAILURE_RETRY(close(fd));
293
perror_plus("close");
295
gpgme_data_release(pgp_data);
300
fprintf_plus(stderr, "Initializing GPGME\n");
81
fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
304
85
gpgme_check_version(NULL);
305
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
306
if(rc != GPG_ERR_NO_ERROR){
307
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
308
gpgme_strsource(rc), gpgme_strerror(rc));
86
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
312
/* Set GPGME home directory for the OpenPGP engine only */
313
rc = gpgme_get_engine_info(&engine_info);
314
if(rc != GPG_ERR_NO_ERROR){
315
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
316
gpgme_strsource(rc), gpgme_strerror(rc));
88
/* Set GPGME home directory */
89
rc = gpgme_get_engine_info (&engine_info);
90
if (rc != GPG_ERR_NO_ERROR){
91
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
92
gpgme_strsource(rc), gpgme_strerror(rc));
319
95
while(engine_info != NULL){
320
96
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
321
97
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
322
engine_info->file_name, tempdir);
98
engine_info->file_name, homedir);
325
101
engine_info = engine_info->next;
327
103
if(engine_info == NULL){
328
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
333
/* Create new GPGME "context" */
334
rc = gpgme_new(&(mc->ctx));
335
if(rc != GPG_ERR_NO_ERROR){
336
fprintf_plus(stderr, "Mandos plugin mandos-client: "
337
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
342
if(not import_key(pubkey) or not import_key(seckey)){
350
* Decrypt OpenPGP data.
351
* Returns -1 on error
353
static ssize_t pgp_packet_decrypt(const char *cryptotext,
357
gpgme_data_t dh_crypto, dh_plain;
360
size_t plaintext_capacity = 0;
361
ssize_t plaintext_length = 0;
364
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
367
/* Create new GPGME data buffer from memory cryptotext */
368
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
370
if(rc != GPG_ERR_NO_ERROR){
371
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
372
gpgme_strsource(rc), gpgme_strerror(rc));
104
fprintf(stderr, "Could not set home dir to %s\n", homedir);
108
/* Create new GPGME data buffer from packet buffer */
109
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
110
if (rc != GPG_ERR_NO_ERROR){
111
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
112
gpgme_strsource(rc), gpgme_strerror(rc));
376
116
/* Create new empty GPGME data buffer for the plaintext */
377
117
rc = gpgme_data_new(&dh_plain);
378
if(rc != GPG_ERR_NO_ERROR){
379
fprintf_plus(stderr, "Mandos plugin mandos-client: "
380
"bad gpgme_data_new: %s: %s\n",
381
gpgme_strsource(rc), gpgme_strerror(rc));
382
gpgme_data_release(dh_crypto);
386
/* Decrypt data from the cryptotext data buffer to the plaintext
388
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
389
if(rc != GPG_ERR_NO_ERROR){
390
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
391
gpgme_strsource(rc), gpgme_strerror(rc));
392
plaintext_length = -1;
394
gpgme_decrypt_result_t result;
395
result = gpgme_op_decrypt_result(mc->ctx);
397
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
399
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
400
result->unsupported_algorithm);
401
fprintf_plus(stderr, "Wrong key usage: %u\n",
402
result->wrong_key_usage);
403
if(result->file_name != NULL){
404
fprintf_plus(stderr, "File name: %s\n", result->file_name);
406
gpgme_recipient_t recipient;
407
recipient = result->recipients;
118
if (rc != GPG_ERR_NO_ERROR){
119
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
120
gpgme_strsource(rc), gpgme_strerror(rc));
124
/* Create new GPGME "context" */
125
rc = gpgme_new(&ctx);
126
if (rc != GPG_ERR_NO_ERROR){
127
fprintf(stderr, "bad gpgme_new: %s: %s\n",
128
gpgme_strsource(rc), gpgme_strerror(rc));
132
/* Decrypt data from the FILE pointer to the plaintext data buffer */
133
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
134
if (rc != GPG_ERR_NO_ERROR){
135
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
136
gpgme_strsource(rc), gpgme_strerror(rc));
141
fprintf(stderr, "decryption of gpg packet succeeded\n");
145
gpgme_decrypt_result_t result;
146
result = gpgme_op_decrypt_result(ctx);
148
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
150
fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm);
151
fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage);
152
if(result->file_name != NULL){
153
fprintf(stderr, "File name: %s\n", result->file_name);
155
gpgme_recipient_t recipient;
156
recipient = result->recipients;
408
158
while(recipient != NULL){
409
fprintf_plus(stderr, "Public key algorithm: %s\n",
410
gpgme_pubkey_algo_name
411
(recipient->pubkey_algo));
412
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
413
fprintf_plus(stderr, "Secret key available: %s\n",
414
recipient->status == GPG_ERR_NO_SECKEY
159
fprintf(stderr, "Public key algorithm: %s\n",
160
gpgme_pubkey_algo_name(recipient->pubkey_algo));
161
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
162
fprintf(stderr, "Secret key available: %s\n",
163
recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
416
164
recipient = recipient->next;
424
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
170
/* Delete the GPGME FILE pointer cryptotext data buffer */
171
gpgme_data_release(dh_crypto);
427
173
/* Seek back to the beginning of the GPGME plaintext data buffer */
428
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
429
perror_plus("gpgme_data_seek");
430
plaintext_length = -1;
174
gpgme_data_seek(dh_plain, 0, SEEK_SET);
436
plaintext_capacity = incbuffer(plaintext,
437
(size_t)plaintext_length,
439
if(plaintext_capacity == 0){
440
perror_plus("incbuffer");
441
plaintext_length = -1;
178
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
179
*new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
180
if (*new_packet == NULL){
184
new_packet_capacity += BUFFER_SIZE;
445
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
187
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
447
188
/* Print the data, if any */
190
/* If password is empty, then a incorrect error will be printed */
453
perror_plus("gpgme_data_read");
454
plaintext_length = -1;
194
perror("gpgme_data_read");
457
plaintext_length += ret;
197
new_packet_length += ret;
461
fprintf_plus(stderr, "Decrypted password is: ");
462
for(ssize_t i = 0; i < plaintext_length; i++){
463
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
465
fprintf(stderr, "\n");
201
fprintf(stderr, "decrypted password is: %s\n", *new_packet);
470
/* Delete the GPGME cryptotext data buffer */
471
gpgme_data_release(dh_crypto);
473
/* Delete the GPGME plaintext data buffer */
204
/* Delete the GPGME plaintext data buffer */
474
205
gpgme_data_release(dh_plain);
475
return plaintext_length;
206
return new_packet_length;
478
static const char * safer_gnutls_strerror(int value){
479
const char *ret = gnutls_strerror(value);
209
static const char * safer_gnutls_strerror (int value) {
210
const char *ret = gnutls_strerror (value);
481
212
ret = "(unknown)";
485
/* GnuTLS log function callback */
486
static void debuggnutls(__attribute__((unused)) int level,
488
fprintf_plus(stderr, "GnuTLS: %s", string);
216
void debuggnutls(int level, const char* string){
217
fprintf(stderr, "%s", string);
491
static int init_gnutls_global(const char *pubkeyfilename,
492
const char *seckeyfilename,
220
int initgnutls(encrypted_session *es){
497
fprintf_plus(stderr, "Initializing GnuTLS\n");
225
fprintf(stderr, "Initializing gnutls\n");
500
ret = gnutls_global_init();
501
if(ret != GNUTLS_E_SUCCESS){
502
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
503
safer_gnutls_strerror(ret));
229
if ((ret = gnutls_global_init ())
230
!= GNUTLS_E_SUCCESS) {
231
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
508
/* "Use a log level over 10 to enable all debugging options."
511
236
gnutls_global_set_log_level(11);
512
237
gnutls_global_set_log_function(debuggnutls);
515
/* OpenPGP credentials */
516
ret = gnutls_certificate_allocate_credentials(&mc->cred);
517
if(ret != GNUTLS_E_SUCCESS){
518
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
519
safer_gnutls_strerror(ret));
520
gnutls_global_deinit();
241
/* openpgp credentials */
242
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
243
!= GNUTLS_E_SUCCESS) {
244
fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
525
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
526
" secret key %s as GnuTLS credentials\n",
249
fprintf(stderr, "Attempting to use openpgp certificate %s"
250
" and keyfile %s as gnutls credentials\n", CERTFILE, KEYFILE);
531
253
ret = gnutls_certificate_set_openpgp_key_file
532
(mc->cred, pubkeyfilename, seckeyfilename,
533
GNUTLS_OPENPGP_FMT_BASE64);
534
if(ret != GNUTLS_E_SUCCESS){
536
"Error[%d] while reading the OpenPGP key pair ('%s',"
537
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
538
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
539
safer_gnutls_strerror(ret));
543
/* GnuTLS server initialization */
544
ret = gnutls_dh_params_init(&mc->dh_params);
545
if(ret != GNUTLS_E_SUCCESS){
546
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
547
" initialization: %s\n",
548
safer_gnutls_strerror(ret));
551
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
552
if(ret != GNUTLS_E_SUCCESS){
553
fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
554
safer_gnutls_strerror(ret));
558
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
564
gnutls_certificate_free_credentials(mc->cred);
565
gnutls_global_deinit();
566
gnutls_dh_params_deinit(mc->dh_params);
570
static int init_gnutls_session(gnutls_session_t *session,
573
/* GnuTLS session creation */
575
ret = gnutls_init(session, GNUTLS_SERVER);
579
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
580
if(ret != GNUTLS_E_SUCCESS){
582
"Error in GnuTLS session initialization: %s\n",
583
safer_gnutls_strerror(ret));
589
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
591
gnutls_deinit(*session);
594
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
595
if(ret != GNUTLS_E_SUCCESS){
596
fprintf_plus(stderr, "Syntax error at: %s\n", err);
597
fprintf_plus(stderr, "GnuTLS error: %s\n",
598
safer_gnutls_strerror(ret));
599
gnutls_deinit(*session);
605
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
608
gnutls_deinit(*session);
611
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
612
if(ret != GNUTLS_E_SUCCESS){
613
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
614
safer_gnutls_strerror(ret));
615
gnutls_deinit(*session);
254
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
255
if (ret != GNUTLS_E_SUCCESS) {
257
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
258
ret, CERTFILE, KEYFILE);
259
fprintf(stdout, "The Error is: %s\n",
260
safer_gnutls_strerror(ret));
264
//Gnutls server initialization
265
if ((ret = gnutls_dh_params_init (&es->dh_params))
266
!= GNUTLS_E_SUCCESS) {
267
fprintf (stderr, "Error in dh parameter initialization: %s\n",
268
safer_gnutls_strerror(ret));
272
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
273
!= GNUTLS_E_SUCCESS) {
274
fprintf (stderr, "Error in prime generation: %s\n",
275
safer_gnutls_strerror(ret));
279
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
281
// Gnutls session creation
282
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
283
!= GNUTLS_E_SUCCESS){
284
fprintf(stderr, "Error in gnutls session initialization: %s\n",
285
safer_gnutls_strerror(ret));
288
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
289
!= GNUTLS_E_SUCCESS) {
290
fprintf(stderr, "Syntax error at: %s\n", err);
291
fprintf(stderr, "Gnutls error: %s\n",
292
safer_gnutls_strerror(ret));
296
if ((ret = gnutls_credentials_set
297
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
298
!= GNUTLS_E_SUCCESS) {
299
fprintf(stderr, "Error setting a credentials set: %s\n",
300
safer_gnutls_strerror(ret));
619
304
/* ignore client certificate if any. */
620
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
305
gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
622
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
307
gnutls_dh_set_prime_bits (es->session, DH_BITS);
627
/* Avahi log function callback */
628
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
629
__attribute__((unused)) const char *txt){}
312
void empty_log(AvahiLogLevel level, const char *txt){}
631
/* Called when a Mandos server is found */
632
static int start_mandos_communication(const char *ip, in_port_t port,
633
AvahiIfIndex if_index,
634
int af, mandos_context *mc){
635
int ret, tcp_sd = -1;
637
struct sockaddr_storage to;
314
int start_mandos_communication(char *ip, uint16_t port){
316
struct sockaddr_in6 to;
317
encrypted_session es;
638
318
char *buffer = NULL;
639
char *decrypted_buffer = NULL;
319
char *decrypted_buffer;
640
320
size_t buffer_length = 0;
641
321
size_t buffer_capacity = 0;
644
gnutls_session_t session;
645
int pf; /* Protocol family */
662
fprintf_plus(stderr, "Bad address family: %d\n", af);
667
/* If the interface is specified and we have a list of interfaces */
668
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
669
/* Check if the interface is one of the interfaces we are using */
672
char *interface = NULL;
673
while((interface=argz_next(mc->interfaces, mc->interfaces_size,
675
if(if_nametoindex(interface) == (unsigned int)if_index){
682
/* This interface does not match any in the list, so we don't
683
connect to the server */
685
char interface[IF_NAMESIZE];
686
if(if_indextoname((unsigned int)if_index, interface) == NULL){
687
perror_plus("if_indextoname");
689
fprintf_plus(stderr, "Skipping server on non-used interface"
691
if_indextoname((unsigned int)if_index,
699
ret = init_gnutls_session(&session, mc);
705
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
706
PRIuMAX "\n", ip, (uintmax_t)port);
709
tcp_sd = socket(pf, SOCK_STREAM, 0);
712
perror_plus("socket");
722
memset(&to, 0, sizeof(to));
724
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
725
ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
727
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
728
ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
732
perror_plus("inet_pton");
322
ssize_t decrypted_buffer_size;
324
const char interface[] = "eth0";
327
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
330
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
337
fprintf(stderr, "Binding to interface %s\n", interface);
340
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
342
perror("setsockopt bindtodevice");
346
memset(&to,0,sizeof(to));
347
to.sin6_family = AF_INET6;
348
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
738
fprintf_plus(stderr, "Bad address: %s\n", ip);
743
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
744
if(IN6_IS_ADDR_LINKLOCAL
745
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
746
if(if_index == AVAHI_IF_UNSPEC){
747
fprintf_plus(stderr, "An IPv6 link-local address is"
748
" incomplete without a network interface\n");
752
/* Set the network interface number as scope */
753
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
756
((struct sockaddr_in *)&to)->sin_port = htons(port);
765
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
766
char interface[IF_NAMESIZE];
767
if(if_indextoname((unsigned int)if_index, interface) == NULL){
768
perror_plus("if_indextoname");
770
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
771
"\n", ip, interface, (uintmax_t)port);
774
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
775
ip, (uintmax_t)port);
777
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
778
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
780
ret = getnameinfo((struct sockaddr *)&to,
781
sizeof(struct sockaddr_in6),
782
addrstr, sizeof(addrstr), NULL, 0,
785
ret = getnameinfo((struct sockaddr *)&to,
786
sizeof(struct sockaddr_in),
787
addrstr, sizeof(addrstr), NULL, 0,
790
if(ret == EAI_SYSTEM){
791
perror_plus("getnameinfo");
792
} else if(ret != 0) {
793
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
794
} else if(strcmp(addrstr, ip) != 0){
795
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
805
ret = connect(tcp_sd, (struct sockaddr *)&to,
806
sizeof(struct sockaddr_in6));
808
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
809
sizeof(struct sockaddr_in));
812
if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
814
perror_plus("connect");
825
const char *out = mandos_protocol_version;
828
size_t out_size = strlen(out);
829
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
830
out_size - written));
833
perror_plus("write");
837
written += (size_t)ret;
838
if(written < out_size){
841
if(out == mandos_protocol_version){
856
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
864
/* This casting via intptr_t is to eliminate warning about casting
865
an int to a pointer type. This is exactly how the GnuTLS Guile
866
function "set-session-transport-fd!" does it. */
867
gnutls_transport_set_ptr(session,
868
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
876
ret = gnutls_handshake(session);
881
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
883
if(ret != GNUTLS_E_SUCCESS){
885
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
892
/* Read OpenPGP packet that contains the wanted password */
895
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
906
buffer_capacity = incbuffer(&buffer, buffer_length,
908
if(buffer_capacity == 0){
910
perror_plus("incbuffer");
920
sret = gnutls_record_recv(session, buffer+buffer_length,
354
fprintf(stderr, "Bad address: %s\n", ip);
357
to.sin6_port = htons(port);
358
to.sin6_scope_id = if_nametoindex(interface);
361
fprintf(stderr, "Connection to: %s\n", ip);
364
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
370
ret = initgnutls (&es);
377
gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
380
fprintf(stderr, "Establishing tls session with %s\n", ip);
384
ret = gnutls_handshake (es.session);
386
if (ret != GNUTLS_E_SUCCESS){
387
fprintf(stderr, "\n*** Handshake failed ***\n");
393
//Retrieve gpg packet that contains the wanted password
396
fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip);
400
if (buffer_length + BUFFER_SIZE > buffer_capacity){
401
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
406
buffer_capacity += BUFFER_SIZE;
409
ret = gnutls_record_recv
410
(es.session, buffer+buffer_length, BUFFER_SIZE);
927
416
case GNUTLS_E_INTERRUPTED:
928
417
case GNUTLS_E_AGAIN:
930
419
case GNUTLS_E_REHANDSHAKE:
932
ret = gnutls_handshake(session);
938
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
940
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
420
ret = gnutls_handshake (es.session);
422
fprintf(stderr, "\n*** Handshake failed ***\n");
948
fprintf_plus(stderr, "Unknown error while reading data from"
949
" encrypted session with Mandos server\n");
950
gnutls_bye(session, GNUTLS_SHUT_RDWR);
955
buffer_length += (size_t) sret;
960
fprintf_plus(stderr, "Closing TLS session\n");
969
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
974
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
976
if(buffer_length > 0){
977
ssize_t decrypted_buffer_size;
978
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
979
&decrypted_buffer, mc);
980
if(decrypted_buffer_size >= 0){
983
while(written < (size_t) decrypted_buffer_size){
989
ret = (int)fwrite(decrypted_buffer + written, 1,
990
(size_t)decrypted_buffer_size - written,
992
if(ret == 0 and ferror(stdout)){
995
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1001
written += (size_t)ret;
1007
/* Shutdown procedure */
1012
free(decrypted_buffer);
1015
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1021
perror_plus("close");
1023
gnutls_deinit(session);
429
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
431
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
435
buffer_length += ret;
439
if (buffer_length > 0){
440
if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) >= 0){
441
fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
442
free(decrypted_buffer);
451
fprintf(stderr, "Closing tls session\n");
455
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
458
gnutls_deinit (es.session);
459
gnutls_certificate_free_credentials (es.cred);
460
gnutls_global_deinit ();
1033
static void resolve_callback(AvahiSServiceResolver *r,
1034
AvahiIfIndex interface,
1035
AvahiProtocol proto,
1036
AvahiResolverEvent event,
1040
const char *host_name,
1041
const AvahiAddress *address,
1043
AVAHI_GCC_UNUSED AvahiStringList *txt,
1044
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1051
/* Called whenever a service has been resolved successfully or
1060
case AVAHI_RESOLVER_FAILURE:
1061
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1062
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1064
avahi_strerror(avahi_server_errno
1065
(((mandos_context*)mc)->server)));
1068
case AVAHI_RESOLVER_FOUND:
1070
char ip[AVAHI_ADDRESS_STR_MAX];
1071
avahi_address_snprint(ip, sizeof(ip), address);
1073
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1074
PRIdMAX ") on port %" PRIu16 "\n", name,
1075
host_name, ip, (intmax_t)interface, port);
1077
int ret = start_mandos_communication(ip, (in_port_t)port,
1079
avahi_proto_to_af(proto),
1082
avahi_simple_poll_quit(simple_poll);
1084
if(not add_server(ip, (in_port_t)port, interface,
1085
avahi_proto_to_af(proto),
1086
&((mandos_context*)mc)->current_server)){
1087
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1093
avahi_s_service_resolver_free(r);
1096
static void browse_callback(AvahiSServiceBrowser *b,
1097
AvahiIfIndex interface,
1098
AvahiProtocol protocol,
1099
AvahiBrowserEvent event,
1103
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1110
/* Called whenever a new services becomes available on the LAN or
1111
is removed from the LAN */
1119
case AVAHI_BROWSER_FAILURE:
1121
fprintf_plus(stderr, "(Avahi browser) %s\n",
1122
avahi_strerror(avahi_server_errno
1123
(((mandos_context*)mc)->server)));
1124
avahi_simple_poll_quit(simple_poll);
1127
case AVAHI_BROWSER_NEW:
1128
/* We ignore the returned Avahi resolver object. In the callback
1129
function we free it. If the Avahi server is terminated before
1130
the callback function is called the Avahi server will free the
1133
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1134
interface, protocol, name, type,
1135
domain, protocol, 0,
1136
resolve_callback, mc) == NULL)
1137
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1139
avahi_strerror(avahi_server_errno
1140
(((mandos_context*)mc)->server)));
1143
case AVAHI_BROWSER_REMOVE:
1146
case AVAHI_BROWSER_ALL_FOR_NOW:
1147
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1149
fprintf_plus(stderr, "No Mandos server found, still"
1156
/* Signal handler that stops main loop after SIGTERM */
1157
static void handle_sigterm(int sig){
1162
signal_received = sig;
1163
int old_errno = errno;
1164
/* set main loop to exit */
1165
if(simple_poll != NULL){
1166
avahi_simple_poll_quit(simple_poll);
1171
bool get_flags(const char *ifname, struct ifreq *ifr){
1175
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1178
perror_plus("socket");
1182
strcpy(ifr->ifr_name, ifname);
1183
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1187
perror_plus("ioctl SIOCGIFFLAGS");
1195
bool good_flags(const char *ifname, const struct ifreq *ifr){
1197
/* Reject the loopback device */
1198
if(ifr->ifr_flags & IFF_LOOPBACK){
1200
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1205
/* Accept point-to-point devices only if connect_to is specified */
1206
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1208
fprintf_plus(stderr, "Accepting point-to-point interface"
1209
" \"%s\"\n", ifname);
1213
/* Otherwise, reject non-broadcast-capable devices */
1214
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1216
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1217
" \"%s\"\n", ifname);
1221
/* Reject non-ARP interfaces (including dummy interfaces) */
1222
if(ifr->ifr_flags & IFF_NOARP){
1224
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1230
/* Accept this device */
1232
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1238
* This function determines if a directory entry in /sys/class/net
1239
* corresponds to an acceptable network device.
1240
* (This function is passed to scandir(3) as a filter function.)
1242
int good_interface(const struct dirent *if_entry){
1243
if(if_entry->d_name[0] == '.'){
1248
if(not get_flags(if_entry->d_name, &ifr)){
1250
fprintf_plus(stderr, "Failed to get flags for interface "
1251
"\"%s\"\n", if_entry->d_name);
1256
if(not good_flags(if_entry->d_name, &ifr)){
1263
* This function determines if a network interface is up.
1265
bool interface_is_up(const char *interface){
1267
if(not get_flags(interface, &ifr)){
1269
fprintf_plus(stderr, "Failed to get flags for interface "
1270
"\"%s\"\n", interface);
1275
return (bool)(ifr.ifr_flags & IFF_UP);
1279
* This function determines if a network interface is running
1281
bool interface_is_running(const char *interface){
1283
if(not get_flags(interface, &ifr)){
1285
fprintf_plus(stderr, "Failed to get flags for interface "
1286
"\"%s\"\n", interface);
1291
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1294
int notdotentries(const struct dirent *direntry){
1295
/* Skip "." and ".." */
1296
if(direntry->d_name[0] == '.'
1297
and (direntry->d_name[1] == '\0'
1298
or (direntry->d_name[1] == '.'
1299
and direntry->d_name[2] == '\0'))){
1305
/* Is this directory entry a runnable program? */
1306
int runnable_hook(const struct dirent *direntry){
1311
if((direntry->d_name)[0] == '\0'){
1316
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1317
"abcdefghijklmnopqrstuvwxyz"
1320
if((direntry->d_name)[sret] != '\0'){
1321
/* Contains non-allowed characters */
1323
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1329
char *fullname = NULL;
1330
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1332
perror_plus("asprintf");
1336
ret = stat(fullname, &st);
1339
perror_plus("Could not stat hook");
1343
if(not (S_ISREG(st.st_mode))){
1344
/* Not a regular file */
1346
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1351
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1352
/* Not executable */
1354
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1360
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1366
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1367
mandos_context *mc){
1369
struct timespec now;
1370
struct timespec waited_time;
1371
intmax_t block_time;
1374
if(mc->current_server == NULL){
1376
fprintf_plus(stderr, "Wait until first server is found."
1379
ret = avahi_simple_poll_iterate(s, -1);
1382
fprintf_plus(stderr, "Check current_server if we should run"
1385
/* the current time */
1386
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1388
perror_plus("clock_gettime");
1391
/* Calculating in ms how long time between now and server
1392
who we visted longest time ago. Now - last seen. */
1393
waited_time.tv_sec = (now.tv_sec
1394
- mc->current_server->last_seen.tv_sec);
1395
waited_time.tv_nsec = (now.tv_nsec
1396
- mc->current_server->last_seen.tv_nsec);
1397
/* total time is 10s/10,000ms.
1398
Converting to s from ms by dividing by 1,000,
1399
and ns to ms by dividing by 1,000,000. */
1400
block_time = ((retry_interval
1401
- ((intmax_t)waited_time.tv_sec * 1000))
1402
- ((intmax_t)waited_time.tv_nsec / 1000000));
1405
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1409
if(block_time <= 0){
1410
ret = start_mandos_communication(mc->current_server->ip,
1411
mc->current_server->port,
1412
mc->current_server->if_index,
1413
mc->current_server->af, mc);
1415
avahi_simple_poll_quit(s);
1418
ret = clock_gettime(CLOCK_MONOTONIC,
1419
&mc->current_server->last_seen);
1421
perror_plus("clock_gettime");
1424
mc->current_server = mc->current_server->next;
1425
block_time = 0; /* Call avahi to find new Mandos
1426
servers, but don't block */
1429
ret = avahi_simple_poll_iterate(s, (int)block_time);
1432
if (ret > 0 or errno != EINTR){
1433
return (ret != 1) ? ret : 0;
1439
/* Set effective uid to 0, return errno */
1440
error_t raise_privileges(void){
1441
error_t old_errno = errno;
1442
error_t ret_errno = 0;
1443
if(seteuid(0) == -1){
1445
perror_plus("seteuid");
1451
/* Set effective and real user ID to 0. Return errno. */
1452
error_t raise_privileges_permanently(void){
1453
error_t old_errno = errno;
1454
error_t ret_errno = raise_privileges();
1459
if(setuid(0) == -1){
1461
perror_plus("seteuid");
1467
/* Set effective user ID to unprivileged saved user ID */
1468
error_t lower_privileges(void){
1469
error_t old_errno = errno;
1470
error_t ret_errno = 0;
1471
if(seteuid(uid) == -1){
1473
perror_plus("seteuid");
1479
/* Lower privileges permanently */
1480
error_t lower_privileges_permanently(void){
1481
error_t old_errno = errno;
1482
error_t ret_errno = 0;
1483
if(setuid(uid) == -1){
1485
perror_plus("setuid");
1491
bool run_network_hooks(const char *mode, const char *interface,
1493
struct dirent **direntries;
1494
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1497
if(errno == ENOENT){
1499
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1500
" found\n", hookdir);
1503
perror_plus("scandir");
1506
struct dirent *direntry;
1508
int devnull = open("/dev/null", O_RDONLY);
1509
for(int i = 0; i < numhooks; i++){
1510
direntry = direntries[i];
1511
char *fullname = NULL;
1512
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1514
perror_plus("asprintf");
1518
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1521
pid_t hook_pid = fork();
1524
/* Raise privileges */
1525
raise_privileges_permanently();
1530
perror_plus("setgid");
1532
/* Reset supplementary groups */
1534
ret = setgroups(0, NULL);
1536
perror_plus("setgroups");
1538
dup2(devnull, STDIN_FILENO);
1540
dup2(STDERR_FILENO, STDOUT_FILENO);
1541
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1543
perror_plus("setenv");
1546
ret = setenv("DEVICE", interface, 1);
1548
perror_plus("setenv");
1551
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1553
perror_plus("setenv");
1556
ret = setenv("MODE", mode, 1);
1558
perror_plus("setenv");
1562
ret = asprintf(&delaystring, "%f", delay);
1564
perror_plus("asprintf");
1567
ret = setenv("DELAY", delaystring, 1);
1570
perror_plus("setenv");
1574
if(connect_to != NULL){
1575
ret = setenv("CONNECT", connect_to, 1);
1577
perror_plus("setenv");
1581
if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1582
perror_plus("execl");
1583
_exit(EXIT_FAILURE);
1587
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1588
perror_plus("waitpid");
1592
if(WIFEXITED(status)){
1593
if(WEXITSTATUS(status) != 0){
1594
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1595
" with status %d\n", direntry->d_name,
1596
WEXITSTATUS(status));
1600
} else if(WIFSIGNALED(status)){
1601
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1602
" signal %d\n", direntry->d_name,
1607
fprintf_plus(stderr, "Warning: network hook \"%s\""
1608
" crashed\n", direntry->d_name);
1615
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1624
error_t bring_up_interface(const char *const interface,
1627
error_t old_errno = errno;
1628
error_t ret_errno = 0;
1629
int ret, ret_setflags;
1630
struct ifreq network;
1631
unsigned int if_index = if_nametoindex(interface);
1633
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1643
if(not interface_is_up(interface)){
1644
if(not get_flags(interface, &network) and debug){
1646
fprintf_plus(stderr, "Failed to get flags for interface "
1647
"\"%s\"\n", interface);
1650
network.ifr_flags |= IFF_UP;
1652
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1655
perror_plus("socket");
1667
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
1671
/* Raise priviliges */
1675
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1676
messages about the network interface to mess up the prompt */
1677
int ret_linux = klogctl(8, NULL, 5);
1678
bool restore_loglevel = true;
1679
if(ret_linux == -1){
1680
restore_loglevel = false;
1681
perror_plus("klogctl");
1683
#endif /* __linux__ */
1684
ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1687
if(restore_loglevel){
1688
ret_linux = klogctl(7, NULL, 0);
1689
if(ret_linux == -1){
1690
perror_plus("klogctl");
1693
#endif /* __linux__ */
1695
/* Lower privileges */
1698
/* Close the socket */
1699
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1701
perror_plus("close");
1704
if(ret_setflags == -1){
1706
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1711
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1715
/* Sleep checking until interface is running.
1716
Check every 0.25s, up to total time of delay */
1717
for(int i=0; i < delay * 4; i++){
1718
if(interface_is_running(interface)){
1721
struct timespec sleeptime = { .tv_nsec = 250000000 };
1722
ret = nanosleep(&sleeptime, NULL);
1723
if(ret == -1 and errno != EINTR){
1724
perror_plus("nanosleep");
1732
error_t take_down_interface(const char *const interface){
1733
error_t old_errno = errno;
1734
struct ifreq network;
1735
unsigned int if_index = if_nametoindex(interface);
1737
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1741
if(interface_is_up(interface)){
1742
error_t ret_errno = 0;
1743
if(not get_flags(interface, &network) and debug){
1745
fprintf_plus(stderr, "Failed to get flags for interface "
1746
"\"%s\"\n", interface);
1749
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1751
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1754
perror_plus("socket");
1760
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
1764
/* Raise priviliges */
1767
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1770
/* Lower privileges */
1773
/* Close the socket */
1774
int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1776
perror_plus("close");
1779
if(ret_setflags == -1){
1781
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1786
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1794
int main(int argc, char *argv[]){
1795
mandos_context mc = { .server = NULL, .dh_bits = 1024,
1796
.priority = "SECURE256:!CTYPE-X.509:"
1797
"+CTYPE-OPENPGP", .current_server = NULL,
1798
.interfaces = NULL, .interfaces_size = 0 };
1799
AvahiSServiceBrowser *sb = NULL;
1804
int exitcode = EXIT_SUCCESS;
1805
char *interfaces_to_take_down = NULL;
1806
size_t interfaces_to_take_down_size = 0;
1807
char tempdir[] = "/tmp/mandosXXXXXX";
1808
bool tempdir_created = false;
1809
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1810
const char *seckey = PATHDIR "/" SECKEY;
1811
const char *pubkey = PATHDIR "/" PUBKEY;
1812
char *interfaces_hooks = NULL;
1814
bool gnutls_initialized = false;
1815
bool gpgme_initialized = false;
1817
double retry_interval = 10; /* 10s between trying a server and
1818
retrying the same server again */
1820
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1821
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1826
/* Lower any group privileges we might have, just to be safe */
1830
perror_plus("setgid");
1833
/* Lower user privileges (temporarily) */
1837
perror_plus("seteuid");
1845
struct argp_option options[] = {
1846
{ .name = "debug", .key = 128,
1847
.doc = "Debug mode", .group = 3 },
1848
{ .name = "connect", .key = 'c',
1849
.arg = "ADDRESS:PORT",
1850
.doc = "Connect directly to a specific Mandos server",
1852
{ .name = "interface", .key = 'i',
1854
.doc = "Network interface that will be used to search for"
1857
{ .name = "seckey", .key = 's',
1859
.doc = "OpenPGP secret key file base name",
1861
{ .name = "pubkey", .key = 'p',
1863
.doc = "OpenPGP public key file base name",
1865
{ .name = "dh-bits", .key = 129,
1867
.doc = "Bit length of the prime number used in the"
1868
" Diffie-Hellman key exchange",
1870
{ .name = "priority", .key = 130,
1872
.doc = "GnuTLS priority string for the TLS handshake",
1874
{ .name = "delay", .key = 131,
1876
.doc = "Maximum delay to wait for interface startup",
1878
{ .name = "retry", .key = 132,
1880
.doc = "Retry interval used when denied by the Mandos server",
1882
{ .name = "network-hook-dir", .key = 133,
1884
.doc = "Directory where network hooks are located",
1887
* These reproduce what we would get without ARGP_NO_HELP
1889
{ .name = "help", .key = '?',
1890
.doc = "Give this help list", .group = -1 },
1891
{ .name = "usage", .key = -3,
1892
.doc = "Give a short usage message", .group = -1 },
1893
{ .name = "version", .key = 'V',
1894
.doc = "Print program version", .group = -1 },
1898
error_t parse_opt(int key, char *arg,
1899
struct argp_state *state){
1902
case 128: /* --debug */
1905
case 'c': /* --connect */
1908
case 'i': /* --interface */
1909
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
1912
argp_error(state, "%s", strerror(ret_errno));
1915
case 's': /* --seckey */
1918
case 'p': /* --pubkey */
1921
case 129: /* --dh-bits */
1923
tmpmax = strtoimax(arg, &tmp, 10);
1924
if(errno != 0 or tmp == arg or *tmp != '\0'
1925
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1926
argp_error(state, "Bad number of DH bits");
1928
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1930
case 130: /* --priority */
1933
case 131: /* --delay */
1935
delay = strtof(arg, &tmp);
1936
if(errno != 0 or tmp == arg or *tmp != '\0'){
1937
argp_error(state, "Bad delay");
1939
case 132: /* --retry */
1941
retry_interval = strtod(arg, &tmp);
1942
if(errno != 0 or tmp == arg or *tmp != '\0'
1943
or (retry_interval * 1000) > INT_MAX
1944
or retry_interval < 0){
1945
argp_error(state, "Bad retry interval");
1948
case 133: /* --network-hook-dir */
1952
* These reproduce what we would get without ARGP_NO_HELP
1954
case '?': /* --help */
1955
argp_state_help(state, state->out_stream,
1956
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1957
& ~(unsigned int)ARGP_HELP_EXIT_OK);
1958
case -3: /* --usage */
1959
argp_state_help(state, state->out_stream,
1960
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1961
case 'V': /* --version */
1962
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
1963
exit(argp_err_exit_status);
1966
return ARGP_ERR_UNKNOWN;
1971
struct argp argp = { .options = options, .parser = parse_opt,
1973
.doc = "Mandos client -- Get and decrypt"
1974
" passwords from a Mandos server" };
1975
ret = argp_parse(&argp, argc, argv,
1976
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
1983
perror_plus("argp_parse");
1984
exitcode = EX_OSERR;
1987
exitcode = EX_USAGE;
1993
/* Work around Debian bug #633582:
1994
<http://bugs.debian.org/633582> */
1996
/* Re-raise priviliges */
1997
if(raise_privileges() == 0){
2000
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2001
int seckey_fd = open(seckey, O_RDONLY);
2002
if(seckey_fd == -1){
2003
perror_plus("open");
2005
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2007
perror_plus("fstat");
2009
if(S_ISREG(st.st_mode)
2010
and st.st_uid == 0 and st.st_gid == 0){
2011
ret = fchown(seckey_fd, uid, gid);
2013
perror_plus("fchown");
2017
TEMP_FAILURE_RETRY(close(seckey_fd));
2021
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2022
int pubkey_fd = open(pubkey, O_RDONLY);
2023
if(pubkey_fd == -1){
2024
perror_plus("open");
2026
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2028
perror_plus("fstat");
2030
if(S_ISREG(st.st_mode)
2031
and st.st_uid == 0 and st.st_gid == 0){
2032
ret = fchown(pubkey_fd, uid, gid);
2034
perror_plus("fchown");
2038
TEMP_FAILURE_RETRY(close(pubkey_fd));
2042
/* Lower privileges */
2047
/* Remove invalid interface names (except "none") */
2049
char *interface = NULL;
2050
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2052
if(strcmp(interface, "none") != 0
2053
and if_nametoindex(interface) == 0){
2054
if(interface[0] != '\0'){
2055
fprintf_plus(stderr, "Not using nonexisting interface"
2056
" \"%s\"\n", interface);
2058
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2064
/* Run network hooks */
2066
if(mc.interfaces != NULL){
2067
interfaces_hooks = malloc(mc.interfaces_size);
2068
if(interfaces_hooks == NULL){
2069
perror_plus("malloc");
2072
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2073
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2075
if(not run_network_hooks("start", interfaces_hooks != NULL ?
2076
interfaces_hooks : "", delay)){
2082
avahi_set_log_function(empty_log);
2085
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2086
from the signal handler */
2087
/* Initialize the pseudo-RNG for Avahi */
2088
srand((unsigned int) time(NULL));
2089
simple_poll = avahi_simple_poll_new();
2090
if(simple_poll == NULL){
2091
fprintf_plus(stderr,
2092
"Avahi: Failed to create simple poll object.\n");
2093
exitcode = EX_UNAVAILABLE;
2097
sigemptyset(&sigterm_action.sa_mask);
2098
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2100
perror_plus("sigaddset");
2101
exitcode = EX_OSERR;
2104
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2106
perror_plus("sigaddset");
2107
exitcode = EX_OSERR;
2110
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2112
perror_plus("sigaddset");
2113
exitcode = EX_OSERR;
2116
/* Need to check if the handler is SIG_IGN before handling:
2117
| [[info:libc:Initial Signal Actions]] |
2118
| [[info:libc:Basic Signal Handling]] |
2120
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2122
perror_plus("sigaction");
2125
if(old_sigterm_action.sa_handler != SIG_IGN){
2126
ret = sigaction(SIGINT, &sigterm_action, NULL);
2128
perror_plus("sigaction");
2129
exitcode = EX_OSERR;
2133
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2135
perror_plus("sigaction");
2138
if(old_sigterm_action.sa_handler != SIG_IGN){
2139
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2141
perror_plus("sigaction");
2142
exitcode = EX_OSERR;
2146
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2148
perror_plus("sigaction");
2151
if(old_sigterm_action.sa_handler != SIG_IGN){
2152
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2154
perror_plus("sigaction");
2155
exitcode = EX_OSERR;
2160
/* If no interfaces were specified, make a list */
2161
if(mc.interfaces == NULL){
2162
struct dirent **direntries;
2163
/* Look for any good interfaces */
2164
ret = scandir(sys_class_net, &direntries, good_interface,
2167
/* Add all found interfaces to interfaces list */
2168
for(int i = 0; i < ret; ++i){
2169
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2170
direntries[i]->d_name);
2173
perror_plus("argz_add");
2177
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2178
direntries[i]->d_name);
2184
fprintf_plus(stderr, "Could not find a network interface\n");
2185
exitcode = EXIT_FAILURE;
2190
/* Bring up interfaces which are down, and remove any "none"s */
2192
char *interface = NULL;
2193
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2195
/* If interface name is "none", stop bringing up interfaces.
2196
Also remove all instances of "none" from the list */
2197
if(strcmp(interface, "none") == 0){
2198
argz_delete(&mc.interfaces, &mc.interfaces_size,
2201
while((interface = argz_next(mc.interfaces,
2202
mc.interfaces_size, interface))){
2203
if(strcmp(interface, "none") == 0){
2204
argz_delete(&mc.interfaces, &mc.interfaces_size,
2211
bool interface_was_up = interface_is_up(interface);
2212
ret = bring_up_interface(interface, delay);
2213
if(not interface_was_up){
2216
perror_plus("Failed to bring up interface");
2218
ret_errno = argz_add(&interfaces_to_take_down,
2219
&interfaces_to_take_down_size,
2223
perror_plus("argz_add");
2228
if(debug and (interfaces_to_take_down == NULL)){
2229
fprintf_plus(stderr, "No interfaces were brought up\n");
2233
/* If we only got one interface, explicitly use only that one */
2234
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2236
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2239
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2246
ret = init_gnutls_global(pubkey, seckey, &mc);
2248
fprintf_plus(stderr, "init_gnutls_global failed\n");
2249
exitcode = EX_UNAVAILABLE;
2252
gnutls_initialized = true;
2259
if(mkdtemp(tempdir) == NULL){
2260
perror_plus("mkdtemp");
2263
tempdir_created = true;
2269
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2270
fprintf_plus(stderr, "init_gpgme failed\n");
2271
exitcode = EX_UNAVAILABLE;
2274
gpgme_initialized = true;
2281
if(connect_to != NULL){
2282
/* Connect directly, do not use Zeroconf */
2283
/* (Mainly meant for debugging) */
2284
char *address = strrchr(connect_to, ':');
2286
if(address == NULL){
2287
fprintf_plus(stderr, "No colon in address\n");
2288
exitcode = EX_USAGE;
2298
tmpmax = strtoimax(address+1, &tmp, 10);
2299
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2300
or tmpmax != (in_port_t)tmpmax){
2301
fprintf_plus(stderr, "Bad port number\n");
2302
exitcode = EX_USAGE;
2310
port = (in_port_t)tmpmax;
2312
/* Colon in address indicates IPv6 */
2314
if(strchr(connect_to, ':') != NULL){
2316
/* Accept [] around IPv6 address - see RFC 5952 */
2317
if(connect_to[0] == '[' and address[-1] == ']')
2325
address = connect_to;
2331
while(not quit_now){
2332
ret = start_mandos_communication(address, port, if_index, af,
2334
if(quit_now or ret == 0){
2338
fprintf_plus(stderr, "Retrying in %d seconds\n",
2339
(int)retry_interval);
2341
sleep((unsigned int)retry_interval);
2345
exitcode = EXIT_SUCCESS;
464
static AvahiSimplePoll *simple_poll = NULL;
465
static AvahiServer *server = NULL;
467
static void resolve_callback(
468
AvahiSServiceResolver *r,
469
AVAHI_GCC_UNUSED AvahiIfIndex interface,
470
AVAHI_GCC_UNUSED AvahiProtocol protocol,
471
AvahiResolverEvent event,
475
const char *host_name,
476
const AvahiAddress *address,
478
AvahiStringList *txt,
479
AvahiLookupResultFlags flags,
480
AVAHI_GCC_UNUSED void* userdata) {
484
/* Called whenever a service has been resolved successfully or timed out */
487
case AVAHI_RESOLVER_FAILURE:
488
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)));
491
case AVAHI_RESOLVER_FOUND: {
492
char ip[AVAHI_ADDRESS_STR_MAX];
493
avahi_address_snprint(ip, sizeof(ip), address);
495
fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
497
int ret = start_mandos_communication(ip, port);
505
avahi_s_service_resolver_free(r);
508
static void browse_callback(
509
AvahiSServiceBrowser *b,
510
AvahiIfIndex interface,
511
AvahiProtocol protocol,
512
AvahiBrowserEvent event,
516
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
519
AvahiServer *s = userdata;
522
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
526
case AVAHI_BROWSER_FAILURE:
528
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
529
avahi_simple_poll_quit(simple_poll);
532
case AVAHI_BROWSER_NEW:
533
/* We ignore the returned resolver object. In the callback
534
function we free it. If the server is terminated before
535
the callback function is called the server will free
536
the resolver for us. */
538
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
539
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
543
case AVAHI_BROWSER_REMOVE:
546
case AVAHI_BROWSER_ALL_FOR_NOW:
547
case AVAHI_BROWSER_CACHE_EXHAUSTED:
552
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
2356
553
AvahiServerConfig config;
2357
/* Do not publish any local Zeroconf records */
554
AvahiSServiceBrowser *sb = NULL;
555
const char db[] = "--debug";
558
int returncode = EXIT_SUCCESS;
559
char *basename = rindex(argv[0], '/');
560
if(basename == NULL){
566
char *program_name = malloc(strlen(basename) + sizeof(db));
568
if (program_name == NULL){
573
program_name[0] = '\0';
575
for (int i = 1; i < argc; i++){
576
if (not strncmp(argv[i], db, 5)){
577
strcat(strcat(strcat(program_name, db ), "="), basename);
578
if(not strcmp(argv[i], db) or not strcmp(argv[i], program_name)){
586
avahi_set_log_function(empty_log);
589
/* Initialize the psuedo-RNG */
592
/* Allocate main loop object */
593
if (!(simple_poll = avahi_simple_poll_new())) {
594
fprintf(stderr, "Failed to create simple poll object.\n");
599
/* Do not publish any local records */
2358
600
avahi_server_config_init(&config);
2359
601
config.publish_hinfo = 0;
2360
602
config.publish_addresses = 0;
2361
603
config.publish_workstation = 0;
2362
604
config.publish_domain = 0;
2364
606
/* Allocate a new server */
2365
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2366
&config, NULL, NULL, &ret_errno);
2368
/* Free the Avahi configuration data */
607
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
609
/* Free the configuration data */
2369
610
avahi_server_config_free(&config);
2372
/* Check if creating the Avahi server object succeeded */
2373
if(mc.server == NULL){
2374
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2375
avahi_strerror(ret_errno));
2376
exitcode = EX_UNAVAILABLE;
2384
/* Create the Avahi service browser */
2385
sb = avahi_s_service_browser_new(mc.server, if_index,
2386
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2387
NULL, 0, browse_callback,
2390
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2391
avahi_strerror(avahi_server_errno(mc.server)));
2392
exitcode = EX_UNAVAILABLE;
2400
/* Run the main loop */
2403
fprintf_plus(stderr, "Starting Avahi loop search\n");
2406
ret = avahi_loop_with_timeout(simple_poll,
2407
(int)(retry_interval * 1000), &mc);
2409
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2410
(ret == 0) ? "successfully" : "with error");
2416
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2419
/* Cleanup things */
2420
free(mc.interfaces);
2423
avahi_s_service_browser_free(sb);
2425
if(mc.server != NULL)
2426
avahi_server_free(mc.server);
2428
if(simple_poll != NULL)
2429
avahi_simple_poll_free(simple_poll);
2431
if(gnutls_initialized){
2432
gnutls_certificate_free_credentials(mc.cred);
2433
gnutls_global_deinit();
2434
gnutls_dh_params_deinit(mc.dh_params);
2437
if(gpgme_initialized){
2438
gpgme_release(mc.ctx);
2441
/* Cleans up the circular linked list of Mandos servers the client
2443
if(mc.current_server != NULL){
2444
mc.current_server->prev->next = NULL;
2445
while(mc.current_server != NULL){
2446
server *next = mc.current_server->next;
2447
free(mc.current_server);
2448
mc.current_server = next;
2452
/* Re-raise priviliges */
2456
/* Run network hooks */
2457
run_network_hooks("stop", interfaces_hooks != NULL ?
2458
interfaces_hooks : "", delay);
2460
/* Take down the network interfaces which were brought up */
2462
char *interface = NULL;
2463
while((interface=argz_next(interfaces_to_take_down,
2464
interfaces_to_take_down_size,
2466
ret_errno = take_down_interface(interface);
2469
perror_plus("Failed to take down interface");
2472
if(debug and (interfaces_to_take_down == NULL)){
2473
fprintf_plus(stderr, "No interfaces needed to be taken"
2478
lower_privileges_permanently();
2481
free(interfaces_to_take_down);
2482
free(interfaces_hooks);
2484
/* Removes the GPGME temp directory and all files inside */
2485
if(tempdir_created){
2486
struct dirent **direntries = NULL;
2487
struct dirent *direntry = NULL;
2488
int numentries = scandir(tempdir, &direntries, notdotentries,
2490
if (numentries > 0){
2491
for(int i = 0; i < numentries; i++){
2492
direntry = direntries[i];
2493
char *fullname = NULL;
2494
ret = asprintf(&fullname, "%s/%s", tempdir,
2497
perror_plus("asprintf");
2500
ret = remove(fullname);
2502
fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2509
/* need to clean even if 0 because man page doesn't specify */
2511
if (numentries == -1){
2512
perror_plus("scandir");
2514
ret = rmdir(tempdir);
2515
if(ret == -1 and errno != ENOENT){
2516
perror_plus("rmdir");
2521
sigemptyset(&old_sigterm_action.sa_mask);
2522
old_sigterm_action.sa_handler = SIG_DFL;
2523
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2524
&old_sigterm_action,
2527
perror_plus("sigaction");
2530
ret = raise(signal_received);
2531
} while(ret != 0 and errno == EINTR);
2533
perror_plus("raise");
2536
TEMP_FAILURE_RETRY(pause());
612
/* Check if creating the server object succeeded */
614
fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
615
returncode = EXIT_FAILURE;
619
/* Create the service browser */
620
if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
621
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
622
returncode = EXIT_FAILURE;
626
/* Run the main loop */
629
fprintf(stderr, "Starting avahi loop search\n");
632
avahi_simple_poll_loop(simple_poll);
637
fprintf(stderr, "%s exiting\n", argv[0]);
642
avahi_s_service_browser_free(sb);
645
avahi_server_free(server);
648
avahi_simple_poll_free(simple_poll);