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);
653
818
/* Set effective uid to 0, return errno */
654
819
__attribute__((warn_unused_result))
655
error_t raise_privileges(void){
656
error_t old_errno = errno;
657
error_t ret_errno = 0;
820
int raise_privileges(void){
821
int old_errno = errno;
658
823
if(seteuid(0) == -1){
661
826
errno = old_errno;
665
830
/* Set effective and real user ID to 0. Return errno. */
666
831
__attribute__((warn_unused_result))
667
error_t raise_privileges_permanently(void){
668
error_t old_errno = errno;
669
error_t ret_errno = raise_privileges();
832
int raise_privileges_permanently(void){
833
int old_errno = errno;
834
int ret = raise_privileges();
671
836
errno = old_errno;
674
839
if(setuid(0) == -1){
677
842
errno = old_errno;
681
846
/* Set effective user ID to unprivileged saved user ID */
682
847
__attribute__((warn_unused_result))
683
error_t lower_privileges(void){
684
error_t old_errno = errno;
685
error_t ret_errno = 0;
848
int lower_privileges(void){
849
int old_errno = errno;
686
851
if(seteuid(uid) == -1){
689
854
errno = old_errno;
693
858
/* Lower privileges permanently */
694
859
__attribute__((warn_unused_result))
695
error_t lower_privileges_permanently(void){
696
error_t old_errno = errno;
697
error_t ret_errno = 0;
860
int lower_privileges_permanently(void){
861
int old_errno = errno;
698
863
if(setuid(uid) == -1){
701
866
errno = old_errno;
705
/* Helper function to add_local_route() and remove_local_route() */
870
/* Helper function to add_local_route() and delete_local_route() */
706
871
__attribute__((nonnull, warn_unused_result))
707
static bool add_remove_local_route(const bool add,
872
static bool add_delete_local_route(const bool add,
708
873
const char *address,
709
874
AvahiIfIndex if_index){
711
876
char helper[] = "mandos-client-iprouteadddel";
712
877
char add_arg[] = "add";
713
char remove_arg[] = "remove";
878
char delete_arg[] = "delete";
879
char debug_flag[] = "--debug";
714
880
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
715
881
if(pluginhelperdir == NULL){
944
if(helperdir_fd == -1){
946
_exit(EX_UNAVAILABLE);
778
948
int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
779
949
helper, O_RDONLY));
780
TEMP_FAILURE_RETRY(close(helperdir_fd));
951
perror_plus("openat");
953
_exit(EX_UNAVAILABLE);
782
957
#pragma GCC diagnostic push
783
958
#pragma GCC diagnostic ignored "-Wcast-qual"
785
960
if(fexecve(helper_fd, (char *const [])
786
{ helper, add ? add_arg : remove_arg, (char *)address,
787
interface, NULL }, environ) == -1){
961
{ helper, add ? add_arg : delete_arg, (char *)address,
962
interface, debug ? debug_flag : NULL, NULL },
789
965
#pragma GCC diagnostic pop
842
1018
__attribute__((nonnull, warn_unused_result))
843
1019
static bool add_local_route(const char *address,
844
1020
AvahiIfIndex if_index){
845
return add_remove_local_route(true, address, if_index);
1022
fprintf_plus(stderr, "Adding route to %s\n", address);
1024
return add_delete_local_route(true, address, if_index);
848
1027
__attribute__((nonnull, warn_unused_result))
849
static bool remove_local_route(const char *address,
1028
static bool delete_local_route(const char *address,
850
1029
AvahiIfIndex if_index){
851
return add_remove_local_route(false, address, if_index);
1031
fprintf_plus(stderr, "Removing route to %s\n", address);
1033
return add_delete_local_route(false, address, if_index);
854
1036
/* Called when a Mandos server is found */
944
1126
goto mandos_end;
947
memset(&to, 0, sizeof(to));
948
1129
if(af == AF_INET6){
949
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
950
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);
951
1133
} else { /* IPv4 */
952
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
953
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);
2818
3030
free(interfaces_to_take_down);
2819
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");
2821
3089
/* Removes the GPGME temp directory and all files inside */
2822
3090
if(tempdir != NULL){
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));
3091
clean_dir_at(-1, tempdir, 0);