/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-2015 Teddy Hogeborn
13
 
 * Copyright © 2008-2015 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
650
650
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
651
651
                      __attribute__((unused)) const char *txt){}
652
652
 
653
 
/* Set effective uid to 0, return errno */
654
 
__attribute__((warn_unused_result))
655
 
error_t raise_privileges(void){
656
 
  error_t old_errno = errno;
657
 
  error_t ret_errno = 0;
658
 
  if(seteuid(0) == -1){
659
 
    ret_errno = errno;
660
 
  }
661
 
  errno = old_errno;
662
 
  return ret_errno;
663
 
}
664
 
 
665
 
/* Set effective and real user ID to 0.  Return errno. */
666
 
__attribute__((warn_unused_result))
667
 
error_t raise_privileges_permanently(void){
668
 
  error_t old_errno = errno;
669
 
  error_t ret_errno = raise_privileges();
670
 
  if(ret_errno != 0){
671
 
    errno = old_errno;
672
 
    return ret_errno;
673
 
  }
674
 
  if(setuid(0) == -1){
675
 
    ret_errno = errno;
676
 
  }
677
 
  errno = old_errno;
678
 
  return ret_errno;
679
 
}
680
 
 
681
 
/* Set effective user ID to unprivileged saved user ID */
682
 
__attribute__((warn_unused_result))
683
 
error_t lower_privileges(void){
684
 
  error_t old_errno = errno;
685
 
  error_t ret_errno = 0;
686
 
  if(seteuid(uid) == -1){
687
 
    ret_errno = errno;
688
 
  }
689
 
  errno = old_errno;
690
 
  return ret_errno;
691
 
}
692
 
 
693
 
/* Lower privileges permanently */
694
 
__attribute__((warn_unused_result))
695
 
error_t lower_privileges_permanently(void){
696
 
  error_t old_errno = errno;
697
 
  error_t ret_errno = 0;
698
 
  if(setuid(uid) == -1){
699
 
    ret_errno = errno;
700
 
  }
701
 
  errno = old_errno;
702
 
  return ret_errno;
703
 
}
704
 
 
705
 
/* Helper function to add_local_route() and remove_local_route() */
706
 
__attribute__((nonnull, warn_unused_result))
707
 
static bool add_remove_local_route(const bool add,
708
 
                                   const char *address,
709
 
                                   AvahiIfIndex if_index){
710
 
  int ret;
711
 
  char helper[] = "mandos-client-iprouteadddel";
712
 
  char add_arg[] = "add";
713
 
  char delete_arg[] = "delete";
714
 
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
715
 
  if(pluginhelperdir == NULL){
716
 
    if(debug){
717
 
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
718
 
                   " variable not set; cannot run helper\n");
719
 
    }
720
 
    return false;
721
 
  }
722
 
  
723
 
  char interface[IF_NAMESIZE];
724
 
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
725
 
    perror_plus("if_indextoname");
726
 
    return false;
727
 
  }
728
 
  
729
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
730
 
  if(devnull == -1){
731
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
732
 
    return false;
733
 
  }
734
 
  pid_t pid = fork();
735
 
  if(pid == 0){
736
 
    /* Child */
737
 
    /* Raise privileges */
738
 
    errno = raise_privileges_permanently();
739
 
    if(errno != 0){
740
 
      perror_plus("Failed to raise privileges");
741
 
      /* _exit(EX_NOPERM); */
742
 
    } else {
743
 
      /* Set group */
744
 
      errno = 0;
745
 
      ret = setgid(0);
746
 
      if(ret == -1){
747
 
        perror_plus("setgid");
748
 
        _exit(EX_NOPERM);
749
 
      }
750
 
      /* Reset supplementary groups */
751
 
      errno = 0;
752
 
      ret = setgroups(0, NULL);
753
 
      if(ret == -1){
754
 
        perror_plus("setgroups");
755
 
        _exit(EX_NOPERM);
756
 
      }
757
 
    }
758
 
    ret = dup2(devnull, STDIN_FILENO);
759
 
    if(ret == -1){
760
 
      perror_plus("dup2(devnull, STDIN_FILENO)");
761
 
      _exit(EX_OSERR);
762
 
    }
763
 
    ret = (int)TEMP_FAILURE_RETRY(close(devnull));
764
 
    if(ret == -1){
765
 
      perror_plus("close");
766
 
      _exit(EX_OSERR);
767
 
    }
768
 
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
769
 
    if(ret == -1){
770
 
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
771
 
      _exit(EX_OSERR);
772
 
    }
773
 
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
774
 
                                                    O_RDONLY
775
 
                                                    | O_DIRECTORY
776
 
                                                    | O_PATH
777
 
                                                    | O_CLOEXEC));
778
 
    if(helperdir_fd == -1){
779
 
      perror_plus("open");
780
 
      _exit(EX_UNAVAILABLE);
781
 
    }
782
 
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
783
 
                                                   helper, O_RDONLY));
784
 
    if(helper_fd == -1){
785
 
      perror_plus("openat");
786
 
      _exit(EX_UNAVAILABLE);
787
 
    }
788
 
    TEMP_FAILURE_RETRY(close(helperdir_fd));
789
 
#ifdef __GNUC__
790
 
#pragma GCC diagnostic push
791
 
#pragma GCC diagnostic ignored "-Wcast-qual"
792
 
#endif
793
 
    if(fexecve(helper_fd, (char *const [])
794
 
               { helper, add ? add_arg : delete_arg, (char *)address,
795
 
                   interface, NULL }, environ) == -1){
796
 
#ifdef __GNUC__
797
 
#pragma GCC diagnostic pop
798
 
#endif
799
 
      perror_plus("fexecve");
800
 
      _exit(EXIT_FAILURE);
801
 
    }
802
 
  }
803
 
  if(pid == -1){
804
 
    perror_plus("fork");
805
 
    return false;
806
 
  }
807
 
  int status;
808
 
  pid_t pret = -1;
809
 
  errno = 0;
810
 
  do {
811
 
    pret = waitpid(pid, &status, 0);
812
 
    if(pret == -1 and errno == EINTR and quit_now){
813
 
      int errno_raising = 0;
814
 
      if((errno = raise_privileges()) != 0){
815
 
        errno_raising = errno;
816
 
        perror_plus("Failed to raise privileges in order to"
817
 
                    " kill helper program");
818
 
      }
819
 
      if(kill(pid, SIGTERM) == -1){
820
 
        perror_plus("kill");
821
 
      }
822
 
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
823
 
        perror_plus("Failed to lower privileges after killing"
824
 
                    " helper program");
825
 
      }
826
 
      return false;
827
 
    }
828
 
  } while(pret == -1 and errno == EINTR);
829
 
  if(pret == -1){
830
 
    perror_plus("waitpid");
831
 
    return false;
832
 
  }
833
 
  if(WIFEXITED(status)){
834
 
    if(WEXITSTATUS(status) != 0){
835
 
      fprintf_plus(stderr, "Error: iprouteadddel exited"
836
 
                   " with status %d\n", WEXITSTATUS(status));
837
 
      return false;
838
 
    }
839
 
    return true;
840
 
  }
841
 
  if(WIFSIGNALED(status)){
842
 
    fprintf_plus(stderr, "Error: iprouteadddel died by"
843
 
                 " signal %d\n", WTERMSIG(status));
844
 
    return false;
845
 
  }
846
 
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
847
 
  return false;
848
 
}
849
 
 
850
 
__attribute__((nonnull, warn_unused_result))
851
 
static bool add_local_route(const char *address,
852
 
                            AvahiIfIndex if_index){
853
 
  return add_remove_local_route(true, address, if_index);
854
 
}
855
 
 
856
 
__attribute__((nonnull, warn_unused_result))
857
 
static bool remove_local_route(const char *address,
858
 
                               AvahiIfIndex if_index){
859
 
  return add_remove_local_route(false, address, if_index);
860
 
}
861
 
 
862
653
/* Called when a Mandos server is found */
863
654
__attribute__((nonnull, warn_unused_result))
864
655
static int start_mandos_communication(const char *ip, in_port_t port,
875
666
  int retval = -1;
876
667
  gnutls_session_t session;
877
668
  int pf;                       /* Protocol family */
878
 
  bool route_added = false;
879
669
  
880
670
  errno = 0;
881
671
  
939
729
                 PRIuMAX "\n", ip, (uintmax_t)port);
940
730
  }
941
731
  
942
 
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
 
732
  tcp_sd = socket(pf, SOCK_STREAM, 0);
943
733
  if(tcp_sd < 0){
944
734
    int e = errno;
945
735
    perror_plus("socket");
1034
824
    goto mandos_end;
1035
825
  }
1036
826
  
1037
 
  while(true){
1038
 
    if(af == AF_INET6){
1039
 
      ret = connect(tcp_sd, (struct sockaddr *)&to,
1040
 
                    sizeof(struct sockaddr_in6));
1041
 
    } else {
1042
 
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1043
 
                    sizeof(struct sockaddr_in));
1044
 
    }
1045
 
    if(ret < 0){
1046
 
      if(errno == ENETUNREACH
1047
 
         and if_index != AVAHI_IF_UNSPEC
1048
 
         and connect_to == NULL
1049
 
         and not route_added and
1050
 
         ((af == AF_INET6 and not
1051
 
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1052
 
                                    &to)->sin6_addr)))
1053
 
          or (af == AF_INET and
1054
 
              /* Not a a IPv4LL address */
1055
 
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1056
 
               & 0xFFFF0000L) != 0xA9FE0000L))){
1057
 
        /* Work around Avahi bug - Avahi does not announce link-local
1058
 
           addresses if it has a global address, so local hosts with
1059
 
           *only* a link-local address (e.g. Mandos clients) cannot
1060
 
           connect to a Mandos server announced by Avahi on a server
1061
 
           host with a global address.  Work around this by retrying
1062
 
           with an explicit route added with the server's address.
1063
 
           
1064
 
           Avahi bug reference:
1065
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1066
 
           https://bugs.debian.org/587961
1067
 
        */
1068
 
        int e = errno;
1069
 
        route_added = add_local_route(ip, if_index);
1070
 
        if(route_added){
1071
 
          continue;
1072
 
        }
1073
 
        errno = e;
1074
 
      }
1075
 
      if(errno != ECONNREFUSED or debug){
1076
 
        int e = errno;
1077
 
        perror_plus("connect");
1078
 
        errno = e;
1079
 
      }
1080
 
      goto mandos_end;
1081
 
    }
1082
 
    
1083
 
    if(quit_now){
1084
 
      errno = EINTR;
1085
 
      goto mandos_end;
1086
 
    }
1087
 
    break;
 
827
  if(af == AF_INET6){
 
828
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
829
                  sizeof(struct sockaddr_in6));
 
830
  } else {
 
831
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
832
                  sizeof(struct sockaddr_in));
 
833
  }
 
834
  if(ret < 0){
 
835
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
836
      int e = errno;
 
837
      perror_plus("connect");
 
838
      errno = e;
 
839
    }
 
840
    goto mandos_end;
 
841
  }
 
842
  
 
843
  if(quit_now){
 
844
    errno = EINTR;
 
845
    goto mandos_end;
1088
846
  }
1089
847
  
1090
848
  const char *out = mandos_protocol_version;
1273
1031
  
1274
1032
 mandos_end:
1275
1033
  {
1276
 
    if(route_added){
1277
 
      if(not remove_local_route(ip, if_index)){
1278
 
        fprintf_plus(stderr, "Failed to remove local route to %s on"
1279
 
                     " interface %d", ip, if_index);
1280
 
      }
1281
 
    }
1282
1034
    int e = errno;
1283
1035
    free(decrypted_buffer);
1284
1036
    free(buffer);
1710
1462
  }
1711
1463
}
1712
1464
 
 
1465
/* Set effective uid to 0, return errno */
 
1466
__attribute__((warn_unused_result))
 
1467
error_t raise_privileges(void){
 
1468
  error_t old_errno = errno;
 
1469
  error_t ret_errno = 0;
 
1470
  if(seteuid(0) == -1){
 
1471
    ret_errno = errno;
 
1472
  }
 
1473
  errno = old_errno;
 
1474
  return ret_errno;
 
1475
}
 
1476
 
 
1477
/* Set effective and real user ID to 0.  Return errno. */
 
1478
__attribute__((warn_unused_result))
 
1479
error_t raise_privileges_permanently(void){
 
1480
  error_t old_errno = errno;
 
1481
  error_t ret_errno = raise_privileges();
 
1482
  if(ret_errno != 0){
 
1483
    errno = old_errno;
 
1484
    return ret_errno;
 
1485
  }
 
1486
  if(setuid(0) == -1){
 
1487
    ret_errno = errno;
 
1488
  }
 
1489
  errno = old_errno;
 
1490
  return ret_errno;
 
1491
}
 
1492
 
 
1493
/* Set effective user ID to unprivileged saved user ID */
 
1494
__attribute__((warn_unused_result))
 
1495
error_t lower_privileges(void){
 
1496
  error_t old_errno = errno;
 
1497
  error_t ret_errno = 0;
 
1498
  if(seteuid(uid) == -1){
 
1499
    ret_errno = errno;
 
1500
  }
 
1501
  errno = old_errno;
 
1502
  return ret_errno;
 
1503
}
 
1504
 
 
1505
/* Lower privileges permanently */
 
1506
__attribute__((warn_unused_result))
 
1507
error_t lower_privileges_permanently(void){
 
1508
  error_t old_errno = errno;
 
1509
  error_t ret_errno = 0;
 
1510
  if(setuid(uid) == -1){
 
1511
    ret_errno = errno;
 
1512
  }
 
1513
  errno = old_errno;
 
1514
  return ret_errno;
 
1515
}
 
1516
 
1713
1517
__attribute__((nonnull))
1714
1518
void run_network_hooks(const char *mode, const char *interface,
1715
1519
                       const float delay){
1716
1520
  struct dirent **direntries = NULL;
1717
1521
  if(hookdir_fd == -1){
1718
 
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1719
 
                      | O_CLOEXEC);
 
1522
    hookdir_fd = open(hookdir, O_RDONLY);
1720
1523
    if(hookdir_fd == -1){
1721
1524
      if(errno == ENOENT){
1722
1525
        if(debug){
1747
1550
  }
1748
1551
  struct dirent *direntry;
1749
1552
  int ret;
1750
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1751
 
  if(devnull == -1){
1752
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1753
 
    return;
1754
 
  }
 
1553
  int devnull = open("/dev/null", O_RDONLY);
1755
1554
  for(int i = 0; i < numhooks; i++){
1756
1555
    direntry = direntries[i];
1757
1556
    if(debug){
1781
1580
        perror_plus("setgroups");
1782
1581
        _exit(EX_NOPERM);
1783
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
      }
1784
1598
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1785
1599
      if(ret == -1){
1786
1600
        perror_plus("setenv");
1821
1635
          _exit(EX_OSERR);
1822
1636
        }
1823
1637
      }
1824
 
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
1825
 
                                                   direntry->d_name,
1826
 
                                                   O_RDONLY));
 
1638
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
1827
1639
      if(hook_fd == -1){
1828
1640
        perror_plus("openat");
1829
1641
        _exit(EXIT_FAILURE);
1832
1644
        perror_plus("close");
1833
1645
        _exit(EXIT_FAILURE);
1834
1646
      }
1835
 
      ret = dup2(devnull, STDIN_FILENO);
1836
 
      if(ret == -1){
1837
 
        perror_plus("dup2(devnull, STDIN_FILENO)");
1838
 
        _exit(EX_OSERR);
1839
 
      }
1840
 
      ret = (int)TEMP_FAILURE_RETRY(close(devnull));
1841
 
      if(ret == -1){
1842
 
        perror_plus("close");
1843
 
        _exit(EX_OSERR);
1844
 
      }
1845
 
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1846
 
      if(ret == -1){
1847
 
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1848
 
        _exit(EX_OSERR);
1849
 
      }
1850
1647
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
1851
1648
                 environ) == -1){
1852
1649
        perror_plus("fexecve");
1853
1650
        _exit(EXIT_FAILURE);
1854
1651
      }
1855
1652
    } else {
1856
 
      if(hook_pid == -1){
1857
 
        perror_plus("fork");
1858
 
        free(direntry);
1859
 
        continue;
1860
 
      }
1861
1653
      int status;
1862
1654
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1863
1655
        perror_plus("waitpid");
2300
2092
      goto end;
2301
2093
    }
2302
2094
  }
2303
 
  
 
2095
    
2304
2096
  {
2305
2097
    /* Work around Debian bug #633582:
2306
2098
       <http://bugs.debian.org/633582> */
2333
2125
          TEMP_FAILURE_RETRY(close(seckey_fd));
2334
2126
        }
2335
2127
      }
2336
 
      
 
2128
    
2337
2129
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2338
2130
        int pubkey_fd = open(pubkey, O_RDONLY);
2339
2131
        if(pubkey_fd == -1){
2354
2146
          TEMP_FAILURE_RETRY(close(pubkey_fd));
2355
2147
        }
2356
2148
      }
2357
 
      
 
2149
    
2358
2150
      /* Lower privileges */
2359
2151
      ret_errno = lower_privileges();
2360
2152
      if(ret_errno != 0){
2829
2621
  /* Removes the GPGME temp directory and all files inside */
2830
2622
  if(tempdir != NULL){
2831
2623
    struct dirent **direntries = NULL;
2832
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2833
 
                                                  | O_NOFOLLOW
2834
 
                                                  | O_DIRECTORY
2835
 
                                                  | O_PATH));
 
2624
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2625
                                                  O_NOFOLLOW));
2836
2626
    if(tempdir_fd == -1){
2837
2627
      perror_plus("open");
2838
2628
    } else {