46
46
#include <stdlib.h> /* free(), EXIT_SUCCESS, srand(),
47
47
strtof(), abort() */
48
48
#include <stdbool.h> /* bool, false, true */
49
#include <string.h> /* strcmp(), strlen(), strerror(),
50
asprintf(), strncpy(), strsignal()
49
#include <string.h> /* memset(), strcmp(), strlen(),
50
strerror(), asprintf(), strcpy() */
52
51
#include <sys/ioctl.h> /* ioctl */
53
52
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
54
53
sockaddr_in6, PF_INET6,
58
57
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
59
58
inet_pton(), connect(),
61
#include <fcntl.h> /* open(), unlinkat(), AT_REMOVEDIR */
60
#include <fcntl.h> /* open(), unlinkat() */
62
61
#include <dirent.h> /* opendir(), struct dirent, readdir()
64
63
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
66
#include <errno.h> /* perror(), errno, EINTR, EINVAL,
67
EAI_SYSTEM, ENETUNREACH,
68
EHOSTUNREACH, ECONNREFUSED, EPROTO,
69
EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
65
#include <errno.h> /* perror(), errno,
71
66
program_invocation_short_name */
72
67
#include <time.h> /* nanosleep(), time(), sleep() */
73
68
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
518
508
fprintf_plus(stderr, "GnuTLS: %s", string);
521
__attribute__((nonnull(1, 2, 4), warn_unused_result))
511
__attribute__((nonnull, warn_unused_result))
522
512
static int init_gnutls_global(const char *pubkeyfilename,
523
513
const char *seckeyfilename,
524
const char *dhparamsfilename,
525
514
mandos_context *mc){
530
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));
534
529
/* "Use a log level over 10 to enable all debugging options."
535
530
* - GnuTLS manual
573
569
safer_gnutls_strerror(ret));
576
/* If a Diffie-Hellman parameters file was given, try to use it */
577
if(dhparamsfilename != NULL){
578
gnutls_datum_t params = { .data = NULL, .size = 0 };
580
int dhpfile = open(dhparamsfilename, O_RDONLY);
583
dhparamsfilename = NULL;
586
size_t params_capacity = 0;
588
params_capacity = incbuffer((char **)¶ms.data,
590
(size_t)params_capacity);
591
if(params_capacity == 0){
592
perror_plus("incbuffer");
595
dhparamsfilename = NULL;
598
ssize_t bytes_read = read(dhpfile,
599
params.data + params.size,
605
/* check bytes_read for failure */
610
dhparamsfilename = NULL;
613
params.size += (unsigned int)bytes_read;
615
if(params.data == NULL){
616
dhparamsfilename = NULL;
618
if(dhparamsfilename == NULL){
621
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
622
GNUTLS_X509_FMT_PEM);
623
if(ret != GNUTLS_E_SUCCESS){
624
fprintf_plus(stderr, "Failed to parse DH parameters in file"
625
" \"%s\": %s\n", dhparamsfilename,
626
safer_gnutls_strerror(ret));
627
dhparamsfilename = NULL;
631
if(dhparamsfilename == NULL){
632
if(mc->dh_bits == 0){
633
/* Find out the optimal number of DH bits */
634
/* Try to read the private key file */
635
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
637
int secfile = open(seckeyfilename, O_RDONLY);
642
size_t buffer_capacity = 0;
644
buffer_capacity = incbuffer((char **)&buffer.data,
646
(size_t)buffer_capacity);
647
if(buffer_capacity == 0){
648
perror_plus("incbuffer");
653
ssize_t bytes_read = read(secfile,
654
buffer.data + buffer.size,
660
/* check bytes_read for failure */
667
buffer.size += (unsigned int)bytes_read;
671
/* If successful, use buffer to parse private key */
672
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
673
if(buffer.data != NULL){
675
gnutls_openpgp_privkey_t privkey = NULL;
676
ret = gnutls_openpgp_privkey_init(&privkey);
677
if(ret != GNUTLS_E_SUCCESS){
678
fprintf_plus(stderr, "Error initializing OpenPGP key"
680
safer_gnutls_strerror(ret));
684
ret = gnutls_openpgp_privkey_import
685
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
686
if(ret != GNUTLS_E_SUCCESS){
687
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
688
safer_gnutls_strerror(ret));
694
/* Use private key to suggest an appropriate
696
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
697
gnutls_openpgp_privkey_deinit(privkey);
699
fprintf_plus(stderr, "This OpenPGP key implies using"
700
" a GnuTLS security parameter \"%s\".\n",
701
safe_string(gnutls_sec_param_get_name
707
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
708
/* Err on the side of caution */
709
sec_param = GNUTLS_SEC_PARAM_ULTRA;
711
fprintf_plus(stderr, "Falling back to security parameter"
713
safe_string(gnutls_sec_param_get_name
718
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
722
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
723
" implies %u DH bits; using that.\n",
724
safe_string(gnutls_sec_param_get_name
729
fprintf_plus(stderr, "Failed to get implied number of DH"
730
" bits for security parameter \"%s\"): %s\n",
731
safe_string(gnutls_sec_param_get_name
733
safer_gnutls_strerror(ret));
737
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
740
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
741
if(ret != GNUTLS_E_SUCCESS){
742
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
743
" bits): %s\n", mc->dh_bits,
744
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));
748
579
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
819
653
/* Set effective uid to 0, return errno */
820
654
__attribute__((warn_unused_result))
821
int raise_privileges(void){
822
int old_errno = errno;
655
error_t raise_privileges(void){
656
error_t old_errno = errno;
657
error_t ret_errno = 0;
824
658
if(seteuid(0) == -1){
827
661
errno = old_errno;
831
665
/* Set effective and real user ID to 0. Return errno. */
832
666
__attribute__((warn_unused_result))
833
int raise_privileges_permanently(void){
834
int old_errno = errno;
835
int ret = raise_privileges();
667
error_t raise_privileges_permanently(void){
668
error_t old_errno = errno;
669
error_t ret_errno = raise_privileges();
837
671
errno = old_errno;
840
674
if(setuid(0) == -1){
843
677
errno = old_errno;
847
681
/* Set effective user ID to unprivileged saved user ID */
848
682
__attribute__((warn_unused_result))
849
int lower_privileges(void){
850
int old_errno = errno;
683
error_t lower_privileges(void){
684
error_t old_errno = errno;
685
error_t ret_errno = 0;
852
686
if(seteuid(uid) == -1){
855
689
errno = old_errno;
859
693
/* Lower privileges permanently */
860
694
__attribute__((warn_unused_result))
861
int lower_privileges_permanently(void){
862
int old_errno = errno;
695
error_t lower_privileges_permanently(void){
696
error_t old_errno = errno;
697
error_t ret_errno = 0;
864
698
if(setuid(uid) == -1){
867
701
errno = old_errno;
871
/* Helper function to add_local_route() and delete_local_route() */
705
/* Helper function to add_local_route() and remove_local_route() */
872
706
__attribute__((nonnull, warn_unused_result))
873
static bool add_delete_local_route(const bool add,
707
static bool add_remove_local_route(const bool add,
874
708
const char *address,
875
709
AvahiIfIndex if_index){
877
711
char helper[] = "mandos-client-iprouteadddel";
878
712
char add_arg[] = "add";
879
char delete_arg[] = "delete";
880
char debug_flag[] = "--debug";
713
char remove_arg[] = "remove";
881
714
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
882
715
if(pluginhelperdir == NULL){
945
if(helperdir_fd == -1){
947
_exit(EX_UNAVAILABLE);
949
778
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
950
779
helper, O_RDONLY));
952
perror_plus("openat");
954
_exit(EX_UNAVAILABLE);
780
TEMP_FAILURE_RETRY(close(helperdir_fd));
958
782
#pragma GCC diagnostic push
959
783
#pragma GCC diagnostic ignored "-Wcast-qual"
961
785
if(fexecve(helper_fd, (char *const [])
962
{ helper, add ? add_arg : delete_arg, (char *)address,
963
interface, debug ? debug_flag : NULL, NULL },
786
{ helper, add ? add_arg : remove_arg, (char *)address,
787
interface, NULL }, environ) == -1){
966
789
#pragma GCC diagnostic pop
1019
842
__attribute__((nonnull, warn_unused_result))
1020
843
static bool add_local_route(const char *address,
1021
844
AvahiIfIndex if_index){
1023
fprintf_plus(stderr, "Adding route to %s\n", address);
1025
return add_delete_local_route(true, address, if_index);
845
return add_remove_local_route(true, address, if_index);
1028
848
__attribute__((nonnull, warn_unused_result))
1029
static bool delete_local_route(const char *address,
849
static bool remove_local_route(const char *address,
1030
850
AvahiIfIndex if_index){
1032
fprintf_plus(stderr, "Removing route to %s\n", address);
1034
return add_delete_local_route(false, address, if_index);
851
return add_remove_local_route(false, address, if_index);
1037
854
/* Called when a Mandos server is found */
1127
944
goto mandos_end;
947
memset(&to, 0, sizeof(to));
1130
948
if(af == AF_INET6){
1131
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1132
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1133
ret = inet_pton(af, ip, &to6->sin6_addr);
949
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
950
ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
1134
951
} else { /* IPv4 */
1135
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1136
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1137
ret = inet_pton(af, ip, &to4->sin_addr);
952
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
953
ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
3048
2818
free(interfaces_to_take_down);
3049
2819
free(interfaces_hooks);
3051
void clean_dir_at(int base, const char * const dirname,
3053
struct dirent **direntries = NULL;
3055
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3061
perror_plus("open");
3063
int numentries = scandirat(dir_fd, ".", &direntries,
3064
notdotentries, alphasort);
3065
if(numentries >= 0){
3066
for(int i = 0; i < numentries; i++){
3068
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3069
dirname, direntries[i]->d_name);
3071
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3073
if(errno == EISDIR){
3074
dret = unlinkat(dir_fd, direntries[i]->d_name,
3077
if((dret == -1) and (errno == ENOTEMPTY)
3078
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3079
== 0) and (level == 0)){
3080
/* Recurse only in this special case */
3081
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3085
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3086
direntries[i]->d_name, strerror(errno));
3089
free(direntries[i]);
3092
/* need to clean even if 0 because man page doesn't specify */
3094
if(numentries == -1){
3095
perror_plus("scandirat");
3097
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3098
if(dret == -1 and errno != ENOENT){
3099
perror_plus("rmdir");
3102
perror_plus("scandirat");
3107
2821
/* Removes the GPGME temp directory and all files inside */
3108
2822
if(tempdir != NULL){
3109
clean_dir_at(-1, tempdir, 0);
2823
struct dirent **direntries = NULL;
2824
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2828
if(tempdir_fd == -1){
2829
perror_plus("open");
2832
#if __GLIBC_PREREQ(2, 15)
2833
int numentries = scandirat(tempdir_fd, ".", &direntries,
2834
notdotentries, alphasort);
2835
#else /* not __GLIBC_PREREQ(2, 15) */
2836
int numentries = scandir(tempdir, &direntries, notdotentries,
2838
#endif /* not __GLIBC_PREREQ(2, 15) */
2839
#else /* not __GLIBC__ */
2840
int numentries = scandir(tempdir, &direntries, notdotentries,
2842
#endif /* not __GLIBC__ */
2843
if(numentries >= 0){
2844
for(int i = 0; i < numentries; i++){
2845
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2847
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2848
" \"%s\", 0): %s\n", tempdir,
2849
direntries[i]->d_name, strerror(errno));
2851
free(direntries[i]);
2854
/* need to clean even if 0 because man page doesn't specify */
2856
if(numentries == -1){
2857
perror_plus("scandir");
2859
ret = rmdir(tempdir);
2860
if(ret == -1 and errno != ENOENT){
2861
perror_plus("rmdir");
2864
TEMP_FAILURE_RETRY(close(tempdir_fd));