/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-06-07 21:58:56 UTC
  • Revision ID: teddy@recompile.se-20140607215856-9q31dfsc68uuvzo4
Make mandos-client use scandirat() if it exists.

* plugins.d/mandos-client.d (hookdir_fd): File descriptor for hookdir.
  (set_cloexec_flag): Define this function if O_CLOEXEC is undefined.
  (run_network_hooks): Open hookdir_fd.  Use scandirat() if it exists.

Show diffs side-by-side

added added

removed removed

Lines of Context:
40
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
41
41
 
42
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
43
 
                                   stdout, ferror() */
 
43
                                   stdout, ferror(), remove() */
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(), unlinkat() */
 
60
#include <fcntl.h>              /* open() */
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(),
77
 
                                   unlinkat() */
 
76
                                   setgid(), pause(), _exit() */
78
77
#include <arpa/inet.h>          /* inet_pton(), htons() */
79
78
#include <iso646.h>             /* not, or, and */
80
79
#include <argp.h>               /* struct argp_option, error_t, struct
1347
1346
    return 0;
1348
1347
  }
1349
1348
  
1350
 
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
 
1349
  char *fullname = NULL;
 
1350
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
 
1351
  if(ret < 0){
 
1352
    perror_plus("asprintf");
 
1353
    return 0;
 
1354
  }
 
1355
  
 
1356
  ret = stat(fullname, &st);
1351
1357
  if(ret == -1){
1352
1358
    if(debug){
1353
1359
      perror_plus("Could not stat hook");
1354
1360
    }
1355
1361
    return 0;
1356
1362
  }
 
1363
  free(fullname);
1357
1364
  if(not (S_ISREG(st.st_mode))){
1358
1365
    /* Not a regular file */
1359
1366
    if(debug){
1458
1465
  error_t ret_errno = 0;
1459
1466
  if(seteuid(0) == -1){
1460
1467
    ret_errno = errno;
 
1468
    perror_plus("seteuid");
1461
1469
  }
1462
1470
  errno = old_errno;
1463
1471
  return ret_errno;
1474
1482
  }
1475
1483
  if(setuid(0) == -1){
1476
1484
    ret_errno = errno;
 
1485
    perror_plus("seteuid");
1477
1486
  }
1478
1487
  errno = old_errno;
1479
1488
  return ret_errno;
1486
1495
  error_t ret_errno = 0;
1487
1496
  if(seteuid(uid) == -1){
1488
1497
    ret_errno = errno;
 
1498
    perror_plus("seteuid");
1489
1499
  }
1490
1500
  errno = old_errno;
1491
1501
  return ret_errno;
1498
1508
  error_t ret_errno = 0;
1499
1509
  if(setuid(uid) == -1){
1500
1510
    ret_errno = errno;
 
1511
    perror_plus("setuid");
1501
1512
  }
1502
1513
  errno = old_errno;
1503
1514
  return ret_errno;
1504
1515
}
1505
1516
 
 
1517
#ifndef O_CLOEXEC
 
1518
/*
 
1519
 * Based on the example in the GNU LibC manual chapter 13.13 "File
 
1520
 * Descriptor Flags".
 
1521
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
 
1522
 */
 
1523
__attribute__((warn_unused_result))
 
1524
static int set_cloexec_flag(int fd){
 
1525
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
 
1526
  /* If reading the flags failed, return error indication now. */
 
1527
  if(ret < 0){
 
1528
    return ret;
 
1529
  }
 
1530
  /* Store modified flag word in the descriptor. */
 
1531
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
 
1532
                                       ret | FD_CLOEXEC));
 
1533
}
 
1534
#endif  /* not O_CLOEXEC */
 
1535
 
1506
1536
__attribute__((nonnull))
1507
1537
void run_network_hooks(const char *mode, const char *interface,
1508
1538
                       const float delay){
1509
 
  struct dirent **direntries = NULL;
 
1539
  struct dirent **direntries;
1510
1540
  if(hookdir_fd == -1){
1511
 
    hookdir_fd = open(hookdir, O_RDONLY);
 
1541
    hookdir_fd = open(hookdir, O_RDONLY |
 
1542
#ifdef O_CLOEXEC
 
1543
                      O_CLOEXEC
 
1544
#else  /* not O_CLOEXEC */
 
1545
                      0
 
1546
#endif  /* not O_CLOEXEC */
 
1547
                      );
1512
1548
    if(hookdir_fd == -1){
1513
1549
      if(errno == ENOENT){
1514
1550
        if(debug){
1520
1556
      }
1521
1557
      return;
1522
1558
    }
 
1559
#ifndef O_CLOEXEC
 
1560
    if(set_cloexec_flag(hookdir_fd) < 0){
 
1561
      perror_plus("set_cloexec_flag");
 
1562
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1563
        perror_plus("close");
 
1564
      } else {
 
1565
        hookdir_fd = -1;
 
1566
      }
 
1567
      return;
 
1568
    }
 
1569
#endif  /* not O_CLOEXEC */
1523
1570
  }
1524
1571
#ifdef __GLIBC__
1525
1572
#if __GLIBC_PREREQ(2, 15)
1542
1589
  int devnull = open("/dev/null", O_RDONLY);
1543
1590
  for(int i = 0; i < numhooks; i++){
1544
1591
    direntry = direntries[i];
 
1592
    char *fullname = NULL;
 
1593
    ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
 
1594
    if(ret < 0){
 
1595
      perror_plus("asprintf");
 
1596
      continue;
 
1597
    }
1545
1598
    if(debug){
1546
1599
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
1547
1600
                   direntry->d_name);
1550
1603
    if(hook_pid == 0){
1551
1604
      /* Child */
1552
1605
      /* Raise privileges */
1553
 
      errno = raise_privileges_permanently();
1554
 
      if(errno != 0){
 
1606
      if(raise_privileges_permanently() != 0){
1555
1607
        perror_plus("Failed to raise privileges");
1556
1608
        _exit(EX_NOPERM);
1557
1609
      }
1624
1676
          _exit(EX_OSERR);
1625
1677
        }
1626
1678
      }
1627
 
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
1628
 
      if(hook_fd == -1){
1629
 
        perror_plus("openat");
1630
 
        _exit(EXIT_FAILURE);
1631
 
      }
1632
 
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1633
 
        perror_plus("close");
1634
 
        _exit(EXIT_FAILURE);
1635
 
      }
1636
 
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
1637
 
                 environ) == -1){
1638
 
        perror_plus("fexecve");
 
1679
      if(execl(fullname, direntry->d_name, mode, NULL) == -1){
 
1680
        perror_plus("execl");
1639
1681
        _exit(EXIT_FAILURE);
1640
1682
      }
1641
1683
    } else {
1642
1684
      int status;
1643
1685
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1644
1686
        perror_plus("waitpid");
 
1687
        free(fullname);
1645
1688
        continue;
1646
1689
      }
1647
1690
      if(WIFEXITED(status)){
1649
1692
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1650
1693
                       " with status %d\n", direntry->d_name,
1651
1694
                       WEXITSTATUS(status));
 
1695
          free(fullname);
1652
1696
          continue;
1653
1697
        }
1654
1698
      } else if(WIFSIGNALED(status)){
1655
1699
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1656
1700
                     " signal %d\n", direntry->d_name,
1657
1701
                     WTERMSIG(status));
 
1702
        free(fullname);
1658
1703
        continue;
1659
1704
      } else {
1660
1705
        fprintf_plus(stderr, "Warning: network hook \"%s\""
1661
1706
                     " crashed\n", direntry->d_name);
 
1707
        free(fullname);
1662
1708
        continue;
1663
1709
      }
1664
1710
    }
 
1711
    free(fullname);
1665
1712
    if(debug){
1666
1713
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1667
1714
                   direntry->d_name);
1668
1715
    }
1669
1716
  }
1670
 
  free(direntries);
1671
1717
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1672
1718
    perror_plus("close");
1673
1719
  } else {
1730
1776
    /* Raise privileges */
1731
1777
    ret_errno = raise_privileges();
1732
1778
    if(ret_errno != 0){
1733
 
      errno = ret_errno;
1734
1779
      perror_plus("Failed to raise privileges");
1735
1780
    }
1736
1781
    
1840
1885
    /* Raise privileges */
1841
1886
    ret_errno = raise_privileges();
1842
1887
    if(ret_errno != 0){
1843
 
      errno = ret_errno;
1844
1888
      perror_plus("Failed to raise privileges");
1845
1889
    }
1846
1890
    
2253
2297
  
2254
2298
  /* If no interfaces were specified, make a list */
2255
2299
  if(mc.interfaces == NULL){
2256
 
    struct dirent **direntries = NULL;
 
2300
    struct dirent **direntries;
2257
2301
    /* Look for any good interfaces */
2258
2302
    ret = scandir(sys_class_net, &direntries, good_interface,
2259
2303
                  alphasort);
2274
2318
      }
2275
2319
      free(direntries);
2276
2320
    } else {
2277
 
      if(ret == 0){
2278
 
        free(direntries);
2279
 
      }
 
2321
      free(direntries);
2280
2322
      fprintf_plus(stderr, "Could not find a network interface\n");
2281
2323
      exitcode = EXIT_FAILURE;
2282
2324
      goto end;
2555
2597
  {
2556
2598
    ret_errno = raise_privileges();
2557
2599
    if(ret_errno != 0){
2558
 
      errno = ret_errno;
2559
2600
      perror_plus("Failed to raise privileges");
2560
2601
    } else {
2561
2602
      
2584
2625
    
2585
2626
    ret_errno = lower_privileges_permanently();
2586
2627
    if(ret_errno != 0){
2587
 
      errno = ret_errno;
2588
2628
      perror_plus("Failed to lower privileges permanently");
2589
2629
    }
2590
2630
  }
2595
2635
  /* Removes the GPGME temp directory and all files inside */
2596
2636
  if(tempdir != NULL){
2597
2637
    struct dirent **direntries = NULL;
2598
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
2599
 
                                                  O_NOFOLLOW));
2600
 
    if(tempdir_fd == -1){
2601
 
      perror_plus("open");
2602
 
    } else {
2603
 
#ifdef __GLIBC__
2604
 
#if __GLIBC_PREREQ(2, 15)
2605
 
      int numentries = scandirat(tempdir_fd, ".", &direntries,
2606
 
                                 notdotentries, alphasort);
2607
 
#else  /* not __GLIBC_PREREQ(2, 15) */
2608
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
2609
 
                               alphasort);
2610
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
2611
 
#else   /* not __GLIBC__ */
2612
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
2613
 
                               alphasort);
2614
 
#endif  /* not __GLIBC__ */
2615
 
      if(numentries >= 0){
2616
 
        for(int i = 0; i < numentries; i++){
2617
 
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2618
 
          if(ret == -1){
2619
 
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2620
 
                         " \"%s\", 0): %s\n", tempdir,
2621
 
                         direntries[i]->d_name, strerror(errno));
2622
 
          }
2623
 
        }
2624
 
        
2625
 
        /* need to clean even if 0 because man page doesn't specify */
2626
 
        free(direntries);
2627
 
        if(numentries == -1){
2628
 
          perror_plus("scandir");
2629
 
        }
2630
 
        ret = rmdir(tempdir);
2631
 
        if(ret == -1 and errno != ENOENT){
2632
 
          perror_plus("rmdir");
2633
 
        }
 
2638
    struct dirent *direntry = NULL;
 
2639
    int numentries = scandir(tempdir, &direntries, notdotentries,
 
2640
                             alphasort);
 
2641
    if(numentries > 0){
 
2642
      for(int i = 0; i < numentries; i++){
 
2643
        direntry = direntries[i];
 
2644
        char *fullname = NULL;
 
2645
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
2646
                       direntry->d_name);
 
2647
        if(ret < 0){
 
2648
          perror_plus("asprintf");
 
2649
          continue;
 
2650
        }
 
2651
        ret = remove(fullname);
 
2652
        if(ret == -1){
 
2653
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
 
2654
                       strerror(errno));
 
2655
        }
 
2656
        free(fullname);
2634
2657
      }
2635
 
      TEMP_FAILURE_RETRY(close(tempdir_fd));
 
2658
    }
 
2659
    
 
2660
    /* need to clean even if 0 because man page doesn't specify */
 
2661
    free(direntries);
 
2662
    if(numentries == -1){
 
2663
      perror_plus("scandir");
 
2664
    }
 
2665
    ret = rmdir(tempdir);
 
2666
    if(ret == -1 and errno != ENOENT){
 
2667
      perror_plus("rmdir");
2636
2668
    }
2637
2669
  }
2638
2670