/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: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

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,
72
73
                                */
73
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
74
75
                                   getuid(), getgid(), seteuid(),
75
 
                                   setgid(), pause(), _exit() */
76
 
#include <arpa/inet.h>          /* inet_pton(), htons, inet_ntop() */
 
76
                                   setgid(), pause(), _exit(),
 
77
                                   unlinkat() */
 
78
#include <arpa/inet.h>          /* inet_pton(), htons() */
77
79
#include <iso646.h>             /* not, or, and */
78
80
#include <argp.h>               /* struct argp_option, error_t, struct
79
81
                                   argp_state, struct argp,
91
93
                                   argz_delete(), argz_append(),
92
94
                                   argz_stringify(), argz_add(),
93
95
                                   argz_count() */
 
96
#include <netdb.h>              /* getnameinfo(), NI_NUMERICHOST,
 
97
                                   EAI_SYSTEM, gai_strerror() */
94
98
 
95
99
#ifdef __linux__
96
100
#include <sys/klog.h>           /* klogctl() */
138
142
static const char sys_class_net[] = "/sys/class/net";
139
143
char *connect_to = NULL;
140
144
const char *hookdir = HOOKDIR;
 
145
int hookdir_fd = -1;
141
146
uid_t uid = 65534;
142
147
gid_t gid = 65534;
143
148
 
180
185
  perror(print_text);
181
186
}
182
187
 
183
 
__attribute__((format (gnu_printf, 2, 3)))
 
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
184
189
int fprintf_plus(FILE *stream, const char *format, ...){
185
190
  va_list ap;
186
191
  va_start (ap, format);
187
192
  
188
193
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
189
194
                             program_invocation_short_name));
190
 
  return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
195
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
191
196
}
192
197
 
193
198
/*
195
200
 * bytes. "buffer_capacity" is how much is currently allocated,
196
201
 * "buffer_length" is how much is already used.
197
202
 */
 
203
__attribute__((nonnull, warn_unused_result))
198
204
size_t incbuffer(char **buffer, size_t buffer_length,
199
205
                 size_t buffer_capacity){
200
206
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
201
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
202
 
    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;
203
213
      return 0;
204
214
    }
 
215
    *buffer = new_buf;
205
216
    buffer_capacity += BUFFER_SIZE;
206
217
  }
207
218
  return buffer_capacity;
208
219
}
209
220
 
210
221
/* Add server to set of servers to retry periodically */
 
222
__attribute__((nonnull, warn_unused_result))
211
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
212
224
                int af, server **current_server){
213
225
  int ret;
222
234
                          .af = af };
223
235
  if(new_server->ip == NULL){
224
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);
225
252
    return false;
226
253
  }
227
254
  /* Special case of first server */
229
256
    new_server->next = new_server;
230
257
    new_server->prev = new_server;
231
258
    *current_server = new_server;
232
 
  /* Place the new server last in the list */
233
259
  } else {
 
260
    /* Place the new server last in the list */
234
261
    new_server->next = *current_server;
235
262
    new_server->prev = (*current_server)->prev;
236
263
    new_server->prev->next = new_server;
237
264
    (*current_server)->prev = new_server;
238
265
  }
239
 
  ret = clock_gettime(CLOCK_MONOTONIC, &(*current_server)->last_seen);
240
 
  if(ret == -1){
241
 
    perror_plus("clock_gettime");
242
 
    return false;
243
 
  }
244
266
  return true;
245
267
}
246
268
 
247
269
/* 
248
270
 * Initialize GPGME.
249
271
 */
250
 
static bool init_gpgme(const char *seckey, const char *pubkey,
251
 
                       const char *tempdir, mandos_context *mc){
 
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){
252
277
  gpgme_error_t rc;
253
278
  gpgme_engine_info_t engine_info;
254
279
  
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;
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
373
                                  char **plaintext,
467
493
  return plaintext_length;
468
494
}
469
495
 
470
 
static const char * safer_gnutls_strerror(int value){
 
496
__attribute__((warn_unused_result, const))
 
497
static const char *safe_string(const char *str){
 
498
  if(str == NULL)
 
499
    return "(unknown)";
 
500
  return str;
 
501
}
 
502
 
 
503
__attribute__((warn_unused_result))
 
504
static const char *safer_gnutls_strerror(int value){
471
505
  const char *ret = gnutls_strerror(value);
472
 
  if(ret == NULL)
473
 
    ret = "(unknown)";
474
 
  return ret;
 
506
  return safe_string(ret);
475
507
}
476
508
 
477
509
/* GnuTLS log function callback */
 
510
__attribute__((nonnull))
478
511
static void debuggnutls(__attribute__((unused)) int level,
479
512
                        const char* string){
480
513
  fprintf_plus(stderr, "GnuTLS: %s", string);
481
514
}
482
515
 
 
516
__attribute__((nonnull, warn_unused_result))
483
517
static int init_gnutls_global(const char *pubkeyfilename,
484
518
                              const char *seckeyfilename,
485
519
                              mandos_context *mc){
486
520
  int ret;
 
521
  unsigned int uret;
487
522
  
488
523
  if(debug){
489
524
    fprintf_plus(stderr, "Initializing GnuTLS\n");
540
575
                 safer_gnutls_strerror(ret));
541
576
    goto globalfail;
542
577
  }
 
578
  if(mc->dh_bits == 0){
 
579
    /* Find out the optimal number of DH bits */
 
580
    /* Try to read the private key file */
 
581
    gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
582
    {
 
583
      int secfile = open(seckeyfilename, O_RDONLY);
 
584
      size_t buffer_capacity = 0;
 
585
      while(true){
 
586
        buffer_capacity = incbuffer((char **)&buffer.data,
 
587
                                    (size_t)buffer.size,
 
588
                                    (size_t)buffer_capacity);
 
589
        if(buffer_capacity == 0){
 
590
          perror_plus("incbuffer");
 
591
          free(buffer.data);
 
592
          buffer.data = NULL;
 
593
          break;
 
594
        }
 
595
        ssize_t bytes_read = read(secfile, buffer.data + buffer.size,
 
596
                                  BUFFER_SIZE);
 
597
        /* EOF */
 
598
        if(bytes_read == 0){
 
599
          break;
 
600
        }
 
601
        /* check bytes_read for failure */
 
602
        if(bytes_read < 0){
 
603
          perror_plus("read");
 
604
          free(buffer.data);
 
605
          buffer.data = NULL;
 
606
          break;
 
607
        }
 
608
        buffer.size += (unsigned int)bytes_read;
 
609
      }
 
610
      close(secfile);
 
611
    }
 
612
    /* If successful, use buffer to parse private key */
 
613
    gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
614
    if(buffer.data != NULL){
 
615
      {
 
616
        gnutls_openpgp_privkey_t privkey = NULL;
 
617
        ret = gnutls_openpgp_privkey_init(&privkey);
 
618
        if(ret != GNUTLS_E_SUCCESS){
 
619
          fprintf_plus(stderr, "Error initializing OpenPGP key"
 
620
                       " structure: %s", safer_gnutls_strerror(ret));
 
621
          free(buffer.data);
 
622
          buffer.data = NULL;
 
623
        } else {
 
624
          ret = gnutls_openpgp_privkey_import(privkey, &buffer,
 
625
                                            GNUTLS_OPENPGP_FMT_BASE64,
 
626
                                              "", 0);
 
627
          if(ret != GNUTLS_E_SUCCESS){
 
628
            fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
629
                         safer_gnutls_strerror(ret));
 
630
            privkey = NULL;
 
631
          }
 
632
          free(buffer.data);
 
633
          buffer.data = NULL;
 
634
          if(privkey != NULL){
 
635
            /* Use private key to suggest an appropriate sec_param */
 
636
            sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
637
            gnutls_openpgp_privkey_deinit(privkey);
 
638
            if(debug){
 
639
              fprintf_plus(stderr, "This OpenPGP key implies using a"
 
640
                           " GnuTLS security parameter \"%s\".\n",
 
641
                           safe_string(gnutls_sec_param_get_name
 
642
                                       (sec_param)));
 
643
            }
 
644
          }
 
645
        }
 
646
      }
 
647
      if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
648
        /* Err on the side of caution */
 
649
        sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
650
        if(debug){
 
651
          fprintf_plus(stderr, "Falling back to security parameter"
 
652
                       " \"%s\"\n",
 
653
                       safe_string(gnutls_sec_param_get_name
 
654
                                   (sec_param)));
 
655
        }
 
656
      }
 
657
    }
 
658
    uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
659
    if(uret != 0){
 
660
      mc->dh_bits = uret;
 
661
      if(debug){
 
662
        fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
663
                     " implies %u DH bits; using that.\n",
 
664
                     safe_string(gnutls_sec_param_get_name
 
665
                                 (sec_param)),
 
666
                     mc->dh_bits);
 
667
      }
 
668
    } else {
 
669
      fprintf_plus(stderr, "Failed to get implied number of DH"
 
670
                   " bits for security parameter \"%s\"): %s\n",
 
671
                   safe_string(gnutls_sec_param_get_name(sec_param)),
 
672
                   safer_gnutls_strerror(ret));
 
673
      goto globalfail;
 
674
    }
 
675
  } else if(debug){
 
676
    fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
677
                 mc->dh_bits);
 
678
  }
543
679
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
544
680
  if(ret != GNUTLS_E_SUCCESS){
545
 
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
546
 
                 safer_gnutls_strerror(ret));
 
681
    fprintf_plus(stderr, "Error in GnuTLS prime generation (%u bits):"
 
682
                 " %s\n", mc->dh_bits, safer_gnutls_strerror(ret));
547
683
    goto globalfail;
548
684
  }
549
685
  
559
695
  return -1;
560
696
}
561
697
 
 
698
__attribute__((nonnull, warn_unused_result))
562
699
static int init_gnutls_session(gnutls_session_t *session,
563
700
                               mandos_context *mc){
564
701
  int ret;
621
758
                      __attribute__((unused)) const char *txt){}
622
759
 
623
760
/* Called when a Mandos server is found */
 
761
__attribute__((nonnull, warn_unused_result))
624
762
static int start_mandos_communication(const char *ip, in_port_t port,
625
763
                                      AvahiIfIndex if_index,
626
764
                                      int af, mandos_context *mc){
627
765
  int ret, tcp_sd = -1;
628
766
  ssize_t sret;
629
 
  union {
630
 
    struct sockaddr_in in;
631
 
    struct sockaddr_in6 in6;
632
 
  } to;
 
767
  struct sockaddr_storage to;
633
768
  char *buffer = NULL;
634
769
  char *decrypted_buffer = NULL;
635
770
  size_t buffer_length = 0;
716
851
  
717
852
  memset(&to, 0, sizeof(to));
718
853
  if(af == AF_INET6){
719
 
    to.in6.sin6_family = (sa_family_t)af;
720
 
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
854
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
 
855
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
721
856
  } else {                      /* IPv4 */
722
 
    to.in.sin_family = (sa_family_t)af;
723
 
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
857
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
 
858
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
724
859
  }
725
860
  if(ret < 0 ){
726
861
    int e = errno;
735
870
    goto mandos_end;
736
871
  }
737
872
  if(af == AF_INET6){
738
 
    to.in6.sin6_port = htons(port);    
739
 
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
740
 
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
741
 
                                -Wunreachable-code*/
 
873
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
 
874
    if(IN6_IS_ADDR_LINKLOCAL
 
875
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
742
876
      if(if_index == AVAHI_IF_UNSPEC){
743
877
        fprintf_plus(stderr, "An IPv6 link-local address is"
744
878
                     " incomplete without a network interface\n");
746
880
        goto mandos_end;
747
881
      }
748
882
      /* Set the network interface number as scope */
749
 
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
883
      ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
750
884
    }
751
885
  } else {
752
 
    to.in.sin_port = htons(port); /* Spurious warnings from
753
 
                                     -Wconversion and
754
 
                                     -Wunreachable-code */
 
886
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
755
887
  }
756
888
  
757
889
  if(quit_now){
774
906
    }
775
907
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
776
908
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
777
 
    const char *pcret;
778
909
    if(af == AF_INET6){
779
 
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
780
 
                        sizeof(addrstr));
 
910
      ret = getnameinfo((struct sockaddr *)&to,
 
911
                        sizeof(struct sockaddr_in6),
 
912
                        addrstr, sizeof(addrstr), NULL, 0,
 
913
                        NI_NUMERICHOST);
781
914
    } else {
782
 
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
783
 
                        sizeof(addrstr));
 
915
      ret = getnameinfo((struct sockaddr *)&to,
 
916
                        sizeof(struct sockaddr_in),
 
917
                        addrstr, sizeof(addrstr), NULL, 0,
 
918
                        NI_NUMERICHOST);
784
919
    }
785
 
    if(pcret == NULL){
786
 
      perror_plus("inet_ntop");
787
 
    } else {
788
 
      if(strcmp(addrstr, ip) != 0){
789
 
        fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
790
 
      }
 
920
    if(ret == EAI_SYSTEM){
 
921
      perror_plus("getnameinfo");
 
922
    } else if(ret != 0) {
 
923
      fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
 
924
    } else if(strcmp(addrstr, ip) != 0){
 
925
      fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
791
926
    }
792
927
  }
793
928
  
797
932
  }
798
933
  
799
934
  if(af == AF_INET6){
800
 
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
935
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
936
                  sizeof(struct sockaddr_in6));
801
937
  } else {
802
 
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
938
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
939
                  sizeof(struct sockaddr_in));
803
940
  }
804
941
  if(ret < 0){
805
 
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
942
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
806
943
      int e = errno;
807
944
      perror_plus("connect");
808
945
      errno = e;
1023
1160
  return retval;
1024
1161
}
1025
1162
 
 
1163
__attribute__((nonnull))
1026
1164
static void resolve_callback(AvahiSServiceResolver *r,
1027
1165
                             AvahiIfIndex interface,
1028
1166
                             AvahiProtocol proto,
1036
1174
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
1037
1175
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
1038
1176
                             flags,
1039
 
                             void* mc){
 
1177
                             void *mc){
1040
1178
  if(r == NULL){
1041
1179
    return;
1042
1180
  }
1045
1183
     timed out */
1046
1184
  
1047
1185
  if(quit_now){
 
1186
    avahi_s_service_resolver_free(r);
1048
1187
    return;
1049
1188
  }
1050
1189
  
1095
1234
                            const char *domain,
1096
1235
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
1097
1236
                            flags,
1098
 
                            void* mc){
 
1237
                            void *mc){
1099
1238
  if(b == NULL){
1100
1239
    return;
1101
1240
  }
1161
1300
  errno = old_errno;
1162
1301
}
1163
1302
 
 
1303
__attribute__((nonnull, warn_unused_result))
1164
1304
bool get_flags(const char *ifname, struct ifreq *ifr){
1165
1305
  int ret;
1166
1306
  error_t ret_errno;
1185
1325
  return true;
1186
1326
}
1187
1327
 
 
1328
__attribute__((nonnull, warn_unused_result))
1188
1329
bool good_flags(const char *ifname, const struct ifreq *ifr){
1189
1330
  
1190
1331
  /* Reject the loopback device */
1232
1373
 * corresponds to an acceptable network device.
1233
1374
 * (This function is passed to scandir(3) as a filter function.)
1234
1375
 */
 
1376
__attribute__((nonnull, warn_unused_result))
1235
1377
int good_interface(const struct dirent *if_entry){
1236
1378
  if(if_entry->d_name[0] == '.'){
1237
1379
    return 0;
1255
1397
/* 
1256
1398
 * This function determines if a network interface is up.
1257
1399
 */
 
1400
__attribute__((nonnull, warn_unused_result))
1258
1401
bool interface_is_up(const char *interface){
1259
1402
  struct ifreq ifr;
1260
1403
  if(not get_flags(interface, &ifr)){
1271
1414
/* 
1272
1415
 * This function determines if a network interface is running
1273
1416
 */
 
1417
__attribute__((nonnull, warn_unused_result))
1274
1418
bool interface_is_running(const char *interface){
1275
1419
  struct ifreq ifr;
1276
1420
  if(not get_flags(interface, &ifr)){
1284
1428
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
1285
1429
}
1286
1430
 
 
1431
__attribute__((nonnull, pure, warn_unused_result))
1287
1432
int notdotentries(const struct dirent *direntry){
1288
1433
  /* Skip "." and ".." */
1289
1434
  if(direntry->d_name[0] == '.'
1296
1441
}
1297
1442
 
1298
1443
/* Is this directory entry a runnable program? */
 
1444
__attribute__((nonnull, warn_unused_result))
1299
1445
int runnable_hook(const struct dirent *direntry){
1300
1446
  int ret;
1301
1447
  size_t sret;
1309
1455
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1310
1456
                "abcdefghijklmnopqrstuvwxyz"
1311
1457
                "0123456789"
1312
 
                "_-");
 
1458
                "_.-");
1313
1459
  if((direntry->d_name)[sret] != '\0'){
1314
1460
    /* Contains non-allowed characters */
1315
1461
    if(debug){
1319
1465
    return 0;
1320
1466
  }
1321
1467
  
1322
 
  char *fullname = NULL;
1323
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1324
 
  if(ret < 0){
1325
 
    perror_plus("asprintf");
1326
 
    return 0;
1327
 
  }
1328
 
  
1329
 
  ret = stat(fullname, &st);
 
1468
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1330
1469
  if(ret == -1){
1331
1470
    if(debug){
1332
1471
      perror_plus("Could not stat hook");
1356
1495
  return 1;
1357
1496
}
1358
1497
 
 
1498
__attribute__((nonnull, warn_unused_result))
1359
1499
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1360
1500
                            mandos_context *mc){
1361
1501
  int ret;
1365
1505
  
1366
1506
  while(true){
1367
1507
    if(mc->current_server == NULL){
1368
 
      if (debug){
 
1508
      if(debug){
1369
1509
        fprintf_plus(stderr, "Wait until first server is found."
1370
1510
                     " No timeout!\n");
1371
1511
      }
1372
1512
      ret = avahi_simple_poll_iterate(s, -1);
1373
1513
    } else {
1374
 
      if (debug){
 
1514
      if(debug){
1375
1515
        fprintf_plus(stderr, "Check current_server if we should run"
1376
1516
                     " it, or wait\n");
1377
1517
      }
1394
1534
                     - ((intmax_t)waited_time.tv_sec * 1000))
1395
1535
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1396
1536
      
1397
 
      if (debug){
 
1537
      if(debug){
1398
1538
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1399
1539
                     block_time);
1400
1540
      }
1422
1562
      ret = avahi_simple_poll_iterate(s, (int)block_time);
1423
1563
    }
1424
1564
    if(ret != 0){
1425
 
      if (ret > 0 or errno != EINTR){
 
1565
      if(ret > 0 or errno != EINTR){
1426
1566
        return (ret != 1) ? ret : 0;
1427
1567
      }
1428
1568
    }
1430
1570
}
1431
1571
 
1432
1572
/* Set effective uid to 0, return errno */
 
1573
__attribute__((warn_unused_result))
1433
1574
error_t raise_privileges(void){
1434
1575
  error_t old_errno = errno;
1435
1576
  error_t ret_errno = 0;
1436
1577
  if(seteuid(0) == -1){
1437
1578
    ret_errno = errno;
1438
 
    perror_plus("seteuid");
1439
1579
  }
1440
1580
  errno = old_errno;
1441
1581
  return ret_errno;
1442
1582
}
1443
1583
 
1444
1584
/* Set effective and real user ID to 0.  Return errno. */
 
1585
__attribute__((warn_unused_result))
1445
1586
error_t raise_privileges_permanently(void){
1446
1587
  error_t old_errno = errno;
1447
1588
  error_t ret_errno = raise_privileges();
1451
1592
  }
1452
1593
  if(setuid(0) == -1){
1453
1594
    ret_errno = errno;
1454
 
    perror_plus("seteuid");
1455
1595
  }
1456
1596
  errno = old_errno;
1457
1597
  return ret_errno;
1458
1598
}
1459
1599
 
1460
1600
/* Set effective user ID to unprivileged saved user ID */
 
1601
__attribute__((warn_unused_result))
1461
1602
error_t lower_privileges(void){
1462
1603
  error_t old_errno = errno;
1463
1604
  error_t ret_errno = 0;
1464
1605
  if(seteuid(uid) == -1){
1465
1606
    ret_errno = errno;
1466
 
    perror_plus("seteuid");
1467
1607
  }
1468
1608
  errno = old_errno;
1469
1609
  return ret_errno;
1470
1610
}
1471
1611
 
1472
1612
/* Lower privileges permanently */
 
1613
__attribute__((warn_unused_result))
1473
1614
error_t lower_privileges_permanently(void){
1474
1615
  error_t old_errno = errno;
1475
1616
  error_t ret_errno = 0;
1476
1617
  if(setuid(uid) == -1){
1477
1618
    ret_errno = errno;
1478
 
    perror_plus("setuid");
1479
1619
  }
1480
1620
  errno = old_errno;
1481
1621
  return ret_errno;
1482
1622
}
1483
1623
 
1484
 
bool run_network_hooks(const char *mode, const char *interface,
 
1624
__attribute__((nonnull))
 
1625
void run_network_hooks(const char *mode, const char *interface,
1485
1626
                       const float delay){
1486
 
  struct dirent **direntries;
 
1627
  struct dirent **direntries = NULL;
 
1628
  if(hookdir_fd == -1){
 
1629
    hookdir_fd = open(hookdir, O_RDONLY);
 
1630
    if(hookdir_fd == -1){
 
1631
      if(errno == ENOENT){
 
1632
        if(debug){
 
1633
          fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
1634
                       " found\n", hookdir);
 
1635
        }
 
1636
      } else {
 
1637
        perror_plus("open");
 
1638
      }
 
1639
      return;
 
1640
    }
 
1641
  }
 
1642
#ifdef __GLIBC__
 
1643
#if __GLIBC_PREREQ(2, 15)
 
1644
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
 
1645
                           runnable_hook, alphasort);
 
1646
#else  /* not __GLIBC_PREREQ(2, 15) */
 
1647
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1648
                         alphasort);
 
1649
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
1650
#else   /* not __GLIBC__ */
 
1651
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1652
                         alphasort);
 
1653
#endif  /* not __GLIBC__ */
 
1654
  if(numhooks == -1){
 
1655
    perror_plus("scandir");
 
1656
    return;
 
1657
  }
1487
1658
  struct dirent *direntry;
1488
1659
  int ret;
1489
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1490
 
                         alphasort);
1491
 
  if(numhooks == -1){
1492
 
    if(errno == ENOENT){
1493
 
      if(debug){
1494
 
        fprintf_plus(stderr, "Network hook directory \"%s\" not"
1495
 
                     " found\n", hookdir);
1496
 
      }
1497
 
    } else {
1498
 
      perror_plus("scandir");
 
1660
  int devnull = open("/dev/null", O_RDONLY);
 
1661
  for(int i = 0; i < numhooks; i++){
 
1662
    direntry = direntries[i];
 
1663
    if(debug){
 
1664
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
1665
                   direntry->d_name);
1499
1666
    }
1500
 
  } else {
1501
 
    int devnull = open("/dev/null", O_RDONLY);
1502
 
    for(int i = 0; i < numhooks; i++){
1503
 
      direntry = direntries[i];
1504
 
      char *fullname = NULL;
1505
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1506
 
      if(ret < 0){
 
1667
    pid_t hook_pid = fork();
 
1668
    if(hook_pid == 0){
 
1669
      /* Child */
 
1670
      /* Raise privileges */
 
1671
      errno = raise_privileges_permanently();
 
1672
      if(errno != 0){
 
1673
        perror_plus("Failed to raise privileges");
 
1674
        _exit(EX_NOPERM);
 
1675
      }
 
1676
      /* Set group */
 
1677
      errno = 0;
 
1678
      ret = setgid(0);
 
1679
      if(ret == -1){
 
1680
        perror_plus("setgid");
 
1681
        _exit(EX_NOPERM);
 
1682
      }
 
1683
      /* Reset supplementary groups */
 
1684
      errno = 0;
 
1685
      ret = setgroups(0, NULL);
 
1686
      if(ret == -1){
 
1687
        perror_plus("setgroups");
 
1688
        _exit(EX_NOPERM);
 
1689
      }
 
1690
      ret = dup2(devnull, STDIN_FILENO);
 
1691
      if(ret == -1){
 
1692
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
1693
        _exit(EX_OSERR);
 
1694
      }
 
1695
      ret = close(devnull);
 
1696
      if(ret == -1){
 
1697
        perror_plus("close");
 
1698
        _exit(EX_OSERR);
 
1699
      }
 
1700
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
1701
      if(ret == -1){
 
1702
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
1703
        _exit(EX_OSERR);
 
1704
      }
 
1705
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
1706
      if(ret == -1){
 
1707
        perror_plus("setenv");
 
1708
        _exit(EX_OSERR);
 
1709
      }
 
1710
      ret = setenv("DEVICE", interface, 1);
 
1711
      if(ret == -1){
 
1712
        perror_plus("setenv");
 
1713
        _exit(EX_OSERR);
 
1714
      }
 
1715
      ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
1716
      if(ret == -1){
 
1717
        perror_plus("setenv");
 
1718
        _exit(EX_OSERR);
 
1719
      }
 
1720
      ret = setenv("MODE", mode, 1);
 
1721
      if(ret == -1){
 
1722
        perror_plus("setenv");
 
1723
        _exit(EX_OSERR);
 
1724
      }
 
1725
      char *delaystring;
 
1726
      ret = asprintf(&delaystring, "%f", (double)delay);
 
1727
      if(ret == -1){
1507
1728
        perror_plus("asprintf");
1508
 
        continue;
1509
 
      }
1510
 
      if(debug){
1511
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1512
 
                     direntry->d_name);
1513
 
      }
1514
 
      pid_t hook_pid = fork();
1515
 
      if(hook_pid == 0){
1516
 
        /* Child */
1517
 
        /* Raise privileges */
1518
 
        raise_privileges_permanently();
1519
 
        /* Set group */
1520
 
        errno = 0;
1521
 
        ret = setgid(0);
1522
 
        if(ret == -1){
1523
 
          perror_plus("setgid");
1524
 
        }
1525
 
        /* Reset supplementary groups */
1526
 
        errno = 0;
1527
 
        ret = setgroups(0, NULL);
1528
 
        if(ret == -1){
1529
 
          perror_plus("setgroups");
1530
 
        }
1531
 
        dup2(devnull, STDIN_FILENO);
1532
 
        close(devnull);
1533
 
        dup2(STDERR_FILENO, STDOUT_FILENO);
1534
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1535
 
        if(ret == -1){
1536
 
          perror_plus("setenv");
1537
 
          _exit(EX_OSERR);
1538
 
        }
1539
 
        ret = setenv("DEVICE", interface, 1);
1540
 
        if(ret == -1){
1541
 
          perror_plus("setenv");
1542
 
          _exit(EX_OSERR);
1543
 
        }
1544
 
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1545
 
        if(ret == -1){
1546
 
          perror_plus("setenv");
1547
 
          _exit(EX_OSERR);
1548
 
        }
1549
 
        ret = setenv("MODE", mode, 1);
1550
 
        if(ret == -1){
1551
 
          perror_plus("setenv");
1552
 
          _exit(EX_OSERR);
1553
 
        }
1554
 
        char *delaystring;
1555
 
        ret = asprintf(&delaystring, "%f", delay);
1556
 
        if(ret == -1){
1557
 
          perror_plus("asprintf");
1558
 
          _exit(EX_OSERR);
1559
 
        }
1560
 
        ret = setenv("DELAY", delaystring, 1);
1561
 
        if(ret == -1){
1562
 
          free(delaystring);
1563
 
          perror_plus("setenv");
1564
 
          _exit(EX_OSERR);
1565
 
        }
 
1729
        _exit(EX_OSERR);
 
1730
      }
 
1731
      ret = setenv("DELAY", delaystring, 1);
 
1732
      if(ret == -1){
1566
1733
        free(delaystring);
1567
 
        if(connect_to != NULL){
1568
 
          ret = setenv("CONNECT", connect_to, 1);
1569
 
          if(ret == -1){
1570
 
            perror_plus("setenv");
1571
 
            _exit(EX_OSERR);
1572
 
          }
1573
 
        }
1574
 
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1575
 
          perror_plus("execl");
1576
 
          _exit(EXIT_FAILURE);
1577
 
        }
 
1734
        perror_plus("setenv");
 
1735
        _exit(EX_OSERR);
 
1736
      }
 
1737
      free(delaystring);
 
1738
      if(connect_to != NULL){
 
1739
        ret = setenv("CONNECT", connect_to, 1);
 
1740
        if(ret == -1){
 
1741
          perror_plus("setenv");
 
1742
          _exit(EX_OSERR);
 
1743
        }
 
1744
      }
 
1745
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
 
1746
      if(hook_fd == -1){
 
1747
        perror_plus("openat");
 
1748
        _exit(EXIT_FAILURE);
 
1749
      }
 
1750
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1751
        perror_plus("close");
 
1752
        _exit(EXIT_FAILURE);
 
1753
      }
 
1754
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
1755
                 environ) == -1){
 
1756
        perror_plus("fexecve");
 
1757
        _exit(EXIT_FAILURE);
 
1758
      }
 
1759
    } else {
 
1760
      if(hook_pid == -1){
 
1761
        perror_plus("fork");
 
1762
        free(direntry);
 
1763
        continue;
 
1764
      }
 
1765
      int status;
 
1766
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1767
        perror_plus("waitpid");
 
1768
        free(direntry);
 
1769
        continue;
 
1770
      }
 
1771
      if(WIFEXITED(status)){
 
1772
        if(WEXITSTATUS(status) != 0){
 
1773
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1774
                       " with status %d\n", direntry->d_name,
 
1775
                       WEXITSTATUS(status));
 
1776
          free(direntry);
 
1777
          continue;
 
1778
        }
 
1779
      } else if(WIFSIGNALED(status)){
 
1780
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1781
                     " signal %d\n", direntry->d_name,
 
1782
                     WTERMSIG(status));
 
1783
        free(direntry);
 
1784
        continue;
1578
1785
      } else {
1579
 
        int status;
1580
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1581
 
          perror_plus("waitpid");
1582
 
          free(fullname);
1583
 
          continue;
1584
 
        }
1585
 
        if(WIFEXITED(status)){
1586
 
          if(WEXITSTATUS(status) != 0){
1587
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1588
 
                         " with status %d\n", direntry->d_name,
1589
 
                         WEXITSTATUS(status));
1590
 
            free(fullname);
1591
 
            continue;
1592
 
          }
1593
 
        } else if(WIFSIGNALED(status)){
1594
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1595
 
                       " signal %d\n", direntry->d_name,
1596
 
                       WTERMSIG(status));
1597
 
          free(fullname);
1598
 
          continue;
1599
 
        } else {
1600
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1601
 
                       " crashed\n", direntry->d_name);
1602
 
          free(fullname);
1603
 
          continue;
1604
 
        }
1605
 
      }
1606
 
      free(fullname);
1607
 
      if(debug){
1608
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1609
 
                     direntry->d_name);
1610
 
      }
1611
 
    }
1612
 
    close(devnull);
1613
 
  }
1614
 
  return true;
 
1786
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1787
                     " crashed\n", direntry->d_name);
 
1788
        free(direntry);
 
1789
        continue;
 
1790
      }
 
1791
    }
 
1792
    if(debug){
 
1793
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1794
                   direntry->d_name);
 
1795
    }
 
1796
    free(direntry);
 
1797
  }
 
1798
  free(direntries);
 
1799
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1800
    perror_plus("close");
 
1801
  } else {
 
1802
    hookdir_fd = -1;
 
1803
  }
 
1804
  close(devnull);
1615
1805
}
1616
1806
 
 
1807
__attribute__((nonnull, warn_unused_result))
1617
1808
error_t bring_up_interface(const char *const interface,
1618
1809
                           const float delay){
1619
 
  int sd = -1;
1620
1810
  error_t old_errno = errno;
1621
 
  error_t ret_errno = 0;
1622
 
  int ret, ret_setflags;
 
1811
  int ret;
1623
1812
  struct ifreq network;
1624
1813
  unsigned int if_index = if_nametoindex(interface);
1625
1814
  if(if_index == 0){
1634
1823
  }
1635
1824
  
1636
1825
  if(not interface_is_up(interface)){
1637
 
    if(not get_flags(interface, &network) and debug){
 
1826
    error_t ret_errno = 0, ioctl_errno = 0;
 
1827
    if(not get_flags(interface, &network)){
1638
1828
      ret_errno = errno;
1639
1829
      fprintf_plus(stderr, "Failed to get flags for interface "
1640
1830
                   "\"%s\"\n", interface);
 
1831
      errno = old_errno;
1641
1832
      return ret_errno;
1642
1833
    }
1643
 
    network.ifr_flags |= IFF_UP;
 
1834
    network.ifr_flags |= IFF_UP; /* set flag */
1644
1835
    
1645
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1646
 
    if(sd < 0){
 
1836
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1837
    if(sd == -1){
1647
1838
      ret_errno = errno;
1648
1839
      perror_plus("socket");
1649
1840
      errno = old_errno;
1650
1841
      return ret_errno;
1651
1842
    }
1652
 
  
 
1843
    
1653
1844
    if(quit_now){
1654
 
      close(sd);
 
1845
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1846
      if(ret == -1){
 
1847
        perror_plus("close");
 
1848
      }
1655
1849
      errno = old_errno;
1656
1850
      return EINTR;
1657
1851
    }
1661
1855
                   interface);
1662
1856
    }
1663
1857
    
1664
 
    /* Raise priviliges */
1665
 
    raise_privileges();
 
1858
    /* Raise privileges */
 
1859
    ret_errno = raise_privileges();
 
1860
    if(ret_errno != 0){
 
1861
      errno = ret_errno;
 
1862
      perror_plus("Failed to raise privileges");
 
1863
    }
1666
1864
    
1667
1865
#ifdef __linux__
1668
 
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1669
 
       messages about the network interface to mess up the prompt */
1670
 
    int ret_linux = klogctl(8, NULL, 5);
1671
 
    bool restore_loglevel = true;
1672
 
    if(ret_linux == -1){
1673
 
      restore_loglevel = false;
1674
 
      perror_plus("klogctl");
 
1866
    int ret_linux;
 
1867
    bool restore_loglevel = false;
 
1868
    if(ret_errno == 0){
 
1869
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1870
         messages about the network interface to mess up the prompt */
 
1871
      ret_linux = klogctl(8, NULL, 5);
 
1872
      if(ret_linux == -1){
 
1873
        perror_plus("klogctl");
 
1874
      } else {
 
1875
        restore_loglevel = true;
 
1876
      }
1675
1877
    }
1676
1878
#endif  /* __linux__ */
1677
 
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1678
 
    ret_errno = errno;
 
1879
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1880
    ioctl_errno = errno;
1679
1881
#ifdef __linux__
1680
1882
    if(restore_loglevel){
1681
1883
      ret_linux = klogctl(7, NULL, 0);
1685
1887
    }
1686
1888
#endif  /* __linux__ */
1687
1889
    
1688
 
    /* Lower privileges */
1689
 
    lower_privileges();
 
1890
    /* If raise_privileges() succeeded above */
 
1891
    if(ret_errno == 0){
 
1892
      /* Lower privileges */
 
1893
      ret_errno = lower_privileges();
 
1894
      if(ret_errno != 0){
 
1895
        errno = ret_errno;
 
1896
        perror_plus("Failed to lower privileges");
 
1897
      }
 
1898
    }
1690
1899
    
1691
1900
    /* Close the socket */
1692
1901
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
1695
1904
    }
1696
1905
    
1697
1906
    if(ret_setflags == -1){
1698
 
      errno = ret_errno;
 
1907
      errno = ioctl_errno;
1699
1908
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1700
1909
      errno = old_errno;
1701
 
      return ret_errno;
 
1910
      return ioctl_errno;
1702
1911
    }
1703
1912
  } else if(debug){
1704
1913
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1722
1931
  return 0;
1723
1932
}
1724
1933
 
 
1934
__attribute__((nonnull, warn_unused_result))
1725
1935
error_t take_down_interface(const char *const interface){
1726
 
  int sd = -1;
1727
1936
  error_t old_errno = errno;
1728
 
  error_t ret_errno = 0;
1729
 
  int ret, ret_setflags;
1730
1937
  struct ifreq network;
1731
1938
  unsigned int if_index = if_nametoindex(interface);
1732
1939
  if(if_index == 0){
1735
1942
    return ENXIO;
1736
1943
  }
1737
1944
  if(interface_is_up(interface)){
 
1945
    error_t ret_errno = 0, ioctl_errno = 0;
1738
1946
    if(not get_flags(interface, &network) and debug){
1739
1947
      ret_errno = errno;
1740
1948
      fprintf_plus(stderr, "Failed to get flags for interface "
1741
1949
                   "\"%s\"\n", interface);
 
1950
      errno = old_errno;
1742
1951
      return ret_errno;
1743
1952
    }
1744
1953
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1745
1954
    
1746
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1747
 
    if(sd < 0){
 
1955
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1956
    if(sd == -1){
1748
1957
      ret_errno = errno;
1749
1958
      perror_plus("socket");
1750
1959
      errno = old_errno;
1756
1965
                   interface);
1757
1966
    }
1758
1967
    
1759
 
    /* Raise priviliges */
1760
 
    raise_privileges();
1761
 
    
1762
 
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1763
 
    ret_errno = errno;
1764
 
    
1765
 
    /* Lower privileges */
1766
 
    lower_privileges();
 
1968
    /* Raise privileges */
 
1969
    ret_errno = raise_privileges();
 
1970
    if(ret_errno != 0){
 
1971
      errno = ret_errno;
 
1972
      perror_plus("Failed to raise privileges");
 
1973
    }
 
1974
    
 
1975
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1976
    ioctl_errno = errno;
 
1977
    
 
1978
    /* If raise_privileges() succeeded above */
 
1979
    if(ret_errno == 0){
 
1980
      /* Lower privileges */
 
1981
      ret_errno = lower_privileges();
 
1982
      if(ret_errno != 0){
 
1983
        errno = ret_errno;
 
1984
        perror_plus("Failed to lower privileges");
 
1985
      }
 
1986
    }
1767
1987
    
1768
1988
    /* Close the socket */
1769
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1989
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1770
1990
    if(ret == -1){
1771
1991
      perror_plus("close");
1772
1992
    }
1773
1993
    
1774
1994
    if(ret_setflags == -1){
1775
 
      errno = ret_errno;
 
1995
      errno = ioctl_errno;
1776
1996
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1777
1997
      errno = old_errno;
1778
 
      return ret_errno;
 
1998
      return ioctl_errno;
1779
1999
    }
1780
2000
  } else if(debug){
1781
2001
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1787
2007
}
1788
2008
 
1789
2009
int main(int argc, char *argv[]){
1790
 
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
 
2010
  mandos_context mc = { .server = NULL, .dh_bits = 0,
1791
2011
                        .priority = "SECURE256:!CTYPE-X.509:"
1792
 
                        "+CTYPE-OPENPGP", .current_server = NULL, 
 
2012
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
1793
2013
                        .interfaces = NULL, .interfaces_size = 0 };
1794
2014
  AvahiSServiceBrowser *sb = NULL;
1795
2015
  error_t ret_errno;
1799
2019
  int exitcode = EXIT_SUCCESS;
1800
2020
  char *interfaces_to_take_down = NULL;
1801
2021
  size_t interfaces_to_take_down_size = 0;
1802
 
  char tempdir[] = "/tmp/mandosXXXXXX";
1803
 
  bool tempdir_created = false;
 
2022
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
2023
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
2024
  char *tempdir = NULL;
1804
2025
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1805
2026
  const char *seckey = PATHDIR "/" SECKEY;
1806
2027
  const char *pubkey = PATHDIR "/" PUBKEY;
1807
2028
  char *interfaces_hooks = NULL;
1808
 
  size_t interfaces_hooks_size = 0;
1809
2029
  
1810
2030
  bool gnutls_initialized = false;
1811
2031
  bool gpgme_initialized = false;
1989
2209
    /* Work around Debian bug #633582:
1990
2210
       <http://bugs.debian.org/633582> */
1991
2211
    
1992
 
    /* Re-raise priviliges */
1993
 
    if(raise_privileges() == 0){
 
2212
    /* Re-raise privileges */
 
2213
    ret_errno = raise_privileges();
 
2214
    if(ret_errno != 0){
 
2215
      errno = ret_errno;
 
2216
      perror_plus("Failed to raise privileges");
 
2217
    } else {
1994
2218
      struct stat st;
1995
2219
      
1996
2220
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2036
2260
      }
2037
2261
    
2038
2262
      /* Lower privileges */
2039
 
      lower_privileges();
 
2263
      ret_errno = lower_privileges();
 
2264
      if(ret_errno != 0){
 
2265
        errno = ret_errno;
 
2266
        perror_plus("Failed to lower privileges");
 
2267
      }
2040
2268
    }
2041
2269
  }
2042
2270
  
2066
2294
        goto end;
2067
2295
      }
2068
2296
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2069
 
      interfaces_hooks_size = mc.interfaces_size;
2070
 
      argz_stringify(interfaces_hooks, interfaces_hooks_size,
2071
 
                     (int)',');
2072
 
    }
2073
 
    if(not run_network_hooks("start", interfaces_hooks != NULL ?
2074
 
                             interfaces_hooks : "", delay)){
2075
 
      goto end;
2076
 
    }
 
2297
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
 
2298
    }
 
2299
    run_network_hooks("start", interfaces_hooks != NULL ?
 
2300
                      interfaces_hooks : "", delay);
2077
2301
  }
2078
2302
  
2079
2303
  if(not debug){
2157
2381
  
2158
2382
  /* If no interfaces were specified, make a list */
2159
2383
  if(mc.interfaces == NULL){
2160
 
    struct dirent **direntries;
 
2384
    struct dirent **direntries = NULL;
2161
2385
    /* Look for any good interfaces */
2162
2386
    ret = scandir(sys_class_net, &direntries, good_interface,
2163
2387
                  alphasort);
2167
2391
        ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2168
2392
                             direntries[i]->d_name);
2169
2393
        if(ret_errno != 0){
 
2394
          errno = ret_errno;
2170
2395
          perror_plus("argz_add");
 
2396
          free(direntries[i]);
2171
2397
          continue;
2172
2398
        }
2173
2399
        if(debug){
2174
2400
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2175
2401
                       direntries[i]->d_name);
2176
2402
        }
 
2403
        free(direntries[i]);
2177
2404
      }
2178
2405
      free(direntries);
2179
2406
    } else {
2180
 
      free(direntries);
 
2407
      if(ret == 0){
 
2408
        free(direntries);
 
2409
      }
2181
2410
      fprintf_plus(stderr, "Could not find a network interface\n");
2182
2411
      exitcode = EXIT_FAILURE;
2183
2412
      goto end;
2206
2435
        break;
2207
2436
      }
2208
2437
      bool interface_was_up = interface_is_up(interface);
2209
 
      ret = bring_up_interface(interface, delay);
 
2438
      errno = bring_up_interface(interface, delay);
2210
2439
      if(not interface_was_up){
2211
 
        if(ret != 0){
2212
 
          errno = ret;
 
2440
        if(errno != 0){
2213
2441
          perror_plus("Failed to bring up interface");
2214
2442
        } else {
2215
 
          ret_errno = argz_add(&interfaces_to_take_down,
2216
 
                               &interfaces_to_take_down_size,
2217
 
                               interface);
 
2443
          errno = argz_add(&interfaces_to_take_down,
 
2444
                           &interfaces_to_take_down_size,
 
2445
                           interface);
 
2446
          if(errno != 0){
 
2447
            perror_plus("argz_add");
 
2448
          }
2218
2449
        }
2219
2450
      }
2220
2451
    }
2249
2480
    goto end;
2250
2481
  }
2251
2482
  
2252
 
  if(mkdtemp(tempdir) == NULL){
 
2483
  /* Try /run/tmp before /tmp */
 
2484
  tempdir = mkdtemp(run_tempdir);
 
2485
  if(tempdir == NULL and errno == ENOENT){
 
2486
      if(debug){
 
2487
        fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
 
2488
                     run_tempdir, old_tempdir);
 
2489
      }
 
2490
      tempdir = mkdtemp(old_tempdir);
 
2491
  }
 
2492
  if(tempdir == NULL){
2253
2493
    perror_plus("mkdtemp");
2254
2494
    goto end;
2255
2495
  }
2256
 
  tempdir_created = true;
2257
2496
  
2258
2497
  if(quit_now){
2259
2498
    goto end;
2331
2570
        fprintf_plus(stderr, "Retrying in %d seconds\n",
2332
2571
                     (int)retry_interval);
2333
2572
      }
2334
 
      sleep((int)retry_interval);
 
2573
      sleep((unsigned int)retry_interval);
2335
2574
    }
2336
2575
    
2337
 
    if (not quit_now){
 
2576
    if(not quit_now){
2338
2577
      exitcode = EXIT_SUCCESS;
2339
2578
    }
2340
2579
    
2395
2634
  if(debug){
2396
2635
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2397
2636
  }
2398
 
 
 
2637
  
2399
2638
  ret = avahi_loop_with_timeout(simple_poll,
2400
2639
                                (int)(retry_interval * 1000), &mc);
2401
2640
  if(debug){
2437
2676
    mc.current_server->prev->next = NULL;
2438
2677
    while(mc.current_server != NULL){
2439
2678
      server *next = mc.current_server->next;
 
2679
#ifdef __GNUC__
 
2680
#pragma GCC diagnostic push
 
2681
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2682
#endif
 
2683
      free((char *)(mc.current_server->ip));
 
2684
#ifdef __GNUC__
 
2685
#pragma GCC diagnostic pop
 
2686
#endif
2440
2687
      free(mc.current_server);
2441
2688
      mc.current_server = next;
2442
2689
    }
2443
2690
  }
2444
2691
  
2445
 
  /* Re-raise priviliges */
 
2692
  /* Re-raise privileges */
2446
2693
  {
2447
 
    raise_privileges();
2448
 
    
2449
 
    /* Run network hooks */
2450
 
    run_network_hooks("stop", interfaces_hooks != NULL ?
2451
 
                      interfaces_hooks : "", delay);
2452
 
    
2453
 
    /* Take down the network interfaces which were brought up */
2454
 
    {
2455
 
      char *interface = NULL;
2456
 
      while((interface=argz_next(interfaces_to_take_down,
2457
 
                                 interfaces_to_take_down_size,
2458
 
                                 interface))){
2459
 
        ret_errno = take_down_interface(interface);
2460
 
        if(ret_errno != 0){
2461
 
          errno = ret_errno;
2462
 
          perror_plus("Failed to take down interface");
2463
 
        }
2464
 
      }
2465
 
      if(debug and (interfaces_to_take_down == NULL)){
2466
 
        fprintf_plus(stderr, "No interfaces needed to be taken"
2467
 
                     " down\n");
2468
 
      }
2469
 
    }
2470
 
    
2471
 
    lower_privileges_permanently();
 
2694
    ret_errno = raise_privileges();
 
2695
    if(ret_errno != 0){
 
2696
      errno = ret_errno;
 
2697
      perror_plus("Failed to raise privileges");
 
2698
    } else {
 
2699
      
 
2700
      /* Run network hooks */
 
2701
      run_network_hooks("stop", interfaces_hooks != NULL ?
 
2702
                        interfaces_hooks : "", delay);
 
2703
      
 
2704
      /* Take down the network interfaces which were brought up */
 
2705
      {
 
2706
        char *interface = NULL;
 
2707
        while((interface=argz_next(interfaces_to_take_down,
 
2708
                                   interfaces_to_take_down_size,
 
2709
                                   interface))){
 
2710
          ret_errno = take_down_interface(interface);
 
2711
          if(ret_errno != 0){
 
2712
            errno = ret_errno;
 
2713
            perror_plus("Failed to take down interface");
 
2714
          }
 
2715
        }
 
2716
        if(debug and (interfaces_to_take_down == NULL)){
 
2717
          fprintf_plus(stderr, "No interfaces needed to be taken"
 
2718
                       " down\n");
 
2719
        }
 
2720
      }
 
2721
    }
 
2722
    
 
2723
    ret_errno = lower_privileges_permanently();
 
2724
    if(ret_errno != 0){
 
2725
      errno = ret_errno;
 
2726
      perror_plus("Failed to lower privileges permanently");
 
2727
    }
2472
2728
  }
2473
2729
  
2474
2730
  free(interfaces_to_take_down);
2475
2731
  free(interfaces_hooks);
2476
2732
  
2477
2733
  /* Removes the GPGME temp directory and all files inside */
2478
 
  if(tempdir_created){
 
2734
  if(tempdir != NULL){
2479
2735
    struct dirent **direntries = NULL;
2480
 
    struct dirent *direntry = NULL;
2481
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2482
 
                             alphasort);
2483
 
    if (numentries > 0){
2484
 
      for(int i = 0; i < numentries; i++){
2485
 
        direntry = direntries[i];
2486
 
        char *fullname = NULL;
2487
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2488
 
                       direntry->d_name);
2489
 
        if(ret < 0){
2490
 
          perror_plus("asprintf");
2491
 
          continue;
2492
 
        }
2493
 
        ret = remove(fullname);
2494
 
        if(ret == -1){
2495
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2496
 
                       strerror(errno));
2497
 
        }
2498
 
        free(fullname);
 
2736
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2737
                                                  O_NOFOLLOW));
 
2738
    if(tempdir_fd == -1){
 
2739
      perror_plus("open");
 
2740
    } else {
 
2741
#ifdef __GLIBC__
 
2742
#if __GLIBC_PREREQ(2, 15)
 
2743
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2744
                                 notdotentries, alphasort);
 
2745
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2746
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2747
                               alphasort);
 
2748
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2749
#else   /* not __GLIBC__ */
 
2750
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2751
                               alphasort);
 
2752
#endif  /* not __GLIBC__ */
 
2753
      if(numentries >= 0){
 
2754
        for(int i = 0; i < numentries; i++){
 
2755
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2756
          if(ret == -1){
 
2757
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2758
                         " \"%s\", 0): %s\n", tempdir,
 
2759
                         direntries[i]->d_name, strerror(errno));
 
2760
          }
 
2761
          free(direntries[i]);
 
2762
        }
 
2763
        
 
2764
        /* need to clean even if 0 because man page doesn't specify */
 
2765
        free(direntries);
 
2766
        if(numentries == -1){
 
2767
          perror_plus("scandir");
 
2768
        }
 
2769
        ret = rmdir(tempdir);
 
2770
        if(ret == -1 and errno != ENOENT){
 
2771
          perror_plus("rmdir");
 
2772
        }
2499
2773
      }
2500
 
    }
2501
 
 
2502
 
    /* need to clean even if 0 because man page doesn't specify */
2503
 
    free(direntries);
2504
 
    if (numentries == -1){
2505
 
      perror_plus("scandir");
2506
 
    }
2507
 
    ret = rmdir(tempdir);
2508
 
    if(ret == -1 and errno != ENOENT){
2509
 
      perror_plus("rmdir");
 
2774
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2510
2775
    }
2511
2776
  }
2512
2777