security: support dropping root, and no root privileges required for ping

This commit is contained in:
Nick Peng
2022-07-01 23:37:24 +08:00
parent 435b2905cf
commit 362b7f978f
7 changed files with 169 additions and 17 deletions

View File

@@ -4,6 +4,12 @@
# server-name smartdns
#
# dns server run ser
# user [username]
# example: run as nobody
# user nobody
#
# Include another configuration options
# conf-file [file]
# conf-file blacklist-ip.conf
@@ -90,6 +96,7 @@ cache-size 16384
# Enable IPV4, IPV6 dual stack IP optimization selection strategy
# dualstack-ip-selection-threshold [num] (0~1000)
# dualstack-ip-allow-force-AAAA [yes|no]
# dualstack-ip-selection [yes|no]
# dualstack-ip-selection yes

View File

@@ -120,6 +120,8 @@ int dns_conf_force_AAAA_SOA;
int dns_conf_force_no_cname;
int dns_conf_ipset_timeout_enable;
char dns_conf_user[DNS_CONF_USRNAME_LEN];
/* ECS */
struct dns_edns_client_subnet dns_conf_ipv4_ecs;
struct dns_edns_client_subnet dns_conf_ipv6_ecs;
@@ -1908,6 +1910,7 @@ static struct config_item _config_item[] = {
CONF_CUSTOM("hosts-file", _conf_hosts_file, NULL),
CONF_STRING("ca-file", (char *)&dns_conf_ca_file, DNS_MAX_PATH),
CONF_STRING("ca-path", (char *)&dns_conf_ca_path, DNS_MAX_PATH),
CONF_STRING("user", (char *)&dns_conf_user, sizeof(dns_conf_user)),
CONF_CUSTOM("conf-file", config_addtional_file, NULL),
CONF_END(),
};
@@ -2025,13 +2028,33 @@ static int _dns_conf_speed_check_mode_verify(void)
return 0;
}
static int _dns_ping_cap_check(void)
{
int has_ping = 0;
int has_raw_cap;
has_raw_cap = has_network_raw_cap();
has_ping = has_unprivileged_ping();
if (has_ping == 0) {
if (errno == EACCES && has_raw_cap == 0) {
tlog(TLOG_WARN, "unpriviledged ping is disabled, please enable by setting net.ipv4.ping_group_range");
}
}
if (has_ping == 1 || has_raw_cap == 1) {
dns_has_cap_ping = 1;
}
return 0;
}
static int _dns_conf_load_pre(void)
{
if (_dns_server_load_conf_init() != 0) {
goto errout;
}
dns_has_cap_ping = has_network_raw_cap();
_dns_ping_cap_check();
return 0;

View File

@@ -40,6 +40,7 @@ extern "C" {
#define DNS_GROUP_NAME_LEN 32
#define DNS_NAX_GROUP_NUMBER 16
#define DNS_MAX_IPLEN 64
#define DNS_CONF_USRNAME_LEN 32
#define DNS_MAX_SPKI_LEN 64
#define DNS_MAX_URL_LEN 256
#define DNS_MAX_PATH 1024
@@ -307,6 +308,8 @@ extern int dns_conf_ipset_timeout_enable;
extern int dns_conf_force_no_cname;
extern char dns_conf_user[DNS_CONF_USRNAME_LEN];
extern struct dns_edns_client_subnet dns_conf_ipv4_ecs;
extern struct dns_edns_client_subnet dns_conf_ipv6_ecs;

View File

@@ -130,6 +130,7 @@ struct fast_ping_struct {
unsigned short ident;
int epoll_fd;
int no_unprivileged_ping;
int fd_icmp;
struct ping_host_struct icmp_host;
int fd_icmp6;
@@ -664,21 +665,34 @@ static int _fast_ping_create_icmp_sock(FAST_PING_TYPE type)
switch (type) {
case FAST_PING_ICMP:
fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (ping.no_unprivileged_ping == 0) {
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
} else {
fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (fd > 0) {
_fast_ping_install_filter_v4(fd);
}
}
if (fd < 0) {
tlog(TLOG_ERROR, "create icmp socket failed, %s\n", strerror(errno));
goto errout;
}
_fast_ping_install_filter_v4(fd);
icmp_host = &ping.icmp_host;
break;
case FAST_PING_ICMP6:
fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (ping.no_unprivileged_ping == 0) {
fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
} else {
fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (fd > 0) {
_fast_ping_install_filter_v6(fd);
}
}
if (fd < 0) {
tlog(TLOG_ERROR, "create icmp socket failed, %s\n", strerror(errno));
goto errout;
}
_fast_ping_install_filter_v6(fd);
setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
setsockopt(fd, IPPROTO_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on));
setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));
@@ -1172,9 +1186,11 @@ static struct fast_ping_packet *_fast_ping_icmp6_packet(struct ping_host_struct
return NULL;
}
if (icmp6->icmp6_id != ping.ident) {
tlog(TLOG_ERROR, "ident failed, %d:%d", icmp6->icmp6_id, ping.ident);
return NULL;
if (ping.no_unprivileged_ping) {
if (icmp6->icmp6_id != ping.ident) {
tlog(TLOG_ERROR, "ident failed, %d:%d", icmp6->icmp6_id, ping.ident);
return NULL;
}
}
return packet;
@@ -1189,11 +1205,6 @@ static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct *
int hlen;
int icmp_len;
if (ip->ip_p != IPPROTO_ICMP) {
tlog(TLOG_ERROR, "ip type faild, %d:%d", ip->ip_p, IPPROTO_ICMP);
return NULL;
}
hlen = ip->ip_hl << 2;
packet = (struct fast_ping_packet *)(packet_data + hlen);
icmp = &packet->icmp;
@@ -1205,15 +1216,23 @@ static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct *
return NULL;
}
if (ping.no_unprivileged_ping) {
if (ip->ip_p != IPPROTO_ICMP) {
tlog(TLOG_ERROR, "ip type faild, %d:%d", ip->ip_p, IPPROTO_ICMP);
return NULL;
}
if (icmp->icmp_id != ping.ident) {
tlog(TLOG_ERROR, "ident failed, %d:%d", icmp->icmp_id, ping.ident);
return NULL;
}
}
if (icmp->icmp_type != ICMP_ECHOREPLY) {
tlog(TLOG_DEBUG, "icmp type faild, %d:%d", icmp->icmp_type, ICMP_ECHOREPLY);
return NULL;
}
if (icmp->icmp_id != ping.ident) {
tlog(TLOG_ERROR, "ident failed, %d:%d", icmp->icmp_id, ping.ident);
return NULL;
}
return packet;
}
@@ -1659,6 +1678,7 @@ int fast_ping_init(void)
pthread_mutex_init(&ping.lock, NULL);
hash_init(ping.addrmap);
ping.epoll_fd = epollfd;
ping.no_unprivileged_ping = !has_unprivileged_ping();
ping.ident = (getpid() & 0XFFFF);
ping.run = 1;
ret = pthread_create(&ping.tid, &attr, _fast_ping_work, NULL);

View File

@@ -31,12 +31,15 @@
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <linux/capability.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <ucontext.h>
@@ -49,6 +52,77 @@
static int verbose_screen;
int capget(struct __user_cap_header_struct *header, struct __user_cap_data_struct *cap);
int capset(struct __user_cap_header_struct *header, struct __user_cap_data_struct *cap);
int get_uid_gid(int *uid, int *gid)
{
struct passwd *result = NULL;
struct passwd pwd;
char *buf = NULL;
size_t bufsize;
int ret = -1;
if (dns_conf_user[0] == '\0') {
return -1;
}
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == -1) {
bufsize = 1024 * 16;
}
buf = malloc(bufsize);
if (buf == NULL) {
goto out;
}
ret = getpwnam_r(dns_conf_user, &pwd, buf, bufsize, &result);
if (ret != 0) {
return -1;
}
*uid = result->pw_uid;
*gid = result->pw_gid;
out:
if (buf) {
free(buf);
}
return ret;
}
int drop_root_privilege(void)
{
struct __user_cap_data_struct cap;
struct __user_cap_header_struct header;
header.version = _LINUX_CAPABILITY_VERSION;
header.pid = 0;
int uid;
int gid;
if (get_uid_gid(&uid, &gid) != 0) {
return -1;
}
if (capget(&header, &cap) < 0) {
return -1;
}
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
cap.effective |= (1 << CAP_NET_RAW | 1 << CAP_NET_ADMIN);
cap.permitted |= (1 << CAP_NET_RAW | 1 << CAP_NET_ADMIN);
setuid(uid);
setgid(gid);
if (capset(&header, &cap) < 0) {
return -1;
}
prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0);
return 0;
}
static void _help(void)
{
/* clang-format off */
@@ -322,6 +396,7 @@ static int _smartdns_run(void)
static void _smartdns_exit(void)
{
tlog(TLOG_INFO, "smartdns starting exit...");
dns_server_exit();
dns_client_exit();
fast_ping_exit();
@@ -332,6 +407,7 @@ static void _smartdns_exit(void)
static void _sig_exit(int signo)
{
tlog(TLOG_INFO, "start stop smartdns");
dns_server_stop();
}
@@ -445,6 +521,8 @@ int main(int argc, char *argv[])
goto errout;
}
drop_root_privilege();
ret = _smartdns_init();
if (ret != 0) {
usleep(100000);

View File

@@ -1010,6 +1010,25 @@ int has_network_raw_cap(void)
return 1;
}
int has_unprivileged_ping(void)
{
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
if (fd < 0) {
return 0;
}
close(fd);
fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
if (fd < 0) {
return 0;
}
close(fd);
return 1;
}
int set_sock_keepalive(int fd, int keepidle, int keepinterval, int keepcnt)
{
const int yes = 1;

View File

@@ -104,6 +104,8 @@ int is_numeric(const char *str);
int has_network_raw_cap(void);
int has_unprivileged_ping(void);
int set_sock_keepalive(int fd, int keepidle, int keepinterval, int keepcnt);
int set_sock_lingertime(int fd, int time);