57
57
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
58
58
inet_pton(), connect(),
60
#include <fcntl.h> /* open(), unlinkat(), AT_REMOVEDIR */
60
#include <fcntl.h> /* open(), unlinkat() */
61
61
#include <dirent.h> /* opendir(), struct dirent, readdir()
63
63
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
65
#include <errno.h> /* perror(), errno, EINTR, EINVAL,
66
EAI_SYSTEM, ENETUNREACH,
67
EHOSTUNREACH, ECONNREFUSED, EPROTO,
68
EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
65
#include <errno.h> /* perror(), errno,
70
66
program_invocation_short_name */
71
67
#include <time.h> /* nanosleep(), time(), sleep() */
72
68
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
517
508
fprintf_plus(stderr, "GnuTLS: %s", string);
520
__attribute__((nonnull(1, 2, 4), warn_unused_result))
511
__attribute__((nonnull, warn_unused_result))
521
512
static int init_gnutls_global(const char *pubkeyfilename,
522
513
const char *seckeyfilename,
523
const char *dhparamsfilename,
524
514
mandos_context *mc){
529
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));
533
529
/* "Use a log level over 10 to enable all debugging options."
534
530
* - GnuTLS manual
572
569
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));
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));
747
579
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
818
653
/* Set effective uid to 0, return errno */
819
654
__attribute__((warn_unused_result))
820
int raise_privileges(void){
821
int old_errno = errno;
655
error_t raise_privileges(void){
656
error_t old_errno = errno;
657
error_t ret_errno = 0;
823
658
if(seteuid(0) == -1){
826
661
errno = old_errno;
830
665
/* Set effective and real user ID to 0. Return errno. */
831
666
__attribute__((warn_unused_result))
832
int raise_privileges_permanently(void){
833
int old_errno = errno;
834
int ret = raise_privileges();
667
error_t raise_privileges_permanently(void){
668
error_t old_errno = errno;
669
error_t ret_errno = raise_privileges();
836
671
errno = old_errno;
839
674
if(setuid(0) == -1){
842
677
errno = old_errno;
846
681
/* Set effective user ID to unprivileged saved user ID */
847
682
__attribute__((warn_unused_result))
848
int lower_privileges(void){
849
int old_errno = errno;
683
error_t lower_privileges(void){
684
error_t old_errno = errno;
685
error_t ret_errno = 0;
851
686
if(seteuid(uid) == -1){
854
689
errno = old_errno;
858
693
/* Lower privileges permanently */
859
694
__attribute__((warn_unused_result))
860
int lower_privileges_permanently(void){
861
int old_errno = errno;
695
error_t lower_privileges_permanently(void){
696
error_t old_errno = errno;
697
error_t ret_errno = 0;
863
698
if(setuid(uid) == -1){
866
701
errno = old_errno;
870
/* Helper function to add_local_route() and delete_local_route() */
705
/* Helper function to add_local_route() and remove_local_route() */
871
706
__attribute__((nonnull, warn_unused_result))
872
static bool add_delete_local_route(const bool add,
707
static bool add_remove_local_route(const bool add,
873
708
const char *address,
874
709
AvahiIfIndex if_index){
876
711
char helper[] = "mandos-client-iprouteadddel";
877
712
char add_arg[] = "add";
878
713
char delete_arg[] = "delete";
879
char debug_flag[] = "--debug";
880
714
char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
881
715
if(pluginhelperdir == NULL){
1018
850
__attribute__((nonnull, warn_unused_result))
1019
851
static bool add_local_route(const char *address,
1020
852
AvahiIfIndex if_index){
1022
fprintf_plus(stderr, "Adding route to %s\n", address);
1024
return add_delete_local_route(true, address, if_index);
853
return add_remove_local_route(true, address, if_index);
1027
856
__attribute__((nonnull, warn_unused_result))
1028
static bool delete_local_route(const char *address,
857
static bool remove_local_route(const char *address,
1029
858
AvahiIfIndex if_index){
1031
fprintf_plus(stderr, "Removing route to %s\n", address);
1033
return add_delete_local_route(false, address, if_index);
859
return add_remove_local_route(false, address, if_index);
1036
862
/* Called when a Mandos server is found */
1126
952
goto mandos_end;
955
memset(&to, 0, sizeof(to));
1129
956
if(af == AF_INET6){
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);
957
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
958
ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
1133
959
} else { /* IPv4 */
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);
960
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
961
ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
3030
2826
free(interfaces_to_take_down);
3031
2827
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");
3089
2829
/* Removes the GPGME temp directory and all files inside */
3090
2830
if(tempdir != NULL){
3091
clean_dir_at(-1, tempdir, 0);
2831
struct dirent **direntries = NULL;
2832
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2836
if(tempdir_fd == -1){
2837
perror_plus("open");
2840
#if __GLIBC_PREREQ(2, 15)
2841
int numentries = scandirat(tempdir_fd, ".", &direntries,
2842
notdotentries, alphasort);
2843
#else /* not __GLIBC_PREREQ(2, 15) */
2844
int numentries = scandir(tempdir, &direntries, notdotentries,
2846
#endif /* not __GLIBC_PREREQ(2, 15) */
2847
#else /* not __GLIBC__ */
2848
int numentries = scandir(tempdir, &direntries, notdotentries,
2850
#endif /* not __GLIBC__ */
2851
if(numentries >= 0){
2852
for(int i = 0; i < numentries; i++){
2853
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2855
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2856
" \"%s\", 0): %s\n", tempdir,
2857
direntries[i]->d_name, strerror(errno));
2859
free(direntries[i]);
2862
/* need to clean even if 0 because man page doesn't specify */
2864
if(numentries == -1){
2865
perror_plus("scandir");
2867
ret = rmdir(tempdir);
2868
if(ret == -1 and errno != ENOENT){
2869
perror_plus("rmdir");
2872
TEMP_FAILURE_RETRY(close(tempdir_fd));