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 © 2008,2009 Teddy Hogeborn
 
 
13
 * Copyright © 2008,2009 Björn Påhlsson
 
 
15
 * This program is free software: you can redistribute it and/or
 
 
16
 * modify it under the terms of the GNU General Public License as
 
 
17
 * published by the Free Software Foundation, either version 3 of the
 
 
18
 * License, or (at your option) any later version.
 
 
20
 * This program is distributed in the hope that it will be useful, but
 
 
21
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
 
23
 * General Public License for more details.
 
 
25
 * You should have received a copy of the GNU General Public License
 
 
26
 * along with this program.  If not, see
 
 
27
 * <http://www.gnu.org/licenses/>.
 
 
29
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
 
32
/* Needed by GPGME, specifically gpgme_data_seek() */
 
 
33
#define _LARGEFILE_SOURCE
 
 
34
#define _FILE_OFFSET_BITS 64
 
 
36
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
 
38
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
 
39
                                   stdout, ferror(), remove() */
 
 
40
#include <stdint.h>             /* uint16_t, uint32_t */
 
 
41
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
 
42
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
 
 
44
#include <stdbool.h>            /* bool, false, true */
 
 
45
#include <string.h>             /* memset(), strcmp(), strlen(),
 
 
46
                                   strerror(), asprintf(), strcpy() */
 
 
47
#include <sys/ioctl.h>          /* ioctl */
 
 
48
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
 
 
49
                                   sockaddr_in6, PF_INET6,
 
 
50
                                   SOCK_STREAM, uid_t, gid_t, open(),
 
 
52
#include <sys/stat.h>           /* open() */
 
 
53
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
 
54
                                   inet_pton(), connect() */
 
 
55
#include <fcntl.h>              /* open() */
 
 
56
#include <dirent.h>             /* opendir(), struct dirent, readdir()
 
 
58
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
 
 
60
#include <assert.h>             /* assert() */
 
 
61
#include <errno.h>              /* perror(), errno */
 
 
62
#include <time.h>               /* nanosleep(), time() */
 
 
63
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
 
64
                                   SIOCSIFFLAGS, if_indextoname(),
 
 
65
                                   if_nametoindex(), IF_NAMESIZE */
 
 
66
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
 
 
67
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
 
 
69
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
 
70
                                   getuid(), getgid(), setuid(),
 
 
72
#include <arpa/inet.h>          /* inet_pton(), htons */
 
 
73
#include <iso646.h>             /* not, or, and */
 
 
74
#include <argp.h>               /* struct argp_option, error_t, struct
 
 
75
                                   argp_state, struct argp,
 
 
76
                                   argp_parse(), ARGP_KEY_ARG,
 
 
77
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
 
 
78
#include <signal.h>             /* sigemptyset(), sigaddset(),
 
 
79
                                   sigaction(), SIGTERM, sigaction,
 
 
83
#include <sys/klog.h>           /* klogctl() */
 
 
84
#endif  /* __linux__ */
 
 
87
/* All Avahi types, constants and functions
 
 
90
#include <avahi-core/core.h>
 
 
91
#include <avahi-core/lookup.h>
 
 
92
#include <avahi-core/log.h>
 
 
93
#include <avahi-common/simple-watch.h>
 
 
94
#include <avahi-common/malloc.h>
 
 
95
#include <avahi-common/error.h>
 
 
98
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
 
101
                                   init_gnutls_session(),
 
 
103
#include <gnutls/openpgp.h>
 
 
104
                          /* gnutls_certificate_set_openpgp_key_file(),
 
 
105
                                   GNUTLS_OPENPGP_FMT_BASE64 */
 
 
108
#include <gpgme.h>              /* All GPGME types, constants and
 
 
111
                                   GPGME_PROTOCOL_OpenPGP,
 
 
114
#define BUFFER_SIZE 256
 
 
116
#define PATHDIR "/conf/conf.d/mandos"
 
 
117
#define SECKEY "seckey.txt"
 
 
118
#define PUBKEY "pubkey.txt"
 
 
121
static const char mandos_protocol_version[] = "1";
 
 
122
const char *argp_program_version = "mandos-client " VERSION;
 
 
123
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
 
125
/* Used for passing in values through the Avahi callback functions */
 
 
127
  AvahiSimplePoll *simple_poll;
 
 
129
  gnutls_certificate_credentials_t cred;
 
 
130
  unsigned int dh_bits;
 
 
131
  gnutls_dh_params_t dh_params;
 
 
132
  const char *priority;
 
 
136
/* global context so signal handler can reach it*/
 
 
137
mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
 
138
                      .dh_bits = 1024, .priority = "SECURE256"
 
 
139
                      ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
 
142
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
 
143
 * bytes. "buffer_capacity" is how much is currently allocated,
 
 
144
 * "buffer_length" is how much is already used.
 
 
146
size_t incbuffer(char **buffer, size_t buffer_length,
 
 
147
                  size_t buffer_capacity){
 
 
148
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
 
149
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
 
153
    buffer_capacity += BUFFER_SIZE;
 
 
155
  return buffer_capacity;
 
 
161
static bool init_gpgme(const char *seckey,
 
 
162
                       const char *pubkey, const char *tempdir){
 
 
165
  gpgme_engine_info_t engine_info;
 
 
169
   * Helper function to insert pub and seckey to the engine keyring.
 
 
171
  bool import_key(const char *filename){
 
 
173
    gpgme_data_t pgp_data;
 
 
175
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
 
181
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
 
182
    if(rc != GPG_ERR_NO_ERROR){
 
 
183
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
 
184
              gpgme_strsource(rc), gpgme_strerror(rc));
 
 
188
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
 
189
    if(rc != GPG_ERR_NO_ERROR){
 
 
190
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
 
 
191
              gpgme_strsource(rc), gpgme_strerror(rc));
 
 
195
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
 
199
    gpgme_data_release(pgp_data);
 
 
204
    fprintf(stderr, "Initializing GPGME\n");
 
 
208
  gpgme_check_version(NULL);
 
 
209
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
 
210
  if(rc != GPG_ERR_NO_ERROR){
 
 
211
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
 
212
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
216
    /* Set GPGME home directory for the OpenPGP engine only */
 
 
217
  rc = gpgme_get_engine_info(&engine_info);
 
 
218
  if(rc != GPG_ERR_NO_ERROR){
 
 
219
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
 
220
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
223
  while(engine_info != NULL){
 
 
224
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
 
 
225
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
 
 
226
                            engine_info->file_name, tempdir);
 
 
229
    engine_info = engine_info->next;
 
 
231
  if(engine_info == NULL){
 
 
232
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
 
 
236
  /* Create new GPGME "context" */
 
 
237
  rc = gpgme_new(&(mc.ctx));
 
 
238
  if(rc != GPG_ERR_NO_ERROR){
 
 
239
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
 
240
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
244
  if(not import_key(pubkey) or not import_key(seckey)){
 
 
252
 * Decrypt OpenPGP data.
 
 
253
 * Returns -1 on error
 
 
255
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
 
258
  gpgme_data_t dh_crypto, dh_plain;
 
 
261
  size_t plaintext_capacity = 0;
 
 
262
  ssize_t plaintext_length = 0;
 
 
265
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
 
268
  /* Create new GPGME data buffer from memory cryptotext */
 
 
269
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
 
271
  if(rc != GPG_ERR_NO_ERROR){
 
 
272
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
 
273
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
277
  /* Create new empty GPGME data buffer for the plaintext */
 
 
278
  rc = gpgme_data_new(&dh_plain);
 
 
279
  if(rc != GPG_ERR_NO_ERROR){
 
 
280
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
 
 
281
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
282
    gpgme_data_release(dh_crypto);
 
 
286
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
 
288
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
 
289
  if(rc != GPG_ERR_NO_ERROR){
 
 
290
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
 
291
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
292
    plaintext_length = -1;
 
 
294
      gpgme_decrypt_result_t result;
 
 
295
      result = gpgme_op_decrypt_result(mc.ctx);
 
 
297
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
 
299
        fprintf(stderr, "Unsupported algorithm: %s\n",
 
 
300
                result->unsupported_algorithm);
 
 
301
        fprintf(stderr, "Wrong key usage: %u\n",
 
 
302
                result->wrong_key_usage);
 
 
303
        if(result->file_name != NULL){
 
 
304
          fprintf(stderr, "File name: %s\n", result->file_name);
 
 
306
        gpgme_recipient_t recipient;
 
 
307
        recipient = result->recipients;
 
 
309
          while(recipient != NULL){
 
 
310
            fprintf(stderr, "Public key algorithm: %s\n",
 
 
311
                    gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
 
312
            fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
 
313
            fprintf(stderr, "Secret key available: %s\n",
 
 
314
                    recipient->status == GPG_ERR_NO_SECKEY
 
 
316
            recipient = recipient->next;
 
 
325
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
 
328
  /* Seek back to the beginning of the GPGME plaintext data buffer */
 
 
329
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
 
330
    perror("gpgme_data_seek");
 
 
331
    plaintext_length = -1;
 
 
337
    plaintext_capacity = incbuffer(plaintext,
 
 
338
                                      (size_t)plaintext_length,
 
 
340
    if(plaintext_capacity == 0){
 
 
342
        plaintext_length = -1;
 
 
346
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
 
348
    /* Print the data, if any */
 
 
354
      perror("gpgme_data_read");
 
 
355
      plaintext_length = -1;
 
 
358
    plaintext_length += ret;
 
 
362
    fprintf(stderr, "Decrypted password is: ");
 
 
363
    for(ssize_t i = 0; i < plaintext_length; i++){
 
 
364
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
 
366
    fprintf(stderr, "\n");
 
 
371
  /* Delete the GPGME cryptotext data buffer */
 
 
372
  gpgme_data_release(dh_crypto);
 
 
374
  /* Delete the GPGME plaintext data buffer */
 
 
375
  gpgme_data_release(dh_plain);
 
 
376
  return plaintext_length;
 
 
379
static const char * safer_gnutls_strerror(int value){
 
 
380
  const char *ret = gnutls_strerror(value); /* Spurious warning from
 
 
381
                                               -Wunreachable-code */
 
 
387
/* GnuTLS log function callback */
 
 
388
static void debuggnutls(__attribute__((unused)) int level,
 
 
390
  fprintf(stderr, "GnuTLS: %s", string);
 
 
393
static int init_gnutls_global(const char *pubkeyfilename,
 
 
394
                              const char *seckeyfilename){
 
 
398
    fprintf(stderr, "Initializing GnuTLS\n");
 
 
401
  ret = gnutls_global_init();
 
 
402
  if(ret != GNUTLS_E_SUCCESS){
 
 
403
    fprintf(stderr, "GnuTLS global_init: %s\n",
 
 
404
            safer_gnutls_strerror(ret));
 
 
409
    /* "Use a log level over 10 to enable all debugging options."
 
 
412
    gnutls_global_set_log_level(11);
 
 
413
    gnutls_global_set_log_function(debuggnutls);
 
 
416
  /* OpenPGP credentials */
 
 
417
  gnutls_certificate_allocate_credentials(&mc.cred);
 
 
418
  if(ret != GNUTLS_E_SUCCESS){
 
 
419
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
 
 
423
            safer_gnutls_strerror(ret));
 
 
424
    gnutls_global_deinit();
 
 
429
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
 
430
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
 
434
  ret = gnutls_certificate_set_openpgp_key_file
 
 
435
    (mc.cred, pubkeyfilename, seckeyfilename,
 
 
436
     GNUTLS_OPENPGP_FMT_BASE64);
 
 
437
  if(ret != GNUTLS_E_SUCCESS){
 
 
439
            "Error[%d] while reading the OpenPGP key pair ('%s',"
 
 
440
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
 
441
    fprintf(stderr, "The GnuTLS error is: %s\n",
 
 
442
            safer_gnutls_strerror(ret));
 
 
446
  /* GnuTLS server initialization */
 
 
447
  ret = gnutls_dh_params_init(&mc.dh_params);
 
 
448
  if(ret != GNUTLS_E_SUCCESS){
 
 
449
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
 
 
450
            " %s\n", safer_gnutls_strerror(ret));
 
 
453
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
 
454
  if(ret != GNUTLS_E_SUCCESS){
 
 
455
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
 
 
456
            safer_gnutls_strerror(ret));
 
 
460
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
 
466
  gnutls_certificate_free_credentials(mc.cred);
 
 
467
  gnutls_global_deinit();
 
 
468
  gnutls_dh_params_deinit(mc.dh_params);
 
 
472
static int init_gnutls_session(gnutls_session_t *session){
 
 
474
  /* GnuTLS session creation */
 
 
475
  ret = gnutls_init(session, GNUTLS_SERVER);
 
 
476
  if(ret != GNUTLS_E_SUCCESS){
 
 
477
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
 
 
478
            safer_gnutls_strerror(ret));
 
 
483
    ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
 
484
    if(ret != GNUTLS_E_SUCCESS){
 
 
485
      fprintf(stderr, "Syntax error at: %s\n", err);
 
 
486
      fprintf(stderr, "GnuTLS error: %s\n",
 
 
487
              safer_gnutls_strerror(ret));
 
 
488
      gnutls_deinit(*session);
 
 
493
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
 
495
  if(ret != GNUTLS_E_SUCCESS){
 
 
496
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
 
 
497
            safer_gnutls_strerror(ret));
 
 
498
    gnutls_deinit(*session);
 
 
502
  /* ignore client certificate if any. */
 
 
503
  gnutls_certificate_server_set_request(*session,
 
 
506
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
 
511
/* Avahi log function callback */
 
 
512
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
 
513
                      __attribute__((unused)) const char *txt){}
 
 
515
/* Called when a Mandos server is found */
 
 
516
static int start_mandos_communication(const char *ip, uint16_t port,
 
 
517
                                      AvahiIfIndex if_index,
 
 
522
    struct sockaddr_in in;
 
 
523
    struct sockaddr_in6 in6;
 
 
526
  char *decrypted_buffer;
 
 
527
  size_t buffer_length = 0;
 
 
528
  size_t buffer_capacity = 0;
 
 
529
  ssize_t decrypted_buffer_size;
 
 
532
  gnutls_session_t session;
 
 
533
  int pf;                       /* Protocol family */
 
 
543
    fprintf(stderr, "Bad address family: %d\n", af);
 
 
547
  ret = init_gnutls_session(&session);
 
 
553
    fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
 
 
557
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
 
563
  memset(&to, 0, sizeof(to));
 
 
565
    to.in6.sin6_family = (uint16_t)af;
 
 
566
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
 
568
    to.in.sin_family = (sa_family_t)af;
 
 
569
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
 
576
    fprintf(stderr, "Bad address: %s\n", ip);
 
 
580
    to.in6.sin6_port = htons(port); /* Spurious warnings from
 
 
582
                                       -Wunreachable-code */
 
 
584
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
 
 
585
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
 
 
587
      if(if_index == AVAHI_IF_UNSPEC){
 
 
588
        fprintf(stderr, "An IPv6 link-local address is incomplete"
 
 
589
                " without a network interface\n");
 
 
592
      /* Set the network interface number as scope */
 
 
593
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
 
596
    to.in.sin_port = htons(port); /* Spurious warnings from
 
 
598
                                     -Wunreachable-code */
 
 
602
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
 
603
      char interface[IF_NAMESIZE];
 
 
604
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
 
605
        perror("if_indextoname");
 
 
607
        fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
 
 
608
                ip, interface, port);
 
 
611
      fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
 
614
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
 
615
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
 
618
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
 
 
621
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
 
 
627
      if(strcmp(addrstr, ip) != 0){
 
 
628
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
 
634
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
 
636
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
 
643
  const char *out = mandos_protocol_version;
 
 
646
    size_t out_size = strlen(out);
 
 
647
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
 
648
                                   out_size - written));
 
 
654
    written += (size_t)ret;
 
 
655
    if(written < out_size){
 
 
658
      if(out == mandos_protocol_version){
 
 
668
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
 
 
671
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
 
 
674
    ret = gnutls_handshake(session);
 
 
675
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
677
  if(ret != GNUTLS_E_SUCCESS){
 
 
679
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
 
686
  /* Read OpenPGP packet that contains the wanted password */
 
 
689
    fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
 
 
694
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
 
696
    if(buffer_capacity == 0){
 
 
702
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
 
709
      case GNUTLS_E_INTERRUPTED:
 
 
712
      case GNUTLS_E_REHANDSHAKE:
 
 
714
          ret = gnutls_handshake(session);
 
 
715
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
717
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
 
724
        fprintf(stderr, "Unknown error while reading data from"
 
 
725
                " encrypted session with Mandos server\n");
 
 
727
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
 
731
      buffer_length += (size_t) sret;
 
 
736
    fprintf(stderr, "Closing TLS session\n");
 
 
739
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
 
741
  if(buffer_length > 0){
 
 
742
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
 
745
    if(decrypted_buffer_size >= 0){
 
 
747
      while(written < (size_t) decrypted_buffer_size){
 
 
748
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
 
749
                          (size_t)decrypted_buffer_size - written,
 
 
751
        if(ret == 0 and ferror(stdout)){
 
 
753
            fprintf(stderr, "Error writing encrypted data: %s\n",
 
 
759
        written += (size_t)ret;
 
 
761
      free(decrypted_buffer);
 
 
769
  /* Shutdown procedure */
 
 
773
  ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
 
777
  gnutls_deinit(session);
 
 
781
static void resolve_callback(AvahiSServiceResolver *r,
 
 
782
                             AvahiIfIndex interface,
 
 
784
                             AvahiResolverEvent event,
 
 
788
                             const char *host_name,
 
 
789
                             const AvahiAddress *address,
 
 
791
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
 
792
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
794
                             AVAHI_GCC_UNUSED void* userdata){
 
 
797
  /* Called whenever a service has been resolved successfully or
 
 
802
  case AVAHI_RESOLVER_FAILURE:
 
 
803
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
 
 
804
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
 
805
            avahi_strerror(avahi_server_errno(mc.server)));
 
 
808
  case AVAHI_RESOLVER_FOUND:
 
 
810
      char ip[AVAHI_ADDRESS_STR_MAX];
 
 
811
      avahi_address_snprint(ip, sizeof(ip), address);
 
 
813
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
 
814
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
 
 
815
                ip, (intmax_t)interface, port);
 
 
817
      int ret = start_mandos_communication(ip, port, interface,
 
 
818
                                           avahi_proto_to_af(proto));
 
 
820
        avahi_simple_poll_quit(mc.simple_poll);
 
 
824
  avahi_s_service_resolver_free(r);
 
 
827
static void browse_callback(AvahiSServiceBrowser *b,
 
 
828
                            AvahiIfIndex interface,
 
 
829
                            AvahiProtocol protocol,
 
 
830
                            AvahiBrowserEvent event,
 
 
834
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
836
                            AVAHI_GCC_UNUSED void* userdata){
 
 
839
  /* Called whenever a new services becomes available on the LAN or
 
 
840
     is removed from the LAN */
 
 
844
  case AVAHI_BROWSER_FAILURE:
 
 
846
    fprintf(stderr, "(Avahi browser) %s\n",
 
 
847
            avahi_strerror(avahi_server_errno(mc.server)));
 
 
848
    avahi_simple_poll_quit(mc.simple_poll);
 
 
851
  case AVAHI_BROWSER_NEW:
 
 
852
    /* We ignore the returned Avahi resolver object. In the callback
 
 
853
       function we free it. If the Avahi server is terminated before
 
 
854
       the callback function is called the Avahi server will free the
 
 
857
    if(!(avahi_s_service_resolver_new(mc.server, interface,
 
 
858
                                       protocol, name, type, domain,
 
 
859
                                       AVAHI_PROTO_INET6, 0,
 
 
860
                                       resolve_callback, NULL)))
 
 
861
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
 
 
862
              name, avahi_strerror(avahi_server_errno(mc.server)));
 
 
865
  case AVAHI_BROWSER_REMOVE:
 
 
868
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
 
869
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
 
871
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
 
877
sig_atomic_t quit_now = 0;
 
 
879
/* stop main loop after sigterm has been called */
 
 
880
static void handle_sigterm(__attribute__((unused)) int sig){
 
 
885
  int old_errno = errno;
 
 
886
  if(mc.simple_poll != NULL){
 
 
887
    avahi_simple_poll_quit(mc.simple_poll);
 
 
892
int main(int argc, char *argv[]){
 
 
893
  AvahiSServiceBrowser *sb = NULL;
 
 
898
  int exitcode = EXIT_SUCCESS;
 
 
899
  const char *interface = "eth0";
 
 
900
  struct ifreq network;
 
 
904
  char *connect_to = NULL;
 
 
905
  char tempdir[] = "/tmp/mandosXXXXXX";
 
 
906
  bool tempdir_created = false;
 
 
907
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
 
908
  const char *seckey = PATHDIR "/" SECKEY;
 
 
909
  const char *pubkey = PATHDIR "/" PUBKEY;
 
 
911
  /* Initialize Mandos context */
 
 
912
  mc = (mandos_context){ .simple_poll = NULL, .server = NULL,
 
 
913
                         .dh_bits = 1024, .priority = "SECURE256"
 
 
914
                         ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
 
915
  bool gnutls_initialized = false;
 
 
916
  bool gpgme_initialized = false;
 
 
919
  struct sigaction old_sigterm_action;
 
 
920
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
 
923
    struct argp_option options[] = {
 
 
924
      { .name = "debug", .key = 128,
 
 
925
        .doc = "Debug mode", .group = 3 },
 
 
926
      { .name = "connect", .key = 'c',
 
 
927
        .arg = "ADDRESS:PORT",
 
 
928
        .doc = "Connect directly to a specific Mandos server",
 
 
930
      { .name = "interface", .key = 'i',
 
 
932
        .doc = "Network interface that will be used to search for"
 
 
935
      { .name = "seckey", .key = 's',
 
 
937
        .doc = "OpenPGP secret key file base name",
 
 
939
      { .name = "pubkey", .key = 'p',
 
 
941
        .doc = "OpenPGP public key file base name",
 
 
943
      { .name = "dh-bits", .key = 129,
 
 
945
        .doc = "Bit length of the prime number used in the"
 
 
946
        " Diffie-Hellman key exchange",
 
 
948
      { .name = "priority", .key = 130,
 
 
950
        .doc = "GnuTLS priority string for the TLS handshake",
 
 
952
      { .name = "delay", .key = 131,
 
 
954
        .doc = "Maximum delay to wait for interface startup",
 
 
959
    error_t parse_opt(int key, char *arg,
 
 
960
                      struct argp_state *state){
 
 
962
      case 128:                 /* --debug */
 
 
965
      case 'c':                 /* --connect */
 
 
968
      case 'i':                 /* --interface */
 
 
971
      case 's':                 /* --seckey */
 
 
974
      case 'p':                 /* --pubkey */
 
 
977
      case 129:                 /* --dh-bits */
 
 
979
        tmpmax = strtoimax(arg, &tmp, 10);
 
 
980
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
 
981
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
 
982
          fprintf(stderr, "Bad number of DH bits\n");
 
 
985
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
 
987
      case 130:                 /* --priority */
 
 
990
      case 131:                 /* --delay */
 
 
992
        delay = strtof(arg, &tmp);
 
 
993
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
 
994
          fprintf(stderr, "Bad delay\n");
 
 
1003
        return ARGP_ERR_UNKNOWN;
 
 
1008
    struct argp argp = { .options = options, .parser = parse_opt,
 
 
1010
                         .doc = "Mandos client -- Get and decrypt"
 
 
1011
                         " passwords from a Mandos server" };
 
 
1012
    ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
 
 
1013
    if(ret == ARGP_ERR_UNKNOWN){
 
 
1014
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
 
1015
      exitcode = EXIT_FAILURE;
 
 
1021
    avahi_set_log_function(empty_log);
 
 
1024
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
 
1025
     from the signal handler */
 
 
1026
  /* Initialize the pseudo-RNG for Avahi */
 
 
1027
  srand((unsigned int) time(NULL));
 
 
1028
  mc.simple_poll = avahi_simple_poll_new();
 
 
1029
  if(mc.simple_poll == NULL){
 
 
1030
    fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
 
 
1031
    exitcode = EXIT_FAILURE;
 
 
1035
  sigemptyset(&sigterm_action.sa_mask);
 
 
1036
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
 
1038
    perror("sigaddset");
 
 
1039
    exitcode = EXIT_FAILURE;
 
 
1042
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
 
1044
    perror("sigaddset");
 
 
1045
    exitcode = EXIT_FAILURE;
 
 
1048
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
 
1050
    perror("sigaddset");
 
 
1051
    exitcode = EXIT_FAILURE;
 
 
1054
  ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
 
 
1056
    perror("sigaction");
 
 
1057
    exitcode = EXIT_FAILURE;
 
 
1061
  /* If the interface is down, bring it up */
 
 
1062
  if(interface[0] != '\0'){
 
 
1064
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
 
1065
       messages to mess up the prompt */
 
 
1066
    ret = klogctl(8, NULL, 5);
 
 
1067
    bool restore_loglevel = true;
 
 
1069
      restore_loglevel = false;
 
 
1072
#endif  /* __linux__ */
 
 
1074
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
 
1077
      exitcode = EXIT_FAILURE;
 
 
1079
      if(restore_loglevel){
 
 
1080
        ret = klogctl(7, NULL, 0);
 
 
1085
#endif  /* __linux__ */
 
 
1088
    strcpy(network.ifr_name, interface);
 
 
1089
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
 
1091
      perror("ioctl SIOCGIFFLAGS");
 
 
1093
      if(restore_loglevel){
 
 
1094
        ret = klogctl(7, NULL, 0);
 
 
1099
#endif  /* __linux__ */
 
 
1100
      exitcode = EXIT_FAILURE;
 
 
1103
    if((network.ifr_flags & IFF_UP) == 0){
 
 
1104
      network.ifr_flags |= IFF_UP;
 
 
1105
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
 
1107
        perror("ioctl SIOCSIFFLAGS");
 
 
1108
        exitcode = EXIT_FAILURE;
 
 
1110
        if(restore_loglevel){
 
 
1111
          ret = klogctl(7, NULL, 0);
 
 
1116
#endif  /* __linux__ */
 
 
1120
    /* sleep checking until interface is running */
 
 
1121
    for(int i=0; i < delay * 4; i++){
 
 
1122
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
 
1124
        perror("ioctl SIOCGIFFLAGS");
 
 
1125
      } else if(network.ifr_flags & IFF_RUNNING){
 
 
1128
      struct timespec sleeptime = { .tv_nsec = 250000000 };
 
 
1129
      ret = nanosleep(&sleeptime, NULL);
 
 
1130
      if(ret == -1 and errno != EINTR){
 
 
1131
        perror("nanosleep");
 
 
1134
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
 
1139
    if(restore_loglevel){
 
 
1140
      /* Restores kernel loglevel to default */
 
 
1141
      ret = klogctl(7, NULL, 0);
 
 
1146
#endif  /* __linux__ */
 
 
1163
  ret = init_gnutls_global(pubkey, seckey);
 
 
1165
    fprintf(stderr, "init_gnutls_global failed\n");
 
 
1166
    exitcode = EXIT_FAILURE;
 
 
1169
    gnutls_initialized = true;
 
 
1172
  if(mkdtemp(tempdir) == NULL){
 
 
1176
  tempdir_created = true;
 
 
1178
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
 
1179
    fprintf(stderr, "init_gpgme failed\n");
 
 
1180
    exitcode = EXIT_FAILURE;
 
 
1183
    gpgme_initialized = true;
 
 
1186
  if(interface[0] != '\0'){
 
 
1187
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
 
1189
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
 
1190
      exitcode = EXIT_FAILURE;
 
 
1195
  if(connect_to != NULL){
 
 
1196
    /* Connect directly, do not use Zeroconf */
 
 
1197
    /* (Mainly meant for debugging) */
 
 
1198
    char *address = strrchr(connect_to, ':');
 
 
1199
    if(address == NULL){
 
 
1200
      fprintf(stderr, "No colon in address\n");
 
 
1201
      exitcode = EXIT_FAILURE;
 
 
1206
    tmpmax = strtoimax(address+1, &tmp, 10);
 
 
1207
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
 
1208
       or tmpmax != (uint16_t)tmpmax){
 
 
1209
      fprintf(stderr, "Bad port number\n");
 
 
1210
      exitcode = EXIT_FAILURE;
 
 
1213
    port = (uint16_t)tmpmax;
 
 
1215
    address = connect_to;
 
 
1216
    /* Colon in address indicates IPv6 */
 
 
1218
    if(strchr(address, ':') != NULL){
 
 
1223
    ret = start_mandos_communication(address, port, if_index, af);
 
 
1225
      exitcode = EXIT_FAILURE;
 
 
1227
      exitcode = EXIT_SUCCESS;
 
 
1233
    AvahiServerConfig config;
 
 
1234
    /* Do not publish any local Zeroconf records */
 
 
1235
    avahi_server_config_init(&config);
 
 
1236
    config.publish_hinfo = 0;
 
 
1237
    config.publish_addresses = 0;
 
 
1238
    config.publish_workstation = 0;
 
 
1239
    config.publish_domain = 0;
 
 
1241
    /* Allocate a new server */
 
 
1242
    mc.server = avahi_server_new(avahi_simple_poll_get
 
 
1243
                                 (mc.simple_poll), &config, NULL,
 
 
1246
    /* Free the Avahi configuration data */
 
 
1247
    avahi_server_config_free(&config);
 
 
1250
  /* Check if creating the Avahi server object succeeded */
 
 
1251
  if(mc.server == NULL){
 
 
1252
    fprintf(stderr, "Failed to create Avahi server: %s\n",
 
 
1253
            avahi_strerror(error));
 
 
1254
    exitcode = EXIT_FAILURE;
 
 
1258
  /* Create the Avahi service browser */
 
 
1259
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
 
1260
                                   AVAHI_PROTO_INET6, "_mandos._tcp",
 
 
1261
                                   NULL, 0, browse_callback, NULL);
 
 
1263
    fprintf(stderr, "Failed to create service browser: %s\n",
 
 
1264
            avahi_strerror(avahi_server_errno(mc.server)));
 
 
1265
    exitcode = EXIT_FAILURE;
 
 
1269
  /* Run the main loop */
 
 
1272
    fprintf(stderr, "Starting Avahi loop search\n");
 
 
1275
  avahi_simple_poll_loop(mc.simple_poll);
 
 
1280
    fprintf(stderr, "%s exiting\n", argv[0]);
 
 
1283
  /* Cleanup things */
 
 
1285
    avahi_s_service_browser_free(sb);
 
 
1287
  if(mc.server != NULL)
 
 
1288
    avahi_server_free(mc.server);
 
 
1290
  if(mc.simple_poll != NULL)
 
 
1291
    avahi_simple_poll_free(mc.simple_poll);
 
 
1293
  if(gnutls_initialized){
 
 
1294
    gnutls_certificate_free_credentials(mc.cred);
 
 
1295
    gnutls_global_deinit();
 
 
1296
    gnutls_dh_params_deinit(mc.dh_params);
 
 
1299
  if(gpgme_initialized){
 
 
1300
    gpgme_release(mc.ctx);
 
 
1303
  /* Removes the temp directory used by GPGME */
 
 
1304
  if(tempdir_created){
 
 
1306
    struct dirent *direntry;
 
 
1307
    d = opendir(tempdir);
 
 
1309
      if(errno != ENOENT){
 
 
1314
        direntry = readdir(d);
 
 
1315
        if(direntry == NULL){
 
 
1318
        /* Skip "." and ".." */
 
 
1319
        if(direntry->d_name[0] == '.'
 
 
1320
           and (direntry->d_name[1] == '\0'
 
 
1321
                or (direntry->d_name[1] == '.'
 
 
1322
                    and direntry->d_name[2] == '\0'))){
 
 
1325
        char *fullname = NULL;
 
 
1326
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
 
1332
        ret = remove(fullname);
 
 
1334
          fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
 
 
1341
    ret = rmdir(tempdir);
 
 
1342
    if(ret == -1 and errno != ENOENT){