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
#ifndef _LARGEFILE_SOURCE
 
 
34
#define _LARGEFILE_SOURCE
 
 
36
#ifndef _FILE_OFFSET_BITS
 
 
37
#define _FILE_OFFSET_BITS 64
 
 
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
 
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
 
43
                                   stdout, ferror(), remove() */
 
 
44
#include <stdint.h>             /* uint16_t, uint32_t */
 
 
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
 
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
 
 
48
#include <stdbool.h>            /* bool, false, true */
 
 
49
#include <string.h>             /* memset(), strcmp(), strlen(),
 
 
50
                                   strerror(), asprintf(), strcpy() */
 
 
51
#include <sys/ioctl.h>          /* ioctl */
 
 
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
 
 
53
                                   sockaddr_in6, PF_INET6,
 
 
54
                                   SOCK_STREAM, uid_t, gid_t, open(),
 
 
56
#include <sys/stat.h>           /* open() */
 
 
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
 
58
                                   inet_pton(), connect() */
 
 
59
#include <fcntl.h>              /* open() */
 
 
60
#include <dirent.h>             /* opendir(), struct dirent, readdir()
 
 
62
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
 
 
64
#include <assert.h>             /* assert() */
 
 
65
#include <errno.h>              /* perror(), errno */
 
 
66
#include <time.h>               /* nanosleep(), time() */
 
 
67
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
 
68
                                   SIOCSIFFLAGS, if_indextoname(),
 
 
69
                                   if_nametoindex(), IF_NAMESIZE */
 
 
70
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
 
 
71
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
 
 
73
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
 
74
                                   getuid(), getgid(), seteuid(),
 
 
76
#include <arpa/inet.h>          /* inet_pton(), htons */
 
 
77
#include <iso646.h>             /* not, or, and */
 
 
78
#include <argp.h>               /* struct argp_option, error_t, struct
 
 
79
                                   argp_state, struct argp,
 
 
80
                                   argp_parse(), ARGP_KEY_ARG,
 
 
81
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
 
 
82
#include <signal.h>             /* sigemptyset(), sigaddset(),
 
 
83
                                   sigaction(), SIGTERM, sig_atomic_t,
 
 
87
#include <sys/klog.h>           /* klogctl() */
 
 
88
#endif  /* __linux__ */
 
 
91
/* All Avahi types, constants and functions
 
 
94
#include <avahi-core/core.h>
 
 
95
#include <avahi-core/lookup.h>
 
 
96
#include <avahi-core/log.h>
 
 
97
#include <avahi-common/simple-watch.h>
 
 
98
#include <avahi-common/malloc.h>
 
 
99
#include <avahi-common/error.h>
 
 
102
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
 
105
                                   init_gnutls_session(),
 
 
107
#include <gnutls/openpgp.h>
 
 
108
                          /* gnutls_certificate_set_openpgp_key_file(),
 
 
109
                                   GNUTLS_OPENPGP_FMT_BASE64 */
 
 
112
#include <gpgme.h>              /* All GPGME types, constants and
 
 
115
                                   GPGME_PROTOCOL_OpenPGP,
 
 
118
#define BUFFER_SIZE 256
 
 
120
#define PATHDIR "/conf/conf.d/mandos"
 
 
121
#define SECKEY "seckey.txt"
 
 
122
#define PUBKEY "pubkey.txt"
 
 
125
static const char mandos_protocol_version[] = "1";
 
 
126
const char *argp_program_version = "mandos-client " VERSION;
 
 
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
 
129
/* Used for passing in values through the Avahi callback functions */
 
 
131
  AvahiSimplePoll *simple_poll;
 
 
133
  gnutls_certificate_credentials_t cred;
 
 
134
  unsigned int dh_bits;
 
 
135
  gnutls_dh_params_t dh_params;
 
 
136
  const char *priority;
 
 
140
/* global context so signal handler can reach it*/
 
 
141
mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
 
142
                      .dh_bits = 1024, .priority = "SECURE256"
 
 
143
                      ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
 
146
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
 
147
 * bytes. "buffer_capacity" is how much is currently allocated,
 
 
148
 * "buffer_length" is how much is already used.
 
 
150
size_t incbuffer(char **buffer, size_t buffer_length,
 
 
151
                  size_t buffer_capacity){
 
 
152
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
 
153
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
 
157
    buffer_capacity += BUFFER_SIZE;
 
 
159
  return buffer_capacity;
 
 
165
static bool init_gpgme(const char *seckey,
 
 
166
                       const char *pubkey, const char *tempdir){
 
 
168
  gpgme_engine_info_t engine_info;
 
 
172
   * Helper function to insert pub and seckey to the engine keyring.
 
 
174
  bool import_key(const char *filename){
 
 
177
    gpgme_data_t pgp_data;
 
 
179
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
 
185
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
 
186
    if(rc != GPG_ERR_NO_ERROR){
 
 
187
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
 
188
              gpgme_strsource(rc), gpgme_strerror(rc));
 
 
192
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
 
193
    if(rc != GPG_ERR_NO_ERROR){
 
 
194
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
 
 
195
              gpgme_strsource(rc), gpgme_strerror(rc));
 
 
199
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
 
203
    gpgme_data_release(pgp_data);
 
 
208
    fprintf(stderr, "Initializing GPGME\n");
 
 
212
  gpgme_check_version(NULL);
 
 
213
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
 
214
  if(rc != GPG_ERR_NO_ERROR){
 
 
215
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
 
216
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
220
    /* Set GPGME home directory for the OpenPGP engine only */
 
 
221
  rc = gpgme_get_engine_info(&engine_info);
 
 
222
  if(rc != GPG_ERR_NO_ERROR){
 
 
223
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
 
224
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
227
  while(engine_info != NULL){
 
 
228
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
 
 
229
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
 
 
230
                            engine_info->file_name, tempdir);
 
 
233
    engine_info = engine_info->next;
 
 
235
  if(engine_info == NULL){
 
 
236
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
 
 
240
  /* Create new GPGME "context" */
 
 
241
  rc = gpgme_new(&(mc.ctx));
 
 
242
  if(rc != GPG_ERR_NO_ERROR){
 
 
243
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
 
244
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
248
  if(not import_key(pubkey) or not import_key(seckey)){
 
 
256
 * Decrypt OpenPGP data.
 
 
257
 * Returns -1 on error
 
 
259
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
 
262
  gpgme_data_t dh_crypto, dh_plain;
 
 
265
  size_t plaintext_capacity = 0;
 
 
266
  ssize_t plaintext_length = 0;
 
 
269
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
 
272
  /* Create new GPGME data buffer from memory cryptotext */
 
 
273
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
 
275
  if(rc != GPG_ERR_NO_ERROR){
 
 
276
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
 
277
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
281
  /* Create new empty GPGME data buffer for the plaintext */
 
 
282
  rc = gpgme_data_new(&dh_plain);
 
 
283
  if(rc != GPG_ERR_NO_ERROR){
 
 
284
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
 
 
285
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
286
    gpgme_data_release(dh_crypto);
 
 
290
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
 
292
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
 
293
  if(rc != GPG_ERR_NO_ERROR){
 
 
294
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
 
295
            gpgme_strsource(rc), gpgme_strerror(rc));
 
 
296
    plaintext_length = -1;
 
 
298
      gpgme_decrypt_result_t result;
 
 
299
      result = gpgme_op_decrypt_result(mc.ctx);
 
 
301
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
 
303
        fprintf(stderr, "Unsupported algorithm: %s\n",
 
 
304
                result->unsupported_algorithm);
 
 
305
        fprintf(stderr, "Wrong key usage: %u\n",
 
 
306
                result->wrong_key_usage);
 
 
307
        if(result->file_name != NULL){
 
 
308
          fprintf(stderr, "File name: %s\n", result->file_name);
 
 
310
        gpgme_recipient_t recipient;
 
 
311
        recipient = result->recipients;
 
 
312
        while(recipient != NULL){
 
 
313
          fprintf(stderr, "Public key algorithm: %s\n",
 
 
314
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
 
315
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
 
316
          fprintf(stderr, "Secret key available: %s\n",
 
 
317
                  recipient->status == GPG_ERR_NO_SECKEY
 
 
319
          recipient = recipient->next;
 
 
327
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
 
330
  /* Seek back to the beginning of the GPGME plaintext data buffer */
 
 
331
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
 
332
    perror("gpgme_data_seek");
 
 
333
    plaintext_length = -1;
 
 
339
    plaintext_capacity = incbuffer(plaintext,
 
 
340
                                      (size_t)plaintext_length,
 
 
342
    if(plaintext_capacity == 0){
 
 
344
        plaintext_length = -1;
 
 
348
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
 
350
    /* Print the data, if any */
 
 
356
      perror("gpgme_data_read");
 
 
357
      plaintext_length = -1;
 
 
360
    plaintext_length += ret;
 
 
364
    fprintf(stderr, "Decrypted password is: ");
 
 
365
    for(ssize_t i = 0; i < plaintext_length; i++){
 
 
366
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
 
368
    fprintf(stderr, "\n");
 
 
373
  /* Delete the GPGME cryptotext data buffer */
 
 
374
  gpgme_data_release(dh_crypto);
 
 
376
  /* Delete the GPGME plaintext data buffer */
 
 
377
  gpgme_data_release(dh_plain);
 
 
378
  return plaintext_length;
 
 
381
static const char * safer_gnutls_strerror(int value){
 
 
382
  const char *ret = gnutls_strerror(value); /* Spurious warning from
 
 
383
                                               -Wunreachable-code */
 
 
389
/* GnuTLS log function callback */
 
 
390
static void debuggnutls(__attribute__((unused)) int level,
 
 
392
  fprintf(stderr, "GnuTLS: %s", string);
 
 
395
static int init_gnutls_global(const char *pubkeyfilename,
 
 
396
                              const char *seckeyfilename){
 
 
400
    fprintf(stderr, "Initializing GnuTLS\n");
 
 
403
  ret = gnutls_global_init();
 
 
404
  if(ret != GNUTLS_E_SUCCESS){
 
 
405
    fprintf(stderr, "GnuTLS global_init: %s\n",
 
 
406
            safer_gnutls_strerror(ret));
 
 
411
    /* "Use a log level over 10 to enable all debugging options."
 
 
414
    gnutls_global_set_log_level(11);
 
 
415
    gnutls_global_set_log_function(debuggnutls);
 
 
418
  /* OpenPGP credentials */
 
 
419
  gnutls_certificate_allocate_credentials(&mc.cred);
 
 
420
  if(ret != GNUTLS_E_SUCCESS){
 
 
421
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
 
 
425
            safer_gnutls_strerror(ret));
 
 
426
    gnutls_global_deinit();
 
 
431
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
 
432
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
 
436
  ret = gnutls_certificate_set_openpgp_key_file
 
 
437
    (mc.cred, pubkeyfilename, seckeyfilename,
 
 
438
     GNUTLS_OPENPGP_FMT_BASE64);
 
 
439
  if(ret != GNUTLS_E_SUCCESS){
 
 
441
            "Error[%d] while reading the OpenPGP key pair ('%s',"
 
 
442
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
 
443
    fprintf(stderr, "The GnuTLS error is: %s\n",
 
 
444
            safer_gnutls_strerror(ret));
 
 
448
  /* GnuTLS server initialization */
 
 
449
  ret = gnutls_dh_params_init(&mc.dh_params);
 
 
450
  if(ret != GNUTLS_E_SUCCESS){
 
 
451
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
 
 
452
            " %s\n", safer_gnutls_strerror(ret));
 
 
455
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
 
456
  if(ret != GNUTLS_E_SUCCESS){
 
 
457
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
 
 
458
            safer_gnutls_strerror(ret));
 
 
462
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
 
468
  gnutls_certificate_free_credentials(mc.cred);
 
 
469
  gnutls_global_deinit();
 
 
470
  gnutls_dh_params_deinit(mc.dh_params);
 
 
474
static int init_gnutls_session(gnutls_session_t *session){
 
 
476
  /* GnuTLS session creation */
 
 
478
    ret = gnutls_init(session, GNUTLS_SERVER);
 
 
479
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
 
480
  if(ret != GNUTLS_E_SUCCESS){
 
 
481
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
 
 
482
            safer_gnutls_strerror(ret));
 
 
488
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
 
489
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
 
490
    if(ret != GNUTLS_E_SUCCESS){
 
 
491
      fprintf(stderr, "Syntax error at: %s\n", err);
 
 
492
      fprintf(stderr, "GnuTLS error: %s\n",
 
 
493
              safer_gnutls_strerror(ret));
 
 
494
      gnutls_deinit(*session);
 
 
500
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
 
502
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
 
503
  if(ret != GNUTLS_E_SUCCESS){
 
 
504
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
 
 
505
            safer_gnutls_strerror(ret));
 
 
506
    gnutls_deinit(*session);
 
 
510
  /* ignore client certificate if any. */
 
 
511
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
 
 
513
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
 
518
/* Avahi log function callback */
 
 
519
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
 
520
                      __attribute__((unused)) const char *txt){}
 
 
522
sig_atomic_t quit_now = 0;
 
 
523
int signal_received = 0;
 
 
525
/* Called when a Mandos server is found */
 
 
526
static int start_mandos_communication(const char *ip, uint16_t port,
 
 
527
                                      AvahiIfIndex if_index,
 
 
529
  int ret, tcp_sd = -1;
 
 
532
    struct sockaddr_in in;
 
 
533
    struct sockaddr_in6 in6;
 
 
536
  char *decrypted_buffer;
 
 
537
  size_t buffer_length = 0;
 
 
538
  size_t buffer_capacity = 0;
 
 
541
  gnutls_session_t session;
 
 
542
  int pf;                       /* Protocol family */
 
 
556
    fprintf(stderr, "Bad address family: %d\n", af);
 
 
560
  ret = init_gnutls_session(&session);
 
 
566
    fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
 
 
570
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
 
581
  memset(&to, 0, sizeof(to));
 
 
583
    to.in6.sin6_family = (sa_family_t)af;
 
 
584
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
 
586
    to.in.sin_family = (sa_family_t)af;
 
 
587
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
 
595
    fprintf(stderr, "Bad address: %s\n", ip);
 
 
600
    to.in6.sin6_port = htons(port); /* Spurious warnings from
 
 
602
                                       -Wunreachable-code */
 
 
604
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
 
 
605
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
 
 
607
      if(if_index == AVAHI_IF_UNSPEC){
 
 
608
        fprintf(stderr, "An IPv6 link-local address is incomplete"
 
 
609
                " without a network interface\n");
 
 
613
      /* Set the network interface number as scope */
 
 
614
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
 
617
    to.in.sin_port = htons(port); /* Spurious warnings from
 
 
619
                                     -Wunreachable-code */
 
 
627
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
 
628
      char interface[IF_NAMESIZE];
 
 
629
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
 
630
        perror("if_indextoname");
 
 
632
        fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
 
 
633
                ip, interface, port);
 
 
636
      fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
 
639
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
 
640
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
 
643
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
 
 
646
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
 
 
652
      if(strcmp(addrstr, ip) != 0){
 
 
653
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
 
663
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
 
665
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
 
677
  const char *out = mandos_protocol_version;
 
 
680
    size_t out_size = strlen(out);
 
 
681
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
 
682
                                   out_size - written));
 
 
688
    written += (size_t)ret;
 
 
689
    if(written < out_size){
 
 
692
      if(out == mandos_protocol_version){
 
 
706
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
 
 
713
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
 
 
720
    ret = gnutls_handshake(session);
 
 
724
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
726
  if(ret != GNUTLS_E_SUCCESS){
 
 
728
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
 
735
  /* Read OpenPGP packet that contains the wanted password */
 
 
738
    fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
 
 
748
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
 
750
    if(buffer_capacity == 0){
 
 
760
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
 
767
      case GNUTLS_E_INTERRUPTED:
 
 
770
      case GNUTLS_E_REHANDSHAKE:
 
 
772
          ret = gnutls_handshake(session);
 
 
777
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
779
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
 
786
        fprintf(stderr, "Unknown error while reading data from"
 
 
787
                " encrypted session with Mandos server\n");
 
 
789
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
 
793
      buffer_length += (size_t) sret;
 
 
798
    fprintf(stderr, "Closing TLS session\n");
 
 
805
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
 
811
  if(buffer_length > 0){
 
 
812
    ssize_t decrypted_buffer_size;
 
 
813
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
 
816
    if(decrypted_buffer_size >= 0){
 
 
819
      while(written < (size_t) decrypted_buffer_size){
 
 
824
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
 
825
                          (size_t)decrypted_buffer_size - written,
 
 
827
        if(ret == 0 and ferror(stdout)){
 
 
829
            fprintf(stderr, "Error writing encrypted data: %s\n",
 
 
835
        written += (size_t)ret;
 
 
837
      free(decrypted_buffer);
 
 
845
  /* Shutdown procedure */
 
 
850
    ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
 
855
  gnutls_deinit(session);
 
 
862
static void resolve_callback(AvahiSServiceResolver *r,
 
 
863
                             AvahiIfIndex interface,
 
 
865
                             AvahiResolverEvent event,
 
 
869
                             const char *host_name,
 
 
870
                             const AvahiAddress *address,
 
 
872
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
 
873
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
875
                             AVAHI_GCC_UNUSED void* userdata){
 
 
878
  /* Called whenever a service has been resolved successfully or
 
 
887
  case AVAHI_RESOLVER_FAILURE:
 
 
888
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
 
 
889
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
 
890
            avahi_strerror(avahi_server_errno(mc.server)));
 
 
893
  case AVAHI_RESOLVER_FOUND:
 
 
895
      char ip[AVAHI_ADDRESS_STR_MAX];
 
 
896
      avahi_address_snprint(ip, sizeof(ip), address);
 
 
898
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
 
899
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
 
 
900
                ip, (intmax_t)interface, port);
 
 
902
      int ret = start_mandos_communication(ip, port, interface,
 
 
903
                                           avahi_proto_to_af(proto));
 
 
905
        avahi_simple_poll_quit(mc.simple_poll);
 
 
909
  avahi_s_service_resolver_free(r);
 
 
912
static void browse_callback(AvahiSServiceBrowser *b,
 
 
913
                            AvahiIfIndex interface,
 
 
914
                            AvahiProtocol protocol,
 
 
915
                            AvahiBrowserEvent event,
 
 
919
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
921
                            AVAHI_GCC_UNUSED void* userdata){
 
 
924
  /* Called whenever a new services becomes available on the LAN or
 
 
925
     is removed from the LAN */
 
 
933
  case AVAHI_BROWSER_FAILURE:
 
 
935
    fprintf(stderr, "(Avahi browser) %s\n",
 
 
936
            avahi_strerror(avahi_server_errno(mc.server)));
 
 
937
    avahi_simple_poll_quit(mc.simple_poll);
 
 
940
  case AVAHI_BROWSER_NEW:
 
 
941
    /* We ignore the returned Avahi resolver object. In the callback
 
 
942
       function we free it. If the Avahi server is terminated before
 
 
943
       the callback function is called the Avahi server will free the
 
 
946
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
 
 
947
                                    name, type, domain, protocol, 0,
 
 
948
                                    resolve_callback, NULL) == NULL)
 
 
949
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
 
 
950
              name, avahi_strerror(avahi_server_errno(mc.server)));
 
 
953
  case AVAHI_BROWSER_REMOVE:
 
 
956
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
 
957
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
 
959
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
 
965
/* stop main loop after sigterm has been called */
 
 
966
static void handle_sigterm(int sig){
 
 
971
  signal_received = sig;
 
 
972
  int old_errno = errno;
 
 
973
  if(mc.simple_poll != NULL){
 
 
974
    avahi_simple_poll_quit(mc.simple_poll);
 
 
979
int main(int argc, char *argv[]){
 
 
980
  AvahiSServiceBrowser *sb = NULL;
 
 
985
  int exitcode = EXIT_SUCCESS;
 
 
986
  const char *interface = "eth0";
 
 
987
  struct ifreq network;
 
 
989
  bool take_down_interface = false;
 
 
992
  char *connect_to = NULL;
 
 
993
  char tempdir[] = "/tmp/mandosXXXXXX";
 
 
994
  bool tempdir_created = false;
 
 
995
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
 
996
  const char *seckey = PATHDIR "/" SECKEY;
 
 
997
  const char *pubkey = PATHDIR "/" PUBKEY;
 
 
999
  bool gnutls_initialized = false;
 
 
1000
  bool gpgme_initialized = false;
 
 
1003
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
 
 
1004
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
 
1009
  /* Lower any group privileges we might have, just to be safe */
 
 
1016
  /* Lower user privileges (temporarily) */
 
 
1028
    struct argp_option options[] = {
 
 
1029
      { .name = "debug", .key = 128,
 
 
1030
        .doc = "Debug mode", .group = 3 },
 
 
1031
      { .name = "connect", .key = 'c',
 
 
1032
        .arg = "ADDRESS:PORT",
 
 
1033
        .doc = "Connect directly to a specific Mandos server",
 
 
1035
      { .name = "interface", .key = 'i',
 
 
1037
        .doc = "Network interface that will be used to search for"
 
 
1040
      { .name = "seckey", .key = 's',
 
 
1042
        .doc = "OpenPGP secret key file base name",
 
 
1044
      { .name = "pubkey", .key = 'p',
 
 
1046
        .doc = "OpenPGP public key file base name",
 
 
1048
      { .name = "dh-bits", .key = 129,
 
 
1050
        .doc = "Bit length of the prime number used in the"
 
 
1051
        " Diffie-Hellman key exchange",
 
 
1053
      { .name = "priority", .key = 130,
 
 
1055
        .doc = "GnuTLS priority string for the TLS handshake",
 
 
1057
      { .name = "delay", .key = 131,
 
 
1059
        .doc = "Maximum delay to wait for interface startup",
 
 
1064
    error_t parse_opt(int key, char *arg,
 
 
1065
                      struct argp_state *state){
 
 
1067
      case 128:                 /* --debug */
 
 
1070
      case 'c':                 /* --connect */
 
 
1073
      case 'i':                 /* --interface */
 
 
1076
      case 's':                 /* --seckey */
 
 
1079
      case 'p':                 /* --pubkey */
 
 
1082
      case 129:                 /* --dh-bits */
 
 
1084
        tmpmax = strtoimax(arg, &tmp, 10);
 
 
1085
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
 
1086
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
 
1087
          fprintf(stderr, "Bad number of DH bits\n");
 
 
1090
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
 
1092
      case 130:                 /* --priority */
 
 
1095
      case 131:                 /* --delay */
 
 
1097
        delay = strtof(arg, &tmp);
 
 
1098
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
 
1099
          fprintf(stderr, "Bad delay\n");
 
 
1108
        return ARGP_ERR_UNKNOWN;
 
 
1113
    struct argp argp = { .options = options, .parser = parse_opt,
 
 
1115
                         .doc = "Mandos client -- Get and decrypt"
 
 
1116
                         " passwords from a Mandos server" };
 
 
1117
    ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
 
 
1118
    if(ret == ARGP_ERR_UNKNOWN){
 
 
1119
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
 
1120
      exitcode = EXIT_FAILURE;
 
 
1126
    avahi_set_log_function(empty_log);
 
 
1129
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
 
1130
     from the signal handler */
 
 
1131
  /* Initialize the pseudo-RNG for Avahi */
 
 
1132
  srand((unsigned int) time(NULL));
 
 
1133
  mc.simple_poll = avahi_simple_poll_new();
 
 
1134
  if(mc.simple_poll == NULL){
 
 
1135
    fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
 
 
1136
    exitcode = EXIT_FAILURE;
 
 
1140
  sigemptyset(&sigterm_action.sa_mask);
 
 
1141
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
 
1143
    perror("sigaddset");
 
 
1144
    exitcode = EXIT_FAILURE;
 
 
1147
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
 
1149
    perror("sigaddset");
 
 
1150
    exitcode = EXIT_FAILURE;
 
 
1153
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
 
1155
    perror("sigaddset");
 
 
1156
    exitcode = EXIT_FAILURE;
 
 
1159
  /* Need to check if the handler is SIG_IGN before handling:
 
 
1160
     | [[info:libc:Initial Signal Actions]] |
 
 
1161
     | [[info:libc:Basic Signal Handling]]  |
 
 
1163
  ret = sigaction(SIGINT, NULL, &old_sigterm_action);
 
 
1165
    perror("sigaction");
 
 
1166
    return EXIT_FAILURE;
 
 
1168
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
 
1169
    ret = sigaction(SIGINT, &sigterm_action, NULL);
 
 
1171
      perror("sigaction");
 
 
1172
      exitcode = EXIT_FAILURE;
 
 
1176
  ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
 
 
1178
    perror("sigaction");
 
 
1179
    return EXIT_FAILURE;
 
 
1181
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
 
1182
    ret = sigaction(SIGHUP, &sigterm_action, NULL);
 
 
1184
      perror("sigaction");
 
 
1185
      exitcode = EXIT_FAILURE;
 
 
1189
  ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
 
 
1191
    perror("sigaction");
 
 
1192
    return EXIT_FAILURE;
 
 
1194
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
 
1195
    ret = sigaction(SIGTERM, &sigterm_action, NULL);
 
 
1197
      perror("sigaction");
 
 
1198
      exitcode = EXIT_FAILURE;
 
 
1203
  /* If the interface is down, bring it up */
 
 
1204
  if(interface[0] != '\0'){
 
 
1205
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
 
1207
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
 
1208
      exitcode = EXIT_FAILURE;
 
 
1216
    /* Re-raise priviliges */
 
 
1224
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
 
1225
       messages to mess up the prompt */
 
 
1226
    ret = klogctl(8, NULL, 5);
 
 
1227
    bool restore_loglevel = true;
 
 
1229
      restore_loglevel = false;
 
 
1232
#endif  /* __linux__ */
 
 
1234
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
 
1237
      exitcode = EXIT_FAILURE;
 
 
1239
      if(restore_loglevel){
 
 
1240
        ret = klogctl(7, NULL, 0);
 
 
1245
#endif  /* __linux__ */
 
 
1246
      /* Lower privileges */
 
 
1254
    strcpy(network.ifr_name, interface);
 
 
1255
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
 
1257
      perror("ioctl SIOCGIFFLAGS");
 
 
1259
      if(restore_loglevel){
 
 
1260
        ret = klogctl(7, NULL, 0);
 
 
1265
#endif  /* __linux__ */
 
 
1266
      exitcode = EXIT_FAILURE;
 
 
1267
      /* Lower privileges */
 
 
1275
    if((network.ifr_flags & IFF_UP) == 0){
 
 
1276
      network.ifr_flags |= IFF_UP;
 
 
1277
      take_down_interface = true;
 
 
1278
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
 
1280
        take_down_interface = false;
 
 
1281
        perror("ioctl SIOCSIFFLAGS");
 
 
1282
        exitcode = EXIT_FAILURE;
 
 
1284
        if(restore_loglevel){
 
 
1285
          ret = klogctl(7, NULL, 0);
 
 
1290
#endif  /* __linux__ */
 
 
1291
        /* Lower privileges */
 
 
1300
    /* sleep checking until interface is running */
 
 
1301
    for(int i=0; i < delay * 4; i++){
 
 
1302
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
 
1304
        perror("ioctl SIOCGIFFLAGS");
 
 
1305
      } else if(network.ifr_flags & IFF_RUNNING){
 
 
1308
      struct timespec sleeptime = { .tv_nsec = 250000000 };
 
 
1309
      ret = nanosleep(&sleeptime, NULL);
 
 
1310
      if(ret == -1 and errno != EINTR){
 
 
1311
        perror("nanosleep");
 
 
1314
    if(not take_down_interface){
 
 
1315
      /* We won't need the socket anymore */
 
 
1316
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
 
1322
    if(restore_loglevel){
 
 
1323
      /* Restores kernel loglevel to default */
 
 
1324
      ret = klogctl(7, NULL, 0);
 
 
1329
#endif  /* __linux__ */
 
 
1330
    /* Lower privileges */
 
 
1332
    if(take_down_interface){
 
 
1333
      /* Lower privileges */
 
 
1339
      /* Lower privileges permanently */
 
 
1351
  ret = init_gnutls_global(pubkey, seckey);
 
 
1353
    fprintf(stderr, "init_gnutls_global failed\n");
 
 
1354
    exitcode = EXIT_FAILURE;
 
 
1357
    gnutls_initialized = true;
 
 
1364
  tempdir_created = true;
 
 
1365
  if(mkdtemp(tempdir) == NULL){
 
 
1366
    tempdir_created = false;
 
 
1375
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
 
1376
    fprintf(stderr, "init_gpgme failed\n");
 
 
1377
    exitcode = EXIT_FAILURE;
 
 
1380
    gpgme_initialized = true;
 
 
1387
  if(connect_to != NULL){
 
 
1388
    /* Connect directly, do not use Zeroconf */
 
 
1389
    /* (Mainly meant for debugging) */
 
 
1390
    char *address = strrchr(connect_to, ':');
 
 
1391
    if(address == NULL){
 
 
1392
      fprintf(stderr, "No colon in address\n");
 
 
1393
      exitcode = EXIT_FAILURE;
 
 
1403
    tmpmax = strtoimax(address+1, &tmp, 10);
 
 
1404
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
 
1405
       or tmpmax != (uint16_t)tmpmax){
 
 
1406
      fprintf(stderr, "Bad port number\n");
 
 
1407
      exitcode = EXIT_FAILURE;
 
 
1415
    port = (uint16_t)tmpmax;
 
 
1417
    address = connect_to;
 
 
1418
    /* Colon in address indicates IPv6 */
 
 
1420
    if(strchr(address, ':') != NULL){
 
 
1430
    ret = start_mandos_communication(address, port, if_index, af);
 
 
1432
      exitcode = EXIT_FAILURE;
 
 
1434
      exitcode = EXIT_SUCCESS;
 
 
1444
    AvahiServerConfig config;
 
 
1445
    /* Do not publish any local Zeroconf records */
 
 
1446
    avahi_server_config_init(&config);
 
 
1447
    config.publish_hinfo = 0;
 
 
1448
    config.publish_addresses = 0;
 
 
1449
    config.publish_workstation = 0;
 
 
1450
    config.publish_domain = 0;
 
 
1452
    /* Allocate a new server */
 
 
1453
    mc.server = avahi_server_new(avahi_simple_poll_get
 
 
1454
                                 (mc.simple_poll), &config, NULL,
 
 
1457
    /* Free the Avahi configuration data */
 
 
1458
    avahi_server_config_free(&config);
 
 
1461
  /* Check if creating the Avahi server object succeeded */
 
 
1462
  if(mc.server == NULL){
 
 
1463
    fprintf(stderr, "Failed to create Avahi server: %s\n",
 
 
1464
            avahi_strerror(error));
 
 
1465
    exitcode = EXIT_FAILURE;
 
 
1473
  /* Create the Avahi service browser */
 
 
1474
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
 
1475
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
 
1476
                                   NULL, 0, browse_callback, NULL);
 
 
1478
    fprintf(stderr, "Failed to create service browser: %s\n",
 
 
1479
            avahi_strerror(avahi_server_errno(mc.server)));
 
 
1480
    exitcode = EXIT_FAILURE;
 
 
1488
  /* Run the main loop */
 
 
1491
    fprintf(stderr, "Starting Avahi loop search\n");
 
 
1494
  avahi_simple_poll_loop(mc.simple_poll);
 
 
1499
    fprintf(stderr, "%s exiting\n", argv[0]);
 
 
1502
  /* Cleanup things */
 
 
1504
    avahi_s_service_browser_free(sb);
 
 
1506
  if(mc.server != NULL)
 
 
1507
    avahi_server_free(mc.server);
 
 
1509
  if(mc.simple_poll != NULL)
 
 
1510
    avahi_simple_poll_free(mc.simple_poll);
 
 
1512
  if(gnutls_initialized){
 
 
1513
    gnutls_certificate_free_credentials(mc.cred);
 
 
1514
    gnutls_global_deinit();
 
 
1515
    gnutls_dh_params_deinit(mc.dh_params);
 
 
1518
  if(gpgme_initialized){
 
 
1519
    gpgme_release(mc.ctx);
 
 
1522
  /* Take down the network interface */
 
 
1523
  if(take_down_interface){
 
 
1524
    /* Re-raise priviliges */
 
 
1531
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
 
1533
        perror("ioctl SIOCGIFFLAGS");
 
 
1534
      } else if(network.ifr_flags & IFF_UP) {
 
 
1535
        network.ifr_flags &= ~IFF_UP; /* clear flag */
 
 
1536
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
 
1538
          perror("ioctl SIOCSIFFLAGS");
 
 
1541
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
 
1545
      /* Lower privileges permanently */
 
 
1554
  /* Removes the temp directory used by GPGME */
 
 
1555
  if(tempdir_created){
 
 
1557
    struct dirent *direntry;
 
 
1558
    d = opendir(tempdir);
 
 
1560
      if(errno != ENOENT){
 
 
1565
        direntry = readdir(d);
 
 
1566
        if(direntry == NULL){
 
 
1569
        /* Skip "." and ".." */
 
 
1570
        if(direntry->d_name[0] == '.'
 
 
1571
           and (direntry->d_name[1] == '\0'
 
 
1572
                or (direntry->d_name[1] == '.'
 
 
1573
                    and direntry->d_name[2] == '\0'))){
 
 
1576
        char *fullname = NULL;
 
 
1577
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
 
1583
        ret = remove(fullname);
 
 
1585
          fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
 
 
1592
    ret = rmdir(tempdir);
 
 
1593
    if(ret == -1 and errno != ENOENT){
 
 
1599
    sigemptyset(&old_sigterm_action.sa_mask);
 
 
1600
    old_sigterm_action.sa_handler = SIG_DFL;
 
 
1601
    ret = sigaction(signal_received, &old_sigterm_action, NULL);
 
 
1603
      perror("sigaction");
 
 
1605
    raise(signal_received);