/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-08-09 13:18:46 UTC
  • Revision ID: teddy@recompile.se-20140809131846-2emd272mf9c9wihe
mandos: Replace unicode() with str().

This is in preparation for the coming Python 3 conversion.

* mandos: Replace "str" with "unicode".  All callers changed.

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 delete_local_route() */
706
 
__attribute__((nonnull, warn_unused_result))
707
 
static bool add_delete_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 debug_flag[] = "--debug";
715
 
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
716
 
  if(pluginhelperdir == NULL){
717
 
    if(debug){
718
 
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
719
 
                   " variable not set; cannot run helper\n");
720
 
    }
721
 
    return false;
722
 
  }
723
 
  
724
 
  char interface[IF_NAMESIZE];
725
 
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
726
 
    perror_plus("if_indextoname");
727
 
    return false;
728
 
  }
729
 
  
730
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
731
 
  if(devnull == -1){
732
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
733
 
    return false;
734
 
  }
735
 
  pid_t pid = fork();
736
 
  if(pid == 0){
737
 
    /* Child */
738
 
    /* Raise privileges */
739
 
    errno = raise_privileges_permanently();
740
 
    if(errno != 0){
741
 
      perror_plus("Failed to raise privileges");
742
 
      /* _exit(EX_NOPERM); */
743
 
    } else {
744
 
      /* Set group */
745
 
      errno = 0;
746
 
      ret = setgid(0);
747
 
      if(ret == -1){
748
 
        perror_plus("setgid");
749
 
        _exit(EX_NOPERM);
750
 
      }
751
 
      /* Reset supplementary groups */
752
 
      errno = 0;
753
 
      ret = setgroups(0, NULL);
754
 
      if(ret == -1){
755
 
        perror_plus("setgroups");
756
 
        _exit(EX_NOPERM);
757
 
      }
758
 
    }
759
 
    ret = dup2(devnull, STDIN_FILENO);
760
 
    if(ret == -1){
761
 
      perror_plus("dup2(devnull, STDIN_FILENO)");
762
 
      _exit(EX_OSERR);
763
 
    }
764
 
    ret = (int)TEMP_FAILURE_RETRY(close(devnull));
765
 
    if(ret == -1){
766
 
      perror_plus("close");
767
 
      _exit(EX_OSERR);
768
 
    }
769
 
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
770
 
    if(ret == -1){
771
 
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
772
 
      _exit(EX_OSERR);
773
 
    }
774
 
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
775
 
                                                    O_RDONLY
776
 
                                                    | O_DIRECTORY
777
 
                                                    | O_PATH
778
 
                                                    | O_CLOEXEC));
779
 
    if(helperdir_fd == -1){
780
 
      perror_plus("open");
781
 
      _exit(EX_UNAVAILABLE);
782
 
    }
783
 
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
784
 
                                                   helper, O_RDONLY));
785
 
    if(helper_fd == -1){
786
 
      perror_plus("openat");
787
 
      _exit(EX_UNAVAILABLE);
788
 
    }
789
 
    TEMP_FAILURE_RETRY(close(helperdir_fd));
790
 
#ifdef __GNUC__
791
 
#pragma GCC diagnostic push
792
 
#pragma GCC diagnostic ignored "-Wcast-qual"
793
 
#endif
794
 
    if(fexecve(helper_fd, (char *const [])
795
 
               { helper, add ? add_arg : delete_arg, (char *)address,
796
 
                   interface, debug ? debug_flag : NULL, NULL },
797
 
               environ) == -1){
798
 
#ifdef __GNUC__
799
 
#pragma GCC diagnostic pop
800
 
#endif
801
 
      perror_plus("fexecve");
802
 
      _exit(EXIT_FAILURE);
803
 
    }
804
 
  }
805
 
  if(pid == -1){
806
 
    perror_plus("fork");
807
 
    return false;
808
 
  }
809
 
  int status;
810
 
  pid_t pret = -1;
811
 
  errno = 0;
812
 
  do {
813
 
    pret = waitpid(pid, &status, 0);
814
 
    if(pret == -1 and errno == EINTR and quit_now){
815
 
      int errno_raising = 0;
816
 
      if((errno = raise_privileges()) != 0){
817
 
        errno_raising = errno;
818
 
        perror_plus("Failed to raise privileges in order to"
819
 
                    " kill helper program");
820
 
      }
821
 
      if(kill(pid, SIGTERM) == -1){
822
 
        perror_plus("kill");
823
 
      }
824
 
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
825
 
        perror_plus("Failed to lower privileges after killing"
826
 
                    " helper program");
827
 
      }
828
 
      return false;
829
 
    }
830
 
  } while(pret == -1 and errno == EINTR);
831
 
  if(pret == -1){
832
 
    perror_plus("waitpid");
833
 
    return false;
834
 
  }
835
 
  if(WIFEXITED(status)){
836
 
    if(WEXITSTATUS(status) != 0){
837
 
      fprintf_plus(stderr, "Error: iprouteadddel exited"
838
 
                   " with status %d\n", WEXITSTATUS(status));
839
 
      return false;
840
 
    }
841
 
    return true;
842
 
  }
843
 
  if(WIFSIGNALED(status)){
844
 
    fprintf_plus(stderr, "Error: iprouteadddel died by"
845
 
                 " signal %d\n", WTERMSIG(status));
846
 
    return false;
847
 
  }
848
 
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
849
 
  return false;
850
 
}
851
 
 
852
 
__attribute__((nonnull, warn_unused_result))
853
 
static bool add_local_route(const char *address,
854
 
                            AvahiIfIndex if_index){
855
 
  if(debug){
856
 
    fprintf_plus(stderr, "Adding route to %s\n", address);
857
 
  }
858
 
  return add_delete_local_route(true, address, if_index);
859
 
}
860
 
 
861
 
__attribute__((nonnull, warn_unused_result))
862
 
static bool delete_local_route(const char *address,
863
 
                               AvahiIfIndex if_index){
864
 
  if(debug){
865
 
    fprintf_plus(stderr, "Removing route to %s\n", address);
866
 
  }
867
 
  return add_delete_local_route(false, address, if_index);
868
 
}
869
 
 
870
653
/* Called when a Mandos server is found */
871
654
__attribute__((nonnull, warn_unused_result))
872
655
static int start_mandos_communication(const char *ip, in_port_t port,
883
666
  int retval = -1;
884
667
  gnutls_session_t session;
885
668
  int pf;                       /* Protocol family */
886
 
  bool route_added = false;
887
669
  
888
670
  errno = 0;
889
671
  
947
729
                 PRIuMAX "\n", ip, (uintmax_t)port);
948
730
  }
949
731
  
950
 
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
 
732
  tcp_sd = socket(pf, SOCK_STREAM, 0);
951
733
  if(tcp_sd < 0){
952
734
    int e = errno;
953
735
    perror_plus("socket");
1042
824
    goto mandos_end;
1043
825
  }
1044
826
  
1045
 
  while(true){
1046
 
    if(af == AF_INET6){
1047
 
      ret = connect(tcp_sd, (struct sockaddr *)&to,
1048
 
                    sizeof(struct sockaddr_in6));
1049
 
    } else {
1050
 
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1051
 
                    sizeof(struct sockaddr_in));
1052
 
    }
1053
 
    if(ret < 0){
1054
 
      if(errno == ENETUNREACH
1055
 
         and if_index != AVAHI_IF_UNSPEC
1056
 
         and connect_to == NULL
1057
 
         and not route_added and
1058
 
         ((af == AF_INET6 and not
1059
 
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1060
 
                                    &to)->sin6_addr)))
1061
 
          or (af == AF_INET and
1062
 
              /* Not a a IPv4LL address */
1063
 
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1064
 
               & 0xFFFF0000L) != 0xA9FE0000L))){
1065
 
        /* Work around Avahi bug - Avahi does not announce link-local
1066
 
           addresses if it has a global address, so local hosts with
1067
 
           *only* a link-local address (e.g. Mandos clients) cannot
1068
 
           connect to a Mandos server announced by Avahi on a server
1069
 
           host with a global address.  Work around this by retrying
1070
 
           with an explicit route added with the server's address.
1071
 
           
1072
 
           Avahi bug reference:
1073
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1074
 
           https://bugs.debian.org/587961
1075
 
        */
1076
 
        if(debug){
1077
 
          fprintf_plus(stderr, "Mandos server unreachable, trying"
1078
 
                       " direct route\n");
1079
 
        }
1080
 
        int e = errno;
1081
 
        route_added = add_local_route(ip, if_index);
1082
 
        if(route_added){
1083
 
          continue;
1084
 
        }
1085
 
        errno = e;
1086
 
      }
1087
 
      if(errno != ECONNREFUSED or debug){
1088
 
        int e = errno;
1089
 
        perror_plus("connect");
1090
 
        errno = e;
1091
 
      }
1092
 
      goto mandos_end;
1093
 
    }
1094
 
    
1095
 
    if(quit_now){
1096
 
      errno = EINTR;
1097
 
      goto mandos_end;
1098
 
    }
1099
 
    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;
1100
846
  }
1101
847
  
1102
848
  const char *out = mandos_protocol_version;
1285
1031
  
1286
1032
 mandos_end:
1287
1033
  {
1288
 
    if(route_added){
1289
 
      if(not delete_local_route(ip, if_index)){
1290
 
        fprintf_plus(stderr, "Failed to delete local route to %s on"
1291
 
                     " interface %d", ip, if_index);
1292
 
      }
1293
 
    }
1294
1034
    int e = errno;
1295
1035
    free(decrypted_buffer);
1296
1036
    free(buffer);
1722
1462
  }
1723
1463
}
1724
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
 
1725
1517
__attribute__((nonnull))
1726
1518
void run_network_hooks(const char *mode, const char *interface,
1727
1519
                       const float delay){
1728
1520
  struct dirent **direntries = NULL;
1729
1521
  if(hookdir_fd == -1){
1730
 
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1731
 
                      | O_CLOEXEC);
 
1522
    hookdir_fd = open(hookdir, O_RDONLY);
1732
1523
    if(hookdir_fd == -1){
1733
1524
      if(errno == ENOENT){
1734
1525
        if(debug){
1759
1550
  }
1760
1551
  struct dirent *direntry;
1761
1552
  int ret;
1762
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1763
 
  if(devnull == -1){
1764
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1765
 
    return;
1766
 
  }
 
1553
  int devnull = open("/dev/null", O_RDONLY);
1767
1554
  for(int i = 0; i < numhooks; i++){
1768
1555
    direntry = direntries[i];
1769
1556
    if(debug){
1793
1580
        perror_plus("setgroups");
1794
1581
        _exit(EX_NOPERM);
1795
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
      }
1796
1598
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1797
1599
      if(ret == -1){
1798
1600
        perror_plus("setenv");
1833
1635
          _exit(EX_OSERR);
1834
1636
        }
1835
1637
      }
1836
 
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
1837
 
                                                   direntry->d_name,
1838
 
                                                   O_RDONLY));
 
1638
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
1839
1639
      if(hook_fd == -1){
1840
1640
        perror_plus("openat");
1841
1641
        _exit(EXIT_FAILURE);
1844
1644
        perror_plus("close");
1845
1645
        _exit(EXIT_FAILURE);
1846
1646
      }
1847
 
      ret = dup2(devnull, STDIN_FILENO);
1848
 
      if(ret == -1){
1849
 
        perror_plus("dup2(devnull, STDIN_FILENO)");
1850
 
        _exit(EX_OSERR);
1851
 
      }
1852
 
      ret = (int)TEMP_FAILURE_RETRY(close(devnull));
1853
 
      if(ret == -1){
1854
 
        perror_plus("close");
1855
 
        _exit(EX_OSERR);
1856
 
      }
1857
 
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1858
 
      if(ret == -1){
1859
 
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1860
 
        _exit(EX_OSERR);
1861
 
      }
1862
1647
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
1863
1648
                 environ) == -1){
1864
1649
        perror_plus("fexecve");
1865
1650
        _exit(EXIT_FAILURE);
1866
1651
      }
1867
1652
    } else {
1868
 
      if(hook_pid == -1){
1869
 
        perror_plus("fork");
1870
 
        free(direntry);
1871
 
        continue;
1872
 
      }
1873
1653
      int status;
1874
1654
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1875
1655
        perror_plus("waitpid");
2312
2092
      goto end;
2313
2093
    }
2314
2094
  }
2315
 
  
 
2095
    
2316
2096
  {
2317
2097
    /* Work around Debian bug #633582:
2318
2098
       <http://bugs.debian.org/633582> */
2345
2125
          TEMP_FAILURE_RETRY(close(seckey_fd));
2346
2126
        }
2347
2127
      }
2348
 
      
 
2128
    
2349
2129
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2350
2130
        int pubkey_fd = open(pubkey, O_RDONLY);
2351
2131
        if(pubkey_fd == -1){
2366
2146
          TEMP_FAILURE_RETRY(close(pubkey_fd));
2367
2147
        }
2368
2148
      }
2369
 
      
 
2149
    
2370
2150
      /* Lower privileges */
2371
2151
      ret_errno = lower_privileges();
2372
2152
      if(ret_errno != 0){
2841
2621
  /* Removes the GPGME temp directory and all files inside */
2842
2622
  if(tempdir != NULL){
2843
2623
    struct dirent **direntries = NULL;
2844
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2845
 
                                                  | O_NOFOLLOW
2846
 
                                                  | O_DIRECTORY
2847
 
                                                  | O_PATH));
 
2624
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2625
                                                  O_NOFOLLOW));
2848
2626
    if(tempdir_fd == -1){
2849
2627
      perror_plus("open");
2850
2628
    } else {