/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(),
57
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
58
                                   inet_pton(), connect(),
59
59
                                   getnameinfo() */
60
 
#include <fcntl.h>              /* open() */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
61
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
62
62
                                 */
63
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
73
73
                                */
74
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
75
75
                                   getuid(), getgid(), seteuid(),
76
 
                                   setgid(), pause(), _exit() */
 
76
                                   setgid(), pause(), _exit(),
 
77
                                   unlinkat() */
77
78
#include <arpa/inet.h>          /* inet_pton(), htons() */
78
79
#include <iso646.h>             /* not, or, and */
79
80
#include <argp.h>               /* struct argp_option, error_t, struct
141
142
static const char sys_class_net[] = "/sys/class/net";
142
143
char *connect_to = NULL;
143
144
const char *hookdir = HOOKDIR;
 
145
int hookdir_fd = -1;
144
146
uid_t uid = 65534;
145
147
gid_t gid = 65534;
146
148
 
183
185
  perror(print_text);
184
186
}
185
187
 
186
 
__attribute__((format (gnu_printf, 2, 3)))
 
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
187
189
int fprintf_plus(FILE *stream, const char *format, ...){
188
190
  va_list ap;
189
191
  va_start (ap, format);
198
200
 * bytes. "buffer_capacity" is how much is currently allocated,
199
201
 * "buffer_length" is how much is already used.
200
202
 */
 
203
__attribute__((nonnull, warn_unused_result))
201
204
size_t incbuffer(char **buffer, size_t buffer_length,
202
205
                 size_t buffer_capacity){
203
206
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
216
219
}
217
220
 
218
221
/* Add server to set of servers to retry periodically */
 
222
__attribute__((nonnull, warn_unused_result))
219
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
220
224
                int af, server **current_server){
221
225
  int ret;
230
234
                          .af = af };
231
235
  if(new_server->ip == NULL){
232
236
    perror_plus("strdup");
 
237
    free(new_server);
233
238
    return false;
234
239
  }
235
240
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
236
241
  if(ret == -1){
237
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);
238
252
    return false;
239
253
  }
240
254
  /* Special case of first server */
242
256
    new_server->next = new_server;
243
257
    new_server->prev = new_server;
244
258
    *current_server = new_server;
245
 
  /* Place the new server last in the list */
246
259
  } else {
 
260
    /* Place the new server last in the list */
247
261
    new_server->next = *current_server;
248
262
    new_server->prev = (*current_server)->prev;
249
263
    new_server->prev->next = new_server;
255
269
/* 
256
270
 * Initialize GPGME.
257
271
 */
258
 
static bool init_gpgme(const char *seckey, const char *pubkey,
259
 
                       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){
260
277
  gpgme_error_t rc;
261
278
  gpgme_engine_info_t engine_info;
262
279
  
263
280
  /*
264
281
   * Helper function to insert pub and seckey to the engine keyring.
265
282
   */
266
 
  bool import_key(const char *filename){
 
283
  bool import_key(const char * const filename){
267
284
    int ret;
268
285
    int fd;
269
286
    gpgme_data_t pgp_data;
350
367
 * Decrypt OpenPGP data.
351
368
 * Returns -1 on error
352
369
 */
 
370
__attribute__((nonnull, warn_unused_result))
353
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
354
372
                                  size_t crypto_size,
355
373
                                  char **plaintext,
475
493
  return plaintext_length;
476
494
}
477
495
 
478
 
static const char * safer_gnutls_strerror(int value){
 
496
__attribute__((warn_unused_result))
 
497
static const char *safer_gnutls_strerror(int value){
479
498
  const char *ret = gnutls_strerror(value);
480
499
  if(ret == NULL)
481
500
    ret = "(unknown)";
483
502
}
484
503
 
485
504
/* GnuTLS log function callback */
 
505
__attribute__((nonnull))
486
506
static void debuggnutls(__attribute__((unused)) int level,
487
507
                        const char* string){
488
508
  fprintf_plus(stderr, "GnuTLS: %s", string);
489
509
}
490
510
 
 
511
__attribute__((nonnull, warn_unused_result))
491
512
static int init_gnutls_global(const char *pubkeyfilename,
492
513
                              const char *seckeyfilename,
493
514
                              mandos_context *mc){
567
588
  return -1;
568
589
}
569
590
 
 
591
__attribute__((nonnull, warn_unused_result))
570
592
static int init_gnutls_session(gnutls_session_t *session,
571
593
                               mandos_context *mc){
572
594
  int ret;
629
651
                      __attribute__((unused)) const char *txt){}
630
652
 
631
653
/* Called when a Mandos server is found */
 
654
__attribute__((nonnull, warn_unused_result))
632
655
static int start_mandos_communication(const char *ip, in_port_t port,
633
656
                                      AvahiIfIndex if_index,
634
657
                                      int af, mandos_context *mc){
740
763
    goto mandos_end;
741
764
  }
742
765
  if(af == AF_INET6){
743
 
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);    
 
766
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
744
767
    if(IN6_IS_ADDR_LINKLOCAL
745
768
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
746
769
      if(if_index == AVAHI_IF_UNSPEC){
809
832
                  sizeof(struct sockaddr_in));
810
833
  }
811
834
  if(ret < 0){
812
 
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
835
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
813
836
      int e = errno;
814
837
      perror_plus("connect");
815
838
      errno = e;
1030
1053
  return retval;
1031
1054
}
1032
1055
 
 
1056
__attribute__((nonnull))
1033
1057
static void resolve_callback(AvahiSServiceResolver *r,
1034
1058
                             AvahiIfIndex interface,
1035
1059
                             AvahiProtocol proto,
1043
1067
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
1044
1068
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
1045
1069
                             flags,
1046
 
                             void* mc){
 
1070
                             void *mc){
1047
1071
  if(r == NULL){
1048
1072
    return;
1049
1073
  }
1052
1076
     timed out */
1053
1077
  
1054
1078
  if(quit_now){
 
1079
    avahi_s_service_resolver_free(r);
1055
1080
    return;
1056
1081
  }
1057
1082
  
1102
1127
                            const char *domain,
1103
1128
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
1104
1129
                            flags,
1105
 
                            void* mc){
 
1130
                            void *mc){
1106
1131
  if(b == NULL){
1107
1132
    return;
1108
1133
  }
1168
1193
  errno = old_errno;
1169
1194
}
1170
1195
 
 
1196
__attribute__((nonnull, warn_unused_result))
1171
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1172
1198
  int ret;
1173
1199
  error_t ret_errno;
1192
1218
  return true;
1193
1219
}
1194
1220
 
 
1221
__attribute__((nonnull, warn_unused_result))
1195
1222
bool good_flags(const char *ifname, const struct ifreq *ifr){
1196
1223
  
1197
1224
  /* Reject the loopback device */
1239
1266
 * corresponds to an acceptable network device.
1240
1267
 * (This function is passed to scandir(3) as a filter function.)
1241
1268
 */
 
1269
__attribute__((nonnull, warn_unused_result))
1242
1270
int good_interface(const struct dirent *if_entry){
1243
1271
  if(if_entry->d_name[0] == '.'){
1244
1272
    return 0;
1262
1290
/* 
1263
1291
 * This function determines if a network interface is up.
1264
1292
 */
 
1293
__attribute__((nonnull, warn_unused_result))
1265
1294
bool interface_is_up(const char *interface){
1266
1295
  struct ifreq ifr;
1267
1296
  if(not get_flags(interface, &ifr)){
1278
1307
/* 
1279
1308
 * This function determines if a network interface is running
1280
1309
 */
 
1310
__attribute__((nonnull, warn_unused_result))
1281
1311
bool interface_is_running(const char *interface){
1282
1312
  struct ifreq ifr;
1283
1313
  if(not get_flags(interface, &ifr)){
1291
1321
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
1292
1322
}
1293
1323
 
 
1324
__attribute__((nonnull, pure, warn_unused_result))
1294
1325
int notdotentries(const struct dirent *direntry){
1295
1326
  /* Skip "." and ".." */
1296
1327
  if(direntry->d_name[0] == '.'
1303
1334
}
1304
1335
 
1305
1336
/* Is this directory entry a runnable program? */
 
1337
__attribute__((nonnull, warn_unused_result))
1306
1338
int runnable_hook(const struct dirent *direntry){
1307
1339
  int ret;
1308
1340
  size_t sret;
1316
1348
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1317
1349
                "abcdefghijklmnopqrstuvwxyz"
1318
1350
                "0123456789"
1319
 
                "_-");
 
1351
                "_.-");
1320
1352
  if((direntry->d_name)[sret] != '\0'){
1321
1353
    /* Contains non-allowed characters */
1322
1354
    if(debug){
1326
1358
    return 0;
1327
1359
  }
1328
1360
  
1329
 
  char *fullname = NULL;
1330
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1331
 
  if(ret < 0){
1332
 
    perror_plus("asprintf");
1333
 
    return 0;
1334
 
  }
1335
 
  
1336
 
  ret = stat(fullname, &st);
 
1361
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1337
1362
  if(ret == -1){
1338
1363
    if(debug){
1339
1364
      perror_plus("Could not stat hook");
1363
1388
  return 1;
1364
1389
}
1365
1390
 
 
1391
__attribute__((nonnull, warn_unused_result))
1366
1392
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1367
1393
                            mandos_context *mc){
1368
1394
  int ret;
1372
1398
  
1373
1399
  while(true){
1374
1400
    if(mc->current_server == NULL){
1375
 
      if (debug){
 
1401
      if(debug){
1376
1402
        fprintf_plus(stderr, "Wait until first server is found."
1377
1403
                     " No timeout!\n");
1378
1404
      }
1379
1405
      ret = avahi_simple_poll_iterate(s, -1);
1380
1406
    } else {
1381
 
      if (debug){
 
1407
      if(debug){
1382
1408
        fprintf_plus(stderr, "Check current_server if we should run"
1383
1409
                     " it, or wait\n");
1384
1410
      }
1401
1427
                     - ((intmax_t)waited_time.tv_sec * 1000))
1402
1428
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1403
1429
      
1404
 
      if (debug){
 
1430
      if(debug){
1405
1431
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1406
1432
                     block_time);
1407
1433
      }
1429
1455
      ret = avahi_simple_poll_iterate(s, (int)block_time);
1430
1456
    }
1431
1457
    if(ret != 0){
1432
 
      if (ret > 0 or errno != EINTR){
 
1458
      if(ret > 0 or errno != EINTR){
1433
1459
        return (ret != 1) ? ret : 0;
1434
1460
      }
1435
1461
    }
1437
1463
}
1438
1464
 
1439
1465
/* Set effective uid to 0, return errno */
 
1466
__attribute__((warn_unused_result))
1440
1467
error_t raise_privileges(void){
1441
1468
  error_t old_errno = errno;
1442
1469
  error_t ret_errno = 0;
1443
1470
  if(seteuid(0) == -1){
1444
1471
    ret_errno = errno;
1445
 
    perror_plus("seteuid");
1446
1472
  }
1447
1473
  errno = old_errno;
1448
1474
  return ret_errno;
1449
1475
}
1450
1476
 
1451
1477
/* Set effective and real user ID to 0.  Return errno. */
 
1478
__attribute__((warn_unused_result))
1452
1479
error_t raise_privileges_permanently(void){
1453
1480
  error_t old_errno = errno;
1454
1481
  error_t ret_errno = raise_privileges();
1458
1485
  }
1459
1486
  if(setuid(0) == -1){
1460
1487
    ret_errno = errno;
1461
 
    perror_plus("seteuid");
1462
1488
  }
1463
1489
  errno = old_errno;
1464
1490
  return ret_errno;
1465
1491
}
1466
1492
 
1467
1493
/* Set effective user ID to unprivileged saved user ID */
 
1494
__attribute__((warn_unused_result))
1468
1495
error_t lower_privileges(void){
1469
1496
  error_t old_errno = errno;
1470
1497
  error_t ret_errno = 0;
1471
1498
  if(seteuid(uid) == -1){
1472
1499
    ret_errno = errno;
1473
 
    perror_plus("seteuid");
1474
1500
  }
1475
1501
  errno = old_errno;
1476
1502
  return ret_errno;
1477
1503
}
1478
1504
 
1479
1505
/* Lower privileges permanently */
 
1506
__attribute__((warn_unused_result))
1480
1507
error_t lower_privileges_permanently(void){
1481
1508
  error_t old_errno = errno;
1482
1509
  error_t ret_errno = 0;
1483
1510
  if(setuid(uid) == -1){
1484
1511
    ret_errno = errno;
1485
 
    perror_plus("setuid");
1486
1512
  }
1487
1513
  errno = old_errno;
1488
1514
  return ret_errno;
1489
1515
}
1490
1516
 
1491
 
bool run_network_hooks(const char *mode, const char *interface,
 
1517
__attribute__((nonnull))
 
1518
void run_network_hooks(const char *mode, const char *interface,
1492
1519
                       const float delay){
1493
 
  struct dirent **direntries;
1494
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1495
 
                         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__ */
1496
1547
  if(numhooks == -1){
1497
 
    if(errno == ENOENT){
1498
 
      if(debug){
1499
 
        fprintf_plus(stderr, "Network hook directory \"%s\" not"
1500
 
                     " found\n", hookdir);
1501
 
      }
1502
 
    } else {
1503
 
      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);
1504
1559
    }
1505
 
  } else {
1506
 
    struct dirent *direntry;
1507
 
    int ret;
1508
 
    int devnull = open("/dev/null", O_RDONLY);
1509
 
    for(int i = 0; i < numhooks; i++){
1510
 
      direntry = direntries[i];
1511
 
      char *fullname = NULL;
1512
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1513
 
      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){
1514
1621
        perror_plus("asprintf");
1515
 
        continue;
1516
 
      }
1517
 
      if(debug){
1518
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1519
 
                     direntry->d_name);
1520
 
      }
1521
 
      pid_t hook_pid = fork();
1522
 
      if(hook_pid == 0){
1523
 
        /* Child */
1524
 
        /* Raise privileges */
1525
 
        raise_privileges_permanently();
1526
 
        /* Set group */
1527
 
        errno = 0;
1528
 
        ret = setgid(0);
1529
 
        if(ret == -1){
1530
 
          perror_plus("setgid");
1531
 
        }
1532
 
        /* Reset supplementary groups */
1533
 
        errno = 0;
1534
 
        ret = setgroups(0, NULL);
1535
 
        if(ret == -1){
1536
 
          perror_plus("setgroups");
1537
 
        }
1538
 
        dup2(devnull, STDIN_FILENO);
1539
 
        close(devnull);
1540
 
        dup2(STDERR_FILENO, STDOUT_FILENO);
1541
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1542
 
        if(ret == -1){
1543
 
          perror_plus("setenv");
1544
 
          _exit(EX_OSERR);
1545
 
        }
1546
 
        ret = setenv("DEVICE", interface, 1);
1547
 
        if(ret == -1){
1548
 
          perror_plus("setenv");
1549
 
          _exit(EX_OSERR);
1550
 
        }
1551
 
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1552
 
        if(ret == -1){
1553
 
          perror_plus("setenv");
1554
 
          _exit(EX_OSERR);
1555
 
        }
1556
 
        ret = setenv("MODE", mode, 1);
1557
 
        if(ret == -1){
1558
 
          perror_plus("setenv");
1559
 
          _exit(EX_OSERR);
1560
 
        }
1561
 
        char *delaystring;
1562
 
        ret = asprintf(&delaystring, "%f", delay);
1563
 
        if(ret == -1){
1564
 
          perror_plus("asprintf");
1565
 
          _exit(EX_OSERR);
1566
 
        }
1567
 
        ret = setenv("DELAY", delaystring, 1);
1568
 
        if(ret == -1){
1569
 
          free(delaystring);
1570
 
          perror_plus("setenv");
1571
 
          _exit(EX_OSERR);
1572
 
        }
 
1622
        _exit(EX_OSERR);
 
1623
      }
 
1624
      ret = setenv("DELAY", delaystring, 1);
 
1625
      if(ret == -1){
1573
1626
        free(delaystring);
1574
 
        if(connect_to != NULL){
1575
 
          ret = setenv("CONNECT", connect_to, 1);
1576
 
          if(ret == -1){
1577
 
            perror_plus("setenv");
1578
 
            _exit(EX_OSERR);
1579
 
          }
1580
 
        }
1581
 
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1582
 
          perror_plus("execl");
1583
 
          _exit(EXIT_FAILURE);
1584
 
        }
 
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;
1585
1678
      } else {
1586
 
        int status;
1587
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1588
 
          perror_plus("waitpid");
1589
 
          free(fullname);
1590
 
          continue;
1591
 
        }
1592
 
        if(WIFEXITED(status)){
1593
 
          if(WEXITSTATUS(status) != 0){
1594
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1595
 
                         " with status %d\n", direntry->d_name,
1596
 
                         WEXITSTATUS(status));
1597
 
            free(fullname);
1598
 
            continue;
1599
 
          }
1600
 
        } else if(WIFSIGNALED(status)){
1601
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1602
 
                       " signal %d\n", direntry->d_name,
1603
 
                       WTERMSIG(status));
1604
 
          free(fullname);
1605
 
          continue;
1606
 
        } else {
1607
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1608
 
                       " crashed\n", direntry->d_name);
1609
 
          free(fullname);
1610
 
          continue;
1611
 
        }
1612
 
      }
1613
 
      free(fullname);
1614
 
      if(debug){
1615
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1616
 
                     direntry->d_name);
1617
 
      }
1618
 
    }
1619
 
    close(devnull);
1620
 
  }
1621
 
  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);
1622
1698
}
1623
1699
 
 
1700
__attribute__((nonnull, warn_unused_result))
1624
1701
error_t bring_up_interface(const char *const interface,
1625
1702
                           const float delay){
1626
 
  int sd = -1;
1627
1703
  error_t old_errno = errno;
1628
 
  error_t ret_errno = 0;
1629
 
  int ret, ret_setflags;
 
1704
  int ret;
1630
1705
  struct ifreq network;
1631
1706
  unsigned int if_index = if_nametoindex(interface);
1632
1707
  if(if_index == 0){
1641
1716
  }
1642
1717
  
1643
1718
  if(not interface_is_up(interface)){
1644
 
    if(not get_flags(interface, &network) and debug){
 
1719
    error_t ret_errno = 0, ioctl_errno = 0;
 
1720
    if(not get_flags(interface, &network)){
1645
1721
      ret_errno = errno;
1646
1722
      fprintf_plus(stderr, "Failed to get flags for interface "
1647
1723
                   "\"%s\"\n", interface);
 
1724
      errno = old_errno;
1648
1725
      return ret_errno;
1649
1726
    }
1650
 
    network.ifr_flags |= IFF_UP;
 
1727
    network.ifr_flags |= IFF_UP; /* set flag */
1651
1728
    
1652
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1653
 
    if(sd < 0){
 
1729
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1730
    if(sd == -1){
1654
1731
      ret_errno = errno;
1655
1732
      perror_plus("socket");
1656
1733
      errno = old_errno;
1657
1734
      return ret_errno;
1658
1735
    }
1659
 
  
 
1736
    
1660
1737
    if(quit_now){
1661
 
      close(sd);
 
1738
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1739
      if(ret == -1){
 
1740
        perror_plus("close");
 
1741
      }
1662
1742
      errno = old_errno;
1663
1743
      return EINTR;
1664
1744
    }
1668
1748
                   interface);
1669
1749
    }
1670
1750
    
1671
 
    /* Raise priviliges */
1672
 
    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
    }
1673
1757
    
1674
1758
#ifdef __linux__
1675
 
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1676
 
       messages about the network interface to mess up the prompt */
1677
 
    int ret_linux = klogctl(8, NULL, 5);
1678
 
    bool restore_loglevel = true;
1679
 
    if(ret_linux == -1){
1680
 
      restore_loglevel = false;
1681
 
      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
      }
1682
1770
    }
1683
1771
#endif  /* __linux__ */
1684
 
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1685
 
    ret_errno = errno;
 
1772
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1773
    ioctl_errno = errno;
1686
1774
#ifdef __linux__
1687
1775
    if(restore_loglevel){
1688
1776
      ret_linux = klogctl(7, NULL, 0);
1692
1780
    }
1693
1781
#endif  /* __linux__ */
1694
1782
    
1695
 
    /* Lower privileges */
1696
 
    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
    }
1697
1792
    
1698
1793
    /* Close the socket */
1699
1794
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
1702
1797
    }
1703
1798
    
1704
1799
    if(ret_setflags == -1){
1705
 
      errno = ret_errno;
 
1800
      errno = ioctl_errno;
1706
1801
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1707
1802
      errno = old_errno;
1708
 
      return ret_errno;
 
1803
      return ioctl_errno;
1709
1804
    }
1710
1805
  } else if(debug){
1711
1806
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1729
1824
  return 0;
1730
1825
}
1731
1826
 
 
1827
__attribute__((nonnull, warn_unused_result))
1732
1828
error_t take_down_interface(const char *const interface){
1733
1829
  error_t old_errno = errno;
1734
1830
  struct ifreq network;
1739
1835
    return ENXIO;
1740
1836
  }
1741
1837
  if(interface_is_up(interface)){
1742
 
    error_t ret_errno = 0;
 
1838
    error_t ret_errno = 0, ioctl_errno = 0;
1743
1839
    if(not get_flags(interface, &network) and debug){
1744
1840
      ret_errno = errno;
1745
1841
      fprintf_plus(stderr, "Failed to get flags for interface "
1746
1842
                   "\"%s\"\n", interface);
 
1843
      errno = old_errno;
1747
1844
      return ret_errno;
1748
1845
    }
1749
1846
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1750
1847
    
1751
1848
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1752
 
    if(sd < 0){
 
1849
    if(sd == -1){
1753
1850
      ret_errno = errno;
1754
1851
      perror_plus("socket");
1755
1852
      errno = old_errno;
1761
1858
                   interface);
1762
1859
    }
1763
1860
    
1764
 
    /* Raise priviliges */
1765
 
    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
    }
1766
1867
    
1767
1868
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1768
 
    ret_errno = errno;
 
1869
    ioctl_errno = errno;
1769
1870
    
1770
 
    /* Lower privileges */
1771
 
    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
    }
1772
1880
    
1773
1881
    /* Close the socket */
1774
1882
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1777
1885
    }
1778
1886
    
1779
1887
    if(ret_setflags == -1){
1780
 
      errno = ret_errno;
 
1888
      errno = ioctl_errno;
1781
1889
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1782
1890
      errno = old_errno;
1783
 
      return ret_errno;
 
1891
      return ioctl_errno;
1784
1892
    }
1785
1893
  } else if(debug){
1786
1894
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1794
1902
int main(int argc, char *argv[]){
1795
1903
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
1796
1904
                        .priority = "SECURE256:!CTYPE-X.509:"
1797
 
                        "+CTYPE-OPENPGP", .current_server = NULL, 
 
1905
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
1798
1906
                        .interfaces = NULL, .interfaces_size = 0 };
1799
1907
  AvahiSServiceBrowser *sb = NULL;
1800
1908
  error_t ret_errno;
1804
1912
  int exitcode = EXIT_SUCCESS;
1805
1913
  char *interfaces_to_take_down = NULL;
1806
1914
  size_t interfaces_to_take_down_size = 0;
1807
 
  char tempdir[] = "/tmp/mandosXXXXXX";
1808
 
  bool tempdir_created = false;
 
1915
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
1916
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
1917
  char *tempdir = NULL;
1809
1918
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1810
1919
  const char *seckey = PATHDIR "/" SECKEY;
1811
1920
  const char *pubkey = PATHDIR "/" PUBKEY;
1993
2102
    /* Work around Debian bug #633582:
1994
2103
       <http://bugs.debian.org/633582> */
1995
2104
    
1996
 
    /* Re-raise priviliges */
1997
 
    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 {
1998
2111
      struct stat st;
1999
2112
      
2000
2113
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2040
2153
      }
2041
2154
    
2042
2155
      /* Lower privileges */
2043
 
      lower_privileges();
 
2156
      ret_errno = lower_privileges();
 
2157
      if(ret_errno != 0){
 
2158
        errno = ret_errno;
 
2159
        perror_plus("Failed to lower privileges");
 
2160
      }
2044
2161
    }
2045
2162
  }
2046
2163
  
2072
2189
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2073
2190
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2074
2191
    }
2075
 
    if(not run_network_hooks("start", interfaces_hooks != NULL ?
2076
 
                             interfaces_hooks : "", delay)){
2077
 
      goto end;
2078
 
    }
 
2192
    run_network_hooks("start", interfaces_hooks != NULL ?
 
2193
                      interfaces_hooks : "", delay);
2079
2194
  }
2080
2195
  
2081
2196
  if(not debug){
2159
2274
  
2160
2275
  /* If no interfaces were specified, make a list */
2161
2276
  if(mc.interfaces == NULL){
2162
 
    struct dirent **direntries;
 
2277
    struct dirent **direntries = NULL;
2163
2278
    /* Look for any good interfaces */
2164
2279
    ret = scandir(sys_class_net, &direntries, good_interface,
2165
2280
                  alphasort);
2171
2286
        if(ret_errno != 0){
2172
2287
          errno = ret_errno;
2173
2288
          perror_plus("argz_add");
 
2289
          free(direntries[i]);
2174
2290
          continue;
2175
2291
        }
2176
2292
        if(debug){
2177
2293
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2178
2294
                       direntries[i]->d_name);
2179
2295
        }
 
2296
        free(direntries[i]);
2180
2297
      }
2181
2298
      free(direntries);
2182
2299
    } else {
2183
 
      free(direntries);
 
2300
      if(ret == 0){
 
2301
        free(direntries);
 
2302
      }
2184
2303
      fprintf_plus(stderr, "Could not find a network interface\n");
2185
2304
      exitcode = EXIT_FAILURE;
2186
2305
      goto end;
2209
2328
        break;
2210
2329
      }
2211
2330
      bool interface_was_up = interface_is_up(interface);
2212
 
      ret = bring_up_interface(interface, delay);
 
2331
      errno = bring_up_interface(interface, delay);
2213
2332
      if(not interface_was_up){
2214
 
        if(ret != 0){
2215
 
          errno = ret;
 
2333
        if(errno != 0){
2216
2334
          perror_plus("Failed to bring up interface");
2217
2335
        } else {
2218
 
          ret_errno = argz_add(&interfaces_to_take_down,
2219
 
                               &interfaces_to_take_down_size,
2220
 
                               interface);
2221
 
          if(ret_errno != 0){
2222
 
            errno = ret_errno;
 
2336
          errno = argz_add(&interfaces_to_take_down,
 
2337
                           &interfaces_to_take_down_size,
 
2338
                           interface);
 
2339
          if(errno != 0){
2223
2340
            perror_plus("argz_add");
2224
2341
          }
2225
2342
        }
2256
2373
    goto end;
2257
2374
  }
2258
2375
  
2259
 
  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){
2260
2386
    perror_plus("mkdtemp");
2261
2387
    goto end;
2262
2388
  }
2263
 
  tempdir_created = true;
2264
2389
  
2265
2390
  if(quit_now){
2266
2391
    goto end;
2341
2466
      sleep((unsigned int)retry_interval);
2342
2467
    }
2343
2468
    
2344
 
    if (not quit_now){
 
2469
    if(not quit_now){
2345
2470
      exitcode = EXIT_SUCCESS;
2346
2471
    }
2347
2472
    
2402
2527
  if(debug){
2403
2528
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2404
2529
  }
2405
 
 
 
2530
  
2406
2531
  ret = avahi_loop_with_timeout(simple_poll,
2407
2532
                                (int)(retry_interval * 1000), &mc);
2408
2533
  if(debug){
2444
2569
    mc.current_server->prev->next = NULL;
2445
2570
    while(mc.current_server != NULL){
2446
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
2447
2580
      free(mc.current_server);
2448
2581
      mc.current_server = next;
2449
2582
    }
2450
2583
  }
2451
2584
  
2452
 
  /* Re-raise priviliges */
 
2585
  /* Re-raise privileges */
2453
2586
  {
2454
 
    raise_privileges();
2455
 
    
2456
 
    /* Run network hooks */
2457
 
    run_network_hooks("stop", interfaces_hooks != NULL ?
2458
 
                      interfaces_hooks : "", delay);
2459
 
    
2460
 
    /* Take down the network interfaces which were brought up */
2461
 
    {
2462
 
      char *interface = NULL;
2463
 
      while((interface=argz_next(interfaces_to_take_down,
2464
 
                                 interfaces_to_take_down_size,
2465
 
                                 interface))){
2466
 
        ret_errno = take_down_interface(interface);
2467
 
        if(ret_errno != 0){
2468
 
          errno = ret_errno;
2469
 
          perror_plus("Failed to take down interface");
2470
 
        }
2471
 
      }
2472
 
      if(debug and (interfaces_to_take_down == NULL)){
2473
 
        fprintf_plus(stderr, "No interfaces needed to be taken"
2474
 
                     " down\n");
2475
 
      }
2476
 
    }
2477
 
    
2478
 
    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
    }
2479
2621
  }
2480
2622
  
2481
2623
  free(interfaces_to_take_down);
2482
2624
  free(interfaces_hooks);
2483
2625
  
2484
2626
  /* Removes the GPGME temp directory and all files inside */
2485
 
  if(tempdir_created){
 
2627
  if(tempdir != NULL){
2486
2628
    struct dirent **direntries = NULL;
2487
 
    struct dirent *direntry = NULL;
2488
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2489
 
                             alphasort);
2490
 
    if (numentries > 0){
2491
 
      for(int i = 0; i < numentries; i++){
2492
 
        direntry = direntries[i];
2493
 
        char *fullname = NULL;
2494
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2495
 
                       direntry->d_name);
2496
 
        if(ret < 0){
2497
 
          perror_plus("asprintf");
2498
 
          continue;
2499
 
        }
2500
 
        ret = remove(fullname);
2501
 
        if(ret == -1){
2502
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2503
 
                       strerror(errno));
2504
 
        }
2505
 
        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
        }
2506
2666
      }
2507
 
    }
2508
 
 
2509
 
    /* need to clean even if 0 because man page doesn't specify */
2510
 
    free(direntries);
2511
 
    if (numentries == -1){
2512
 
      perror_plus("scandir");
2513
 
    }
2514
 
    ret = rmdir(tempdir);
2515
 
    if(ret == -1 and errno != ENOENT){
2516
 
      perror_plus("rmdir");
 
2667
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2517
2668
    }
2518
2669
  }
2519
2670