/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-03-10 18:03:38 UTC
  • Revision ID: teddy@recompile.se-20150310180338-pcxw6r2qmw9k6br9
Add ":!RSA" to GnuTLS priority string, to disallow non-DHE kx.

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

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

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

2. It is only available since GnuTLS 3.2.4.

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008-2013 Teddy Hogeborn
13
 
 * Copyright © 2008-2013 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);
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){
213
219
}
214
220
 
215
221
/* Add server to set of servers to retry periodically */
 
222
__attribute__((nonnull, warn_unused_result))
216
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
217
224
                int af, server **current_server){
218
225
  int ret;
227
234
                          .af = af };
228
235
  if(new_server->ip == NULL){
229
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);
230
252
    return false;
231
253
  }
232
254
  /* Special case of first server */
234
256
    new_server->next = new_server;
235
257
    new_server->prev = new_server;
236
258
    *current_server = new_server;
237
 
  /* Place the new server last in the list */
238
259
  } else {
 
260
    /* Place the new server last in the list */
239
261
    new_server->next = *current_server;
240
262
    new_server->prev = (*current_server)->prev;
241
263
    new_server->prev->next = new_server;
242
264
    (*current_server)->prev = new_server;
243
265
  }
244
 
  ret = clock_gettime(CLOCK_MONOTONIC, &(*current_server)->last_seen);
245
 
  if(ret == -1){
246
 
    perror_plus("clock_gettime");
247
 
    return false;
248
 
  }
249
266
  return true;
250
267
}
251
268
 
252
269
/* 
253
270
 * Initialize GPGME.
254
271
 */
255
 
static bool init_gpgme(const char *seckey, const char *pubkey,
256
 
                       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){
257
277
  gpgme_error_t rc;
258
278
  gpgme_engine_info_t engine_info;
259
279
  
260
280
  /*
261
281
   * Helper function to insert pub and seckey to the engine keyring.
262
282
   */
263
 
  bool import_key(const char *filename){
 
283
  bool import_key(const char * const filename){
264
284
    int ret;
265
285
    int fd;
266
286
    gpgme_data_t pgp_data;
347
367
 * Decrypt OpenPGP data.
348
368
 * Returns -1 on error
349
369
 */
 
370
__attribute__((nonnull, warn_unused_result))
350
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
351
372
                                  size_t crypto_size,
352
373
                                  char **plaintext,
472
493
  return plaintext_length;
473
494
}
474
495
 
475
 
static const char * safer_gnutls_strerror(int value){
 
496
__attribute__((warn_unused_result))
 
497
static const char *safer_gnutls_strerror(int value){
476
498
  const char *ret = gnutls_strerror(value);
477
499
  if(ret == NULL)
478
500
    ret = "(unknown)";
480
502
}
481
503
 
482
504
/* GnuTLS log function callback */
 
505
__attribute__((nonnull))
483
506
static void debuggnutls(__attribute__((unused)) int level,
484
507
                        const char* string){
485
508
  fprintf_plus(stderr, "GnuTLS: %s", string);
486
509
}
487
510
 
 
511
__attribute__((nonnull, warn_unused_result))
488
512
static int init_gnutls_global(const char *pubkeyfilename,
489
513
                              const char *seckeyfilename,
490
514
                              mandos_context *mc){
564
588
  return -1;
565
589
}
566
590
 
 
591
__attribute__((nonnull, warn_unused_result))
567
592
static int init_gnutls_session(gnutls_session_t *session,
568
593
                               mandos_context *mc){
569
594
  int ret;
626
651
                      __attribute__((unused)) const char *txt){}
627
652
 
628
653
/* Called when a Mandos server is found */
 
654
__attribute__((nonnull, warn_unused_result))
629
655
static int start_mandos_communication(const char *ip, in_port_t port,
630
656
                                      AvahiIfIndex if_index,
631
657
                                      int af, mandos_context *mc){
632
658
  int ret, tcp_sd = -1;
633
659
  ssize_t sret;
634
 
  union {
635
 
    struct sockaddr_in in;
636
 
    struct sockaddr_in6 in6;
637
 
  } to;
 
660
  struct sockaddr_storage to;
638
661
  char *buffer = NULL;
639
662
  char *decrypted_buffer = NULL;
640
663
  size_t buffer_length = 0;
721
744
  
722
745
  memset(&to, 0, sizeof(to));
723
746
  if(af == AF_INET6){
724
 
    to.in6.sin6_family = (sa_family_t)af;
725
 
    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);
726
749
  } else {                      /* IPv4 */
727
 
    to.in.sin_family = (sa_family_t)af;
728
 
    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);
729
752
  }
730
753
  if(ret < 0 ){
731
754
    int e = errno;
740
763
    goto mandos_end;
741
764
  }
742
765
  if(af == AF_INET6){
743
 
    to.in6.sin6_port = htons(port);    
744
 
#ifdef __GNUC__
745
 
#pragma GCC diagnostic push
746
 
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
747
 
#endif
748
 
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
749
 
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower */
750
 
#ifdef __GNUC__
751
 
#pragma GCC diagnostic pop
752
 
#endif
 
766
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
 
767
    if(IN6_IS_ADDR_LINKLOCAL
 
768
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
753
769
      if(if_index == AVAHI_IF_UNSPEC){
754
770
        fprintf_plus(stderr, "An IPv6 link-local address is"
755
771
                     " incomplete without a network interface\n");
757
773
        goto mandos_end;
758
774
      }
759
775
      /* Set the network interface number as scope */
760
 
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
776
      ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
761
777
    }
762
778
  } else {
763
 
    to.in.sin_port = htons(port);
 
779
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
764
780
  }
765
781
  
766
782
  if(quit_now){
783
799
    }
784
800
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
785
801
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
786
 
    const char *pcret;
787
802
    if(af == AF_INET6){
788
 
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
789
 
                        sizeof(addrstr));
 
803
      ret = getnameinfo((struct sockaddr *)&to,
 
804
                        sizeof(struct sockaddr_in6),
 
805
                        addrstr, sizeof(addrstr), NULL, 0,
 
806
                        NI_NUMERICHOST);
790
807
    } else {
791
 
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
792
 
                        sizeof(addrstr));
 
808
      ret = getnameinfo((struct sockaddr *)&to,
 
809
                        sizeof(struct sockaddr_in),
 
810
                        addrstr, sizeof(addrstr), NULL, 0,
 
811
                        NI_NUMERICHOST);
793
812
    }
794
 
    if(pcret == NULL){
795
 
      perror_plus("inet_ntop");
796
 
    } else {
797
 
      if(strcmp(addrstr, ip) != 0){
798
 
        fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
799
 
      }
 
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);
800
819
    }
801
820
  }
802
821
  
806
825
  }
807
826
  
808
827
  if(af == AF_INET6){
809
 
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
828
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
829
                  sizeof(struct sockaddr_in6));
810
830
  } else {
811
 
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
831
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
832
                  sizeof(struct sockaddr_in));
812
833
  }
813
834
  if(ret < 0){
814
 
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
835
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
815
836
      int e = errno;
816
837
      perror_plus("connect");
817
838
      errno = e;
1032
1053
  return retval;
1033
1054
}
1034
1055
 
 
1056
__attribute__((nonnull))
1035
1057
static void resolve_callback(AvahiSServiceResolver *r,
1036
1058
                             AvahiIfIndex interface,
1037
1059
                             AvahiProtocol proto,
1045
1067
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
1046
1068
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
1047
1069
                             flags,
1048
 
                             void* mc){
 
1070
                             void *mc){
1049
1071
  if(r == NULL){
1050
1072
    return;
1051
1073
  }
1054
1076
     timed out */
1055
1077
  
1056
1078
  if(quit_now){
 
1079
    avahi_s_service_resolver_free(r);
1057
1080
    return;
1058
1081
  }
1059
1082
  
1104
1127
                            const char *domain,
1105
1128
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
1106
1129
                            flags,
1107
 
                            void* mc){
 
1130
                            void *mc){
1108
1131
  if(b == NULL){
1109
1132
    return;
1110
1133
  }
1170
1193
  errno = old_errno;
1171
1194
}
1172
1195
 
 
1196
__attribute__((nonnull, warn_unused_result))
1173
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1174
1198
  int ret;
1175
1199
  error_t ret_errno;
1194
1218
  return true;
1195
1219
}
1196
1220
 
 
1221
__attribute__((nonnull, warn_unused_result))
1197
1222
bool good_flags(const char *ifname, const struct ifreq *ifr){
1198
1223
  
1199
1224
  /* Reject the loopback device */
1241
1266
 * corresponds to an acceptable network device.
1242
1267
 * (This function is passed to scandir(3) as a filter function.)
1243
1268
 */
 
1269
__attribute__((nonnull, warn_unused_result))
1244
1270
int good_interface(const struct dirent *if_entry){
1245
1271
  if(if_entry->d_name[0] == '.'){
1246
1272
    return 0;
1264
1290
/* 
1265
1291
 * This function determines if a network interface is up.
1266
1292
 */
 
1293
__attribute__((nonnull, warn_unused_result))
1267
1294
bool interface_is_up(const char *interface){
1268
1295
  struct ifreq ifr;
1269
1296
  if(not get_flags(interface, &ifr)){
1280
1307
/* 
1281
1308
 * This function determines if a network interface is running
1282
1309
 */
 
1310
__attribute__((nonnull, warn_unused_result))
1283
1311
bool interface_is_running(const char *interface){
1284
1312
  struct ifreq ifr;
1285
1313
  if(not get_flags(interface, &ifr)){
1293
1321
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
1294
1322
}
1295
1323
 
 
1324
__attribute__((nonnull, pure, warn_unused_result))
1296
1325
int notdotentries(const struct dirent *direntry){
1297
1326
  /* Skip "." and ".." */
1298
1327
  if(direntry->d_name[0] == '.'
1305
1334
}
1306
1335
 
1307
1336
/* Is this directory entry a runnable program? */
 
1337
__attribute__((nonnull, warn_unused_result))
1308
1338
int runnable_hook(const struct dirent *direntry){
1309
1339
  int ret;
1310
1340
  size_t sret;
1318
1348
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1319
1349
                "abcdefghijklmnopqrstuvwxyz"
1320
1350
                "0123456789"
1321
 
                "_-");
 
1351
                "_.-");
1322
1352
  if((direntry->d_name)[sret] != '\0'){
1323
1353
    /* Contains non-allowed characters */
1324
1354
    if(debug){
1328
1358
    return 0;
1329
1359
  }
1330
1360
  
1331
 
  char *fullname = NULL;
1332
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1333
 
  if(ret < 0){
1334
 
    perror_plus("asprintf");
1335
 
    return 0;
1336
 
  }
1337
 
  
1338
 
  ret = stat(fullname, &st);
 
1361
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1339
1362
  if(ret == -1){
1340
1363
    if(debug){
1341
1364
      perror_plus("Could not stat hook");
1365
1388
  return 1;
1366
1389
}
1367
1390
 
 
1391
__attribute__((nonnull, warn_unused_result))
1368
1392
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1369
1393
                            mandos_context *mc){
1370
1394
  int ret;
1374
1398
  
1375
1399
  while(true){
1376
1400
    if(mc->current_server == NULL){
1377
 
      if (debug){
 
1401
      if(debug){
1378
1402
        fprintf_plus(stderr, "Wait until first server is found."
1379
1403
                     " No timeout!\n");
1380
1404
      }
1381
1405
      ret = avahi_simple_poll_iterate(s, -1);
1382
1406
    } else {
1383
 
      if (debug){
 
1407
      if(debug){
1384
1408
        fprintf_plus(stderr, "Check current_server if we should run"
1385
1409
                     " it, or wait\n");
1386
1410
      }
1403
1427
                     - ((intmax_t)waited_time.tv_sec * 1000))
1404
1428
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1405
1429
      
1406
 
      if (debug){
 
1430
      if(debug){
1407
1431
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1408
1432
                     block_time);
1409
1433
      }
1431
1455
      ret = avahi_simple_poll_iterate(s, (int)block_time);
1432
1456
    }
1433
1457
    if(ret != 0){
1434
 
      if (ret > 0 or errno != EINTR){
 
1458
      if(ret > 0 or errno != EINTR){
1435
1459
        return (ret != 1) ? ret : 0;
1436
1460
      }
1437
1461
    }
1439
1463
}
1440
1464
 
1441
1465
/* Set effective uid to 0, return errno */
 
1466
__attribute__((warn_unused_result))
1442
1467
error_t raise_privileges(void){
1443
1468
  error_t old_errno = errno;
1444
1469
  error_t ret_errno = 0;
1445
1470
  if(seteuid(0) == -1){
1446
1471
    ret_errno = errno;
1447
 
    perror_plus("seteuid");
1448
1472
  }
1449
1473
  errno = old_errno;
1450
1474
  return ret_errno;
1451
1475
}
1452
1476
 
1453
1477
/* Set effective and real user ID to 0.  Return errno. */
 
1478
__attribute__((warn_unused_result))
1454
1479
error_t raise_privileges_permanently(void){
1455
1480
  error_t old_errno = errno;
1456
1481
  error_t ret_errno = raise_privileges();
1460
1485
  }
1461
1486
  if(setuid(0) == -1){
1462
1487
    ret_errno = errno;
1463
 
    perror_plus("seteuid");
1464
1488
  }
1465
1489
  errno = old_errno;
1466
1490
  return ret_errno;
1467
1491
}
1468
1492
 
1469
1493
/* Set effective user ID to unprivileged saved user ID */
 
1494
__attribute__((warn_unused_result))
1470
1495
error_t lower_privileges(void){
1471
1496
  error_t old_errno = errno;
1472
1497
  error_t ret_errno = 0;
1473
1498
  if(seteuid(uid) == -1){
1474
1499
    ret_errno = errno;
1475
 
    perror_plus("seteuid");
1476
1500
  }
1477
1501
  errno = old_errno;
1478
1502
  return ret_errno;
1479
1503
}
1480
1504
 
1481
1505
/* Lower privileges permanently */
 
1506
__attribute__((warn_unused_result))
1482
1507
error_t lower_privileges_permanently(void){
1483
1508
  error_t old_errno = errno;
1484
1509
  error_t ret_errno = 0;
1485
1510
  if(setuid(uid) == -1){
1486
1511
    ret_errno = errno;
1487
 
    perror_plus("setuid");
1488
1512
  }
1489
1513
  errno = old_errno;
1490
1514
  return ret_errno;
1491
1515
}
1492
1516
 
1493
 
bool run_network_hooks(const char *mode, const char *interface,
 
1517
__attribute__((nonnull))
 
1518
void run_network_hooks(const char *mode, const char *interface,
1494
1519
                       const float delay){
1495
 
  struct dirent **direntries;
1496
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1497
 
                         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__ */
1498
1547
  if(numhooks == -1){
1499
 
    if(errno == ENOENT){
1500
 
      if(debug){
1501
 
        fprintf_plus(stderr, "Network hook directory \"%s\" not"
1502
 
                     " found\n", hookdir);
1503
 
      }
1504
 
    } else {
1505
 
      perror_plus("scandir");
 
1548
    perror_plus("scandir");
 
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);
1506
1559
    }
1507
 
  } else {
1508
 
    struct dirent *direntry;
1509
 
    int ret;
1510
 
    int devnull = open("/dev/null", O_RDONLY);
1511
 
    for(int i = 0; i < numhooks; i++){
1512
 
      direntry = direntries[i];
1513
 
      char *fullname = NULL;
1514
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1515
 
      if(ret < 0){
 
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){
1516
1621
        perror_plus("asprintf");
1517
 
        continue;
1518
 
      }
1519
 
      if(debug){
1520
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1521
 
                     direntry->d_name);
1522
 
      }
1523
 
      pid_t hook_pid = fork();
1524
 
      if(hook_pid == 0){
1525
 
        /* Child */
1526
 
        /* Raise privileges */
1527
 
        raise_privileges_permanently();
1528
 
        /* Set group */
1529
 
        errno = 0;
1530
 
        ret = setgid(0);
1531
 
        if(ret == -1){
1532
 
          perror_plus("setgid");
1533
 
        }
1534
 
        /* Reset supplementary groups */
1535
 
        errno = 0;
1536
 
        ret = setgroups(0, NULL);
1537
 
        if(ret == -1){
1538
 
          perror_plus("setgroups");
1539
 
        }
1540
 
        dup2(devnull, STDIN_FILENO);
1541
 
        close(devnull);
1542
 
        dup2(STDERR_FILENO, STDOUT_FILENO);
1543
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1544
 
        if(ret == -1){
1545
 
          perror_plus("setenv");
1546
 
          _exit(EX_OSERR);
1547
 
        }
1548
 
        ret = setenv("DEVICE", interface, 1);
1549
 
        if(ret == -1){
1550
 
          perror_plus("setenv");
1551
 
          _exit(EX_OSERR);
1552
 
        }
1553
 
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1554
 
        if(ret == -1){
1555
 
          perror_plus("setenv");
1556
 
          _exit(EX_OSERR);
1557
 
        }
1558
 
        ret = setenv("MODE", mode, 1);
1559
 
        if(ret == -1){
1560
 
          perror_plus("setenv");
1561
 
          _exit(EX_OSERR);
1562
 
        }
1563
 
        char *delaystring;
1564
 
        ret = asprintf(&delaystring, "%f", delay);
1565
 
        if(ret == -1){
1566
 
          perror_plus("asprintf");
1567
 
          _exit(EX_OSERR);
1568
 
        }
1569
 
        ret = setenv("DELAY", delaystring, 1);
1570
 
        if(ret == -1){
1571
 
          free(delaystring);
1572
 
          perror_plus("setenv");
1573
 
          _exit(EX_OSERR);
1574
 
        }
 
1622
        _exit(EX_OSERR);
 
1623
      }
 
1624
      ret = setenv("DELAY", delaystring, 1);
 
1625
      if(ret == -1){
1575
1626
        free(delaystring);
1576
 
        if(connect_to != NULL){
1577
 
          ret = setenv("CONNECT", connect_to, 1);
1578
 
          if(ret == -1){
1579
 
            perror_plus("setenv");
1580
 
            _exit(EX_OSERR);
1581
 
          }
1582
 
        }
1583
 
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1584
 
          perror_plus("execl");
1585
 
          _exit(EXIT_FAILURE);
1586
 
        }
 
1627
        perror_plus("setenv");
 
1628
        _exit(EX_OSERR);
 
1629
      }
 
1630
      free(delaystring);
 
1631
      if(connect_to != NULL){
 
1632
        ret = setenv("CONNECT", connect_to, 1);
 
1633
        if(ret == -1){
 
1634
          perror_plus("setenv");
 
1635
          _exit(EX_OSERR);
 
1636
        }
 
1637
      }
 
1638
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
 
1639
      if(hook_fd == -1){
 
1640
        perror_plus("openat");
 
1641
        _exit(EXIT_FAILURE);
 
1642
      }
 
1643
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1644
        perror_plus("close");
 
1645
        _exit(EXIT_FAILURE);
 
1646
      }
 
1647
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
1648
                 environ) == -1){
 
1649
        perror_plus("fexecve");
 
1650
        _exit(EXIT_FAILURE);
 
1651
      }
 
1652
    } else {
 
1653
      if(hook_pid == -1){
 
1654
        perror_plus("fork");
 
1655
        free(direntry);
 
1656
        continue;
 
1657
      }
 
1658
      int status;
 
1659
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1660
        perror_plus("waitpid");
 
1661
        free(direntry);
 
1662
        continue;
 
1663
      }
 
1664
      if(WIFEXITED(status)){
 
1665
        if(WEXITSTATUS(status) != 0){
 
1666
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1667
                       " with status %d\n", direntry->d_name,
 
1668
                       WEXITSTATUS(status));
 
1669
          free(direntry);
 
1670
          continue;
 
1671
        }
 
1672
      } else if(WIFSIGNALED(status)){
 
1673
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1674
                     " signal %d\n", direntry->d_name,
 
1675
                     WTERMSIG(status));
 
1676
        free(direntry);
 
1677
        continue;
1587
1678
      } else {
1588
 
        int status;
1589
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1590
 
          perror_plus("waitpid");
1591
 
          free(fullname);
1592
 
          continue;
1593
 
        }
1594
 
        if(WIFEXITED(status)){
1595
 
          if(WEXITSTATUS(status) != 0){
1596
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1597
 
                         " with status %d\n", direntry->d_name,
1598
 
                         WEXITSTATUS(status));
1599
 
            free(fullname);
1600
 
            continue;
1601
 
          }
1602
 
        } else if(WIFSIGNALED(status)){
1603
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1604
 
                       " signal %d\n", direntry->d_name,
1605
 
                       WTERMSIG(status));
1606
 
          free(fullname);
1607
 
          continue;
1608
 
        } else {
1609
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1610
 
                       " crashed\n", direntry->d_name);
1611
 
          free(fullname);
1612
 
          continue;
1613
 
        }
1614
 
      }
1615
 
      free(fullname);
1616
 
      if(debug){
1617
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1618
 
                     direntry->d_name);
1619
 
      }
1620
 
    }
1621
 
    close(devnull);
1622
 
  }
1623
 
  return true;
 
1679
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1680
                     " crashed\n", direntry->d_name);
 
1681
        free(direntry);
 
1682
        continue;
 
1683
      }
 
1684
    }
 
1685
    if(debug){
 
1686
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1687
                   direntry->d_name);
 
1688
    }
 
1689
    free(direntry);
 
1690
  }
 
1691
  free(direntries);
 
1692
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1693
    perror_plus("close");
 
1694
  } else {
 
1695
    hookdir_fd = -1;
 
1696
  }
 
1697
  close(devnull);
1624
1698
}
1625
1699
 
 
1700
__attribute__((nonnull, warn_unused_result))
1626
1701
error_t bring_up_interface(const char *const interface,
1627
1702
                           const float delay){
1628
 
  int sd = -1;
1629
1703
  error_t old_errno = errno;
1630
 
  error_t ret_errno = 0;
1631
 
  int ret, ret_setflags;
 
1704
  int ret;
1632
1705
  struct ifreq network;
1633
1706
  unsigned int if_index = if_nametoindex(interface);
1634
1707
  if(if_index == 0){
1643
1716
  }
1644
1717
  
1645
1718
  if(not interface_is_up(interface)){
1646
 
    if(not get_flags(interface, &network) and debug){
 
1719
    error_t ret_errno = 0, ioctl_errno = 0;
 
1720
    if(not get_flags(interface, &network)){
1647
1721
      ret_errno = errno;
1648
1722
      fprintf_plus(stderr, "Failed to get flags for interface "
1649
1723
                   "\"%s\"\n", interface);
 
1724
      errno = old_errno;
1650
1725
      return ret_errno;
1651
1726
    }
1652
 
    network.ifr_flags |= IFF_UP;
 
1727
    network.ifr_flags |= IFF_UP; /* set flag */
1653
1728
    
1654
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1655
 
    if(sd < 0){
 
1729
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1730
    if(sd == -1){
1656
1731
      ret_errno = errno;
1657
1732
      perror_plus("socket");
1658
1733
      errno = old_errno;
1659
1734
      return ret_errno;
1660
1735
    }
1661
 
  
 
1736
    
1662
1737
    if(quit_now){
1663
 
      close(sd);
 
1738
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1739
      if(ret == -1){
 
1740
        perror_plus("close");
 
1741
      }
1664
1742
      errno = old_errno;
1665
1743
      return EINTR;
1666
1744
    }
1670
1748
                   interface);
1671
1749
    }
1672
1750
    
1673
 
    /* Raise priviliges */
1674
 
    raise_privileges();
 
1751
    /* Raise privileges */
 
1752
    ret_errno = raise_privileges();
 
1753
    if(ret_errno != 0){
 
1754
      errno = ret_errno;
 
1755
      perror_plus("Failed to raise privileges");
 
1756
    }
1675
1757
    
1676
1758
#ifdef __linux__
1677
 
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1678
 
       messages about the network interface to mess up the prompt */
1679
 
    int ret_linux = klogctl(8, NULL, 5);
1680
 
    bool restore_loglevel = true;
1681
 
    if(ret_linux == -1){
1682
 
      restore_loglevel = false;
1683
 
      perror_plus("klogctl");
 
1759
    int ret_linux;
 
1760
    bool restore_loglevel = false;
 
1761
    if(ret_errno == 0){
 
1762
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1763
         messages about the network interface to mess up the prompt */
 
1764
      ret_linux = klogctl(8, NULL, 5);
 
1765
      if(ret_linux == -1){
 
1766
        perror_plus("klogctl");
 
1767
      } else {
 
1768
        restore_loglevel = true;
 
1769
      }
1684
1770
    }
1685
1771
#endif  /* __linux__ */
1686
 
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1687
 
    ret_errno = errno;
 
1772
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1773
    ioctl_errno = errno;
1688
1774
#ifdef __linux__
1689
1775
    if(restore_loglevel){
1690
1776
      ret_linux = klogctl(7, NULL, 0);
1694
1780
    }
1695
1781
#endif  /* __linux__ */
1696
1782
    
1697
 
    /* Lower privileges */
1698
 
    lower_privileges();
 
1783
    /* If raise_privileges() succeeded above */
 
1784
    if(ret_errno == 0){
 
1785
      /* Lower privileges */
 
1786
      ret_errno = lower_privileges();
 
1787
      if(ret_errno != 0){
 
1788
        errno = ret_errno;
 
1789
        perror_plus("Failed to lower privileges");
 
1790
      }
 
1791
    }
1699
1792
    
1700
1793
    /* Close the socket */
1701
1794
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
1704
1797
    }
1705
1798
    
1706
1799
    if(ret_setflags == -1){
1707
 
      errno = ret_errno;
 
1800
      errno = ioctl_errno;
1708
1801
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1709
1802
      errno = old_errno;
1710
 
      return ret_errno;
 
1803
      return ioctl_errno;
1711
1804
    }
1712
1805
  } else if(debug){
1713
1806
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1731
1824
  return 0;
1732
1825
}
1733
1826
 
 
1827
__attribute__((nonnull, warn_unused_result))
1734
1828
error_t take_down_interface(const char *const interface){
1735
1829
  error_t old_errno = errno;
1736
1830
  struct ifreq network;
1741
1835
    return ENXIO;
1742
1836
  }
1743
1837
  if(interface_is_up(interface)){
1744
 
    error_t ret_errno = 0;
 
1838
    error_t ret_errno = 0, ioctl_errno = 0;
1745
1839
    if(not get_flags(interface, &network) and debug){
1746
1840
      ret_errno = errno;
1747
1841
      fprintf_plus(stderr, "Failed to get flags for interface "
1748
1842
                   "\"%s\"\n", interface);
 
1843
      errno = old_errno;
1749
1844
      return ret_errno;
1750
1845
    }
1751
1846
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1752
1847
    
1753
1848
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1754
 
    if(sd < 0){
 
1849
    if(sd == -1){
1755
1850
      ret_errno = errno;
1756
1851
      perror_plus("socket");
1757
1852
      errno = old_errno;
1763
1858
                   interface);
1764
1859
    }
1765
1860
    
1766
 
    /* Raise priviliges */
1767
 
    raise_privileges();
 
1861
    /* Raise privileges */
 
1862
    ret_errno = raise_privileges();
 
1863
    if(ret_errno != 0){
 
1864
      errno = ret_errno;
 
1865
      perror_plus("Failed to raise privileges");
 
1866
    }
1768
1867
    
1769
1868
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1770
 
    ret_errno = errno;
 
1869
    ioctl_errno = errno;
1771
1870
    
1772
 
    /* Lower privileges */
1773
 
    lower_privileges();
 
1871
    /* If raise_privileges() succeeded above */
 
1872
    if(ret_errno == 0){
 
1873
      /* Lower privileges */
 
1874
      ret_errno = lower_privileges();
 
1875
      if(ret_errno != 0){
 
1876
        errno = ret_errno;
 
1877
        perror_plus("Failed to lower privileges");
 
1878
      }
 
1879
    }
1774
1880
    
1775
1881
    /* Close the socket */
1776
1882
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1779
1885
    }
1780
1886
    
1781
1887
    if(ret_setflags == -1){
1782
 
      errno = ret_errno;
 
1888
      errno = ioctl_errno;
1783
1889
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1784
1890
      errno = old_errno;
1785
 
      return ret_errno;
 
1891
      return ioctl_errno;
1786
1892
    }
1787
1893
  } else if(debug){
1788
1894
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1796
1902
int main(int argc, char *argv[]){
1797
1903
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
1798
1904
                        .priority = "SECURE256:!CTYPE-X.509:"
1799
 
                        "+CTYPE-OPENPGP", .current_server = NULL, 
 
1905
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
1800
1906
                        .interfaces = NULL, .interfaces_size = 0 };
1801
1907
  AvahiSServiceBrowser *sb = NULL;
1802
1908
  error_t ret_errno;
1806
1912
  int exitcode = EXIT_SUCCESS;
1807
1913
  char *interfaces_to_take_down = NULL;
1808
1914
  size_t interfaces_to_take_down_size = 0;
1809
 
  char tempdir[] = "/tmp/mandosXXXXXX";
1810
 
  bool tempdir_created = false;
 
1915
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
1916
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
1917
  char *tempdir = NULL;
1811
1918
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1812
1919
  const char *seckey = PATHDIR "/" SECKEY;
1813
1920
  const char *pubkey = PATHDIR "/" PUBKEY;
1995
2102
    /* Work around Debian bug #633582:
1996
2103
       <http://bugs.debian.org/633582> */
1997
2104
    
1998
 
    /* Re-raise priviliges */
1999
 
    if(raise_privileges() == 0){
 
2105
    /* Re-raise privileges */
 
2106
    ret_errno = raise_privileges();
 
2107
    if(ret_errno != 0){
 
2108
      errno = ret_errno;
 
2109
      perror_plus("Failed to raise privileges");
 
2110
    } else {
2000
2111
      struct stat st;
2001
2112
      
2002
2113
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2042
2153
      }
2043
2154
    
2044
2155
      /* Lower privileges */
2045
 
      lower_privileges();
 
2156
      ret_errno = lower_privileges();
 
2157
      if(ret_errno != 0){
 
2158
        errno = ret_errno;
 
2159
        perror_plus("Failed to lower privileges");
 
2160
      }
2046
2161
    }
2047
2162
  }
2048
2163
  
2074
2189
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2075
2190
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2076
2191
    }
2077
 
    if(not run_network_hooks("start", interfaces_hooks != NULL ?
2078
 
                             interfaces_hooks : "", delay)){
2079
 
      goto end;
2080
 
    }
 
2192
    run_network_hooks("start", interfaces_hooks != NULL ?
 
2193
                      interfaces_hooks : "", delay);
2081
2194
  }
2082
2195
  
2083
2196
  if(not debug){
2161
2274
  
2162
2275
  /* If no interfaces were specified, make a list */
2163
2276
  if(mc.interfaces == NULL){
2164
 
    struct dirent **direntries;
 
2277
    struct dirent **direntries = NULL;
2165
2278
    /* Look for any good interfaces */
2166
2279
    ret = scandir(sys_class_net, &direntries, good_interface,
2167
2280
                  alphasort);
2173
2286
        if(ret_errno != 0){
2174
2287
          errno = ret_errno;
2175
2288
          perror_plus("argz_add");
 
2289
          free(direntries[i]);
2176
2290
          continue;
2177
2291
        }
2178
2292
        if(debug){
2179
2293
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2180
2294
                       direntries[i]->d_name);
2181
2295
        }
 
2296
        free(direntries[i]);
2182
2297
      }
2183
2298
      free(direntries);
2184
2299
    } else {
2185
 
      free(direntries);
 
2300
      if(ret == 0){
 
2301
        free(direntries);
 
2302
      }
2186
2303
      fprintf_plus(stderr, "Could not find a network interface\n");
2187
2304
      exitcode = EXIT_FAILURE;
2188
2305
      goto end;
2211
2328
        break;
2212
2329
      }
2213
2330
      bool interface_was_up = interface_is_up(interface);
2214
 
      ret = bring_up_interface(interface, delay);
 
2331
      errno = bring_up_interface(interface, delay);
2215
2332
      if(not interface_was_up){
2216
 
        if(ret != 0){
2217
 
          errno = ret;
 
2333
        if(errno != 0){
2218
2334
          perror_plus("Failed to bring up interface");
2219
2335
        } else {
2220
 
          ret_errno = argz_add(&interfaces_to_take_down,
2221
 
                               &interfaces_to_take_down_size,
2222
 
                               interface);
2223
 
          if(ret_errno != 0){
2224
 
            errno = ret_errno;
 
2336
          errno = argz_add(&interfaces_to_take_down,
 
2337
                           &interfaces_to_take_down_size,
 
2338
                           interface);
 
2339
          if(errno != 0){
2225
2340
            perror_plus("argz_add");
2226
2341
          }
2227
2342
        }
2258
2373
    goto end;
2259
2374
  }
2260
2375
  
2261
 
  if(mkdtemp(tempdir) == NULL){
 
2376
  /* Try /run/tmp before /tmp */
 
2377
  tempdir = mkdtemp(run_tempdir);
 
2378
  if(tempdir == NULL and errno == ENOENT){
 
2379
      if(debug){
 
2380
        fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
 
2381
                     run_tempdir, old_tempdir);
 
2382
      }
 
2383
      tempdir = mkdtemp(old_tempdir);
 
2384
  }
 
2385
  if(tempdir == NULL){
2262
2386
    perror_plus("mkdtemp");
2263
2387
    goto end;
2264
2388
  }
2265
 
  tempdir_created = true;
2266
2389
  
2267
2390
  if(quit_now){
2268
2391
    goto end;
2343
2466
      sleep((unsigned int)retry_interval);
2344
2467
    }
2345
2468
    
2346
 
    if (not quit_now){
 
2469
    if(not quit_now){
2347
2470
      exitcode = EXIT_SUCCESS;
2348
2471
    }
2349
2472
    
2404
2527
  if(debug){
2405
2528
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2406
2529
  }
2407
 
 
 
2530
  
2408
2531
  ret = avahi_loop_with_timeout(simple_poll,
2409
2532
                                (int)(retry_interval * 1000), &mc);
2410
2533
  if(debug){
2446
2569
    mc.current_server->prev->next = NULL;
2447
2570
    while(mc.current_server != NULL){
2448
2571
      server *next = mc.current_server->next;
 
2572
#ifdef __GNUC__
 
2573
#pragma GCC diagnostic push
 
2574
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2575
#endif
 
2576
      free((char *)(mc.current_server->ip));
 
2577
#ifdef __GNUC__
 
2578
#pragma GCC diagnostic pop
 
2579
#endif
2449
2580
      free(mc.current_server);
2450
2581
      mc.current_server = next;
2451
2582
    }
2452
2583
  }
2453
2584
  
2454
 
  /* Re-raise priviliges */
 
2585
  /* Re-raise privileges */
2455
2586
  {
2456
 
    raise_privileges();
2457
 
    
2458
 
    /* Run network hooks */
2459
 
    run_network_hooks("stop", interfaces_hooks != NULL ?
2460
 
                      interfaces_hooks : "", delay);
2461
 
    
2462
 
    /* Take down the network interfaces which were brought up */
2463
 
    {
2464
 
      char *interface = NULL;
2465
 
      while((interface=argz_next(interfaces_to_take_down,
2466
 
                                 interfaces_to_take_down_size,
2467
 
                                 interface))){
2468
 
        ret_errno = take_down_interface(interface);
2469
 
        if(ret_errno != 0){
2470
 
          errno = ret_errno;
2471
 
          perror_plus("Failed to take down interface");
2472
 
        }
2473
 
      }
2474
 
      if(debug and (interfaces_to_take_down == NULL)){
2475
 
        fprintf_plus(stderr, "No interfaces needed to be taken"
2476
 
                     " down\n");
2477
 
      }
2478
 
    }
2479
 
    
2480
 
    lower_privileges_permanently();
 
2587
    ret_errno = raise_privileges();
 
2588
    if(ret_errno != 0){
 
2589
      errno = ret_errno;
 
2590
      perror_plus("Failed to raise privileges");
 
2591
    } else {
 
2592
      
 
2593
      /* Run network hooks */
 
2594
      run_network_hooks("stop", interfaces_hooks != NULL ?
 
2595
                        interfaces_hooks : "", delay);
 
2596
      
 
2597
      /* Take down the network interfaces which were brought up */
 
2598
      {
 
2599
        char *interface = NULL;
 
2600
        while((interface=argz_next(interfaces_to_take_down,
 
2601
                                   interfaces_to_take_down_size,
 
2602
                                   interface))){
 
2603
          ret_errno = take_down_interface(interface);
 
2604
          if(ret_errno != 0){
 
2605
            errno = ret_errno;
 
2606
            perror_plus("Failed to take down interface");
 
2607
          }
 
2608
        }
 
2609
        if(debug and (interfaces_to_take_down == NULL)){
 
2610
          fprintf_plus(stderr, "No interfaces needed to be taken"
 
2611
                       " down\n");
 
2612
        }
 
2613
      }
 
2614
    }
 
2615
    
 
2616
    ret_errno = lower_privileges_permanently();
 
2617
    if(ret_errno != 0){
 
2618
      errno = ret_errno;
 
2619
      perror_plus("Failed to lower privileges permanently");
 
2620
    }
2481
2621
  }
2482
2622
  
2483
2623
  free(interfaces_to_take_down);
2484
2624
  free(interfaces_hooks);
2485
2625
  
2486
2626
  /* Removes the GPGME temp directory and all files inside */
2487
 
  if(tempdir_created){
 
2627
  if(tempdir != NULL){
2488
2628
    struct dirent **direntries = NULL;
2489
 
    struct dirent *direntry = NULL;
2490
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2491
 
                             alphasort);
2492
 
    if (numentries > 0){
2493
 
      for(int i = 0; i < numentries; i++){
2494
 
        direntry = direntries[i];
2495
 
        char *fullname = NULL;
2496
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2497
 
                       direntry->d_name);
2498
 
        if(ret < 0){
2499
 
          perror_plus("asprintf");
2500
 
          continue;
2501
 
        }
2502
 
        ret = remove(fullname);
2503
 
        if(ret == -1){
2504
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2505
 
                       strerror(errno));
2506
 
        }
2507
 
        free(fullname);
 
2629
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2630
                                                  O_NOFOLLOW));
 
2631
    if(tempdir_fd == -1){
 
2632
      perror_plus("open");
 
2633
    } else {
 
2634
#ifdef __GLIBC__
 
2635
#if __GLIBC_PREREQ(2, 15)
 
2636
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2637
                                 notdotentries, alphasort);
 
2638
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2639
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2640
                               alphasort);
 
2641
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2642
#else   /* not __GLIBC__ */
 
2643
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2644
                               alphasort);
 
2645
#endif  /* not __GLIBC__ */
 
2646
      if(numentries >= 0){
 
2647
        for(int i = 0; i < numentries; i++){
 
2648
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2649
          if(ret == -1){
 
2650
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2651
                         " \"%s\", 0): %s\n", tempdir,
 
2652
                         direntries[i]->d_name, strerror(errno));
 
2653
          }
 
2654
          free(direntries[i]);
 
2655
        }
 
2656
        
 
2657
        /* need to clean even if 0 because man page doesn't specify */
 
2658
        free(direntries);
 
2659
        if(numentries == -1){
 
2660
          perror_plus("scandir");
 
2661
        }
 
2662
        ret = rmdir(tempdir);
 
2663
        if(ret == -1 and errno != ENOENT){
 
2664
          perror_plus("rmdir");
 
2665
        }
2508
2666
      }
2509
 
    }
2510
 
 
2511
 
    /* need to clean even if 0 because man page doesn't specify */
2512
 
    free(direntries);
2513
 
    if (numentries == -1){
2514
 
      perror_plus("scandir");
2515
 
    }
2516
 
    ret = rmdir(tempdir);
2517
 
    if(ret == -1 and errno != ENOENT){
2518
 
      perror_plus("rmdir");
 
2667
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2519
2668
    }
2520
2669
  }
2521
2670