62
60
#include <string.h> /* memset */
63
61
#include <arpa/inet.h> /* inet_pton() */
64
62
#include <iso646.h> /* not */
65
#include <net/if.h> /* IF_NAMESIZE */
68
65
#include <errno.h> /* perror() */
72
69
#include <getopt.h>
72
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
74
#define CERTFILE CERT_ROOT "openpgp-client.txt"
75
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
74
76
#define BUFFER_SIZE 256
76
static const char *keydir = "/conf/conf.d/mandos";
77
static const char *pubkeyfile = "pubkey.txt";
78
static const char *seckeyfile = "seckey.txt";
80
79
bool debug = false;
82
/* Used for passing in values through all the callback functions */
84
AvahiSimplePoll *simple_poll;
82
gnutls_session_t session;
86
83
gnutls_certificate_credentials_t cred;
92
* Decrypt OpenPGP data using keyrings in HOMEDIR.
95
static ssize_t pgp_packet_decrypt (const char *cryptotext,
84
gnutls_dh_params_t dh_params;
88
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
char **new_packet, const char *homedir){
99
90
gpgme_data_t dh_crypto, dh_plain;
103
size_t plaintext_capacity = 0;
104
ssize_t plaintext_length = 0;
94
ssize_t new_packet_capacity = 0;
95
ssize_t new_packet_length = 0;
105
96
gpgme_engine_info_t engine_info;
108
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
99
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
112
103
gpgme_check_version(NULL);
113
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
114
if (rc != GPG_ERR_NO_ERROR){
115
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
116
gpgme_strsource(rc), gpgme_strerror(rc));
104
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
120
/* Set GPGME home directory for the OpenPGP engine only */
106
/* Set GPGME home directory */
121
107
rc = gpgme_get_engine_info (&engine_info);
122
108
if (rc != GPG_ERR_NO_ERROR){
123
109
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
192
/* Delete the GPGME FILE pointer cryptotext data buffer */
193
gpgme_data_release(dh_crypto);
210
195
/* Seek back to the beginning of the GPGME plaintext data buffer */
211
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
212
perror("pgpme_data_seek");
213
plaintext_length = -1;
196
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
219
if (plaintext_length + BUFFER_SIZE
220
> (ssize_t) plaintext_capacity){
221
*plaintext = realloc(*plaintext, plaintext_capacity
200
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
201
*new_packet = realloc(*new_packet,
202
(unsigned int)new_packet_capacity
223
if (*plaintext == NULL){
204
if (*new_packet == NULL){
224
205
perror("realloc");
225
plaintext_length = -1;
228
plaintext_capacity += BUFFER_SIZE;
208
new_packet_capacity += BUFFER_SIZE;
231
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
211
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
233
213
/* Print the data, if any */
239
218
perror("gpgme_data_read");
240
plaintext_length = -1;
243
plaintext_length += ret;
221
new_packet_length += ret;
247
fprintf(stderr, "Decrypted password is: ");
248
for(ssize_t i = 0; i < plaintext_length; i++){
249
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
251
fprintf(stderr, "\n");
256
/* Delete the GPGME cryptotext data buffer */
257
gpgme_data_release(dh_crypto);
224
/* FIXME: check characters before printing to screen so to not print
225
terminal control characters */
227
/* fprintf(stderr, "decrypted password is: "); */
228
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
229
/* fprintf(stderr, "\n"); */
259
232
/* Delete the GPGME plaintext data buffer */
260
233
gpgme_data_release(dh_plain);
261
return plaintext_length;
234
return new_packet_length;
264
237
static const char * safer_gnutls_strerror (int value) {
271
/* GnuTLS log function callback */
272
static void debuggnutls(__attribute__((unused)) int level,
274
fprintf(stderr, "GnuTLS: %s", string);
244
void debuggnutls(__attribute__((unused)) int level,
246
fprintf(stderr, "%s", string);
277
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
278
gnutls_dh_params_t *dh_params){
249
int initgnutls(encrypted_session *es){
282
254
fprintf(stderr, "Initializing GnuTLS\n");
285
257
if ((ret = gnutls_global_init ())
286
258
!= GNUTLS_E_SUCCESS) {
287
fprintf (stderr, "GnuTLS global_init: %s\n",
288
safer_gnutls_strerror(ret));
259
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
293
/* "Use a log level over 10 to enable all debugging options."
296
264
gnutls_global_set_log_level(11);
297
265
gnutls_global_set_log_function(debuggnutls);
300
/* OpenPGP credentials */
301
if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
268
/* openpgp credentials */
269
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
302
270
!= GNUTLS_E_SUCCESS) {
303
fprintf (stderr, "GnuTLS memory error: %s\n",
271
fprintf (stderr, "memory error: %s\n",
304
272
safer_gnutls_strerror(ret));
309
277
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
310
" and keyfile %s as GnuTLS credentials\n", pubkeyfile,
278
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
314
282
ret = gnutls_certificate_set_openpgp_key_file
315
(mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
283
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
316
284
if (ret != GNUTLS_E_SUCCESS) {
318
"Error[%d] while reading the OpenPGP key pair ('%s',"
319
" '%s')\n", ret, pubkeyfile, seckeyfile);
320
fprintf(stdout, "The GnuTLS error is: %s\n",
286
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
288
ret, CERTFILE, KEYFILE);
289
fprintf(stdout, "The Error is: %s\n",
321
290
safer_gnutls_strerror(ret));
325
/* GnuTLS server initialization */
326
ret = gnutls_dh_params_init(dh_params);
327
if (ret != GNUTLS_E_SUCCESS) {
328
fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
329
" %s\n", safer_gnutls_strerror(ret));
332
ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits);
333
if (ret != GNUTLS_E_SUCCESS) {
334
fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
335
safer_gnutls_strerror(ret));
339
gnutls_certificate_set_dh_params(mc->cred, *dh_params);
341
/* GnuTLS session creation */
342
ret = gnutls_init(session, GNUTLS_SERVER);
343
if (ret != GNUTLS_E_SUCCESS){
294
//GnuTLS server initialization
295
if ((ret = gnutls_dh_params_init (&es->dh_params))
296
!= GNUTLS_E_SUCCESS) {
297
fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
safer_gnutls_strerror(ret));
302
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
303
!= GNUTLS_E_SUCCESS) {
304
fprintf (stderr, "Error in prime generation: %s\n",
305
safer_gnutls_strerror(ret));
309
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
311
// GnuTLS session creation
312
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
313
!= GNUTLS_E_SUCCESS){
344
314
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
345
315
safer_gnutls_strerror(ret));
350
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
351
if (ret != GNUTLS_E_SUCCESS) {
352
fprintf(stderr, "Syntax error at: %s\n", err);
353
fprintf(stderr, "GnuTLS error: %s\n",
354
safer_gnutls_strerror(ret));
318
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
319
!= GNUTLS_E_SUCCESS) {
320
fprintf(stderr, "Syntax error at: %s\n", err);
321
fprintf(stderr, "GnuTLS error: %s\n",
322
safer_gnutls_strerror(ret));
359
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
361
if (ret != GNUTLS_E_SUCCESS) {
362
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
326
if ((ret = gnutls_credentials_set
327
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Error setting a credentials set: %s\n",
363
330
safer_gnutls_strerror(ret));
367
334
/* ignore client certificate if any. */
368
gnutls_certificate_server_set_request (*session,
335
gnutls_certificate_server_set_request (es->session,
369
336
GNUTLS_CERT_IGNORE);
371
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
338
gnutls_dh_set_prime_bits (es->session, DH_BITS);
376
/* Avahi log function callback */
377
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
378
__attribute__((unused)) const char *txt){}
343
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
__attribute__((unused)) const char *txt){}
380
/* Called when a Mandos server is found */
381
static int start_mandos_communication(const char *ip, uint16_t port,
382
AvahiIfIndex if_index,
346
int start_mandos_communication(const char *ip, uint16_t port,
347
unsigned int if_index){
385
349
struct sockaddr_in6 to;
350
encrypted_session es;
386
351
char *buffer = NULL;
387
352
char *decrypted_buffer;
388
353
size_t buffer_length = 0;
608
566
avahi_s_service_resolver_free(r);
611
static void browse_callback( AvahiSServiceBrowser *b,
612
AvahiIfIndex interface,
613
AvahiProtocol protocol,
614
AvahiBrowserEvent event,
618
AVAHI_GCC_UNUSED AvahiLookupResultFlags
621
mandos_context *mc = userdata;
622
assert(b); /* Spurious warning */
624
/* Called whenever a new services becomes available on the LAN or
625
is removed from the LAN */
629
case AVAHI_BROWSER_FAILURE:
631
fprintf(stderr, "(Avahi browser) %s\n",
632
avahi_strerror(avahi_server_errno(mc->server)));
633
avahi_simple_poll_quit(mc->simple_poll);
636
case AVAHI_BROWSER_NEW:
637
/* We ignore the returned Avahi resolver object. In the callback
638
function we free it. If the Avahi server is terminated before
639
the callback function is called the Avahi server will free the
642
if (!(avahi_s_service_resolver_new(mc->server, interface,
643
protocol, name, type, domain,
644
AVAHI_PROTO_INET6, 0,
645
resolve_callback, mc)))
646
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
647
name, avahi_strerror(avahi_server_errno(mc->server)));
650
case AVAHI_BROWSER_REMOVE:
653
case AVAHI_BROWSER_ALL_FOR_NOW:
654
case AVAHI_BROWSER_CACHE_EXHAUSTED:
656
fprintf(stderr, "No Mandos server found, still searching...\n");
569
static void browse_callback(
570
AvahiSServiceBrowser *b,
571
AvahiIfIndex interface,
572
AvahiProtocol protocol,
573
AvahiBrowserEvent event,
577
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
580
AvahiServer *s = userdata;
581
assert(b); /* Spurious warning */
583
/* Called whenever a new services becomes available on the LAN or
584
is removed from the LAN */
588
case AVAHI_BROWSER_FAILURE:
590
fprintf(stderr, "(Browser) %s\n",
591
avahi_strerror(avahi_server_errno(server)));
592
avahi_simple_poll_quit(simple_poll);
595
case AVAHI_BROWSER_NEW:
596
/* We ignore the returned resolver object. In the callback
597
function we free it. If the server is terminated before
598
the callback function is called the server will free
599
the resolver for us. */
601
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
603
AVAHI_PROTO_INET6, 0,
604
resolve_callback, s)))
605
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
606
avahi_strerror(avahi_server_errno(s)));
609
case AVAHI_BROWSER_REMOVE:
612
case AVAHI_BROWSER_ALL_FOR_NOW:
613
case AVAHI_BROWSER_CACHE_EXHAUSTED:
662
/* Combines file name and path and returns the malloced new
663
string. some sane checks could/should be added */
664
static const char *combinepath(const char *first, const char *second){
665
size_t f_len = strlen(first);
666
size_t s_len = strlen(second);
667
char *tmp = malloc(f_len + s_len + 2);
672
memcpy(tmp, first, f_len); /* Spurious warning */
676
memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
678
tmp[f_len + 1 + s_len] = '\0';
683
int main(int argc, char *argv[]){
618
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
619
AvahiServerConfig config;
684
620
AvahiSServiceBrowser *sb = NULL;
687
int exitcode = EXIT_SUCCESS;
623
int returncode = EXIT_SUCCESS;
688
624
const char *interface = "eth0";
689
struct ifreq network;
691
char *connect_to = NULL;
692
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
693
mandos_context mc = { .simple_poll = NULL, .server = NULL,
694
.dh_bits = 1024, .priority = "SECURE256"};
697
/* Temporary int to get the address of for getopt_long */
698
int debug_int = debug ? 1 : 0;
700
struct option long_options[] = {
701
{"debug", no_argument, &debug_int, 1},
702
{"connect", required_argument, NULL, 'c'},
703
{"interface", required_argument, NULL, 'i'},
704
{"keydir", required_argument, NULL, 'd'},
705
{"seckey", required_argument, NULL, 's'},
706
{"pubkey", required_argument, NULL, 'p'},
707
{"dh-bits", required_argument, NULL, 'D'},
708
{"priority", required_argument, NULL, 'P'},
711
int option_index = 0;
712
ret = getopt_long (argc, argv, "i:", long_options,
739
mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
746
mc.priority = optarg;
750
/* getopt_long() has already printed a message about the
751
unrcognized option, so just exit. */
755
/* Set the global debug flag from the temporary int */
756
debug = debug_int ? true : false;
759
pubkeyfile = combinepath(keydir, pubkeyfile);
760
if (pubkeyfile == NULL){
761
perror("combinepath");
762
exitcode = EXIT_FAILURE;
766
seckeyfile = combinepath(keydir, seckeyfile);
767
if (seckeyfile == NULL){
768
perror("combinepath");
772
if_index = (AvahiIfIndex) if_nametoindex(interface);
774
fprintf(stderr, "No such interface: \"%s\"\n", interface);
778
if(connect_to != NULL){
779
/* Connect directly, do not use Zeroconf */
780
/* (Mainly meant for debugging) */
781
char *address = strrchr(connect_to, ':');
783
fprintf(stderr, "No colon in address\n");
787
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
789
perror("Bad port number");
793
address = connect_to;
794
ret = start_mandos_communication(address, port, if_index, &mc);
802
/* If the interface is down, bring it up */
804
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
807
exitcode = EXIT_FAILURE;
810
strcpy(network.ifr_name, interface); /* Spurious warning */
811
ret = ioctl(sd, SIOCGIFFLAGS, &network);
813
perror("ioctl SIOCGIFFLAGS");
814
exitcode = EXIT_FAILURE;
817
if((network.ifr_flags & IFF_UP) == 0){
818
network.ifr_flags |= IFF_UP;
819
ret = ioctl(sd, SIOCSIFFLAGS, &network);
821
perror("ioctl SIOCSIFFLAGS");
822
exitcode = EXIT_FAILURE;
627
static struct option long_options[] = {
628
{"debug", no_argument, (int *)&debug, 1},
629
{"interface", required_argument, 0, 'i'},
632
int option_index = 0;
633
ret = getopt_long (argc, argv, "i:", long_options,
830
652
avahi_set_log_function(empty_log);
833
/* Initialize the pseudo-RNG for Avahi */
655
/* Initialize the psuedo-RNG */
834
656
srand((unsigned int) time(NULL));
836
/* Allocate main Avahi loop object */
837
mc.simple_poll = avahi_simple_poll_new();
838
if (mc.simple_poll == NULL) {
839
fprintf(stderr, "Avahi: Failed to create simple poll"
841
exitcode = EXIT_FAILURE;
846
AvahiServerConfig config;
847
/* Do not publish any local Zeroconf records */
848
avahi_server_config_init(&config);
849
config.publish_hinfo = 0;
850
config.publish_addresses = 0;
851
config.publish_workstation = 0;
852
config.publish_domain = 0;
854
/* Allocate a new server */
855
mc.server = avahi_server_new(avahi_simple_poll_get
856
(mc.simple_poll), &config, NULL,
859
/* Free the Avahi configuration data */
860
avahi_server_config_free(&config);
863
/* Check if creating the Avahi server object succeeded */
864
if (mc.server == NULL) {
865
fprintf(stderr, "Failed to create Avahi server: %s\n",
658
/* Allocate main loop object */
659
if (!(simple_poll = avahi_simple_poll_new())) {
660
fprintf(stderr, "Failed to create simple poll object.\n");
665
/* Do not publish any local records */
666
avahi_server_config_init(&config);
667
config.publish_hinfo = 0;
668
config.publish_addresses = 0;
669
config.publish_workstation = 0;
670
config.publish_domain = 0;
672
/* Allocate a new server */
673
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
&config, NULL, NULL, &error);
676
/* Free the configuration data */
677
avahi_server_config_free(&config);
679
/* Check if creating the server object succeeded */
681
fprintf(stderr, "Failed to create server: %s\n",
866
682
avahi_strerror(error));
867
exitcode = EXIT_FAILURE;
683
returncode = EXIT_FAILURE;
871
/* Create the Avahi service browser */
872
sb = avahi_s_service_browser_new(mc.server, if_index,
687
/* Create the service browser */
688
sb = avahi_s_service_browser_new(server,
690
if_nametoindex(interface),
873
691
AVAHI_PROTO_INET6,
874
692
"_mandos._tcp", NULL, 0,
875
browse_callback, &mc);
693
browse_callback, server);
877
695
fprintf(stderr, "Failed to create service browser: %s\n",
878
avahi_strerror(avahi_server_errno(mc.server)));
879
exitcode = EXIT_FAILURE;
696
avahi_strerror(avahi_server_errno(server)));
697
returncode = EXIT_FAILURE;
883
701
/* Run the main loop */
886
fprintf(stderr, "Starting Avahi loop search\n");
704
fprintf(stderr, "Starting avahi loop search\n");
889
avahi_simple_poll_loop(mc.simple_poll);
707
avahi_simple_poll_loop(simple_poll);
894
712
fprintf(stderr, "%s exiting\n", argv[0]);
897
715
/* Cleanup things */
899
717
avahi_s_service_browser_free(sb);
901
if (mc.server != NULL)
902
avahi_server_free(mc.server);
904
if (mc.simple_poll != NULL)
905
avahi_simple_poll_free(mc.simple_poll);
720
avahi_server_free(server);
723
avahi_simple_poll_free(simple_poll);