9
9
* "browse_callback", and parts of "main".
11
11
* Everything else is
12
* Copyright © 2008-2015 Teddy Hogeborn
13
* Copyright © 2008-2015 Björn Påhlsson
15
* This program is free software: you can redistribute it and/or
16
* modify it under the terms of the GNU General Public License as
17
* published by the Free Software Foundation, either version 3 of the
18
* License, or (at your option) any later version.
20
* This program is distributed in the hope that it will be useful, but
12
* Copyright © 2008-2018 Teddy Hogeborn
13
* Copyright © 2008-2018 Björn Påhlsson
15
* This file is part of Mandos.
17
* Mandos is free software: you can redistribute it and/or modify it
18
* under the terms of the GNU General Public License as published by
19
* the Free Software Foundation, either version 3 of the License, or
20
* (at your option) any later version.
22
* Mandos is distributed in the hope that it will be useful, but
21
23
* WITHOUT ANY WARRANTY; without even the implied warranty of
22
24
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23
25
* General Public License for more details.
25
27
* You should have received a copy of the GNU General Public License
26
* along with this program. If not, see
27
* <http://www.gnu.org/licenses/>.
28
* along with Mandos. If not, see <http://www.gnu.org/licenses/>.
29
30
* Contact the authors at <mandos@recompile.se>.
46
47
#include <stdlib.h> /* free(), EXIT_SUCCESS, srand(),
47
48
strtof(), abort() */
48
49
#include <stdbool.h> /* bool, false, true */
49
#include <string.h> /* memset(), strcmp(), strlen(),
50
strerror(), asprintf(), strcpy() */
50
#include <string.h> /* strcmp(), strlen(), strerror(),
51
asprintf(), strncpy(), strsignal()
51
53
#include <sys/ioctl.h> /* ioctl */
52
54
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
53
55
sockaddr_in6, PF_INET6,
57
59
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
58
60
inet_pton(), connect(),
60
#include <fcntl.h> /* open(), unlinkat() */
62
#include <fcntl.h> /* open(), unlinkat(), AT_REMOVEDIR */
61
63
#include <dirent.h> /* opendir(), struct dirent, readdir()
63
65
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
65
#include <errno.h> /* perror(), errno,
67
#include <errno.h> /* perror(), errno, EINTR, EINVAL,
68
EAI_SYSTEM, ENETUNREACH,
69
EHOSTUNREACH, ECONNREFUSED, EPROTO,
70
EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
66
72
program_invocation_short_name */
67
73
#include <time.h> /* nanosleep(), time(), sleep() */
68
74
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
508
519
fprintf_plus(stderr, "GnuTLS: %s", string);
511
__attribute__((nonnull, warn_unused_result))
522
__attribute__((nonnull(1, 2, 4), warn_unused_result))
512
523
static int init_gnutls_global(const char *pubkeyfilename,
513
524
const char *seckeyfilename,
525
const char *dhparamsfilename,
514
526
mandos_context *mc){
518
531
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
535
/* "Use a log level over 10 to enable all debugging options."
530
536
* - GnuTLS manual
569
574
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));
577
/* If a Diffie-Hellman parameters file was given, try to use it */
578
if(dhparamsfilename != NULL){
579
gnutls_datum_t params = { .data = NULL, .size = 0 };
581
int dhpfile = open(dhparamsfilename, O_RDONLY);
584
dhparamsfilename = NULL;
587
size_t params_capacity = 0;
589
params_capacity = incbuffer((char **)¶ms.data,
591
(size_t)params_capacity);
592
if(params_capacity == 0){
593
perror_plus("incbuffer");
596
dhparamsfilename = NULL;
599
ssize_t bytes_read = read(dhpfile,
600
params.data + params.size,
606
/* check bytes_read for failure */
611
dhparamsfilename = NULL;
614
params.size += (unsigned int)bytes_read;
616
ret = close(dhpfile);
618
perror_plus("close");
620
if(params.data == NULL){
621
dhparamsfilename = NULL;
623
if(dhparamsfilename == NULL){
626
ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms,
627
GNUTLS_X509_FMT_PEM);
628
if(ret != GNUTLS_E_SUCCESS){
629
fprintf_plus(stderr, "Failed to parse DH parameters in file"
630
" \"%s\": %s\n", dhparamsfilename,
631
safer_gnutls_strerror(ret));
632
dhparamsfilename = NULL;
637
if(dhparamsfilename == NULL){
638
if(mc->dh_bits == 0){
639
/* Find out the optimal number of DH bits */
640
/* Try to read the private key file */
641
gnutls_datum_t buffer = { .data = NULL, .size = 0 };
643
int secfile = open(seckeyfilename, O_RDONLY);
648
size_t buffer_capacity = 0;
650
buffer_capacity = incbuffer((char **)&buffer.data,
652
(size_t)buffer_capacity);
653
if(buffer_capacity == 0){
654
perror_plus("incbuffer");
659
ssize_t bytes_read = read(secfile,
660
buffer.data + buffer.size,
666
/* check bytes_read for failure */
673
buffer.size += (unsigned int)bytes_read;
677
/* If successful, use buffer to parse private key */
678
gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
679
if(buffer.data != NULL){
681
gnutls_openpgp_privkey_t privkey = NULL;
682
ret = gnutls_openpgp_privkey_init(&privkey);
683
if(ret != GNUTLS_E_SUCCESS){
684
fprintf_plus(stderr, "Error initializing OpenPGP key"
686
safer_gnutls_strerror(ret));
690
ret = gnutls_openpgp_privkey_import
691
(privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
692
if(ret != GNUTLS_E_SUCCESS){
693
fprintf_plus(stderr, "Error importing OpenPGP key : %s",
694
safer_gnutls_strerror(ret));
700
/* Use private key to suggest an appropriate
702
sec_param = gnutls_openpgp_privkey_sec_param(privkey);
703
gnutls_openpgp_privkey_deinit(privkey);
705
fprintf_plus(stderr, "This OpenPGP key implies using"
706
" a GnuTLS security parameter \"%s\".\n",
707
safe_string(gnutls_sec_param_get_name
713
if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
714
/* Err on the side of caution */
715
sec_param = GNUTLS_SEC_PARAM_ULTRA;
717
fprintf_plus(stderr, "Falling back to security parameter"
719
safe_string(gnutls_sec_param_get_name
724
uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
728
fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
729
" implies %u DH bits; using that.\n",
730
safe_string(gnutls_sec_param_get_name
735
fprintf_plus(stderr, "Failed to get implied number of DH"
736
" bits for security parameter \"%s\"): %s\n",
737
safe_string(gnutls_sec_param_get_name
739
safer_gnutls_strerror(ret));
743
fprintf_plus(stderr, "DH bits explicitly set to %u\n",
746
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
747
if(ret != GNUTLS_E_SUCCESS){
748
fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
749
" bits): %s\n", mc->dh_bits,
750
safer_gnutls_strerror(ret));
579
754
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
653
825
/* Set effective uid to 0, return errno */
654
826
__attribute__((warn_unused_result))
655
error_t raise_privileges(void){
656
error_t old_errno = errno;
657
error_t ret_errno = 0;
827
int raise_privileges(void){
828
int old_errno = errno;
658
830
if(seteuid(0) == -1){
661
833
errno = old_errno;
665
837
/* Set effective and real user ID to 0. Return errno. */
666
838
__attribute__((warn_unused_result))
667
error_t raise_privileges_permanently(void){
668
error_t old_errno = errno;
669
error_t ret_errno = raise_privileges();
839
int raise_privileges_permanently(void){
840
int old_errno = errno;
841
int ret = raise_privileges();
671
843
errno = old_errno;
674
846
if(setuid(0) == -1){
677
849
errno = old_errno;
681
853
/* Set effective user ID to unprivileged saved user ID */
682
854
__attribute__((warn_unused_result))
683
error_t lower_privileges(void){
684
error_t old_errno = errno;
685
error_t ret_errno = 0;
855
int lower_privileges(void){
856
int old_errno = errno;
686
858
if(seteuid(uid) == -1){
689
861
errno = old_errno;
693
865
/* Lower privileges permanently */
694
866
__attribute__((warn_unused_result))
695
error_t lower_privileges_permanently(void){
696
error_t old_errno = errno;
697
error_t ret_errno = 0;
867
int lower_privileges_permanently(void){
868
int old_errno = errno;
698
870
if(setuid(uid) == -1){
701
873
errno = old_errno;
705
877
/* Helper function to add_local_route() and delete_local_route() */
960
1134
goto mandos_end;
963
memset(&to, 0, sizeof(to));
964
1137
if(af == AF_INET6){
965
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
966
ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
1138
struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1139
*to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1140
ret = inet_pton(af, ip, &to6->sin6_addr);
967
1141
} else { /* IPv4 */
968
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
969
ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
1142
struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
1143
*to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
1144
ret = inet_pton(af, ip, &to4->sin_addr);
1745
#if __GLIBC_PREREQ(2, 15)
1940
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1942
perror_plus("open(\"/dev/null\", O_RDONLY)");
1746
1945
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1747
1946
runnable_hook, alphasort);
1748
#else /* not __GLIBC_PREREQ(2, 15) */
1749
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1751
#endif /* not __GLIBC_PREREQ(2, 15) */
1752
#else /* not __GLIBC__ */
1753
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1755
#endif /* not __GLIBC__ */
1756
1947
if(numhooks == -1){
1757
1948
perror_plus("scandir");
1760
1952
struct dirent *direntry;
1762
int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1764
perror_plus("open(\"/dev/null\", O_RDONLY)");
1767
1954
for(int i = 0; i < numhooks; i++){
1768
1955
direntry = direntries[i];
2838
3065
free(interfaces_to_take_down);
2839
3066
free(interfaces_hooks);
3068
void clean_dir_at(int base, const char * const dirname,
3070
struct dirent **direntries = NULL;
3072
int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3078
perror_plus("open");
3081
int numentries = scandirat(dir_fd, ".", &direntries,
3082
notdotentries, alphasort);
3083
if(numentries >= 0){
3084
for(int i = 0; i < numentries; i++){
3086
fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3087
dirname, direntries[i]->d_name);
3089
dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3091
if(errno == EISDIR){
3092
dret = unlinkat(dir_fd, direntries[i]->d_name,
3095
if((dret == -1) and (errno == ENOTEMPTY)
3096
and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3097
== 0) and (level == 0)){
3098
/* Recurse only in this special case */
3099
clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3102
if((dret == -1) and (errno != ENOENT)){
3103
fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3104
direntries[i]->d_name, strerror(errno));
3107
free(direntries[i]);
3110
/* need to clean even if 0 because man page doesn't specify */
3112
dret = unlinkat(base, dirname, AT_REMOVEDIR);
3113
if(dret == -1 and errno != ENOENT){
3114
perror_plus("rmdir");
3117
perror_plus("scandirat");
2841
3122
/* Removes the GPGME temp directory and all files inside */
2842
3123
if(tempdir != NULL){
2843
struct dirent **direntries = NULL;
2844
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2848
if(tempdir_fd == -1){
2849
perror_plus("open");
2852
#if __GLIBC_PREREQ(2, 15)
2853
int numentries = scandirat(tempdir_fd, ".", &direntries,
2854
notdotentries, alphasort);
2855
#else /* not __GLIBC_PREREQ(2, 15) */
2856
int numentries = scandir(tempdir, &direntries, notdotentries,
2858
#endif /* not __GLIBC_PREREQ(2, 15) */
2859
#else /* not __GLIBC__ */
2860
int numentries = scandir(tempdir, &direntries, notdotentries,
2862
#endif /* not __GLIBC__ */
2863
if(numentries >= 0){
2864
for(int i = 0; i < numentries; i++){
2865
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2867
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2868
" \"%s\", 0): %s\n", tempdir,
2869
direntries[i]->d_name, strerror(errno));
2871
free(direntries[i]);
2874
/* need to clean even if 0 because man page doesn't specify */
2876
if(numentries == -1){
2877
perror_plus("scandir");
2879
ret = rmdir(tempdir);
2880
if(ret == -1 and errno != ENOENT){
2881
perror_plus("rmdir");
2884
TEMP_FAILURE_RETRY(close(tempdir_fd));
3124
clean_dir_at(-1, tempdir, 0);