58
64
#include <string.h> /* memset */
59
65
#include <arpa/inet.h> /* inet_pton() */
60
66
#include <iso646.h> /* not */
67
#include <net/if.h> /* IF_NAMESIZE */
63
70
#include <errno.h> /* perror() */
67
74
#include <getopt.h>
69
76
#define BUFFER_SIZE 256
72
static const char *certdir = "/conf/conf.d/mandos";
73
static const char *certfile = "openpgp-client.txt";
74
static const char *certkey = "openpgp-client-key.txt";
78
static const char *keydir = "/conf/conf.d/mandos";
79
static const char *pubkeyfile = "pubkey.txt";
80
static const char *seckeyfile = "seckey.txt";
76
82
bool debug = false;
84
const char mandos_protocol_version[] = "1";
86
/* Used for passing in values through all the callback functions */
79
gnutls_session_t session;
88
AvahiSimplePoll *simple_poll;
80
90
gnutls_certificate_credentials_t cred;
81
92
gnutls_dh_params_t dh_params;
85
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
96
size_t adjustbuffer(char **buffer, size_t buffer_length,
97
size_t buffer_capacity){
98
if (buffer_length + BUFFER_SIZE > buffer_capacity){
99
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
103
buffer_capacity += BUFFER_SIZE;
105
return buffer_capacity;
109
* Decrypt OpenPGP data using keyrings in HOMEDIR.
110
* Returns -1 on error
112
static ssize_t pgp_packet_decrypt (const char *cryptotext,
87
115
const char *homedir){
88
116
gpgme_data_t dh_crypto, dh_plain;
92
ssize_t new_packet_capacity = 0;
93
ssize_t new_packet_length = 0;
120
size_t plaintext_capacity = 0;
121
ssize_t plaintext_length = 0;
94
122
gpgme_engine_info_t engine_info;
97
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
125
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
195
/* Delete the GPGME FILE pointer cryptotext data buffer */
196
gpgme_data_release(dh_crypto);
198
227
/* Seek back to the beginning of the GPGME plaintext data buffer */
199
228
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
200
229
perror("pgpme_data_seek");
230
plaintext_length = -1;
205
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
206
*new_packet = realloc(*new_packet,
207
(unsigned int)new_packet_capacity
209
if (*new_packet == NULL){
213
new_packet_capacity += BUFFER_SIZE;
236
plaintext_capacity = adjustbuffer(plaintext, (size_t)plaintext_length,
238
if (plaintext_capacity == 0){
239
perror("adjustbuffer");
240
plaintext_length = -1;
216
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
244
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
218
246
/* Print the data, if any */
223
252
perror("gpgme_data_read");
253
plaintext_length = -1;
226
new_packet_length += ret;
256
plaintext_length += ret;
229
/* FIXME: check characters before printing to screen so to not print
230
terminal control characters */
232
/* fprintf(stderr, "decrypted password is: "); */
233
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
234
/* fprintf(stderr, "\n"); */
260
fprintf(stderr, "Decrypted password is: ");
261
for(ssize_t i = 0; i < plaintext_length; i++){
262
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
264
fprintf(stderr, "\n");
269
/* Delete the GPGME cryptotext data buffer */
270
gpgme_data_release(dh_crypto);
237
272
/* Delete the GPGME plaintext data buffer */
238
273
gpgme_data_release(dh_plain);
239
return new_packet_length;
274
return plaintext_length;
242
277
static const char * safer_gnutls_strerror (int value) {
262
297
if ((ret = gnutls_global_init ())
263
298
!= GNUTLS_E_SUCCESS) {
264
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
299
fprintf (stderr, "GnuTLS global_init: %s\n",
300
safer_gnutls_strerror(ret));
305
/* "Use a log level over 10 to enable all debugging options."
269
308
gnutls_global_set_log_level(11);
270
309
gnutls_global_set_log_function(debuggnutls);
273
/* openpgp credentials */
274
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
312
/* OpenPGP credentials */
313
if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
275
314
!= GNUTLS_E_SUCCESS) {
276
fprintf (stderr, "memory error: %s\n",
315
fprintf (stderr, "GnuTLS memory error: %s\n",
277
316
safer_gnutls_strerror(ret));
282
321
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
283
" and keyfile %s as GnuTLS credentials\n", certfile,
322
" and keyfile %s as GnuTLS credentials\n", pubkeyfile,
287
326
ret = gnutls_certificate_set_openpgp_key_file
288
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
327
(mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
289
328
if (ret != GNUTLS_E_SUCCESS) {
291
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
293
ret, certfile, certkey);
294
fprintf(stdout, "The Error is: %s\n",
330
"Error[%d] while reading the OpenPGP key pair ('%s',"
331
" '%s')\n", ret, pubkeyfile, seckeyfile);
332
fprintf(stdout, "The GnuTLS error is: %s\n",
295
333
safer_gnutls_strerror(ret));
299
//GnuTLS server initialization
300
if ((ret = gnutls_dh_params_init (&es->dh_params))
301
!= GNUTLS_E_SUCCESS) {
302
fprintf (stderr, "Error in dh parameter initialization: %s\n",
303
safer_gnutls_strerror(ret));
307
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
308
!= GNUTLS_E_SUCCESS) {
309
fprintf (stderr, "Error in prime generation: %s\n",
310
safer_gnutls_strerror(ret));
314
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
316
// GnuTLS session creation
317
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
318
!= GNUTLS_E_SUCCESS){
337
/* GnuTLS server initialization */
338
ret = gnutls_dh_params_init(&mc->dh_params);
339
if (ret != GNUTLS_E_SUCCESS) {
340
fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
341
" %s\n", safer_gnutls_strerror(ret));
344
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
345
if (ret != GNUTLS_E_SUCCESS) {
346
fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
347
safer_gnutls_strerror(ret));
351
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
356
static int init_gnutls_session(mandos_context *mc, gnutls_session_t *session){
358
/* GnuTLS session creation */
359
ret = gnutls_init(session, GNUTLS_SERVER);
360
if (ret != GNUTLS_E_SUCCESS){
319
361
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
320
362
safer_gnutls_strerror(ret));
323
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
324
!= GNUTLS_E_SUCCESS) {
325
fprintf(stderr, "Syntax error at: %s\n", err);
326
fprintf(stderr, "GnuTLS error: %s\n",
327
safer_gnutls_strerror(ret));
367
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
368
if (ret != GNUTLS_E_SUCCESS) {
369
fprintf(stderr, "Syntax error at: %s\n", err);
370
fprintf(stderr, "GnuTLS error: %s\n",
371
safer_gnutls_strerror(ret));
331
if ((ret = gnutls_credentials_set
332
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
333
!= GNUTLS_E_SUCCESS) {
334
fprintf(stderr, "Error setting a credentials set: %s\n",
376
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
378
if (ret != GNUTLS_E_SUCCESS) {
379
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
335
380
safer_gnutls_strerror(ret));
339
384
/* ignore client certificate if any. */
340
gnutls_certificate_server_set_request (es->session,
385
gnutls_certificate_server_set_request (*session,
341
386
GNUTLS_CERT_IGNORE);
343
gnutls_dh_set_prime_bits (es->session, DH_BITS);
388
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
393
/* Avahi log function callback */
348
394
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
349
395
__attribute__((unused)) const char *txt){}
397
/* Called when a Mandos server is found */
351
398
static int start_mandos_communication(const char *ip, uint16_t port,
352
AvahiIfIndex if_index){
399
AvahiIfIndex if_index,
354
402
struct sockaddr_in6 to;
355
encrypted_session es;
356
403
char *buffer = NULL;
357
404
char *decrypted_buffer;
358
405
size_t buffer_length = 0;
359
406
size_t buffer_capacity = 0;
360
407
ssize_t decrypted_buffer_size;
363
410
char interface[IF_NAMESIZE];
411
gnutls_session_t session;
412
gnutls_dh_params_t dh_params;
414
ret = init_gnutls_session (mc, &session);
366
420
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
403
456
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
404
/* char addrstr[INET6_ADDRSTRLEN]; */
405
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
406
/* sizeof(addrstr)) == NULL){ */
407
/* perror("inet_ntop"); */
409
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
410
/* addrstr, ntohs(to.sin6_port)); */
457
char addrstr[INET6_ADDRSTRLEN] = "";
458
if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
459
sizeof(addrstr)) == NULL){
462
if(strcmp(addrstr, ip) != 0){
463
fprintf(stderr, "Canonical address form: %s\n", addrstr);
414
468
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
469
538
case GNUTLS_E_AGAIN:
471
540
case GNUTLS_E_REHANDSHAKE:
472
ret = gnutls_handshake (es.session);
541
ret = gnutls_handshake (session);
474
fprintf(stderr, "\n*** Handshake failed ***\n");
543
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
475
544
gnutls_perror (ret);
481
550
fprintf(stderr, "Unknown error while reading data from"
482
" encrypted session with mandos server\n");
551
" encrypted session with Mandos server\n");
484
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
553
gnutls_bye (session, GNUTLS_SHUT_RDWR);
488
557
buffer_length += (size_t) ret;
562
fprintf(stderr, "Closing TLS session\n");
565
gnutls_bye (session, GNUTLS_SHUT_RDWR);
492
567
if (buffer_length > 0){
493
568
decrypted_buffer_size = pgp_packet_decrypt(buffer,
495
570
&decrypted_buffer,
497
572
if (decrypted_buffer_size >= 0){
498
574
while(written < (size_t) decrypted_buffer_size){
499
575
ret = (int)fwrite (decrypted_buffer + written, 1,
500
576
(size_t)decrypted_buffer_size - written,
521
fprintf(stderr, "Closing TLS session\n");
594
/* Shutdown procedure */
525
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
528
gnutls_deinit (es.session);
529
gnutls_certificate_free_credentials (es.cred);
599
gnutls_deinit (session);
600
gnutls_certificate_free_credentials (mc->cred);
530
601
gnutls_global_deinit ();
534
static AvahiSimplePoll *simple_poll = NULL;
535
static AvahiServer *server = NULL;
537
static void resolve_callback(
538
AvahiSServiceResolver *r,
539
AvahiIfIndex interface,
540
AVAHI_GCC_UNUSED AvahiProtocol protocol,
541
AvahiResolverEvent event,
545
const char *host_name,
546
const AvahiAddress *address,
548
AVAHI_GCC_UNUSED AvahiStringList *txt,
549
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
550
AVAHI_GCC_UNUSED void* userdata) {
605
static void resolve_callback(AvahiSServiceResolver *r,
606
AvahiIfIndex interface,
607
AVAHI_GCC_UNUSED AvahiProtocol protocol,
608
AvahiResolverEvent event,
612
const char *host_name,
613
const AvahiAddress *address,
615
AVAHI_GCC_UNUSED AvahiStringList *txt,
616
AVAHI_GCC_UNUSED AvahiLookupResultFlags
619
mandos_context *mc = userdata;
552
620
assert(r); /* Spurious warning */
554
622
/* Called whenever a service has been resolved successfully or
579
647
avahi_s_service_resolver_free(r);
582
static void browse_callback(
583
AvahiSServiceBrowser *b,
584
AvahiIfIndex interface,
585
AvahiProtocol protocol,
586
AvahiBrowserEvent event,
590
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
593
AvahiServer *s = userdata;
594
assert(b); /* Spurious warning */
596
/* Called whenever a new services becomes available on the LAN or
597
is removed from the LAN */
601
case AVAHI_BROWSER_FAILURE:
603
fprintf(stderr, "(Browser) %s\n",
604
avahi_strerror(avahi_server_errno(server)));
605
avahi_simple_poll_quit(simple_poll);
608
case AVAHI_BROWSER_NEW:
609
/* We ignore the returned resolver object. In the callback
610
function we free it. If the server is terminated before
611
the callback function is called the server will free
612
the resolver for us. */
614
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
616
AVAHI_PROTO_INET6, 0,
617
resolve_callback, s)))
618
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
619
avahi_strerror(avahi_server_errno(s)));
622
case AVAHI_BROWSER_REMOVE:
625
case AVAHI_BROWSER_ALL_FOR_NOW:
626
case AVAHI_BROWSER_CACHE_EXHAUSTED:
650
static void browse_callback( AvahiSServiceBrowser *b,
651
AvahiIfIndex interface,
652
AvahiProtocol protocol,
653
AvahiBrowserEvent event,
657
AVAHI_GCC_UNUSED AvahiLookupResultFlags
660
mandos_context *mc = userdata;
661
assert(b); /* Spurious warning */
663
/* Called whenever a new services becomes available on the LAN or
664
is removed from the LAN */
668
case AVAHI_BROWSER_FAILURE:
670
fprintf(stderr, "(Avahi browser) %s\n",
671
avahi_strerror(avahi_server_errno(mc->server)));
672
avahi_simple_poll_quit(mc->simple_poll);
675
case AVAHI_BROWSER_NEW:
676
/* We ignore the returned Avahi resolver object. In the callback
677
function we free it. If the Avahi server is terminated before
678
the callback function is called the Avahi server will free the
681
if (!(avahi_s_service_resolver_new(mc->server, interface,
682
protocol, name, type, domain,
683
AVAHI_PROTO_INET6, 0,
684
resolve_callback, mc)))
685
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
686
name, avahi_strerror(avahi_server_errno(mc->server)));
689
case AVAHI_BROWSER_REMOVE:
692
case AVAHI_BROWSER_ALL_FOR_NOW:
693
case AVAHI_BROWSER_CACHE_EXHAUSTED:
695
fprintf(stderr, "No Mandos server found, still searching...\n");
631
701
/* Combines file name and path and returns the malloced new
641
memcpy(tmp, first, f_len);
711
memcpy(tmp, first, f_len); /* Spurious warning */
643
713
tmp[f_len] = '/';
645
memcpy(tmp + f_len + 1, second, s_len);
715
memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
647
717
tmp[f_len + 1 + s_len] = '\0';
652
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
653
AvahiServerConfig config;
722
int main(int argc, char *argv[]){
654
723
AvahiSServiceBrowser *sb = NULL;
657
int returncode = EXIT_SUCCESS;
658
const char *interface = NULL;
726
int exitcode = EXIT_SUCCESS;
727
const char *interface = "eth0";
728
struct ifreq network;
732
char *connect_to = NULL;
659
733
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
660
char *connect_to = NULL;
663
static struct option long_options[] = {
664
{"debug", no_argument, (int *)&debug, 1},
665
{"connect", required_argument, 0, 'C'},
666
{"interface", required_argument, 0, 'i'},
667
{"certdir", required_argument, 0, 'd'},
668
{"certkey", required_argument, 0, 'c'},
669
{"certfile", required_argument, 0, 'k'},
672
int option_index = 0;
673
ret = getopt_long (argc, argv, "i:", long_options,
703
certfile = combinepath(certdir, certfile);
704
if (certfile == NULL){
705
perror("combinepath");
709
if(interface != NULL){
710
if_index = (AvahiIfIndex) if_nametoindex(interface);
712
fprintf(stderr, "No such interface: \"%s\"\n", interface);
734
mandos_context mc = { .simple_poll = NULL, .server = NULL,
735
.dh_bits = 1024, .priority = "SECURE256"};
738
/* Temporary int to get the address of for getopt_long */
739
int debug_int = debug ? 1 : 0;
741
struct option long_options[] = {
742
{"debug", no_argument, &debug_int, 1},
743
{"connect", required_argument, NULL, 'c'},
744
{"interface", required_argument, NULL, 'i'},
745
{"keydir", required_argument, NULL, 'd'},
746
{"seckey", required_argument, NULL, 's'},
747
{"pubkey", required_argument, NULL, 'p'},
748
{"dh-bits", required_argument, NULL, 'D'},
749
{"priority", required_argument, NULL, 'P'},
752
int option_index = 0;
753
ret = getopt_long (argc, argv, "i:", long_options,
780
mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
787
mc.priority = optarg;
791
/* getopt_long() has already printed a message about the
792
unrcognized option, so just exit. */
796
/* Set the global debug flag from the temporary int */
797
debug = debug_int ? true : false;
800
pubkeyfile = combinepath(keydir, pubkeyfile);
801
if (pubkeyfile == NULL){
802
perror("combinepath");
803
exitcode = EXIT_FAILURE;
807
seckeyfile = combinepath(keydir, seckeyfile);
808
if (seckeyfile == NULL){
809
perror("combinepath");
813
ret = init_gnutls_global(&mc);
815
fprintf(stderr, "init_gnutls_global\n");
832
if_index = (AvahiIfIndex) if_nametoindex(interface);
834
fprintf(stderr, "No such interface: \"%s\"\n", interface);
717
838
if(connect_to != NULL){
720
841
char *address = strrchr(connect_to, ':');
721
842
if(address == NULL){
722
843
fprintf(stderr, "No colon in address\n");
844
exitcode = EXIT_FAILURE;
726
848
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
728
850
perror("Bad port number");
851
exitcode = EXIT_FAILURE;
732
855
address = connect_to;
733
ret = start_mandos_communication(address, port, if_index);
856
ret = start_mandos_communication(address, port, if_index, &mc);
858
exitcode = EXIT_FAILURE;
860
exitcode = EXIT_SUCCESS;
741
certkey = combinepath(certdir, certkey);
742
if (certkey == NULL){
743
perror("combinepath");
865
/* If the interface is down, bring it up */
867
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
870
exitcode = EXIT_FAILURE;
873
strcpy(network.ifr_name, interface); /* Spurious warning */
874
ret = ioctl(sd, SIOCGIFFLAGS, &network);
876
perror("ioctl SIOCGIFFLAGS");
877
exitcode = EXIT_FAILURE;
880
if((network.ifr_flags & IFF_UP) == 0){
881
network.ifr_flags |= IFF_UP;
882
ret = ioctl(sd, SIOCSIFFLAGS, &network);
884
perror("ioctl SIOCSIFFLAGS");
885
exitcode = EXIT_FAILURE;
748
893
avahi_set_log_function(empty_log);
751
/* Initialize the psuedo-RNG */
896
/* Initialize the pseudo-RNG for Avahi */
752
897
srand((unsigned int) time(NULL));
754
/* Allocate main loop object */
755
if (!(simple_poll = avahi_simple_poll_new())) {
756
fprintf(stderr, "Failed to create simple poll object.\n");
761
/* Do not publish any local records */
762
avahi_server_config_init(&config);
763
config.publish_hinfo = 0;
764
config.publish_addresses = 0;
765
config.publish_workstation = 0;
766
config.publish_domain = 0;
768
/* Allocate a new server */
769
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
770
&config, NULL, NULL, &error);
772
/* Free the configuration data */
773
avahi_server_config_free(&config);
775
/* Check if creating the server object succeeded */
777
fprintf(stderr, "Failed to create server: %s\n",
899
/* Allocate main Avahi loop object */
900
mc.simple_poll = avahi_simple_poll_new();
901
if (mc.simple_poll == NULL) {
902
fprintf(stderr, "Avahi: Failed to create simple poll"
904
exitcode = EXIT_FAILURE;
909
AvahiServerConfig config;
910
/* Do not publish any local Zeroconf records */
911
avahi_server_config_init(&config);
912
config.publish_hinfo = 0;
913
config.publish_addresses = 0;
914
config.publish_workstation = 0;
915
config.publish_domain = 0;
917
/* Allocate a new server */
918
mc.server = avahi_server_new(avahi_simple_poll_get
919
(mc.simple_poll), &config, NULL,
922
/* Free the Avahi configuration data */
923
avahi_server_config_free(&config);
926
/* Check if creating the Avahi server object succeeded */
927
if (mc.server == NULL) {
928
fprintf(stderr, "Failed to create Avahi server: %s\n",
778
929
avahi_strerror(error));
779
returncode = EXIT_FAILURE;
930
exitcode = EXIT_FAILURE;
783
/* Create the service browser */
784
sb = avahi_s_service_browser_new(server, if_index,
934
/* Create the Avahi service browser */
935
sb = avahi_s_service_browser_new(mc.server, if_index,
785
936
AVAHI_PROTO_INET6,
786
937
"_mandos._tcp", NULL, 0,
787
browse_callback, server);
938
browse_callback, &mc);
789
940
fprintf(stderr, "Failed to create service browser: %s\n",
790
avahi_strerror(avahi_server_errno(server)));
791
returncode = EXIT_FAILURE;
941
avahi_strerror(avahi_server_errno(mc.server)));
942
exitcode = EXIT_FAILURE;
795
946
/* Run the main loop */
798
fprintf(stderr, "Starting avahi loop search\n");
949
fprintf(stderr, "Starting Avahi loop search\n");
801
avahi_simple_poll_loop(simple_poll);
952
avahi_simple_poll_loop(mc.simple_poll);
806
957
fprintf(stderr, "%s exiting\n", argv[0]);
809
960
/* Cleanup things */
811
962
avahi_s_service_browser_free(sb);
814
avahi_server_free(server);
964
if (mc.server != NULL)
965
avahi_server_free(mc.server);
817
avahi_simple_poll_free(simple_poll);
967
if (mc.simple_poll != NULL)
968
avahi_simple_poll_free(mc.simple_poll);