/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2014-07-25 22:44:20 UTC
  • mto: This revision was merged to the branch mainline in revision 724.
  • Revision ID: teddy@recompile.se-20140725224420-4a5ct2ptt0hsc92z
Require Python 2.7.

This is in preparation for the eventual move to Python 3, which will
happen as soon as all Python modules required by Mandos are available.
The mandos-ctl and mandos-monitor programs are already portable
between Python 2.6 and Python 3 without changes; this change will
bring the requirement up to Python 2.7.

* INSTALL (Prerequisites/Libraries/Mandos Server): Document
                                                   requirement of
                                                   Python 2.7; remove
                                                   Python-argparse
                                                   which is in the
                                                   Python 2.7 standard
                                                   library.
* debian/control (Source: mandos/Build-Depends-Indep): Depend on
                                                       exactly the
                                                       python2.7
                                                       package and all
                                                       the Python 2.7
                                                       versions of the
                                                       python modules.
  (Package: mandos/Depends): - '' - but still depend on python (<=2.7)
                            and the generic versions of the Python
                            modules; this is for mandos-ctl and
                            mandos-monitor, both of which are
                            compatible with Python 3, and use
                            #!/usr/bin/python.
* mandos: Use #!/usr/bin/python2.7 instead of #!/usr/bin/python.

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,
88
89
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
89
90
                                   WEXITSTATUS(), WTERMSIG() */
90
91
#include <grp.h>                /* setgroups() */
 
92
#include <argz.h>               /* argz_add_sep(), argz_next(),
 
93
                                   argz_delete(), argz_append(),
 
94
                                   argz_stringify(), argz_add(),
 
95
                                   argz_count() */
 
96
#include <netdb.h>              /* getnameinfo(), NI_NUMERICHOST,
 
97
                                   EAI_SYSTEM, gai_strerror() */
91
98
 
92
99
#ifdef __linux__
93
100
#include <sys/klog.h>           /* klogctl() */
135
142
static const char sys_class_net[] = "/sys/class/net";
136
143
char *connect_to = NULL;
137
144
const char *hookdir = HOOKDIR;
 
145
int hookdir_fd = -1;
138
146
uid_t uid = 65534;
139
147
gid_t gid = 65534;
140
148
 
141
149
/* Doubly linked list that need to be circularly linked when used */
142
150
typedef struct server{
143
151
  const char *ip;
144
 
  uint16_t port;
 
152
  in_port_t port;
145
153
  AvahiIfIndex if_index;
146
154
  int af;
147
155
  struct timespec last_seen;
151
159
 
152
160
/* Used for passing in values through the Avahi callback functions */
153
161
typedef struct {
154
 
  AvahiSimplePoll *simple_poll;
155
162
  AvahiServer *server;
156
163
  gnutls_certificate_credentials_t cred;
157
164
  unsigned int dh_bits;
159
166
  const char *priority;
160
167
  gpgme_ctx_t ctx;
161
168
  server *current_server;
 
169
  char *interfaces;
 
170
  size_t interfaces_size;
162
171
} mandos_context;
163
172
 
164
 
/* global context so signal handler can reach it*/
165
 
mandos_context mc = { .simple_poll = NULL, .server = NULL,
166
 
                      .dh_bits = 1024, .priority = "SECURE256"
167
 
                      ":!CTYPE-X.509:+CTYPE-OPENPGP",
168
 
                      .current_server = NULL };
 
173
/* global so signal handler can reach it*/
 
174
AvahiSimplePoll *simple_poll;
169
175
 
170
176
sig_atomic_t quit_now = 0;
171
177
int signal_received = 0;
179
185
  perror(print_text);
180
186
}
181
187
 
182
 
__attribute__((format (gnu_printf, 2, 3)))
 
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
183
189
int fprintf_plus(FILE *stream, const char *format, ...){
184
190
  va_list ap;
185
191
  va_start (ap, format);
186
192
  
187
193
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
188
194
                             program_invocation_short_name));
189
 
  return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
195
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
190
196
}
191
197
 
192
198
/*
194
200
 * bytes. "buffer_capacity" is how much is currently allocated,
195
201
 * "buffer_length" is how much is already used.
196
202
 */
 
203
__attribute__((nonnull, warn_unused_result))
197
204
size_t incbuffer(char **buffer, size_t buffer_length,
198
205
                 size_t buffer_capacity){
199
206
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
200
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
201
 
    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;
202
213
      return 0;
203
214
    }
 
215
    *buffer = new_buf;
204
216
    buffer_capacity += BUFFER_SIZE;
205
217
  }
206
218
  return buffer_capacity;
207
219
}
208
220
 
209
221
/* Add server to set of servers to retry periodically */
210
 
bool add_server(const char *ip, uint16_t port, AvahiIfIndex if_index,
211
 
                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){
212
225
  int ret;
213
226
  server *new_server = malloc(sizeof(server));
214
227
  if(new_server == NULL){
221
234
                          .af = af };
222
235
  if(new_server->ip == NULL){
223
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);
224
252
    return false;
225
253
  }
226
254
  /* Special case of first server */
227
 
  if (mc.current_server == NULL){
 
255
  if(*current_server == NULL){
228
256
    new_server->next = new_server;
229
257
    new_server->prev = new_server;
230
 
    mc.current_server = new_server;
231
 
  /* Place the new server last in the list */
 
258
    *current_server = new_server;
232
259
  } else {
233
 
    new_server->next = mc.current_server;
234
 
    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;
235
263
    new_server->prev->next = new_server;
236
 
    mc.current_server->prev = new_server;
237
 
  }
238
 
  ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
239
 
  if(ret == -1){
240
 
    perror_plus("clock_gettime");
241
 
    return false;
 
264
    (*current_server)->prev = new_server;
242
265
  }
243
266
  return true;
244
267
}
246
269
/* 
247
270
 * Initialize GPGME.
248
271
 */
249
 
static bool init_gpgme(const char *seckey, const char *pubkey,
250
 
                       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){
251
277
  gpgme_error_t rc;
252
278
  gpgme_engine_info_t engine_info;
253
279
  
254
 
  
255
280
  /*
256
281
   * Helper function to insert pub and seckey to the engine keyring.
257
282
   */
258
 
  bool import_key(const char *filename){
 
283
  bool import_key(const char * const filename){
259
284
    int ret;
260
285
    int fd;
261
286
    gpgme_data_t pgp_data;
273
298
      return false;
274
299
    }
275
300
    
276
 
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
301
    rc = gpgme_op_import(mc->ctx, pgp_data);
277
302
    if(rc != GPG_ERR_NO_ERROR){
278
303
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
279
304
                   gpgme_strsource(rc), gpgme_strerror(rc));
323
348
  }
324
349
  
325
350
  /* Create new GPGME "context" */
326
 
  rc = gpgme_new(&(mc.ctx));
 
351
  rc = gpgme_new(&(mc->ctx));
327
352
  if(rc != GPG_ERR_NO_ERROR){
328
353
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
329
354
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
342
367
 * Decrypt OpenPGP data.
343
368
 * Returns -1 on error
344
369
 */
 
370
__attribute__((nonnull, warn_unused_result))
345
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
346
372
                                  size_t crypto_size,
347
 
                                  char **plaintext){
 
373
                                  char **plaintext,
 
374
                                  mandos_context *mc){
348
375
  gpgme_data_t dh_crypto, dh_plain;
349
376
  gpgme_error_t rc;
350
377
  ssize_t ret;
376
403
  
377
404
  /* Decrypt data from the cryptotext data buffer to the plaintext
378
405
     data buffer */
379
 
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
406
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
380
407
  if(rc != GPG_ERR_NO_ERROR){
381
408
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
382
409
                 gpgme_strsource(rc), gpgme_strerror(rc));
383
410
    plaintext_length = -1;
384
411
    if(debug){
385
412
      gpgme_decrypt_result_t result;
386
 
      result = gpgme_op_decrypt_result(mc.ctx);
 
413
      result = gpgme_op_decrypt_result(mc->ctx);
387
414
      if(result == NULL){
388
415
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
389
416
      } else {
466
493
  return plaintext_length;
467
494
}
468
495
 
469
 
static const char * safer_gnutls_strerror(int value){
470
 
  const char *ret = gnutls_strerror(value); /* Spurious warning from
471
 
                                               -Wunreachable-code */
 
496
__attribute__((warn_unused_result))
 
497
static const char *safer_gnutls_strerror(int value){
 
498
  const char *ret = gnutls_strerror(value);
472
499
  if(ret == NULL)
473
500
    ret = "(unknown)";
474
501
  return ret;
475
502
}
476
503
 
477
504
/* GnuTLS log function callback */
 
505
__attribute__((nonnull))
478
506
static void debuggnutls(__attribute__((unused)) int level,
479
507
                        const char* string){
480
508
  fprintf_plus(stderr, "GnuTLS: %s", string);
481
509
}
482
510
 
 
511
__attribute__((nonnull, warn_unused_result))
483
512
static int init_gnutls_global(const char *pubkeyfilename,
484
 
                              const char *seckeyfilename){
 
513
                              const char *seckeyfilename,
 
514
                              mandos_context *mc){
485
515
  int ret;
486
516
  
487
517
  if(debug){
504
534
  }
505
535
  
506
536
  /* OpenPGP credentials */
507
 
  ret = gnutls_certificate_allocate_credentials(&mc.cred);
 
537
  ret = gnutls_certificate_allocate_credentials(&mc->cred);
508
538
  if(ret != GNUTLS_E_SUCCESS){
509
539
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
510
540
                 safer_gnutls_strerror(ret));
520
550
  }
521
551
  
522
552
  ret = gnutls_certificate_set_openpgp_key_file
523
 
    (mc.cred, pubkeyfilename, seckeyfilename,
 
553
    (mc->cred, pubkeyfilename, seckeyfilename,
524
554
     GNUTLS_OPENPGP_FMT_BASE64);
525
555
  if(ret != GNUTLS_E_SUCCESS){
526
556
    fprintf_plus(stderr,
532
562
  }
533
563
  
534
564
  /* GnuTLS server initialization */
535
 
  ret = gnutls_dh_params_init(&mc.dh_params);
 
565
  ret = gnutls_dh_params_init(&mc->dh_params);
536
566
  if(ret != GNUTLS_E_SUCCESS){
537
567
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
538
568
                 " initialization: %s\n",
539
569
                 safer_gnutls_strerror(ret));
540
570
    goto globalfail;
541
571
  }
542
 
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
572
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
543
573
  if(ret != GNUTLS_E_SUCCESS){
544
574
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
545
575
                 safer_gnutls_strerror(ret));
546
576
    goto globalfail;
547
577
  }
548
578
  
549
 
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
579
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
550
580
  
551
581
  return 0;
552
582
  
553
583
 globalfail:
554
584
  
555
 
  gnutls_certificate_free_credentials(mc.cred);
 
585
  gnutls_certificate_free_credentials(mc->cred);
556
586
  gnutls_global_deinit();
557
 
  gnutls_dh_params_deinit(mc.dh_params);
 
587
  gnutls_dh_params_deinit(mc->dh_params);
558
588
  return -1;
559
589
}
560
590
 
561
 
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){
562
594
  int ret;
563
595
  /* GnuTLS session creation */
564
596
  do {
576
608
  {
577
609
    const char *err;
578
610
    do {
579
 
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
611
      ret = gnutls_priority_set_direct(*session, mc->priority, &err);
580
612
      if(quit_now){
581
613
        gnutls_deinit(*session);
582
614
        return -1;
593
625
  
594
626
  do {
595
627
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
596
 
                                 mc.cred);
 
628
                                 mc->cred);
597
629
    if(quit_now){
598
630
      gnutls_deinit(*session);
599
631
      return -1;
609
641
  /* ignore client certificate if any. */
610
642
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
611
643
  
612
 
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
644
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
613
645
  
614
646
  return 0;
615
647
}
619
651
                      __attribute__((unused)) const char *txt){}
620
652
 
621
653
/* Called when a Mandos server is found */
622
 
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,
623
656
                                      AvahiIfIndex if_index,
624
 
                                      int af){
 
657
                                      int af, mandos_context *mc){
625
658
  int ret, tcp_sd = -1;
626
659
  ssize_t sret;
627
 
  union {
628
 
    struct sockaddr_in in;
629
 
    struct sockaddr_in6 in6;
630
 
  } to;
 
660
  struct sockaddr_storage to;
631
661
  char *buffer = NULL;
632
662
  char *decrypted_buffer = NULL;
633
663
  size_t buffer_length = 0;
657
687
    return -1;
658
688
  }
659
689
  
660
 
  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);
661
723
  if(ret != 0){
662
724
    return -1;
663
725
  }
664
726
  
665
727
  if(debug){
666
728
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
667
 
                 PRIu16 "\n", ip, port);
 
729
                 PRIuMAX "\n", ip, (uintmax_t)port);
668
730
  }
669
731
  
670
732
  tcp_sd = socket(pf, SOCK_STREAM, 0);
682
744
  
683
745
  memset(&to, 0, sizeof(to));
684
746
  if(af == AF_INET6){
685
 
    to.in6.sin6_family = (sa_family_t)af;
686
 
    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);
687
749
  } else {                      /* IPv4 */
688
 
    to.in.sin_family = (sa_family_t)af;
689
 
    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);
690
752
  }
691
753
  if(ret < 0 ){
692
754
    int e = errno;
701
763
    goto mandos_end;
702
764
  }
703
765
  if(af == AF_INET6){
704
 
    to.in6.sin6_port = htons(port); /* Spurious warnings from
705
 
                                       -Wconversion and
706
 
                                       -Wunreachable-code */
707
 
    
708
 
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
709
 
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
710
 
                                -Wunreachable-code*/
 
766
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
 
767
    if(IN6_IS_ADDR_LINKLOCAL
 
768
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
711
769
      if(if_index == AVAHI_IF_UNSPEC){
712
770
        fprintf_plus(stderr, "An IPv6 link-local address is"
713
771
                     " incomplete without a network interface\n");
715
773
        goto mandos_end;
716
774
      }
717
775
      /* Set the network interface number as scope */
718
 
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
776
      ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
719
777
    }
720
778
  } else {
721
 
    to.in.sin_port = htons(port); /* Spurious warnings from
722
 
                                     -Wconversion and
723
 
                                     -Wunreachable-code */
 
779
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
724
780
  }
725
781
  
726
782
  if(quit_now){
734
790
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
735
791
        perror_plus("if_indextoname");
736
792
      } else {
737
 
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIu16
738
 
                     "\n", ip, interface, port);
 
793
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
 
794
                     "\n", ip, interface, (uintmax_t)port);
739
795
      }
740
796
    } else {
741
 
      fprintf_plus(stderr, "Connection to: %s, port %" PRIu16 "\n",
742
 
                   ip, port);
 
797
      fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
 
798
                   ip, (uintmax_t)port);
743
799
    }
744
800
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
745
801
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
746
 
    const char *pcret;
747
802
    if(af == AF_INET6){
748
 
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
749
 
                        sizeof(addrstr));
 
803
      ret = getnameinfo((struct sockaddr *)&to,
 
804
                        sizeof(struct sockaddr_in6),
 
805
                        addrstr, sizeof(addrstr), NULL, 0,
 
806
                        NI_NUMERICHOST);
750
807
    } else {
751
 
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
752
 
                        sizeof(addrstr));
 
808
      ret = getnameinfo((struct sockaddr *)&to,
 
809
                        sizeof(struct sockaddr_in),
 
810
                        addrstr, sizeof(addrstr), NULL, 0,
 
811
                        NI_NUMERICHOST);
753
812
    }
754
 
    if(pcret == NULL){
755
 
      perror_plus("inet_ntop");
756
 
    } else {
757
 
      if(strcmp(addrstr, ip) != 0){
758
 
        fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
759
 
      }
 
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);
760
819
    }
761
820
  }
762
821
  
766
825
  }
767
826
  
768
827
  if(af == AF_INET6){
769
 
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
828
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
829
                  sizeof(struct sockaddr_in6));
770
830
  } else {
771
 
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
831
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
832
                  sizeof(struct sockaddr_in));
772
833
  }
773
834
  if(ret < 0){
774
 
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
835
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
775
836
      int e = errno;
776
837
      perror_plus("connect");
777
838
      errno = e;
938
999
  if(buffer_length > 0){
939
1000
    ssize_t decrypted_buffer_size;
940
1001
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
941
 
                                               &decrypted_buffer);
 
1002
                                               &decrypted_buffer, mc);
942
1003
    if(decrypted_buffer_size >= 0){
943
1004
      
944
1005
      written = 0;
992
1053
  return retval;
993
1054
}
994
1055
 
 
1056
__attribute__((nonnull))
995
1057
static void resolve_callback(AvahiSServiceResolver *r,
996
1058
                             AvahiIfIndex interface,
997
1059
                             AvahiProtocol proto,
1005
1067
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
1006
1068
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
1007
1069
                             flags,
1008
 
                             AVAHI_GCC_UNUSED void* userdata){
1009
 
  assert(r);
 
1070
                             void *mc){
 
1071
  if(r == NULL){
 
1072
    return;
 
1073
  }
1010
1074
  
1011
1075
  /* Called whenever a service has been resolved successfully or
1012
1076
     timed out */
1013
1077
  
1014
1078
  if(quit_now){
 
1079
    avahi_s_service_resolver_free(r);
1015
1080
    return;
1016
1081
  }
1017
1082
  
1021
1086
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1022
1087
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
1023
1088
                 domain,
1024
 
                 avahi_strerror(avahi_server_errno(mc.server)));
 
1089
                 avahi_strerror(avahi_server_errno
 
1090
                                (((mandos_context*)mc)->server)));
1025
1091
    break;
1026
1092
    
1027
1093
  case AVAHI_RESOLVER_FOUND:
1033
1099
                     PRIdMAX ") on port %" PRIu16 "\n", name,
1034
1100
                     host_name, ip, (intmax_t)interface, port);
1035
1101
      }
1036
 
      int ret = start_mandos_communication(ip, port, interface,
1037
 
                                           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);
1038
1106
      if(ret == 0){
1039
 
        avahi_simple_poll_quit(mc.simple_poll);
 
1107
        avahi_simple_poll_quit(simple_poll);
1040
1108
      } else {
1041
 
        if(not add_server(ip, port, interface,
1042
 
                          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)){
1043
1112
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1044
1113
                       " list\n", name);
1045
1114
        }
1058
1127
                            const char *domain,
1059
1128
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
1060
1129
                            flags,
1061
 
                            AVAHI_GCC_UNUSED void* userdata){
1062
 
  assert(b);
 
1130
                            void *mc){
 
1131
  if(b == NULL){
 
1132
    return;
 
1133
  }
1063
1134
  
1064
1135
  /* Called whenever a new services becomes available on the LAN or
1065
1136
     is removed from the LAN */
1073
1144
  case AVAHI_BROWSER_FAILURE:
1074
1145
    
1075
1146
    fprintf_plus(stderr, "(Avahi browser) %s\n",
1076
 
                 avahi_strerror(avahi_server_errno(mc.server)));
1077
 
    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);
1078
1150
    return;
1079
1151
    
1080
1152
  case AVAHI_BROWSER_NEW:
1083
1155
       the callback function is called the Avahi server will free the
1084
1156
       resolver for us. */
1085
1157
    
1086
 
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1087
 
                                    name, type, domain, protocol, 0,
1088
 
                                    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)
1089
1162
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1090
1163
                   " %s\n", name,
1091
 
                   avahi_strerror(avahi_server_errno(mc.server)));
 
1164
                   avahi_strerror(avahi_server_errno
 
1165
                                  (((mandos_context*)mc)->server)));
1092
1166
    break;
1093
1167
    
1094
1168
  case AVAHI_BROWSER_REMOVE:
1113
1187
  signal_received = sig;
1114
1188
  int old_errno = errno;
1115
1189
  /* set main loop to exit */
1116
 
  if(mc.simple_poll != NULL){
1117
 
    avahi_simple_poll_quit(mc.simple_poll);
 
1190
  if(simple_poll != NULL){
 
1191
    avahi_simple_poll_quit(simple_poll);
1118
1192
  }
1119
1193
  errno = old_errno;
1120
1194
}
1121
1195
 
 
1196
__attribute__((nonnull, warn_unused_result))
1122
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1123
1198
  int ret;
 
1199
  error_t ret_errno;
1124
1200
  
1125
1201
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1126
1202
  if(s < 0){
 
1203
    ret_errno = errno;
1127
1204
    perror_plus("socket");
 
1205
    errno = ret_errno;
1128
1206
    return false;
1129
1207
  }
1130
1208
  strcpy(ifr->ifr_name, ifname);
1131
1209
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1132
1210
  if(ret == -1){
1133
1211
    if(debug){
 
1212
      ret_errno = errno;
1134
1213
      perror_plus("ioctl SIOCGIFFLAGS");
 
1214
      errno = ret_errno;
1135
1215
    }
1136
1216
    return false;
1137
1217
  }
1138
1218
  return true;
1139
1219
}
1140
1220
 
 
1221
__attribute__((nonnull, warn_unused_result))
1141
1222
bool good_flags(const char *ifname, const struct ifreq *ifr){
1142
1223
  
1143
1224
  /* Reject the loopback device */
1185
1266
 * corresponds to an acceptable network device.
1186
1267
 * (This function is passed to scandir(3) as a filter function.)
1187
1268
 */
 
1269
__attribute__((nonnull, warn_unused_result))
1188
1270
int good_interface(const struct dirent *if_entry){
1189
1271
  if(if_entry->d_name[0] == '.'){
1190
1272
    return 0;
1206
1288
}
1207
1289
 
1208
1290
/* 
1209
 
 * This function determines if a directory entry in /sys/class/net
1210
 
 * corresponds to an acceptable network device which is up.
1211
 
 * (This function is passed to scandir(3) as a filter function.)
1212
 
 */
1213
 
int up_interface(const struct dirent *if_entry){
1214
 
  if(if_entry->d_name[0] == '.'){
1215
 
    return 0;
1216
 
  }
1217
 
  
1218
 
  struct ifreq ifr;
1219
 
  if(not get_flags(if_entry->d_name, &ifr)){
1220
 
    if(debug){
1221
 
      fprintf_plus(stderr, "Failed to get flags for interface "
1222
 
                   "\"%s\"\n", if_entry->d_name);
1223
 
    }
1224
 
    return 0;
1225
 
  }
1226
 
  
1227
 
  /* Reject down interfaces */
1228
 
  if(not (ifr.ifr_flags & IFF_UP)){
1229
 
    if(debug){
1230
 
      fprintf_plus(stderr, "Rejecting down interface \"%s\"\n",
1231
 
                   if_entry->d_name);
1232
 
    }
1233
 
    return 0;
1234
 
  }
1235
 
  
1236
 
  /* Reject non-running interfaces */
1237
 
  if(not (ifr.ifr_flags & IFF_RUNNING)){
1238
 
    if(debug){
1239
 
      fprintf_plus(stderr, "Rejecting non-running interface \"%s\"\n",
1240
 
                   if_entry->d_name);
1241
 
    }
1242
 
    return 0;
1243
 
  }
1244
 
  
1245
 
  if(not good_flags(if_entry->d_name, &ifr)){
1246
 
    return 0;
1247
 
  }
1248
 
  return 1;
1249
 
}
1250
 
 
 
1291
 * This function determines if a network interface is up.
 
1292
 */
 
1293
__attribute__((nonnull, warn_unused_result))
 
1294
bool interface_is_up(const char *interface){
 
1295
  struct ifreq ifr;
 
1296
  if(not get_flags(interface, &ifr)){
 
1297
    if(debug){
 
1298
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1299
                   "\"%s\"\n", interface);
 
1300
    }
 
1301
    return false;
 
1302
  }
 
1303
  
 
1304
  return (bool)(ifr.ifr_flags & IFF_UP);
 
1305
}
 
1306
 
 
1307
/* 
 
1308
 * This function determines if a network interface is running
 
1309
 */
 
1310
__attribute__((nonnull, warn_unused_result))
 
1311
bool interface_is_running(const char *interface){
 
1312
  struct ifreq ifr;
 
1313
  if(not get_flags(interface, &ifr)){
 
1314
    if(debug){
 
1315
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1316
                   "\"%s\"\n", interface);
 
1317
    }
 
1318
    return false;
 
1319
  }
 
1320
  
 
1321
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
 
1322
}
 
1323
 
 
1324
__attribute__((nonnull, pure, warn_unused_result))
1251
1325
int notdotentries(const struct dirent *direntry){
1252
1326
  /* Skip "." and ".." */
1253
1327
  if(direntry->d_name[0] == '.'
1260
1334
}
1261
1335
 
1262
1336
/* Is this directory entry a runnable program? */
 
1337
__attribute__((nonnull, warn_unused_result))
1263
1338
int runnable_hook(const struct dirent *direntry){
1264
1339
  int ret;
1265
1340
  size_t sret;
1273
1348
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1274
1349
                "abcdefghijklmnopqrstuvwxyz"
1275
1350
                "0123456789"
1276
 
                "_-");
 
1351
                "_.-");
1277
1352
  if((direntry->d_name)[sret] != '\0'){
1278
1353
    /* Contains non-allowed characters */
1279
1354
    if(debug){
1283
1358
    return 0;
1284
1359
  }
1285
1360
  
1286
 
  char *fullname = NULL;
1287
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1288
 
  if(ret < 0){
1289
 
    perror_plus("asprintf");
1290
 
    return 0;
1291
 
  }
1292
 
  
1293
 
  ret = stat(fullname, &st);
 
1361
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1294
1362
  if(ret == -1){
1295
1363
    if(debug){
1296
1364
      perror_plus("Could not stat hook");
1320
1388
  return 1;
1321
1389
}
1322
1390
 
1323
 
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){
1324
1394
  int ret;
1325
1395
  struct timespec now;
1326
1396
  struct timespec waited_time;
1327
1397
  intmax_t block_time;
1328
1398
  
1329
1399
  while(true){
1330
 
    if(mc.current_server == NULL){
1331
 
      if (debug){
 
1400
    if(mc->current_server == NULL){
 
1401
      if(debug){
1332
1402
        fprintf_plus(stderr, "Wait until first server is found."
1333
1403
                     " No timeout!\n");
1334
1404
      }
1335
1405
      ret = avahi_simple_poll_iterate(s, -1);
1336
1406
    } else {
1337
 
      if (debug){
 
1407
      if(debug){
1338
1408
        fprintf_plus(stderr, "Check current_server if we should run"
1339
1409
                     " it, or wait\n");
1340
1410
      }
1347
1417
      /* Calculating in ms how long time between now and server
1348
1418
         who we visted longest time ago. Now - last seen.  */
1349
1419
      waited_time.tv_sec = (now.tv_sec
1350
 
                            - mc.current_server->last_seen.tv_sec);
 
1420
                            - mc->current_server->last_seen.tv_sec);
1351
1421
      waited_time.tv_nsec = (now.tv_nsec
1352
 
                             - mc.current_server->last_seen.tv_nsec);
 
1422
                             - mc->current_server->last_seen.tv_nsec);
1353
1423
      /* total time is 10s/10,000ms.
1354
1424
         Converting to s from ms by dividing by 1,000,
1355
1425
         and ns to ms by dividing by 1,000,000. */
1357
1427
                     - ((intmax_t)waited_time.tv_sec * 1000))
1358
1428
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1359
1429
      
1360
 
      if (debug){
 
1430
      if(debug){
1361
1431
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1362
1432
                     block_time);
1363
1433
      }
1364
1434
      
1365
1435
      if(block_time <= 0){
1366
 
        ret = start_mandos_communication(mc.current_server->ip,
1367
 
                                         mc.current_server->port,
1368
 
                                         mc.current_server->if_index,
1369
 
                                         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);
1370
1440
        if(ret == 0){
1371
 
          avahi_simple_poll_quit(mc.simple_poll);
 
1441
          avahi_simple_poll_quit(s);
1372
1442
          return 0;
1373
1443
        }
1374
1444
        ret = clock_gettime(CLOCK_MONOTONIC,
1375
 
                            &mc.current_server->last_seen);
 
1445
                            &mc->current_server->last_seen);
1376
1446
        if(ret == -1){
1377
1447
          perror_plus("clock_gettime");
1378
1448
          return -1;
1379
1449
        }
1380
 
        mc.current_server = mc.current_server->next;
 
1450
        mc->current_server = mc->current_server->next;
1381
1451
        block_time = 0;         /* Call avahi to find new Mandos
1382
1452
                                   servers, but don't block */
1383
1453
      }
1385
1455
      ret = avahi_simple_poll_iterate(s, (int)block_time);
1386
1456
    }
1387
1457
    if(ret != 0){
1388
 
      if (ret > 0 or errno != EINTR){
 
1458
      if(ret > 0 or errno != EINTR){
1389
1459
        return (ret != 1) ? ret : 0;
1390
1460
      }
1391
1461
    }
1393
1463
}
1394
1464
 
1395
1465
/* Set effective uid to 0, return errno */
1396
 
int raise_privileges(void){
1397
 
  int old_errno = errno;
1398
 
  int ret_errno = 0;
1399
 
  errno = 0;
 
1466
__attribute__((warn_unused_result))
 
1467
error_t raise_privileges(void){
 
1468
  error_t old_errno = errno;
 
1469
  error_t ret_errno = 0;
1400
1470
  if(seteuid(0) == -1){
1401
 
    perror_plus("seteuid");
 
1471
    ret_errno = errno;
1402
1472
  }
1403
 
  ret_errno = errno;
1404
1473
  errno = old_errno;
1405
1474
  return ret_errno;
1406
1475
}
1407
1476
 
1408
1477
/* Set effective and real user ID to 0.  Return errno. */
1409
 
int raise_privileges_permanently(void){
1410
 
  int old_errno = errno;
1411
 
  int ret_errno = raise_privileges();
 
1478
__attribute__((warn_unused_result))
 
1479
error_t raise_privileges_permanently(void){
 
1480
  error_t old_errno = errno;
 
1481
  error_t ret_errno = raise_privileges();
1412
1482
  if(ret_errno != 0){
1413
1483
    errno = old_errno;
1414
1484
    return ret_errno;
1415
1485
  }
1416
 
  errno = 0;
1417
1486
  if(setuid(0) == -1){
1418
 
    perror_plus("seteuid");
 
1487
    ret_errno = errno;
1419
1488
  }
1420
 
  ret_errno = errno;
1421
1489
  errno = old_errno;
1422
1490
  return ret_errno;
1423
1491
}
1424
1492
 
1425
1493
/* Set effective user ID to unprivileged saved user ID */
1426
 
int lower_privileges(void){
1427
 
  int old_errno = errno;
1428
 
  int ret_errno = 0;
1429
 
  errno = 0;
 
1494
__attribute__((warn_unused_result))
 
1495
error_t lower_privileges(void){
 
1496
  error_t old_errno = errno;
 
1497
  error_t ret_errno = 0;
1430
1498
  if(seteuid(uid) == -1){
1431
 
    perror_plus("seteuid");
1432
 
  }
1433
 
  ret_errno = errno;
1434
 
  errno = old_errno;
1435
 
  return ret_errno;
1436
 
}
1437
 
 
1438
 
bool run_network_hooks(const char *mode, const char *interface,
 
1499
    ret_errno = errno;
 
1500
  }
 
1501
  errno = old_errno;
 
1502
  return ret_errno;
 
1503
}
 
1504
 
 
1505
/* Lower privileges permanently */
 
1506
__attribute__((warn_unused_result))
 
1507
error_t lower_privileges_permanently(void){
 
1508
  error_t old_errno = errno;
 
1509
  error_t ret_errno = 0;
 
1510
  if(setuid(uid) == -1){
 
1511
    ret_errno = errno;
 
1512
  }
 
1513
  errno = old_errno;
 
1514
  return ret_errno;
 
1515
}
 
1516
 
 
1517
__attribute__((nonnull))
 
1518
void run_network_hooks(const char *mode, const char *interface,
1439
1519
                       const float delay){
1440
 
  struct dirent **direntries;
1441
 
  struct dirent *direntry;
1442
 
  int ret;
1443
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1444
 
                         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__ */
1445
1547
  if(numhooks == -1){
1446
1548
    perror_plus("scandir");
1447
 
  } else {
1448
 
    int devnull = open("/dev/null", O_RDONLY);
1449
 
    for(int i = 0; i < numhooks; i++){
1450
 
      direntry = direntries[i];
1451
 
      char *fullname = NULL;
1452
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1453
 
      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){
1454
1621
        perror_plus("asprintf");
1455
 
        continue;
1456
 
      }
1457
 
      if(debug){
1458
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1459
 
                     direntry->d_name);
1460
 
      }
1461
 
      pid_t hook_pid = fork();
1462
 
      if(hook_pid == 0){
1463
 
        /* Child */
1464
 
        /* Raise privileges */
1465
 
        raise_privileges_permanently();
1466
 
        /* Set group */
1467
 
        errno = 0;
1468
 
        ret = setgid(0);
1469
 
        if(ret == -1){
1470
 
          perror_plus("setgid");
1471
 
        }
1472
 
        /* Reset supplementary groups */
1473
 
        errno = 0;
1474
 
        ret = setgroups(0, NULL);
1475
 
        if(ret == -1){
1476
 
          perror_plus("setgroups");
1477
 
        }
1478
 
        dup2(devnull, STDIN_FILENO);
1479
 
        close(devnull);
1480
 
        dup2(STDERR_FILENO, STDOUT_FILENO);
1481
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1482
 
        if(ret == -1){
1483
 
          perror_plus("setenv");
1484
 
          _exit(EX_OSERR);
1485
 
        }
1486
 
        ret = setenv("DEVICE", interface, 1);
1487
 
        if(ret == -1){
1488
 
          perror_plus("setenv");
1489
 
          _exit(EX_OSERR);
1490
 
        }
1491
 
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1492
 
        if(ret == -1){
1493
 
          perror_plus("setenv");
1494
 
          _exit(EX_OSERR);
1495
 
        }
1496
 
        ret = setenv("MODE", mode, 1);
1497
 
        if(ret == -1){
1498
 
          perror_plus("setenv");
1499
 
          _exit(EX_OSERR);
1500
 
        }
1501
 
        char *delaystring;
1502
 
        ret = asprintf(&delaystring, "%f", delay);
1503
 
        if(ret == -1){
1504
 
          perror_plus("asprintf");
1505
 
          _exit(EX_OSERR);
1506
 
        }
1507
 
        ret = setenv("DELAY", delaystring, 1);
1508
 
        if(ret == -1){
1509
 
          free(delaystring);
1510
 
          perror_plus("setenv");
1511
 
          _exit(EX_OSERR);
1512
 
        }
 
1622
        _exit(EX_OSERR);
 
1623
      }
 
1624
      ret = setenv("DELAY", delaystring, 1);
 
1625
      if(ret == -1){
1513
1626
        free(delaystring);
1514
 
        if(connect_to != NULL){
1515
 
          ret = setenv("CONNECT", connect_to, 1);
1516
 
          if(ret == -1){
1517
 
            perror_plus("setenv");
1518
 
            _exit(EX_OSERR);
1519
 
          }
1520
 
        }
1521
 
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1522
 
          perror_plus("execl");
1523
 
          _exit(EXIT_FAILURE);
1524
 
        }
 
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
      int status;
 
1654
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1655
        perror_plus("waitpid");
 
1656
        free(direntry);
 
1657
        continue;
 
1658
      }
 
1659
      if(WIFEXITED(status)){
 
1660
        if(WEXITSTATUS(status) != 0){
 
1661
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1662
                       " with status %d\n", direntry->d_name,
 
1663
                       WEXITSTATUS(status));
 
1664
          free(direntry);
 
1665
          continue;
 
1666
        }
 
1667
      } else if(WIFSIGNALED(status)){
 
1668
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1669
                     " signal %d\n", direntry->d_name,
 
1670
                     WTERMSIG(status));
 
1671
        free(direntry);
 
1672
        continue;
1525
1673
      } else {
1526
 
        int status;
1527
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1528
 
          perror_plus("waitpid");
1529
 
          free(fullname);
1530
 
          continue;
1531
 
        }
1532
 
        if(WIFEXITED(status)){
1533
 
          if(WEXITSTATUS(status) != 0){
1534
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1535
 
                         " with status %d\n", direntry->d_name,
1536
 
                         WEXITSTATUS(status));
1537
 
            free(fullname);
1538
 
            continue;
1539
 
          }
1540
 
        } else if(WIFSIGNALED(status)){
1541
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1542
 
                       " signal %d\n", direntry->d_name,
1543
 
                       WTERMSIG(status));
1544
 
          free(fullname);
1545
 
          continue;
1546
 
        } else {
1547
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1548
 
                       " crashed\n", direntry->d_name);
1549
 
          free(fullname);
1550
 
          continue;
1551
 
        }
1552
 
      }
1553
 
      free(fullname);
1554
 
      if(debug){
1555
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1556
 
                     direntry->d_name);
1557
 
      }
1558
 
    }
1559
 
    close(devnull);
1560
 
  }
1561
 
  return true;
 
1674
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1675
                     " crashed\n", direntry->d_name);
 
1676
        free(direntry);
 
1677
        continue;
 
1678
      }
 
1679
    }
 
1680
    if(debug){
 
1681
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1682
                   direntry->d_name);
 
1683
    }
 
1684
    free(direntry);
 
1685
  }
 
1686
  free(direntries);
 
1687
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1688
    perror_plus("close");
 
1689
  } else {
 
1690
    hookdir_fd = -1;
 
1691
  }
 
1692
  close(devnull);
1562
1693
}
1563
1694
 
1564
 
int bring_up_interface(const char * const interface, const float delay){
1565
 
  int sd = -1;
 
1695
__attribute__((nonnull, warn_unused_result))
 
1696
error_t bring_up_interface(const char *const interface,
 
1697
                           const float delay){
 
1698
  error_t old_errno = errno;
1566
1699
  int ret;
1567
1700
  struct ifreq network;
1568
 
  AvahiIfIndex if_index = (AvahiIfIndex)if_nametoindex(interface);
 
1701
  unsigned int if_index = if_nametoindex(interface);
1569
1702
  if(if_index == 0){
1570
1703
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1571
 
    return EX_UNAVAILABLE;
 
1704
    errno = old_errno;
 
1705
    return ENXIO;
1572
1706
  }
1573
1707
  
1574
1708
  if(quit_now){
 
1709
    errno = old_errno;
1575
1710
    return EINTR;
1576
1711
  }
1577
1712
  
1578
 
  /* Re-raise priviliges */
1579
 
  raise_privileges();
1580
 
    
1581
 
#ifdef __linux__
1582
 
  /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1583
 
     messages about the network interface to mess up the prompt */
1584
 
  ret = klogctl(8, NULL, 5);
1585
 
  bool restore_loglevel = true;
1586
 
  if(ret == -1){
1587
 
    restore_loglevel = false;
1588
 
    perror_plus("klogctl");
1589
 
  }
1590
 
#endif  /* __linux__ */
1591
 
    
1592
 
  sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1593
 
  if(sd < 0){
1594
 
    perror_plus("socket");
1595
 
#ifdef __linux__
1596
 
    if(restore_loglevel){
1597
 
      ret = klogctl(7, NULL, 0);
1598
 
      if(ret == -1){
1599
 
        perror_plus("klogctl");
1600
 
      }
1601
 
    }
1602
 
#endif  /* __linux__ */
1603
 
    /* Lower privileges */
1604
 
    lower_privileges();
1605
 
    return EX_OSERR;
1606
 
;
1607
 
  }
1608
 
  strcpy(network.ifr_name, interface);
1609
 
  ret = ioctl(sd, SIOCGIFFLAGS, &network);
1610
 
  if(ret == -1){
1611
 
    perror_plus("ioctl SIOCGIFFLAGS");
1612
 
#ifdef __linux__
1613
 
    if(restore_loglevel){
1614
 
      ret = klogctl(7, NULL, 0);
1615
 
      if(ret == -1){
1616
 
        perror_plus("klogctl");
1617
 
      }
1618
 
    }
1619
 
#endif  /* __linux__ */
1620
 
    /* Lower privileges */
1621
 
    lower_privileges();
1622
 
    return EX_OSERR;
1623
 
  }
1624
 
  if((network.ifr_flags & IFF_UP) == 0){
1625
 
    network.ifr_flags |= IFF_UP;
1626
 
    ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1713
  if(not interface_is_up(interface)){
 
1714
    error_t ret_errno = 0, ioctl_errno = 0;
 
1715
    if(not get_flags(interface, &network)){
 
1716
      ret_errno = errno;
 
1717
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1718
                   "\"%s\"\n", interface);
 
1719
      errno = old_errno;
 
1720
      return ret_errno;
 
1721
    }
 
1722
    network.ifr_flags |= IFF_UP; /* set flag */
 
1723
    
 
1724
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1725
    if(sd == -1){
 
1726
      ret_errno = errno;
 
1727
      perror_plus("socket");
 
1728
      errno = old_errno;
 
1729
      return ret_errno;
 
1730
    }
 
1731
    
 
1732
    if(quit_now){
 
1733
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1734
      if(ret == -1){
 
1735
        perror_plus("close");
 
1736
      }
 
1737
      errno = old_errno;
 
1738
      return EINTR;
 
1739
    }
 
1740
    
 
1741
    if(debug){
 
1742
      fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
 
1743
                   interface);
 
1744
    }
 
1745
    
 
1746
    /* Raise privileges */
 
1747
    ret_errno = raise_privileges();
 
1748
    if(ret_errno != 0){
 
1749
      errno = ret_errno;
 
1750
      perror_plus("Failed to raise privileges");
 
1751
    }
 
1752
    
 
1753
#ifdef __linux__
 
1754
    int ret_linux;
 
1755
    bool restore_loglevel = false;
 
1756
    if(ret_errno == 0){
 
1757
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1758
         messages about the network interface to mess up the prompt */
 
1759
      ret_linux = klogctl(8, NULL, 5);
 
1760
      if(ret_linux == -1){
 
1761
        perror_plus("klogctl");
 
1762
      } else {
 
1763
        restore_loglevel = true;
 
1764
      }
 
1765
    }
 
1766
#endif  /* __linux__ */
 
1767
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1768
    ioctl_errno = errno;
 
1769
#ifdef __linux__
 
1770
    if(restore_loglevel){
 
1771
      ret_linux = klogctl(7, NULL, 0);
 
1772
      if(ret_linux == -1){
 
1773
        perror_plus("klogctl");
 
1774
      }
 
1775
    }
 
1776
#endif  /* __linux__ */
 
1777
    
 
1778
    /* If raise_privileges() succeeded above */
 
1779
    if(ret_errno == 0){
 
1780
      /* Lower privileges */
 
1781
      ret_errno = lower_privileges();
 
1782
      if(ret_errno != 0){
 
1783
        errno = ret_errno;
 
1784
        perror_plus("Failed to lower privileges");
 
1785
      }
 
1786
    }
 
1787
    
 
1788
    /* Close the socket */
 
1789
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
1627
1790
    if(ret == -1){
 
1791
      perror_plus("close");
 
1792
    }
 
1793
    
 
1794
    if(ret_setflags == -1){
 
1795
      errno = ioctl_errno;
1628
1796
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1629
 
#ifdef __linux__
1630
 
      if(restore_loglevel){
1631
 
        ret = klogctl(7, NULL, 0);
1632
 
        if(ret == -1){
1633
 
          perror_plus("klogctl");
1634
 
        }
1635
 
      }
1636
 
#endif  /* __linux__ */
1637
 
        /* Lower privileges */
1638
 
      lower_privileges();
1639
 
      return EX_OSERR;
 
1797
      errno = old_errno;
 
1798
      return ioctl_errno;
1640
1799
    }
 
1800
  } else if(debug){
 
1801
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
 
1802
                 interface);
1641
1803
  }
 
1804
  
1642
1805
  /* Sleep checking until interface is running.
1643
1806
     Check every 0.25s, up to total time of delay */
1644
1807
  for(int i=0; i < delay * 4; i++){
1645
 
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
1646
 
    if(ret == -1){
1647
 
      perror_plus("ioctl SIOCGIFFLAGS");
1648
 
    } else if(network.ifr_flags & IFF_RUNNING){
 
1808
    if(interface_is_running(interface)){
1649
1809
      break;
1650
1810
    }
1651
1811
    struct timespec sleeptime = { .tv_nsec = 250000000 };
1654
1814
      perror_plus("nanosleep");
1655
1815
    }
1656
1816
  }
1657
 
  /* Close the socket */
1658
 
  ret = (int)TEMP_FAILURE_RETRY(close(sd));
1659
 
  if(ret == -1){
1660
 
    perror_plus("close");
 
1817
  
 
1818
  errno = old_errno;
 
1819
  return 0;
 
1820
}
 
1821
 
 
1822
__attribute__((nonnull, warn_unused_result))
 
1823
error_t take_down_interface(const char *const interface){
 
1824
  error_t old_errno = errno;
 
1825
  struct ifreq network;
 
1826
  unsigned int if_index = if_nametoindex(interface);
 
1827
  if(if_index == 0){
 
1828
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
1829
    errno = old_errno;
 
1830
    return ENXIO;
1661
1831
  }
1662
 
#ifdef __linux__
1663
 
  if(restore_loglevel){
1664
 
    /* Restores kernel loglevel to default */
1665
 
    ret = klogctl(7, NULL, 0);
 
1832
  if(interface_is_up(interface)){
 
1833
    error_t ret_errno = 0, ioctl_errno = 0;
 
1834
    if(not get_flags(interface, &network) and debug){
 
1835
      ret_errno = errno;
 
1836
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1837
                   "\"%s\"\n", interface);
 
1838
      errno = old_errno;
 
1839
      return ret_errno;
 
1840
    }
 
1841
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
 
1842
    
 
1843
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1844
    if(sd == -1){
 
1845
      ret_errno = errno;
 
1846
      perror_plus("socket");
 
1847
      errno = old_errno;
 
1848
      return ret_errno;
 
1849
    }
 
1850
    
 
1851
    if(debug){
 
1852
      fprintf_plus(stderr, "Taking down interface \"%s\"\n",
 
1853
                   interface);
 
1854
    }
 
1855
    
 
1856
    /* Raise privileges */
 
1857
    ret_errno = raise_privileges();
 
1858
    if(ret_errno != 0){
 
1859
      errno = ret_errno;
 
1860
      perror_plus("Failed to raise privileges");
 
1861
    }
 
1862
    
 
1863
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1864
    ioctl_errno = errno;
 
1865
    
 
1866
    /* If raise_privileges() succeeded above */
 
1867
    if(ret_errno == 0){
 
1868
      /* Lower privileges */
 
1869
      ret_errno = lower_privileges();
 
1870
      if(ret_errno != 0){
 
1871
        errno = ret_errno;
 
1872
        perror_plus("Failed to lower privileges");
 
1873
      }
 
1874
    }
 
1875
    
 
1876
    /* Close the socket */
 
1877
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1666
1878
    if(ret == -1){
1667
 
      perror_plus("klogctl");
1668
 
    }
 
1879
      perror_plus("close");
 
1880
    }
 
1881
    
 
1882
    if(ret_setflags == -1){
 
1883
      errno = ioctl_errno;
 
1884
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
 
1885
      errno = old_errno;
 
1886
      return ioctl_errno;
 
1887
    }
 
1888
  } else if(debug){
 
1889
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
 
1890
                 interface);
1669
1891
  }
1670
 
#endif  /* __linux__ */
1671
 
  /* Lower privileges */
1672
 
  lower_privileges();
1673
 
  return errno;
 
1892
  
 
1893
  errno = old_errno;
 
1894
  return 0;
1674
1895
}
1675
1896
 
1676
1897
int main(int argc, char *argv[]){
 
1898
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
 
1899
                        .priority = "SECURE256:!CTYPE-X.509:"
 
1900
                        "+CTYPE-OPENPGP", .current_server = NULL,
 
1901
                        .interfaces = NULL, .interfaces_size = 0 };
1677
1902
  AvahiSServiceBrowser *sb = NULL;
1678
 
  int error;
 
1903
  error_t ret_errno;
1679
1904
  int ret;
1680
1905
  intmax_t tmpmax;
1681
1906
  char *tmp;
1682
1907
  int exitcode = EXIT_SUCCESS;
1683
 
  const char *interface = "";
1684
 
  struct ifreq network;
1685
 
  int sd = -1;
1686
 
  bool take_down_interface = false;
1687
 
  char tempdir[] = "/tmp/mandosXXXXXX";
1688
 
  bool tempdir_created = false;
 
1908
  char *interfaces_to_take_down = NULL;
 
1909
  size_t interfaces_to_take_down_size = 0;
 
1910
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
1911
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
1912
  char *tempdir = NULL;
1689
1913
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1690
1914
  const char *seckey = PATHDIR "/" SECKEY;
1691
1915
  const char *pubkey = PATHDIR "/" PUBKEY;
 
1916
  char *interfaces_hooks = NULL;
1692
1917
  
1693
1918
  bool gnutls_initialized = false;
1694
1919
  bool gpgme_initialized = false;
1785
2010
        connect_to = arg;
1786
2011
        break;
1787
2012
      case 'i':                 /* --interface */
1788
 
        interface = arg;
 
2013
        ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
 
2014
                                 arg, (int)',');
 
2015
        if(ret_errno != 0){
 
2016
          argp_error(state, "%s", strerror(ret_errno));
 
2017
        }
1789
2018
        break;
1790
2019
      case 's':                 /* --seckey */
1791
2020
        seckey = arg;
1868
2097
    /* Work around Debian bug #633582:
1869
2098
       <http://bugs.debian.org/633582> */
1870
2099
    
1871
 
    /* Re-raise priviliges */
1872
 
    if(raise_privileges() == 0){
 
2100
    /* Re-raise privileges */
 
2101
    ret_errno = raise_privileges();
 
2102
    if(ret_errno != 0){
 
2103
      errno = ret_errno;
 
2104
      perror_plus("Failed to raise privileges");
 
2105
    } else {
1873
2106
      struct stat st;
1874
2107
      
1875
2108
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1915
2148
      }
1916
2149
    
1917
2150
      /* Lower privileges */
1918
 
      errno = 0;
1919
 
      ret = seteuid(uid);
1920
 
      if(ret == -1){
1921
 
        perror_plus("seteuid");
 
2151
      ret_errno = lower_privileges();
 
2152
      if(ret_errno != 0){
 
2153
        errno = ret_errno;
 
2154
        perror_plus("Failed to lower privileges");
 
2155
      }
 
2156
    }
 
2157
  }
 
2158
  
 
2159
  /* Remove invalid interface names (except "none") */
 
2160
  {
 
2161
    char *interface = NULL;
 
2162
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
2163
                                 interface))){
 
2164
      if(strcmp(interface, "none") != 0
 
2165
         and if_nametoindex(interface) == 0){
 
2166
        if(interface[0] != '\0'){
 
2167
          fprintf_plus(stderr, "Not using nonexisting interface"
 
2168
                       " \"%s\"\n", interface);
 
2169
        }
 
2170
        argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
 
2171
        interface = NULL;
1922
2172
      }
1923
2173
    }
1924
2174
  }
1925
2175
  
1926
2176
  /* Run network hooks */
1927
 
  if(not run_network_hooks("start", interface, delay)){
1928
 
    goto end;
 
2177
  {
 
2178
    if(mc.interfaces != NULL){
 
2179
      interfaces_hooks = malloc(mc.interfaces_size);
 
2180
      if(interfaces_hooks == NULL){
 
2181
        perror_plus("malloc");
 
2182
        goto end;
 
2183
      }
 
2184
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
 
2185
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
 
2186
    }
 
2187
    run_network_hooks("start", interfaces_hooks != NULL ?
 
2188
                      interfaces_hooks : "", delay);
1929
2189
  }
1930
2190
  
1931
2191
  if(not debug){
1932
2192
    avahi_set_log_function(empty_log);
1933
2193
  }
1934
2194
  
1935
 
  if(interface[0] == '\0'){
1936
 
    struct dirent **direntries;
1937
 
    /* First look for interfaces that are up */
1938
 
    ret = scandir(sys_class_net, &direntries, up_interface,
1939
 
                  alphasort);
1940
 
    if(ret == 0){
1941
 
      /* No up interfaces, look for any good interfaces */
1942
 
      free(direntries);
1943
 
      ret = scandir(sys_class_net, &direntries, good_interface,
1944
 
                    alphasort);
1945
 
    }
1946
 
    if(ret >= 1){
1947
 
      /* Pick the first interface returned */
1948
 
      interface = strdup(direntries[0]->d_name);
1949
 
      if(debug){
1950
 
        fprintf_plus(stderr, "Using interface \"%s\"\n", interface);
1951
 
      }
1952
 
      if(interface == NULL){
1953
 
        perror_plus("malloc");
1954
 
        free(direntries);
1955
 
        exitcode = EXIT_FAILURE;
1956
 
        goto end;
1957
 
      }
1958
 
      free(direntries);
1959
 
    } else {
1960
 
      free(direntries);
1961
 
      fprintf_plus(stderr, "Could not find a network interface\n");
1962
 
      exitcode = EXIT_FAILURE;
1963
 
      goto end;
1964
 
    }
1965
 
  }
1966
 
  
1967
2195
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
1968
2196
     from the signal handler */
1969
2197
  /* Initialize the pseudo-RNG for Avahi */
1970
2198
  srand((unsigned int) time(NULL));
1971
 
  mc.simple_poll = avahi_simple_poll_new();
1972
 
  if(mc.simple_poll == NULL){
 
2199
  simple_poll = avahi_simple_poll_new();
 
2200
  if(simple_poll == NULL){
1973
2201
    fprintf_plus(stderr,
1974
2202
                 "Avahi: Failed to create simple poll object.\n");
1975
2203
    exitcode = EX_UNAVAILABLE;
2039
2267
    }
2040
2268
  }
2041
2269
  
2042
 
  /* If the interface is down, bring it up */
2043
 
  if((interface[0] != '\0') and (strcmp(interface, "none") != 0)){
2044
 
    ret = bring_up_interface(interface, delay);
2045
 
    if(ret){
2046
 
      errno = ret;
2047
 
      perror_plus("Failed to bring up interface");
2048
 
    }
 
2270
  /* If no interfaces were specified, make a list */
 
2271
  if(mc.interfaces == NULL){
 
2272
    struct dirent **direntries = NULL;
 
2273
    /* Look for any good interfaces */
 
2274
    ret = scandir(sys_class_net, &direntries, good_interface,
 
2275
                  alphasort);
 
2276
    if(ret >= 1){
 
2277
      /* Add all found interfaces to interfaces list */
 
2278
      for(int i = 0; i < ret; ++i){
 
2279
        ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
 
2280
                             direntries[i]->d_name);
 
2281
        if(ret_errno != 0){
 
2282
          errno = ret_errno;
 
2283
          perror_plus("argz_add");
 
2284
          free(direntries[i]);
 
2285
          continue;
 
2286
        }
 
2287
        if(debug){
 
2288
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
 
2289
                       direntries[i]->d_name);
 
2290
        }
 
2291
        free(direntries[i]);
 
2292
      }
 
2293
      free(direntries);
 
2294
    } else {
 
2295
      if(ret == 0){
 
2296
        free(direntries);
 
2297
      }
 
2298
      fprintf_plus(stderr, "Could not find a network interface\n");
 
2299
      exitcode = EXIT_FAILURE;
 
2300
      goto end;
 
2301
    }
 
2302
  }
 
2303
  
 
2304
  /* Bring up interfaces which are down, and remove any "none"s */
 
2305
  {
 
2306
    char *interface = NULL;
 
2307
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
2308
                                 interface))){
 
2309
      /* If interface name is "none", stop bringing up interfaces.
 
2310
         Also remove all instances of "none" from the list */
 
2311
      if(strcmp(interface, "none") == 0){
 
2312
        argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2313
                    interface);
 
2314
        interface = NULL;
 
2315
        while((interface = argz_next(mc.interfaces,
 
2316
                                     mc.interfaces_size, interface))){
 
2317
          if(strcmp(interface, "none") == 0){
 
2318
            argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2319
                        interface);
 
2320
            interface = NULL;
 
2321
          }
 
2322
        }
 
2323
        break;
 
2324
      }
 
2325
      bool interface_was_up = interface_is_up(interface);
 
2326
      errno = bring_up_interface(interface, delay);
 
2327
      if(not interface_was_up){
 
2328
        if(errno != 0){
 
2329
          perror_plus("Failed to bring up interface");
 
2330
        } else {
 
2331
          errno = argz_add(&interfaces_to_take_down,
 
2332
                           &interfaces_to_take_down_size,
 
2333
                           interface);
 
2334
          if(errno != 0){
 
2335
            perror_plus("argz_add");
 
2336
          }
 
2337
        }
 
2338
      }
 
2339
    }
 
2340
    if(debug and (interfaces_to_take_down == NULL)){
 
2341
      fprintf_plus(stderr, "No interfaces were brought up\n");
 
2342
    }
 
2343
  }
 
2344
  
 
2345
  /* If we only got one interface, explicitly use only that one */
 
2346
  if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
 
2347
    if(debug){
 
2348
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
 
2349
                   mc.interfaces);
 
2350
    }
 
2351
    if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2049
2352
  }
2050
2353
  
2051
2354
  if(quit_now){
2052
2355
    goto end;
2053
2356
  }
2054
2357
  
2055
 
  ret = init_gnutls_global(pubkey, seckey);
 
2358
  ret = init_gnutls_global(pubkey, seckey, &mc);
2056
2359
  if(ret == -1){
2057
2360
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2058
2361
    exitcode = EX_UNAVAILABLE;
2065
2368
    goto end;
2066
2369
  }
2067
2370
  
2068
 
  if(mkdtemp(tempdir) == NULL){
 
2371
  /* Try /run/tmp before /tmp */
 
2372
  tempdir = mkdtemp(run_tempdir);
 
2373
  if(tempdir == NULL and errno == ENOENT){
 
2374
      if(debug){
 
2375
        fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
 
2376
                     run_tempdir, old_tempdir);
 
2377
      }
 
2378
      tempdir = mkdtemp(old_tempdir);
 
2379
  }
 
2380
  if(tempdir == NULL){
2069
2381
    perror_plus("mkdtemp");
2070
2382
    goto end;
2071
2383
  }
2072
 
  tempdir_created = true;
2073
2384
  
2074
2385
  if(quit_now){
2075
2386
    goto end;
2076
2387
  }
2077
2388
  
2078
 
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
2389
  if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2079
2390
    fprintf_plus(stderr, "init_gpgme failed\n");
2080
2391
    exitcode = EX_UNAVAILABLE;
2081
2392
    goto end;
2102
2413
      goto end;
2103
2414
    }
2104
2415
    
2105
 
    uint16_t port;
 
2416
    in_port_t port;
2106
2417
    errno = 0;
2107
2418
    tmpmax = strtoimax(address+1, &tmp, 10);
2108
2419
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
2109
 
       or tmpmax != (uint16_t)tmpmax){
 
2420
       or tmpmax != (in_port_t)tmpmax){
2110
2421
      fprintf_plus(stderr, "Bad port number\n");
2111
2422
      exitcode = EX_USAGE;
2112
2423
      goto end;
2113
2424
    }
2114
 
  
 
2425
    
2115
2426
    if(quit_now){
2116
2427
      goto end;
2117
2428
    }
2118
2429
    
2119
 
    port = (uint16_t)tmpmax;
 
2430
    port = (in_port_t)tmpmax;
2120
2431
    *address = '\0';
2121
2432
    /* Colon in address indicates IPv6 */
2122
2433
    int af;
2138
2449
    }
2139
2450
    
2140
2451
    while(not quit_now){
2141
 
      ret = start_mandos_communication(address, port, if_index, af);
 
2452
      ret = start_mandos_communication(address, port, if_index, af,
 
2453
                                       &mc);
2142
2454
      if(quit_now or ret == 0){
2143
2455
        break;
2144
2456
      }
2146
2458
        fprintf_plus(stderr, "Retrying in %d seconds\n",
2147
2459
                     (int)retry_interval);
2148
2460
      }
2149
 
      sleep((int)retry_interval);
 
2461
      sleep((unsigned int)retry_interval);
2150
2462
    }
2151
2463
    
2152
 
    if (not quit_now){
 
2464
    if(not quit_now){
2153
2465
      exitcode = EXIT_SUCCESS;
2154
2466
    }
2155
2467
    
2170
2482
    config.publish_domain = 0;
2171
2483
    
2172
2484
    /* Allocate a new server */
2173
 
    mc.server = avahi_server_new(avahi_simple_poll_get
2174
 
                                 (mc.simple_poll), &config, NULL,
2175
 
                                 NULL, &error);
 
2485
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
2486
                                 &config, NULL, NULL, &ret_errno);
2176
2487
    
2177
2488
    /* Free the Avahi configuration data */
2178
2489
    avahi_server_config_free(&config);
2181
2492
  /* Check if creating the Avahi server object succeeded */
2182
2493
  if(mc.server == NULL){
2183
2494
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2184
 
                 avahi_strerror(error));
 
2495
                 avahi_strerror(ret_errno));
2185
2496
    exitcode = EX_UNAVAILABLE;
2186
2497
    goto end;
2187
2498
  }
2193
2504
  /* Create the Avahi service browser */
2194
2505
  sb = avahi_s_service_browser_new(mc.server, if_index,
2195
2506
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2196
 
                                   NULL, 0, browse_callback, NULL);
 
2507
                                   NULL, 0, browse_callback,
 
2508
                                   (void *)&mc);
2197
2509
  if(sb == NULL){
2198
2510
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
2199
2511
                 avahi_strerror(avahi_server_errno(mc.server)));
2210
2522
  if(debug){
2211
2523
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2212
2524
  }
2213
 
 
2214
 
  ret = avahi_loop_with_timeout(mc.simple_poll,
2215
 
                                (int)(retry_interval * 1000));
 
2525
  
 
2526
  ret = avahi_loop_with_timeout(simple_poll,
 
2527
                                (int)(retry_interval * 1000), &mc);
2216
2528
  if(debug){
2217
2529
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2218
2530
                 (ret == 0) ? "successfully" : "with error");
2225
2537
  }
2226
2538
  
2227
2539
  /* Cleanup things */
 
2540
  free(mc.interfaces);
 
2541
  
2228
2542
  if(sb != NULL)
2229
2543
    avahi_s_service_browser_free(sb);
2230
2544
  
2231
2545
  if(mc.server != NULL)
2232
2546
    avahi_server_free(mc.server);
2233
2547
  
2234
 
  if(mc.simple_poll != NULL)
2235
 
    avahi_simple_poll_free(mc.simple_poll);
 
2548
  if(simple_poll != NULL)
 
2549
    avahi_simple_poll_free(simple_poll);
2236
2550
  
2237
2551
  if(gnutls_initialized){
2238
2552
    gnutls_certificate_free_credentials(mc.cred);
2250
2564
    mc.current_server->prev->next = NULL;
2251
2565
    while(mc.current_server != NULL){
2252
2566
      server *next = mc.current_server->next;
 
2567
#ifdef __GNUC__
 
2568
#pragma GCC diagnostic push
 
2569
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2570
#endif
 
2571
      free((char *)(mc.current_server->ip));
 
2572
#ifdef __GNUC__
 
2573
#pragma GCC diagnostic pop
 
2574
#endif
2253
2575
      free(mc.current_server);
2254
2576
      mc.current_server = next;
2255
2577
    }
2256
2578
  }
2257
2579
  
2258
 
  /* Run network hooks */
2259
 
  run_network_hooks("stop", interface, delay);
2260
 
  
2261
 
  /* Re-raise priviliges */
 
2580
  /* Re-raise privileges */
2262
2581
  {
2263
 
    raise_privileges();
 
2582
    ret_errno = raise_privileges();
 
2583
    if(ret_errno != 0){
 
2584
      errno = ret_errno;
 
2585
      perror_plus("Failed to raise privileges");
 
2586
    } else {
 
2587
      
 
2588
      /* Run network hooks */
 
2589
      run_network_hooks("stop", interfaces_hooks != NULL ?
 
2590
                        interfaces_hooks : "", delay);
 
2591
      
 
2592
      /* Take down the network interfaces which were brought up */
 
2593
      {
 
2594
        char *interface = NULL;
 
2595
        while((interface=argz_next(interfaces_to_take_down,
 
2596
                                   interfaces_to_take_down_size,
 
2597
                                   interface))){
 
2598
          ret_errno = take_down_interface(interface);
 
2599
          if(ret_errno != 0){
 
2600
            errno = ret_errno;
 
2601
            perror_plus("Failed to take down interface");
 
2602
          }
 
2603
        }
 
2604
        if(debug and (interfaces_to_take_down == NULL)){
 
2605
          fprintf_plus(stderr, "No interfaces needed to be taken"
 
2606
                       " down\n");
 
2607
        }
 
2608
      }
 
2609
    }
2264
2610
    
2265
 
    /* Take down the network interface */
2266
 
    if(take_down_interface and geteuid() == 0){
2267
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
2268
 
      if(ret == -1){
2269
 
        perror_plus("ioctl SIOCGIFFLAGS");
2270
 
      } else if(network.ifr_flags & IFF_UP){
2271
 
        network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2272
 
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
2273
 
        if(ret == -1){
2274
 
          perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2275
 
        }
2276
 
      }
2277
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
2278
 
      if(ret == -1){
2279
 
        perror_plus("close");
2280
 
      }
 
2611
    ret_errno = lower_privileges_permanently();
 
2612
    if(ret_errno != 0){
 
2613
      errno = ret_errno;
 
2614
      perror_plus("Failed to lower privileges permanently");
2281
2615
    }
2282
2616
  }
2283
 
  /* Lower privileges permanently */
2284
 
  errno = 0;
2285
 
  ret = setuid(uid);
2286
 
  if(ret == -1){
2287
 
    perror_plus("setuid");
2288
 
  }
 
2617
  
 
2618
  free(interfaces_to_take_down);
 
2619
  free(interfaces_hooks);
2289
2620
  
2290
2621
  /* Removes the GPGME temp directory and all files inside */
2291
 
  if(tempdir_created){
 
2622
  if(tempdir != NULL){
2292
2623
    struct dirent **direntries = NULL;
2293
 
    struct dirent *direntry = NULL;
2294
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2295
 
                             alphasort);
2296
 
    if (numentries > 0){
2297
 
      for(int i = 0; i < numentries; i++){
2298
 
        direntry = direntries[i];
2299
 
        char *fullname = NULL;
2300
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2301
 
                       direntry->d_name);
2302
 
        if(ret < 0){
2303
 
          perror_plus("asprintf");
2304
 
          continue;
2305
 
        }
2306
 
        ret = remove(fullname);
2307
 
        if(ret == -1){
2308
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2309
 
                       strerror(errno));
2310
 
        }
2311
 
        free(fullname);
 
2624
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2625
                                                  O_NOFOLLOW));
 
2626
    if(tempdir_fd == -1){
 
2627
      perror_plus("open");
 
2628
    } else {
 
2629
#ifdef __GLIBC__
 
2630
#if __GLIBC_PREREQ(2, 15)
 
2631
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2632
                                 notdotentries, alphasort);
 
2633
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2634
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2635
                               alphasort);
 
2636
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2637
#else   /* not __GLIBC__ */
 
2638
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2639
                               alphasort);
 
2640
#endif  /* not __GLIBC__ */
 
2641
      if(numentries >= 0){
 
2642
        for(int i = 0; i < numentries; i++){
 
2643
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2644
          if(ret == -1){
 
2645
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2646
                         " \"%s\", 0): %s\n", tempdir,
 
2647
                         direntries[i]->d_name, strerror(errno));
 
2648
          }
 
2649
          free(direntries[i]);
 
2650
        }
 
2651
        
 
2652
        /* need to clean even if 0 because man page doesn't specify */
 
2653
        free(direntries);
 
2654
        if(numentries == -1){
 
2655
          perror_plus("scandir");
 
2656
        }
 
2657
        ret = rmdir(tempdir);
 
2658
        if(ret == -1 and errno != ENOENT){
 
2659
          perror_plus("rmdir");
 
2660
        }
2312
2661
      }
2313
 
    }
2314
 
 
2315
 
    /* need to clean even if 0 because man page doesn't specify */
2316
 
    free(direntries);
2317
 
    if (numentries == -1){
2318
 
      perror_plus("scandir");
2319
 
    }
2320
 
    ret = rmdir(tempdir);
2321
 
    if(ret == -1 and errno != ENOENT){
2322
 
      perror_plus("rmdir");
 
2662
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2323
2663
    }
2324
2664
  }
2325
2665