31
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#ifndef _LARGEFILE_SOURCE
32
34
#define _LARGEFILE_SOURCE
36
#ifndef _FILE_OFFSET_BITS
33
37
#define _FILE_OFFSET_BITS 64
39
#include <net/if.h> /* if_nametoindex */
40
#include <sys/ioctl.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
40
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
42
#include <stdio.h> /* fprintf(), stderr, fwrite(),
43
stdout, ferror(), remove() */
44
#include <stdint.h> /* uint16_t, uint32_t */
45
#include <stddef.h> /* NULL, size_t, ssize_t */
46
#include <stdlib.h> /* free(), EXIT_SUCCESS, EXIT_FAILURE,
48
#include <stdbool.h> /* bool, false, true */
49
#include <string.h> /* memset(), strcmp(), strlen(),
50
strerror(), asprintf(), strcpy() */
51
#include <sys/ioctl.h> /* ioctl */
52
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
53
sockaddr_in6, PF_INET6,
54
SOCK_STREAM, uid_t, gid_t, open(),
56
#include <sys/stat.h> /* open() */
57
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
58
inet_pton(), connect() */
59
#include <fcntl.h> /* open() */
60
#include <dirent.h> /* opendir(), struct dirent, readdir()
62
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
64
#include <assert.h> /* assert() */
65
#include <errno.h> /* perror(), errno */
66
#include <time.h> /* nanosleep(), time() */
42
67
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
68
SIOCSIFFLAGS, if_indextoname(),
69
if_nametoindex(), IF_NAMESIZE */
70
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
71
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
73
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
74
getuid(), getgid(), setuid(),
76
#include <arpa/inet.h> /* inet_pton(), htons */
77
#include <iso646.h> /* not, or, and */
78
#include <argp.h> /* struct argp_option, error_t, struct
79
argp_state, struct argp,
80
argp_parse(), ARGP_KEY_ARG,
81
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
82
#include <signal.h> /* sigemptyset(), sigaddset(),
83
sigaction(), SIGTERM, sig_atomic_t,
87
#include <sys/klog.h> /* klogctl() */
88
#endif /* __linux__ */
91
/* All Avahi types, constants and functions
45
94
#include <avahi-core/core.h>
46
95
#include <avahi-core/lookup.h>
47
96
#include <avahi-core/log.h>
49
98
#include <avahi-common/malloc.h>
50
99
#include <avahi-common/error.h>
53
#include <sys/types.h> /* socket(), inet_pton() */
54
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
55
struct in6_addr, inet_pton() */
56
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
57
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
59
#include <unistd.h> /* close() */
60
#include <netinet/in.h>
61
#include <stdbool.h> /* true */
62
#include <string.h> /* memset */
63
#include <arpa/inet.h> /* inet_pton() */
64
#include <iso646.h> /* not */
67
#include <errno.h> /* perror() */
102
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
105
init_gnutls_session(),
107
#include <gnutls/openpgp.h>
108
/* gnutls_certificate_set_openpgp_key_file(),
109
GNUTLS_OPENPGP_FMT_BASE64 */
112
#include <gpgme.h> /* All GPGME types, constants and
115
GPGME_PROTOCOL_OpenPGP,
73
118
#define BUFFER_SIZE 256
75
static const char *keydir = "/conf/conf.d/mandos";
76
static const char *pubkeyfile = "pubkey.txt";
77
static const char *seckeyfile = "seckey.txt";
120
#define PATHDIR "/conf/conf.d/mandos"
121
#define SECKEY "seckey.txt"
122
#define PUBKEY "pubkey.txt"
79
124
bool debug = false;
125
static const char mandos_protocol_version[] = "1";
126
const char *argp_program_version = "mandos-client " VERSION;
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
81
/* Used for passing in values through all the callback functions */
129
/* Used for passing in values through the Avahi callback functions */
83
131
AvahiSimplePoll *simple_poll;
84
132
AvahiServer *server;
85
133
gnutls_certificate_credentials_t cred;
86
134
unsigned int dh_bits;
135
gnutls_dh_params_t dh_params;
87
136
const char *priority;
140
/* global context so signal handler can reach it*/
141
mandos_context mc = { .simple_poll = NULL, .server = NULL,
142
.dh_bits = 1024, .priority = "SECURE256"
143
":!CTYPE-X.509:+CTYPE-OPENPGP" };
146
* Make additional room in "buffer" for at least BUFFER_SIZE more
147
* bytes. "buffer_capacity" is how much is currently allocated,
148
* "buffer_length" is how much is already used.
150
size_t incbuffer(char **buffer, size_t buffer_length,
151
size_t buffer_capacity){
152
if(buffer_length + BUFFER_SIZE > buffer_capacity){
153
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
157
buffer_capacity += BUFFER_SIZE;
159
return buffer_capacity;
91
* Decrypt OpenPGP data using keyrings in HOMEDIR.
94
static ssize_t pgp_packet_decrypt (const char *cryptotext,
98
gpgme_data_t dh_crypto, dh_plain;
165
static bool init_gpgme(const char *seckey,
166
const char *pubkey, const char *tempdir){
100
167
gpgme_error_t rc;
102
ssize_t plaintext_capacity = 0;
103
ssize_t plaintext_length = 0;
104
168
gpgme_engine_info_t engine_info;
107
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
172
* Helper function to insert pub and seckey to the engine keyring.
174
bool import_key(const char *filename){
177
gpgme_data_t pgp_data;
179
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
185
rc = gpgme_data_new_from_fd(&pgp_data, fd);
186
if(rc != GPG_ERR_NO_ERROR){
187
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
188
gpgme_strsource(rc), gpgme_strerror(rc));
192
rc = gpgme_op_import(mc.ctx, pgp_data);
193
if(rc != GPG_ERR_NO_ERROR){
194
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
195
gpgme_strsource(rc), gpgme_strerror(rc));
199
ret = (int)TEMP_FAILURE_RETRY(close(fd));
203
gpgme_data_release(pgp_data);
208
fprintf(stderr, "Initializing GPGME\n");
111
212
gpgme_check_version(NULL);
112
213
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
113
if (rc != GPG_ERR_NO_ERROR){
214
if(rc != GPG_ERR_NO_ERROR){
114
215
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
115
216
gpgme_strsource(rc), gpgme_strerror(rc));
119
/* Set GPGME home directory for the OpenPGP engine only */
120
rc = gpgme_get_engine_info (&engine_info);
121
if (rc != GPG_ERR_NO_ERROR){
220
/* Set GPGME home directory for the OpenPGP engine only */
221
rc = gpgme_get_engine_info(&engine_info);
222
if(rc != GPG_ERR_NO_ERROR){
122
223
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
123
224
gpgme_strsource(rc), gpgme_strerror(rc));
126
227
while(engine_info != NULL){
127
228
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
128
229
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
129
engine_info->file_name, homedir);
230
engine_info->file_name, tempdir);
132
233
engine_info = engine_info->next;
134
235
if(engine_info == NULL){
135
fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
236
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
240
/* Create new GPGME "context" */
241
rc = gpgme_new(&(mc.ctx));
242
if(rc != GPG_ERR_NO_ERROR){
243
fprintf(stderr, "bad gpgme_new: %s: %s\n",
244
gpgme_strsource(rc), gpgme_strerror(rc));
248
if(not import_key(pubkey) or not import_key(seckey)){
256
* Decrypt OpenPGP data.
257
* Returns -1 on error
259
static ssize_t pgp_packet_decrypt(const char *cryptotext,
262
gpgme_data_t dh_crypto, dh_plain;
265
size_t plaintext_capacity = 0;
266
ssize_t plaintext_length = 0;
269
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
139
272
/* Create new GPGME data buffer from memory cryptotext */
140
273
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
142
if (rc != GPG_ERR_NO_ERROR){
275
if(rc != GPG_ERR_NO_ERROR){
143
276
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
144
277
gpgme_strsource(rc), gpgme_strerror(rc));
260
378
return plaintext_length;
263
static const char * safer_gnutls_strerror (int value) {
264
const char *ret = gnutls_strerror (value);
381
static const char * safer_gnutls_strerror(int value){
382
const char *ret = gnutls_strerror(value); /* Spurious warning from
383
-Wunreachable-code */
266
385
ret = "(unknown)";
389
/* GnuTLS log function callback */
270
390
static void debuggnutls(__attribute__((unused)) int level,
271
391
const char* string){
272
fprintf(stderr, "%s", string);
392
fprintf(stderr, "GnuTLS: %s", string);
275
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
276
gnutls_dh_params_t *dh_params){
395
static int init_gnutls_global(const char *pubkeyfilename,
396
const char *seckeyfilename){
281
400
fprintf(stderr, "Initializing GnuTLS\n");
284
if ((ret = gnutls_global_init ())
285
!= GNUTLS_E_SUCCESS) {
286
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
403
ret = gnutls_global_init();
404
if(ret != GNUTLS_E_SUCCESS){
405
fprintf(stderr, "GnuTLS global_init: %s\n",
406
safer_gnutls_strerror(ret));
411
/* "Use a log level over 10 to enable all debugging options."
291
414
gnutls_global_set_log_level(11);
292
415
gnutls_global_set_log_function(debuggnutls);
295
/* openpgp credentials */
296
if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
297
!= GNUTLS_E_SUCCESS) {
298
fprintf (stderr, "memory error: %s\n",
299
safer_gnutls_strerror(ret));
418
/* OpenPGP credentials */
419
gnutls_certificate_allocate_credentials(&mc.cred);
420
if(ret != GNUTLS_E_SUCCESS){
421
fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
425
safer_gnutls_strerror(ret));
426
gnutls_global_deinit();
304
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
305
" and keyfile %s as GnuTLS credentials\n", pubkeyfile,
431
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
432
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
309
436
ret = gnutls_certificate_set_openpgp_key_file
310
(mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
311
if (ret != GNUTLS_E_SUCCESS) {
313
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
315
ret, pubkeyfile, seckeyfile);
316
fprintf(stdout, "The Error is: %s\n",
317
safer_gnutls_strerror(ret));
321
//GnuTLS server initialization
322
if ((ret = gnutls_dh_params_init(dh_params))
323
!= GNUTLS_E_SUCCESS) {
324
fprintf (stderr, "Error in dh parameter initialization: %s\n",
325
safer_gnutls_strerror(ret));
329
if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
330
!= GNUTLS_E_SUCCESS) {
331
fprintf (stderr, "Error in prime generation: %s\n",
332
safer_gnutls_strerror(ret));
336
gnutls_certificate_set_dh_params(mc->cred, *dh_params);
338
// GnuTLS session creation
339
if ((ret = gnutls_init(session, GNUTLS_SERVER))
340
!= GNUTLS_E_SUCCESS){
437
(mc.cred, pubkeyfilename, seckeyfilename,
438
GNUTLS_OPENPGP_FMT_BASE64);
439
if(ret != GNUTLS_E_SUCCESS){
441
"Error[%d] while reading the OpenPGP key pair ('%s',"
442
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
443
fprintf(stderr, "The GnuTLS error is: %s\n",
444
safer_gnutls_strerror(ret));
448
/* GnuTLS server initialization */
449
ret = gnutls_dh_params_init(&mc.dh_params);
450
if(ret != GNUTLS_E_SUCCESS){
451
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
452
" %s\n", safer_gnutls_strerror(ret));
455
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
456
if(ret != GNUTLS_E_SUCCESS){
457
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
458
safer_gnutls_strerror(ret));
462
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
468
gnutls_certificate_free_credentials(mc.cred);
469
gnutls_global_deinit();
470
gnutls_dh_params_deinit(mc.dh_params);
474
static int init_gnutls_session(gnutls_session_t *session){
476
/* GnuTLS session creation */
477
ret = gnutls_init(session, GNUTLS_SERVER);
478
if(ret != GNUTLS_E_SUCCESS){
341
479
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
342
480
safer_gnutls_strerror(ret));
345
if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
346
!= GNUTLS_E_SUCCESS) {
347
fprintf(stderr, "Syntax error at: %s\n", err);
348
fprintf(stderr, "GnuTLS error: %s\n",
349
safer_gnutls_strerror(ret));
485
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
486
if(ret != GNUTLS_E_SUCCESS){
487
fprintf(stderr, "Syntax error at: %s\n", err);
488
fprintf(stderr, "GnuTLS error: %s\n",
489
safer_gnutls_strerror(ret));
490
gnutls_deinit(*session);
353
if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
355
!= GNUTLS_E_SUCCESS) {
356
fprintf(stderr, "Error setting a credentials set: %s\n",
495
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
497
if(ret != GNUTLS_E_SUCCESS){
498
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
357
499
safer_gnutls_strerror(ret));
500
gnutls_deinit(*session);
361
504
/* ignore client certificate if any. */
362
gnutls_certificate_server_set_request (*session,
505
gnutls_certificate_server_set_request(*session,
365
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
508
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
513
/* Avahi log function callback */
370
514
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
371
515
__attribute__((unused)) const char *txt){}
517
sig_atomic_t quit_now = 0;
518
int signal_received = 0;
520
/* Called when a Mandos server is found */
373
521
static int start_mandos_communication(const char *ip, uint16_t port,
374
522
AvahiIfIndex if_index,
377
struct sockaddr_in6 to;
524
int ret, tcp_sd = -1;
527
struct sockaddr_in in;
528
struct sockaddr_in6 in6;
378
530
char *buffer = NULL;
379
531
char *decrypted_buffer;
380
532
size_t buffer_length = 0;
381
533
size_t buffer_capacity = 0;
382
ssize_t decrypted_buffer_size;
385
char interface[IF_NAMESIZE];
386
536
gnutls_session_t session;
387
gnutls_dh_params_t dh_params;
537
int pf; /* Protocol family */
551
fprintf(stderr, "Bad address family: %d\n", af);
555
ret = init_gnutls_session(&session);
390
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
561
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
394
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
565
tcp_sd = socket(pf, SOCK_STREAM, 0);
396
567
perror("socket");
401
if(if_indextoname((unsigned int)if_index, interface) == NULL){
402
perror("if_indextoname");
405
fprintf(stderr, "Binding to interface %s\n", interface);
408
memset(&to,0,sizeof(to)); /* Spurious warning */
409
to.sin6_family = AF_INET6;
410
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
576
memset(&to, 0, sizeof(to));
578
to.in6.sin6_family = (sa_family_t)af;
579
ret = inet_pton(af, ip, &to.in6.sin6_addr);
581
to.in.sin_family = (sa_family_t)af;
582
ret = inet_pton(af, ip, &to.in.sin_addr);
412
585
perror("inet_pton");
416
590
fprintf(stderr, "Bad address: %s\n", ip);
419
to.sin6_port = htons(port); /* Spurious warning */
595
to.in6.sin6_port = htons(port); /* Spurious warnings from
597
-Wunreachable-code */
599
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
600
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
602
if(if_index == AVAHI_IF_UNSPEC){
603
fprintf(stderr, "An IPv6 link-local address is incomplete"
604
" without a network interface\n");
608
/* Set the network interface number as scope */
609
to.in6.sin6_scope_id = (uint32_t)if_index;
612
to.in.sin_port = htons(port); /* Spurious warnings from
614
-Wunreachable-code */
421
to.sin6_scope_id = (uint32_t)if_index;
424
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
425
char addrstr[INET6_ADDRSTRLEN] = "";
426
if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
427
sizeof(addrstr)) == NULL){
622
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
623
char interface[IF_NAMESIZE];
624
if(if_indextoname((unsigned int)if_index, interface) == NULL){
625
perror("if_indextoname");
627
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
628
ip, interface, port);
631
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
634
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
635
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
638
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
641
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
428
645
perror("inet_ntop");
430
647
if(strcmp(addrstr, ip) != 0){
436
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
658
ret = connect(tcp_sd, &to.in6, sizeof(to));
660
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
438
663
perror("connect");
442
ret = initgnutls (mc, &session, &dh_params);
448
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
672
const char *out = mandos_protocol_version;
675
size_t out_size = strlen(out);
676
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
677
out_size - written));
683
written += (size_t)ret;
684
if(written < out_size){
687
if(out == mandos_protocol_version){
451
701
fprintf(stderr, "Establishing TLS session with %s\n", ip);
454
ret = gnutls_handshake (session);
456
if (ret != GNUTLS_E_SUCCESS){
708
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
715
ret = gnutls_handshake(session);
719
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
721
if(ret != GNUTLS_E_SUCCESS){
458
fprintf(stderr, "\n*** Handshake failed ***\n");
723
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
465
//Retrieve OpenPGP packet that contains the wanted password
730
/* Read OpenPGP packet that contains the wanted password */
468
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
733
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
473
if (buffer_length + BUFFER_SIZE > buffer_capacity){
474
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
479
buffer_capacity += BUFFER_SIZE;
482
ret = gnutls_record_recv(session, buffer+buffer_length,
743
buffer_capacity = incbuffer(&buffer, buffer_length,
745
if(buffer_capacity == 0){
755
sret = gnutls_record_recv(session, buffer+buffer_length,
489
762
case GNUTLS_E_INTERRUPTED:
490
763
case GNUTLS_E_AGAIN:
492
765
case GNUTLS_E_REHANDSHAKE:
493
ret = gnutls_handshake (session);
495
fprintf(stderr, "\n*** Handshake failed ***\n");
767
ret = gnutls_handshake(session);
772
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
774
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
502
781
fprintf(stderr, "Unknown error while reading data from"
503
" encrypted session with mandos server\n");
782
" encrypted session with Mandos server\n");
505
gnutls_bye (session, GNUTLS_SHUT_RDWR);
784
gnutls_bye(session, GNUTLS_SHUT_RDWR);
509
buffer_length += (size_t) ret;
788
buffer_length += (size_t) sret;
513
if (buffer_length > 0){
793
fprintf(stderr, "Closing TLS session\n");
800
gnutls_bye(session, GNUTLS_SHUT_RDWR);
806
if(buffer_length > 0){
807
ssize_t decrypted_buffer_size;
514
808
decrypted_buffer_size = pgp_packet_decrypt(buffer,
518
if (decrypted_buffer_size >= 0){
811
if(decrypted_buffer_size >= 0){
519
814
while(written < (size_t) decrypted_buffer_size){
520
ret = (int)fwrite (decrypted_buffer + written, 1,
521
(size_t)decrypted_buffer_size - written,
819
ret = (int)fwrite(decrypted_buffer + written, 1,
820
(size_t)decrypted_buffer_size - written,
523
822
if(ret == 0 and ferror(stdout)){
525
824
fprintf(stderr, "Error writing encrypted data: %s\n",
585
890
char ip[AVAHI_ADDRESS_STR_MAX];
586
891
avahi_address_snprint(ip, sizeof(ip), address);
588
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
589
" port %d\n", name, host_name, ip, port);
893
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
894
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
895
ip, (intmax_t)interface, port);
591
int ret = start_mandos_communication(ip, port, interface, mc);
897
int ret = start_mandos_communication(ip, port, interface,
898
avahi_proto_to_af(proto));
900
avahi_simple_poll_quit(mc.simple_poll);
597
904
avahi_s_service_resolver_free(r);
600
static void browse_callback( AvahiSServiceBrowser *b,
601
AvahiIfIndex interface,
602
AvahiProtocol protocol,
603
AvahiBrowserEvent event,
607
AVAHI_GCC_UNUSED AvahiLookupResultFlags
610
mandos_context *mc = userdata;
611
assert(b); /* Spurious warning */
907
static void browse_callback(AvahiSServiceBrowser *b,
908
AvahiIfIndex interface,
909
AvahiProtocol protocol,
910
AvahiBrowserEvent event,
914
AVAHI_GCC_UNUSED AvahiLookupResultFlags
916
AVAHI_GCC_UNUSED void* userdata){
613
919
/* Called whenever a new services becomes available on the LAN or
614
920
is removed from the LAN */
618
928
case AVAHI_BROWSER_FAILURE:
620
fprintf(stderr, "(Browser) %s\n",
621
avahi_strerror(avahi_server_errno(mc->server)));
622
avahi_simple_poll_quit(mc->simple_poll);
930
fprintf(stderr, "(Avahi browser) %s\n",
931
avahi_strerror(avahi_server_errno(mc.server)));
932
avahi_simple_poll_quit(mc.simple_poll);
625
935
case AVAHI_BROWSER_NEW:
626
/* We ignore the returned resolver object. In the callback
627
function we free it. If the server is terminated before
628
the callback function is called the server will free
629
the resolver for us. */
936
/* We ignore the returned Avahi resolver object. In the callback
937
function we free it. If the Avahi server is terminated before
938
the callback function is called the Avahi server will free the
631
if (!(avahi_s_service_resolver_new(mc->server, interface,
632
protocol, name, type, domain,
633
AVAHI_PROTO_INET6, 0,
634
resolve_callback, mc)))
635
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
636
avahi_strerror(avahi_server_errno(mc->server)));
941
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
942
name, type, domain, protocol, 0,
943
resolve_callback, NULL) == NULL)
944
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
945
name, avahi_strerror(avahi_server_errno(mc.server)));
639
948
case AVAHI_BROWSER_REMOVE:
642
951
case AVAHI_BROWSER_ALL_FOR_NOW:
643
952
case AVAHI_BROWSER_CACHE_EXHAUSTED:
954
fprintf(stderr, "No Mandos server found, still searching...\n");
648
/* Combines file name and path and returns the malloced new
649
string. some sane checks could/should be added */
650
static const char *combinepath(const char *first, const char *second){
651
size_t f_len = strlen(first);
652
size_t s_len = strlen(second);
653
char *tmp = malloc(f_len + s_len + 2);
658
memcpy(tmp, first, f_len); /* Spurious warning */
662
memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
664
tmp[f_len + 1 + s_len] = '\0';
960
/* stop main loop after sigterm has been called */
961
static void handle_sigterm(int sig){
966
signal_received = sig;
967
int old_errno = errno;
968
if(mc.simple_poll != NULL){
969
avahi_simple_poll_quit(mc.simple_poll);
669
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
670
AvahiServerConfig config;
671
AvahiSServiceBrowser *sb = NULL;
675
int returncode = EXIT_SUCCESS;
676
const char *interface = "eth0";
677
struct ifreq network;
679
char *connect_to = NULL;
680
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
681
mandos_context mc = { .simple_poll = NULL, .server = NULL,
682
.dh_bits = 1024, .priority = "SECURE256"};
974
int main(int argc, char *argv[]){
975
AvahiSServiceBrowser *sb = NULL;
980
int exitcode = EXIT_SUCCESS;
981
const char *interface = "eth0";
982
struct ifreq network;
984
bool take_down_interface = false;
987
char *connect_to = NULL;
988
char tempdir[] = "/tmp/mandosXXXXXX";
989
bool tempdir_created = false;
990
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
991
const char *seckey = PATHDIR "/" SECKEY;
992
const char *pubkey = PATHDIR "/" PUBKEY;
994
bool gnutls_initialized = false;
995
bool gpgme_initialized = false;
998
struct sigaction old_sigterm_action;
999
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1002
struct argp_option options[] = {
1003
{ .name = "debug", .key = 128,
1004
.doc = "Debug mode", .group = 3 },
1005
{ .name = "connect", .key = 'c',
1006
.arg = "ADDRESS:PORT",
1007
.doc = "Connect directly to a specific Mandos server",
1009
{ .name = "interface", .key = 'i',
1011
.doc = "Network interface that will be used to search for"
1014
{ .name = "seckey", .key = 's',
1016
.doc = "OpenPGP secret key file base name",
1018
{ .name = "pubkey", .key = 'p',
1020
.doc = "OpenPGP public key file base name",
1022
{ .name = "dh-bits", .key = 129,
1024
.doc = "Bit length of the prime number used in the"
1025
" Diffie-Hellman key exchange",
1027
{ .name = "priority", .key = 130,
1029
.doc = "GnuTLS priority string for the TLS handshake",
1031
{ .name = "delay", .key = 131,
1033
.doc = "Maximum delay to wait for interface startup",
684
debug_int = debug ? 1 : 0;
686
struct option long_options[] = {
687
{"debug", no_argument, &debug_int, 1},
688
{"connect", required_argument, NULL, 'c'},
689
{"interface", required_argument, NULL, 'i'},
690
{"keydir", required_argument, NULL, 'd'},
691
{"seckey", required_argument, NULL, 's'},
692
{"pubkey", required_argument, NULL, 'p'},
693
{"dh-bits", required_argument, NULL, 'D'},
694
{"priority", required_argument, NULL, 'P'},
697
int option_index = 0;
698
ret = getopt_long (argc, argv, "i:", long_options,
725
mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
732
mc.priority = optarg;
1038
error_t parse_opt(int key, char *arg,
1039
struct argp_state *state){
1041
case 128: /* --debug */
1044
case 'c': /* --connect */
1047
case 'i': /* --interface */
1050
case 's': /* --seckey */
1053
case 'p': /* --pubkey */
1056
case 129: /* --dh-bits */
1058
tmpmax = strtoimax(arg, &tmp, 10);
1059
if(errno != 0 or tmp == arg or *tmp != '\0'
1060
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1061
fprintf(stderr, "Bad number of DH bits\n");
1064
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1066
case 130: /* --priority */
1069
case 131: /* --delay */
1071
delay = strtof(arg, &tmp);
1072
if(errno != 0 or tmp == arg or *tmp != '\0'){
1073
fprintf(stderr, "Bad delay\n");
1082
return ARGP_ERR_UNKNOWN;
739
debug = debug_int ? true : false;
741
pubkeyfile = combinepath(keydir, pubkeyfile);
742
if (pubkeyfile == NULL){
743
perror("combinepath");
744
returncode = EXIT_FAILURE;
748
seckeyfile = combinepath(keydir, seckeyfile);
749
if (seckeyfile == NULL){
750
perror("combinepath");
1087
struct argp argp = { .options = options, .parser = parse_opt,
1089
.doc = "Mandos client -- Get and decrypt"
1090
" passwords from a Mandos server" };
1091
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1092
if(ret == ARGP_ERR_UNKNOWN){
1093
fprintf(stderr, "Unknown error while parsing arguments\n");
1094
exitcode = EXIT_FAILURE;
1100
avahi_set_log_function(empty_log);
1103
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1104
from the signal handler */
1105
/* Initialize the pseudo-RNG for Avahi */
1106
srand((unsigned int) time(NULL));
1107
mc.simple_poll = avahi_simple_poll_new();
1108
if(mc.simple_poll == NULL){
1109
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1110
exitcode = EXIT_FAILURE;
1114
sigemptyset(&sigterm_action.sa_mask);
1115
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1117
perror("sigaddset");
1118
exitcode = EXIT_FAILURE;
1121
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1123
perror("sigaddset");
1124
exitcode = EXIT_FAILURE;
1127
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1129
perror("sigaddset");
1130
exitcode = EXIT_FAILURE;
1133
/* Need to check if the handler is SIG_IGN before handling:
1134
| [[info:libc:Initial Signal Actions]] |
1135
| [[info:libc:Basic Signal Handling]] |
1137
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1139
perror("sigaction");
1140
return EXIT_FAILURE;
1142
if(old_sigterm_action.sa_handler != SIG_IGN){
1143
ret = sigaction(SIGINT, &sigterm_action, NULL);
1145
perror("sigaction");
1146
exitcode = EXIT_FAILURE;
1150
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1152
perror("sigaction");
1153
return EXIT_FAILURE;
1155
if(old_sigterm_action.sa_handler != SIG_IGN){
1156
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1158
perror("sigaction");
1159
exitcode = EXIT_FAILURE;
1163
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1165
perror("sigaction");
1166
return EXIT_FAILURE;
1168
if(old_sigterm_action.sa_handler != SIG_IGN){
1169
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1171
perror("sigaction");
1172
exitcode = EXIT_FAILURE;
1177
/* If the interface is down, bring it up */
1178
if(interface[0] != '\0'){
754
1179
if_index = (AvahiIfIndex) if_nametoindex(interface);
755
1180
if(if_index == 0){
756
1181
fprintf(stderr, "No such interface: \"%s\"\n", interface);
760
if(connect_to != NULL){
761
/* Connect directly, do not use Zeroconf */
762
/* (Mainly meant for debugging) */
763
char *address = strrchr(connect_to, ':');
765
fprintf(stderr, "No colon in address\n");
769
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
771
perror("Bad port number");
775
address = connect_to;
776
ret = start_mandos_communication(address, port, if_index, &mc);
1182
exitcode = EXIT_FAILURE;
1191
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1192
messages to mess up the prompt */
1193
ret = klogctl(8, NULL, 5);
1194
bool restore_loglevel = true;
1196
restore_loglevel = false;
1199
#endif /* __linux__ */
784
1201
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
786
1203
perror("socket");
787
returncode = EXIT_FAILURE;
1204
exitcode = EXIT_FAILURE;
1206
if(restore_loglevel){
1207
ret = klogctl(7, NULL, 0);
1212
#endif /* __linux__ */
790
strcpy(network.ifr_name, interface); /* Spurious warning */
1215
strcpy(network.ifr_name, interface);
791
1216
ret = ioctl(sd, SIOCGIFFLAGS, &network);
794
1218
perror("ioctl SIOCGIFFLAGS");
795
returncode = EXIT_FAILURE;
1220
if(restore_loglevel){
1221
ret = klogctl(7, NULL, 0);
1226
#endif /* __linux__ */
1227
exitcode = EXIT_FAILURE;
798
1230
if((network.ifr_flags & IFF_UP) == 0){
799
1231
network.ifr_flags |= IFF_UP;
1232
take_down_interface = true;
800
1233
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1235
take_down_interface = false;
802
1236
perror("ioctl SIOCSIFFLAGS");
803
returncode = EXIT_FAILURE;
810
avahi_set_log_function(empty_log);
813
/* Initialize the psuedo-RNG */
814
srand((unsigned int) time(NULL));
816
/* Allocate main loop object */
817
if (!(mc.simple_poll = avahi_simple_poll_new())) {
818
fprintf(stderr, "Failed to create simple poll object.\n");
819
returncode = EXIT_FAILURE;
823
/* Do not publish any local records */
1237
exitcode = EXIT_FAILURE;
1239
if(restore_loglevel){
1240
ret = klogctl(7, NULL, 0);
1245
#endif /* __linux__ */
1249
/* sleep checking until interface is running */
1250
for(int i=0; i < delay * 4; i++){
1251
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1253
perror("ioctl SIOCGIFFLAGS");
1254
} else if(network.ifr_flags & IFF_RUNNING){
1257
struct timespec sleeptime = { .tv_nsec = 250000000 };
1258
ret = nanosleep(&sleeptime, NULL);
1259
if(ret == -1 and errno != EINTR){
1260
perror("nanosleep");
1263
if(not take_down_interface){
1264
/* We won't need the socket anymore */
1265
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1271
if(restore_loglevel){
1272
/* Restores kernel loglevel to default */
1273
ret = klogctl(7, NULL, 0);
1278
#endif /* __linux__ */
1303
ret = init_gnutls_global(pubkey, seckey);
1305
fprintf(stderr, "init_gnutls_global failed\n");
1306
exitcode = EXIT_FAILURE;
1309
gnutls_initialized = true;
1316
tempdir_created = true;
1317
if(mkdtemp(tempdir) == NULL){
1318
tempdir_created = false;
1327
if(not init_gpgme(pubkey, seckey, tempdir)){
1328
fprintf(stderr, "init_gpgme failed\n");
1329
exitcode = EXIT_FAILURE;
1332
gpgme_initialized = true;
1339
if(connect_to != NULL){
1340
/* Connect directly, do not use Zeroconf */
1341
/* (Mainly meant for debugging) */
1342
char *address = strrchr(connect_to, ':');
1343
if(address == NULL){
1344
fprintf(stderr, "No colon in address\n");
1345
exitcode = EXIT_FAILURE;
1355
tmpmax = strtoimax(address+1, &tmp, 10);
1356
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1357
or tmpmax != (uint16_t)tmpmax){
1358
fprintf(stderr, "Bad port number\n");
1359
exitcode = EXIT_FAILURE;
1367
port = (uint16_t)tmpmax;
1369
address = connect_to;
1370
/* Colon in address indicates IPv6 */
1372
if(strchr(address, ':') != NULL){
1382
ret = start_mandos_communication(address, port, if_index, af);
1384
exitcode = EXIT_FAILURE;
1386
exitcode = EXIT_SUCCESS;
1396
AvahiServerConfig config;
1397
/* Do not publish any local Zeroconf records */
824
1398
avahi_server_config_init(&config);
825
1399
config.publish_hinfo = 0;
826
1400
config.publish_addresses = 0;
827
1401
config.publish_workstation = 0;
828
1402
config.publish_domain = 0;
830
1404
/* Allocate a new server */
831
mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
832
&config, NULL, NULL, &error);
1405
mc.server = avahi_server_new(avahi_simple_poll_get
1406
(mc.simple_poll), &config, NULL,
834
/* Free the configuration data */
1409
/* Free the Avahi configuration data */
835
1410
avahi_server_config_free(&config);
837
/* Check if creating the server object succeeded */
839
fprintf(stderr, "Failed to create server: %s\n",
840
avahi_strerror(error));
841
returncode = EXIT_FAILURE;
845
/* Create the service browser */
846
sb = avahi_s_service_browser_new(mc.server, if_index,
848
"_mandos._tcp", NULL, 0,
849
browse_callback, &mc);
851
fprintf(stderr, "Failed to create service browser: %s\n",
852
avahi_strerror(avahi_server_errno(mc.server)));
853
returncode = EXIT_FAILURE;
857
/* Run the main loop */
860
fprintf(stderr, "Starting avahi loop search\n");
863
avahi_simple_poll_loop(mc.simple_poll);
868
fprintf(stderr, "%s exiting\n", argv[0]);
873
avahi_s_service_browser_free(sb);
876
avahi_server_free(mc.server);
879
avahi_simple_poll_free(mc.simple_poll);
1413
/* Check if creating the Avahi server object succeeded */
1414
if(mc.server == NULL){
1415
fprintf(stderr, "Failed to create Avahi server: %s\n",
1416
avahi_strerror(error));
1417
exitcode = EXIT_FAILURE;
1425
/* Create the Avahi service browser */
1426
sb = avahi_s_service_browser_new(mc.server, if_index,
1427
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1428
NULL, 0, browse_callback, NULL);
1430
fprintf(stderr, "Failed to create service browser: %s\n",
1431
avahi_strerror(avahi_server_errno(mc.server)));
1432
exitcode = EXIT_FAILURE;
1440
/* Run the main loop */
1443
fprintf(stderr, "Starting Avahi loop search\n");
1446
avahi_simple_poll_loop(mc.simple_poll);
1451
fprintf(stderr, "%s exiting\n", argv[0]);
1454
/* Cleanup things */
1456
avahi_s_service_browser_free(sb);
1458
if(mc.server != NULL)
1459
avahi_server_free(mc.server);
1461
if(mc.simple_poll != NULL)
1462
avahi_simple_poll_free(mc.simple_poll);
1464
if(gnutls_initialized){
1465
gnutls_certificate_free_credentials(mc.cred);
1466
gnutls_global_deinit();
1467
gnutls_dh_params_deinit(mc.dh_params);
1470
if(gpgme_initialized){
1471
gpgme_release(mc.ctx);
1474
/* Take down the network interface */
1475
if(take_down_interface){
1476
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1478
perror("ioctl SIOCGIFFLAGS");
1479
} else if(network.ifr_flags & IFF_UP) {
1480
network.ifr_flags &= ~IFF_UP; /* clear flag */
1481
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1483
perror("ioctl SIOCSIFFLAGS");
1486
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1492
/* Removes the temp directory used by GPGME */
1493
if(tempdir_created){
1495
struct dirent *direntry;
1496
d = opendir(tempdir);
1498
if(errno != ENOENT){
1503
direntry = readdir(d);
1504
if(direntry == NULL){
1507
/* Skip "." and ".." */
1508
if(direntry->d_name[0] == '.'
1509
and (direntry->d_name[1] == '\0'
1510
or (direntry->d_name[1] == '.'
1511
and direntry->d_name[2] == '\0'))){
1514
char *fullname = NULL;
1515
ret = asprintf(&fullname, "%s/%s", tempdir,
1521
ret = remove(fullname);
1523
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1530
ret = rmdir(tempdir);
1531
if(ret == -1 and errno != ENOENT){
1537
sigemptyset(&old_sigterm_action.sa_mask);
1538
old_sigterm_action.sa_handler = SIG_DFL;
1539
ret = sigaction(signal_received, &old_sigterm_action, NULL);
1541
perror("sigaction");
1543
raise(signal_received);