57
57
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
58
58
inet_pton(), connect(),
60
#include <fcntl.h> /* open(), unlinkat() */
60
#include <fcntl.h> /* open(), unlinkat(), AT_REMOVEDIR */
61
61
#include <dirent.h> /* opendir(), struct dirent, readdir()
63
63
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
65
#include <errno.h> /* perror(), errno,
65
#include <errno.h> /* perror(), errno, EINTR, EINVAL,
66
EAI_SYSTEM, ENETUNREACH,
67
EHOSTUNREACH, ECONNREFUSED, EPROTO,
68
EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
66
70
program_invocation_short_name */
67
71
#include <time.h> /* nanosleep(), time(), sleep() */
68
72
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
508
517
fprintf_plus(stderr, "GnuTLS: %s", string);
511
__attribute__((nonnull, warn_unused_result))
520
__attribute__((nonnull(1, 2, 4), warn_unused_result))
512
521
static int init_gnutls_global(const char *pubkeyfilename,
513
522
const char *seckeyfilename,
523
const char *dhparamsfilename,
514
524
mandos_context *mc){
518
529
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
533
/* "Use a log level over 10 to enable all debugging options."
530
534
* - GnuTLS manual
569
572
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));
575
/* If a Diffie-Hellman parameters file was given, try to use it */
576
if(dhparamsfilename != NULL){
577
gnutls_datum_t params = { .data = NULL, .size = 0 };
579
int dhpfile = open(dhparamsfilename, O_RDONLY);
582
dhparamsfilename = NULL;
585
size_t params_capacity = 0;
587
params_capacity = incbuffer((char **)¶ms.data,
589
(size_t)params_capacity);
590
if(params_capacity == 0){
591
perror_plus("incbuffer");
594
dhparamsfilename = NULL;
597
ssize_t bytes_read = read(dhpfile,
598
params.data + params.size,
604
/* check bytes_read for failure */
609
dhparamsfilename = NULL;
612
params.size += (unsigned int)bytes_read;
614
if(params.data == NULL){
615
dhparamsfilename = NULL;
617
if(dhparamsfilename == NULL){
620
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
621
GNUTLS_X509_FMT_PEM);
622
if(ret != GNUTLS_E_SUCCESS){
623
fprintf_plus(stderr, "Failed to parse DH parameters in file"
624
" \"%s\": %s\n", dhparamsfilename,
625
safer_gnutls_strerror(ret));
626
dhparamsfilename = NULL;
630
if(dhparamsfilename == NULL){
631
if(mc->dh_bits == 0){
632
/* Find out the optimal number of DH bits */
633
/* Try to read the private key file */
634
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
636
int secfile = open(seckeyfilename, O_RDONLY);
641
size_t buffer_capacity = 0;
643
buffer_capacity = incbuffer((char **)&buffer.data,
645
(size_t)buffer_capacity);
646
if(buffer_capacity == 0){
647
perror_plus("incbuffer");
652
ssize_t bytes_read = read(secfile,
653
buffer.data + buffer.size,
659
/* check bytes_read for failure */
666
buffer.size += (unsigned int)bytes_read;
670
/* If successful, use buffer to parse private key */
671
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
672
if(buffer.data != NULL){
674
gnutls_openpgp_privkey_t privkey = NULL;
675
ret = gnutls_openpgp_privkey_init(&privkey);
676
if(ret != GNUTLS_E_SUCCESS){
677
fprintf_plus(stderr, "Error initializing OpenPGP key"
679
safer_gnutls_strerror(ret));
683
ret = gnutls_openpgp_privkey_import
684
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
685
if(ret != GNUTLS_E_SUCCESS){
686
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
687
safer_gnutls_strerror(ret));
693
/* Use private key to suggest an appropriate
695
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
696
gnutls_openpgp_privkey_deinit(privkey);
698
fprintf_plus(stderr, "This OpenPGP key implies using"
699
" a GnuTLS security parameter \"%s\".\n",
700
safe_string(gnutls_sec_param_get_name
706
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
707
/* Err on the side of caution */
708
sec_param = GNUTLS_SEC_PARAM_ULTRA;
710
fprintf_plus(stderr, "Falling back to security parameter"
712
safe_string(gnutls_sec_param_get_name
717
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
721
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
722
" implies %u DH bits; using that.\n",
723
safe_string(gnutls_sec_param_get_name
728
fprintf_plus(stderr, "Failed to get implied number of DH"
729
" bits for security parameter \"%s\"): %s\n",
730
safe_string(gnutls_sec_param_get_name
732
safer_gnutls_strerror(ret));
736
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
739
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
740
if(ret != GNUTLS_E_SUCCESS){
741
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
742
" bits): %s\n", mc->dh_bits,
743
safer_gnutls_strerror(ret));
579
747
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
650
815
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
651
816
__attribute__((unused)) const char *txt){}
818
/* Set effective uid to 0, return errno */
819
__attribute__((warn_unused_result))
820
int raise_privileges(void){
821
int old_errno = errno;
823
if(seteuid(0) == -1){
830
/* Set effective and real user ID to 0. Return errno. */
831
__attribute__((warn_unused_result))
832
int raise_privileges_permanently(void){
833
int old_errno = errno;
834
int ret = raise_privileges();
846
/* Set effective user ID to unprivileged saved user ID */
847
__attribute__((warn_unused_result))
848
int lower_privileges(void){
849
int old_errno = errno;
851
if(seteuid(uid) == -1){
858
/* Lower privileges permanently */
859
__attribute__((warn_unused_result))
860
int lower_privileges_permanently(void){
861
int old_errno = errno;
863
if(setuid(uid) == -1){
870
/* Helper function to add_local_route() and delete_local_route() */
871
__attribute__((nonnull, warn_unused_result))
872
static bool add_delete_local_route(const bool add,
874
AvahiIfIndex if_index){
876
char helper[] = "mandos-client-iprouteadddel";
877
char add_arg[] = "add";
878
char delete_arg[] = "delete";
879
char debug_flag[] = "--debug";
880
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
881
if(pluginhelperdir == NULL){
883
fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
884
" variable not set; cannot run helper\n");
889
char interface[IF_NAMESIZE];
890
if(if_indextoname((unsigned int)if_index, interface) == NULL){
891
perror_plus("if_indextoname");
895
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
897
perror_plus("open(\"/dev/null\", O_RDONLY)");
903
/* Raise privileges */
904
errno = raise_privileges_permanently();
906
perror_plus("Failed to raise privileges");
907
/* _exit(EX_NOPERM); */
913
perror_plus("setgid");
916
/* Reset supplementary groups */
918
ret = setgroups(0, NULL);
920
perror_plus("setgroups");
924
ret = dup2(devnull, STDIN_FILENO);
926
perror_plus("dup2(devnull, STDIN_FILENO)");
929
ret = close(devnull);
931
perror_plus("close");
934
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
936
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
939
int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
944
if(helperdir_fd == -1){
946
_exit(EX_UNAVAILABLE);
948
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
951
perror_plus("openat");
953
_exit(EX_UNAVAILABLE);
957
#pragma GCC diagnostic push
958
#pragma GCC diagnostic ignored "-Wcast-qual"
960
if(fexecve(helper_fd, (char *const [])
961
{ helper, add ? add_arg : delete_arg, (char *)address,
962
interface, debug ? debug_flag : NULL, NULL },
965
#pragma GCC diagnostic pop
967
perror_plus("fexecve");
979
pret = waitpid(pid, &status, 0);
980
if(pret == -1 and errno == EINTR and quit_now){
981
int errno_raising = 0;
982
if((errno = raise_privileges()) != 0){
983
errno_raising = errno;
984
perror_plus("Failed to raise privileges in order to"
985
" kill helper program");
987
if(kill(pid, SIGTERM) == -1){
990
if((errno_raising == 0) and (errno = lower_privileges()) != 0){
991
perror_plus("Failed to lower privileges after killing"
996
} while(pret == -1 and errno == EINTR);
998
perror_plus("waitpid");
1001
if(WIFEXITED(status)){
1002
if(WEXITSTATUS(status) != 0){
1003
fprintf_plus(stderr, "Error: iprouteadddel exited"
1004
" with status %d\n", WEXITSTATUS(status));
1009
if(WIFSIGNALED(status)){
1010
fprintf_plus(stderr, "Error: iprouteadddel died by"
1011
" signal %d\n", WTERMSIG(status));
1014
fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
1018
__attribute__((nonnull, warn_unused_result))
1019
static bool add_local_route(const char *address,
1020
AvahiIfIndex if_index){
1022
fprintf_plus(stderr, "Adding route to %s\n", address);
1024
return add_delete_local_route(true, address, if_index);
1027
__attribute__((nonnull, warn_unused_result))
1028
static bool delete_local_route(const char *address,
1029
AvahiIfIndex if_index){
1031
fprintf_plus(stderr, "Removing route to %s\n", address);
1033
return add_delete_local_route(false, address, if_index);
653
1036
/* Called when a Mandos server is found */
654
1037
__attribute__((nonnull, warn_unused_result))
655
1038
static int start_mandos_communication(const char *ip, in_port_t port,
742
1126
goto mandos_end;
745
memset(&to, 0, sizeof(to));
746
1129
if(af == AF_INET6){
747
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
748
ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
1130
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1131
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1132
ret = inet_pton(af, ip, &to6->sin6_addr);
749
1133
} else { /* IPv4 */
750
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
751
ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
1134
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1135
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1136
ret = inet_pton(af, ip, &to4->sin_addr);
824
1209
goto mandos_end;
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");
1214
ret = connect(tcp_sd, (struct sockaddr *)&to,
1215
sizeof(struct sockaddr_in6));
1217
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
1218
sizeof(struct sockaddr_in));
1221
if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1222
and if_index != AVAHI_IF_UNSPEC
1223
and connect_to == NULL
1224
and not route_added and
1225
((af == AF_INET6 and not
1226
IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
1228
or (af == AF_INET and
1229
/* Not a a IPv4LL address */
1230
(ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
1231
& 0xFFFF0000L) != 0xA9FE0000L))){
1232
/* Work around Avahi bug - Avahi does not announce link-local
1233
addresses if it has a global address, so local hosts with
1234
*only* a link-local address (e.g. Mandos clients) cannot
1235
connect to a Mandos server announced by Avahi on a server
1236
host with a global address. Work around this by retrying
1237
with an explicit route added with the server's address.
1239
Avahi bug reference:
1240
http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1241
https://bugs.debian.org/587961
1244
fprintf_plus(stderr, "Mandos server unreachable, trying"
1248
route_added = add_local_route(ip, if_index);
1254
if(errno != ECONNREFUSED or debug){
1256
perror_plus("connect");
848
1269
const char *out = mandos_protocol_version;
1464
/* Set effective uid to 0, return errno */
1465
__attribute__((warn_unused_result))
1466
error_t raise_privileges(void){
1467
error_t old_errno = errno;
1468
error_t ret_errno = 0;
1469
if(seteuid(0) == -1){
1476
/* Set effective and real user ID to 0. Return errno. */
1477
__attribute__((warn_unused_result))
1478
error_t raise_privileges_permanently(void){
1479
error_t old_errno = errno;
1480
error_t ret_errno = raise_privileges();
1485
if(setuid(0) == -1){
1492
/* Set effective user ID to unprivileged saved user ID */
1493
__attribute__((warn_unused_result))
1494
error_t lower_privileges(void){
1495
error_t old_errno = errno;
1496
error_t ret_errno = 0;
1497
if(seteuid(uid) == -1){
1504
/* Lower privileges permanently */
1505
__attribute__((warn_unused_result))
1506
error_t lower_privileges_permanently(void){
1507
error_t old_errno = errno;
1508
error_t ret_errno = 0;
1509
if(setuid(uid) == -1){
1516
1893
__attribute__((nonnull))
1517
1894
void run_network_hooks(const char *mode, const char *interface,
1518
1895
const float delay){
1519
1896
struct dirent **direntries = NULL;
1520
1897
if(hookdir_fd == -1){
1521
hookdir_fd = open(hookdir, O_RDONLY);
1898
hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
1522
1900
if(hookdir_fd == -1){
1523
1901
if(errno == ENOENT){
1535
#if __GLIBC_PREREQ(2, 15)
1536
1912
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1537
1913
runnable_hook, alphasort);
1538
#else /* not __GLIBC_PREREQ(2, 15) */
1539
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1541
#endif /* not __GLIBC_PREREQ(2, 15) */
1542
#else /* not __GLIBC__ */
1543
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1545
#endif /* not __GLIBC__ */
1546
1914
if(numhooks == -1){
1547
1915
perror_plus("scandir");
1550
1918
struct dirent *direntry;
1552
int devnull = open("/dev/null", O_RDONLY);
1920
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1922
perror_plus("open(\"/dev/null\", O_RDONLY)");
1553
1925
for(int i = 0; i < numhooks; i++){
1554
1926
direntry = direntries[i];
2610
3030
free(interfaces_to_take_down);
2611
3031
free(interfaces_hooks);
3033
void clean_dir_at(int base, const char * const dirname,
3035
struct dirent **direntries = NULL;
3037
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3043
perror_plus("open");
3045
int numentries = scandirat(dir_fd, ".", &direntries,
3046
notdotentries, alphasort);
3047
if(numentries >= 0){
3048
for(int i = 0; i < numentries; i++){
3050
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3051
dirname, direntries[i]->d_name);
3053
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3055
if(errno == EISDIR){
3056
dret = unlinkat(dir_fd, direntries[i]->d_name,
3059
if((dret == -1) and (errno == ENOTEMPTY)
3060
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3061
== 0) and (level == 0)){
3062
/* Recurse only in this special case */
3063
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3067
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3068
direntries[i]->d_name, strerror(errno));
3071
free(direntries[i]);
3074
/* need to clean even if 0 because man page doesn't specify */
3076
if(numentries == -1){
3077
perror_plus("scandirat");
3079
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3080
if(dret == -1 and errno != ENOENT){
3081
perror_plus("rmdir");
3084
perror_plus("scandirat");
2613
3089
/* Removes the GPGME temp directory and all files inside */
2614
3090
if(tempdir != NULL){
2615
struct dirent **direntries = NULL;
2616
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
2618
if(tempdir_fd == -1){
2619
perror_plus("open");
2622
#if __GLIBC_PREREQ(2, 15)
2623
int numentries = scandirat(tempdir_fd, ".", &direntries,
2624
notdotentries, alphasort);
2625
#else /* not __GLIBC_PREREQ(2, 15) */
2626
int numentries = scandir(tempdir, &direntries, notdotentries,
2628
#endif /* not __GLIBC_PREREQ(2, 15) */
2629
#else /* not __GLIBC__ */
2630
int numentries = scandir(tempdir, &direntries, notdotentries,
2632
#endif /* not __GLIBC__ */
2633
if(numentries >= 0){
2634
for(int i = 0; i < numentries; i++){
2635
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2637
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2638
" \"%s\", 0): %s\n", tempdir,
2639
direntries[i]->d_name, strerror(errno));
2643
/* need to clean even if 0 because man page doesn't specify */
2645
if(numentries == -1){
2646
perror_plus("scandir");
2648
ret = rmdir(tempdir);
2649
if(ret == -1 and errno != ENOENT){
2650
perror_plus("rmdir");
2653
TEMP_FAILURE_RETRY(close(tempdir_fd));
3091
clean_dir_at(-1, tempdir, 0);