/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

  • Committer: Teddy Hogeborn
  • Date: 2015-03-10 18:03:38 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150310180338-pcxw6r2qmw9k6br9
Add ":!RSA" to GnuTLS priority string, to disallow non-DHE kx.

If Mandos was somehow made to use a non-ephemeral Diffie-Hellman key
exchange algorithm in the TLS handshake, any saved network traffic
could then be decrypted later if the Mandos client key was obtained.
By default, Mandos uses ephemeral DH key exchanges which does not have
this problem, but a non-ephemeral key exchange algorithm was still
enabled by default.  The simplest solution is to simply turn that off,
which ensures that Mandos will always use ephemeral DH key exchanges.

There is a "PFS" priority string specifier, but we can't use it because:

1. Security-wise, it is a mix between "NORMAL" and "SECURE128" - it
   enables a lot more algorithms than "SECURE256".

2. It is only available since GnuTLS 3.2.4.

Thanks to Andreas Fischer <af@bantuX.org> for reporting this issue.

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008-2012 Teddy Hogeborn
13
 
 * Copyright © 2008-2012 Björn Påhlsson
 
12
 * Copyright © 2008-2014 Teddy Hogeborn
 
13
 * Copyright © 2008-2014 Björn Påhlsson
14
14
 * 
15
15
 * This program is free software: you can redistribute it and/or
16
16
 * modify it under the terms of the GNU General Public License as
32
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
33
#ifndef _LARGEFILE_SOURCE
34
34
#define _LARGEFILE_SOURCE
35
 
#endif
 
35
#endif  /* not _LARGEFILE_SOURCE */
36
36
#ifndef _FILE_OFFSET_BITS
37
37
#define _FILE_OFFSET_BITS 64
38
 
#endif
 
38
#endif  /* not _FILE_OFFSET_BITS */
39
39
 
40
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
41
41
 
42
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
43
 
                                   stdout, ferror(), remove() */
 
43
                                   stdout, ferror() */
44
44
#include <stdint.h>             /* uint16_t, uint32_t, intptr_t */
45
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
46
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
55
55
                                   opendir(), DIR */
56
56
#include <sys/stat.h>           /* open(), S_ISREG */
57
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
 
                                   inet_pton(), connect() */
59
 
#include <fcntl.h>              /* open() */
 
58
                                   inet_pton(), connect(),
 
59
                                   getnameinfo() */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
60
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
61
62
                                 */
62
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
63
64
                                   strtoimax() */
64
 
#include <assert.h>             /* assert() */
65
65
#include <errno.h>              /* perror(), errno,
66
66
                                   program_invocation_short_name */
67
67
#include <time.h>               /* nanosleep(), time(), sleep() */
73
73
                                */
74
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
75
75
                                   getuid(), getgid(), seteuid(),
76
 
                                   setgid(), pause(), _exit() */
77
 
#include <arpa/inet.h>          /* inet_pton(), htons, inet_ntop() */
 
76
                                   setgid(), pause(), _exit(),
 
77
                                   unlinkat() */
 
78
#include <arpa/inet.h>          /* inet_pton(), htons() */
78
79
#include <iso646.h>             /* not, or, and */
79
80
#include <argp.h>               /* struct argp_option, error_t, struct
80
81
                                   argp_state, struct argp,
92
93
                                   argz_delete(), argz_append(),
93
94
                                   argz_stringify(), argz_add(),
94
95
                                   argz_count() */
 
96
#include <netdb.h>              /* getnameinfo(), NI_NUMERICHOST,
 
97
                                   EAI_SYSTEM, gai_strerror() */
95
98
 
96
99
#ifdef __linux__
97
100
#include <sys/klog.h>           /* klogctl() */
139
142
static const char sys_class_net[] = "/sys/class/net";
140
143
char *connect_to = NULL;
141
144
const char *hookdir = HOOKDIR;
 
145
int hookdir_fd = -1;
142
146
uid_t uid = 65534;
143
147
gid_t gid = 65534;
144
148
 
145
149
/* Doubly linked list that need to be circularly linked when used */
146
150
typedef struct server{
147
151
  const char *ip;
148
 
  uint16_t port;
 
152
  in_port_t port;
149
153
  AvahiIfIndex if_index;
150
154
  int af;
151
155
  struct timespec last_seen;
155
159
 
156
160
/* Used for passing in values through the Avahi callback functions */
157
161
typedef struct {
158
 
  AvahiSimplePoll *simple_poll;
159
162
  AvahiServer *server;
160
163
  gnutls_certificate_credentials_t cred;
161
164
  unsigned int dh_bits;
163
166
  const char *priority;
164
167
  gpgme_ctx_t ctx;
165
168
  server *current_server;
 
169
  char *interfaces;
 
170
  size_t interfaces_size;
166
171
} mandos_context;
167
172
 
168
 
/* global context so signal handler can reach it*/
169
 
mandos_context mc = { .simple_poll = NULL, .server = NULL,
170
 
                      .dh_bits = 1024, .priority = "SECURE256"
171
 
                      ":!CTYPE-X.509:+CTYPE-OPENPGP",
172
 
                      .current_server = NULL };
 
173
/* global so signal handler can reach it*/
 
174
AvahiSimplePoll *simple_poll;
173
175
 
174
176
sig_atomic_t quit_now = 0;
175
177
int signal_received = 0;
183
185
  perror(print_text);
184
186
}
185
187
 
186
 
__attribute__((format (gnu_printf, 2, 3)))
 
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
187
189
int fprintf_plus(FILE *stream, const char *format, ...){
188
190
  va_list ap;
189
191
  va_start (ap, format);
190
192
  
191
193
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
192
194
                             program_invocation_short_name));
193
 
  return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
195
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
194
196
}
195
197
 
196
198
/*
198
200
 * bytes. "buffer_capacity" is how much is currently allocated,
199
201
 * "buffer_length" is how much is already used.
200
202
 */
 
203
__attribute__((nonnull, warn_unused_result))
201
204
size_t incbuffer(char **buffer, size_t buffer_length,
202
205
                 size_t buffer_capacity){
203
206
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
204
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
205
 
    if(buffer == NULL){
 
207
    char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
208
    if(new_buf == NULL){
 
209
      int old_errno = errno;
 
210
      free(*buffer);
 
211
      errno = old_errno;
 
212
      *buffer = NULL;
206
213
      return 0;
207
214
    }
 
215
    *buffer = new_buf;
208
216
    buffer_capacity += BUFFER_SIZE;
209
217
  }
210
218
  return buffer_capacity;
211
219
}
212
220
 
213
221
/* Add server to set of servers to retry periodically */
214
 
bool add_server(const char *ip, uint16_t port, AvahiIfIndex if_index,
215
 
                int af){
 
222
__attribute__((nonnull, warn_unused_result))
 
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
 
224
                int af, server **current_server){
216
225
  int ret;
217
226
  server *new_server = malloc(sizeof(server));
218
227
  if(new_server == NULL){
225
234
                          .af = af };
226
235
  if(new_server->ip == NULL){
227
236
    perror_plus("strdup");
 
237
    free(new_server);
 
238
    return false;
 
239
  }
 
240
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
 
241
  if(ret == -1){
 
242
    perror_plus("clock_gettime");
 
243
#ifdef __GNUC__
 
244
#pragma GCC diagnostic push
 
245
#pragma GCC diagnostic ignored "-Wcast-qual"
 
246
#endif
 
247
    free((char *)(new_server->ip));
 
248
#ifdef __GNUC__
 
249
#pragma GCC diagnostic pop
 
250
#endif
 
251
    free(new_server);
228
252
    return false;
229
253
  }
230
254
  /* Special case of first server */
231
 
  if (mc.current_server == NULL){
 
255
  if(*current_server == NULL){
232
256
    new_server->next = new_server;
233
257
    new_server->prev = new_server;
234
 
    mc.current_server = new_server;
235
 
  /* Place the new server last in the list */
 
258
    *current_server = new_server;
236
259
  } else {
237
 
    new_server->next = mc.current_server;
238
 
    new_server->prev = mc.current_server->prev;
 
260
    /* Place the new server last in the list */
 
261
    new_server->next = *current_server;
 
262
    new_server->prev = (*current_server)->prev;
239
263
    new_server->prev->next = new_server;
240
 
    mc.current_server->prev = new_server;
241
 
  }
242
 
  ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
243
 
  if(ret == -1){
244
 
    perror_plus("clock_gettime");
245
 
    return false;
 
264
    (*current_server)->prev = new_server;
246
265
  }
247
266
  return true;
248
267
}
250
269
/* 
251
270
 * Initialize GPGME.
252
271
 */
253
 
static bool init_gpgme(const char *seckey, const char *pubkey,
254
 
                       const char *tempdir){
 
272
__attribute__((nonnull, warn_unused_result))
 
273
static bool init_gpgme(const char * const seckey,
 
274
                       const char * const pubkey,
 
275
                       const char * const tempdir,
 
276
                       mandos_context *mc){
255
277
  gpgme_error_t rc;
256
278
  gpgme_engine_info_t engine_info;
257
279
  
258
 
  
259
280
  /*
260
281
   * Helper function to insert pub and seckey to the engine keyring.
261
282
   */
262
 
  bool import_key(const char *filename){
 
283
  bool import_key(const char * const filename){
263
284
    int ret;
264
285
    int fd;
265
286
    gpgme_data_t pgp_data;
277
298
      return false;
278
299
    }
279
300
    
280
 
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
301
    rc = gpgme_op_import(mc->ctx, pgp_data);
281
302
    if(rc != GPG_ERR_NO_ERROR){
282
303
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
283
304
                   gpgme_strsource(rc), gpgme_strerror(rc));
327
348
  }
328
349
  
329
350
  /* Create new GPGME "context" */
330
 
  rc = gpgme_new(&(mc.ctx));
 
351
  rc = gpgme_new(&(mc->ctx));
331
352
  if(rc != GPG_ERR_NO_ERROR){
332
353
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
333
354
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
346
367
 * Decrypt OpenPGP data.
347
368
 * Returns -1 on error
348
369
 */
 
370
__attribute__((nonnull, warn_unused_result))
349
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
350
372
                                  size_t crypto_size,
351
 
                                  char **plaintext){
 
373
                                  char **plaintext,
 
374
                                  mandos_context *mc){
352
375
  gpgme_data_t dh_crypto, dh_plain;
353
376
  gpgme_error_t rc;
354
377
  ssize_t ret;
380
403
  
381
404
  /* Decrypt data from the cryptotext data buffer to the plaintext
382
405
     data buffer */
383
 
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
406
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
384
407
  if(rc != GPG_ERR_NO_ERROR){
385
408
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
386
409
                 gpgme_strsource(rc), gpgme_strerror(rc));
387
410
    plaintext_length = -1;
388
411
    if(debug){
389
412
      gpgme_decrypt_result_t result;
390
 
      result = gpgme_op_decrypt_result(mc.ctx);
 
413
      result = gpgme_op_decrypt_result(mc->ctx);
391
414
      if(result == NULL){
392
415
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
393
416
      } else {
470
493
  return plaintext_length;
471
494
}
472
495
 
473
 
static const char * safer_gnutls_strerror(int value){
474
 
  const char *ret = gnutls_strerror(value); /* Spurious warning from
475
 
                                               -Wunreachable-code */
 
496
__attribute__((warn_unused_result))
 
497
static const char *safer_gnutls_strerror(int value){
 
498
  const char *ret = gnutls_strerror(value);
476
499
  if(ret == NULL)
477
500
    ret = "(unknown)";
478
501
  return ret;
479
502
}
480
503
 
481
504
/* GnuTLS log function callback */
 
505
__attribute__((nonnull))
482
506
static void debuggnutls(__attribute__((unused)) int level,
483
507
                        const char* string){
484
508
  fprintf_plus(stderr, "GnuTLS: %s", string);
485
509
}
486
510
 
 
511
__attribute__((nonnull, warn_unused_result))
487
512
static int init_gnutls_global(const char *pubkeyfilename,
488
 
                              const char *seckeyfilename){
 
513
                              const char *seckeyfilename,
 
514
                              mandos_context *mc){
489
515
  int ret;
490
516
  
491
517
  if(debug){
508
534
  }
509
535
  
510
536
  /* OpenPGP credentials */
511
 
  ret = gnutls_certificate_allocate_credentials(&mc.cred);
 
537
  ret = gnutls_certificate_allocate_credentials(&mc->cred);
512
538
  if(ret != GNUTLS_E_SUCCESS){
513
539
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
514
540
                 safer_gnutls_strerror(ret));
524
550
  }
525
551
  
526
552
  ret = gnutls_certificate_set_openpgp_key_file
527
 
    (mc.cred, pubkeyfilename, seckeyfilename,
 
553
    (mc->cred, pubkeyfilename, seckeyfilename,
528
554
     GNUTLS_OPENPGP_FMT_BASE64);
529
555
  if(ret != GNUTLS_E_SUCCESS){
530
556
    fprintf_plus(stderr,
536
562
  }
537
563
  
538
564
  /* GnuTLS server initialization */
539
 
  ret = gnutls_dh_params_init(&mc.dh_params);
 
565
  ret = gnutls_dh_params_init(&mc->dh_params);
540
566
  if(ret != GNUTLS_E_SUCCESS){
541
567
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
542
568
                 " initialization: %s\n",
543
569
                 safer_gnutls_strerror(ret));
544
570
    goto globalfail;
545
571
  }
546
 
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
572
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
547
573
  if(ret != GNUTLS_E_SUCCESS){
548
574
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
549
575
                 safer_gnutls_strerror(ret));
550
576
    goto globalfail;
551
577
  }
552
578
  
553
 
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
579
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
554
580
  
555
581
  return 0;
556
582
  
557
583
 globalfail:
558
584
  
559
 
  gnutls_certificate_free_credentials(mc.cred);
 
585
  gnutls_certificate_free_credentials(mc->cred);
560
586
  gnutls_global_deinit();
561
 
  gnutls_dh_params_deinit(mc.dh_params);
 
587
  gnutls_dh_params_deinit(mc->dh_params);
562
588
  return -1;
563
589
}
564
590
 
565
 
static int init_gnutls_session(gnutls_session_t *session){
 
591
__attribute__((nonnull, warn_unused_result))
 
592
static int init_gnutls_session(gnutls_session_t *session,
 
593
                               mandos_context *mc){
566
594
  int ret;
567
595
  /* GnuTLS session creation */
568
596
  do {
580
608
  {
581
609
    const char *err;
582
610
    do {
583
 
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
611
      ret = gnutls_priority_set_direct(*session, mc->priority, &err);
584
612
      if(quit_now){
585
613
        gnutls_deinit(*session);
586
614
        return -1;
597
625
  
598
626
  do {
599
627
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
600
 
                                 mc.cred);
 
628
                                 mc->cred);
601
629
    if(quit_now){
602
630
      gnutls_deinit(*session);
603
631
      return -1;
613
641
  /* ignore client certificate if any. */
614
642
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
615
643
  
616
 
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
644
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
617
645
  
618
646
  return 0;
619
647
}
623
651
                      __attribute__((unused)) const char *txt){}
624
652
 
625
653
/* Called when a Mandos server is found */
626
 
static int start_mandos_communication(const char *ip, uint16_t port,
 
654
__attribute__((nonnull, warn_unused_result))
 
655
static int start_mandos_communication(const char *ip, in_port_t port,
627
656
                                      AvahiIfIndex if_index,
628
 
                                      int af){
 
657
                                      int af, mandos_context *mc){
629
658
  int ret, tcp_sd = -1;
630
659
  ssize_t sret;
631
 
  union {
632
 
    struct sockaddr_in in;
633
 
    struct sockaddr_in6 in6;
634
 
  } to;
 
660
  struct sockaddr_storage to;
635
661
  char *buffer = NULL;
636
662
  char *decrypted_buffer = NULL;
637
663
  size_t buffer_length = 0;
661
687
    return -1;
662
688
  }
663
689
  
664
 
  ret = init_gnutls_session(&session);
 
690
  /* If the interface is specified and we have a list of interfaces */
 
691
  if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
 
692
    /* Check if the interface is one of the interfaces we are using */
 
693
    bool match = false;
 
694
    {
 
695
      char *interface = NULL;
 
696
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
697
                                 interface))){
 
698
        if(if_nametoindex(interface) == (unsigned int)if_index){
 
699
          match = true;
 
700
          break;
 
701
        }
 
702
      }
 
703
    }
 
704
    if(not match){
 
705
      /* This interface does not match any in the list, so we don't
 
706
         connect to the server */
 
707
      if(debug){
 
708
        char interface[IF_NAMESIZE];
 
709
        if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
710
          perror_plus("if_indextoname");
 
711
        } else {
 
712
          fprintf_plus(stderr, "Skipping server on non-used interface"
 
713
                       " \"%s\"\n",
 
714
                       if_indextoname((unsigned int)if_index,
 
715
                                      interface));
 
716
        }
 
717
      }
 
718
      return -1;
 
719
    }
 
720
  }
 
721
  
 
722
  ret = init_gnutls_session(&session, mc);
665
723
  if(ret != 0){
666
724
    return -1;
667
725
  }
668
726
  
669
727
  if(debug){
670
728
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
671
 
                 PRIu16 "\n", ip, port);
 
729
                 PRIuMAX "\n", ip, (uintmax_t)port);
672
730
  }
673
731
  
674
732
  tcp_sd = socket(pf, SOCK_STREAM, 0);
686
744
  
687
745
  memset(&to, 0, sizeof(to));
688
746
  if(af == AF_INET6){
689
 
    to.in6.sin6_family = (sa_family_t)af;
690
 
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
747
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
 
748
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
691
749
  } else {                      /* IPv4 */
692
 
    to.in.sin_family = (sa_family_t)af;
693
 
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
750
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
 
751
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
694
752
  }
695
753
  if(ret < 0 ){
696
754
    int e = errno;
705
763
    goto mandos_end;
706
764
  }
707
765
  if(af == AF_INET6){
708
 
    to.in6.sin6_port = htons(port); /* Spurious warnings from
709
 
                                       -Wconversion and
710
 
                                       -Wunreachable-code */
711
 
    
712
 
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
713
 
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
714
 
                                -Wunreachable-code*/
 
766
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
 
767
    if(IN6_IS_ADDR_LINKLOCAL
 
768
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
715
769
      if(if_index == AVAHI_IF_UNSPEC){
716
770
        fprintf_plus(stderr, "An IPv6 link-local address is"
717
771
                     " incomplete without a network interface\n");
719
773
        goto mandos_end;
720
774
      }
721
775
      /* Set the network interface number as scope */
722
 
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
776
      ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
723
777
    }
724
778
  } else {
725
 
    to.in.sin_port = htons(port); /* Spurious warnings from
726
 
                                     -Wconversion and
727
 
                                     -Wunreachable-code */
 
779
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
728
780
  }
729
781
  
730
782
  if(quit_now){
738
790
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
739
791
        perror_plus("if_indextoname");
740
792
      } else {
741
 
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIu16
742
 
                     "\n", ip, interface, port);
 
793
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
 
794
                     "\n", ip, interface, (uintmax_t)port);
743
795
      }
744
796
    } else {
745
 
      fprintf_plus(stderr, "Connection to: %s, port %" PRIu16 "\n",
746
 
                   ip, port);
 
797
      fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
 
798
                   ip, (uintmax_t)port);
747
799
    }
748
800
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
749
801
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
750
 
    const char *pcret;
751
802
    if(af == AF_INET6){
752
 
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
753
 
                        sizeof(addrstr));
 
803
      ret = getnameinfo((struct sockaddr *)&to,
 
804
                        sizeof(struct sockaddr_in6),
 
805
                        addrstr, sizeof(addrstr), NULL, 0,
 
806
                        NI_NUMERICHOST);
754
807
    } else {
755
 
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
756
 
                        sizeof(addrstr));
 
808
      ret = getnameinfo((struct sockaddr *)&to,
 
809
                        sizeof(struct sockaddr_in),
 
810
                        addrstr, sizeof(addrstr), NULL, 0,
 
811
                        NI_NUMERICHOST);
757
812
    }
758
 
    if(pcret == NULL){
759
 
      perror_plus("inet_ntop");
760
 
    } else {
761
 
      if(strcmp(addrstr, ip) != 0){
762
 
        fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
763
 
      }
 
813
    if(ret == EAI_SYSTEM){
 
814
      perror_plus("getnameinfo");
 
815
    } else if(ret != 0) {
 
816
      fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
 
817
    } else if(strcmp(addrstr, ip) != 0){
 
818
      fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
764
819
    }
765
820
  }
766
821
  
770
825
  }
771
826
  
772
827
  if(af == AF_INET6){
773
 
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
828
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
829
                  sizeof(struct sockaddr_in6));
774
830
  } else {
775
 
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
831
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
832
                  sizeof(struct sockaddr_in));
776
833
  }
777
834
  if(ret < 0){
778
 
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
835
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
779
836
      int e = errno;
780
837
      perror_plus("connect");
781
838
      errno = e;
942
999
  if(buffer_length > 0){
943
1000
    ssize_t decrypted_buffer_size;
944
1001
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
945
 
                                               &decrypted_buffer);
 
1002
                                               &decrypted_buffer, mc);
946
1003
    if(decrypted_buffer_size >= 0){
947
1004
      
948
1005
      written = 0;
996
1053
  return retval;
997
1054
}
998
1055
 
 
1056
__attribute__((nonnull))
999
1057
static void resolve_callback(AvahiSServiceResolver *r,
1000
1058
                             AvahiIfIndex interface,
1001
1059
                             AvahiProtocol proto,
1009
1067
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
1010
1068
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
1011
1069
                             flags,
1012
 
                             AVAHI_GCC_UNUSED void* userdata){
1013
 
  assert(r);
 
1070
                             void *mc){
 
1071
  if(r == NULL){
 
1072
    return;
 
1073
  }
1014
1074
  
1015
1075
  /* Called whenever a service has been resolved successfully or
1016
1076
     timed out */
1017
1077
  
1018
1078
  if(quit_now){
 
1079
    avahi_s_service_resolver_free(r);
1019
1080
    return;
1020
1081
  }
1021
1082
  
1025
1086
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1026
1087
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
1027
1088
                 domain,
1028
 
                 avahi_strerror(avahi_server_errno(mc.server)));
 
1089
                 avahi_strerror(avahi_server_errno
 
1090
                                (((mandos_context*)mc)->server)));
1029
1091
    break;
1030
1092
    
1031
1093
  case AVAHI_RESOLVER_FOUND:
1037
1099
                     PRIdMAX ") on port %" PRIu16 "\n", name,
1038
1100
                     host_name, ip, (intmax_t)interface, port);
1039
1101
      }
1040
 
      int ret = start_mandos_communication(ip, port, interface,
1041
 
                                           avahi_proto_to_af(proto));
 
1102
      int ret = start_mandos_communication(ip, (in_port_t)port,
 
1103
                                           interface,
 
1104
                                           avahi_proto_to_af(proto),
 
1105
                                           mc);
1042
1106
      if(ret == 0){
1043
 
        avahi_simple_poll_quit(mc.simple_poll);
 
1107
        avahi_simple_poll_quit(simple_poll);
1044
1108
      } else {
1045
 
        if(not add_server(ip, port, interface,
1046
 
                          avahi_proto_to_af(proto))){
 
1109
        if(not add_server(ip, (in_port_t)port, interface,
 
1110
                          avahi_proto_to_af(proto),
 
1111
                          &((mandos_context*)mc)->current_server)){
1047
1112
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1048
1113
                       " list\n", name);
1049
1114
        }
1062
1127
                            const char *domain,
1063
1128
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
1064
1129
                            flags,
1065
 
                            AVAHI_GCC_UNUSED void* userdata){
1066
 
  assert(b);
 
1130
                            void *mc){
 
1131
  if(b == NULL){
 
1132
    return;
 
1133
  }
1067
1134
  
1068
1135
  /* Called whenever a new services becomes available on the LAN or
1069
1136
     is removed from the LAN */
1077
1144
  case AVAHI_BROWSER_FAILURE:
1078
1145
    
1079
1146
    fprintf_plus(stderr, "(Avahi browser) %s\n",
1080
 
                 avahi_strerror(avahi_server_errno(mc.server)));
1081
 
    avahi_simple_poll_quit(mc.simple_poll);
 
1147
                 avahi_strerror(avahi_server_errno
 
1148
                                (((mandos_context*)mc)->server)));
 
1149
    avahi_simple_poll_quit(simple_poll);
1082
1150
    return;
1083
1151
    
1084
1152
  case AVAHI_BROWSER_NEW:
1087
1155
       the callback function is called the Avahi server will free the
1088
1156
       resolver for us. */
1089
1157
    
1090
 
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1091
 
                                    name, type, domain, protocol, 0,
1092
 
                                    resolve_callback, NULL) == NULL)
 
1158
    if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
 
1159
                                    interface, protocol, name, type,
 
1160
                                    domain, protocol, 0,
 
1161
                                    resolve_callback, mc) == NULL)
1093
1162
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1094
1163
                   " %s\n", name,
1095
 
                   avahi_strerror(avahi_server_errno(mc.server)));
 
1164
                   avahi_strerror(avahi_server_errno
 
1165
                                  (((mandos_context*)mc)->server)));
1096
1166
    break;
1097
1167
    
1098
1168
  case AVAHI_BROWSER_REMOVE:
1117
1187
  signal_received = sig;
1118
1188
  int old_errno = errno;
1119
1189
  /* set main loop to exit */
1120
 
  if(mc.simple_poll != NULL){
1121
 
    avahi_simple_poll_quit(mc.simple_poll);
 
1190
  if(simple_poll != NULL){
 
1191
    avahi_simple_poll_quit(simple_poll);
1122
1192
  }
1123
1193
  errno = old_errno;
1124
1194
}
1125
1195
 
 
1196
__attribute__((nonnull, warn_unused_result))
1126
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1127
1198
  int ret;
1128
1199
  error_t ret_errno;
1147
1218
  return true;
1148
1219
}
1149
1220
 
 
1221
__attribute__((nonnull, warn_unused_result))
1150
1222
bool good_flags(const char *ifname, const struct ifreq *ifr){
1151
1223
  
1152
1224
  /* Reject the loopback device */
1194
1266
 * corresponds to an acceptable network device.
1195
1267
 * (This function is passed to scandir(3) as a filter function.)
1196
1268
 */
 
1269
__attribute__((nonnull, warn_unused_result))
1197
1270
int good_interface(const struct dirent *if_entry){
1198
1271
  if(if_entry->d_name[0] == '.'){
1199
1272
    return 0;
1217
1290
/* 
1218
1291
 * This function determines if a network interface is up.
1219
1292
 */
 
1293
__attribute__((nonnull, warn_unused_result))
1220
1294
bool interface_is_up(const char *interface){
1221
1295
  struct ifreq ifr;
1222
1296
  if(not get_flags(interface, &ifr)){
1233
1307
/* 
1234
1308
 * This function determines if a network interface is running
1235
1309
 */
 
1310
__attribute__((nonnull, warn_unused_result))
1236
1311
bool interface_is_running(const char *interface){
1237
1312
  struct ifreq ifr;
1238
1313
  if(not get_flags(interface, &ifr)){
1246
1321
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
1247
1322
}
1248
1323
 
 
1324
__attribute__((nonnull, pure, warn_unused_result))
1249
1325
int notdotentries(const struct dirent *direntry){
1250
1326
  /* Skip "." and ".." */
1251
1327
  if(direntry->d_name[0] == '.'
1258
1334
}
1259
1335
 
1260
1336
/* Is this directory entry a runnable program? */
 
1337
__attribute__((nonnull, warn_unused_result))
1261
1338
int runnable_hook(const struct dirent *direntry){
1262
1339
  int ret;
1263
1340
  size_t sret;
1271
1348
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1272
1349
                "abcdefghijklmnopqrstuvwxyz"
1273
1350
                "0123456789"
1274
 
                "_-");
 
1351
                "_.-");
1275
1352
  if((direntry->d_name)[sret] != '\0'){
1276
1353
    /* Contains non-allowed characters */
1277
1354
    if(debug){
1281
1358
    return 0;
1282
1359
  }
1283
1360
  
1284
 
  char *fullname = NULL;
1285
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1286
 
  if(ret < 0){
1287
 
    perror_plus("asprintf");
1288
 
    return 0;
1289
 
  }
1290
 
  
1291
 
  ret = stat(fullname, &st);
 
1361
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1292
1362
  if(ret == -1){
1293
1363
    if(debug){
1294
1364
      perror_plus("Could not stat hook");
1318
1388
  return 1;
1319
1389
}
1320
1390
 
1321
 
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
 
1391
__attribute__((nonnull, warn_unused_result))
 
1392
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
 
1393
                            mandos_context *mc){
1322
1394
  int ret;
1323
1395
  struct timespec now;
1324
1396
  struct timespec waited_time;
1325
1397
  intmax_t block_time;
1326
1398
  
1327
1399
  while(true){
1328
 
    if(mc.current_server == NULL){
1329
 
      if (debug){
 
1400
    if(mc->current_server == NULL){
 
1401
      if(debug){
1330
1402
        fprintf_plus(stderr, "Wait until first server is found."
1331
1403
                     " No timeout!\n");
1332
1404
      }
1333
1405
      ret = avahi_simple_poll_iterate(s, -1);
1334
1406
    } else {
1335
 
      if (debug){
 
1407
      if(debug){
1336
1408
        fprintf_plus(stderr, "Check current_server if we should run"
1337
1409
                     " it, or wait\n");
1338
1410
      }
1345
1417
      /* Calculating in ms how long time between now and server
1346
1418
         who we visted longest time ago. Now - last seen.  */
1347
1419
      waited_time.tv_sec = (now.tv_sec
1348
 
                            - mc.current_server->last_seen.tv_sec);
 
1420
                            - mc->current_server->last_seen.tv_sec);
1349
1421
      waited_time.tv_nsec = (now.tv_nsec
1350
 
                             - mc.current_server->last_seen.tv_nsec);
 
1422
                             - mc->current_server->last_seen.tv_nsec);
1351
1423
      /* total time is 10s/10,000ms.
1352
1424
         Converting to s from ms by dividing by 1,000,
1353
1425
         and ns to ms by dividing by 1,000,000. */
1355
1427
                     - ((intmax_t)waited_time.tv_sec * 1000))
1356
1428
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1357
1429
      
1358
 
      if (debug){
 
1430
      if(debug){
1359
1431
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1360
1432
                     block_time);
1361
1433
      }
1362
1434
      
1363
1435
      if(block_time <= 0){
1364
 
        ret = start_mandos_communication(mc.current_server->ip,
1365
 
                                         mc.current_server->port,
1366
 
                                         mc.current_server->if_index,
1367
 
                                         mc.current_server->af);
 
1436
        ret = start_mandos_communication(mc->current_server->ip,
 
1437
                                         mc->current_server->port,
 
1438
                                         mc->current_server->if_index,
 
1439
                                         mc->current_server->af, mc);
1368
1440
        if(ret == 0){
1369
 
          avahi_simple_poll_quit(mc.simple_poll);
 
1441
          avahi_simple_poll_quit(s);
1370
1442
          return 0;
1371
1443
        }
1372
1444
        ret = clock_gettime(CLOCK_MONOTONIC,
1373
 
                            &mc.current_server->last_seen);
 
1445
                            &mc->current_server->last_seen);
1374
1446
        if(ret == -1){
1375
1447
          perror_plus("clock_gettime");
1376
1448
          return -1;
1377
1449
        }
1378
 
        mc.current_server = mc.current_server->next;
 
1450
        mc->current_server = mc->current_server->next;
1379
1451
        block_time = 0;         /* Call avahi to find new Mandos
1380
1452
                                   servers, but don't block */
1381
1453
      }
1383
1455
      ret = avahi_simple_poll_iterate(s, (int)block_time);
1384
1456
    }
1385
1457
    if(ret != 0){
1386
 
      if (ret > 0 or errno != EINTR){
 
1458
      if(ret > 0 or errno != EINTR){
1387
1459
        return (ret != 1) ? ret : 0;
1388
1460
      }
1389
1461
    }
1391
1463
}
1392
1464
 
1393
1465
/* Set effective uid to 0, return errno */
 
1466
__attribute__((warn_unused_result))
1394
1467
error_t raise_privileges(void){
1395
1468
  error_t old_errno = errno;
1396
1469
  error_t ret_errno = 0;
1397
1470
  if(seteuid(0) == -1){
1398
1471
    ret_errno = errno;
1399
 
    perror_plus("seteuid");
1400
1472
  }
1401
1473
  errno = old_errno;
1402
1474
  return ret_errno;
1403
1475
}
1404
1476
 
1405
1477
/* Set effective and real user ID to 0.  Return errno. */
 
1478
__attribute__((warn_unused_result))
1406
1479
error_t raise_privileges_permanently(void){
1407
1480
  error_t old_errno = errno;
1408
1481
  error_t ret_errno = raise_privileges();
1412
1485
  }
1413
1486
  if(setuid(0) == -1){
1414
1487
    ret_errno = errno;
1415
 
    perror_plus("seteuid");
1416
1488
  }
1417
1489
  errno = old_errno;
1418
1490
  return ret_errno;
1419
1491
}
1420
1492
 
1421
1493
/* Set effective user ID to unprivileged saved user ID */
 
1494
__attribute__((warn_unused_result))
1422
1495
error_t lower_privileges(void){
1423
1496
  error_t old_errno = errno;
1424
1497
  error_t ret_errno = 0;
1425
1498
  if(seteuid(uid) == -1){
1426
1499
    ret_errno = errno;
1427
 
    perror_plus("seteuid");
1428
1500
  }
1429
1501
  errno = old_errno;
1430
1502
  return ret_errno;
1431
1503
}
1432
1504
 
1433
1505
/* Lower privileges permanently */
 
1506
__attribute__((warn_unused_result))
1434
1507
error_t lower_privileges_permanently(void){
1435
1508
  error_t old_errno = errno;
1436
1509
  error_t ret_errno = 0;
1437
1510
  if(setuid(uid) == -1){
1438
1511
    ret_errno = errno;
1439
 
    perror_plus("setuid");
1440
1512
  }
1441
1513
  errno = old_errno;
1442
1514
  return ret_errno;
1443
1515
}
1444
1516
 
1445
 
bool run_network_hooks(const char *mode, const char *interface,
 
1517
__attribute__((nonnull))
 
1518
void run_network_hooks(const char *mode, const char *interface,
1446
1519
                       const float delay){
1447
 
  struct dirent **direntries;
1448
 
  struct dirent *direntry;
1449
 
  int ret;
1450
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1451
 
                         alphasort);
 
1520
  struct dirent **direntries = NULL;
 
1521
  if(hookdir_fd == -1){
 
1522
    hookdir_fd = open(hookdir, O_RDONLY);
 
1523
    if(hookdir_fd == -1){
 
1524
      if(errno == ENOENT){
 
1525
        if(debug){
 
1526
          fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
1527
                       " found\n", hookdir);
 
1528
        }
 
1529
      } else {
 
1530
        perror_plus("open");
 
1531
      }
 
1532
      return;
 
1533
    }
 
1534
  }
 
1535
#ifdef __GLIBC__
 
1536
#if __GLIBC_PREREQ(2, 15)
 
1537
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
 
1538
                           runnable_hook, alphasort);
 
1539
#else  /* not __GLIBC_PREREQ(2, 15) */
 
1540
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1541
                         alphasort);
 
1542
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
1543
#else   /* not __GLIBC__ */
 
1544
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1545
                         alphasort);
 
1546
#endif  /* not __GLIBC__ */
1452
1547
  if(numhooks == -1){
1453
1548
    perror_plus("scandir");
1454
 
  } else {
1455
 
    int devnull = open("/dev/null", O_RDONLY);
1456
 
    for(int i = 0; i < numhooks; i++){
1457
 
      direntry = direntries[i];
1458
 
      char *fullname = NULL;
1459
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1460
 
      if(ret < 0){
 
1549
    return;
 
1550
  }
 
1551
  struct dirent *direntry;
 
1552
  int ret;
 
1553
  int devnull = open("/dev/null", O_RDONLY);
 
1554
  for(int i = 0; i < numhooks; i++){
 
1555
    direntry = direntries[i];
 
1556
    if(debug){
 
1557
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
1558
                   direntry->d_name);
 
1559
    }
 
1560
    pid_t hook_pid = fork();
 
1561
    if(hook_pid == 0){
 
1562
      /* Child */
 
1563
      /* Raise privileges */
 
1564
      errno = raise_privileges_permanently();
 
1565
      if(errno != 0){
 
1566
        perror_plus("Failed to raise privileges");
 
1567
        _exit(EX_NOPERM);
 
1568
      }
 
1569
      /* Set group */
 
1570
      errno = 0;
 
1571
      ret = setgid(0);
 
1572
      if(ret == -1){
 
1573
        perror_plus("setgid");
 
1574
        _exit(EX_NOPERM);
 
1575
      }
 
1576
      /* Reset supplementary groups */
 
1577
      errno = 0;
 
1578
      ret = setgroups(0, NULL);
 
1579
      if(ret == -1){
 
1580
        perror_plus("setgroups");
 
1581
        _exit(EX_NOPERM);
 
1582
      }
 
1583
      ret = dup2(devnull, STDIN_FILENO);
 
1584
      if(ret == -1){
 
1585
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
1586
        _exit(EX_OSERR);
 
1587
      }
 
1588
      ret = close(devnull);
 
1589
      if(ret == -1){
 
1590
        perror_plus("close");
 
1591
        _exit(EX_OSERR);
 
1592
      }
 
1593
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
1594
      if(ret == -1){
 
1595
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
1596
        _exit(EX_OSERR);
 
1597
      }
 
1598
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
1599
      if(ret == -1){
 
1600
        perror_plus("setenv");
 
1601
        _exit(EX_OSERR);
 
1602
      }
 
1603
      ret = setenv("DEVICE", interface, 1);
 
1604
      if(ret == -1){
 
1605
        perror_plus("setenv");
 
1606
        _exit(EX_OSERR);
 
1607
      }
 
1608
      ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
1609
      if(ret == -1){
 
1610
        perror_plus("setenv");
 
1611
        _exit(EX_OSERR);
 
1612
      }
 
1613
      ret = setenv("MODE", mode, 1);
 
1614
      if(ret == -1){
 
1615
        perror_plus("setenv");
 
1616
        _exit(EX_OSERR);
 
1617
      }
 
1618
      char *delaystring;
 
1619
      ret = asprintf(&delaystring, "%f", (double)delay);
 
1620
      if(ret == -1){
1461
1621
        perror_plus("asprintf");
1462
 
        continue;
1463
 
      }
1464
 
      if(debug){
1465
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1466
 
                     direntry->d_name);
1467
 
      }
1468
 
      pid_t hook_pid = fork();
1469
 
      if(hook_pid == 0){
1470
 
        /* Child */
1471
 
        /* Raise privileges */
1472
 
        raise_privileges_permanently();
1473
 
        /* Set group */
1474
 
        errno = 0;
1475
 
        ret = setgid(0);
1476
 
        if(ret == -1){
1477
 
          perror_plus("setgid");
1478
 
        }
1479
 
        /* Reset supplementary groups */
1480
 
        errno = 0;
1481
 
        ret = setgroups(0, NULL);
1482
 
        if(ret == -1){
1483
 
          perror_plus("setgroups");
1484
 
        }
1485
 
        dup2(devnull, STDIN_FILENO);
1486
 
        close(devnull);
1487
 
        dup2(STDERR_FILENO, STDOUT_FILENO);
1488
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1489
 
        if(ret == -1){
1490
 
          perror_plus("setenv");
1491
 
          _exit(EX_OSERR);
1492
 
        }
1493
 
        ret = setenv("DEVICE", interface, 1);
1494
 
        if(ret == -1){
1495
 
          perror_plus("setenv");
1496
 
          _exit(EX_OSERR);
1497
 
        }
1498
 
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1499
 
        if(ret == -1){
1500
 
          perror_plus("setenv");
1501
 
          _exit(EX_OSERR);
1502
 
        }
1503
 
        ret = setenv("MODE", mode, 1);
1504
 
        if(ret == -1){
1505
 
          perror_plus("setenv");
1506
 
          _exit(EX_OSERR);
1507
 
        }
1508
 
        char *delaystring;
1509
 
        ret = asprintf(&delaystring, "%f", delay);
1510
 
        if(ret == -1){
1511
 
          perror_plus("asprintf");
1512
 
          _exit(EX_OSERR);
1513
 
        }
1514
 
        ret = setenv("DELAY", delaystring, 1);
1515
 
        if(ret == -1){
1516
 
          free(delaystring);
1517
 
          perror_plus("setenv");
1518
 
          _exit(EX_OSERR);
1519
 
        }
 
1622
        _exit(EX_OSERR);
 
1623
      }
 
1624
      ret = setenv("DELAY", delaystring, 1);
 
1625
      if(ret == -1){
1520
1626
        free(delaystring);
1521
 
        if(connect_to != NULL){
1522
 
          ret = setenv("CONNECT", connect_to, 1);
1523
 
          if(ret == -1){
1524
 
            perror_plus("setenv");
1525
 
            _exit(EX_OSERR);
1526
 
          }
1527
 
        }
1528
 
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1529
 
          perror_plus("execl");
1530
 
          _exit(EXIT_FAILURE);
1531
 
        }
 
1627
        perror_plus("setenv");
 
1628
        _exit(EX_OSERR);
 
1629
      }
 
1630
      free(delaystring);
 
1631
      if(connect_to != NULL){
 
1632
        ret = setenv("CONNECT", connect_to, 1);
 
1633
        if(ret == -1){
 
1634
          perror_plus("setenv");
 
1635
          _exit(EX_OSERR);
 
1636
        }
 
1637
      }
 
1638
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
 
1639
      if(hook_fd == -1){
 
1640
        perror_plus("openat");
 
1641
        _exit(EXIT_FAILURE);
 
1642
      }
 
1643
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1644
        perror_plus("close");
 
1645
        _exit(EXIT_FAILURE);
 
1646
      }
 
1647
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
1648
                 environ) == -1){
 
1649
        perror_plus("fexecve");
 
1650
        _exit(EXIT_FAILURE);
 
1651
      }
 
1652
    } else {
 
1653
      if(hook_pid == -1){
 
1654
        perror_plus("fork");
 
1655
        free(direntry);
 
1656
        continue;
 
1657
      }
 
1658
      int status;
 
1659
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1660
        perror_plus("waitpid");
 
1661
        free(direntry);
 
1662
        continue;
 
1663
      }
 
1664
      if(WIFEXITED(status)){
 
1665
        if(WEXITSTATUS(status) != 0){
 
1666
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1667
                       " with status %d\n", direntry->d_name,
 
1668
                       WEXITSTATUS(status));
 
1669
          free(direntry);
 
1670
          continue;
 
1671
        }
 
1672
      } else if(WIFSIGNALED(status)){
 
1673
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1674
                     " signal %d\n", direntry->d_name,
 
1675
                     WTERMSIG(status));
 
1676
        free(direntry);
 
1677
        continue;
1532
1678
      } else {
1533
 
        int status;
1534
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1535
 
          perror_plus("waitpid");
1536
 
          free(fullname);
1537
 
          continue;
1538
 
        }
1539
 
        if(WIFEXITED(status)){
1540
 
          if(WEXITSTATUS(status) != 0){
1541
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1542
 
                         " with status %d\n", direntry->d_name,
1543
 
                         WEXITSTATUS(status));
1544
 
            free(fullname);
1545
 
            continue;
1546
 
          }
1547
 
        } else if(WIFSIGNALED(status)){
1548
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1549
 
                       " signal %d\n", direntry->d_name,
1550
 
                       WTERMSIG(status));
1551
 
          free(fullname);
1552
 
          continue;
1553
 
        } else {
1554
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1555
 
                       " crashed\n", direntry->d_name);
1556
 
          free(fullname);
1557
 
          continue;
1558
 
        }
1559
 
      }
1560
 
      free(fullname);
1561
 
      if(debug){
1562
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1563
 
                     direntry->d_name);
1564
 
      }
1565
 
    }
1566
 
    close(devnull);
1567
 
  }
1568
 
  return true;
 
1679
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1680
                     " crashed\n", direntry->d_name);
 
1681
        free(direntry);
 
1682
        continue;
 
1683
      }
 
1684
    }
 
1685
    if(debug){
 
1686
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1687
                   direntry->d_name);
 
1688
    }
 
1689
    free(direntry);
 
1690
  }
 
1691
  free(direntries);
 
1692
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1693
    perror_plus("close");
 
1694
  } else {
 
1695
    hookdir_fd = -1;
 
1696
  }
 
1697
  close(devnull);
1569
1698
}
1570
1699
 
 
1700
__attribute__((nonnull, warn_unused_result))
1571
1701
error_t bring_up_interface(const char *const interface,
1572
1702
                           const float delay){
1573
 
  int sd = -1;
1574
1703
  error_t old_errno = errno;
1575
 
  error_t ret_errno = 0;
1576
 
  int ret, ret_setflags;
 
1704
  int ret;
1577
1705
  struct ifreq network;
1578
1706
  unsigned int if_index = if_nametoindex(interface);
1579
1707
  if(if_index == 0){
1588
1716
  }
1589
1717
  
1590
1718
  if(not interface_is_up(interface)){
1591
 
    if(not get_flags(interface, &network) and debug){
 
1719
    error_t ret_errno = 0, ioctl_errno = 0;
 
1720
    if(not get_flags(interface, &network)){
1592
1721
      ret_errno = errno;
1593
1722
      fprintf_plus(stderr, "Failed to get flags for interface "
1594
1723
                   "\"%s\"\n", interface);
 
1724
      errno = old_errno;
1595
1725
      return ret_errno;
1596
1726
    }
1597
 
    network.ifr_flags |= IFF_UP;
 
1727
    network.ifr_flags |= IFF_UP; /* set flag */
1598
1728
    
1599
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1600
 
    if(sd < 0){
 
1729
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1730
    if(sd == -1){
1601
1731
      ret_errno = errno;
1602
1732
      perror_plus("socket");
1603
1733
      errno = old_errno;
1604
1734
      return ret_errno;
1605
1735
    }
1606
 
  
 
1736
    
1607
1737
    if(quit_now){
1608
 
      close(sd);
 
1738
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1739
      if(ret == -1){
 
1740
        perror_plus("close");
 
1741
      }
1609
1742
      errno = old_errno;
1610
1743
      return EINTR;
1611
1744
    }
1615
1748
                   interface);
1616
1749
    }
1617
1750
    
1618
 
    /* Raise priviliges */
1619
 
    raise_privileges();
 
1751
    /* Raise privileges */
 
1752
    ret_errno = raise_privileges();
 
1753
    if(ret_errno != 0){
 
1754
      errno = ret_errno;
 
1755
      perror_plus("Failed to raise privileges");
 
1756
    }
1620
1757
    
1621
1758
#ifdef __linux__
1622
 
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1623
 
       messages about the network interface to mess up the prompt */
1624
 
    int ret_linux = klogctl(8, NULL, 5);
1625
 
    bool restore_loglevel = true;
1626
 
    if(ret_linux == -1){
1627
 
      restore_loglevel = false;
1628
 
      perror_plus("klogctl");
 
1759
    int ret_linux;
 
1760
    bool restore_loglevel = false;
 
1761
    if(ret_errno == 0){
 
1762
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1763
         messages about the network interface to mess up the prompt */
 
1764
      ret_linux = klogctl(8, NULL, 5);
 
1765
      if(ret_linux == -1){
 
1766
        perror_plus("klogctl");
 
1767
      } else {
 
1768
        restore_loglevel = true;
 
1769
      }
1629
1770
    }
1630
1771
#endif  /* __linux__ */
1631
 
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1632
 
    ret_errno = errno;
 
1772
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1773
    ioctl_errno = errno;
1633
1774
#ifdef __linux__
1634
1775
    if(restore_loglevel){
1635
1776
      ret_linux = klogctl(7, NULL, 0);
1639
1780
    }
1640
1781
#endif  /* __linux__ */
1641
1782
    
1642
 
    /* Lower privileges */
1643
 
    lower_privileges();
 
1783
    /* If raise_privileges() succeeded above */
 
1784
    if(ret_errno == 0){
 
1785
      /* Lower privileges */
 
1786
      ret_errno = lower_privileges();
 
1787
      if(ret_errno != 0){
 
1788
        errno = ret_errno;
 
1789
        perror_plus("Failed to lower privileges");
 
1790
      }
 
1791
    }
1644
1792
    
1645
1793
    /* Close the socket */
1646
1794
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
1649
1797
    }
1650
1798
    
1651
1799
    if(ret_setflags == -1){
1652
 
      errno = ret_errno;
 
1800
      errno = ioctl_errno;
1653
1801
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1654
1802
      errno = old_errno;
1655
 
      return ret_errno;
 
1803
      return ioctl_errno;
1656
1804
    }
1657
1805
  } else if(debug){
1658
1806
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1676
1824
  return 0;
1677
1825
}
1678
1826
 
 
1827
__attribute__((nonnull, warn_unused_result))
1679
1828
error_t take_down_interface(const char *const interface){
1680
 
  int sd = -1;
1681
1829
  error_t old_errno = errno;
1682
 
  error_t ret_errno = 0;
1683
 
  int ret, ret_setflags;
1684
1830
  struct ifreq network;
1685
1831
  unsigned int if_index = if_nametoindex(interface);
1686
1832
  if(if_index == 0){
1689
1835
    return ENXIO;
1690
1836
  }
1691
1837
  if(interface_is_up(interface)){
 
1838
    error_t ret_errno = 0, ioctl_errno = 0;
1692
1839
    if(not get_flags(interface, &network) and debug){
1693
1840
      ret_errno = errno;
1694
1841
      fprintf_plus(stderr, "Failed to get flags for interface "
1695
1842
                   "\"%s\"\n", interface);
 
1843
      errno = old_errno;
1696
1844
      return ret_errno;
1697
1845
    }
1698
1846
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1699
1847
    
1700
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1701
 
    if(sd < 0){
 
1848
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1849
    if(sd == -1){
1702
1850
      ret_errno = errno;
1703
1851
      perror_plus("socket");
1704
1852
      errno = old_errno;
1710
1858
                   interface);
1711
1859
    }
1712
1860
    
1713
 
    /* Raise priviliges */
1714
 
    raise_privileges();
1715
 
    
1716
 
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1717
 
    ret_errno = errno;
1718
 
    
1719
 
    /* Lower privileges */
1720
 
    lower_privileges();
 
1861
    /* Raise privileges */
 
1862
    ret_errno = raise_privileges();
 
1863
    if(ret_errno != 0){
 
1864
      errno = ret_errno;
 
1865
      perror_plus("Failed to raise privileges");
 
1866
    }
 
1867
    
 
1868
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1869
    ioctl_errno = errno;
 
1870
    
 
1871
    /* If raise_privileges() succeeded above */
 
1872
    if(ret_errno == 0){
 
1873
      /* Lower privileges */
 
1874
      ret_errno = lower_privileges();
 
1875
      if(ret_errno != 0){
 
1876
        errno = ret_errno;
 
1877
        perror_plus("Failed to lower privileges");
 
1878
      }
 
1879
    }
1721
1880
    
1722
1881
    /* Close the socket */
1723
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1882
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1724
1883
    if(ret == -1){
1725
1884
      perror_plus("close");
1726
1885
    }
1727
1886
    
1728
1887
    if(ret_setflags == -1){
1729
 
      errno = ret_errno;
 
1888
      errno = ioctl_errno;
1730
1889
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1731
1890
      errno = old_errno;
1732
 
      return ret_errno;
 
1891
      return ioctl_errno;
1733
1892
    }
1734
1893
  } else if(debug){
1735
1894
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1741
1900
}
1742
1901
 
1743
1902
int main(int argc, char *argv[]){
 
1903
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
 
1904
                        .priority = "SECURE256:!CTYPE-X.509:"
 
1905
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
 
1906
                        .interfaces = NULL, .interfaces_size = 0 };
1744
1907
  AvahiSServiceBrowser *sb = NULL;
1745
1908
  error_t ret_errno;
1746
1909
  int ret;
1747
1910
  intmax_t tmpmax;
1748
1911
  char *tmp;
1749
1912
  int exitcode = EXIT_SUCCESS;
1750
 
  char *interfaces = NULL;
1751
 
  size_t interfaces_size = 0;
1752
1913
  char *interfaces_to_take_down = NULL;
1753
1914
  size_t interfaces_to_take_down_size = 0;
1754
 
  char tempdir[] = "/tmp/mandosXXXXXX";
1755
 
  bool tempdir_created = false;
 
1915
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
1916
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
1917
  char *tempdir = NULL;
1756
1918
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1757
1919
  const char *seckey = PATHDIR "/" SECKEY;
1758
1920
  const char *pubkey = PATHDIR "/" PUBKEY;
1759
1921
  char *interfaces_hooks = NULL;
1760
 
  size_t interfaces_hooks_size = 0;
1761
1922
  
1762
1923
  bool gnutls_initialized = false;
1763
1924
  bool gpgme_initialized = false;
1854
2015
        connect_to = arg;
1855
2016
        break;
1856
2017
      case 'i':                 /* --interface */
1857
 
        ret_errno = argz_add_sep(&interfaces, &interfaces_size, arg,
1858
 
                                 (int)',');
 
2018
        ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
 
2019
                                 arg, (int)',');
1859
2020
        if(ret_errno != 0){
1860
2021
          argp_error(state, "%s", strerror(ret_errno));
1861
2022
        }
1941
2102
    /* Work around Debian bug #633582:
1942
2103
       <http://bugs.debian.org/633582> */
1943
2104
    
1944
 
    /* Re-raise priviliges */
1945
 
    if(raise_privileges() == 0){
 
2105
    /* Re-raise privileges */
 
2106
    ret_errno = raise_privileges();
 
2107
    if(ret_errno != 0){
 
2108
      errno = ret_errno;
 
2109
      perror_plus("Failed to raise privileges");
 
2110
    } else {
1946
2111
      struct stat st;
1947
2112
      
1948
2113
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1988
2153
      }
1989
2154
    
1990
2155
      /* Lower privileges */
1991
 
      errno = 0;
1992
 
      ret = seteuid(uid);
1993
 
      if(ret == -1){
1994
 
        perror_plus("seteuid");
 
2156
      ret_errno = lower_privileges();
 
2157
      if(ret_errno != 0){
 
2158
        errno = ret_errno;
 
2159
        perror_plus("Failed to lower privileges");
1995
2160
      }
1996
2161
    }
1997
2162
  }
1998
2163
  
1999
 
  /* Remove empty interface names */
 
2164
  /* Remove invalid interface names (except "none") */
2000
2165
  {
2001
2166
    char *interface = NULL;
2002
 
    while((interface = argz_next(interfaces, interfaces_size,
 
2167
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2003
2168
                                 interface))){
2004
 
      if(if_nametoindex(interface) == 0){
2005
 
        if(interface[0] != '\0' and strcmp(interface, "none") != 0){
 
2169
      if(strcmp(interface, "none") != 0
 
2170
         and if_nametoindex(interface) == 0){
 
2171
        if(interface[0] != '\0'){
2006
2172
          fprintf_plus(stderr, "Not using nonexisting interface"
2007
2173
                       " \"%s\"\n", interface);
2008
2174
        }
2009
 
        argz_delete(&interfaces, &interfaces_size, interface);
 
2175
        argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2010
2176
        interface = NULL;
2011
2177
      }
2012
2178
    }
2014
2180
  
2015
2181
  /* Run network hooks */
2016
2182
  {
2017
 
    ret_errno = argz_append(&interfaces_hooks, &interfaces_hooks_size,
2018
 
                            interfaces, interfaces_size);
2019
 
    if(ret_errno != 0){
2020
 
      errno = ret_errno;
2021
 
      perror_plus("argz_append");
2022
 
      goto end;
2023
 
    }
2024
 
    argz_stringify(interfaces_hooks, interfaces_hooks_size, (int)',');
2025
 
    if(not run_network_hooks("start", interfaces_hooks, delay)){
2026
 
      goto end;
2027
 
    }
 
2183
    if(mc.interfaces != NULL){
 
2184
      interfaces_hooks = malloc(mc.interfaces_size);
 
2185
      if(interfaces_hooks == NULL){
 
2186
        perror_plus("malloc");
 
2187
        goto end;
 
2188
      }
 
2189
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
 
2190
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
 
2191
    }
 
2192
    run_network_hooks("start", interfaces_hooks != NULL ?
 
2193
                      interfaces_hooks : "", delay);
2028
2194
  }
2029
2195
  
2030
2196
  if(not debug){
2035
2201
     from the signal handler */
2036
2202
  /* Initialize the pseudo-RNG for Avahi */
2037
2203
  srand((unsigned int) time(NULL));
2038
 
  mc.simple_poll = avahi_simple_poll_new();
2039
 
  if(mc.simple_poll == NULL){
 
2204
  simple_poll = avahi_simple_poll_new();
 
2205
  if(simple_poll == NULL){
2040
2206
    fprintf_plus(stderr,
2041
2207
                 "Avahi: Failed to create simple poll object.\n");
2042
2208
    exitcode = EX_UNAVAILABLE;
2107
2273
  }
2108
2274
  
2109
2275
  /* If no interfaces were specified, make a list */
2110
 
  if(interfaces == NULL){
2111
 
    struct dirent **direntries;
 
2276
  if(mc.interfaces == NULL){
 
2277
    struct dirent **direntries = NULL;
2112
2278
    /* Look for any good interfaces */
2113
2279
    ret = scandir(sys_class_net, &direntries, good_interface,
2114
2280
                  alphasort);
2115
2281
    if(ret >= 1){
2116
2282
      /* Add all found interfaces to interfaces list */
2117
2283
      for(int i = 0; i < ret; ++i){
2118
 
        ret_errno = argz_add(&interfaces, &interfaces_size,
 
2284
        ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2119
2285
                             direntries[i]->d_name);
2120
2286
        if(ret_errno != 0){
 
2287
          errno = ret_errno;
2121
2288
          perror_plus("argz_add");
 
2289
          free(direntries[i]);
2122
2290
          continue;
2123
2291
        }
2124
2292
        if(debug){
2125
2293
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2126
2294
                       direntries[i]->d_name);
2127
2295
        }
 
2296
        free(direntries[i]);
2128
2297
      }
2129
2298
      free(direntries);
2130
2299
    } else {
2131
 
      free(direntries);
 
2300
      if(ret == 0){
 
2301
        free(direntries);
 
2302
      }
2132
2303
      fprintf_plus(stderr, "Could not find a network interface\n");
2133
2304
      exitcode = EXIT_FAILURE;
2134
2305
      goto end;
2135
2306
    }
2136
2307
  }
2137
2308
  
2138
 
  /* If we only got one interface, explicitly use only that one */
2139
 
  if(argz_count(interfaces, interfaces_size) == 1){
2140
 
    if(debug){
2141
 
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
2142
 
                   interfaces);
2143
 
    }
2144
 
    if_index = (AvahiIfIndex)if_nametoindex(interfaces);
2145
 
  }
2146
 
  
2147
 
  /* Bring up interfaces which are down */
2148
 
  if(not (argz_count(interfaces, interfaces_size) == 1
2149
 
          and strcmp(interfaces, "none") == 0)){
 
2309
  /* Bring up interfaces which are down, and remove any "none"s */
 
2310
  {
2150
2311
    char *interface = NULL;
2151
 
    while((interface = argz_next(interfaces, interfaces_size,
 
2312
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2152
2313
                                 interface))){
 
2314
      /* If interface name is "none", stop bringing up interfaces.
 
2315
         Also remove all instances of "none" from the list */
 
2316
      if(strcmp(interface, "none") == 0){
 
2317
        argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2318
                    interface);
 
2319
        interface = NULL;
 
2320
        while((interface = argz_next(mc.interfaces,
 
2321
                                     mc.interfaces_size, interface))){
 
2322
          if(strcmp(interface, "none") == 0){
 
2323
            argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2324
                        interface);
 
2325
            interface = NULL;
 
2326
          }
 
2327
        }
 
2328
        break;
 
2329
      }
2153
2330
      bool interface_was_up = interface_is_up(interface);
2154
 
      ret = bring_up_interface(interface, delay);
 
2331
      errno = bring_up_interface(interface, delay);
2155
2332
      if(not interface_was_up){
2156
 
        if(ret != 0){
2157
 
          errno = ret;
 
2333
        if(errno != 0){
2158
2334
          perror_plus("Failed to bring up interface");
2159
2335
        } else {
2160
 
          ret_errno = argz_add(&interfaces_to_take_down,
2161
 
                               &interfaces_to_take_down_size,
2162
 
                               interface);
 
2336
          errno = argz_add(&interfaces_to_take_down,
 
2337
                           &interfaces_to_take_down_size,
 
2338
                           interface);
 
2339
          if(errno != 0){
 
2340
            perror_plus("argz_add");
 
2341
          }
2163
2342
        }
2164
2343
      }
2165
2344
    }
2166
 
    free(interfaces);
2167
 
    interfaces = NULL;
2168
 
    interfaces_size = 0;
2169
2345
    if(debug and (interfaces_to_take_down == NULL)){
2170
2346
      fprintf_plus(stderr, "No interfaces were brought up\n");
2171
2347
    }
2172
2348
  }
2173
2349
  
 
2350
  /* If we only got one interface, explicitly use only that one */
 
2351
  if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
 
2352
    if(debug){
 
2353
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
 
2354
                   mc.interfaces);
 
2355
    }
 
2356
    if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
 
2357
  }
 
2358
  
2174
2359
  if(quit_now){
2175
2360
    goto end;
2176
2361
  }
2177
2362
  
2178
 
  ret = init_gnutls_global(pubkey, seckey);
 
2363
  ret = init_gnutls_global(pubkey, seckey, &mc);
2179
2364
  if(ret == -1){
2180
2365
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2181
2366
    exitcode = EX_UNAVAILABLE;
2188
2373
    goto end;
2189
2374
  }
2190
2375
  
2191
 
  if(mkdtemp(tempdir) == NULL){
 
2376
  /* Try /run/tmp before /tmp */
 
2377
  tempdir = mkdtemp(run_tempdir);
 
2378
  if(tempdir == NULL and errno == ENOENT){
 
2379
      if(debug){
 
2380
        fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
 
2381
                     run_tempdir, old_tempdir);
 
2382
      }
 
2383
      tempdir = mkdtemp(old_tempdir);
 
2384
  }
 
2385
  if(tempdir == NULL){
2192
2386
    perror_plus("mkdtemp");
2193
2387
    goto end;
2194
2388
  }
2195
 
  tempdir_created = true;
2196
2389
  
2197
2390
  if(quit_now){
2198
2391
    goto end;
2199
2392
  }
2200
2393
  
2201
 
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
2394
  if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2202
2395
    fprintf_plus(stderr, "init_gpgme failed\n");
2203
2396
    exitcode = EX_UNAVAILABLE;
2204
2397
    goto end;
2225
2418
      goto end;
2226
2419
    }
2227
2420
    
2228
 
    uint16_t port;
 
2421
    in_port_t port;
2229
2422
    errno = 0;
2230
2423
    tmpmax = strtoimax(address+1, &tmp, 10);
2231
2424
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
2232
 
       or tmpmax != (uint16_t)tmpmax){
 
2425
       or tmpmax != (in_port_t)tmpmax){
2233
2426
      fprintf_plus(stderr, "Bad port number\n");
2234
2427
      exitcode = EX_USAGE;
2235
2428
      goto end;
2236
2429
    }
2237
 
  
 
2430
    
2238
2431
    if(quit_now){
2239
2432
      goto end;
2240
2433
    }
2241
2434
    
2242
 
    port = (uint16_t)tmpmax;
 
2435
    port = (in_port_t)tmpmax;
2243
2436
    *address = '\0';
2244
2437
    /* Colon in address indicates IPv6 */
2245
2438
    int af;
2261
2454
    }
2262
2455
    
2263
2456
    while(not quit_now){
2264
 
      ret = start_mandos_communication(address, port, if_index, af);
 
2457
      ret = start_mandos_communication(address, port, if_index, af,
 
2458
                                       &mc);
2265
2459
      if(quit_now or ret == 0){
2266
2460
        break;
2267
2461
      }
2269
2463
        fprintf_plus(stderr, "Retrying in %d seconds\n",
2270
2464
                     (int)retry_interval);
2271
2465
      }
2272
 
      sleep((int)retry_interval);
 
2466
      sleep((unsigned int)retry_interval);
2273
2467
    }
2274
2468
    
2275
 
    if (not quit_now){
 
2469
    if(not quit_now){
2276
2470
      exitcode = EXIT_SUCCESS;
2277
2471
    }
2278
2472
    
2293
2487
    config.publish_domain = 0;
2294
2488
    
2295
2489
    /* Allocate a new server */
2296
 
    mc.server = avahi_server_new(avahi_simple_poll_get
2297
 
                                 (mc.simple_poll), &config, NULL,
2298
 
                                 NULL, &ret_errno);
 
2490
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
2491
                                 &config, NULL, NULL, &ret_errno);
2299
2492
    
2300
2493
    /* Free the Avahi configuration data */
2301
2494
    avahi_server_config_free(&config);
2316
2509
  /* Create the Avahi service browser */
2317
2510
  sb = avahi_s_service_browser_new(mc.server, if_index,
2318
2511
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2319
 
                                   NULL, 0, browse_callback, NULL);
 
2512
                                   NULL, 0, browse_callback,
 
2513
                                   (void *)&mc);
2320
2514
  if(sb == NULL){
2321
2515
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
2322
2516
                 avahi_strerror(avahi_server_errno(mc.server)));
2333
2527
  if(debug){
2334
2528
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2335
2529
  }
2336
 
 
2337
 
  ret = avahi_loop_with_timeout(mc.simple_poll,
2338
 
                                (int)(retry_interval * 1000));
 
2530
  
 
2531
  ret = avahi_loop_with_timeout(simple_poll,
 
2532
                                (int)(retry_interval * 1000), &mc);
2339
2533
  if(debug){
2340
2534
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2341
2535
                 (ret == 0) ? "successfully" : "with error");
2348
2542
  }
2349
2543
  
2350
2544
  /* Cleanup things */
 
2545
  free(mc.interfaces);
 
2546
  
2351
2547
  if(sb != NULL)
2352
2548
    avahi_s_service_browser_free(sb);
2353
2549
  
2354
2550
  if(mc.server != NULL)
2355
2551
    avahi_server_free(mc.server);
2356
2552
  
2357
 
  if(mc.simple_poll != NULL)
2358
 
    avahi_simple_poll_free(mc.simple_poll);
 
2553
  if(simple_poll != NULL)
 
2554
    avahi_simple_poll_free(simple_poll);
2359
2555
  
2360
2556
  if(gnutls_initialized){
2361
2557
    gnutls_certificate_free_credentials(mc.cred);
2373
2569
    mc.current_server->prev->next = NULL;
2374
2570
    while(mc.current_server != NULL){
2375
2571
      server *next = mc.current_server->next;
 
2572
#ifdef __GNUC__
 
2573
#pragma GCC diagnostic push
 
2574
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2575
#endif
 
2576
      free((char *)(mc.current_server->ip));
 
2577
#ifdef __GNUC__
 
2578
#pragma GCC diagnostic pop
 
2579
#endif
2376
2580
      free(mc.current_server);
2377
2581
      mc.current_server = next;
2378
2582
    }
2379
2583
  }
2380
2584
  
2381
 
  /* Re-raise priviliges */
 
2585
  /* Re-raise privileges */
2382
2586
  {
2383
 
    raise_privileges();
2384
 
    
2385
 
    /* Run network hooks */
2386
 
    run_network_hooks("stop", interfaces_hooks, delay);
2387
 
    
2388
 
    /* Take down the network interfaces which were brought up */
2389
 
    {
2390
 
      char *interface = NULL;
2391
 
      while((interface=argz_next(interfaces_to_take_down,
2392
 
                                 interfaces_to_take_down_size,
2393
 
                                 interface))){
2394
 
        ret_errno = take_down_interface(interface);
2395
 
        if(ret_errno != 0){
2396
 
          errno = ret_errno;
2397
 
          perror_plus("Failed to take down interface");
2398
 
        }
2399
 
      }
2400
 
      if(debug and (interfaces_to_take_down == NULL)){
2401
 
        fprintf_plus(stderr, "No interfaces needed to be taken"
2402
 
                     " down\n");
2403
 
      }
2404
 
    }
2405
 
    
2406
 
    lower_privileges_permanently();
 
2587
    ret_errno = raise_privileges();
 
2588
    if(ret_errno != 0){
 
2589
      errno = ret_errno;
 
2590
      perror_plus("Failed to raise privileges");
 
2591
    } else {
 
2592
      
 
2593
      /* Run network hooks */
 
2594
      run_network_hooks("stop", interfaces_hooks != NULL ?
 
2595
                        interfaces_hooks : "", delay);
 
2596
      
 
2597
      /* Take down the network interfaces which were brought up */
 
2598
      {
 
2599
        char *interface = NULL;
 
2600
        while((interface=argz_next(interfaces_to_take_down,
 
2601
                                   interfaces_to_take_down_size,
 
2602
                                   interface))){
 
2603
          ret_errno = take_down_interface(interface);
 
2604
          if(ret_errno != 0){
 
2605
            errno = ret_errno;
 
2606
            perror_plus("Failed to take down interface");
 
2607
          }
 
2608
        }
 
2609
        if(debug and (interfaces_to_take_down == NULL)){
 
2610
          fprintf_plus(stderr, "No interfaces needed to be taken"
 
2611
                       " down\n");
 
2612
        }
 
2613
      }
 
2614
    }
 
2615
    
 
2616
    ret_errno = lower_privileges_permanently();
 
2617
    if(ret_errno != 0){
 
2618
      errno = ret_errno;
 
2619
      perror_plus("Failed to lower privileges permanently");
 
2620
    }
2407
2621
  }
2408
2622
  
2409
2623
  free(interfaces_to_take_down);
2410
2624
  free(interfaces_hooks);
2411
2625
  
2412
2626
  /* Removes the GPGME temp directory and all files inside */
2413
 
  if(tempdir_created){
 
2627
  if(tempdir != NULL){
2414
2628
    struct dirent **direntries = NULL;
2415
 
    struct dirent *direntry = NULL;
2416
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2417
 
                             alphasort);
2418
 
    if (numentries > 0){
2419
 
      for(int i = 0; i < numentries; i++){
2420
 
        direntry = direntries[i];
2421
 
        char *fullname = NULL;
2422
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2423
 
                       direntry->d_name);
2424
 
        if(ret < 0){
2425
 
          perror_plus("asprintf");
2426
 
          continue;
2427
 
        }
2428
 
        ret = remove(fullname);
2429
 
        if(ret == -1){
2430
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2431
 
                       strerror(errno));
2432
 
        }
2433
 
        free(fullname);
 
2629
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2630
                                                  O_NOFOLLOW));
 
2631
    if(tempdir_fd == -1){
 
2632
      perror_plus("open");
 
2633
    } else {
 
2634
#ifdef __GLIBC__
 
2635
#if __GLIBC_PREREQ(2, 15)
 
2636
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2637
                                 notdotentries, alphasort);
 
2638
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2639
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2640
                               alphasort);
 
2641
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2642
#else   /* not __GLIBC__ */
 
2643
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2644
                               alphasort);
 
2645
#endif  /* not __GLIBC__ */
 
2646
      if(numentries >= 0){
 
2647
        for(int i = 0; i < numentries; i++){
 
2648
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2649
          if(ret == -1){
 
2650
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2651
                         " \"%s\", 0): %s\n", tempdir,
 
2652
                         direntries[i]->d_name, strerror(errno));
 
2653
          }
 
2654
          free(direntries[i]);
 
2655
        }
 
2656
        
 
2657
        /* need to clean even if 0 because man page doesn't specify */
 
2658
        free(direntries);
 
2659
        if(numentries == -1){
 
2660
          perror_plus("scandir");
 
2661
        }
 
2662
        ret = rmdir(tempdir);
 
2663
        if(ret == -1 and errno != ENOENT){
 
2664
          perror_plus("rmdir");
 
2665
        }
2434
2666
      }
2435
 
    }
2436
 
 
2437
 
    /* need to clean even if 0 because man page doesn't specify */
2438
 
    free(direntries);
2439
 
    if (numentries == -1){
2440
 
      perror_plus("scandir");
2441
 
    }
2442
 
    ret = rmdir(tempdir);
2443
 
    if(ret == -1 and errno != ENOENT){
2444
 
      perror_plus("rmdir");
 
2667
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2445
2668
    }
2446
2669
  }
2447
2670