513
508
fprintf_plus(stderr, "GnuTLS: %s", string);
516
__attribute__((nonnull(1, 2, 4), warn_unused_result))
511
__attribute__((nonnull, warn_unused_result))
517
512
static int init_gnutls_global(const char *pubkeyfilename,
518
513
const char *seckeyfilename,
519
const char *dhparamsfilename,
520
514
mandos_context *mc){
525
518
fprintf_plus(stderr, "Initializing GnuTLS\n");
521
ret = gnutls_global_init();
522
if(ret != GNUTLS_E_SUCCESS){
523
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
524
safer_gnutls_strerror(ret));
529
529
/* "Use a log level over 10 to enable all debugging options."
530
530
* - GnuTLS manual
568
569
safer_gnutls_strerror(ret));
571
/* If a Diffie-Hellman parameters file was given, try to use it */
572
if(dhparamsfilename != NULL){
573
gnutls_datum_t params = { .data = NULL, .size = 0 };
575
int dhpfile = open(dhparamsfilename, O_RDONLY);
578
dhparamsfilename = NULL;
581
size_t params_capacity = 0;
583
params_capacity = incbuffer((char **)¶ms.data,
585
(size_t)params_capacity);
586
if(params_capacity == 0){
587
perror_plus("incbuffer");
590
dhparamsfilename = NULL;
593
ssize_t bytes_read = read(dhpfile,
594
params.data + params.size,
600
/* check bytes_read for failure */
605
dhparamsfilename = NULL;
608
params.size += (unsigned int)bytes_read;
610
if(params.data == NULL){
611
dhparamsfilename = NULL;
613
if(dhparamsfilename == NULL){
616
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
617
GNUTLS_X509_FMT_PEM);
618
if(ret != GNUTLS_E_SUCCESS){
619
fprintf_plus(stderr, "Failed to parse DH parameters in file"
620
" \"%s\": %s\n", dhparamsfilename,
621
safer_gnutls_strerror(ret));
622
dhparamsfilename = NULL;
626
if(dhparamsfilename == NULL){
627
if(mc->dh_bits == 0){
628
/* Find out the optimal number of DH bits */
629
/* Try to read the private key file */
630
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
632
int secfile = open(seckeyfilename, O_RDONLY);
637
size_t buffer_capacity = 0;
639
buffer_capacity = incbuffer((char **)&buffer.data,
641
(size_t)buffer_capacity);
642
if(buffer_capacity == 0){
643
perror_plus("incbuffer");
648
ssize_t bytes_read = read(secfile,
649
buffer.data + buffer.size,
655
/* check bytes_read for failure */
662
buffer.size += (unsigned int)bytes_read;
666
/* If successful, use buffer to parse private key */
667
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
668
if(buffer.data != NULL){
670
gnutls_openpgp_privkey_t privkey = NULL;
671
ret = gnutls_openpgp_privkey_init(&privkey);
672
if(ret != GNUTLS_E_SUCCESS){
673
fprintf_plus(stderr, "Error initializing OpenPGP key"
675
safer_gnutls_strerror(ret));
679
ret = gnutls_openpgp_privkey_import
680
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
681
if(ret != GNUTLS_E_SUCCESS){
682
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
683
safer_gnutls_strerror(ret));
689
/* Use private key to suggest an appropriate
691
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
692
gnutls_openpgp_privkey_deinit(privkey);
694
fprintf_plus(stderr, "This OpenPGP key implies using"
695
" a GnuTLS security parameter \"%s\".\n",
696
safe_string(gnutls_sec_param_get_name
702
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
703
/* Err on the side of caution */
704
sec_param = GNUTLS_SEC_PARAM_ULTRA;
706
fprintf_plus(stderr, "Falling back to security parameter"
708
safe_string(gnutls_sec_param_get_name
713
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
717
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
718
" implies %u DH bits; using that.\n",
719
safe_string(gnutls_sec_param_get_name
724
fprintf_plus(stderr, "Failed to get implied number of DH"
725
" bits for security parameter \"%s\"): %s\n",
726
safe_string(gnutls_sec_param_get_name
728
safer_gnutls_strerror(ret));
732
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
735
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
736
if(ret != GNUTLS_E_SUCCESS){
737
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
738
" bits): %s\n", mc->dh_bits,
739
safer_gnutls_strerror(ret));
572
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
573
if(ret != GNUTLS_E_SUCCESS){
574
fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
575
safer_gnutls_strerror(ret));
743
579
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
811
650
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
812
651
__attribute__((unused)) const char *txt){}
814
/* Set effective uid to 0, return errno */
815
__attribute__((warn_unused_result))
816
error_t raise_privileges(void){
817
error_t old_errno = errno;
818
error_t ret_errno = 0;
819
if(seteuid(0) == -1){
826
/* Set effective and real user ID to 0. Return errno. */
827
__attribute__((warn_unused_result))
828
error_t raise_privileges_permanently(void){
829
error_t old_errno = errno;
830
error_t ret_errno = raise_privileges();
842
/* Set effective user ID to unprivileged saved user ID */
843
__attribute__((warn_unused_result))
844
error_t lower_privileges(void){
845
error_t old_errno = errno;
846
error_t ret_errno = 0;
847
if(seteuid(uid) == -1){
854
/* Lower privileges permanently */
855
__attribute__((warn_unused_result))
856
error_t lower_privileges_permanently(void){
857
error_t old_errno = errno;
858
error_t ret_errno = 0;
859
if(setuid(uid) == -1){
866
/* Helper function to add_local_route() and delete_local_route() */
867
__attribute__((nonnull, warn_unused_result))
868
static bool add_delete_local_route(const bool add,
870
AvahiIfIndex if_index){
872
char helper[] = "mandos-client-iprouteadddel";
873
char add_arg[] = "add";
874
char delete_arg[] = "delete";
875
char debug_flag[] = "--debug";
876
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
877
if(pluginhelperdir == NULL){
879
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
880
" variable not set; cannot run helper\n");
885
char interface[IF_NAMESIZE];
886
if(if_indextoname((unsigned int)if_index, interface) == NULL){
887
perror_plus("if_indextoname");
891
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
893
perror_plus("open(\"/dev/null\", O_RDONLY)");
899
/* Raise privileges */
900
errno = raise_privileges_permanently();
902
perror_plus("Failed to raise privileges");
903
/* _exit(EX_NOPERM); */
909
perror_plus("setgid");
912
/* Reset supplementary groups */
914
ret = setgroups(0, NULL);
916
perror_plus("setgroups");
920
ret = dup2(devnull, STDIN_FILENO);
922
perror_plus("dup2(devnull, STDIN_FILENO)");
925
ret = close(devnull);
927
perror_plus("close");
930
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
932
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
935
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
940
if(helperdir_fd == -1){
942
_exit(EX_UNAVAILABLE);
944
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
947
perror_plus("openat");
949
_exit(EX_UNAVAILABLE);
953
#pragma GCC diagnostic push
954
#pragma GCC diagnostic ignored "-Wcast-qual"
956
if(fexecve(helper_fd, (char *const [])
957
{ helper, add ? add_arg : delete_arg, (char *)address,
958
interface, debug ? debug_flag : NULL, NULL },
961
#pragma GCC diagnostic pop
963
perror_plus("fexecve");
975
pret = waitpid(pid, &status, 0);
976
if(pret == -1 and errno == EINTR and quit_now){
977
int errno_raising = 0;
978
if((errno = raise_privileges()) != 0){
979
errno_raising = errno;
980
perror_plus("Failed to raise privileges in order to"
981
" kill helper program");
983
if(kill(pid, SIGTERM) == -1){
986
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
987
perror_plus("Failed to lower privileges after killing"
992
} while(pret == -1 and errno == EINTR);
994
perror_plus("waitpid");
997
if(WIFEXITED(status)){
998
if(WEXITSTATUS(status) != 0){
999
fprintf_plus(stderr, "Error: iprouteadddel exited"
1000
" with status %d\n", WEXITSTATUS(status));
1005
if(WIFSIGNALED(status)){
1006
fprintf_plus(stderr, "Error: iprouteadddel died by"
1007
" signal %d\n", WTERMSIG(status));
1010
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1014
__attribute__((nonnull, warn_unused_result))
1015
static bool add_local_route(const char *address,
1016
AvahiIfIndex if_index){
1018
fprintf_plus(stderr, "Adding route to %s\n", address);
1020
return add_delete_local_route(true, address, if_index);
1023
__attribute__((nonnull, warn_unused_result))
1024
static bool delete_local_route(const char *address,
1025
AvahiIfIndex if_index){
1027
fprintf_plus(stderr, "Removing route to %s\n", address);
1029
return add_delete_local_route(false, address, if_index);
1032
653
/* Called when a Mandos server is found */
1033
654
__attribute__((nonnull, warn_unused_result))
1034
655
static int start_mandos_communication(const char *ip, in_port_t port,
1205
824
goto mandos_end;
1210
ret = connect(tcp_sd, (struct sockaddr *)&to,
1211
sizeof(struct sockaddr_in6));
1213
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1214
sizeof(struct sockaddr_in));
1217
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1218
and if_index != AVAHI_IF_UNSPEC
1219
and connect_to == NULL
1220
and not route_added and
1221
((af == AF_INET6 and not
1222
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1224
or (af == AF_INET and
1225
/* Not a a IPv4LL address */
1226
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1227
& 0xFFFF0000L) != 0xA9FE0000L))){
1228
/* Work around Avahi bug - Avahi does not announce link-local
1229
addresses if it has a global address, so local hosts with
1230
*only* a link-local address (e.g. Mandos clients) cannot
1231
connect to a Mandos server announced by Avahi on a server
1232
host with a global address. Work around this by retrying
1233
with an explicit route added with the server's address.
1235
Avahi bug reference:
1236
http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1237
https://bugs.debian.org/587961
1240
fprintf_plus(stderr, "Mandos server unreachable, trying"
1244
route_added = add_local_route(ip, if_index);
1250
if(errno != ECONNREFUSED or debug){
1252
perror_plus("connect");
828
ret = connect(tcp_sd, (struct sockaddr *)&to,
829
sizeof(struct sockaddr_in6));
831
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
832
sizeof(struct sockaddr_in));
835
if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
837
perror_plus("connect");
1265
848
const char *out = mandos_protocol_version;
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){
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();
1486
if(setuid(0) == -1){
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){
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){
1889
1517
__attribute__((nonnull))
1890
1518
void run_network_hooks(const char *mode, const char *interface,
1891
1519
const float delay){
1892
1520
struct dirent **direntries = NULL;
1893
1521
if(hookdir_fd == -1){
1894
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1522
hookdir_fd = open(hookdir, O_RDONLY);
1896
1523
if(hookdir_fd == -1){
1897
1524
if(errno == ENOENT){
1536
#if __GLIBC_PREREQ(2, 15)
1908
1537
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1909
1538
runnable_hook, alphasort);
1539
#else /* not __GLIBC_PREREQ(2, 15) */
1540
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1542
#endif /* not __GLIBC_PREREQ(2, 15) */
1543
#else /* not __GLIBC__ */
1544
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1546
#endif /* not __GLIBC__ */
1910
1547
if(numhooks == -1){
1911
1548
perror_plus("scandir");
1914
1551
struct dirent *direntry;
1916
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1918
perror_plus("open(\"/dev/null\", O_RDONLY)");
1553
int devnull = open("/dev/null", O_RDONLY);
1921
1554
for(int i = 0; i < numhooks; i++){
1922
1555
direntry = direntries[i];
3027
2621
/* Removes the GPGME temp directory and all files inside */
3028
2622
if(tempdir != NULL){
3029
2623
struct dirent **direntries = NULL;
3030
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2624
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
3034
2626
if(tempdir_fd == -1){
3035
2627
perror_plus("open");
2630
#if __GLIBC_PREREQ(2, 15)
3037
2631
int numentries = scandirat(tempdir_fd, ".", &direntries,
3038
2632
notdotentries, alphasort);
2633
#else /* not __GLIBC_PREREQ(2, 15) */
2634
int numentries = scandir(tempdir, &direntries, notdotentries,
2636
#endif /* not __GLIBC_PREREQ(2, 15) */
2637
#else /* not __GLIBC__ */
2638
int numentries = scandir(tempdir, &direntries, notdotentries,
2640
#endif /* not __GLIBC__ */
3039
2641
if(numentries >= 0){
3040
2642
for(int i = 0; i < numentries; i++){
3041
2643
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);