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>.
 
 
31
/* Needed by GPGME, specifically gpgme_data_seek() */
 
 
32
#define _LARGEFILE_SOURCE
 
 
33
#define _FILE_OFFSET_BITS 64
 
 
35
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
 
 
41
#include <net/if.h>             /* if_nametoindex */
 
 
42
#include <sys/ioctl.h>          /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
 
44
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
 
47
#include <avahi-core/core.h>
 
 
48
#include <avahi-core/lookup.h>
 
 
49
#include <avahi-core/log.h>
 
 
50
#include <avahi-common/simple-watch.h>
 
 
51
#include <avahi-common/malloc.h>
 
 
52
#include <avahi-common/error.h>
 
 
54
/* Mandos client part */
 
 
55
#include <sys/types.h>          /* socket(), inet_pton() */
 
 
56
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
 
57
                                   struct in6_addr, inet_pton() */
 
 
58
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
 
 
59
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
 
 
61
#include <unistd.h>             /* close() */
 
 
62
#include <netinet/in.h>
 
 
63
#include <stdbool.h>            /* true */
 
 
64
#include <string.h>             /* memset */
 
 
65
#include <arpa/inet.h>          /* inet_pton() */
 
 
66
#include <iso646.h>             /* not */
 
 
67
#include <net/if.h>             /* IF_NAMESIZE */
 
 
68
#include <argp.h>               /* struct argp_option,
 
 
69
                                   struct argp_state, struct argp,
 
 
72
#include <errno.h>              /* perror() */
 
 
75
#define BUFFER_SIZE 256
 
 
78
static const char *keydir = "/conf/conf.d/mandos";
 
 
79
static const char mandos_protocol_version[] = "1";
 
 
80
const char *argp_program_version = "mandosclient 0.9";
 
 
81
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
 
83
/* Used for passing in values through the Avahi callback functions */
 
 
85
  AvahiSimplePoll *simple_poll;
 
 
87
  gnutls_certificate_credentials_t cred;
 
 
89
  gnutls_dh_params_t dh_params;
 
 
94
 * Make room in "buffer" for at least BUFFER_SIZE additional bytes.
 
 
95
 * "buffer_capacity" is how much is currently allocated,
 
 
96
 * "buffer_length" is how much is already used.
 
 
98
size_t adjustbuffer(char **buffer, size_t buffer_length,
 
 
99
                  size_t buffer_capacity){
 
 
100
  if (buffer_length + BUFFER_SIZE > buffer_capacity){
 
 
101
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
 
105
    buffer_capacity += BUFFER_SIZE;
 
 
107
  return buffer_capacity;
 
 
111
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
 
 
112
 * Returns -1 on error
 
 
114
static ssize_t pgp_packet_decrypt (const char *cryptotext,
 
 
117
                                   const char *homedir){
 
 
118
  gpgme_data_t dh_crypto, dh_plain;
 
 
122
  size_t plaintext_capacity = 0;
 
 
123
  ssize_t plaintext_length = 0;
 
 
124
  gpgme_engine_info_t engine_info;
 
 
127
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
 
131
  gpgme_check_version(NULL);
 
 
132
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
 
133
  if (rc != GPG_ERR_NO_ERROR){
 
 
134
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
 
135
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
139
  /* Set GPGME home directory for the OpenPGP engine only */
 
 
140
  rc = gpgme_get_engine_info (&engine_info);
 
 
141
  if (rc != GPG_ERR_NO_ERROR){
 
 
142
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
 
143
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
146
  while(engine_info != NULL){
 
 
147
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
 
 
148
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
 
 
149
                            engine_info->file_name, homedir);
 
 
152
    engine_info = engine_info->next;
 
 
154
  if(engine_info == NULL){
 
 
155
    fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
 
 
159
  /* Create new GPGME data buffer from memory cryptotext */
 
 
160
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
 
162
  if (rc != GPG_ERR_NO_ERROR){
 
 
163
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
 
164
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
168
  /* Create new empty GPGME data buffer for the plaintext */
 
 
169
  rc = gpgme_data_new(&dh_plain);
 
 
170
  if (rc != GPG_ERR_NO_ERROR){
 
 
171
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
 
 
172
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
173
    gpgme_data_release(dh_crypto);
 
 
177
  /* Create new GPGME "context" */
 
 
178
  rc = gpgme_new(&ctx);
 
 
179
  if (rc != GPG_ERR_NO_ERROR){
 
 
180
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
 
181
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
182
    plaintext_length = -1;
 
 
186
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
 
188
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
 
 
189
  if (rc != GPG_ERR_NO_ERROR){
 
 
190
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
 
191
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
192
    plaintext_length = -1;
 
 
197
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
 
201
    gpgme_decrypt_result_t result;
 
 
202
    result = gpgme_op_decrypt_result(ctx);
 
 
204
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
 
206
      fprintf(stderr, "Unsupported algorithm: %s\n",
 
 
207
              result->unsupported_algorithm);
 
 
208
      fprintf(stderr, "Wrong key usage: %d\n",
 
 
209
              result->wrong_key_usage);
 
 
210
      if(result->file_name != NULL){
 
 
211
        fprintf(stderr, "File name: %s\n", result->file_name);
 
 
213
      gpgme_recipient_t recipient;
 
 
214
      recipient = result->recipients;
 
 
216
        while(recipient != NULL){
 
 
217
          fprintf(stderr, "Public key algorithm: %s\n",
 
 
218
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
 
219
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
 
220
          fprintf(stderr, "Secret key available: %s\n",
 
 
221
                  recipient->status == GPG_ERR_NO_SECKEY
 
 
223
          recipient = recipient->next;
 
 
229
  /* Seek back to the beginning of the GPGME plaintext data buffer */
 
 
230
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
 
 
231
    perror("pgpme_data_seek");
 
 
232
    plaintext_length = -1;
 
 
238
    plaintext_capacity = adjustbuffer(plaintext,
 
 
239
                                      (size_t)plaintext_length,
 
 
241
    if (plaintext_capacity == 0){
 
 
242
        perror("adjustbuffer");
 
 
243
        plaintext_length = -1;
 
 
247
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
 
249
    /* Print the data, if any */
 
 
255
      perror("gpgme_data_read");
 
 
256
      plaintext_length = -1;
 
 
259
    plaintext_length += ret;
 
 
263
    fprintf(stderr, "Decrypted password is: ");
 
 
264
    for(ssize_t i = 0; i < plaintext_length; i++){
 
 
265
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
 
267
    fprintf(stderr, "\n");
 
 
272
  /* Delete the GPGME cryptotext data buffer */
 
 
273
  gpgme_data_release(dh_crypto);
 
 
275
  /* Delete the GPGME plaintext data buffer */
 
 
276
  gpgme_data_release(dh_plain);
 
 
277
  return plaintext_length;
 
 
280
static const char * safer_gnutls_strerror (int value) {
 
 
281
  const char *ret = gnutls_strerror (value);
 
 
287
/* GnuTLS log function callback */
 
 
288
static void debuggnutls(__attribute__((unused)) int level,
 
 
290
  fprintf(stderr, "GnuTLS: %s", string);
 
 
293
static int init_gnutls_global(mandos_context *mc,
 
 
294
                              const char *pubkeyfile,
 
 
295
                              const char *seckeyfile){
 
 
299
    fprintf(stderr, "Initializing GnuTLS\n");
 
 
302
  if ((ret = gnutls_global_init ())
 
 
303
      != GNUTLS_E_SUCCESS) {
 
 
304
    fprintf (stderr, "GnuTLS global_init: %s\n",
 
 
305
             safer_gnutls_strerror(ret));
 
 
310
    /* "Use a log level over 10 to enable all debugging options."
 
 
313
    gnutls_global_set_log_level(11);
 
 
314
    gnutls_global_set_log_function(debuggnutls);
 
 
317
  /* OpenPGP credentials */
 
 
318
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
 
 
319
      != GNUTLS_E_SUCCESS) {
 
 
320
    fprintf (stderr, "GnuTLS memory error: %s\n",
 
 
321
             safer_gnutls_strerror(ret));
 
 
322
    gnutls_global_deinit ();
 
 
327
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
 
 
328
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
 
 
332
  ret = gnutls_certificate_set_openpgp_key_file
 
 
333
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
 
 
334
  if (ret != GNUTLS_E_SUCCESS) {
 
 
336
            "Error[%d] while reading the OpenPGP key pair ('%s',"
 
 
337
            " '%s')\n", ret, pubkeyfile, seckeyfile);
 
 
338
    fprintf(stdout, "The GnuTLS error is: %s\n",
 
 
339
            safer_gnutls_strerror(ret));
 
 
343
  /* GnuTLS server initialization */
 
 
344
  ret = gnutls_dh_params_init(&mc->dh_params);
 
 
345
  if (ret != GNUTLS_E_SUCCESS) {
 
 
346
    fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
 
 
347
             " %s\n", safer_gnutls_strerror(ret));
 
 
350
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
 
351
  if (ret != GNUTLS_E_SUCCESS) {
 
 
352
    fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
 
 
353
             safer_gnutls_strerror(ret));
 
 
357
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
 
363
  gnutls_certificate_free_credentials (mc->cred);
 
 
364
  gnutls_global_deinit ();
 
 
369
static int init_gnutls_session(mandos_context *mc,
 
 
370
                               gnutls_session_t *session){
 
 
372
  /* GnuTLS session creation */
 
 
373
  ret = gnutls_init(session, GNUTLS_SERVER);
 
 
374
  if (ret != GNUTLS_E_SUCCESS){
 
 
375
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
 
 
376
            safer_gnutls_strerror(ret));
 
 
381
    ret = gnutls_priority_set_direct(*session, mc->priority, &err);
 
 
382
    if (ret != GNUTLS_E_SUCCESS) {
 
 
383
      fprintf(stderr, "Syntax error at: %s\n", err);
 
 
384
      fprintf(stderr, "GnuTLS error: %s\n",
 
 
385
              safer_gnutls_strerror(ret));
 
 
386
      gnutls_deinit (*session);
 
 
391
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
 
393
  if (ret != GNUTLS_E_SUCCESS) {
 
 
394
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
 
 
395
            safer_gnutls_strerror(ret));
 
 
396
    gnutls_deinit (*session);
 
 
400
  /* ignore client certificate if any. */
 
 
401
  gnutls_certificate_server_set_request (*session,
 
 
404
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
 
 
409
/* Avahi log function callback */
 
 
410
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
 
411
                      __attribute__((unused)) const char *txt){}
 
 
413
/* Called when a Mandos server is found */
 
 
414
static int start_mandos_communication(const char *ip, uint16_t port,
 
 
415
                                      AvahiIfIndex if_index,
 
 
418
  union { struct sockaddr in; struct sockaddr_in6 in6; } to;
 
 
420
  char *decrypted_buffer;
 
 
421
  size_t buffer_length = 0;
 
 
422
  size_t buffer_capacity = 0;
 
 
423
  ssize_t decrypted_buffer_size;
 
 
426
  char interface[IF_NAMESIZE];
 
 
427
  gnutls_session_t session;
 
 
429
  ret = init_gnutls_session (mc, &session);
 
 
435
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
 
 
439
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
 
 
446
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
 
447
      perror("if_indextoname");
 
 
450
    fprintf(stderr, "Binding to interface %s\n", interface);
 
 
453
  memset(&to,0,sizeof(to));     /* Spurious warning */
 
 
454
  to.in6.sin6_family = AF_INET6;
 
 
455
  /* It would be nice to have a way to detect if we were passed an
 
 
456
     IPv4 address here.   Now we assume an IPv6 address. */
 
 
457
  ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
 
 
463
    fprintf(stderr, "Bad address: %s\n", ip);
 
 
466
  to.in6.sin6_port = htons(port);       /* Spurious warning */
 
 
468
  to.in6.sin6_scope_id = (uint32_t)if_index;
 
 
471
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
 
472
    char addrstr[INET6_ADDRSTRLEN] = "";
 
 
473
    if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
 
 
474
                 sizeof(addrstr)) == NULL){
 
 
477
      if(strcmp(addrstr, ip) != 0){
 
 
478
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
 
483
  ret = connect(tcp_sd, &to.in, sizeof(to));
 
 
489
  const char *out = mandos_protocol_version;
 
 
492
    size_t out_size = strlen(out);
 
 
493
    ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
 
494
                                   out_size - written));
 
 
500
    written += (size_t)ret;
 
 
501
    if(written < out_size){
 
 
504
      if (out == mandos_protocol_version){
 
 
514
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
 
 
517
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
 
 
519
  ret = gnutls_handshake (session);
 
 
521
  if (ret != GNUTLS_E_SUCCESS){
 
 
523
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
 
530
  /* Read OpenPGP packet that contains the wanted password */
 
 
533
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
 
 
538
    buffer_capacity = adjustbuffer(&buffer, buffer_length,
 
 
540
    if (buffer_capacity == 0){
 
 
541
      perror("adjustbuffer");
 
 
546
    ret = gnutls_record_recv(session, buffer+buffer_length,
 
 
553
      case GNUTLS_E_INTERRUPTED:
 
 
556
      case GNUTLS_E_REHANDSHAKE:
 
 
557
        ret = gnutls_handshake (session);
 
 
559
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
 
566
        fprintf(stderr, "Unknown error while reading data from"
 
 
567
                " encrypted session with Mandos server\n");
 
 
569
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
 
 
573
      buffer_length += (size_t) ret;
 
 
578
    fprintf(stderr, "Closing TLS session\n");
 
 
581
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
 
 
583
  if (buffer_length > 0){
 
 
584
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
 
588
    if (decrypted_buffer_size >= 0){
 
 
590
      while(written < (size_t) decrypted_buffer_size){
 
 
591
        ret = (int)fwrite (decrypted_buffer + written, 1,
 
 
592
                           (size_t)decrypted_buffer_size - written,
 
 
594
        if(ret == 0 and ferror(stdout)){
 
 
596
            fprintf(stderr, "Error writing encrypted data: %s\n",
 
 
602
        written += (size_t)ret;
 
 
604
      free(decrypted_buffer);
 
 
610
  /* Shutdown procedure */
 
 
615
  gnutls_deinit (session);
 
 
619
static void resolve_callback(AvahiSServiceResolver *r,
 
 
620
                             AvahiIfIndex interface,
 
 
621
                             AVAHI_GCC_UNUSED AvahiProtocol protocol,
 
 
622
                             AvahiResolverEvent event,
 
 
626
                             const char *host_name,
 
 
627
                             const AvahiAddress *address,
 
 
629
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
 
630
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
633
  mandos_context *mc = userdata;
 
 
634
  assert(r);                    /* Spurious warning */
 
 
636
  /* Called whenever a service has been resolved successfully or
 
 
641
  case AVAHI_RESOLVER_FAILURE:
 
 
642
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
 
 
643
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
 
644
            avahi_strerror(avahi_server_errno(mc->server)));
 
 
647
  case AVAHI_RESOLVER_FOUND:
 
 
649
      char ip[AVAHI_ADDRESS_STR_MAX];
 
 
650
      avahi_address_snprint(ip, sizeof(ip), address);
 
 
652
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %d) on"
 
 
653
                " port %d\n", name, host_name, ip, interface, port);
 
 
655
      int ret = start_mandos_communication(ip, port, interface, mc);
 
 
661
  avahi_s_service_resolver_free(r);
 
 
664
static void browse_callback( AvahiSServiceBrowser *b,
 
 
665
                             AvahiIfIndex interface,
 
 
666
                             AvahiProtocol protocol,
 
 
667
                             AvahiBrowserEvent event,
 
 
671
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
674
  mandos_context *mc = userdata;
 
 
675
  assert(b);                    /* Spurious warning */
 
 
677
  /* Called whenever a new services becomes available on the LAN or
 
 
678
     is removed from the LAN */
 
 
682
  case AVAHI_BROWSER_FAILURE:
 
 
684
    fprintf(stderr, "(Avahi browser) %s\n",
 
 
685
            avahi_strerror(avahi_server_errno(mc->server)));
 
 
686
    avahi_simple_poll_quit(mc->simple_poll);
 
 
689
  case AVAHI_BROWSER_NEW:
 
 
690
    /* We ignore the returned Avahi resolver object. In the callback
 
 
691
       function we free it. If the Avahi server is terminated before
 
 
692
       the callback function is called the Avahi server will free the
 
 
695
    if (!(avahi_s_service_resolver_new(mc->server, interface,
 
 
696
                                       protocol, name, type, domain,
 
 
697
                                       AVAHI_PROTO_INET6, 0,
 
 
698
                                       resolve_callback, mc)))
 
 
699
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
 
 
700
              name, avahi_strerror(avahi_server_errno(mc->server)));
 
 
703
  case AVAHI_BROWSER_REMOVE:
 
 
706
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
 
707
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
 
709
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
 
715
/* Combines file name and path and returns the malloced new
 
 
716
   string. some sane checks could/should be added */
 
 
717
static const char *combinepath(const char *first, const char *second){
 
 
718
  size_t f_len = strlen(first);
 
 
719
  size_t s_len = strlen(second);
 
 
720
  char *tmp = malloc(f_len + s_len + 2);
 
 
725
    memcpy(tmp, first, f_len);  /* Spurious warning */
 
 
729
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
 
 
731
  tmp[f_len + 1 + s_len] = '\0';
 
 
736
int main(int argc, char *argv[]){
 
 
737
    AvahiSServiceBrowser *sb = NULL;
 
 
740
    int exitcode = EXIT_SUCCESS;
 
 
741
    const char *interface = "eth0";
 
 
742
    struct ifreq network;
 
 
746
    char *connect_to = NULL;
 
 
747
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
 
748
    const char *pubkeyfile = "pubkey.txt";
 
 
749
    const char *seckeyfile = "seckey.txt";
 
 
750
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
 
751
                          .dh_bits = 1024, .priority = "SECURE256"};
 
 
752
    bool gnutls_initalized = false;
 
 
755
      struct argp_option options[] = {
 
 
756
        { .name = "debug", .key = 128,
 
 
757
          .doc = "Debug mode", .group = 3 },
 
 
758
        { .name = "connect", .key = 'c',
 
 
760
          .doc = "Connect directly to a sepcified mandos server",
 
 
762
        { .name = "interface", .key = 'i',
 
 
764
          .doc = "Interface that Avahi will conntect through",
 
 
766
        { .name = "keydir", .key = 'd',
 
 
768
          .doc = "Directory where the openpgp keyring is",
 
 
770
        { .name = "seckey", .key = 's',
 
 
772
          .doc = "Secret openpgp key for gnutls authentication",
 
 
774
        { .name = "pubkey", .key = 'p',
 
 
776
          .doc = "Public openpgp key for gnutls authentication",
 
 
778
        { .name = "dh-bits", .key = 129,
 
 
780
          .doc = "dh-bits to use in gnutls communication",
 
 
782
        { .name = "priority", .key = 130,
 
 
784
          .doc = "GNUTLS priority", .group = 1 },
 
 
789
      error_t parse_opt (int key, char *arg,
 
 
790
                         struct argp_state *state) {
 
 
791
        /* Get the INPUT argument from `argp_parse', which we know is
 
 
792
           a pointer to our plugin list pointer. */
 
 
814
          mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
 
 
829
          return ARGP_ERR_UNKNOWN;
 
 
834
      struct argp argp = { .options = options, .parser = parse_opt,
 
 
836
                           .doc = "Mandos client -- Get and decrypt"
 
 
837
                           " passwords from mandos server" };
 
 
838
      argp_parse (&argp, argc, argv, 0, 0, NULL);
 
 
841
    pubkeyfile = combinepath(keydir, pubkeyfile);
 
 
842
    if (pubkeyfile == NULL){
 
 
843
      perror("combinepath");
 
 
844
      exitcode = EXIT_FAILURE;
 
 
848
    seckeyfile = combinepath(keydir, seckeyfile);
 
 
849
    if (seckeyfile == NULL){
 
 
850
      perror("combinepath");
 
 
854
    ret = init_gnutls_global(&mc, pubkeyfile, seckeyfile);
 
 
856
      fprintf(stderr, "init_gnutls_global\n");
 
 
859
      gnutls_initalized = true;
 
 
875
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
 
877
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
 
881
    if(connect_to != NULL){
 
 
882
      /* Connect directly, do not use Zeroconf */
 
 
883
      /* (Mainly meant for debugging) */
 
 
884
      char *address = strrchr(connect_to, ':');
 
 
886
        fprintf(stderr, "No colon in address\n");
 
 
887
        exitcode = EXIT_FAILURE;
 
 
891
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
 
 
893
        perror("Bad port number");
 
 
894
        exitcode = EXIT_FAILURE;
 
 
898
      address = connect_to;
 
 
899
      ret = start_mandos_communication(address, port, if_index, &mc);
 
 
901
        exitcode = EXIT_FAILURE;
 
 
903
        exitcode = EXIT_SUCCESS;
 
 
908
    /* If the interface is down, bring it up */
 
 
910
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
 
913
        exitcode = EXIT_FAILURE;
 
 
916
      strcpy(network.ifr_name, interface); /* Spurious warning */
 
 
917
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
 
919
        perror("ioctl SIOCGIFFLAGS");
 
 
920
        exitcode = EXIT_FAILURE;
 
 
923
      if((network.ifr_flags & IFF_UP) == 0){
 
 
924
        network.ifr_flags |= IFF_UP;
 
 
925
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
 
927
          perror("ioctl SIOCSIFFLAGS");
 
 
928
          exitcode = EXIT_FAILURE;
 
 
936
      avahi_set_log_function(empty_log);
 
 
939
    /* Initialize the pseudo-RNG for Avahi */
 
 
940
    srand((unsigned int) time(NULL));
 
 
942
    /* Allocate main Avahi loop object */
 
 
943
    mc.simple_poll = avahi_simple_poll_new();
 
 
944
    if (mc.simple_poll == NULL) {
 
 
945
        fprintf(stderr, "Avahi: Failed to create simple poll"
 
 
947
        exitcode = EXIT_FAILURE;
 
 
952
      AvahiServerConfig config;
 
 
953
      /* Do not publish any local Zeroconf records */
 
 
954
      avahi_server_config_init(&config);
 
 
955
      config.publish_hinfo = 0;
 
 
956
      config.publish_addresses = 0;
 
 
957
      config.publish_workstation = 0;
 
 
958
      config.publish_domain = 0;
 
 
960
      /* Allocate a new server */
 
 
961
      mc.server = avahi_server_new(avahi_simple_poll_get
 
 
962
                                   (mc.simple_poll), &config, NULL,
 
 
965
      /* Free the Avahi configuration data */
 
 
966
      avahi_server_config_free(&config);
 
 
969
    /* Check if creating the Avahi server object succeeded */
 
 
970
    if (mc.server == NULL) {
 
 
971
        fprintf(stderr, "Failed to create Avahi server: %s\n",
 
 
972
                avahi_strerror(error));
 
 
973
        exitcode = EXIT_FAILURE;
 
 
977
    /* Create the Avahi service browser */
 
 
978
    sb = avahi_s_service_browser_new(mc.server, if_index,
 
 
980
                                     "_mandos._tcp", NULL, 0,
 
 
981
                                     browse_callback, &mc);
 
 
983
        fprintf(stderr, "Failed to create service browser: %s\n",
 
 
984
                avahi_strerror(avahi_server_errno(mc.server)));
 
 
985
        exitcode = EXIT_FAILURE;
 
 
989
    /* Run the main loop */
 
 
992
      fprintf(stderr, "Starting Avahi loop search\n");
 
 
995
    avahi_simple_poll_loop(mc.simple_poll);
 
 
1000
      fprintf(stderr, "%s exiting\n", argv[0]);
 
 
1003
    /* Cleanup things */
 
 
1005
        avahi_s_service_browser_free(sb);
 
 
1007
    if (mc.server != NULL)
 
 
1008
        avahi_server_free(mc.server);
 
 
1010
    if (mc.simple_poll != NULL)
 
 
1011
        avahi_simple_poll_free(mc.simple_poll);
 
 
1015
    if (gnutls_initalized){
 
 
1016
      gnutls_certificate_free_credentials (mc.cred);
 
 
1017
      gnutls_global_deinit ();