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-2018 Teddy Hogeborn
 
 
13
 * Copyright © 2008-2018 Björn Påhlsson
 
 
15
 * This file is part of Mandos.
 
 
17
 * Mandos is free software: you can redistribute it and/or modify it
 
 
18
 * under the terms of the GNU General Public License as published by
 
 
19
 * the Free Software Foundation, either version 3 of the License, or
 
 
20
 * (at your option) any later version.
 
 
22
 * Mandos is distributed in the hope that it will be useful, but
 
 
23
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
 
25
 * General Public License for more details.
 
 
27
 * You should have received a copy of the GNU General Public License
 
 
28
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
 
30
 * Contact the authors at <mandos@recompile.se>.
 
 
33
/* Needed by GPGME, specifically gpgme_data_seek() */
 
 
34
#ifndef _LARGEFILE_SOURCE
 
 
35
#define _LARGEFILE_SOURCE
 
 
36
#endif  /* not _LARGEFILE_SOURCE */
 
 
37
#ifndef _FILE_OFFSET_BITS
 
 
38
#define _FILE_OFFSET_BITS 64
 
 
39
#endif  /* not _FILE_OFFSET_BITS */
 
 
41
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
 
43
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
 
45
#include <stdint.h>             /* uint16_t, uint32_t, intptr_t */
 
 
46
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
 
47
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
 
 
49
#include <stdbool.h>            /* bool, false, true */
 
 
50
#include <string.h>             /* strcmp(), strlen(), strerror(),
 
 
51
                                   asprintf(), strncpy(), strsignal()
 
 
53
#include <sys/ioctl.h>          /* ioctl */
 
 
54
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
 
 
55
                                   sockaddr_in6, PF_INET6,
 
 
56
                                   SOCK_STREAM, uid_t, gid_t, open(),
 
 
58
#include <sys/stat.h>           /* open(), S_ISREG */
 
 
59
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
 
60
                                   inet_pton(), connect(),
 
 
62
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
 
 
63
#include <dirent.h>             /* opendir(), struct dirent, readdir()
 
 
65
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
 
 
67
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
 
 
68
                                   EAI_SYSTEM, ENETUNREACH,
 
 
69
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
 
 
70
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
 
 
72
                                   program_invocation_short_name */
 
 
73
#include <time.h>               /* nanosleep(), time(), sleep() */
 
 
74
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
 
75
                                   SIOCSIFFLAGS, if_indextoname(),
 
 
76
                                   if_nametoindex(), IF_NAMESIZE */
 
 
77
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
 
 
78
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
 
 
80
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
 
81
                                   getuid(), getgid(), seteuid(),
 
 
82
                                   setgid(), pause(), _exit(),
 
 
84
#include <arpa/inet.h>          /* inet_pton(), htons() */
 
 
85
#include <iso646.h>             /* not, or, and */
 
 
86
#include <argp.h>               /* struct argp_option, error_t, struct
 
 
87
                                   argp_state, struct argp,
 
 
88
                                   argp_parse(), ARGP_KEY_ARG,
 
 
89
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
 
 
90
#include <signal.h>             /* sigemptyset(), sigaddset(),
 
 
91
                                   sigaction(), SIGTERM, sig_atomic_t,
 
 
93
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
 
 
94
                                   EX_NOHOST, EX_IOERR, EX_PROTOCOL */
 
 
95
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
 
 
96
                                   WEXITSTATUS(), WTERMSIG() */
 
 
97
#include <grp.h>                /* setgroups() */
 
 
98
#include <argz.h>               /* argz_add_sep(), argz_next(),
 
 
99
                                   argz_delete(), argz_append(),
 
 
100
                                   argz_stringify(), argz_add(),
 
 
102
#include <netdb.h>              /* getnameinfo(), NI_NUMERICHOST,
 
 
103
                                   EAI_SYSTEM, gai_strerror() */
 
 
106
#include <sys/klog.h>           /* klogctl() */
 
 
107
#endif  /* __linux__ */
 
 
110
/* All Avahi types, constants and functions
 
 
113
#include <avahi-core/core.h>
 
 
114
#include <avahi-core/lookup.h>
 
 
115
#include <avahi-core/log.h>
 
 
116
#include <avahi-common/simple-watch.h>
 
 
117
#include <avahi-common/malloc.h>
 
 
118
#include <avahi-common/error.h>
 
 
121
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
 
124
                                   init_gnutls_session(),
 
 
126
#include <gnutls/openpgp.h>
 
 
127
                         /* gnutls_certificate_set_openpgp_key_file(),
 
 
128
                            GNUTLS_OPENPGP_FMT_BASE64 */
 
 
131
#include <gpgme.h>              /* All GPGME types, constants and
 
 
134
                                   GPGME_PROTOCOL_OpenPGP,
 
 
137
#define BUFFER_SIZE 256
 
 
139
#define PATHDIR "/conf/conf.d/mandos"
 
 
140
#define SECKEY "seckey.txt"
 
 
141
#define PUBKEY "pubkey.txt"
 
 
142
#define HOOKDIR "/lib/mandos/network-hooks.d"
 
 
145
static const char mandos_protocol_version[] = "1";
 
 
146
const char *argp_program_version = "mandos-client " VERSION;
 
 
147
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
 
148
static const char sys_class_net[] = "/sys/class/net";
 
 
149
char *connect_to = NULL;
 
 
150
const char *hookdir = HOOKDIR;
 
 
155
/* Doubly linked list that need to be circularly linked when used */
 
 
156
typedef struct server{
 
 
159
  AvahiIfIndex if_index;
 
 
161
  struct timespec last_seen;
 
 
166
/* Used for passing in values through the Avahi callback functions */
 
 
169
  gnutls_certificate_credentials_t cred;
 
 
170
  unsigned int dh_bits;
 
 
171
  gnutls_dh_params_t dh_params;
 
 
172
  const char *priority;
 
 
174
  server *current_server;
 
 
176
  size_t interfaces_size;
 
 
179
/* global so signal handler can reach it*/
 
 
180
AvahiSimplePoll *simple_poll;
 
 
182
sig_atomic_t quit_now = 0;
 
 
183
int signal_received = 0;
 
 
185
/* Function to use when printing errors */
 
 
186
void perror_plus(const char *print_text){
 
 
188
  fprintf(stderr, "Mandos plugin %s: ",
 
 
189
          program_invocation_short_name);
 
 
194
__attribute__((format (gnu_printf, 2, 3), nonnull))
 
 
195
int fprintf_plus(FILE *stream, const char *format, ...){
 
 
197
  va_start (ap, format);
 
 
199
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
 
 
200
                             program_invocation_short_name));
 
 
201
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
 
205
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
 
206
 * bytes. "buffer_capacity" is how much is currently allocated,
 
 
207
 * "buffer_length" is how much is already used.
 
 
209
__attribute__((nonnull, warn_unused_result))
 
 
210
size_t incbuffer(char **buffer, size_t buffer_length,
 
 
211
                 size_t buffer_capacity){
 
 
212
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
 
213
    char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
 
215
      int old_errno = errno;
 
 
222
    buffer_capacity += BUFFER_SIZE;
 
 
224
  return buffer_capacity;
 
 
227
/* Add server to set of servers to retry periodically */
 
 
228
__attribute__((nonnull, warn_unused_result))
 
 
229
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
 
 
230
                int af, server **current_server){
 
 
232
  server *new_server = malloc(sizeof(server));
 
 
233
  if(new_server == NULL){
 
 
234
    perror_plus("malloc");
 
 
237
  *new_server = (server){ .ip = strdup(ip),
 
 
239
                          .if_index = if_index,
 
 
241
  if(new_server->ip == NULL){
 
 
242
    perror_plus("strdup");
 
 
246
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
 
 
248
    perror_plus("clock_gettime");
 
 
250
#pragma GCC diagnostic push
 
 
251
#pragma GCC diagnostic ignored "-Wcast-qual"
 
 
253
    free((char *)(new_server->ip));
 
 
255
#pragma GCC diagnostic pop
 
 
260
  /* Special case of first server */
 
 
261
  if(*current_server == NULL){
 
 
262
    new_server->next = new_server;
 
 
263
    new_server->prev = new_server;
 
 
264
    *current_server = new_server;
 
 
266
    /* Place the new server last in the list */
 
 
267
    new_server->next = *current_server;
 
 
268
    new_server->prev = (*current_server)->prev;
 
 
269
    new_server->prev->next = new_server;
 
 
270
    (*current_server)->prev = new_server;
 
 
278
__attribute__((nonnull, warn_unused_result))
 
 
279
static bool init_gpgme(const char * const seckey,
 
 
280
                       const char * const pubkey,
 
 
281
                       const char * const tempdir,
 
 
284
  gpgme_engine_info_t engine_info;
 
 
287
   * Helper function to insert pub and seckey to the engine keyring.
 
 
289
  bool import_key(const char * const filename){
 
 
292
    gpgme_data_t pgp_data;
 
 
294
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
 
300
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
 
301
    if(rc != GPG_ERR_NO_ERROR){
 
 
302
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
 
303
                   gpgme_strsource(rc), gpgme_strerror(rc));
 
 
307
    rc = gpgme_op_import(mc->ctx, pgp_data);
 
 
308
    if(rc != GPG_ERR_NO_ERROR){
 
 
309
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
 
 
310
                   gpgme_strsource(rc), gpgme_strerror(rc));
 
 
314
      gpgme_import_result_t import_result
 
 
315
        = gpgme_op_import_result(mc->ctx);
 
 
316
      if((import_result->imported < 1
 
 
317
          or import_result->not_imported > 0)
 
 
318
         and import_result->unchanged == 0){
 
 
319
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
 
 
321
                     "The total number of considered keys: %d\n",
 
 
322
                     import_result->considered);
 
 
324
                     "The number of keys without user ID: %d\n",
 
 
325
                     import_result->no_user_id);
 
 
327
                     "The total number of imported keys: %d\n",
 
 
328
                     import_result->imported);
 
 
329
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
 
 
330
                     import_result->imported_rsa);
 
 
331
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
 
 
332
                     import_result->unchanged);
 
 
333
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
 
 
334
                     import_result->new_user_ids);
 
 
335
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
 
 
336
                     import_result->new_sub_keys);
 
 
337
        fprintf_plus(stderr, "The number of new signatures: %d\n",
 
 
338
                     import_result->new_signatures);
 
 
339
        fprintf_plus(stderr, "The number of new revocations: %d\n",
 
 
340
                     import_result->new_revocations);
 
 
342
                     "The total number of secret keys read: %d\n",
 
 
343
                     import_result->secret_read);
 
 
345
                     "The number of imported secret keys: %d\n",
 
 
346
                     import_result->secret_imported);
 
 
348
                     "The number of unchanged secret keys: %d\n",
 
 
349
                     import_result->secret_unchanged);
 
 
350
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
 
 
351
                     import_result->not_imported);
 
 
352
        for(gpgme_import_status_t import_status
 
 
353
              = import_result->imports;
 
 
354
            import_status != NULL;
 
 
355
            import_status = import_status->next){
 
 
356
          fprintf_plus(stderr, "Import status for key: %s\n",
 
 
358
          if(import_status->result != GPG_ERR_NO_ERROR){
 
 
359
            fprintf_plus(stderr, "Import result: %s: %s\n",
 
 
360
                         gpgme_strsource(import_status->result),
 
 
361
                         gpgme_strerror(import_status->result));
 
 
363
          fprintf_plus(stderr, "Key status:\n");
 
 
365
                       import_status->status & GPGME_IMPORT_NEW
 
 
366
                       ? "The key was new.\n"
 
 
367
                       : "The key was not new.\n");
 
 
369
                       import_status->status & GPGME_IMPORT_UID
 
 
370
                       ? "The key contained new user IDs.\n"
 
 
371
                       : "The key did not contain new user IDs.\n");
 
 
373
                       import_status->status & GPGME_IMPORT_SIG
 
 
374
                       ? "The key contained new signatures.\n"
 
 
375
                       : "The key did not contain new signatures.\n");
 
 
377
                       import_status->status & GPGME_IMPORT_SUBKEY
 
 
378
                       ? "The key contained new sub keys.\n"
 
 
379
                       : "The key did not contain new sub keys.\n");
 
 
381
                       import_status->status & GPGME_IMPORT_SECRET
 
 
382
                       ? "The key contained a secret key.\n"
 
 
383
                       : "The key did not contain a secret key.\n");
 
 
391
      perror_plus("close");
 
 
393
    gpgme_data_release(pgp_data);
 
 
398
    fprintf_plus(stderr, "Initializing GPGME\n");
 
 
402
  gpgme_check_version(NULL);
 
 
403
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
 
404
  if(rc != GPG_ERR_NO_ERROR){
 
 
405
    fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
 
406
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
410
  /* Set GPGME home directory for the OpenPGP engine only */
 
 
411
  rc = gpgme_get_engine_info(&engine_info);
 
 
412
  if(rc != GPG_ERR_NO_ERROR){
 
 
413
    fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
 
414
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
417
  while(engine_info != NULL){
 
 
418
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
 
 
419
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
 
 
420
                            engine_info->file_name, tempdir);
 
 
423
    engine_info = engine_info->next;
 
 
425
  if(engine_info == NULL){
 
 
426
    fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
 
 
431
  /* Create new GPGME "context" */
 
 
432
  rc = gpgme_new(&(mc->ctx));
 
 
433
  if(rc != GPG_ERR_NO_ERROR){
 
 
434
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
 
 
435
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
439
  if(not import_key(pubkey) or not import_key(seckey)){
 
 
447
 * Decrypt OpenPGP data.
 
 
448
 * Returns -1 on error
 
 
450
__attribute__((nonnull, warn_unused_result))
 
 
451
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
 
455
  gpgme_data_t dh_crypto, dh_plain;
 
 
458
  size_t plaintext_capacity = 0;
 
 
459
  ssize_t plaintext_length = 0;
 
 
462
    fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
 
 
465
  /* Create new GPGME data buffer from memory cryptotext */
 
 
466
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
 
468
  if(rc != GPG_ERR_NO_ERROR){
 
 
469
    fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
 
470
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
474
  /* Create new empty GPGME data buffer for the plaintext */
 
 
475
  rc = gpgme_data_new(&dh_plain);
 
 
476
  if(rc != GPG_ERR_NO_ERROR){
 
 
477
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
 
 
478
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
479
    gpgme_data_release(dh_crypto);
 
 
483
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
 
485
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
 
 
486
  if(rc != GPG_ERR_NO_ERROR){
 
 
487
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
 
488
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
489
    plaintext_length = -1;
 
 
491
      gpgme_decrypt_result_t result;
 
 
492
      result = gpgme_op_decrypt_result(mc->ctx);
 
 
494
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
 
 
496
        if(result->unsupported_algorithm != NULL) {
 
 
497
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
 
498
                       result->unsupported_algorithm);
 
 
500
        fprintf_plus(stderr, "Wrong key usage: %s\n",
 
 
501
                     result->wrong_key_usage ? "Yes" : "No");
 
 
502
        if(result->file_name != NULL){
 
 
503
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
 
 
506
        for(gpgme_recipient_t r = result->recipients; r != NULL;
 
 
508
          fprintf_plus(stderr, "Public key algorithm: %s\n",
 
 
509
                       gpgme_pubkey_algo_name(r->pubkey_algo));
 
 
510
          fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
 
 
511
          fprintf_plus(stderr, "Secret key available: %s\n",
 
 
512
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
 
 
520
    fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
 
 
523
  /* Seek back to the beginning of the GPGME plaintext data buffer */
 
 
524
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
 
525
    perror_plus("gpgme_data_seek");
 
 
526
    plaintext_length = -1;
 
 
532
    plaintext_capacity = incbuffer(plaintext,
 
 
533
                                   (size_t)plaintext_length,
 
 
535
    if(plaintext_capacity == 0){
 
 
536
      perror_plus("incbuffer");
 
 
537
      plaintext_length = -1;
 
 
541
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
 
543
    /* Print the data, if any */
 
 
549
      perror_plus("gpgme_data_read");
 
 
550
      plaintext_length = -1;
 
 
553
    plaintext_length += ret;
 
 
557
    fprintf_plus(stderr, "Decrypted password is: ");
 
 
558
    for(ssize_t i = 0; i < plaintext_length; i++){
 
 
559
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
 
561
    fprintf(stderr, "\n");
 
 
566
  /* Delete the GPGME cryptotext data buffer */
 
 
567
  gpgme_data_release(dh_crypto);
 
 
569
  /* Delete the GPGME plaintext data buffer */
 
 
570
  gpgme_data_release(dh_plain);
 
 
571
  return plaintext_length;
 
 
574
__attribute__((warn_unused_result, const))
 
 
575
static const char *safe_string(const char *str){
 
 
581
__attribute__((warn_unused_result))
 
 
582
static const char *safer_gnutls_strerror(int value){
 
 
583
  const char *ret = gnutls_strerror(value);
 
 
584
  return safe_string(ret);
 
 
587
/* GnuTLS log function callback */
 
 
588
__attribute__((nonnull))
 
 
589
static void debuggnutls(__attribute__((unused)) int level,
 
 
591
  fprintf_plus(stderr, "GnuTLS: %s", string);
 
 
594
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
 
595
static int init_gnutls_global(const char *pubkeyfilename,
 
 
596
                              const char *seckeyfilename,
 
 
597
                              const char *dhparamsfilename,
 
 
603
    fprintf_plus(stderr, "Initializing GnuTLS\n");
 
 
607
    /* "Use a log level over 10 to enable all debugging options."
 
 
610
    gnutls_global_set_log_level(11);
 
 
611
    gnutls_global_set_log_function(debuggnutls);
 
 
614
  /* OpenPGP credentials */
 
 
615
  ret = gnutls_certificate_allocate_credentials(&mc->cred);
 
 
616
  if(ret != GNUTLS_E_SUCCESS){
 
 
617
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
 
 
618
                 safer_gnutls_strerror(ret));
 
 
623
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
 
 
624
                 " secret key %s as GnuTLS credentials\n",
 
 
629
  ret = gnutls_certificate_set_openpgp_key_file
 
 
630
    (mc->cred, pubkeyfilename, seckeyfilename,
 
 
631
     GNUTLS_OPENPGP_FMT_BASE64);
 
 
632
  if(ret != GNUTLS_E_SUCCESS){
 
 
634
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
 
635
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
 
636
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
 
 
637
                 safer_gnutls_strerror(ret));
 
 
641
  /* GnuTLS server initialization */
 
 
642
  ret = gnutls_dh_params_init(&mc->dh_params);
 
 
643
  if(ret != GNUTLS_E_SUCCESS){
 
 
644
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
 
 
645
                 " initialization: %s\n",
 
 
646
                 safer_gnutls_strerror(ret));
 
 
649
  /* If a Diffie-Hellman parameters file was given, try to use it */
 
 
650
  if(dhparamsfilename != NULL){
 
 
651
    gnutls_datum_t params = { .data = NULL, .size = 0 };
 
 
653
      int dhpfile = open(dhparamsfilename, O_RDONLY);
 
 
656
        dhparamsfilename = NULL;
 
 
659
      size_t params_capacity = 0;
 
 
661
        params_capacity = incbuffer((char **)¶ms.data,
 
 
663
                                    (size_t)params_capacity);
 
 
664
        if(params_capacity == 0){
 
 
665
          perror_plus("incbuffer");
 
 
668
          dhparamsfilename = NULL;
 
 
671
        ssize_t bytes_read = read(dhpfile,
 
 
672
                                  params.data + params.size,
 
 
678
        /* check bytes_read for failure */
 
 
683
          dhparamsfilename = NULL;
 
 
686
        params.size += (unsigned int)bytes_read;
 
 
688
      ret = close(dhpfile);
 
 
690
        perror_plus("close");
 
 
692
      if(params.data == NULL){
 
 
693
        dhparamsfilename = NULL;
 
 
695
      if(dhparamsfilename == NULL){
 
 
698
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
 
 
699
                                          GNUTLS_X509_FMT_PEM);
 
 
700
      if(ret != GNUTLS_E_SUCCESS){
 
 
701
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
 
 
702
                     " \"%s\": %s\n", dhparamsfilename,
 
 
703
                     safer_gnutls_strerror(ret));
 
 
704
        dhparamsfilename = NULL;
 
 
709
  if(dhparamsfilename == NULL){
 
 
710
    if(mc->dh_bits == 0){
 
 
711
      /* Find out the optimal number of DH bits */
 
 
712
      /* Try to read the private key file */
 
 
713
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
 
715
        int secfile = open(seckeyfilename, O_RDONLY);
 
 
720
        size_t buffer_capacity = 0;
 
 
722
          buffer_capacity = incbuffer((char **)&buffer.data,
 
 
724
                                      (size_t)buffer_capacity);
 
 
725
          if(buffer_capacity == 0){
 
 
726
            perror_plus("incbuffer");
 
 
731
          ssize_t bytes_read = read(secfile,
 
 
732
                                    buffer.data + buffer.size,
 
 
738
          /* check bytes_read for failure */
 
 
745
          buffer.size += (unsigned int)bytes_read;
 
 
749
      /* If successful, use buffer to parse private key */
 
 
750
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
 
751
      if(buffer.data != NULL){
 
 
753
          gnutls_openpgp_privkey_t privkey = NULL;
 
 
754
          ret = gnutls_openpgp_privkey_init(&privkey);
 
 
755
          if(ret != GNUTLS_E_SUCCESS){
 
 
756
            fprintf_plus(stderr, "Error initializing OpenPGP key"
 
 
758
                         safer_gnutls_strerror(ret));
 
 
762
            ret = gnutls_openpgp_privkey_import
 
 
763
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
 
 
764
            if(ret != GNUTLS_E_SUCCESS){
 
 
765
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
 
766
                           safer_gnutls_strerror(ret));
 
 
772
              /* Use private key to suggest an appropriate
 
 
774
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
 
775
              gnutls_openpgp_privkey_deinit(privkey);
 
 
777
                fprintf_plus(stderr, "This OpenPGP key implies using"
 
 
778
                             " a GnuTLS security parameter \"%s\".\n",
 
 
779
                             safe_string(gnutls_sec_param_get_name
 
 
785
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
 
786
          /* Err on the side of caution */
 
 
787
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
 
789
            fprintf_plus(stderr, "Falling back to security parameter"
 
 
791
                         safe_string(gnutls_sec_param_get_name
 
 
796
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
 
800
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
 
801
                       " implies %u DH bits; using that.\n",
 
 
802
                       safe_string(gnutls_sec_param_get_name
 
 
807
        fprintf_plus(stderr, "Failed to get implied number of DH"
 
 
808
                     " bits for security parameter \"%s\"): %s\n",
 
 
809
                     safe_string(gnutls_sec_param_get_name
 
 
811
                     safer_gnutls_strerror(ret));
 
 
815
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
 
818
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
 
819
    if(ret != GNUTLS_E_SUCCESS){
 
 
820
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
 
 
821
                   " bits): %s\n", mc->dh_bits,
 
 
822
                   safer_gnutls_strerror(ret));
 
 
826
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
 
832
  gnutls_certificate_free_credentials(mc->cred);
 
 
833
  gnutls_dh_params_deinit(mc->dh_params);
 
 
837
__attribute__((nonnull, warn_unused_result))
 
 
838
static int init_gnutls_session(gnutls_session_t *session,
 
 
841
  /* GnuTLS session creation */
 
 
843
    ret = gnutls_init(session, GNUTLS_SERVER);
 
 
847
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
 
848
  if(ret != GNUTLS_E_SUCCESS){
 
 
850
                 "Error in GnuTLS session initialization: %s\n",
 
 
851
                 safer_gnutls_strerror(ret));
 
 
857
      ret = gnutls_priority_set_direct(*session, mc->priority, &err);
 
 
859
        gnutls_deinit(*session);
 
 
862
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
 
863
    if(ret != GNUTLS_E_SUCCESS){
 
 
864
      fprintf_plus(stderr, "Syntax error at: %s\n", err);
 
 
865
      fprintf_plus(stderr, "GnuTLS error: %s\n",
 
 
866
                   safer_gnutls_strerror(ret));
 
 
867
      gnutls_deinit(*session);
 
 
873
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
 
876
      gnutls_deinit(*session);
 
 
879
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
 
880
  if(ret != GNUTLS_E_SUCCESS){
 
 
881
    fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
 
 
882
                 safer_gnutls_strerror(ret));
 
 
883
    gnutls_deinit(*session);
 
 
887
  /* ignore client certificate if any. */
 
 
888
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
 
 
893
/* Avahi log function callback */
 
 
894
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
 
895
                      __attribute__((unused)) const char *txt){}
 
 
897
/* Set effective uid to 0, return errno */
 
 
898
__attribute__((warn_unused_result))
 
 
899
int raise_privileges(void){
 
 
900
  int old_errno = errno;
 
 
902
  if(seteuid(0) == -1){
 
 
909
/* Set effective and real user ID to 0.  Return errno. */
 
 
910
__attribute__((warn_unused_result))
 
 
911
int raise_privileges_permanently(void){
 
 
912
  int old_errno = errno;
 
 
913
  int ret = raise_privileges();
 
 
925
/* Set effective user ID to unprivileged saved user ID */
 
 
926
__attribute__((warn_unused_result))
 
 
927
int lower_privileges(void){
 
 
928
  int old_errno = errno;
 
 
930
  if(seteuid(uid) == -1){
 
 
937
/* Lower privileges permanently */
 
 
938
__attribute__((warn_unused_result))
 
 
939
int lower_privileges_permanently(void){
 
 
940
  int old_errno = errno;
 
 
942
  if(setuid(uid) == -1){
 
 
949
/* Helper function to add_local_route() and delete_local_route() */
 
 
950
__attribute__((nonnull, warn_unused_result))
 
 
951
static bool add_delete_local_route(const bool add,
 
 
953
                                   AvahiIfIndex if_index){
 
 
955
  char helper[] = "mandos-client-iprouteadddel";
 
 
956
  char add_arg[] = "add";
 
 
957
  char delete_arg[] = "delete";
 
 
958
  char debug_flag[] = "--debug";
 
 
959
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
 
 
960
  if(pluginhelperdir == NULL){
 
 
962
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
 
 
963
                   " variable not set; cannot run helper\n");
 
 
968
  char interface[IF_NAMESIZE];
 
 
969
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
 
970
    perror_plus("if_indextoname");
 
 
974
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
 
976
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
 
982
    /* Raise privileges */
 
 
983
    errno = raise_privileges_permanently();
 
 
985
      perror_plus("Failed to raise privileges");
 
 
986
      /* _exit(EX_NOPERM); */
 
 
992
        perror_plus("setgid");
 
 
995
      /* Reset supplementary groups */
 
 
997
      ret = setgroups(0, NULL);
 
 
999
        perror_plus("setgroups");
 
 
1003
    ret = dup2(devnull, STDIN_FILENO);
 
 
1005
      perror_plus("dup2(devnull, STDIN_FILENO)");
 
 
1008
    ret = close(devnull);
 
 
1010
      perror_plus("close");
 
 
1013
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
 
1015
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
 
1018
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
 
 
1023
    if(helperdir_fd == -1){
 
 
1024
      perror_plus("open");
 
 
1025
      _exit(EX_UNAVAILABLE);
 
 
1027
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
 
 
1029
    if(helper_fd == -1){
 
 
1030
      perror_plus("openat");
 
 
1031
      close(helperdir_fd);
 
 
1032
      _exit(EX_UNAVAILABLE);
 
 
1034
    close(helperdir_fd);
 
 
1036
#pragma GCC diagnostic push
 
 
1037
#pragma GCC diagnostic ignored "-Wcast-qual"
 
 
1039
    if(fexecve(helper_fd, (char *const [])
 
 
1040
               { helper, add ? add_arg : delete_arg, (char *)address,
 
 
1041
                   interface, debug ? debug_flag : NULL, NULL },
 
 
1044
#pragma GCC diagnostic pop
 
 
1046
      perror_plus("fexecve");
 
 
1047
      _exit(EXIT_FAILURE);
 
 
1051
    perror_plus("fork");
 
 
1058
    pret = waitpid(pid, &status, 0);
 
 
1059
    if(pret == -1 and errno == EINTR and quit_now){
 
 
1060
      int errno_raising = 0;
 
 
1061
      if((errno = raise_privileges()) != 0){
 
 
1062
        errno_raising = errno;
 
 
1063
        perror_plus("Failed to raise privileges in order to"
 
 
1064
                    " kill helper program");
 
 
1066
      if(kill(pid, SIGTERM) == -1){
 
 
1067
        perror_plus("kill");
 
 
1069
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
 
 
1070
        perror_plus("Failed to lower privileges after killing"
 
 
1075
  } while(pret == -1 and errno == EINTR);
 
 
1077
    perror_plus("waitpid");
 
 
1080
  if(WIFEXITED(status)){
 
 
1081
    if(WEXITSTATUS(status) != 0){
 
 
1082
      fprintf_plus(stderr, "Error: iprouteadddel exited"
 
 
1083
                   " with status %d\n", WEXITSTATUS(status));
 
 
1088
  if(WIFSIGNALED(status)){
 
 
1089
    fprintf_plus(stderr, "Error: iprouteadddel died by"
 
 
1090
                 " signal %d\n", WTERMSIG(status));
 
 
1093
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
 
 
1097
__attribute__((nonnull, warn_unused_result))
 
 
1098
static bool add_local_route(const char *address,
 
 
1099
                            AvahiIfIndex if_index){
 
 
1101
    fprintf_plus(stderr, "Adding route to %s\n", address);
 
 
1103
  return add_delete_local_route(true, address, if_index);
 
 
1106
__attribute__((nonnull, warn_unused_result))
 
 
1107
static bool delete_local_route(const char *address,
 
 
1108
                               AvahiIfIndex if_index){
 
 
1110
    fprintf_plus(stderr, "Removing route to %s\n", address);
 
 
1112
  return add_delete_local_route(false, address, if_index);
 
 
1115
/* Called when a Mandos server is found */
 
 
1116
__attribute__((nonnull, warn_unused_result))
 
 
1117
static int start_mandos_communication(const char *ip, in_port_t port,
 
 
1118
                                      AvahiIfIndex if_index,
 
 
1119
                                      int af, mandos_context *mc){
 
 
1120
  int ret, tcp_sd = -1;
 
 
1122
  struct sockaddr_storage to;
 
 
1123
  char *buffer = NULL;
 
 
1124
  char *decrypted_buffer = NULL;
 
 
1125
  size_t buffer_length = 0;
 
 
1126
  size_t buffer_capacity = 0;
 
 
1129
  gnutls_session_t session;
 
 
1130
  int pf;                       /* Protocol family */
 
 
1131
  bool route_added = false;
 
 
1148
    fprintf_plus(stderr, "Bad address family: %d\n", af);
 
 
1153
  /* If the interface is specified and we have a list of interfaces */
 
 
1154
  if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
 
 
1155
    /* Check if the interface is one of the interfaces we are using */
 
 
1158
      char *interface = NULL;
 
 
1159
      while((interface = argz_next(mc->interfaces,
 
 
1160
                                   mc->interfaces_size,
 
 
1162
        if(if_nametoindex(interface) == (unsigned int)if_index){
 
 
1169
      /* This interface does not match any in the list, so we don't
 
 
1170
         connect to the server */
 
 
1172
        char interface[IF_NAMESIZE];
 
 
1173
        if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
 
1174
          perror_plus("if_indextoname");
 
 
1176
          fprintf_plus(stderr, "Skipping server on non-used interface"
 
 
1178
                       if_indextoname((unsigned int)if_index,
 
 
1186
  ret = init_gnutls_session(&session, mc);
 
 
1192
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
 
 
1193
                 PRIuMAX "\n", ip, (uintmax_t)port);
 
 
1196
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
 
 
1199
    perror_plus("socket");
 
 
1210
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
 
 
1211
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
 
 
1212
    ret = inet_pton(af, ip, &to6->sin6_addr);
 
 
1214
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
 
 
1215
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
 
 
1216
    ret = inet_pton(af, ip, &to4->sin_addr);
 
 
1220
    perror_plus("inet_pton");
 
 
1226
    fprintf_plus(stderr, "Bad address: %s\n", ip);
 
 
1231
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
 
 
1232
    if(IN6_IS_ADDR_LINKLOCAL
 
 
1233
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
 
 
1234
      if(if_index == AVAHI_IF_UNSPEC){
 
 
1235
        fprintf_plus(stderr, "An IPv6 link-local address is"
 
 
1236
                     " incomplete without a network interface\n");
 
 
1240
      /* Set the network interface number as scope */
 
 
1241
      ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
 
 
1244
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
 
 
1253
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
 
1254
      char interface[IF_NAMESIZE];
 
 
1255
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
 
1256
        perror_plus("if_indextoname");
 
 
1258
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
 
 
1259
                     "\n", ip, interface, (uintmax_t)port);
 
 
1262
      fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
 
 
1263
                   ip, (uintmax_t)port);
 
 
1265
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
 
1266
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
 
1268
      ret = getnameinfo((struct sockaddr *)&to,
 
 
1269
                        sizeof(struct sockaddr_in6),
 
 
1270
                        addrstr, sizeof(addrstr), NULL, 0,
 
 
1273
      ret = getnameinfo((struct sockaddr *)&to,
 
 
1274
                        sizeof(struct sockaddr_in),
 
 
1275
                        addrstr, sizeof(addrstr), NULL, 0,
 
 
1278
    if(ret == EAI_SYSTEM){
 
 
1279
      perror_plus("getnameinfo");
 
 
1280
    } else if(ret != 0) {
 
 
1281
      fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
 
 
1282
    } else if(strcmp(addrstr, ip) != 0){
 
 
1283
      fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
 
 
1294
      ret = connect(tcp_sd, (struct sockaddr *)&to,
 
 
1295
                    sizeof(struct sockaddr_in6));
 
 
1297
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
 
1298
                    sizeof(struct sockaddr_in));
 
 
1301
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
 
1302
         and if_index != AVAHI_IF_UNSPEC
 
 
1303
         and connect_to == NULL
 
 
1304
         and not route_added and
 
 
1305
         ((af == AF_INET6 and not
 
 
1306
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
 
 
1308
          or (af == AF_INET and
 
 
1309
              /* Not a a IPv4LL address */
 
 
1310
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
 
 
1311
               & 0xFFFF0000L) != 0xA9FE0000L))){
 
 
1312
        /* Work around Avahi bug - Avahi does not announce link-local
 
 
1313
           addresses if it has a global address, so local hosts with
 
 
1314
           *only* a link-local address (e.g. Mandos clients) cannot
 
 
1315
           connect to a Mandos server announced by Avahi on a server
 
 
1316
           host with a global address.  Work around this by retrying
 
 
1317
           with an explicit route added with the server's address.
 
 
1319
           Avahi bug reference:
 
 
1320
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
 
1321
           https://bugs.debian.org/587961
 
 
1324
          fprintf_plus(stderr, "Mandos server unreachable, trying"
 
 
1328
        route_added = add_local_route(ip, if_index);
 
 
1334
      if(errno != ECONNREFUSED or debug){
 
 
1336
        perror_plus("connect");
 
 
1349
  const char *out = mandos_protocol_version;
 
 
1352
    size_t out_size = strlen(out);
 
 
1353
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
 
1354
                                        out_size - written));
 
 
1357
      perror_plus("write");
 
 
1361
    written += (size_t)ret;
 
 
1362
    if(written < out_size){
 
 
1365
      if(out == mandos_protocol_version){
 
 
1380
    fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
 
 
1388
  /* This casting via intptr_t is to eliminate warning about casting
 
 
1389
     an int to a pointer type.  This is exactly how the GnuTLS Guile
 
 
1390
     function "set-session-transport-fd!" does it. */
 
 
1391
  gnutls_transport_set_ptr(session,
 
 
1392
                           (gnutls_transport_ptr_t)(intptr_t)tcp_sd);
 
 
1400
    ret = gnutls_handshake(session);
 
 
1405
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
1407
  if(ret != GNUTLS_E_SUCCESS){
 
 
1409
      fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
 
 
1416
  /* Read OpenPGP packet that contains the wanted password */
 
 
1419
    fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
 
 
1430
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
 
1432
    if(buffer_capacity == 0){
 
 
1434
      perror_plus("incbuffer");
 
 
1444
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
 
1451
      case GNUTLS_E_INTERRUPTED:
 
 
1452
      case GNUTLS_E_AGAIN:
 
 
1454
      case GNUTLS_E_REHANDSHAKE:
 
 
1456
          ret = gnutls_handshake(session);
 
 
1462
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
1464
          fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
 
 
1472
        fprintf_plus(stderr, "Unknown error while reading data from"
 
 
1473
                     " encrypted session with Mandos server\n");
 
 
1474
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
 
1479
      buffer_length += (size_t) sret;
 
 
1484
    fprintf_plus(stderr, "Closing TLS session\n");
 
 
1493
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
 
1498
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
1500
  if(buffer_length > 0){
 
 
1501
    ssize_t decrypted_buffer_size;
 
 
1502
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
 
 
1503
                                               &decrypted_buffer, mc);
 
 
1504
    if(decrypted_buffer_size >= 0){
 
 
1508
      while(written < (size_t) decrypted_buffer_size){
 
 
1514
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
 
1515
                          (size_t)decrypted_buffer_size - written,
 
 
1517
        if(ret == 0 and ferror(stdout)){
 
 
1520
            fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
 
1526
        written += (size_t)ret;
 
 
1528
      ret = fflush(stdout);
 
 
1532
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
 
1542
  /* Shutdown procedure */
 
 
1547
      if(not delete_local_route(ip, if_index)){
 
 
1548
        fprintf_plus(stderr, "Failed to delete local route to %s on"
 
 
1549
                     " interface %d", ip, if_index);
 
 
1553
    free(decrypted_buffer);
 
 
1556
      ret = close(tcp_sd);
 
 
1562
      perror_plus("close");
 
 
1564
    gnutls_deinit(session);
 
 
1574
static void resolve_callback(AvahiSServiceResolver *r,
 
 
1575
                             AvahiIfIndex interface,
 
 
1576
                             AvahiProtocol proto,
 
 
1577
                             AvahiResolverEvent event,
 
 
1581
                             const char *host_name,
 
 
1582
                             const AvahiAddress *address,
 
 
1584
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
 
1585
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
1592
  /* Called whenever a service has been resolved successfully or
 
 
1596
    avahi_s_service_resolver_free(r);
 
 
1602
  case AVAHI_RESOLVER_FAILURE:
 
 
1603
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
 
 
1604
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
 
 
1606
                 avahi_strerror(avahi_server_errno
 
 
1607
                                (((mandos_context*)mc)->server)));
 
 
1610
  case AVAHI_RESOLVER_FOUND:
 
 
1612
      char ip[AVAHI_ADDRESS_STR_MAX];
 
 
1613
      avahi_address_snprint(ip, sizeof(ip), address);
 
 
1615
        fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
 
1616
                     PRIdMAX ") on port %" PRIu16 "\n", name,
 
 
1617
                     host_name, ip, (intmax_t)interface, port);
 
 
1619
      int ret = start_mandos_communication(ip, (in_port_t)port,
 
 
1621
                                           avahi_proto_to_af(proto),
 
 
1624
        avahi_simple_poll_quit(simple_poll);
 
 
1626
        if(not add_server(ip, (in_port_t)port, interface,
 
 
1627
                          avahi_proto_to_af(proto),
 
 
1628
                          &((mandos_context*)mc)->current_server)){
 
 
1629
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
 
 
1635
  avahi_s_service_resolver_free(r);
 
 
1638
static void browse_callback(AvahiSServiceBrowser *b,
 
 
1639
                            AvahiIfIndex interface,
 
 
1640
                            AvahiProtocol protocol,
 
 
1641
                            AvahiBrowserEvent event,
 
 
1645
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
1652
  /* Called whenever a new services becomes available on the LAN or
 
 
1653
     is removed from the LAN */
 
 
1661
  case AVAHI_BROWSER_FAILURE:
 
 
1663
    fprintf_plus(stderr, "(Avahi browser) %s\n",
 
 
1664
                 avahi_strerror(avahi_server_errno
 
 
1665
                                (((mandos_context*)mc)->server)));
 
 
1666
    avahi_simple_poll_quit(simple_poll);
 
 
1669
  case AVAHI_BROWSER_NEW:
 
 
1670
    /* We ignore the returned Avahi resolver object. In the callback
 
 
1671
       function we free it. If the Avahi server is terminated before
 
 
1672
       the callback function is called the Avahi server will free the
 
 
1675
    if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
 
 
1676
                                    interface, protocol, name, type,
 
 
1677
                                    domain, protocol, 0,
 
 
1678
                                    resolve_callback, mc) == NULL)
 
 
1679
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
 
 
1681
                   avahi_strerror(avahi_server_errno
 
 
1682
                                  (((mandos_context*)mc)->server)));
 
 
1685
  case AVAHI_BROWSER_REMOVE:
 
 
1688
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
 
1689
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
 
1691
      fprintf_plus(stderr, "No Mandos server found, still"
 
 
1698
/* Signal handler that stops main loop after SIGTERM */
 
 
1699
static void handle_sigterm(int sig){
 
 
1704
  signal_received = sig;
 
 
1705
  int old_errno = errno;
 
 
1706
  /* set main loop to exit */
 
 
1707
  if(simple_poll != NULL){
 
 
1708
    avahi_simple_poll_quit(simple_poll);
 
 
1713
__attribute__((nonnull, warn_unused_result))
 
 
1714
bool get_flags(const char *ifname, struct ifreq *ifr){
 
 
1718
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
 
1721
    perror_plus("socket");
 
 
1725
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
 
1726
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
 
1727
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
 
 
1731
      perror_plus("ioctl SIOCGIFFLAGS");
 
 
1734
    if((close(s) == -1) and debug){
 
 
1736
      perror_plus("close");
 
 
1741
  if((close(s) == -1) and debug){
 
 
1743
    perror_plus("close");
 
 
1749
__attribute__((nonnull, warn_unused_result))
 
 
1750
bool good_flags(const char *ifname, const struct ifreq *ifr){
 
 
1752
  /* Reject the loopback device */
 
 
1753
  if(ifr->ifr_flags & IFF_LOOPBACK){
 
 
1755
      fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
 
 
1760
  /* Accept point-to-point devices only if connect_to is specified */
 
 
1761
  if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
 
 
1763
      fprintf_plus(stderr, "Accepting point-to-point interface"
 
 
1764
                   " \"%s\"\n", ifname);
 
 
1768
  /* Otherwise, reject non-broadcast-capable devices */
 
 
1769
  if(not (ifr->ifr_flags & IFF_BROADCAST)){
 
 
1771
      fprintf_plus(stderr, "Rejecting non-broadcast interface"
 
 
1772
                   " \"%s\"\n", ifname);
 
 
1776
  /* Reject non-ARP interfaces (including dummy interfaces) */
 
 
1777
  if(ifr->ifr_flags & IFF_NOARP){
 
 
1779
      fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
 
 
1785
  /* Accept this device */
 
 
1787
    fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
 
 
1793
 * This function determines if a directory entry in /sys/class/net
 
 
1794
 * corresponds to an acceptable network device.
 
 
1795
 * (This function is passed to scandir(3) as a filter function.)
 
 
1797
__attribute__((nonnull, warn_unused_result))
 
 
1798
int good_interface(const struct dirent *if_entry){
 
 
1799
  if(if_entry->d_name[0] == '.'){
 
 
1804
  if(not get_flags(if_entry->d_name, &ifr)){
 
 
1806
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
1807
                   "\"%s\"\n", if_entry->d_name);
 
 
1812
  if(not good_flags(if_entry->d_name, &ifr)){
 
 
1819
 * This function determines if a network interface is up.
 
 
1821
__attribute__((nonnull, warn_unused_result))
 
 
1822
bool interface_is_up(const char *interface){
 
 
1824
  if(not get_flags(interface, &ifr)){
 
 
1826
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
1827
                   "\"%s\"\n", interface);
 
 
1832
  return (bool)(ifr.ifr_flags & IFF_UP);
 
 
1836
 * This function determines if a network interface is running
 
 
1838
__attribute__((nonnull, warn_unused_result))
 
 
1839
bool interface_is_running(const char *interface){
 
 
1841
  if(not get_flags(interface, &ifr)){
 
 
1843
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
1844
                   "\"%s\"\n", interface);
 
 
1849
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
 
 
1852
__attribute__((nonnull, pure, warn_unused_result))
 
 
1853
int notdotentries(const struct dirent *direntry){
 
 
1854
  /* Skip "." and ".." */
 
 
1855
  if(direntry->d_name[0] == '.'
 
 
1856
     and (direntry->d_name[1] == '\0'
 
 
1857
          or (direntry->d_name[1] == '.'
 
 
1858
              and direntry->d_name[2] == '\0'))){
 
 
1864
/* Is this directory entry a runnable program? */
 
 
1865
__attribute__((nonnull, warn_unused_result))
 
 
1866
int runnable_hook(const struct dirent *direntry){
 
 
1871
  if((direntry->d_name)[0] == '\0'){
 
 
1876
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
 
1877
                "abcdefghijklmnopqrstuvwxyz"
 
 
1880
  if((direntry->d_name)[sret] != '\0'){
 
 
1881
    /* Contains non-allowed characters */
 
 
1883
      fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
 
 
1889
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
 
 
1892
      perror_plus("Could not stat hook");
 
 
1896
  if(not (S_ISREG(st.st_mode))){
 
 
1897
    /* Not a regular file */
 
 
1899
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
 
 
1904
  if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
 
 
1905
    /* Not executable */
 
 
1907
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
 
 
1913
    fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
 
 
1919
__attribute__((nonnull, warn_unused_result))
 
 
1920
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
 
 
1921
                            mandos_context *mc){
 
 
1923
  struct timespec now;
 
 
1924
  struct timespec waited_time;
 
 
1925
  intmax_t block_time;
 
 
1928
    if(mc->current_server == NULL){
 
 
1930
        fprintf_plus(stderr, "Wait until first server is found."
 
 
1933
      ret = avahi_simple_poll_iterate(s, -1);
 
 
1936
        fprintf_plus(stderr, "Check current_server if we should run"
 
 
1939
      /* the current time */
 
 
1940
      ret = clock_gettime(CLOCK_MONOTONIC, &now);
 
 
1942
        perror_plus("clock_gettime");
 
 
1945
      /* Calculating in ms how long time between now and server
 
 
1946
         who we visted longest time ago. Now - last seen.  */
 
 
1947
      waited_time.tv_sec = (now.tv_sec
 
 
1948
                            - mc->current_server->last_seen.tv_sec);
 
 
1949
      waited_time.tv_nsec = (now.tv_nsec
 
 
1950
                             - mc->current_server->last_seen.tv_nsec);
 
 
1951
      /* total time is 10s/10,000ms.
 
 
1952
         Converting to s from ms by dividing by 1,000,
 
 
1953
         and ns to ms by dividing by 1,000,000. */
 
 
1954
      block_time = ((retry_interval
 
 
1955
                     - ((intmax_t)waited_time.tv_sec * 1000))
 
 
1956
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
 
 
1959
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
 
 
1963
      if(block_time <= 0){
 
 
1964
        ret = start_mandos_communication(mc->current_server->ip,
 
 
1965
                                         mc->current_server->port,
 
 
1966
                                         mc->current_server->if_index,
 
 
1967
                                         mc->current_server->af, mc);
 
 
1969
          avahi_simple_poll_quit(s);
 
 
1972
        ret = clock_gettime(CLOCK_MONOTONIC,
 
 
1973
                            &mc->current_server->last_seen);
 
 
1975
          perror_plus("clock_gettime");
 
 
1978
        mc->current_server = mc->current_server->next;
 
 
1979
        block_time = 0;         /* Call avahi to find new Mandos
 
 
1980
                                   servers, but don't block */
 
 
1983
      ret = avahi_simple_poll_iterate(s, (int)block_time);
 
 
1986
      if(ret > 0 or errno != EINTR){
 
 
1987
        return (ret != 1) ? ret : 0;
 
 
1993
__attribute__((nonnull))
 
 
1994
void run_network_hooks(const char *mode, const char *interface,
 
 
1996
  struct dirent **direntries = NULL;
 
 
1997
  if(hookdir_fd == -1){
 
 
1998
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
 
 
2000
    if(hookdir_fd == -1){
 
 
2001
      if(errno == ENOENT){
 
 
2003
          fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
 
2004
                       " found\n", hookdir);
 
 
2007
        perror_plus("open");
 
 
2012
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
 
2014
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
 
2017
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
 
 
2018
                           runnable_hook, alphasort);
 
 
2020
    perror_plus("scandir");
 
 
2024
  struct dirent *direntry;
 
 
2026
  for(int i = 0; i < numhooks; i++){
 
 
2027
    direntry = direntries[i];
 
 
2029
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
 
2032
    pid_t hook_pid = fork();
 
 
2035
      /* Raise privileges */
 
 
2036
      errno = raise_privileges_permanently();
 
 
2038
        perror_plus("Failed to raise privileges");
 
 
2045
        perror_plus("setgid");
 
 
2048
      /* Reset supplementary groups */
 
 
2050
      ret = setgroups(0, NULL);
 
 
2052
        perror_plus("setgroups");
 
 
2055
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
 
2057
        perror_plus("setenv");
 
 
2060
      ret = setenv("DEVICE", interface, 1);
 
 
2062
        perror_plus("setenv");
 
 
2065
      ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
 
2067
        perror_plus("setenv");
 
 
2070
      ret = setenv("MODE", mode, 1);
 
 
2072
        perror_plus("setenv");
 
 
2076
      ret = asprintf(&delaystring, "%f", (double)delay);
 
 
2078
        perror_plus("asprintf");
 
 
2081
      ret = setenv("DELAY", delaystring, 1);
 
 
2084
        perror_plus("setenv");
 
 
2088
      if(connect_to != NULL){
 
 
2089
        ret = setenv("CONNECT", connect_to, 1);
 
 
2091
          perror_plus("setenv");
 
 
2095
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
 
 
2099
        perror_plus("openat");
 
 
2100
        _exit(EXIT_FAILURE);
 
 
2102
      if(close(hookdir_fd) == -1){
 
 
2103
        perror_plus("close");
 
 
2104
        _exit(EXIT_FAILURE);
 
 
2106
      ret = dup2(devnull, STDIN_FILENO);
 
 
2108
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
 
2111
      ret = close(devnull);
 
 
2113
        perror_plus("close");
 
 
2116
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
 
2118
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
 
2121
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
 
2123
        perror_plus("fexecve");
 
 
2124
        _exit(EXIT_FAILURE);
 
 
2128
        perror_plus("fork");
 
 
2133
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
 
2134
        perror_plus("waitpid");
 
 
2138
      if(WIFEXITED(status)){
 
 
2139
        if(WEXITSTATUS(status) != 0){
 
 
2140
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
 
2141
                       " with status %d\n", direntry->d_name,
 
 
2142
                       WEXITSTATUS(status));
 
 
2146
      } else if(WIFSIGNALED(status)){
 
 
2147
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
 
2148
                     " signal %d\n", direntry->d_name,
 
 
2153
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
 
2154
                     " crashed\n", direntry->d_name);
 
 
2160
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
 
2166
  if(close(hookdir_fd) == -1){
 
 
2167
    perror_plus("close");
 
 
2174
__attribute__((nonnull, warn_unused_result))
 
 
2175
int bring_up_interface(const char *const interface,
 
 
2177
  int old_errno = errno;
 
 
2179
  struct ifreq network;
 
 
2180
  unsigned int if_index = if_nametoindex(interface);
 
 
2182
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
 
2192
  if(not interface_is_up(interface)){
 
 
2194
    int ioctl_errno = 0;
 
 
2195
    if(not get_flags(interface, &network)){
 
 
2197
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
2198
                   "\"%s\"\n", interface);
 
 
2202
    network.ifr_flags |= IFF_UP; /* set flag */
 
 
2204
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
 
2207
      perror_plus("socket");
 
 
2215
        perror_plus("close");
 
 
2222
      fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
 
 
2226
    /* Raise privileges */
 
 
2227
    ret_errno = raise_privileges();
 
 
2230
      perror_plus("Failed to raise privileges");
 
 
2235
    bool restore_loglevel = false;
 
 
2237
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
 
2238
         messages about the network interface to mess up the prompt */
 
 
2239
      ret_linux = klogctl(8, NULL, 5);
 
 
2240
      if(ret_linux == -1){
 
 
2241
        perror_plus("klogctl");
 
 
2243
        restore_loglevel = true;
 
 
2246
#endif  /* __linux__ */
 
 
2247
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
 
2248
    ioctl_errno = errno;
 
 
2250
    if(restore_loglevel){
 
 
2251
      ret_linux = klogctl(7, NULL, 0);
 
 
2252
      if(ret_linux == -1){
 
 
2253
        perror_plus("klogctl");
 
 
2256
#endif  /* __linux__ */
 
 
2258
    /* If raise_privileges() succeeded above */
 
 
2260
      /* Lower privileges */
 
 
2261
      ret_errno = lower_privileges();
 
 
2264
        perror_plus("Failed to lower privileges");
 
 
2268
    /* Close the socket */
 
 
2271
      perror_plus("close");
 
 
2274
    if(ret_setflags == -1){
 
 
2275
      errno = ioctl_errno;
 
 
2276
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
 
 
2281
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
 
 
2285
  /* Sleep checking until interface is running.
 
 
2286
     Check every 0.25s, up to total time of delay */
 
 
2287
  for(int i = 0; i < delay * 4; i++){
 
 
2288
    if(interface_is_running(interface)){
 
 
2291
    struct timespec sleeptime = { .tv_nsec = 250000000 };
 
 
2292
    ret = nanosleep(&sleeptime, NULL);
 
 
2293
    if(ret == -1 and errno != EINTR){
 
 
2294
      perror_plus("nanosleep");
 
 
2302
__attribute__((nonnull, warn_unused_result))
 
 
2303
int take_down_interface(const char *const interface){
 
 
2304
  int old_errno = errno;
 
 
2305
  struct ifreq network;
 
 
2306
  unsigned int if_index = if_nametoindex(interface);
 
 
2308
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
 
2312
  if(interface_is_up(interface)){
 
 
2314
    int ioctl_errno = 0;
 
 
2315
    if(not get_flags(interface, &network) and debug){
 
 
2317
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
2318
                   "\"%s\"\n", interface);
 
 
2322
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
 
 
2324
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
 
2327
      perror_plus("socket");
 
 
2333
      fprintf_plus(stderr, "Taking down interface \"%s\"\n",
 
 
2337
    /* Raise privileges */
 
 
2338
    ret_errno = raise_privileges();
 
 
2341
      perror_plus("Failed to raise privileges");
 
 
2344
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
 
2345
    ioctl_errno = errno;
 
 
2347
    /* If raise_privileges() succeeded above */
 
 
2349
      /* Lower privileges */
 
 
2350
      ret_errno = lower_privileges();
 
 
2353
        perror_plus("Failed to lower privileges");
 
 
2357
    /* Close the socket */
 
 
2358
    int ret = close(sd);
 
 
2360
      perror_plus("close");
 
 
2363
    if(ret_setflags == -1){
 
 
2364
      errno = ioctl_errno;
 
 
2365
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
 
 
2370
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
 
 
2378
int main(int argc, char *argv[]){
 
 
2379
  mandos_context mc = { .server = NULL, .dh_bits = 0,
 
 
2380
                        .priority = "SECURE256:!CTYPE-X.509"
 
 
2381
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
 
2382
                        .current_server = NULL, .interfaces = NULL,
 
 
2383
                        .interfaces_size = 0 };
 
 
2384
  AvahiSServiceBrowser *sb = NULL;
 
 
2389
  int exitcode = EXIT_SUCCESS;
 
 
2390
  char *interfaces_to_take_down = NULL;
 
 
2391
  size_t interfaces_to_take_down_size = 0;
 
 
2392
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
 
2393
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
 
2394
  char *tempdir = NULL;
 
 
2395
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
 
2396
  const char *seckey = PATHDIR "/" SECKEY;
 
 
2397
  const char *pubkey = PATHDIR "/" PUBKEY;
 
 
2398
  const char *dh_params_file = NULL;
 
 
2399
  char *interfaces_hooks = NULL;
 
 
2401
  bool gnutls_initialized = false;
 
 
2402
  bool gpgme_initialized = false;
 
 
2404
  double retry_interval = 10; /* 10s between trying a server and
 
 
2405
                                 retrying the same server again */
 
 
2407
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
 
 
2408
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
 
2413
  /* Lower any group privileges we might have, just to be safe */
 
 
2417
    perror_plus("setgid");
 
 
2420
  /* Lower user privileges (temporarily) */
 
 
2424
    perror_plus("seteuid");
 
 
2432
    struct argp_option options[] = {
 
 
2433
      { .name = "debug", .key = 128,
 
 
2434
        .doc = "Debug mode", .group = 3 },
 
 
2435
      { .name = "connect", .key = 'c',
 
 
2436
        .arg = "ADDRESS:PORT",
 
 
2437
        .doc = "Connect directly to a specific Mandos server",
 
 
2439
      { .name = "interface", .key = 'i',
 
 
2441
        .doc = "Network interface that will be used to search for"
 
 
2444
      { .name = "seckey", .key = 's',
 
 
2446
        .doc = "OpenPGP secret key file base name",
 
 
2448
      { .name = "pubkey", .key = 'p',
 
 
2450
        .doc = "OpenPGP public key file base name",
 
 
2452
      { .name = "dh-bits", .key = 129,
 
 
2454
        .doc = "Bit length of the prime number used in the"
 
 
2455
        " Diffie-Hellman key exchange",
 
 
2457
      { .name = "dh-params", .key = 134,
 
 
2459
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
 
 
2460
        " for the Diffie-Hellman key exchange",
 
 
2462
      { .name = "priority", .key = 130,
 
 
2464
        .doc = "GnuTLS priority string for the TLS handshake",
 
 
2466
      { .name = "delay", .key = 131,
 
 
2468
        .doc = "Maximum delay to wait for interface startup",
 
 
2470
      { .name = "retry", .key = 132,
 
 
2472
        .doc = "Retry interval used when denied by the Mandos server",
 
 
2474
      { .name = "network-hook-dir", .key = 133,
 
 
2476
        .doc = "Directory where network hooks are located",
 
 
2479
       * These reproduce what we would get without ARGP_NO_HELP
 
 
2481
      { .name = "help", .key = '?',
 
 
2482
        .doc = "Give this help list", .group = -1 },
 
 
2483
      { .name = "usage", .key = -3,
 
 
2484
        .doc = "Give a short usage message", .group = -1 },
 
 
2485
      { .name = "version", .key = 'V',
 
 
2486
        .doc = "Print program version", .group = -1 },
 
 
2490
    error_t parse_opt(int key, char *arg,
 
 
2491
                      struct argp_state *state){
 
 
2494
      case 128:                 /* --debug */
 
 
2497
      case 'c':                 /* --connect */
 
 
2500
      case 'i':                 /* --interface */
 
 
2501
        ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
 
 
2504
          argp_error(state, "%s", strerror(ret_errno));
 
 
2507
      case 's':                 /* --seckey */
 
 
2510
      case 'p':                 /* --pubkey */
 
 
2513
      case 129:                 /* --dh-bits */
 
 
2515
        tmpmax = strtoimax(arg, &tmp, 10);
 
 
2516
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
 
2517
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
 
2518
          argp_error(state, "Bad number of DH bits");
 
 
2520
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
 
2522
      case 134:                 /* --dh-params */
 
 
2523
        dh_params_file = arg;
 
 
2525
      case 130:                 /* --priority */
 
 
2528
      case 131:                 /* --delay */
 
 
2530
        delay = strtof(arg, &tmp);
 
 
2531
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
 
2532
          argp_error(state, "Bad delay");
 
 
2534
      case 132:                 /* --retry */
 
 
2536
        retry_interval = strtod(arg, &tmp);
 
 
2537
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
 
2538
           or (retry_interval * 1000) > INT_MAX
 
 
2539
           or retry_interval < 0){
 
 
2540
          argp_error(state, "Bad retry interval");
 
 
2543
      case 133:                 /* --network-hook-dir */
 
 
2547
         * These reproduce what we would get without ARGP_NO_HELP
 
 
2549
      case '?':                 /* --help */
 
 
2550
        argp_state_help(state, state->out_stream,
 
 
2551
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
 
2552
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
 
2553
      case -3:                  /* --usage */
 
 
2554
        argp_state_help(state, state->out_stream,
 
 
2555
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
 
2556
      case 'V':                 /* --version */
 
 
2557
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
 
 
2558
        exit(argp_err_exit_status);
 
 
2561
        return ARGP_ERR_UNKNOWN;
 
 
2566
    struct argp argp = { .options = options, .parser = parse_opt,
 
 
2568
                         .doc = "Mandos client -- Get and decrypt"
 
 
2569
                         " passwords from a Mandos server" };
 
 
2570
    ret_errno = argp_parse(&argp, argc, argv,
 
 
2571
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
 
2578
      perror_plus("argp_parse");
 
 
2579
      exitcode = EX_OSERR;
 
 
2582
      exitcode = EX_USAGE;
 
 
2588
    /* Work around Debian bug #633582:
 
 
2589
       <https://bugs.debian.org/633582> */
 
 
2591
    /* Re-raise privileges */
 
 
2592
    ret = raise_privileges();
 
 
2595
      perror_plus("Failed to raise privileges");
 
 
2599
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
 
 
2600
        int seckey_fd = open(seckey, O_RDONLY);
 
 
2601
        if(seckey_fd == -1){
 
 
2602
          perror_plus("open");
 
 
2604
          ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
 
 
2606
            perror_plus("fstat");
 
 
2608
            if(S_ISREG(st.st_mode)
 
 
2609
               and st.st_uid == 0 and st.st_gid == 0){
 
 
2610
              ret = fchown(seckey_fd, uid, gid);
 
 
2612
                perror_plus("fchown");
 
 
2620
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
 
 
2621
        int pubkey_fd = open(pubkey, O_RDONLY);
 
 
2622
        if(pubkey_fd == -1){
 
 
2623
          perror_plus("open");
 
 
2625
          ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
 
 
2627
            perror_plus("fstat");
 
 
2629
            if(S_ISREG(st.st_mode)
 
 
2630
               and st.st_uid == 0 and st.st_gid == 0){
 
 
2631
              ret = fchown(pubkey_fd, uid, gid);
 
 
2633
                perror_plus("fchown");
 
 
2641
      if(dh_params_file != NULL
 
 
2642
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
 
 
2643
        int dhparams_fd = open(dh_params_file, O_RDONLY);
 
 
2644
        if(dhparams_fd == -1){
 
 
2645
          perror_plus("open");
 
 
2647
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
 
 
2649
            perror_plus("fstat");
 
 
2651
            if(S_ISREG(st.st_mode)
 
 
2652
               and st.st_uid == 0 and st.st_gid == 0){
 
 
2653
              ret = fchown(dhparams_fd, uid, gid);
 
 
2655
                perror_plus("fchown");
 
 
2663
      /* Lower privileges */
 
 
2664
      ret = lower_privileges();
 
 
2667
        perror_plus("Failed to lower privileges");
 
 
2672
  /* Remove invalid interface names (except "none") */
 
 
2674
    char *interface = NULL;
 
 
2675
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
 
2677
      if(strcmp(interface, "none") != 0
 
 
2678
         and if_nametoindex(interface) == 0){
 
 
2679
        if(interface[0] != '\0'){
 
 
2680
          fprintf_plus(stderr, "Not using nonexisting interface"
 
 
2681
                       " \"%s\"\n", interface);
 
 
2683
        argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
 
 
2689
  /* Run network hooks */
 
 
2691
    if(mc.interfaces != NULL){
 
 
2692
      interfaces_hooks = malloc(mc.interfaces_size);
 
 
2693
      if(interfaces_hooks == NULL){
 
 
2694
        perror_plus("malloc");
 
 
2697
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
 
 
2698
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
 
 
2700
    run_network_hooks("start", interfaces_hooks != NULL ?
 
 
2701
                      interfaces_hooks : "", delay);
 
 
2705
    avahi_set_log_function(empty_log);
 
 
2708
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
 
2709
     from the signal handler */
 
 
2710
  /* Initialize the pseudo-RNG for Avahi */
 
 
2711
  srand((unsigned int) time(NULL));
 
 
2712
  simple_poll = avahi_simple_poll_new();
 
 
2713
  if(simple_poll == NULL){
 
 
2714
    fprintf_plus(stderr,
 
 
2715
                 "Avahi: Failed to create simple poll object.\n");
 
 
2716
    exitcode = EX_UNAVAILABLE;
 
 
2720
  sigemptyset(&sigterm_action.sa_mask);
 
 
2721
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
 
2723
    perror_plus("sigaddset");
 
 
2724
    exitcode = EX_OSERR;
 
 
2727
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
 
2729
    perror_plus("sigaddset");
 
 
2730
    exitcode = EX_OSERR;
 
 
2733
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
 
2735
    perror_plus("sigaddset");
 
 
2736
    exitcode = EX_OSERR;
 
 
2739
  /* Need to check if the handler is SIG_IGN before handling:
 
 
2740
     | [[info:libc:Initial Signal Actions]] |
 
 
2741
     | [[info:libc:Basic Signal Handling]]  |
 
 
2743
  ret = sigaction(SIGINT, NULL, &old_sigterm_action);
 
 
2745
    perror_plus("sigaction");
 
 
2748
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
 
2749
    ret = sigaction(SIGINT, &sigterm_action, NULL);
 
 
2751
      perror_plus("sigaction");
 
 
2752
      exitcode = EX_OSERR;
 
 
2756
  ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
 
 
2758
    perror_plus("sigaction");
 
 
2761
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
 
2762
    ret = sigaction(SIGHUP, &sigterm_action, NULL);
 
 
2764
      perror_plus("sigaction");
 
 
2765
      exitcode = EX_OSERR;
 
 
2769
  ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
 
 
2771
    perror_plus("sigaction");
 
 
2774
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
 
2775
    ret = sigaction(SIGTERM, &sigterm_action, NULL);
 
 
2777
      perror_plus("sigaction");
 
 
2778
      exitcode = EX_OSERR;
 
 
2783
  /* If no interfaces were specified, make a list */
 
 
2784
  if(mc.interfaces == NULL){
 
 
2785
    struct dirent **direntries = NULL;
 
 
2786
    /* Look for any good interfaces */
 
 
2787
    ret = scandir(sys_class_net, &direntries, good_interface,
 
 
2790
      /* Add all found interfaces to interfaces list */
 
 
2791
      for(int i = 0; i < ret; ++i){
 
 
2792
        ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
 
 
2793
                             direntries[i]->d_name);
 
 
2796
          perror_plus("argz_add");
 
 
2797
          free(direntries[i]);
 
 
2801
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
 
 
2802
                       direntries[i]->d_name);
 
 
2804
        free(direntries[i]);
 
 
2811
      fprintf_plus(stderr, "Could not find a network interface\n");
 
 
2812
      exitcode = EXIT_FAILURE;
 
 
2817
  /* Bring up interfaces which are down, and remove any "none"s */
 
 
2819
    char *interface = NULL;
 
 
2820
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
 
2822
      /* If interface name is "none", stop bringing up interfaces.
 
 
2823
         Also remove all instances of "none" from the list */
 
 
2824
      if(strcmp(interface, "none") == 0){
 
 
2825
        argz_delete(&mc.interfaces, &mc.interfaces_size,
 
 
2828
        while((interface = argz_next(mc.interfaces,
 
 
2829
                                     mc.interfaces_size, interface))){
 
 
2830
          if(strcmp(interface, "none") == 0){
 
 
2831
            argz_delete(&mc.interfaces, &mc.interfaces_size,
 
 
2838
      bool interface_was_up = interface_is_up(interface);
 
 
2839
      errno = bring_up_interface(interface, delay);
 
 
2840
      if(not interface_was_up){
 
 
2842
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
 
2843
                       " %s\n", interface, strerror(errno));
 
 
2845
          errno = argz_add(&interfaces_to_take_down,
 
 
2846
                           &interfaces_to_take_down_size,
 
 
2849
            perror_plus("argz_add");
 
 
2854
    if(debug and (interfaces_to_take_down == NULL)){
 
 
2855
      fprintf_plus(stderr, "No interfaces were brought up\n");
 
 
2859
  /* If we only got one interface, explicitly use only that one */
 
 
2860
  if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
 
 
2862
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
 
 
2865
    if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
 
 
2872
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
 
2874
    fprintf_plus(stderr, "init_gnutls_global failed\n");
 
 
2875
    exitcode = EX_UNAVAILABLE;
 
 
2878
    gnutls_initialized = true;
 
 
2885
  /* Try /run/tmp before /tmp */
 
 
2886
  tempdir = mkdtemp(run_tempdir);
 
 
2887
  if(tempdir == NULL and errno == ENOENT){
 
 
2889
        fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
 
 
2890
                     run_tempdir, old_tempdir);
 
 
2892
      tempdir = mkdtemp(old_tempdir);
 
 
2894
  if(tempdir == NULL){
 
 
2895
    perror_plus("mkdtemp");
 
 
2903
  if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
 
 
2904
    fprintf_plus(stderr, "init_gpgme failed\n");
 
 
2905
    exitcode = EX_UNAVAILABLE;
 
 
2908
    gpgme_initialized = true;
 
 
2915
  if(connect_to != NULL){
 
 
2916
    /* Connect directly, do not use Zeroconf */
 
 
2917
    /* (Mainly meant for debugging) */
 
 
2918
    char *address = strrchr(connect_to, ':');
 
 
2920
    if(address == NULL){
 
 
2921
      fprintf_plus(stderr, "No colon in address\n");
 
 
2922
      exitcode = EX_USAGE;
 
 
2932
    tmpmax = strtoimax(address+1, &tmp, 10);
 
 
2933
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
 
2934
       or tmpmax != (in_port_t)tmpmax){
 
 
2935
      fprintf_plus(stderr, "Bad port number\n");
 
 
2936
      exitcode = EX_USAGE;
 
 
2944
    port = (in_port_t)tmpmax;
 
 
2946
    /* Colon in address indicates IPv6 */
 
 
2948
    if(strchr(connect_to, ':') != NULL){
 
 
2950
      /* Accept [] around IPv6 address - see RFC 5952 */
 
 
2951
      if(connect_to[0] == '[' and address[-1] == ']')
 
 
2959
    address = connect_to;
 
 
2965
    while(not quit_now){
 
 
2966
      ret = start_mandos_communication(address, port, if_index, af,
 
 
2968
      if(quit_now or ret == 0){
 
 
2972
        fprintf_plus(stderr, "Retrying in %d seconds\n",
 
 
2973
                     (int)retry_interval);
 
 
2975
      sleep((unsigned int)retry_interval);
 
 
2979
      exitcode = EXIT_SUCCESS;
 
 
2990
    AvahiServerConfig config;
 
 
2991
    /* Do not publish any local Zeroconf records */
 
 
2992
    avahi_server_config_init(&config);
 
 
2993
    config.publish_hinfo = 0;
 
 
2994
    config.publish_addresses = 0;
 
 
2995
    config.publish_workstation = 0;
 
 
2996
    config.publish_domain = 0;
 
 
2998
    /* Allocate a new server */
 
 
2999
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
 
3000
                                 &config, NULL, NULL, &ret);
 
 
3002
    /* Free the Avahi configuration data */
 
 
3003
    avahi_server_config_free(&config);
 
 
3006
  /* Check if creating the Avahi server object succeeded */
 
 
3007
  if(mc.server == NULL){
 
 
3008
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
 
 
3009
                 avahi_strerror(ret));
 
 
3010
    exitcode = EX_UNAVAILABLE;
 
 
3018
  /* Create the Avahi service browser */
 
 
3019
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
 
3020
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
 
3021
                                   NULL, 0, browse_callback,
 
 
3024
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
 
 
3025
                 avahi_strerror(avahi_server_errno(mc.server)));
 
 
3026
    exitcode = EX_UNAVAILABLE;
 
 
3034
  /* Run the main loop */
 
 
3037
    fprintf_plus(stderr, "Starting Avahi loop search\n");
 
 
3040
  ret = avahi_loop_with_timeout(simple_poll,
 
 
3041
                                (int)(retry_interval * 1000), &mc);
 
 
3043
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
 
 
3044
                 (ret == 0) ? "successfully" : "with error");
 
 
3050
    if(signal_received){
 
 
3051
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
 
 
3052
                   argv[0], signal_received,
 
 
3053
                   strsignal(signal_received));
 
 
3055
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
 
3059
  /* Cleanup things */
 
 
3060
  free(mc.interfaces);
 
 
3063
    avahi_s_service_browser_free(sb);
 
 
3065
  if(mc.server != NULL)
 
 
3066
    avahi_server_free(mc.server);
 
 
3068
  if(simple_poll != NULL)
 
 
3069
    avahi_simple_poll_free(simple_poll);
 
 
3071
  if(gnutls_initialized){
 
 
3072
    gnutls_certificate_free_credentials(mc.cred);
 
 
3073
    gnutls_dh_params_deinit(mc.dh_params);
 
 
3076
  if(gpgme_initialized){
 
 
3077
    gpgme_release(mc.ctx);
 
 
3080
  /* Cleans up the circular linked list of Mandos servers the client
 
 
3082
  if(mc.current_server != NULL){
 
 
3083
    mc.current_server->prev->next = NULL;
 
 
3084
    while(mc.current_server != NULL){
 
 
3085
      server *next = mc.current_server->next;
 
 
3087
#pragma GCC diagnostic push
 
 
3088
#pragma GCC diagnostic ignored "-Wcast-qual"
 
 
3090
      free((char *)(mc.current_server->ip));
 
 
3092
#pragma GCC diagnostic pop
 
 
3094
      free(mc.current_server);
 
 
3095
      mc.current_server = next;
 
 
3099
  /* Re-raise privileges */
 
 
3101
    ret = raise_privileges();
 
 
3104
      perror_plus("Failed to raise privileges");
 
 
3107
      /* Run network hooks */
 
 
3108
      run_network_hooks("stop", interfaces_hooks != NULL ?
 
 
3109
                        interfaces_hooks : "", delay);
 
 
3111
      /* Take down the network interfaces which were brought up */
 
 
3113
        char *interface = NULL;
 
 
3114
        while((interface = argz_next(interfaces_to_take_down,
 
 
3115
                                     interfaces_to_take_down_size,
 
 
3117
          ret = take_down_interface(interface);
 
 
3120
            perror_plus("Failed to take down interface");
 
 
3123
        if(debug and (interfaces_to_take_down == NULL)){
 
 
3124
          fprintf_plus(stderr, "No interfaces needed to be taken"
 
 
3130
    ret = lower_privileges_permanently();
 
 
3133
      perror_plus("Failed to lower privileges permanently");
 
 
3137
  free(interfaces_to_take_down);
 
 
3138
  free(interfaces_hooks);
 
 
3140
  void clean_dir_at(int base, const char * const dirname,
 
 
3142
    struct dirent **direntries = NULL;
 
 
3144
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
 
 
3150
      perror_plus("open");
 
 
3153
    int numentries = scandirat(dir_fd, ".", &direntries,
 
 
3154
                               notdotentries, alphasort);
 
 
3155
    if(numentries >= 0){
 
 
3156
      for(int i = 0; i < numentries; i++){
 
 
3158
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
 
 
3159
                       dirname, direntries[i]->d_name);
 
 
3161
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
 
 
3163
          if(errno == EISDIR){
 
 
3164
              dret = unlinkat(dir_fd, direntries[i]->d_name,
 
 
3167
          if((dret == -1) and (errno == ENOTEMPTY)
 
 
3168
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
 
 
3169
                  == 0) and (level == 0)){
 
 
3170
            /* Recurse only in this special case */
 
 
3171
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
 
 
3174
          if((dret == -1) and (errno != ENOENT)){
 
 
3175
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
 
 
3176
                         direntries[i]->d_name, strerror(errno));
 
 
3179
        free(direntries[i]);
 
 
3182
      /* need to clean even if 0 because man page doesn't specify */
 
 
3184
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
 
 
3185
      if(dret == -1 and errno != ENOENT){
 
 
3186
        perror_plus("rmdir");
 
 
3189
      perror_plus("scandirat");
 
 
3194
  /* Removes the GPGME temp directory and all files inside */
 
 
3195
  if(tempdir != NULL){
 
 
3196
    clean_dir_at(-1, tempdir, 0);
 
 
3200
    sigemptyset(&old_sigterm_action.sa_mask);
 
 
3201
    old_sigterm_action.sa_handler = SIG_DFL;
 
 
3202
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
 
3203
                                            &old_sigterm_action,
 
 
3206
      perror_plus("sigaction");
 
 
3209
      ret = raise(signal_received);
 
 
3210
    } while(ret != 0 and errno == EINTR);
 
 
3212
      perror_plus("raise");
 
 
3215
    TEMP_FAILURE_RETRY(pause());