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-2013 Teddy Hogeborn
 
 
13
 * Copyright © 2008-2013 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@recompile.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, intptr_t */
 
 
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
 
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
 
 
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(), S_ISREG */
 
 
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 <errno.h>              /* perror(), errno,
 
 
65
                                   program_invocation_short_name */
 
 
66
#include <time.h>               /* nanosleep(), time(), sleep() */
 
 
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(),
 
 
75
                                   setgid(), pause(), _exit() */
 
 
76
#include <arpa/inet.h>          /* inet_pton(), htons, inet_ntop() */
 
 
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,
 
 
85
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
 
 
86
                                   EX_NOHOST, EX_IOERR, EX_PROTOCOL */
 
 
87
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
 
 
88
                                   WEXITSTATUS(), WTERMSIG() */
 
 
89
#include <grp.h>                /* setgroups() */
 
 
90
#include <argz.h>               /* argz_add_sep(), argz_next(),
 
 
91
                                   argz_delete(), argz_append(),
 
 
92
                                   argz_stringify(), argz_add(),
 
 
96
#include <sys/klog.h>           /* klogctl() */
 
 
97
#endif  /* __linux__ */
 
 
100
/* All Avahi types, constants and functions
 
 
103
#include <avahi-core/core.h>
 
 
104
#include <avahi-core/lookup.h>
 
 
105
#include <avahi-core/log.h>
 
 
106
#include <avahi-common/simple-watch.h>
 
 
107
#include <avahi-common/malloc.h>
 
 
108
#include <avahi-common/error.h>
 
 
111
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
 
114
                                   init_gnutls_session(),
 
 
116
#include <gnutls/openpgp.h>
 
 
117
                         /* gnutls_certificate_set_openpgp_key_file(),
 
 
118
                            GNUTLS_OPENPGP_FMT_BASE64 */
 
 
121
#include <gpgme.h>              /* All GPGME types, constants and
 
 
124
                                   GPGME_PROTOCOL_OpenPGP,
 
 
127
#define BUFFER_SIZE 256
 
 
129
#define PATHDIR "/conf/conf.d/mandos"
 
 
130
#define SECKEY "seckey.txt"
 
 
131
#define PUBKEY "pubkey.txt"
 
 
132
#define HOOKDIR "/lib/mandos/network-hooks.d"
 
 
135
static const char mandos_protocol_version[] = "1";
 
 
136
const char *argp_program_version = "mandos-client " VERSION;
 
 
137
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
 
138
static const char sys_class_net[] = "/sys/class/net";
 
 
139
char *connect_to = NULL;
 
 
140
const char *hookdir = HOOKDIR;
 
 
144
/* Doubly linked list that need to be circularly linked when used */
 
 
145
typedef struct server{
 
 
148
  AvahiIfIndex if_index;
 
 
150
  struct timespec last_seen;
 
 
155
/* Used for passing in values through the Avahi callback functions */
 
 
158
  gnutls_certificate_credentials_t cred;
 
 
159
  unsigned int dh_bits;
 
 
160
  gnutls_dh_params_t dh_params;
 
 
161
  const char *priority;
 
 
163
  server *current_server;
 
 
165
  size_t interfaces_size;
 
 
168
/* global so signal handler can reach it*/
 
 
169
AvahiSimplePoll *simple_poll;
 
 
171
sig_atomic_t quit_now = 0;
 
 
172
int signal_received = 0;
 
 
174
/* Function to use when printing errors */
 
 
175
void perror_plus(const char *print_text){
 
 
177
  fprintf(stderr, "Mandos plugin %s: ",
 
 
178
          program_invocation_short_name);
 
 
183
__attribute__((format (gnu_printf, 2, 3)))
 
 
184
int fprintf_plus(FILE *stream, const char *format, ...){
 
 
186
  va_start (ap, format);
 
 
188
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
 
 
189
                             program_invocation_short_name));
 
 
190
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
 
194
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
 
195
 * bytes. "buffer_capacity" is how much is currently allocated,
 
 
196
 * "buffer_length" is how much is already used.
 
 
198
size_t incbuffer(char **buffer, size_t buffer_length,
 
 
199
                 size_t buffer_capacity){
 
 
200
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
 
201
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
 
205
    buffer_capacity += BUFFER_SIZE;
 
 
207
  return buffer_capacity;
 
 
210
/* Add server to set of servers to retry periodically */
 
 
211
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
 
 
212
                int af, server **current_server){
 
 
214
  server *new_server = malloc(sizeof(server));
 
 
215
  if(new_server == NULL){
 
 
216
    perror_plus("malloc");
 
 
219
  *new_server = (server){ .ip = strdup(ip),
 
 
221
                          .if_index = if_index,
 
 
223
  if(new_server->ip == NULL){
 
 
224
    perror_plus("strdup");
 
 
227
  /* Special case of first server */
 
 
228
  if(*current_server == NULL){
 
 
229
    new_server->next = new_server;
 
 
230
    new_server->prev = new_server;
 
 
231
    *current_server = new_server;
 
 
232
  /* Place the new server last in the list */
 
 
234
    new_server->next = *current_server;
 
 
235
    new_server->prev = (*current_server)->prev;
 
 
236
    new_server->prev->next = new_server;
 
 
237
    (*current_server)->prev = new_server;
 
 
239
  ret = clock_gettime(CLOCK_MONOTONIC, &(*current_server)->last_seen);
 
 
241
    perror_plus("clock_gettime");
 
 
250
static bool init_gpgme(const char *seckey, const char *pubkey,
 
 
251
                       const char *tempdir, mandos_context *mc){
 
 
253
  gpgme_engine_info_t engine_info;
 
 
256
   * Helper function to insert pub and seckey to the engine keyring.
 
 
258
  bool import_key(const char *filename){
 
 
261
    gpgme_data_t pgp_data;
 
 
263
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
 
269
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
 
270
    if(rc != GPG_ERR_NO_ERROR){
 
 
271
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
 
272
                   gpgme_strsource(rc), gpgme_strerror(rc));
 
 
276
    rc = gpgme_op_import(mc->ctx, pgp_data);
 
 
277
    if(rc != GPG_ERR_NO_ERROR){
 
 
278
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
 
 
279
                   gpgme_strsource(rc), gpgme_strerror(rc));
 
 
283
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
 
285
      perror_plus("close");
 
 
287
    gpgme_data_release(pgp_data);
 
 
292
    fprintf_plus(stderr, "Initializing GPGME\n");
 
 
296
  gpgme_check_version(NULL);
 
 
297
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
 
298
  if(rc != GPG_ERR_NO_ERROR){
 
 
299
    fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
 
300
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
304
  /* Set GPGME home directory for the OpenPGP engine only */
 
 
305
  rc = gpgme_get_engine_info(&engine_info);
 
 
306
  if(rc != GPG_ERR_NO_ERROR){
 
 
307
    fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
 
308
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
311
  while(engine_info != NULL){
 
 
312
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
 
 
313
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
 
 
314
                            engine_info->file_name, tempdir);
 
 
317
    engine_info = engine_info->next;
 
 
319
  if(engine_info == NULL){
 
 
320
    fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
 
 
325
  /* Create new GPGME "context" */
 
 
326
  rc = gpgme_new(&(mc->ctx));
 
 
327
  if(rc != GPG_ERR_NO_ERROR){
 
 
328
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
 
329
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
 
 
334
  if(not import_key(pubkey) or not import_key(seckey)){
 
 
342
 * Decrypt OpenPGP data.
 
 
343
 * Returns -1 on error
 
 
345
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
 
349
  gpgme_data_t dh_crypto, dh_plain;
 
 
352
  size_t plaintext_capacity = 0;
 
 
353
  ssize_t plaintext_length = 0;
 
 
356
    fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
 
 
359
  /* Create new GPGME data buffer from memory cryptotext */
 
 
360
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
 
362
  if(rc != GPG_ERR_NO_ERROR){
 
 
363
    fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
 
364
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
368
  /* Create new empty GPGME data buffer for the plaintext */
 
 
369
  rc = gpgme_data_new(&dh_plain);
 
 
370
  if(rc != GPG_ERR_NO_ERROR){
 
 
371
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
 
372
                 "bad gpgme_data_new: %s: %s\n",
 
 
373
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
374
    gpgme_data_release(dh_crypto);
 
 
378
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
 
380
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
 
 
381
  if(rc != GPG_ERR_NO_ERROR){
 
 
382
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
 
383
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
 
384
    plaintext_length = -1;
 
 
386
      gpgme_decrypt_result_t result;
 
 
387
      result = gpgme_op_decrypt_result(mc->ctx);
 
 
389
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
 
 
391
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
 
392
                     result->unsupported_algorithm);
 
 
393
        fprintf_plus(stderr, "Wrong key usage: %u\n",
 
 
394
                     result->wrong_key_usage);
 
 
395
        if(result->file_name != NULL){
 
 
396
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
 
 
398
        gpgme_recipient_t recipient;
 
 
399
        recipient = result->recipients;
 
 
400
        while(recipient != NULL){
 
 
401
          fprintf_plus(stderr, "Public key algorithm: %s\n",
 
 
402
                       gpgme_pubkey_algo_name
 
 
403
                       (recipient->pubkey_algo));
 
 
404
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
 
405
          fprintf_plus(stderr, "Secret key available: %s\n",
 
 
406
                       recipient->status == GPG_ERR_NO_SECKEY
 
 
408
          recipient = recipient->next;
 
 
416
    fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
 
 
419
  /* Seek back to the beginning of the GPGME plaintext data buffer */
 
 
420
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
 
421
    perror_plus("gpgme_data_seek");
 
 
422
    plaintext_length = -1;
 
 
428
    plaintext_capacity = incbuffer(plaintext,
 
 
429
                                   (size_t)plaintext_length,
 
 
431
    if(plaintext_capacity == 0){
 
 
432
      perror_plus("incbuffer");
 
 
433
      plaintext_length = -1;
 
 
437
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
 
439
    /* Print the data, if any */
 
 
445
      perror_plus("gpgme_data_read");
 
 
446
      plaintext_length = -1;
 
 
449
    plaintext_length += ret;
 
 
453
    fprintf_plus(stderr, "Decrypted password is: ");
 
 
454
    for(ssize_t i = 0; i < plaintext_length; i++){
 
 
455
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
 
457
    fprintf(stderr, "\n");
 
 
462
  /* Delete the GPGME cryptotext data buffer */
 
 
463
  gpgme_data_release(dh_crypto);
 
 
465
  /* Delete the GPGME plaintext data buffer */
 
 
466
  gpgme_data_release(dh_plain);
 
 
467
  return plaintext_length;
 
 
470
static const char * safer_gnutls_strerror(int value){
 
 
471
  const char *ret = gnutls_strerror(value);
 
 
477
/* GnuTLS log function callback */
 
 
478
static void debuggnutls(__attribute__((unused)) int level,
 
 
480
  fprintf_plus(stderr, "GnuTLS: %s", string);
 
 
483
static int init_gnutls_global(const char *pubkeyfilename,
 
 
484
                              const char *seckeyfilename,
 
 
489
    fprintf_plus(stderr, "Initializing GnuTLS\n");
 
 
492
  ret = gnutls_global_init();
 
 
493
  if(ret != GNUTLS_E_SUCCESS){
 
 
494
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
 
 
495
                 safer_gnutls_strerror(ret));
 
 
500
    /* "Use a log level over 10 to enable all debugging options."
 
 
503
    gnutls_global_set_log_level(11);
 
 
504
    gnutls_global_set_log_function(debuggnutls);
 
 
507
  /* OpenPGP credentials */
 
 
508
  ret = gnutls_certificate_allocate_credentials(&mc->cred);
 
 
509
  if(ret != GNUTLS_E_SUCCESS){
 
 
510
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
 
 
511
                 safer_gnutls_strerror(ret));
 
 
512
    gnutls_global_deinit();
 
 
517
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
 
 
518
                 " secret key %s as GnuTLS credentials\n",
 
 
523
  ret = gnutls_certificate_set_openpgp_key_file
 
 
524
    (mc->cred, pubkeyfilename, seckeyfilename,
 
 
525
     GNUTLS_OPENPGP_FMT_BASE64);
 
 
526
  if(ret != GNUTLS_E_SUCCESS){
 
 
528
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
 
529
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
 
530
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
 
 
531
                 safer_gnutls_strerror(ret));
 
 
535
  /* GnuTLS server initialization */
 
 
536
  ret = gnutls_dh_params_init(&mc->dh_params);
 
 
537
  if(ret != GNUTLS_E_SUCCESS){
 
 
538
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
 
 
539
                 " initialization: %s\n",
 
 
540
                 safer_gnutls_strerror(ret));
 
 
543
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
 
544
  if(ret != GNUTLS_E_SUCCESS){
 
 
545
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
 
 
546
                 safer_gnutls_strerror(ret));
 
 
550
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
 
556
  gnutls_certificate_free_credentials(mc->cred);
 
 
557
  gnutls_global_deinit();
 
 
558
  gnutls_dh_params_deinit(mc->dh_params);
 
 
562
static int init_gnutls_session(gnutls_session_t *session,
 
 
565
  /* GnuTLS session creation */
 
 
567
    ret = gnutls_init(session, GNUTLS_SERVER);
 
 
571
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
 
572
  if(ret != GNUTLS_E_SUCCESS){
 
 
574
                 "Error in GnuTLS session initialization: %s\n",
 
 
575
                 safer_gnutls_strerror(ret));
 
 
581
      ret = gnutls_priority_set_direct(*session, mc->priority, &err);
 
 
583
        gnutls_deinit(*session);
 
 
586
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
 
587
    if(ret != GNUTLS_E_SUCCESS){
 
 
588
      fprintf_plus(stderr, "Syntax error at: %s\n", err);
 
 
589
      fprintf_plus(stderr, "GnuTLS error: %s\n",
 
 
590
                   safer_gnutls_strerror(ret));
 
 
591
      gnutls_deinit(*session);
 
 
597
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
 
600
      gnutls_deinit(*session);
 
 
603
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
 
604
  if(ret != GNUTLS_E_SUCCESS){
 
 
605
    fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
 
 
606
                 safer_gnutls_strerror(ret));
 
 
607
    gnutls_deinit(*session);
 
 
611
  /* ignore client certificate if any. */
 
 
612
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
 
 
614
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
 
 
619
/* Avahi log function callback */
 
 
620
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
 
621
                      __attribute__((unused)) const char *txt){}
 
 
623
/* Called when a Mandos server is found */
 
 
624
static int start_mandos_communication(const char *ip, in_port_t port,
 
 
625
                                      AvahiIfIndex if_index,
 
 
626
                                      int af, mandos_context *mc){
 
 
627
  int ret, tcp_sd = -1;
 
 
630
    struct sockaddr_in in;
 
 
631
    struct sockaddr_in6 in6;
 
 
634
  char *decrypted_buffer = NULL;
 
 
635
  size_t buffer_length = 0;
 
 
636
  size_t buffer_capacity = 0;
 
 
639
  gnutls_session_t session;
 
 
640
  int pf;                       /* Protocol family */
 
 
657
    fprintf_plus(stderr, "Bad address family: %d\n", af);
 
 
662
  /* If the interface is specified and we have a list of interfaces */
 
 
663
  if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
 
 
664
    /* Check if the interface is one of the interfaces we are using */
 
 
667
      char *interface = NULL;
 
 
668
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
 
670
        if(if_nametoindex(interface) == (unsigned int)if_index){
 
 
677
      /* This interface does not match any in the list, so we don't
 
 
678
         connect to the server */
 
 
680
        char interface[IF_NAMESIZE];
 
 
681
        if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
 
682
          perror_plus("if_indextoname");
 
 
684
          fprintf_plus(stderr, "Skipping server on non-used interface"
 
 
686
                       if_indextoname((unsigned int)if_index,
 
 
694
  ret = init_gnutls_session(&session, mc);
 
 
700
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
 
 
701
                 PRIuMAX "\n", ip, (uintmax_t)port);
 
 
704
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
 
707
    perror_plus("socket");
 
 
717
  memset(&to, 0, sizeof(to));
 
 
719
    to.in6.sin6_family = (sa_family_t)af;
 
 
720
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
 
722
    to.in.sin_family = (sa_family_t)af;
 
 
723
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
 
727
    perror_plus("inet_pton");
 
 
733
    fprintf_plus(stderr, "Bad address: %s\n", ip);
 
 
738
    to.in6.sin6_port = htons(port);    
 
 
739
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
 
 
740
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
 
 
742
      if(if_index == AVAHI_IF_UNSPEC){
 
 
743
        fprintf_plus(stderr, "An IPv6 link-local address is"
 
 
744
                     " incomplete without a network interface\n");
 
 
748
      /* Set the network interface number as scope */
 
 
749
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
 
752
    to.in.sin_port = htons(port); /* Spurious warnings from
 
 
754
                                     -Wunreachable-code */
 
 
763
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
 
764
      char interface[IF_NAMESIZE];
 
 
765
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
 
766
        perror_plus("if_indextoname");
 
 
768
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
 
 
769
                     "\n", ip, interface, (uintmax_t)port);
 
 
772
      fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
 
 
773
                   ip, (uintmax_t)port);
 
 
775
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
 
776
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
 
779
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
 
 
782
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
 
 
786
      perror_plus("inet_ntop");
 
 
788
      if(strcmp(addrstr, ip) != 0){
 
 
789
        fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
 
 
800
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
 
802
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
 
805
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
 
807
      perror_plus("connect");
 
 
818
  const char *out = mandos_protocol_version;
 
 
821
    size_t out_size = strlen(out);
 
 
822
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
 
823
                                        out_size - written));
 
 
826
      perror_plus("write");
 
 
830
    written += (size_t)ret;
 
 
831
    if(written < out_size){
 
 
834
      if(out == mandos_protocol_version){
 
 
849
    fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
 
 
857
  /* This casting via intptr_t is to eliminate warning about casting
 
 
858
     an int to a pointer type.  This is exactly how the GnuTLS Guile
 
 
859
     function "set-session-transport-fd!" does it. */
 
 
860
  gnutls_transport_set_ptr(session,
 
 
861
                           (gnutls_transport_ptr_t)(intptr_t)tcp_sd);
 
 
869
    ret = gnutls_handshake(session);
 
 
874
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
876
  if(ret != GNUTLS_E_SUCCESS){
 
 
878
      fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
 
 
885
  /* Read OpenPGP packet that contains the wanted password */
 
 
888
    fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
 
 
899
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
 
901
    if(buffer_capacity == 0){
 
 
903
      perror_plus("incbuffer");
 
 
913
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
 
920
      case GNUTLS_E_INTERRUPTED:
 
 
923
      case GNUTLS_E_REHANDSHAKE:
 
 
925
          ret = gnutls_handshake(session);
 
 
931
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
933
          fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
 
 
941
        fprintf_plus(stderr, "Unknown error while reading data from"
 
 
942
                     " encrypted session with Mandos server\n");
 
 
943
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
 
948
      buffer_length += (size_t) sret;
 
 
953
    fprintf_plus(stderr, "Closing TLS session\n");
 
 
962
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
 
967
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
 
969
  if(buffer_length > 0){
 
 
970
    ssize_t decrypted_buffer_size;
 
 
971
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
 
 
972
                                               &decrypted_buffer, mc);
 
 
973
    if(decrypted_buffer_size >= 0){
 
 
976
      while(written < (size_t) decrypted_buffer_size){
 
 
982
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
 
983
                          (size_t)decrypted_buffer_size - written,
 
 
985
        if(ret == 0 and ferror(stdout)){
 
 
988
            fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
 
994
        written += (size_t)ret;
 
 
1000
  /* Shutdown procedure */
 
 
1005
    free(decrypted_buffer);
 
 
1008
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
 
1014
      perror_plus("close");
 
 
1016
    gnutls_deinit(session);
 
 
1026
static void resolve_callback(AvahiSServiceResolver *r,
 
 
1027
                             AvahiIfIndex interface,
 
 
1028
                             AvahiProtocol proto,
 
 
1029
                             AvahiResolverEvent event,
 
 
1033
                             const char *host_name,
 
 
1034
                             const AvahiAddress *address,
 
 
1036
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
 
1037
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
1044
  /* Called whenever a service has been resolved successfully or
 
 
1053
  case AVAHI_RESOLVER_FAILURE:
 
 
1054
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
 
 
1055
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
 
 
1057
                 avahi_strerror(avahi_server_errno
 
 
1058
                                (((mandos_context*)mc)->server)));
 
 
1061
  case AVAHI_RESOLVER_FOUND:
 
 
1063
      char ip[AVAHI_ADDRESS_STR_MAX];
 
 
1064
      avahi_address_snprint(ip, sizeof(ip), address);
 
 
1066
        fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
 
1067
                     PRIdMAX ") on port %" PRIu16 "\n", name,
 
 
1068
                     host_name, ip, (intmax_t)interface, port);
 
 
1070
      int ret = start_mandos_communication(ip, (in_port_t)port,
 
 
1072
                                           avahi_proto_to_af(proto),
 
 
1075
        avahi_simple_poll_quit(simple_poll);
 
 
1077
        if(not add_server(ip, (in_port_t)port, interface,
 
 
1078
                          avahi_proto_to_af(proto),
 
 
1079
                          &((mandos_context*)mc)->current_server)){
 
 
1080
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
 
 
1086
  avahi_s_service_resolver_free(r);
 
 
1089
static void browse_callback(AvahiSServiceBrowser *b,
 
 
1090
                            AvahiIfIndex interface,
 
 
1091
                            AvahiProtocol protocol,
 
 
1092
                            AvahiBrowserEvent event,
 
 
1096
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
 
1103
  /* Called whenever a new services becomes available on the LAN or
 
 
1104
     is removed from the LAN */
 
 
1112
  case AVAHI_BROWSER_FAILURE:
 
 
1114
    fprintf_plus(stderr, "(Avahi browser) %s\n",
 
 
1115
                 avahi_strerror(avahi_server_errno
 
 
1116
                                (((mandos_context*)mc)->server)));
 
 
1117
    avahi_simple_poll_quit(simple_poll);
 
 
1120
  case AVAHI_BROWSER_NEW:
 
 
1121
    /* We ignore the returned Avahi resolver object. In the callback
 
 
1122
       function we free it. If the Avahi server is terminated before
 
 
1123
       the callback function is called the Avahi server will free the
 
 
1126
    if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
 
 
1127
                                    interface, protocol, name, type,
 
 
1128
                                    domain, protocol, 0,
 
 
1129
                                    resolve_callback, mc) == NULL)
 
 
1130
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
 
 
1132
                   avahi_strerror(avahi_server_errno
 
 
1133
                                  (((mandos_context*)mc)->server)));
 
 
1136
  case AVAHI_BROWSER_REMOVE:
 
 
1139
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
 
1140
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
 
1142
      fprintf_plus(stderr, "No Mandos server found, still"
 
 
1149
/* Signal handler that stops main loop after SIGTERM */
 
 
1150
static void handle_sigterm(int sig){
 
 
1155
  signal_received = sig;
 
 
1156
  int old_errno = errno;
 
 
1157
  /* set main loop to exit */
 
 
1158
  if(simple_poll != NULL){
 
 
1159
    avahi_simple_poll_quit(simple_poll);
 
 
1164
bool get_flags(const char *ifname, struct ifreq *ifr){
 
 
1168
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
 
1171
    perror_plus("socket");
 
 
1175
  strcpy(ifr->ifr_name, ifname);
 
 
1176
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
 
 
1180
      perror_plus("ioctl SIOCGIFFLAGS");
 
 
1188
bool good_flags(const char *ifname, const struct ifreq *ifr){
 
 
1190
  /* Reject the loopback device */
 
 
1191
  if(ifr->ifr_flags & IFF_LOOPBACK){
 
 
1193
      fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
 
 
1198
  /* Accept point-to-point devices only if connect_to is specified */
 
 
1199
  if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
 
 
1201
      fprintf_plus(stderr, "Accepting point-to-point interface"
 
 
1202
                   " \"%s\"\n", ifname);
 
 
1206
  /* Otherwise, reject non-broadcast-capable devices */
 
 
1207
  if(not (ifr->ifr_flags & IFF_BROADCAST)){
 
 
1209
      fprintf_plus(stderr, "Rejecting non-broadcast interface"
 
 
1210
                   " \"%s\"\n", ifname);
 
 
1214
  /* Reject non-ARP interfaces (including dummy interfaces) */
 
 
1215
  if(ifr->ifr_flags & IFF_NOARP){
 
 
1217
      fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
 
 
1223
  /* Accept this device */
 
 
1225
    fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
 
 
1231
 * This function determines if a directory entry in /sys/class/net
 
 
1232
 * corresponds to an acceptable network device.
 
 
1233
 * (This function is passed to scandir(3) as a filter function.)
 
 
1235
int good_interface(const struct dirent *if_entry){
 
 
1236
  if(if_entry->d_name[0] == '.'){
 
 
1241
  if(not get_flags(if_entry->d_name, &ifr)){
 
 
1243
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
1244
                   "\"%s\"\n", if_entry->d_name);
 
 
1249
  if(not good_flags(if_entry->d_name, &ifr)){
 
 
1256
 * This function determines if a network interface is up.
 
 
1258
bool interface_is_up(const char *interface){
 
 
1260
  if(not get_flags(interface, &ifr)){
 
 
1262
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
1263
                   "\"%s\"\n", interface);
 
 
1268
  return (bool)(ifr.ifr_flags & IFF_UP);
 
 
1272
 * This function determines if a network interface is running
 
 
1274
bool interface_is_running(const char *interface){
 
 
1276
  if(not get_flags(interface, &ifr)){
 
 
1278
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
1279
                   "\"%s\"\n", interface);
 
 
1284
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
 
 
1287
int notdotentries(const struct dirent *direntry){
 
 
1288
  /* Skip "." and ".." */
 
 
1289
  if(direntry->d_name[0] == '.'
 
 
1290
     and (direntry->d_name[1] == '\0'
 
 
1291
          or (direntry->d_name[1] == '.'
 
 
1292
              and direntry->d_name[2] == '\0'))){
 
 
1298
/* Is this directory entry a runnable program? */
 
 
1299
int runnable_hook(const struct dirent *direntry){
 
 
1304
  if((direntry->d_name)[0] == '\0'){
 
 
1309
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
 
1310
                "abcdefghijklmnopqrstuvwxyz"
 
 
1313
  if((direntry->d_name)[sret] != '\0'){
 
 
1314
    /* Contains non-allowed characters */
 
 
1316
      fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
 
 
1322
  char *fullname = NULL;
 
 
1323
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
 
 
1325
    perror_plus("asprintf");
 
 
1329
  ret = stat(fullname, &st);
 
 
1332
      perror_plus("Could not stat hook");
 
 
1336
  if(not (S_ISREG(st.st_mode))){
 
 
1337
    /* Not a regular file */
 
 
1339
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
 
 
1344
  if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
 
 
1345
    /* Not executable */
 
 
1347
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
 
 
1353
    fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
 
 
1359
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
 
 
1360
                            mandos_context *mc){
 
 
1362
  struct timespec now;
 
 
1363
  struct timespec waited_time;
 
 
1364
  intmax_t block_time;
 
 
1367
    if(mc->current_server == NULL){
 
 
1369
        fprintf_plus(stderr, "Wait until first server is found."
 
 
1372
      ret = avahi_simple_poll_iterate(s, -1);
 
 
1375
        fprintf_plus(stderr, "Check current_server if we should run"
 
 
1378
      /* the current time */
 
 
1379
      ret = clock_gettime(CLOCK_MONOTONIC, &now);
 
 
1381
        perror_plus("clock_gettime");
 
 
1384
      /* Calculating in ms how long time between now and server
 
 
1385
         who we visted longest time ago. Now - last seen.  */
 
 
1386
      waited_time.tv_sec = (now.tv_sec
 
 
1387
                            - mc->current_server->last_seen.tv_sec);
 
 
1388
      waited_time.tv_nsec = (now.tv_nsec
 
 
1389
                             - mc->current_server->last_seen.tv_nsec);
 
 
1390
      /* total time is 10s/10,000ms.
 
 
1391
         Converting to s from ms by dividing by 1,000,
 
 
1392
         and ns to ms by dividing by 1,000,000. */
 
 
1393
      block_time = ((retry_interval
 
 
1394
                     - ((intmax_t)waited_time.tv_sec * 1000))
 
 
1395
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
 
 
1398
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
 
 
1402
      if(block_time <= 0){
 
 
1403
        ret = start_mandos_communication(mc->current_server->ip,
 
 
1404
                                         mc->current_server->port,
 
 
1405
                                         mc->current_server->if_index,
 
 
1406
                                         mc->current_server->af, mc);
 
 
1408
          avahi_simple_poll_quit(s);
 
 
1411
        ret = clock_gettime(CLOCK_MONOTONIC,
 
 
1412
                            &mc->current_server->last_seen);
 
 
1414
          perror_plus("clock_gettime");
 
 
1417
        mc->current_server = mc->current_server->next;
 
 
1418
        block_time = 0;         /* Call avahi to find new Mandos
 
 
1419
                                   servers, but don't block */
 
 
1422
      ret = avahi_simple_poll_iterate(s, (int)block_time);
 
 
1425
      if (ret > 0 or errno != EINTR){
 
 
1426
        return (ret != 1) ? ret : 0;
 
 
1432
/* Set effective uid to 0, return errno */
 
 
1433
error_t raise_privileges(void){
 
 
1434
  error_t old_errno = errno;
 
 
1435
  error_t ret_errno = 0;
 
 
1436
  if(seteuid(0) == -1){
 
 
1438
    perror_plus("seteuid");
 
 
1444
/* Set effective and real user ID to 0.  Return errno. */
 
 
1445
error_t raise_privileges_permanently(void){
 
 
1446
  error_t old_errno = errno;
 
 
1447
  error_t ret_errno = raise_privileges();
 
 
1452
  if(setuid(0) == -1){
 
 
1454
    perror_plus("seteuid");
 
 
1460
/* Set effective user ID to unprivileged saved user ID */
 
 
1461
error_t lower_privileges(void){
 
 
1462
  error_t old_errno = errno;
 
 
1463
  error_t ret_errno = 0;
 
 
1464
  if(seteuid(uid) == -1){
 
 
1466
    perror_plus("seteuid");
 
 
1472
/* Lower privileges permanently */
 
 
1473
error_t lower_privileges_permanently(void){
 
 
1474
  error_t old_errno = errno;
 
 
1475
  error_t ret_errno = 0;
 
 
1476
  if(setuid(uid) == -1){
 
 
1478
    perror_plus("setuid");
 
 
1484
bool run_network_hooks(const char *mode, const char *interface,
 
 
1486
  struct dirent **direntries;
 
 
1487
  struct dirent *direntry;
 
 
1489
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
 
1492
    if(errno == ENOENT){
 
 
1494
        fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
 
1495
                     " found\n", hookdir);
 
 
1498
      perror_plus("scandir");
 
 
1501
    int devnull = open("/dev/null", O_RDONLY);
 
 
1502
    for(int i = 0; i < numhooks; i++){
 
 
1503
      direntry = direntries[i];
 
 
1504
      char *fullname = NULL;
 
 
1505
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
 
 
1507
        perror_plus("asprintf");
 
 
1511
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
 
1514
      pid_t hook_pid = fork();
 
 
1517
        /* Raise privileges */
 
 
1518
        raise_privileges_permanently();
 
 
1523
          perror_plus("setgid");
 
 
1525
        /* Reset supplementary groups */
 
 
1527
        ret = setgroups(0, NULL);
 
 
1529
          perror_plus("setgroups");
 
 
1531
        dup2(devnull, STDIN_FILENO);
 
 
1533
        dup2(STDERR_FILENO, STDOUT_FILENO);
 
 
1534
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
 
1536
          perror_plus("setenv");
 
 
1539
        ret = setenv("DEVICE", interface, 1);
 
 
1541
          perror_plus("setenv");
 
 
1544
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
 
1546
          perror_plus("setenv");
 
 
1549
        ret = setenv("MODE", mode, 1);
 
 
1551
          perror_plus("setenv");
 
 
1555
        ret = asprintf(&delaystring, "%f", delay);
 
 
1557
          perror_plus("asprintf");
 
 
1560
        ret = setenv("DELAY", delaystring, 1);
 
 
1563
          perror_plus("setenv");
 
 
1567
        if(connect_to != NULL){
 
 
1568
          ret = setenv("CONNECT", connect_to, 1);
 
 
1570
            perror_plus("setenv");
 
 
1574
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
 
 
1575
          perror_plus("execl");
 
 
1576
          _exit(EXIT_FAILURE);
 
 
1580
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
 
1581
          perror_plus("waitpid");
 
 
1585
        if(WIFEXITED(status)){
 
 
1586
          if(WEXITSTATUS(status) != 0){
 
 
1587
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
 
1588
                         " with status %d\n", direntry->d_name,
 
 
1589
                         WEXITSTATUS(status));
 
 
1593
        } else if(WIFSIGNALED(status)){
 
 
1594
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
 
1595
                       " signal %d\n", direntry->d_name,
 
 
1600
          fprintf_plus(stderr, "Warning: network hook \"%s\""
 
 
1601
                       " crashed\n", direntry->d_name);
 
 
1608
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
 
1617
error_t bring_up_interface(const char *const interface,
 
 
1620
  error_t old_errno = errno;
 
 
1621
  error_t ret_errno = 0;
 
 
1622
  int ret, ret_setflags;
 
 
1623
  struct ifreq network;
 
 
1624
  unsigned int if_index = if_nametoindex(interface);
 
 
1626
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
 
1636
  if(not interface_is_up(interface)){
 
 
1637
    if(not get_flags(interface, &network) and debug){
 
 
1639
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
1640
                   "\"%s\"\n", interface);
 
 
1643
    network.ifr_flags |= IFF_UP;
 
 
1645
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
 
1648
      perror_plus("socket");
 
 
1660
      fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
 
 
1664
    /* Raise priviliges */
 
 
1668
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
 
1669
       messages about the network interface to mess up the prompt */
 
 
1670
    int ret_linux = klogctl(8, NULL, 5);
 
 
1671
    bool restore_loglevel = true;
 
 
1672
    if(ret_linux == -1){
 
 
1673
      restore_loglevel = false;
 
 
1674
      perror_plus("klogctl");
 
 
1676
#endif  /* __linux__ */
 
 
1677
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
 
1680
    if(restore_loglevel){
 
 
1681
      ret_linux = klogctl(7, NULL, 0);
 
 
1682
      if(ret_linux == -1){
 
 
1683
        perror_plus("klogctl");
 
 
1686
#endif  /* __linux__ */
 
 
1688
    /* Lower privileges */
 
 
1691
    /* Close the socket */
 
 
1692
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
 
1694
      perror_plus("close");
 
 
1697
    if(ret_setflags == -1){
 
 
1699
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
 
 
1704
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
 
 
1708
  /* Sleep checking until interface is running.
 
 
1709
     Check every 0.25s, up to total time of delay */
 
 
1710
  for(int i=0; i < delay * 4; i++){
 
 
1711
    if(interface_is_running(interface)){
 
 
1714
    struct timespec sleeptime = { .tv_nsec = 250000000 };
 
 
1715
    ret = nanosleep(&sleeptime, NULL);
 
 
1716
    if(ret == -1 and errno != EINTR){
 
 
1717
      perror_plus("nanosleep");
 
 
1725
error_t take_down_interface(const char *const interface){
 
 
1727
  error_t old_errno = errno;
 
 
1728
  error_t ret_errno = 0;
 
 
1729
  int ret, ret_setflags;
 
 
1730
  struct ifreq network;
 
 
1731
  unsigned int if_index = if_nametoindex(interface);
 
 
1733
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
 
1737
  if(interface_is_up(interface)){
 
 
1738
    if(not get_flags(interface, &network) and debug){
 
 
1740
      fprintf_plus(stderr, "Failed to get flags for interface "
 
 
1741
                   "\"%s\"\n", interface);
 
 
1744
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
 
 
1746
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
 
1749
      perror_plus("socket");
 
 
1755
      fprintf_plus(stderr, "Taking down interface \"%s\"\n",
 
 
1759
    /* Raise priviliges */
 
 
1762
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
 
1765
    /* Lower privileges */
 
 
1768
    /* Close the socket */
 
 
1769
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
 
1771
      perror_plus("close");
 
 
1774
    if(ret_setflags == -1){
 
 
1776
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
 
 
1781
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
 
 
1789
int main(int argc, char *argv[]){
 
 
1790
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
 
 
1791
                        .priority = "SECURE256:!CTYPE-X.509:"
 
 
1792
                        "+CTYPE-OPENPGP", .current_server = NULL, 
 
 
1793
                        .interfaces = NULL, .interfaces_size = 0 };
 
 
1794
  AvahiSServiceBrowser *sb = NULL;
 
 
1799
  int exitcode = EXIT_SUCCESS;
 
 
1800
  char *interfaces_to_take_down = NULL;
 
 
1801
  size_t interfaces_to_take_down_size = 0;
 
 
1802
  char tempdir[] = "/tmp/mandosXXXXXX";
 
 
1803
  bool tempdir_created = false;
 
 
1804
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
 
1805
  const char *seckey = PATHDIR "/" SECKEY;
 
 
1806
  const char *pubkey = PATHDIR "/" PUBKEY;
 
 
1807
  char *interfaces_hooks = NULL;
 
 
1808
  size_t interfaces_hooks_size = 0;
 
 
1810
  bool gnutls_initialized = false;
 
 
1811
  bool gpgme_initialized = false;
 
 
1813
  double retry_interval = 10; /* 10s between trying a server and
 
 
1814
                                 retrying the same server again */
 
 
1816
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
 
 
1817
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
 
1822
  /* Lower any group privileges we might have, just to be safe */
 
 
1826
    perror_plus("setgid");
 
 
1829
  /* Lower user privileges (temporarily) */
 
 
1833
    perror_plus("seteuid");
 
 
1841
    struct argp_option options[] = {
 
 
1842
      { .name = "debug", .key = 128,
 
 
1843
        .doc = "Debug mode", .group = 3 },
 
 
1844
      { .name = "connect", .key = 'c',
 
 
1845
        .arg = "ADDRESS:PORT",
 
 
1846
        .doc = "Connect directly to a specific Mandos server",
 
 
1848
      { .name = "interface", .key = 'i',
 
 
1850
        .doc = "Network interface that will be used to search for"
 
 
1853
      { .name = "seckey", .key = 's',
 
 
1855
        .doc = "OpenPGP secret key file base name",
 
 
1857
      { .name = "pubkey", .key = 'p',
 
 
1859
        .doc = "OpenPGP public key file base name",
 
 
1861
      { .name = "dh-bits", .key = 129,
 
 
1863
        .doc = "Bit length of the prime number used in the"
 
 
1864
        " Diffie-Hellman key exchange",
 
 
1866
      { .name = "priority", .key = 130,
 
 
1868
        .doc = "GnuTLS priority string for the TLS handshake",
 
 
1870
      { .name = "delay", .key = 131,
 
 
1872
        .doc = "Maximum delay to wait for interface startup",
 
 
1874
      { .name = "retry", .key = 132,
 
 
1876
        .doc = "Retry interval used when denied by the Mandos server",
 
 
1878
      { .name = "network-hook-dir", .key = 133,
 
 
1880
        .doc = "Directory where network hooks are located",
 
 
1883
       * These reproduce what we would get without ARGP_NO_HELP
 
 
1885
      { .name = "help", .key = '?',
 
 
1886
        .doc = "Give this help list", .group = -1 },
 
 
1887
      { .name = "usage", .key = -3,
 
 
1888
        .doc = "Give a short usage message", .group = -1 },
 
 
1889
      { .name = "version", .key = 'V',
 
 
1890
        .doc = "Print program version", .group = -1 },
 
 
1894
    error_t parse_opt(int key, char *arg,
 
 
1895
                      struct argp_state *state){
 
 
1898
      case 128:                 /* --debug */
 
 
1901
      case 'c':                 /* --connect */
 
 
1904
      case 'i':                 /* --interface */
 
 
1905
        ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
 
 
1908
          argp_error(state, "%s", strerror(ret_errno));
 
 
1911
      case 's':                 /* --seckey */
 
 
1914
      case 'p':                 /* --pubkey */
 
 
1917
      case 129:                 /* --dh-bits */
 
 
1919
        tmpmax = strtoimax(arg, &tmp, 10);
 
 
1920
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
 
1921
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
 
1922
          argp_error(state, "Bad number of DH bits");
 
 
1924
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
 
1926
      case 130:                 /* --priority */
 
 
1929
      case 131:                 /* --delay */
 
 
1931
        delay = strtof(arg, &tmp);
 
 
1932
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
 
1933
          argp_error(state, "Bad delay");
 
 
1935
      case 132:                 /* --retry */
 
 
1937
        retry_interval = strtod(arg, &tmp);
 
 
1938
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
 
1939
           or (retry_interval * 1000) > INT_MAX
 
 
1940
           or retry_interval < 0){
 
 
1941
          argp_error(state, "Bad retry interval");
 
 
1944
      case 133:                 /* --network-hook-dir */
 
 
1948
         * These reproduce what we would get without ARGP_NO_HELP
 
 
1950
      case '?':                 /* --help */
 
 
1951
        argp_state_help(state, state->out_stream,
 
 
1952
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
 
1953
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
 
1954
      case -3:                  /* --usage */
 
 
1955
        argp_state_help(state, state->out_stream,
 
 
1956
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
 
1957
      case 'V':                 /* --version */
 
 
1958
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
 
 
1959
        exit(argp_err_exit_status);
 
 
1962
        return ARGP_ERR_UNKNOWN;
 
 
1967
    struct argp argp = { .options = options, .parser = parse_opt,
 
 
1969
                         .doc = "Mandos client -- Get and decrypt"
 
 
1970
                         " passwords from a Mandos server" };
 
 
1971
    ret = argp_parse(&argp, argc, argv,
 
 
1972
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
 
1979
      perror_plus("argp_parse");
 
 
1980
      exitcode = EX_OSERR;
 
 
1983
      exitcode = EX_USAGE;
 
 
1989
    /* Work around Debian bug #633582:
 
 
1990
       <http://bugs.debian.org/633582> */
 
 
1992
    /* Re-raise priviliges */
 
 
1993
    if(raise_privileges() == 0){
 
 
1996
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
 
 
1997
        int seckey_fd = open(seckey, O_RDONLY);
 
 
1998
        if(seckey_fd == -1){
 
 
1999
          perror_plus("open");
 
 
2001
          ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
 
 
2003
            perror_plus("fstat");
 
 
2005
            if(S_ISREG(st.st_mode)
 
 
2006
               and st.st_uid == 0 and st.st_gid == 0){
 
 
2007
              ret = fchown(seckey_fd, uid, gid);
 
 
2009
                perror_plus("fchown");
 
 
2013
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
 
2017
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
 
 
2018
        int pubkey_fd = open(pubkey, O_RDONLY);
 
 
2019
        if(pubkey_fd == -1){
 
 
2020
          perror_plus("open");
 
 
2022
          ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
 
 
2024
            perror_plus("fstat");
 
 
2026
            if(S_ISREG(st.st_mode)
 
 
2027
               and st.st_uid == 0 and st.st_gid == 0){
 
 
2028
              ret = fchown(pubkey_fd, uid, gid);
 
 
2030
                perror_plus("fchown");
 
 
2034
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
 
2038
      /* Lower privileges */
 
 
2043
  /* Remove invalid interface names (except "none") */
 
 
2045
    char *interface = NULL;
 
 
2046
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
 
2048
      if(strcmp(interface, "none") != 0
 
 
2049
         and if_nametoindex(interface) == 0){
 
 
2050
        if(interface[0] != '\0'){
 
 
2051
          fprintf_plus(stderr, "Not using nonexisting interface"
 
 
2052
                       " \"%s\"\n", interface);
 
 
2054
        argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
 
 
2060
  /* Run network hooks */
 
 
2062
    if(mc.interfaces != NULL){
 
 
2063
      interfaces_hooks = malloc(mc.interfaces_size);
 
 
2064
      if(interfaces_hooks == NULL){
 
 
2065
        perror_plus("malloc");
 
 
2068
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
 
 
2069
      interfaces_hooks_size = mc.interfaces_size;
 
 
2070
      argz_stringify(interfaces_hooks, interfaces_hooks_size,
 
 
2073
    if(not run_network_hooks("start", interfaces_hooks != NULL ?
 
 
2074
                             interfaces_hooks : "", delay)){
 
 
2080
    avahi_set_log_function(empty_log);
 
 
2083
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
 
2084
     from the signal handler */
 
 
2085
  /* Initialize the pseudo-RNG for Avahi */
 
 
2086
  srand((unsigned int) time(NULL));
 
 
2087
  simple_poll = avahi_simple_poll_new();
 
 
2088
  if(simple_poll == NULL){
 
 
2089
    fprintf_plus(stderr,
 
 
2090
                 "Avahi: Failed to create simple poll object.\n");
 
 
2091
    exitcode = EX_UNAVAILABLE;
 
 
2095
  sigemptyset(&sigterm_action.sa_mask);
 
 
2096
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
 
2098
    perror_plus("sigaddset");
 
 
2099
    exitcode = EX_OSERR;
 
 
2102
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
 
2104
    perror_plus("sigaddset");
 
 
2105
    exitcode = EX_OSERR;
 
 
2108
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
 
2110
    perror_plus("sigaddset");
 
 
2111
    exitcode = EX_OSERR;
 
 
2114
  /* Need to check if the handler is SIG_IGN before handling:
 
 
2115
     | [[info:libc:Initial Signal Actions]] |
 
 
2116
     | [[info:libc:Basic Signal Handling]]  |
 
 
2118
  ret = sigaction(SIGINT, NULL, &old_sigterm_action);
 
 
2120
    perror_plus("sigaction");
 
 
2123
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
 
2124
    ret = sigaction(SIGINT, &sigterm_action, NULL);
 
 
2126
      perror_plus("sigaction");
 
 
2127
      exitcode = EX_OSERR;
 
 
2131
  ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
 
 
2133
    perror_plus("sigaction");
 
 
2136
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
 
2137
    ret = sigaction(SIGHUP, &sigterm_action, NULL);
 
 
2139
      perror_plus("sigaction");
 
 
2140
      exitcode = EX_OSERR;
 
 
2144
  ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
 
 
2146
    perror_plus("sigaction");
 
 
2149
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
 
2150
    ret = sigaction(SIGTERM, &sigterm_action, NULL);
 
 
2152
      perror_plus("sigaction");
 
 
2153
      exitcode = EX_OSERR;
 
 
2158
  /* If no interfaces were specified, make a list */
 
 
2159
  if(mc.interfaces == NULL){
 
 
2160
    struct dirent **direntries;
 
 
2161
    /* Look for any good interfaces */
 
 
2162
    ret = scandir(sys_class_net, &direntries, good_interface,
 
 
2165
      /* Add all found interfaces to interfaces list */
 
 
2166
      for(int i = 0; i < ret; ++i){
 
 
2167
        ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
 
 
2168
                             direntries[i]->d_name);
 
 
2170
          perror_plus("argz_add");
 
 
2174
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
 
 
2175
                       direntries[i]->d_name);
 
 
2181
      fprintf_plus(stderr, "Could not find a network interface\n");
 
 
2182
      exitcode = EXIT_FAILURE;
 
 
2187
  /* Bring up interfaces which are down, and remove any "none"s */
 
 
2189
    char *interface = NULL;
 
 
2190
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
 
2192
      /* If interface name is "none", stop bringing up interfaces.
 
 
2193
         Also remove all instances of "none" from the list */
 
 
2194
      if(strcmp(interface, "none") == 0){
 
 
2195
        argz_delete(&mc.interfaces, &mc.interfaces_size,
 
 
2198
        while((interface = argz_next(mc.interfaces,
 
 
2199
                                     mc.interfaces_size, interface))){
 
 
2200
          if(strcmp(interface, "none") == 0){
 
 
2201
            argz_delete(&mc.interfaces, &mc.interfaces_size,
 
 
2208
      bool interface_was_up = interface_is_up(interface);
 
 
2209
      ret = bring_up_interface(interface, delay);
 
 
2210
      if(not interface_was_up){
 
 
2213
          perror_plus("Failed to bring up interface");
 
 
2215
          ret_errno = argz_add(&interfaces_to_take_down,
 
 
2216
                               &interfaces_to_take_down_size,
 
 
2221
    if(debug and (interfaces_to_take_down == NULL)){
 
 
2222
      fprintf_plus(stderr, "No interfaces were brought up\n");
 
 
2226
  /* If we only got one interface, explicitly use only that one */
 
 
2227
  if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
 
 
2229
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
 
 
2232
    if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
 
 
2239
  ret = init_gnutls_global(pubkey, seckey, &mc);
 
 
2241
    fprintf_plus(stderr, "init_gnutls_global failed\n");
 
 
2242
    exitcode = EX_UNAVAILABLE;
 
 
2245
    gnutls_initialized = true;
 
 
2252
  if(mkdtemp(tempdir) == NULL){
 
 
2253
    perror_plus("mkdtemp");
 
 
2256
  tempdir_created = true;
 
 
2262
  if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
 
 
2263
    fprintf_plus(stderr, "init_gpgme failed\n");
 
 
2264
    exitcode = EX_UNAVAILABLE;
 
 
2267
    gpgme_initialized = true;
 
 
2274
  if(connect_to != NULL){
 
 
2275
    /* Connect directly, do not use Zeroconf */
 
 
2276
    /* (Mainly meant for debugging) */
 
 
2277
    char *address = strrchr(connect_to, ':');
 
 
2279
    if(address == NULL){
 
 
2280
      fprintf_plus(stderr, "No colon in address\n");
 
 
2281
      exitcode = EX_USAGE;
 
 
2291
    tmpmax = strtoimax(address+1, &tmp, 10);
 
 
2292
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
 
2293
       or tmpmax != (in_port_t)tmpmax){
 
 
2294
      fprintf_plus(stderr, "Bad port number\n");
 
 
2295
      exitcode = EX_USAGE;
 
 
2303
    port = (in_port_t)tmpmax;
 
 
2305
    /* Colon in address indicates IPv6 */
 
 
2307
    if(strchr(connect_to, ':') != NULL){
 
 
2309
      /* Accept [] around IPv6 address - see RFC 5952 */
 
 
2310
      if(connect_to[0] == '[' and address[-1] == ']')
 
 
2318
    address = connect_to;
 
 
2324
    while(not quit_now){
 
 
2325
      ret = start_mandos_communication(address, port, if_index, af,
 
 
2327
      if(quit_now or ret == 0){
 
 
2331
        fprintf_plus(stderr, "Retrying in %d seconds\n",
 
 
2332
                     (int)retry_interval);
 
 
2334
      sleep((unsigned int)retry_interval);
 
 
2338
      exitcode = EXIT_SUCCESS;
 
 
2349
    AvahiServerConfig config;
 
 
2350
    /* Do not publish any local Zeroconf records */
 
 
2351
    avahi_server_config_init(&config);
 
 
2352
    config.publish_hinfo = 0;
 
 
2353
    config.publish_addresses = 0;
 
 
2354
    config.publish_workstation = 0;
 
 
2355
    config.publish_domain = 0;
 
 
2357
    /* Allocate a new server */
 
 
2358
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
 
2359
                                 &config, NULL, NULL, &ret_errno);
 
 
2361
    /* Free the Avahi configuration data */
 
 
2362
    avahi_server_config_free(&config);
 
 
2365
  /* Check if creating the Avahi server object succeeded */
 
 
2366
  if(mc.server == NULL){
 
 
2367
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
 
 
2368
                 avahi_strerror(ret_errno));
 
 
2369
    exitcode = EX_UNAVAILABLE;
 
 
2377
  /* Create the Avahi service browser */
 
 
2378
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
 
2379
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
 
2380
                                   NULL, 0, browse_callback,
 
 
2383
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
 
 
2384
                 avahi_strerror(avahi_server_errno(mc.server)));
 
 
2385
    exitcode = EX_UNAVAILABLE;
 
 
2393
  /* Run the main loop */
 
 
2396
    fprintf_plus(stderr, "Starting Avahi loop search\n");
 
 
2399
  ret = avahi_loop_with_timeout(simple_poll,
 
 
2400
                                (int)(retry_interval * 1000), &mc);
 
 
2402
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
 
 
2403
                 (ret == 0) ? "successfully" : "with error");
 
 
2409
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
 
2412
  /* Cleanup things */
 
 
2413
  free(mc.interfaces);
 
 
2416
    avahi_s_service_browser_free(sb);
 
 
2418
  if(mc.server != NULL)
 
 
2419
    avahi_server_free(mc.server);
 
 
2421
  if(simple_poll != NULL)
 
 
2422
    avahi_simple_poll_free(simple_poll);
 
 
2424
  if(gnutls_initialized){
 
 
2425
    gnutls_certificate_free_credentials(mc.cred);
 
 
2426
    gnutls_global_deinit();
 
 
2427
    gnutls_dh_params_deinit(mc.dh_params);
 
 
2430
  if(gpgme_initialized){
 
 
2431
    gpgme_release(mc.ctx);
 
 
2434
  /* Cleans up the circular linked list of Mandos servers the client
 
 
2436
  if(mc.current_server != NULL){
 
 
2437
    mc.current_server->prev->next = NULL;
 
 
2438
    while(mc.current_server != NULL){
 
 
2439
      server *next = mc.current_server->next;
 
 
2440
      free(mc.current_server);
 
 
2441
      mc.current_server = next;
 
 
2445
  /* Re-raise priviliges */
 
 
2449
    /* Run network hooks */
 
 
2450
    run_network_hooks("stop", interfaces_hooks != NULL ?
 
 
2451
                      interfaces_hooks : "", delay);
 
 
2453
    /* Take down the network interfaces which were brought up */
 
 
2455
      char *interface = NULL;
 
 
2456
      while((interface=argz_next(interfaces_to_take_down,
 
 
2457
                                 interfaces_to_take_down_size,
 
 
2459
        ret_errno = take_down_interface(interface);
 
 
2462
          perror_plus("Failed to take down interface");
 
 
2465
      if(debug and (interfaces_to_take_down == NULL)){
 
 
2466
        fprintf_plus(stderr, "No interfaces needed to be taken"
 
 
2471
    lower_privileges_permanently();
 
 
2474
  free(interfaces_to_take_down);
 
 
2475
  free(interfaces_hooks);
 
 
2477
  /* Removes the GPGME temp directory and all files inside */
 
 
2478
  if(tempdir_created){
 
 
2479
    struct dirent **direntries = NULL;
 
 
2480
    struct dirent *direntry = NULL;
 
 
2481
    int numentries = scandir(tempdir, &direntries, notdotentries,
 
 
2483
    if (numentries > 0){
 
 
2484
      for(int i = 0; i < numentries; i++){
 
 
2485
        direntry = direntries[i];
 
 
2486
        char *fullname = NULL;
 
 
2487
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
 
2490
          perror_plus("asprintf");
 
 
2493
        ret = remove(fullname);
 
 
2495
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
 
 
2502
    /* need to clean even if 0 because man page doesn't specify */
 
 
2504
    if (numentries == -1){
 
 
2505
      perror_plus("scandir");
 
 
2507
    ret = rmdir(tempdir);
 
 
2508
    if(ret == -1 and errno != ENOENT){
 
 
2509
      perror_plus("rmdir");
 
 
2514
    sigemptyset(&old_sigterm_action.sa_mask);
 
 
2515
    old_sigterm_action.sa_handler = SIG_DFL;
 
 
2516
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
 
2517
                                            &old_sigterm_action,
 
 
2520
      perror_plus("sigaction");
 
 
2523
      ret = raise(signal_received);
 
 
2524
    } while(ret != 0 and errno == EINTR);
 
 
2526
      perror_plus("raise");
 
 
2529
    TEMP_FAILURE_RETRY(pause());