/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:
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
233
234
                          .af = af };
234
235
  if(new_server->ip == NULL){
235
236
    perror_plus("strdup");
 
237
    free(new_server);
236
238
    return false;
237
239
  }
238
240
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
239
241
  if(ret == -1){
240
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);
241
252
    return false;
242
253
  }
243
254
  /* Special case of first server */
1065
1076
     timed out */
1066
1077
  
1067
1078
  if(quit_now){
 
1079
    avahi_s_service_resolver_free(r);
1068
1080
    return;
1069
1081
  }
1070
1082
  
1457
1469
  error_t ret_errno = 0;
1458
1470
  if(seteuid(0) == -1){
1459
1471
    ret_errno = errno;
1460
 
    perror_plus("seteuid");
1461
1472
  }
1462
1473
  errno = old_errno;
1463
1474
  return ret_errno;
1474
1485
  }
1475
1486
  if(setuid(0) == -1){
1476
1487
    ret_errno = errno;
1477
 
    perror_plus("seteuid");
1478
1488
  }
1479
1489
  errno = old_errno;
1480
1490
  return ret_errno;
1487
1497
  error_t ret_errno = 0;
1488
1498
  if(seteuid(uid) == -1){
1489
1499
    ret_errno = errno;
1490
 
    perror_plus("seteuid");
1491
1500
  }
1492
1501
  errno = old_errno;
1493
1502
  return ret_errno;
1500
1509
  error_t ret_errno = 0;
1501
1510
  if(setuid(uid) == -1){
1502
1511
    ret_errno = errno;
1503
 
    perror_plus("setuid");
1504
1512
  }
1505
1513
  errno = old_errno;
1506
1514
  return ret_errno;
1507
1515
}
1508
1516
 
1509
 
#ifndef O_CLOEXEC
1510
 
/*
1511
 
 * Based on the example in the GNU LibC manual chapter 13.13 "File
1512
 
 * Descriptor Flags".
1513
 
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
1514
 
 */
1515
 
__attribute__((warn_unused_result))
1516
 
static int set_cloexec_flag(int fd){
1517
 
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
1518
 
  /* If reading the flags failed, return error indication now. */
1519
 
  if(ret < 0){
1520
 
    return ret;
1521
 
  }
1522
 
  /* Store modified flag word in the descriptor. */
1523
 
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
1524
 
                                       ret | FD_CLOEXEC));
1525
 
}
1526
 
#endif  /* not O_CLOEXEC */
1527
 
 
1528
1517
__attribute__((nonnull))
1529
1518
void run_network_hooks(const char *mode, const char *interface,
1530
1519
                       const float delay){
1531
 
  struct dirent **direntries;
 
1520
  struct dirent **direntries = NULL;
1532
1521
  if(hookdir_fd == -1){
1533
 
    hookdir_fd = open(hookdir, O_RDONLY |
1534
 
#ifdef O_CLOEXEC
1535
 
                      O_CLOEXEC
1536
 
#else  /* not O_CLOEXEC */
1537
 
                      0
1538
 
#endif  /* not O_CLOEXEC */
1539
 
                      );
 
1522
    hookdir_fd = open(hookdir, O_RDONLY);
1540
1523
    if(hookdir_fd == -1){
1541
1524
      if(errno == ENOENT){
1542
1525
        if(debug){
1548
1531
      }
1549
1532
      return;
1550
1533
    }
1551
 
#ifndef O_CLOEXEC
1552
 
    if(set_cloexec_flag(hookdir_fd) < 0){
1553
 
      perror_plus("set_cloexec_flag");
1554
 
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1555
 
        perror_plus("close");
1556
 
      } else {
1557
 
        hookdir_fd = -1;
1558
 
      }
1559
 
      return;
1560
 
    }
1561
 
#endif  /* not O_CLOEXEC */
1562
1534
  }
1563
1535
#ifdef __GLIBC__
1564
1536
#if __GLIBC_PREREQ(2, 15)
1589
1561
    if(hook_pid == 0){
1590
1562
      /* Child */
1591
1563
      /* Raise privileges */
1592
 
      if(raise_privileges_permanently() != 0){
 
1564
      errno = raise_privileges_permanently();
 
1565
      if(errno != 0){
1593
1566
        perror_plus("Failed to raise privileges");
1594
1567
        _exit(EX_NOPERM);
1595
1568
      }
1662
1635
          _exit(EX_OSERR);
1663
1636
        }
1664
1637
      }
1665
 
      if(fexecve(hookdir_fd, (char *const [])
1666
 
                 { direntry->d_name, NULL }, environ) == -1){
 
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){
1667
1649
        perror_plus("fexecve");
1668
1650
        _exit(EXIT_FAILURE);
1669
1651
      }
1670
1652
    } else {
 
1653
      if(hook_pid == -1){
 
1654
        perror_plus("fork");
 
1655
        free(direntry);
 
1656
        continue;
 
1657
      }
1671
1658
      int status;
1672
1659
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1673
1660
        perror_plus("waitpid");
 
1661
        free(direntry);
1674
1662
        continue;
1675
1663
      }
1676
1664
      if(WIFEXITED(status)){
1678
1666
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1679
1667
                       " with status %d\n", direntry->d_name,
1680
1668
                       WEXITSTATUS(status));
 
1669
          free(direntry);
1681
1670
          continue;
1682
1671
        }
1683
1672
      } else if(WIFSIGNALED(status)){
1684
1673
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1685
1674
                     " signal %d\n", direntry->d_name,
1686
1675
                     WTERMSIG(status));
 
1676
        free(direntry);
1687
1677
        continue;
1688
1678
      } else {
1689
1679
        fprintf_plus(stderr, "Warning: network hook \"%s\""
1690
1680
                     " crashed\n", direntry->d_name);
 
1681
        free(direntry);
1691
1682
        continue;
1692
1683
      }
1693
1684
    }
1695
1686
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1696
1687
                   direntry->d_name);
1697
1688
    }
 
1689
    free(direntry);
1698
1690
  }
 
1691
  free(direntries);
1699
1692
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1700
1693
    perror_plus("close");
1701
1694
  } else {
1758
1751
    /* Raise privileges */
1759
1752
    ret_errno = raise_privileges();
1760
1753
    if(ret_errno != 0){
 
1754
      errno = ret_errno;
1761
1755
      perror_plus("Failed to raise privileges");
1762
1756
    }
1763
1757
    
1867
1861
    /* Raise privileges */
1868
1862
    ret_errno = raise_privileges();
1869
1863
    if(ret_errno != 0){
 
1864
      errno = ret_errno;
1870
1865
      perror_plus("Failed to raise privileges");
1871
1866
    }
1872
1867
    
1907
1902
int main(int argc, char *argv[]){
1908
1903
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
1909
1904
                        .priority = "SECURE256:!CTYPE-X.509:"
1910
 
                        "+CTYPE-OPENPGP", .current_server = NULL,
 
1905
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
1911
1906
                        .interfaces = NULL, .interfaces_size = 0 };
1912
1907
  AvahiSServiceBrowser *sb = NULL;
1913
1908
  error_t ret_errno;
2279
2274
  
2280
2275
  /* If no interfaces were specified, make a list */
2281
2276
  if(mc.interfaces == NULL){
2282
 
    struct dirent **direntries;
 
2277
    struct dirent **direntries = NULL;
2283
2278
    /* Look for any good interfaces */
2284
2279
    ret = scandir(sys_class_net, &direntries, good_interface,
2285
2280
                  alphasort);
2291
2286
        if(ret_errno != 0){
2292
2287
          errno = ret_errno;
2293
2288
          perror_plus("argz_add");
 
2289
          free(direntries[i]);
2294
2290
          continue;
2295
2291
        }
2296
2292
        if(debug){
2297
2293
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2298
2294
                       direntries[i]->d_name);
2299
2295
        }
 
2296
        free(direntries[i]);
2300
2297
      }
2301
2298
      free(direntries);
2302
2299
    } else {
2303
 
      free(direntries);
 
2300
      if(ret == 0){
 
2301
        free(direntries);
 
2302
      }
2304
2303
      fprintf_plus(stderr, "Could not find a network interface\n");
2305
2304
      exitcode = EXIT_FAILURE;
2306
2305
      goto end;
2570
2569
    mc.current_server->prev->next = NULL;
2571
2570
    while(mc.current_server != NULL){
2572
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
2573
2580
      free(mc.current_server);
2574
2581
      mc.current_server = next;
2575
2582
    }
2579
2586
  {
2580
2587
    ret_errno = raise_privileges();
2581
2588
    if(ret_errno != 0){
 
2589
      errno = ret_errno;
2582
2590
      perror_plus("Failed to raise privileges");
2583
2591
    } else {
2584
2592
      
2607
2615
    
2608
2616
    ret_errno = lower_privileges_permanently();
2609
2617
    if(ret_errno != 0){
 
2618
      errno = ret_errno;
2610
2619
      perror_plus("Failed to lower privileges permanently");
2611
2620
    }
2612
2621
  }
2617
2626
  /* Removes the GPGME temp directory and all files inside */
2618
2627
  if(tempdir != NULL){
2619
2628
    struct dirent **direntries = NULL;
2620
 
    struct dirent *direntry = NULL;
2621
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2622
 
                             alphasort);
2623
 
    if(numentries > 0){
2624
 
      for(int i = 0; i < numentries; i++){
2625
 
        direntry = direntries[i];
2626
 
        char *fullname = NULL;
2627
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2628
 
                       direntry->d_name);
2629
 
        if(ret < 0){
2630
 
          perror_plus("asprintf");
2631
 
          continue;
2632
 
        }
2633
 
        ret = remove(fullname);
2634
 
        if(ret == -1){
2635
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2636
 
                       strerror(errno));
2637
 
        }
2638
 
        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
        }
2639
2666
      }
2640
 
    }
2641
 
    
2642
 
    /* need to clean even if 0 because man page doesn't specify */
2643
 
    free(direntries);
2644
 
    if(numentries == -1){
2645
 
      perror_plus("scandir");
2646
 
    }
2647
 
    ret = rmdir(tempdir);
2648
 
    if(ret == -1 and errno != ENOENT){
2649
 
      perror_plus("rmdir");
 
2667
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2650
2668
    }
2651
2669
  }
2652
2670