1
/* -*- coding: utf-8 -*- */
3
* Mandos client - get and decrypt data from a Mandos server
5
* This program is partly derived from an example program for an Avahi
6
* service browser, downloaded from
7
* <http://avahi.org/browser/examples/core-browse-services.c>. This
8
* includes the following functions: "resolve_callback",
9
* "browse_callback", and parts of "main".
12
* Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
14
* This program is free software: you can redistribute it and/or
15
* modify it under the terms of the GNU General Public License as
16
* published by the Free Software Foundation, either version 3 of the
17
* License, or (at your option) any later version.
19
* This program is distributed in the hope that it will be useful, but
20
* WITHOUT ANY WARRANTY; without even the implied warranty of
21
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
* General Public License for more details.
24
* You should have received a copy of the GNU General Public License
25
* along with this program. If not, see
26
* <http://www.gnu.org/licenses/>.
28
* Contact the authors at <mandos@fukt.bsnet.se>.
2
This file is part of avahi.
4
avahi is free software; you can redistribute it and/or modify it
5
under the terms of the GNU Lesser General Public License as
6
published by the Free Software Foundation; either version 2.1 of the
7
License, or (at your option) any later version.
9
avahi is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12
Public License for more details.
14
You should have received a copy of the GNU Lesser General Public
15
License along with avahi; if not, write to the Free Software
16
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
31
/* Needed by GPGME, specifically gpgme_data_seek() */
32
20
#define _LARGEFILE_SOURCE
33
21
#define _FILE_OFFSET_BITS 64
35
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */
38
24
#include <assert.h>
39
25
#include <stdlib.h>
41
27
#include <net/if.h> /* if_nametoindex */
42
#include <sys/ioctl.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
43
#include <net/if.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
45
29
#include <avahi-core/core.h>
46
30
#include <avahi-core/lookup.h>
67
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"
73
59
#define BUFFER_SIZE 256
74
60
#define DH_BITS 1024
76
static const char *certdir = "/conf/conf.d/mandos";
77
static const char *certfile = "openpgp-client.txt";
78
static const char *certkey = "openpgp-client-key.txt";
80
62
bool debug = false;
82
const char mandos_protocol_version[] = "1";
85
AvahiSimplePoll *simple_poll;
65
gnutls_session_t session;
87
66
gnutls_certificate_credentials_t cred;
92
size_t adjustbuffer(char *buffer, size_t buffer_length,
93
size_t buffer_capacity){
94
if (buffer_length + BUFFER_SIZE > buffer_capacity){
95
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
99
buffer_capacity += BUFFER_SIZE;
101
return buffer_capacity;
104
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
106
const char *homedir){
67
gnutls_dh_params_t dh_params;
71
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
107
72
gpgme_data_t dh_crypto, dh_plain;
111
76
size_t new_packet_capacity = 0;
112
ssize_t new_packet_length = 0;
77
size_t new_packet_length = 0;
113
78
gpgme_engine_info_t engine_info;
116
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
81
fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
120
85
gpgme_check_version(NULL);
121
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
122
if (rc != GPG_ERR_NO_ERROR){
123
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
124
gpgme_strsource(rc), gpgme_strerror(rc));
86
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
128
88
/* Set GPGME home directory */
129
89
rc = gpgme_get_engine_info (&engine_info);
287
237
gnutls_global_set_log_function(debuggnutls);
290
241
/* openpgp credentials */
291
242
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
292
243
!= GNUTLS_E_SUCCESS) {
293
fprintf (stderr, "memory error: %s\n",
294
safer_gnutls_strerror(ret));
244
fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
299
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
300
" and keyfile %s as GnuTLS credentials\n", certfile,
249
fprintf(stderr, "Attempting to use openpgp certificate %s"
250
" and keyfile %s as gnutls credentials\n", CERTFILE, KEYFILE);
304
253
ret = gnutls_certificate_set_openpgp_key_file
305
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
254
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
306
255
if (ret != GNUTLS_E_SUCCESS) {
308
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
310
ret, certfile, certkey);
257
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
258
ret, CERTFILE, KEYFILE);
311
259
fprintf(stdout, "The Error is: %s\n",
312
260
safer_gnutls_strerror(ret));
316
//GnuTLS server initialization
264
//Gnutls server initialization
317
265
if ((ret = gnutls_dh_params_init (&es->dh_params))
318
266
!= GNUTLS_E_SUCCESS) {
319
267
fprintf (stderr, "Error in dh parameter initialization: %s\n",
320
268
safer_gnutls_strerror(ret));
324
272
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
325
273
!= GNUTLS_E_SUCCESS) {
326
274
fprintf (stderr, "Error in prime generation: %s\n",
327
275
safer_gnutls_strerror(ret));
331
279
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
333
// GnuTLS session creation
281
// Gnutls session creation
334
282
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
335
283
!= GNUTLS_E_SUCCESS){
336
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
284
fprintf(stderr, "Error in gnutls session initialization: %s\n",
337
285
safer_gnutls_strerror(ret));
340
if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
288
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
341
289
!= GNUTLS_E_SUCCESS) {
342
290
fprintf(stderr, "Syntax error at: %s\n", err);
343
fprintf(stderr, "GnuTLS error: %s\n",
291
fprintf(stderr, "Gnutls error: %s\n",
344
292
safer_gnutls_strerror(ret));
348
296
if ((ret = gnutls_credentials_set
349
297
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
350
298
!= GNUTLS_E_SUCCESS) {
575
static void resolve_callback( AvahiSServiceResolver *r,
576
AvahiIfIndex interface,
577
AVAHI_GCC_UNUSED AvahiProtocol protocol,
578
AvahiResolverEvent event,
582
const char *host_name,
583
const AvahiAddress *address,
585
AVAHI_GCC_UNUSED AvahiStringList *txt,
586
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
587
AVAHI_GCC_UNUSED void* userdata) {
588
mandos_context *mc = userdata;
589
assert(r); /* Spurious warning */
591
/* Called whenever a service has been resolved successfully or
596
case AVAHI_RESOLVER_FAILURE:
597
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
598
" type '%s' in domain '%s': %s\n", name, type, domain,
599
avahi_strerror(avahi_server_errno(mc->server)));
602
case AVAHI_RESOLVER_FOUND:
604
char ip[AVAHI_ADDRESS_STR_MAX];
605
avahi_address_snprint(ip, sizeof(ip), address);
607
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
608
" port %d\n", name, host_name, ip, port);
610
int ret = start_mandos_communication(ip, port, interface, mc);
616
avahi_s_service_resolver_free(r);
619
static void browse_callback( AvahiSServiceBrowser *b,
620
AvahiIfIndex interface,
621
AvahiProtocol protocol,
622
AvahiBrowserEvent event,
626
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
628
mandos_context *mc = userdata;
629
assert(b); /* Spurious warning */
631
/* Called whenever a new services becomes available on the LAN or
632
is removed from the LAN */
636
case AVAHI_BROWSER_FAILURE:
638
fprintf(stderr, "(Browser) %s\n",
639
avahi_strerror(avahi_server_errno(mc->server)));
640
avahi_simple_poll_quit(mc->simple_poll);
643
case AVAHI_BROWSER_NEW:
644
/* We ignore the returned resolver object. In the callback
645
function we free it. If the server is terminated before
646
the callback function is called the server will free
647
the resolver for us. */
649
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
651
AVAHI_PROTO_INET6, 0,
652
resolve_callback, mc)))
653
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
654
avahi_strerror(avahi_server_errno(s)));
657
case AVAHI_BROWSER_REMOVE:
660
case AVAHI_BROWSER_ALL_FOR_NOW:
661
case AVAHI_BROWSER_CACHE_EXHAUSTED:
666
/* Combines file name and path and returns the malloced new
667
string. some sane checks could/should be added */
668
static const char *combinepath(const char *first, const char *second){
669
size_t f_len = strlen(first);
670
size_t s_len = strlen(second);
671
char *tmp = malloc(f_len + s_len + 2);
676
memcpy(tmp, first, f_len);
680
memcpy(tmp + f_len + 1, second, s_len);
682
tmp[f_len + 1 + s_len] = '\0';
465
static AvahiSimplePoll *simple_poll = NULL;
466
static AvahiServer *server = NULL;
468
static void resolve_callback(
469
AvahiSServiceResolver *r,
470
AVAHI_GCC_UNUSED AvahiIfIndex interface,
471
AVAHI_GCC_UNUSED AvahiProtocol protocol,
472
AvahiResolverEvent event,
476
const char *host_name,
477
const AvahiAddress *address,
479
AvahiStringList *txt,
480
AvahiLookupResultFlags flags,
481
AVAHI_GCC_UNUSED void* userdata) {
485
/* Called whenever a service has been resolved successfully or timed out */
488
case AVAHI_RESOLVER_FAILURE:
489
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)));
492
case AVAHI_RESOLVER_FOUND: {
493
char ip[AVAHI_ADDRESS_STR_MAX];
494
avahi_address_snprint(ip, sizeof(ip), address);
496
fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
498
int ret = start_mandos_communcation(ip, port);
506
avahi_s_service_resolver_free(r);
509
static void browse_callback(
510
AvahiSServiceBrowser *b,
511
AvahiIfIndex interface,
512
AvahiProtocol protocol,
513
AvahiBrowserEvent event,
517
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
520
AvahiServer *s = userdata;
523
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
527
case AVAHI_BROWSER_FAILURE:
529
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
530
avahi_simple_poll_quit(simple_poll);
533
case AVAHI_BROWSER_NEW:
534
/* We ignore the returned resolver object. In the callback
535
function we free it. If the server is terminated before
536
the callback function is called the server will free
537
the resolver for us. */
539
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
540
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
544
case AVAHI_BROWSER_REMOVE:
547
case AVAHI_BROWSER_ALL_FOR_NOW:
548
case AVAHI_BROWSER_CACHE_EXHAUSTED:
687
553
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
688
554
AvahiServerConfig config;
689
555
AvahiSServiceBrowser *sb = NULL;
556
const char db[] = "--debug";
692
559
int returncode = EXIT_SUCCESS;
693
const char *interface = "eth0";
694
struct ifreq network;
696
char *connect_to = NULL;
697
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
698
mandos_context mc = { .simple_poll = NULL, .server = NULL,
699
.dh_bits = 2048, .priority = "SECURE256"};
702
static struct option long_options[] = {
703
{"debug", no_argument, (int *)&debug, 1},
704
{"connect", required_argument, 0, 'C'},
705
{"interface", required_argument, 0, 'i'},
706
{"certdir", required_argument, 0, 'd'},
707
{"certkey", required_argument, 0, 'c'},
708
{"certfile", required_argument, 0, 'k'},
709
{"dh_bits", required_argument, 0, 'D'},
710
{"priority", required_argument, 0, 'p'},
713
int option_index = 0;
714
ret = getopt_long (argc, argv, "i:", long_options,
743
tmp = strtol(optarg, NULL, 10);
744
if (errno == ERANGE){
560
char *basename = rindex(argv[0], '/');
561
if(basename == NULL){
567
char *program_name = malloc(strlen(basename) + sizeof(db));
569
if (program_name == NULL){
574
program_name[0] = '\0';
576
for (int i = 1; i < argc; i++){
577
if (not strncmp(argv[i], db, 5)){
578
strcat(strcat(strcat(program_name, db ), "="), basename);
579
if(not strcmp(argv[i], db) or not strcmp(argv[i], program_name)){
752
mc.priority = optarg;
759
certfile = combinepath(certdir, certfile);
760
if (certfile == NULL){
761
perror("combinepath");
762
returncode = EXIT_FAILURE;
766
certkey = combinepath(certdir, certkey);
767
if (certkey == NULL){
768
perror("combinepath");
769
returncode = EXIT_FAILURE;
773
if_index = (AvahiIfIndex) if_nametoindex(interface);
775
fprintf(stderr, "No such interface: \"%s\"\n", interface);
779
if(connect_to != NULL){
780
/* Connect directly, do not use Zeroconf */
781
/* (Mainly meant for debugging) */
782
char *address = strrchr(connect_to, ':');
784
fprintf(stderr, "No colon in address\n");
788
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
790
perror("Bad port number");
794
address = connect_to;
795
ret = start_mandos_communication(address, port, if_index);
803
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
806
returncode = EXIT_FAILURE;
809
strcpy(network.ifr_name, interface);
810
ret = ioctl(sd, SIOCGIFFLAGS, &network);
813
perror("ioctl SIOCGIFFLAGS");
814
returncode = 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
returncode = EXIT_FAILURE;
829
587
avahi_set_log_function(empty_log);
832
590
/* Initialize the psuedo-RNG */
833
srand((unsigned int) time(NULL));
835
593
/* Allocate main loop object */
836
if (!(mc.simple_poll = avahi_simple_poll_new())) {
594
if (!(simple_poll = avahi_simple_poll_new())) {
837
595
fprintf(stderr, "Failed to create simple poll object.\n");
838
returncode = EXIT_FAILURE;