/mandos/trunk

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

« back to all changes in this revision

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

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

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

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008-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
      int status;
 
1654
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1655
        perror_plus("waitpid");
 
1656
        free(direntry);
 
1657
        continue;
 
1658
      }
 
1659
      if(WIFEXITED(status)){
 
1660
        if(WEXITSTATUS(status) != 0){
 
1661
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1662
                       " with status %d\n", direntry->d_name,
 
1663
                       WEXITSTATUS(status));
 
1664
          free(direntry);
 
1665
          continue;
 
1666
        }
 
1667
      } else if(WIFSIGNALED(status)){
 
1668
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1669
                     " signal %d\n", direntry->d_name,
 
1670
                     WTERMSIG(status));
 
1671
        free(direntry);
 
1672
        continue;
1585
1673
      } 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;
 
1674
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1675
                     " crashed\n", direntry->d_name);
 
1676
        free(direntry);
 
1677
        continue;
 
1678
      }
 
1679
    }
 
1680
    if(debug){
 
1681
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1682
                   direntry->d_name);
 
1683
    }
 
1684
    free(direntry);
 
1685
  }
 
1686
  free(direntries);
 
1687
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1688
    perror_plus("close");
 
1689
  } else {
 
1690
    hookdir_fd = -1;
 
1691
  }
 
1692
  close(devnull);
1622
1693
}
1623
1694
 
 
1695
__attribute__((nonnull, warn_unused_result))
1624
1696
error_t bring_up_interface(const char *const interface,
1625
1697
                           const float delay){
1626
 
  int sd = -1;
1627
1698
  error_t old_errno = errno;
1628
 
  error_t ret_errno = 0;
1629
 
  int ret, ret_setflags;
 
1699
  int ret;
1630
1700
  struct ifreq network;
1631
1701
  unsigned int if_index = if_nametoindex(interface);
1632
1702
  if(if_index == 0){
1641
1711
  }
1642
1712
  
1643
1713
  if(not interface_is_up(interface)){
1644
 
    if(not get_flags(interface, &network) and debug){
 
1714
    error_t ret_errno = 0, ioctl_errno = 0;
 
1715
    if(not get_flags(interface, &network)){
1645
1716
      ret_errno = errno;
1646
1717
      fprintf_plus(stderr, "Failed to get flags for interface "
1647
1718
                   "\"%s\"\n", interface);
 
1719
      errno = old_errno;
1648
1720
      return ret_errno;
1649
1721
    }
1650
 
    network.ifr_flags |= IFF_UP;
 
1722
    network.ifr_flags |= IFF_UP; /* set flag */
1651
1723
    
1652
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1653
 
    if(sd < 0){
 
1724
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1725
    if(sd == -1){
1654
1726
      ret_errno = errno;
1655
1727
      perror_plus("socket");
1656
1728
      errno = old_errno;
1657
1729
      return ret_errno;
1658
1730
    }
1659
 
  
 
1731
    
1660
1732
    if(quit_now){
1661
 
      close(sd);
 
1733
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1734
      if(ret == -1){
 
1735
        perror_plus("close");
 
1736
      }
1662
1737
      errno = old_errno;
1663
1738
      return EINTR;
1664
1739
    }
1668
1743
                   interface);
1669
1744
    }
1670
1745
    
1671
 
    /* Raise priviliges */
1672
 
    raise_privileges();
 
1746
    /* Raise privileges */
 
1747
    ret_errno = raise_privileges();
 
1748
    if(ret_errno != 0){
 
1749
      errno = ret_errno;
 
1750
      perror_plus("Failed to raise privileges");
 
1751
    }
1673
1752
    
1674
1753
#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");
 
1754
    int ret_linux;
 
1755
    bool restore_loglevel = false;
 
1756
    if(ret_errno == 0){
 
1757
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1758
         messages about the network interface to mess up the prompt */
 
1759
      ret_linux = klogctl(8, NULL, 5);
 
1760
      if(ret_linux == -1){
 
1761
        perror_plus("klogctl");
 
1762
      } else {
 
1763
        restore_loglevel = true;
 
1764
      }
1682
1765
    }
1683
1766
#endif  /* __linux__ */
1684
 
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1685
 
    ret_errno = errno;
 
1767
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1768
    ioctl_errno = errno;
1686
1769
#ifdef __linux__
1687
1770
    if(restore_loglevel){
1688
1771
      ret_linux = klogctl(7, NULL, 0);
1692
1775
    }
1693
1776
#endif  /* __linux__ */
1694
1777
    
1695
 
    /* Lower privileges */
1696
 
    lower_privileges();
 
1778
    /* If raise_privileges() succeeded above */
 
1779
    if(ret_errno == 0){
 
1780
      /* Lower privileges */
 
1781
      ret_errno = lower_privileges();
 
1782
      if(ret_errno != 0){
 
1783
        errno = ret_errno;
 
1784
        perror_plus("Failed to lower privileges");
 
1785
      }
 
1786
    }
1697
1787
    
1698
1788
    /* Close the socket */
1699
1789
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
1702
1792
    }
1703
1793
    
1704
1794
    if(ret_setflags == -1){
1705
 
      errno = ret_errno;
 
1795
      errno = ioctl_errno;
1706
1796
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1707
1797
      errno = old_errno;
1708
 
      return ret_errno;
 
1798
      return ioctl_errno;
1709
1799
    }
1710
1800
  } else if(debug){
1711
1801
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1729
1819
  return 0;
1730
1820
}
1731
1821
 
 
1822
__attribute__((nonnull, warn_unused_result))
1732
1823
error_t take_down_interface(const char *const interface){
1733
1824
  error_t old_errno = errno;
1734
1825
  struct ifreq network;
1739
1830
    return ENXIO;
1740
1831
  }
1741
1832
  if(interface_is_up(interface)){
1742
 
    error_t ret_errno = 0;
 
1833
    error_t ret_errno = 0, ioctl_errno = 0;
1743
1834
    if(not get_flags(interface, &network) and debug){
1744
1835
      ret_errno = errno;
1745
1836
      fprintf_plus(stderr, "Failed to get flags for interface "
1746
1837
                   "\"%s\"\n", interface);
 
1838
      errno = old_errno;
1747
1839
      return ret_errno;
1748
1840
    }
1749
1841
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1750
1842
    
1751
1843
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1752
 
    if(sd < 0){
 
1844
    if(sd == -1){
1753
1845
      ret_errno = errno;
1754
1846
      perror_plus("socket");
1755
1847
      errno = old_errno;
1761
1853
                   interface);
1762
1854
    }
1763
1855
    
1764
 
    /* Raise priviliges */
1765
 
    raise_privileges();
 
1856
    /* Raise privileges */
 
1857
    ret_errno = raise_privileges();
 
1858
    if(ret_errno != 0){
 
1859
      errno = ret_errno;
 
1860
      perror_plus("Failed to raise privileges");
 
1861
    }
1766
1862
    
1767
1863
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1768
 
    ret_errno = errno;
 
1864
    ioctl_errno = errno;
1769
1865
    
1770
 
    /* Lower privileges */
1771
 
    lower_privileges();
 
1866
    /* If raise_privileges() succeeded above */
 
1867
    if(ret_errno == 0){
 
1868
      /* Lower privileges */
 
1869
      ret_errno = lower_privileges();
 
1870
      if(ret_errno != 0){
 
1871
        errno = ret_errno;
 
1872
        perror_plus("Failed to lower privileges");
 
1873
      }
 
1874
    }
1772
1875
    
1773
1876
    /* Close the socket */
1774
1877
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1777
1880
    }
1778
1881
    
1779
1882
    if(ret_setflags == -1){
1780
 
      errno = ret_errno;
 
1883
      errno = ioctl_errno;
1781
1884
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1782
1885
      errno = old_errno;
1783
 
      return ret_errno;
 
1886
      return ioctl_errno;
1784
1887
    }
1785
1888
  } else if(debug){
1786
1889
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1794
1897
int main(int argc, char *argv[]){
1795
1898
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
1796
1899
                        .priority = "SECURE256:!CTYPE-X.509:"
1797
 
                        "+CTYPE-OPENPGP", .current_server = NULL, 
 
1900
                        "+CTYPE-OPENPGP", .current_server = NULL,
1798
1901
                        .interfaces = NULL, .interfaces_size = 0 };
1799
1902
  AvahiSServiceBrowser *sb = NULL;
1800
1903
  error_t ret_errno;
1804
1907
  int exitcode = EXIT_SUCCESS;
1805
1908
  char *interfaces_to_take_down = NULL;
1806
1909
  size_t interfaces_to_take_down_size = 0;
1807
 
  char tempdir[] = "/tmp/mandosXXXXXX";
1808
 
  bool tempdir_created = false;
 
1910
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
1911
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
1912
  char *tempdir = NULL;
1809
1913
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1810
1914
  const char *seckey = PATHDIR "/" SECKEY;
1811
1915
  const char *pubkey = PATHDIR "/" PUBKEY;
1993
2097
    /* Work around Debian bug #633582:
1994
2098
       <http://bugs.debian.org/633582> */
1995
2099
    
1996
 
    /* Re-raise priviliges */
1997
 
    if(raise_privileges() == 0){
 
2100
    /* Re-raise privileges */
 
2101
    ret_errno = raise_privileges();
 
2102
    if(ret_errno != 0){
 
2103
      errno = ret_errno;
 
2104
      perror_plus("Failed to raise privileges");
 
2105
    } else {
1998
2106
      struct stat st;
1999
2107
      
2000
2108
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2040
2148
      }
2041
2149
    
2042
2150
      /* Lower privileges */
2043
 
      lower_privileges();
 
2151
      ret_errno = lower_privileges();
 
2152
      if(ret_errno != 0){
 
2153
        errno = ret_errno;
 
2154
        perror_plus("Failed to lower privileges");
 
2155
      }
2044
2156
    }
2045
2157
  }
2046
2158
  
2072
2184
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2073
2185
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2074
2186
    }
2075
 
    if(not run_network_hooks("start", interfaces_hooks != NULL ?
2076
 
                             interfaces_hooks : "", delay)){
2077
 
      goto end;
2078
 
    }
 
2187
    run_network_hooks("start", interfaces_hooks != NULL ?
 
2188
                      interfaces_hooks : "", delay);
2079
2189
  }
2080
2190
  
2081
2191
  if(not debug){
2159
2269
  
2160
2270
  /* If no interfaces were specified, make a list */
2161
2271
  if(mc.interfaces == NULL){
2162
 
    struct dirent **direntries;
 
2272
    struct dirent **direntries = NULL;
2163
2273
    /* Look for any good interfaces */
2164
2274
    ret = scandir(sys_class_net, &direntries, good_interface,
2165
2275
                  alphasort);
2171
2281
        if(ret_errno != 0){
2172
2282
          errno = ret_errno;
2173
2283
          perror_plus("argz_add");
 
2284
          free(direntries[i]);
2174
2285
          continue;
2175
2286
        }
2176
2287
        if(debug){
2177
2288
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2178
2289
                       direntries[i]->d_name);
2179
2290
        }
 
2291
        free(direntries[i]);
2180
2292
      }
2181
2293
      free(direntries);
2182
2294
    } else {
2183
 
      free(direntries);
 
2295
      if(ret == 0){
 
2296
        free(direntries);
 
2297
      }
2184
2298
      fprintf_plus(stderr, "Could not find a network interface\n");
2185
2299
      exitcode = EXIT_FAILURE;
2186
2300
      goto end;
2209
2323
        break;
2210
2324
      }
2211
2325
      bool interface_was_up = interface_is_up(interface);
2212
 
      ret = bring_up_interface(interface, delay);
 
2326
      errno = bring_up_interface(interface, delay);
2213
2327
      if(not interface_was_up){
2214
 
        if(ret != 0){
2215
 
          errno = ret;
 
2328
        if(errno != 0){
2216
2329
          perror_plus("Failed to bring up interface");
2217
2330
        } 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;
 
2331
          errno = argz_add(&interfaces_to_take_down,
 
2332
                           &interfaces_to_take_down_size,
 
2333
                           interface);
 
2334
          if(errno != 0){
2223
2335
            perror_plus("argz_add");
2224
2336
          }
2225
2337
        }
2256
2368
    goto end;
2257
2369
  }
2258
2370
  
2259
 
  if(mkdtemp(tempdir) == NULL){
 
2371
  /* Try /run/tmp before /tmp */
 
2372
  tempdir = mkdtemp(run_tempdir);
 
2373
  if(tempdir == NULL and errno == ENOENT){
 
2374
      if(debug){
 
2375
        fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
 
2376
                     run_tempdir, old_tempdir);
 
2377
      }
 
2378
      tempdir = mkdtemp(old_tempdir);
 
2379
  }
 
2380
  if(tempdir == NULL){
2260
2381
    perror_plus("mkdtemp");
2261
2382
    goto end;
2262
2383
  }
2263
 
  tempdir_created = true;
2264
2384
  
2265
2385
  if(quit_now){
2266
2386
    goto end;
2341
2461
      sleep((unsigned int)retry_interval);
2342
2462
    }
2343
2463
    
2344
 
    if (not quit_now){
 
2464
    if(not quit_now){
2345
2465
      exitcode = EXIT_SUCCESS;
2346
2466
    }
2347
2467
    
2402
2522
  if(debug){
2403
2523
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2404
2524
  }
2405
 
 
 
2525
  
2406
2526
  ret = avahi_loop_with_timeout(simple_poll,
2407
2527
                                (int)(retry_interval * 1000), &mc);
2408
2528
  if(debug){
2444
2564
    mc.current_server->prev->next = NULL;
2445
2565
    while(mc.current_server != NULL){
2446
2566
      server *next = mc.current_server->next;
 
2567
#ifdef __GNUC__
 
2568
#pragma GCC diagnostic push
 
2569
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2570
#endif
 
2571
      free((char *)(mc.current_server->ip));
 
2572
#ifdef __GNUC__
 
2573
#pragma GCC diagnostic pop
 
2574
#endif
2447
2575
      free(mc.current_server);
2448
2576
      mc.current_server = next;
2449
2577
    }
2450
2578
  }
2451
2579
  
2452
 
  /* Re-raise priviliges */
 
2580
  /* Re-raise privileges */
2453
2581
  {
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();
 
2582
    ret_errno = raise_privileges();
 
2583
    if(ret_errno != 0){
 
2584
      errno = ret_errno;
 
2585
      perror_plus("Failed to raise privileges");
 
2586
    } else {
 
2587
      
 
2588
      /* Run network hooks */
 
2589
      run_network_hooks("stop", interfaces_hooks != NULL ?
 
2590
                        interfaces_hooks : "", delay);
 
2591
      
 
2592
      /* Take down the network interfaces which were brought up */
 
2593
      {
 
2594
        char *interface = NULL;
 
2595
        while((interface=argz_next(interfaces_to_take_down,
 
2596
                                   interfaces_to_take_down_size,
 
2597
                                   interface))){
 
2598
          ret_errno = take_down_interface(interface);
 
2599
          if(ret_errno != 0){
 
2600
            errno = ret_errno;
 
2601
            perror_plus("Failed to take down interface");
 
2602
          }
 
2603
        }
 
2604
        if(debug and (interfaces_to_take_down == NULL)){
 
2605
          fprintf_plus(stderr, "No interfaces needed to be taken"
 
2606
                       " down\n");
 
2607
        }
 
2608
      }
 
2609
    }
 
2610
    
 
2611
    ret_errno = lower_privileges_permanently();
 
2612
    if(ret_errno != 0){
 
2613
      errno = ret_errno;
 
2614
      perror_plus("Failed to lower privileges permanently");
 
2615
    }
2479
2616
  }
2480
2617
  
2481
2618
  free(interfaces_to_take_down);
2482
2619
  free(interfaces_hooks);
2483
2620
  
2484
2621
  /* Removes the GPGME temp directory and all files inside */
2485
 
  if(tempdir_created){
 
2622
  if(tempdir != NULL){
2486
2623
    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);
 
2624
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2625
                                                  O_NOFOLLOW));
 
2626
    if(tempdir_fd == -1){
 
2627
      perror_plus("open");
 
2628
    } else {
 
2629
#ifdef __GLIBC__
 
2630
#if __GLIBC_PREREQ(2, 15)
 
2631
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2632
                                 notdotentries, alphasort);
 
2633
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2634
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2635
                               alphasort);
 
2636
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2637
#else   /* not __GLIBC__ */
 
2638
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2639
                               alphasort);
 
2640
#endif  /* not __GLIBC__ */
 
2641
      if(numentries >= 0){
 
2642
        for(int i = 0; i < numentries; i++){
 
2643
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2644
          if(ret == -1){
 
2645
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2646
                         " \"%s\", 0): %s\n", tempdir,
 
2647
                         direntries[i]->d_name, strerror(errno));
 
2648
          }
 
2649
          free(direntries[i]);
 
2650
        }
 
2651
        
 
2652
        /* need to clean even if 0 because man page doesn't specify */
 
2653
        free(direntries);
 
2654
        if(numentries == -1){
 
2655
          perror_plus("scandir");
 
2656
        }
 
2657
        ret = rmdir(tempdir);
 
2658
        if(ret == -1 and errno != ENOENT){
 
2659
          perror_plus("rmdir");
 
2660
        }
2506
2661
      }
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");
 
2662
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2517
2663
    }
2518
2664
  }
2519
2665