diff --git a/config-examples/odyssey-dev.conf b/config-examples/odyssey-dev.conf index de98c1f86..a0080d2ed 100644 --- a/config-examples/odyssey-dev.conf +++ b/config-examples/odyssey-dev.conf @@ -111,4 +111,4 @@ locks_dir "/tmp/odyssey" graceful_die_on_errors yes enable_online_restart no -bindwith_reuseport yes +bindwith_reuseport yes \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index 90ee6e5da..43bd6397f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -69,6 +69,7 @@ COPY --from=base /prep_stmts/pkg/pstmts-test /pstmts-test COPY --from=base /config-validation/pkg/config-validation /config-validation COPY ./docker/scram /scram COPY ./docker/hba /hba +COPY ./docker/rule-address /rule-address COPY ./docker/auth_query /auth_query COPY ./docker/ldap /ldap COPY ./docker/lagpolling /lagpolling diff --git a/docker/auth_query/test_auth_query.sh b/docker/auth_query/test_auth_query.sh index c7b7fc102..6e179228d 100755 --- a/docker/auth_query/test_auth_query.sh +++ b/docker/auth_query/test_auth_query.sh @@ -28,5 +28,4 @@ PGPASSWORD=passwd psql -h localhost -p 6432 -U auth_query_user_md5 -c "SELECT 1" exit 1 } - ody-stop diff --git a/docker/bin/setup b/docker/bin/setup index aebb54700..5dfa81268 100755 --- a/docker/bin/setup +++ b/docker/bin/setup @@ -49,7 +49,7 @@ sudo -u postgres /usr/bin/pg_basebackup -D /var/lib/postgresql/14/repl -R -h loc sudo -u postgres /usr/lib/postgresql/14/bin/pg_ctl -D /var/lib/postgresql/14/repl/ -o '-p 5433' start # Create databases -for database_name in db scram_db ldap_db auth_query_db db1 hba_db tsa_db; do +for database_name in db scram_db ldap_db auth_query_db db1 hba_db tsa_db addr_db; do sudo -u postgres createdb $database_name >> "$SETUP_LOG" 2>&1 || { echo "ERROR: 'createdb $database_name' failed, examine the log" cat "$SETUP_LOG" @@ -127,6 +127,14 @@ psql -h localhost -p 5432 -U postgres -c "create user user_allow password 'corr exit 1 } +# Create users +psql -h localhost -p 5432 -U postgres -c "create user user_addr_correct password 'correct_password'; create user user_addr_incorrect password 'correct_password'; create user user_addr_default password 'correct_password'; create user user_addr_empty password 'correct_password'; create user user_addr_hostname_localhost password 'correct_password';" >> $SETUP_LOG 2>&1 || { + echo "ERROR: users creation failed, examine the log" + cat "$SETUP_LOG" + cat "$PG_LOG" + exit 1 +} + for i in `seq 0 9` do # Create tables diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index b1de0363b..fe4004f37 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -6,6 +6,13 @@ cd /test_dir/test && /usr/bin/odyssey_test setup +# odyssey rule-address test +/rule-address/test.sh +if [ $? -eq 1 ] +then + exit 1 +fi + # odyssey target session attrs test /tsa/tsa.sh if [ $? -eq 1 ] @@ -74,4 +81,4 @@ ody-start /ody-integration-test ody-stop -teardown +teardown \ No newline at end of file diff --git a/docker/rule-address/addr.conf b/docker/rule-address/addr.conf new file mode 100644 index 000000000..c57e75bb0 --- /dev/null +++ b/docker/rule-address/addr.conf @@ -0,0 +1,64 @@ +listen { + host "*" + port 6432 +} + +storage "postgres_server" { + type "remote" + host "127.0.0.1" + port 5432 +} + +database "addr_db" { + user "user_addr_correct" "127.0.0.0/24" { + authentication "clear_text" + password "correct_password" + storage "postgres_server" + pool "session" + } + + user "user_addr_incorrect" "255.0.0.0/24" { + authentication "clear_text" + password "correct_password" + storage "postgres_server" + pool "session" + } + + user "user_addr_default" default { + authentication "clear_text" + password "correct_password" + storage "postgres_server" + pool "session" + } + + user "user_addr_empty" { + authentication "clear_text" + password "correct_password" + storage "postgres_server" + pool "session" + } + + user "user_addr_hostname_localhost" "localhost" { + authentication "clear_text" + password "correct_password" + storage "postgres_server" + pool "session" + } +} + +daemonize yes +pid_file "/var/run/odyssey.pid" + +unix_socket_dir "/tmp" +unix_socket_mode "0644" + +locks_dir "/tmp" + +log_format "%p %t %l [%i %s] (%c) %m\n" +log_file "/var/log/odyssey.log" +log_to_stdout no +log_config yes +log_debug yes +log_session yes +log_stats no +log_query yes diff --git a/docker/rule-address/test.sh b/docker/rule-address/test.sh new file mode 100755 index 000000000..165051285 --- /dev/null +++ b/docker/rule-address/test.sh @@ -0,0 +1,79 @@ +#!/bin/bash -x + +set -ex + +/usr/bin/odyssey /rule-address/addr.conf + +PGPASSWORD=correct_password psql -h localhost -p 6432 -U user_addr_correct -c "SELECT 1" addr_db > /dev/null 2>&1 || { + echo "ERROR: failed auth with correct addr, correct password and plain password in config" + + cat /var/log/odyssey.log + + exit 1 +} + +PGPASSWORD=incorrect_password psql -h localhost -p 6432 -U user_addr_correct -c "SELECT 1" addr_db > /dev/null 2>&1 && { + echo "ERROR: successfully auth with correct addr, but incorrect password" + + cat /var/log/odyssey.log + + exit 1 +} + +PGPASSWORD=correct_password psql -h localhost -p 6432 -U user_addr_incorrect -c "SELECT 1" addr_db > /dev/null 2>&1 && { + echo "ERROR: successfully auth with incorrect addr" + + cat /var/log/odyssey.log + + exit 1 +} + +PGPASSWORD=correct_password psql -h localhost -p 6432 -U user_addr_default -c "SELECT 1" addr_db > /dev/null 2>&1 || { + echo "ERROR: failed auth with correct addr, correct password and plain password in config" + + cat /var/log/odyssey.log + + exit 1 +} + +PGPASSWORD=incorrect_password psql -h localhost -p 6432 -U user_addr_default -c "SELECT 1" addr_db > /dev/null 2>&1 && { + echo "ERROR: successfully auth with correct addr, but incorrect password" + + cat /var/log/odyssey.log + + exit 1 +} + +PGPASSWORD=correct_password psql -h localhost -p 6432 -U user_addr_empty -c "SELECT 1" addr_db > /dev/null 2>&1 || { + echo "ERROR: failed auth with correct addr, correct password and plain password in config" + + cat /var/log/odyssey.log + + exit 1 +} + +PGPASSWORD=incorrect_password psql -h localhost -p 6432 -U user_addr_empty -c "SELECT 1" addr_db > /dev/null 2>&1 && { + echo "ERROR: successfully auth with correct addr, but incorrect password" + + cat /var/log/odyssey.log + + exit 1 +} + +PGPASSWORD=correct_password psql -h localhost -p 6432 -U user_addr_hostname_localhost -c "SELECT 1" addr_db > /dev/null 2>&1 || { + echo "ERROR: failed auth with correct addr, correct password and plain password in config" + + cat /var/log/odyssey.log + + exit 1 +} + +PGPASSWORD=incorrect_password psql -h localhost -p 6432 -U user_addr_hostname_localhost -c "SELECT 1" addr_db > /dev/null 2>&1 && { + echo "ERROR: successfully auth with correct addr, but incorrect password" + + cat /var/log/odyssey.log + + exit 1 +} + +ody-stop diff --git a/sources/CMakeLists.txt b/sources/CMakeLists.txt index 14137272a..97ac47fd2 100644 --- a/sources/CMakeLists.txt +++ b/sources/CMakeLists.txt @@ -47,6 +47,7 @@ set(od_src storage.c murmurhash.c hashmap.c + address.c hba.c hba_reader.c hba_rule.c diff --git a/sources/address.c b/sources/address.c new file mode 100644 index 000000000..7730ee010 --- /dev/null +++ b/sources/address.c @@ -0,0 +1,242 @@ +#include +#include +#include + +/* +* Odyssey. +* +* Scalable PostgreSQL connection pooler. +*/ + +od_address_range_t od_address_range_create_default() +{ + od_address_range_t address_range = { .string_value = strdup("all"), + .string_value_len = strlen("all"), + .is_default = 1 }; + return address_range; +} + +int od_address_range_copy(od_address_range_t *src, od_address_range_t *dst) +{ + dst->string_value = strndup(src->string_value, src->string_value_len); + dst->string_value_len = src->string_value_len; + dst->addr = src->addr; + dst->mask = src->mask; + dst->is_default = src->is_default; + dst->is_hostname = src->is_hostname; +} + +int od_address_range_read_prefix(od_address_range_t *address_range, + char *prefix) +{ + char *end = NULL; + long len = strtol(prefix, &end, 10); + if (*prefix == '\0' || *end != '\0') { + return -1; + } + if (address_range->addr.ss_family == AF_INET) { + if (len > 32) + return -1; + struct sockaddr_in *addr = + (struct sockaddr_in *)&address_range->mask; + uint32 mask; + if (len > 0) + mask = 0xffffffffUL << (32 - (int)len); + else + mask = 0; + addr->sin_addr.s_addr = od_bswap32(mask); + return 0; + } else if (address_range->addr.ss_family == AF_INET6) { + if (len > 128) + return -1; + struct sockaddr_in6 *addr = + (struct sockaddr_in6 *)&address_range->mask; + int i; + for (i = 0; i < 16; i++) { + if (len <= 0) + addr->sin6_addr.s6_addr[i] = 0; + else if (len >= 8) + addr->sin6_addr.s6_addr[i] = 0xff; + else { + addr->sin6_addr.s6_addr[i] = + (0xff << (8 - (int)len)) & 0xff; + } + len -= 8; + } + return 0; + } + + return -1; +} + +int od_address_read(struct sockaddr_storage *dest, const char *addr) +{ + int rc; + rc = inet_pton(AF_INET, addr, &((struct sockaddr_in *)dest)->sin_addr); + if (rc > 0) { + dest->ss_family = AF_INET; + return 0; + } + if (inet_pton(AF_INET6, addr, + &((struct sockaddr_in6 *)dest)->sin6_addr) > 0) { + dest->ss_family = AF_INET6; + return 0; + } + return -1; +} + +static bool od_address_ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b) +{ + return (a->sin_addr.s_addr == b->sin_addr.s_addr); +} + +static bool od_address_ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b) +{ + int i; + for (i = 0; i < 16; i++) + if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i]) + return false; + return true; +} + +bool od_address_equals(struct sockaddr *firstAddress, + struct sockaddr *secondAddress) +{ + if (firstAddress->sa_family == secondAddress->sa_family) { + if (firstAddress->sa_family == AF_INET) { + if (od_address_ipv4eq( + (struct sockaddr_in *)firstAddress, + (struct sockaddr_in *)secondAddress)) + return true; + } else if (firstAddress->sa_family == AF_INET6) { + if (od_address_ipv6eq( + (struct sockaddr_in6 *)firstAddress, + (struct sockaddr_in6 *)secondAddress)) + return true; + } + } + return false; +} + +bool od_address_range_equals(od_address_range_t *first, + od_address_range_t *second) +{ + if (first->is_hostname == second->is_hostname) + return pg_strcasecmp(first->string_value, + second->string_value) == 0; + + return od_address_equals((struct sockaddr *)&first->addr, + (struct sockaddr *)&second->addr) && + od_address_equals((struct sockaddr *)&first->mask, + (struct sockaddr *)&second->mask); +} + +static bool od_address_hostname_match(const char *pattern, + const char *actual_hostname) +{ + if (pattern[0] == '.') /* suffix match */ + { + size_t plen = strlen(pattern); + size_t hlen = strlen(actual_hostname); + if (hlen < plen) + return false; + return (pg_strcasecmp(pattern, + actual_hostname + (hlen - plen)) == 0); + } else + return (pg_strcasecmp(pattern, actual_hostname) == 0); +} + +/* + * Check to see if a connecting IP matches a given host name. + */ +static bool od_address_check_hostname(struct sockaddr_storage *client_sa, + const char *hostname) +{ + struct addrinfo *gai_result, *gai; + int ret; + bool found; + + char client_hostname[NI_MAXHOST]; + + ret = getnameinfo(client_sa, sizeof(*client_sa), client_hostname, + sizeof(client_hostname), NULL, 0, NI_NAMEREQD); + + if (ret != 0) + return false; + + /* Now see if remote host name matches this pg_hba line */ + if (!od_address_hostname_match(hostname, client_hostname)) + return false; + + /* Lookup IP from host name and check against original IP */ + ret = getaddrinfo(client_hostname, NULL, NULL, &gai_result); + if (ret != 0) + return false; + + found = false; + for (gai = gai_result; gai; gai = gai->ai_next) { + found = od_address_equals(gai->ai_addr, + (struct sockaddr *)client_sa); + if (found) { + break; + } + } + + if (gai_result) + freeaddrinfo(gai_result); + + return found; +} + +bool od_address_validate(od_address_range_t *address_range, + struct sockaddr_storage *sa) +{ + if (address_range->is_hostname) + return od_address_check_hostname(sa, + address_range->string_value); + + if (address_range->addr.ss_family != sa->ss_family) + return false; + + if (sa->ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + struct sockaddr_in *addr = + (struct sockaddr_in *)&address_range->addr; + struct sockaddr_in *mask = + (struct sockaddr_in *)&address_range->mask; + in_addr_t client_addr = sin->sin_addr.s_addr; + in_addr_t client_net = mask->sin_addr.s_addr & client_addr; + return (client_net ^ addr->sin_addr.s_addr) == 0; + } else if (sa->ss_family == AF_INET6) { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa; + struct sockaddr_in6 *addr = + (struct sockaddr_in6 *)&address_range->addr; + struct sockaddr_in6 *mask = + (struct sockaddr_in6 *)&address_range->mask; + for (int i = 0; i < 16; ++i) { + uint8_t client_net_byte = mask->sin6_addr.s6_addr[i] & + sin->sin6_addr.s6_addr[i]; + if (client_net_byte ^ addr->sin6_addr.s6_addr[i]) { + return false; + } + } + return true; + } + + return false; +} + +int od_address_hostname_validate(char *hostname) +{ + regex_t regex; + char *valid_rfc952_hostname_regex = + "^(\\.?(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9]))$"; + int reti = regcomp(®ex, valid_rfc952_hostname_regex, REG_EXTENDED); + if (reti) + return -1; + reti = regexec(®ex, hostname, 0, NULL, 0); + if (reti == 0) + return 0; + else + return 1; +} diff --git a/sources/address.h b/sources/address.h new file mode 100644 index 000000000..a393f74b3 --- /dev/null +++ b/sources/address.h @@ -0,0 +1,37 @@ +#ifndef ODYSSEY_ADDRESS_H +#define ODYSSEY_ADDRESS_H + +/* + * Odyssey. + * + * Scalable PostgreSQL connection pooler. + */ + +typedef struct od_address_range od_address_range_t; + +struct od_address_range { + char *string_value; + int string_value_len; + struct sockaddr_storage addr; + struct sockaddr_storage mask; + int is_hostname; + int is_default; +}; + +od_address_range_t od_address_range_create_default(); + +int od_address_range_copy(od_address_range_t *, od_address_range_t *); + +int od_address_range_read_prefix(od_address_range_t *, char *); + +int od_address_read(struct sockaddr_storage *, const char *); + +bool od_address_equals(struct sockaddr *, struct sockaddr *); + +bool od_address_range_equals(od_address_range_t *, od_address_range_t *); + +bool od_address_validate(od_address_range_t *, struct sockaddr_storage *); + +int od_address_hostname_validate(char *); + +#endif /* ODYSSEY_ADDRESS_H */ \ No newline at end of file diff --git a/sources/auth_query.c b/sources/auth_query.c index 922671dcc..51c914dfe 100644 --- a/sources/auth_query.c +++ b/sources/auth_query.c @@ -178,9 +178,17 @@ int od_auth_query(od_client_t *client, char *peer) kiwi_var_set(&auth_client->startup.database, KIWI_VAR_UNDEF, rule->auth_query_db, strlen(rule->auth_query_db) + 1); + /* set io from client */ + od_io_t auth_client_io = auth_client->io; + auth_client->io = client->io; + /* route */ od_router_status_t status; status = od_router_route(router, auth_client); + + /* return io auth_client back */ + auth_client->io = auth_client_io; + if (status != OD_ROUTER_OK) { od_debug(&instance->logger, "auth_query", auth_client, NULL, "failed to route internal auth query client: %s", diff --git a/sources/client.h b/sources/client.h index 68e49b2b2..7735f68ef 100644 --- a/sources/client.h +++ b/sources/client.h @@ -43,6 +43,8 @@ struct od_client { uint64_t time_setup; uint64_t time_last_active; + bool is_watchdog; + kiwi_be_startup_t startup; kiwi_vars_t vars; kiwi_key_t key; diff --git a/sources/config_reader.c b/sources/config_reader.c index 74d0fe1d4..201541b30 100644 --- a/sources/config_reader.c +++ b/sources/config_reader.c @@ -399,6 +399,38 @@ static bool od_config_reader_is(od_config_reader_t *reader, int id) return true; } +static inline bool od_config_reader_symbol_is(od_config_reader_t *reader, + char symbol) +{ + od_token_t token; + int rc; + rc = od_parser_next(&reader->parser, &token); + od_parser_push(&reader->parser, &token); + if (rc != OD_PARSER_SYMBOL) + return false; + if (token.value.num != (int64_t)symbol) + return false; + return true; +} + +static bool od_config_reader_keyword_is(od_config_reader_t *reader, + od_keyword_t *keyword) +{ + od_token_t token; + int rc; + rc = od_parser_next(&reader->parser, &token); + od_parser_push(&reader->parser, &token); + if (rc != OD_PARSER_KEYWORD) + return false; + od_keyword_t *match; + match = od_keyword_match(od_config_keywords, &token); + if (keyword == NULL) + return false; + if (keyword != match) + return false; + return true; +} + bool od_config_reader_keyword(od_config_reader_t *reader, od_keyword_t *keyword) { od_token_t token; @@ -1718,33 +1750,111 @@ static int od_config_reader_route(od_config_reader_t *reader, char *db_name, } user_name_len = strlen(user_name); + /* address and mask or default */ + char *addr_str = NULL; + char *mask_str = NULL; + + od_address_range_t address_range; + address_range = od_address_range_create_default(); + address_range.string_value = NULL; + address_range.string_value_len = 0; + address_range.is_default = 0; + address_range.is_hostname = 0; + + if (od_config_reader_is(reader, OD_PARSER_STRING)) { + if (!od_config_reader_string(reader, + &address_range.string_value)) + return NOT_OK_RESPONSE; + } else { + bool is_default_keyword; + is_default_keyword = od_config_reader_keyword_is( + reader, &od_config_keywords[OD_LDEFAULT]); + + if (!is_default_keyword && + !od_config_reader_symbol_is(reader, '{')) + return NOT_OK_RESPONSE; + + if (is_default_keyword) + od_config_reader_keyword( + reader, &od_config_keywords[OD_LDEFAULT]); + + address_range = od_address_range_create_default(); + if (address_range.string_value == NULL) + return NOT_OK_RESPONSE; + } + + if (address_range.is_default == 0) { + addr_str = strdup(address_range.string_value); + mask_str = strchr(addr_str, '/'); + if (mask_str) + *mask_str++ = 0; + + if (od_address_read(&address_range.addr, addr_str) == + NOT_OK_RESPONSE) { + int is_valid_hostname = od_address_hostname_validate( + address_range.string_value); + if (is_valid_hostname == -1) { + od_config_reader_error( + reader, NULL, + "could not compile regex"); + return NOT_OK_RESPONSE; + } else if (is_valid_hostname == 0) { + address_range.is_hostname = 1; + } else { + od_config_reader_error(reader, NULL, + "invalid address"); + return NOT_OK_RESPONSE; + } + } else if (mask_str) { + if (od_address_range_read_prefix(&address_range, + mask_str) == -1) { + od_config_reader_error( + reader, NULL, + "invalid network prefix length"); + return NOT_OK_RESPONSE; + } + } else { + od_config_reader_error(reader, NULL, + "expected network mask"); + return NOT_OK_RESPONSE; + } + } + /* ensure rule does not exists and add new rule */ od_rule_t *rule; - rule = od_rules_match(reader->rules, db_name, user_name, db_is_default, - user_is_default, 0); + rule = od_rules_match(reader->rules, db_name, user_name, &address_range, + db_is_default, user_is_default, 0); if (rule) { od_errorf(reader->error, "route '%s.%s': is redefined", db_name, user_name); free(user_name); return NOT_OK_RESPONSE; } + rule = od_rules_add(reader->rules); if (rule == NULL) { free(user_name); return NOT_OK_RESPONSE; } + rule->user_is_default = user_is_default; rule->user_name_len = user_name_len; rule->user_name = strdup(user_name); free(user_name); if (rule->user_name == NULL) return NOT_OK_RESPONSE; + rule->db_is_default = db_is_default; rule->db_name_len = db_name_len; rule->db_name = strdup(db_name); if (rule->db_name == NULL) return NOT_OK_RESPONSE; + address_range.string_value_len = strlen(address_range.string_value); + rule->address_range = address_range; + + free(addr_str); + /* { */ if (!od_config_reader_symbol(reader, '{')) return NOT_OK_RESPONSE; @@ -1764,8 +1874,9 @@ static inline int od_config_reader_watchdog(od_config_reader_t *reader, /* ensure rule does not exists and add new rule */ od_rule_t *rule; + od_address_range_t address_range = od_address_range_create_default(); rule = od_rules_match(reader->rules, watchdog->route_db, - watchdog->route_usr, 0, 0, 1); + watchdog->route_usr, &address_range, 0, 0, 1); if (rule) { od_errorf(reader->error, "route '%s.%s': is redefined", watchdog->route_db, watchdog->route_usr); @@ -1787,6 +1898,8 @@ static inline int od_config_reader_watchdog(od_config_reader_t *reader, if (rule->db_name == NULL) return NOT_OK_RESPONSE; + rule->address_range = address_range; + /* { */ if (!od_config_reader_symbol(reader, '{')) return NOT_OK_RESPONSE; diff --git a/sources/dns.c b/sources/dns.c index b171765d8..76865ea17 100644 --- a/sources/dns.c +++ b/sources/dns.c @@ -11,8 +11,8 @@ #include #include -static int od_getsockaddrname(struct sockaddr *sa, char *buf, int size, - int add_addr, int add_port) +int od_getsockaddrname(struct sockaddr *sa, char *buf, int size, int add_addr, + int add_port) { char addr[128]; if (sa->sa_family == AF_INET) { diff --git a/sources/dns.h b/sources/dns.h index 542647fd6..44956a8a8 100644 --- a/sources/dns.h +++ b/sources/dns.h @@ -7,6 +7,7 @@ * Scalable PostgreSQL connection pooler. */ +int od_getsockaddrname(struct sockaddr *, char *, int, int, int); int od_getaddrname(struct addrinfo *, char *, int, int, int); int od_getpeername(machine_io_t *, char *, int, int, int); int od_getsockname(machine_io_t *, char *, int, int, int); diff --git a/sources/frontend.c b/sources/frontend.c index 72e113dc6..dbf00e64a 100644 --- a/sources/frontend.c +++ b/sources/frontend.c @@ -2110,6 +2110,9 @@ void od_frontend(void *arg) goto cleanup; } + char peer[128]; + od_getpeername(client->io.io, peer, sizeof(peer), 1, 0); + if (instance->config.log_session) { od_log(&instance->logger, "startup", client, NULL, "route '%s.%s' to '%s.%s'", diff --git a/sources/hba.c b/sources/hba.c index ee54b25fb..4badc9f00 100644 --- a/sources/hba.c +++ b/sources/hba.c @@ -39,32 +39,6 @@ void od_hba_reload(od_hba_t *hba, od_hba_rules_t *rules) od_hba_unlock(hba); } -bool od_hba_validate_addr(od_hba_rule_t *rule, struct sockaddr_storage *sa) -{ - struct sockaddr_in *sin = (struct sockaddr_in *)sa; - struct sockaddr_in *rule_addr = (struct sockaddr_in *)&rule->addr; - struct sockaddr_in *rule_mask = (struct sockaddr_in *)&rule->mask; - in_addr_t client_addr = sin->sin_addr.s_addr; - in_addr_t client_net = rule_mask->sin_addr.s_addr & client_addr; - return (client_net ^ rule_addr->sin_addr.s_addr) == 0; -} - -bool od_hba_validate_addr6(od_hba_rule_t *rule, struct sockaddr_storage *sa) -{ - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa; - struct sockaddr_in6 *rule_addr = (struct sockaddr_in6 *)&rule->addr; - struct sockaddr_in6 *rule_mask = (struct sockaddr_in6 *)&rule->mask; - for (int i = 0; i < 16; ++i) { - uint8_t client_net_byte = rule_mask->sin6_addr.s6_addr[i] & - sin->sin6_addr.s6_addr[i]; - if (client_net_byte ^ rule_addr->sin6_addr.s6_addr[i]) { - return false; - } - } - - return true; -} - bool od_hba_validate_name(char *client_name, od_hba_rule_name_t *name, char *client_other_name) { @@ -128,14 +102,9 @@ int od_hba_process(od_client_t *client) } else if (rule->connection_type == OD_CONFIG_HBA_HOSTNOSSL && client->startup.is_ssl_request) { continue; - } else if (sa.ss_family == AF_INET) { - if (rule->addr.ss_family != AF_INET || - !od_hba_validate_addr(rule, &sa)) { - continue; - } - } else if (sa.ss_family == AF_INET6) { - if (rule->addr.ss_family != AF_INET6 || - !od_hba_validate_addr6(rule, &sa)) { + } else if (sa.ss_family == AF_INET || + sa.ss_family == AF_INET6) { + if (!od_address_validate(&rule->address_range, &sa)) { continue; } } diff --git a/sources/hba_reader.c b/sources/hba_reader.c index 3a38b46b5..c9c5583c4 100644 --- a/sources/hba_reader.c +++ b/sources/hba_reader.c @@ -9,6 +9,7 @@ #include #include #include +#include enum { OD_LLOCAL, @@ -174,69 +175,6 @@ static int od_hba_reader_value(od_config_reader_t *reader, void **dest) } } -static int od_hba_reader_address(struct sockaddr_storage *dest, - const char *addr) -{ - int rc; - rc = inet_pton(AF_INET, addr, &((struct sockaddr_in *)dest)->sin_addr); - if (rc > 0) { - dest->ss_family = AF_INET; - return 0; - } - if (inet_pton(AF_INET6, addr, - &((struct sockaddr_in6 *)dest)->sin6_addr) > 0) { - dest->ss_family = AF_INET6; - return 0; - } - return -1; -} - -static inline uint32 od_hba_bswap32(uint32 x) -{ - return ((x << 24) & 0xff000000) | ((x << 8) & 0x00ff0000) | - ((x >> 8) & 0x0000ff00) | ((x >> 24) & 0x000000ff); -} - -int od_hba_reader_prefix(od_hba_rule_t *hba, char *prefix) -{ - char *end = NULL; - long len = strtoul(prefix, &end, 10); - if (*prefix == '\0' || *end != '\0') { - return -1; - } - if (hba->addr.ss_family == AF_INET) { - if (len > 32) - return -1; - struct sockaddr_in *addr = (struct sockaddr_in *)&hba->mask; - long mask; - if (len > 0) - mask = (0xffffffffUL << (32 - (int)len)) & 0xffffffffUL; - else - mask = 0; - addr->sin_addr.s_addr = od_hba_bswap32(mask); - return 0; - } else if (hba->addr.ss_family == AF_INET6) { - if (len > 128) - return -1; - struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&hba->mask; - int i; - for (i = 0; i < 16; i++) { - if (len <= 0) - addr->sin6_addr.s6_addr[i] = 0; - else if (len >= 8) - addr->sin6_addr.s6_addr[i] = 0xff; - else { - addr->sin6_addr.s6_addr[i] = - (0xff << (8 - (int)len)) & 0xff; - } - len -= 8; - } - return 0; - } - - return -1; -} - static int od_hba_reader_name(od_config_reader_t *reader, struct od_hba_rule_name *name, bool is_db) { @@ -347,8 +285,8 @@ int od_hba_reader_parse(od_config_reader_t *reader) if (mask) *mask++ = 0; - if (od_hba_reader_address(&hba->addr, address) == - NOT_OK_RESPONSE) { + if (od_address_read(&hba->address_range.addr, + address) == NOT_OK_RESPONSE) { od_hba_reader_error(reader, "invalid IP address"); goto error; @@ -356,7 +294,8 @@ int od_hba_reader_parse(od_config_reader_t *reader) /* network mask */ if (mask) { - if (od_hba_reader_prefix(hba, mask) == -1) { + if (od_address_range_read_prefix( + &hba->address_range, mask) == -1) { od_hba_reader_error( reader, "invalid network prefix length"); @@ -371,8 +310,8 @@ int od_hba_reader_parse(od_config_reader_t *reader) "expected network mask"); goto error; } - if (od_hba_reader_address(&hba->mask, - address) == -1) { + if (od_address_read(&hba->address_range.mask, + address) == -1) { od_hba_reader_error( reader, "invalid network mask"); goto error; diff --git a/sources/hba_reader.h b/sources/hba_reader.h index 4a3e767b6..37c4a3d74 100644 --- a/sources/hba_reader.h +++ b/sources/hba_reader.h @@ -2,6 +2,5 @@ #define ODYSSEY_HBA_READER_H int od_hba_reader_parse(od_config_reader_t *reader); -int od_hba_reader_prefix(od_hba_rule_t *hba, char *prefix); #endif /* ODYSSEY_HBA_READER_H */ diff --git a/sources/hba_rule.h b/sources/hba_rule.h index 02900227f..acfbe512c 100644 --- a/sources/hba_rule.h +++ b/sources/hba_rule.h @@ -42,8 +42,7 @@ struct od_hba_rule { od_hba_rule_conn_type_t connection_type; od_hba_rule_name_t database; od_hba_rule_name_t user; - struct sockaddr_storage addr; - struct sockaddr_storage mask; + od_address_range_t address_range; od_hba_rule_auth_method_t auth_method; od_list_t link; }; diff --git a/sources/misc.c b/sources/misc.c index 76ac9bfb0..354c83be7 100644 --- a/sources/misc.c +++ b/sources/misc.c @@ -11,6 +11,32 @@ #include #include +int pg_strcasecmp(const char *s1, const char *s2) +{ + for (;;) { + unsigned char ch1 = (unsigned char)*s1++; + unsigned char ch2 = (unsigned char)*s2++; + + if (ch1 != ch2) { + if (ch1 >= 'A' && ch1 <= 'Z') + ch1 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) + ch1 = tolower(ch1); + + if (ch2 >= 'A' && ch2 <= 'Z') + ch2 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) + ch2 = tolower(ch2); + + if (ch1 != ch2) + return (int)ch1 - (int)ch2; + } + if (ch1 == 0) + break; + } + return 0; +} + int pg_strncasecmp(const char *s1, const char *s2, size_t n) { while (n-- > 0) { diff --git a/sources/misc.h b/sources/misc.h index 19bf65324..533024aea 100644 --- a/sources/misc.h +++ b/sources/misc.h @@ -7,6 +7,7 @@ * Scalable PostgreSQL connection pooler. */ +extern int pg_strcasecmp(const char *s1, const char *s2); extern bool parse_bool(const char *value, bool *result); extern bool parse_bool_with_len(const char *value, size_t len, bool *result); diff --git a/sources/odyssey.h b/sources/odyssey.h index 0d8c3c388..99ecaf6d3 100644 --- a/sources/odyssey.h +++ b/sources/odyssey.h @@ -52,6 +52,8 @@ #include "sources/pam.h" #endif +#include "sources/address.h" + #include "sources/storage.h" #include "sources/pool.h" #include "sources/rules.h" diff --git a/sources/route_id.h b/sources/route_id.h index 4e42c21ad..8d9ae24ad 100644 --- a/sources/route_id.h +++ b/sources/route_id.h @@ -43,6 +43,7 @@ static inline int od_route_id_copy(od_route_id_t *dest, od_route_id_t *id) return -1; memcpy(dest->database, id->database, id->database_len); dest->database_len = id->database_len; + dest->user = malloc(id->user_len); if (dest->user == NULL) { free(dest->database); @@ -51,6 +52,7 @@ static inline int od_route_id_copy(od_route_id_t *dest, od_route_id_t *id) } memcpy(dest->user, id->user, id->user_len); dest->user_len = id->user_len; + dest->physical_rep = id->physical_rep; dest->logical_rep = id->logical_rep; return 0; diff --git a/sources/router.c b/sources/router.c index 72c88cb75..2f32dd7ef 100644 --- a/sources/router.c +++ b/sources/router.c @@ -103,7 +103,9 @@ static inline int od_drop_obsolete_rule_connections_cb(od_route_t *route, assert(rule); assert(obsolete_rule); if (strcmp(rule->user_name, obsolete_rule->usr_name) == 0 && - strcmp(rule->db_name, obsolete_rule->db_name) == 0) { + strcmp(rule->db_name, obsolete_rule->db_name) == 0 && + od_address_range_equals(&rule->address_range, + &obsolete_rule->address_range)) { od_route_kill_client_pool(route); return 0; } @@ -138,7 +140,8 @@ int od_router_reconfigure(od_router_t *router, od_rules_t *rules) od_rule_key_t *rk; rk = od_container_of(i, od_rule_key_t, link); od_log(&instance->logger, "reload config", NULL, NULL, - "added rule: %s %s", rk->usr_name, rk->db_name); + "added rule: %s %s %s", rk->usr_name, + rk->db_name, rk->address_range.string_value); } od_list_foreach(&deleted, i) @@ -146,8 +149,8 @@ int od_router_reconfigure(od_router_t *router, od_rules_t *rules) od_rule_key_t *rk; rk = od_container_of(i, od_rule_key_t, link); od_log(&instance->logger, "reload config", NULL, NULL, - "deleted rule: %s %s", rk->usr_name, - rk->db_name); + "deleted rule: %s %s %s", rk->usr_name, + rk->db_name, rk->address_range.string_value); } { @@ -350,14 +353,24 @@ od_router_status_t od_router_route(od_router_t *router, od_client_t *client) /* match latest version of route rule */ od_rule_t *rule = NULL; // initialize rule for (line 365) and flag '-Wmaybe-uninitialized' + + struct sockaddr_storage sa; + int salen; + struct sockaddr *saddr; + int rc; switch (client->type) { case OD_POOL_CLIENT_INTERNAL: rule = od_rules_forward(&router->rules, startup->database.value, - startup->user.value, 1); + startup->user.value, NULL, 1); break; case OD_POOL_CLIENT_EXTERNAL: + salen = sizeof(sa); + saddr = (struct sockaddr *)&sa; + rc = machine_getpeername(client->io.io, saddr, &salen); + if (rc == -1) + return OD_ROUTER_ERROR; rule = od_rules_forward(&router->rules, startup->database.value, - startup->user.value, 0); + startup->user.value, &sa, 0); break; case OD_POOL_CLIENT_UNDEF: // create that case for correct work of '-Wswitch' flag break; @@ -368,8 +381,9 @@ od_router_status_t od_router_route(od_router_t *router, od_client_t *client) return OD_ROUTER_ERROR_NOT_FOUND; } od_debug(&instance->logger, "routing", NULL, NULL, - "matching rule: %s %s with %s routing type to %s client", + "matching rule: %s %s %s with %s routing type to %s client", rule->db_name, rule->user_name, + rule->address_range.string_value, rule->pool->routing_type == NULL ? "client visible" : rule->pool->routing_type, client->type == OD_POOL_CLIENT_INTERNAL ? "internal" : diff --git a/sources/rules.c b/sources/rules.c index e517ca0cd..3e1c53c7b 100644 --- a/sources/rules.c +++ b/sources/rules.c @@ -178,6 +178,8 @@ void od_rules_rule_free(od_rule_t *rule) free(rule->db_name); if (rule->user_name) free(rule->user_name); + if (rule->address_range.string_value) + free(rule->address_range.string_value); if (rule->password) free(rule->password); if (rule->auth) @@ -256,12 +258,17 @@ void od_rules_unref(od_rule_t *rule) } od_rule_t *od_rules_forward(od_rules_t *rules, char *db_name, char *user_name, + struct sockaddr_storage *user_addr, int pool_internal) { - od_rule_t *rule_db_user = NULL; - od_rule_t *rule_db_default = NULL; - od_rule_t *rule_default_user = NULL; - od_rule_t *rule_default_default = NULL; + od_rule_t *rule_db_user_default = NULL; + od_rule_t *rule_db_default_default = NULL; + od_rule_t *rule_default_user_default = NULL; + od_rule_t *rule_default_default_default = NULL; + od_rule_t *rule_db_user_addr = NULL; + od_rule_t *rule_db_default_addr = NULL; + od_rule_t *rule_default_user_addr = NULL; + od_rule_t *rule_default_default_addr = NULL; od_list_t *i; od_list_foreach(&rules->rules, i) @@ -281,33 +288,67 @@ od_rule_t *od_rules_forward(od_rules_t *rules, char *db_name, char *user_name, } } if (rule->db_is_default) { - if (rule->user_is_default) - rule_default_default = rule; - else if (strcmp(rule->user_name, user_name) == 0) - rule_default_user = rule; + if (rule->user_is_default) { + if (rule->address_range.is_default) + rule_default_default_default = rule; + else if (od_address_validate( + &rule->address_range, + user_addr)) + rule_default_default_addr = rule; + } else if (strcmp(rule->user_name, user_name) == 0) { + if (rule->address_range.is_default) + rule_default_user_default = rule; + else if (od_address_validate( + &rule->address_range, + user_addr)) + rule_default_user_addr = rule; + } } else if (strcmp(rule->db_name, db_name) == 0) { - if (rule->user_is_default) - rule_db_default = rule; - else if (strcmp(rule->user_name, user_name) == 0) - rule_db_user = rule; + if (rule->user_is_default) { + if (rule->address_range.is_default) + rule_db_default_default = rule; + else if (od_address_validate( + &rule->address_range, + user_addr)) + rule_db_default_addr = rule; + } else if (strcmp(rule->user_name, user_name) == 0) { + if (rule->address_range.is_default) + rule_db_user_default = rule; + else if (od_address_validate( + &rule->address_range, + user_addr)) + rule_db_user_addr = rule; + } } } - if (rule_db_user) - return rule_db_user; + if (rule_db_user_addr) + return rule_db_user_addr; + + if (rule_db_user_default) + return rule_db_user_default; + + if (rule_db_default_addr) + return rule_db_default_addr; + + if (rule_default_user_addr) + return rule_default_user_addr; - if (rule_db_default) - return rule_db_default; + if (rule_db_default_default) + return rule_db_default_default; - if (rule_default_user) - return rule_default_user; + if (rule_default_user_default) + return rule_default_user_default; - return rule_default_default; + if (rule_default_default_addr) + return rule_default_default_addr; + + return rule_default_default_default; } od_rule_t *od_rules_match(od_rules_t *rules, char *db_name, char *user_name, - int db_is_default, int user_is_default, - int pool_internal) + od_address_range_t *address_range, int db_is_default, + int user_is_default, int pool_internal) { od_list_t *i; od_list_foreach(&rules->rules, i) @@ -327,15 +368,25 @@ od_rule_t *od_rules_match(od_rules_t *rules, char *db_name, char *user_name, } if (strcmp(rule->db_name, db_name) == 0 && strcmp(rule->user_name, user_name) == 0 && + rule->address_range.is_default == + address_range->is_default && rule->db_is_default == db_is_default && - rule->user_is_default == user_is_default) - return rule; + rule->user_is_default == user_is_default) { + if (address_range->is_default == 0) { + if (od_address_range_equals(&rule->address_range, + address_range)) + return rule; + } else { + return rule; + } + } } return NULL; } -static inline od_rule_t *od_rules_match_active(od_rules_t *rules, char *db_name, - char *user_name) +static inline od_rule_t * +od_rules_match_active(od_rules_t *rules, char *db_name, char *user_name, + od_address_range_t *address_range) { od_list_t *i; od_list_foreach(&rules->rules, i) @@ -345,7 +396,9 @@ static inline od_rule_t *od_rules_match_active(od_rules_t *rules, char *db_name, if (rule->obsolete) continue; if (strcmp(rule->db_name, db_name) == 0 && - strcmp(rule->user_name, user_name) == 0) + strcmp(rule->user_name, user_name) == 0 && + od_address_range_equals(&rule->address_range, + address_range)) return rule; } return NULL; @@ -607,7 +660,9 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, rule_new = od_container_of(j, od_rule_t, link); if (strcmp(rule_old->user_name, rule_new->user_name) == 0 && - strcmp(rule_old->db_name, rule_new->db_name) == 0) { + strcmp(rule_old->db_name, rule_new->db_name) == 0 && + od_address_range_equals(&rule_old->address_range, + &rule_new->address_range)) { ok = 1; break; } @@ -623,6 +678,9 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, rk->db_name = strndup(rule_old->db_name, rule_old->db_name_len); + od_address_range_copy(&rule_old->address_range, + &rk->address_range); + od_list_append(deleted, &rk->link); } }; @@ -643,7 +701,9 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, rule_old = od_container_of(j, od_rule_t, link); if (strcmp(rule_old->user_name, rule_new->user_name) == 0 && - strcmp(rule_old->db_name, rule_new->db_name) == 0) { + strcmp(rule_old->db_name, rule_new->db_name) == 0 && + od_address_range_equals(&rule_old->address_range, + &rule_new->address_range)) { ok = 1; break; } @@ -659,6 +719,9 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, rk->db_name = strndup(rule_new->db_name, rule_new->db_name_len); + od_address_range_copy(&rule_new->address_range, + &rk->address_range); + od_list_append(added, &rk->link); } }; @@ -672,7 +735,8 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, /* find and compare origin rule */ od_rule_t *origin; origin = od_rules_match_active(rules, rule->db_name, - rule->user_name); + rule->user_name, + &rule->address_range); if (origin) { if (od_rules_rule_compare(origin, rule)) { origin->mark = 0; @@ -690,6 +754,10 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, origin->user_name_len); rk->db_name = strndup(origin->db_name, origin->db_name_len); + + od_address_range_copy(&origin->address_range, + &rk->address_range); + od_list_append(to_drop, &rk->link); } @@ -733,13 +801,13 @@ __attribute__((hot)) int od_rules_merge(od_rules_t *rules, od_rules_t *src, } int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, - char *user_name) + char *user_name, char *address_range_string) { /* pooling mode */ if (!pool->type) { od_error(logger, "rules", NULL, NULL, - "rule '%s.%s': pooling mode is not set", db_name, - user_name); + "rule '%s.%s %s': pooling mode is not set", db_name, + user_name, address_range_string); return NOT_OK_RESPONSE; } if (strcmp(pool->type, "session") == 0) { @@ -750,8 +818,8 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, pool->pool = OD_RULE_POOL_STATEMENT; } else { od_error(logger, "rules", NULL, NULL, - "rule '%s.%s': unknown pooling mode", db_name, - user_name); + "rule '%s.%s %s': unknown pooling mode", db_name, + user_name, address_range_string); return NOT_OK_RESPONSE; } @@ -759,16 +827,16 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, if (!pool->routing_type) { od_debug( logger, "rules", NULL, NULL, - "rule '%s.%s': pool routing mode is not set, assuming \"client_visible\" by default", - db_name, user_name); + "rule '%s.%s %s': pool routing mode is not set, assuming \"client_visible\" by default", + db_name, user_name, address_range_string); } else if (strcmp(pool->routing_type, "internal") == 0) { pool->routing = OD_RULE_POOL_INTERVAL; } else if (strcmp(pool->routing_type, "client_visible") == 0) { pool->routing = OD_RULE_POOL_CLIENT_VISIBLE; } else { od_error(logger, "rules", NULL, NULL, - "rule '%s.%s': unknown pool routing mode", db_name, - user_name); + "rule '%s.%s %s': unknown pool routing mode", db_name, + user_name, address_range_string); return NOT_OK_RESPONSE; } @@ -777,24 +845,24 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, pool->pool == OD_RULE_POOL_SESSION) { od_error( logger, "rules", NULL, NULL, - "rule '%s.%s': prepared statements support in session pool makes no sence", - db_name, user_name); + "rule '%s.%s %s': prepared statements support in session pool makes no sence", + db_name, user_name, address_range_string); return NOT_OK_RESPONSE; } if (pool->reserve_prepared_statement && pool->discard) { od_error( logger, "rules", NULL, NULL, - "rule '%s.%s': pool discard is forbidden when using prepared statements support", - db_name, user_name); + "rule '%s.%s %s': pool discard is forbidden when using prepared statements support", + db_name, user_name, address_range_string); return NOT_OK_RESPONSE; } if (pool->smart_discard && !pool->reserve_prepared_statement) { od_error( logger, "rules", NULL, NULL, - "rule '%s.%s': pool smart discard is forbidden without using prepared statements support", - db_name, user_name); + "rule '%s.%s %s': pool smart discard is forbidden without using prepared statements support", + db_name, user_name, address_range_string); return NOT_OK_RESPONSE; } @@ -802,8 +870,8 @@ int od_pool_validate(od_logger_t *logger, od_rule_pool_t *pool, char *db_name, if (strcasestr(pool->discard_query, "DEALLOCATE ALL")) { od_error( logger, "rules", NULL, NULL, - "rule '%s.%s': cannot support prepared statements when 'DEALLOCATE ALL' present in discard string", - db_name, user_name); + "rule '%s.%s %s': cannot support prepared statements when 'DEALLOCATE ALL' present in discard string", + db_name, user_name, address_range_string); return NOT_OK_RESPONSE; } } @@ -825,20 +893,23 @@ int od_rules_autogenerate_defaults(od_rules_t *rules, od_logger_t *logger) /* match storage and make a copy of in the user rules */ if (rule->auth_query != NULL && !od_rules_match(rules, rule->db_name, rule->user_name, - rule->db_is_default, rule->user_is_default, - 1)) { + &rule->address_range, rule->db_is_default, + rule->user_is_default, 1)) { need_autogen = true; break; } } - if (!need_autogen || - od_rules_match(rules, "default_db", "default_user", 1, 1, 1)) { + od_address_range_t default_address_range = + od_address_range_create_default(); + + if (!need_autogen || od_rules_match(rules, "default_db", "default_user", + &default_address_range, 1, 1, 1)) { return OK_RESPONSE; } - default_rule = - od_rules_match(rules, "default_db", "default_user", 1, 1, 0); + default_rule = od_rules_match(rules, "default_db", "default_user", + &default_address_range, 1, 1, 0); if (!default_rule) { od_log(logger, "config", NULL, NULL, "skipping default internal rule auto-generation: no default rule provided"); @@ -875,6 +946,8 @@ int od_rules_autogenerate_defaults(od_rules_t *rules, od_logger_t *logger) if (rule->db_name == NULL) return NOT_OK_RESPONSE; + rule->address_range = default_address_range; + /* force several default settings */ #define OD_DEFAULT_INTERNAL_POLL_SZ 0 rule->pool->type = strdup("transaction"); @@ -992,9 +1065,11 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, /* match storage and make a copy of in the user rules */ if (rule->storage_name == NULL) { - od_error(logger, "rules", NULL, NULL, - "rule '%s.%s': no rule storage is specified", - rule->db_name, rule->user_name); + od_error( + logger, "rules", NULL, NULL, + "rule '%s.%s %s': no rule storage is specified", + rule->db_name, rule->user_name, + rule->address_range.string_value); return NOT_OK_RESPONSE; } @@ -1002,8 +1077,9 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, storage = od_rules_storage_match(rules, rule->storage_name); if (storage == NULL) { od_error(logger, "rules", NULL, NULL, - "rule '%s.%s': no rule storage '%s' found", + "rule '%s.%s %s': no rule storage '%s' found", rule->db_name, rule->user_name, + rule->address_range.string_value, rule->storage_name); return NOT_OK_RESPONSE; } @@ -1014,7 +1090,9 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, } if (od_pool_validate(logger, rule->pool, rule->db_name, - rule->user_name) == NOT_OK_RESPONSE) { + rule->user_name, + rule->address_range.string_value) == + NOT_OK_RESPONSE) { return NOT_OK_RESPONSE; } @@ -1022,16 +1100,18 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, if (rule->user_role != OD_RULE_ROLE_UNDEF) { od_error( logger, "rules validate", NULL, NULL, - "rule '%s.%s': role set for non-local storage", - rule->db_name, rule->user_name); + "rule '%s.%s %s': role set for non-local storage", + rule->db_name, rule->user_name, + rule->address_range.string_value); return NOT_OK_RESPONSE; } } else { if (rule->user_role == OD_RULE_ROLE_UNDEF) { od_error( logger, "rules validate", NULL, NULL, - "rule '%s.%s': force stat role for local storage", - rule->db_name, rule->user_name); + "rule '%s.%s %s': force stat role for local storage", + rule->db_name, rule->user_name, + rule->address_range.string_value); rule->user_role = OD_RULE_ROLE_STAT; } } @@ -1040,8 +1120,9 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, if (!rule->auth) { od_error( logger, "rules", NULL, NULL, - "rule '%s.%s': authentication mode is not defined", - rule->db_name, rule->user_name); + "rule '%s.%s %s': authentication mode is not defined", + rule->db_name, rule->user_name, + rule->address_range.string_value); return -1; } if (strcmp(rule->auth, "none") == 0) { @@ -1058,7 +1139,8 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, logger, "rules", NULL, NULL, "auth query and pam service auth method cannot be " "used simultaneously", - rule->db_name, rule->user_name); + rule->db_name, rule->user_name, + rule->address_range.string_value); return -1; } #endif @@ -1074,8 +1156,9 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, ) { od_error(logger, "rules", NULL, NULL, - "rule '%s.%s': password is not set", - rule->db_name, rule->user_name); + "rule '%s.%s %s': password is not set", + rule->db_name, rule->user_name, + rule->address_range.string_value); return -1; } } else if (strcmp(rule->auth, "md5") == 0) { @@ -1083,8 +1166,9 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, if (rule->password == NULL && rule->auth_query == NULL) { od_error(logger, "rules", NULL, NULL, - "rule '%s.%s': password is not set", - rule->db_name, rule->user_name); + "rule '%s.%s %s': password is not set", + rule->db_name, rule->user_name, + rule->address_range.string_value); return -1; } } else if (strcmp(rule->auth, "scram-sha-256") == 0) { @@ -1092,8 +1176,9 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, if (rule->password == NULL && rule->auth_query == NULL) { od_error(logger, "rules", NULL, NULL, - "rule '%s.%s': password is not set", - rule->db_name, rule->user_name); + "rule '%s.%s %s': password is not set", + rule->db_name, rule->user_name, + rule->address_range.string_value); return -1; } } else if (strcmp(rule->auth, "cert") == 0) { @@ -1101,8 +1186,9 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, } else { od_error( logger, "rules", NULL, NULL, - "rule '%s.%s': has unknown authentication mode", - rule->db_name, rule->user_name); + "rule '%s.%s %s': has unknown authentication mode", + rule->db_name, rule->user_name, + rule->address_range.string_value); return -1; } @@ -1111,15 +1197,17 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config, if (rule->auth_query_user == NULL) { od_error( logger, "rules", NULL, NULL, - "rule '%s.%s': auth_query_user is not set", - rule->db_name, rule->user_name); + "rule '%s.%s %s': auth_query_user is not set", + rule->db_name, rule->user_name, + rule->address_range.string_value); return -1; } if (rule->auth_query_db == NULL) { od_error( logger, "rules", NULL, NULL, - "rule '%s.%s': auth_query_db is not set", - rule->db_name, rule->user_name); + "rule '%s.%s %s': auth_query_db is not set", + rule->db_name, rule->user_name, + rule->address_range.string_value); return -1; } } @@ -1221,8 +1309,8 @@ void od_rules_print(od_rules_t *rules, od_logger_t *logger) rule = od_container_of(i, od_rule_t, link); if (rule->obsolete) continue; - od_log(logger, "rules", NULL, NULL, "<%s.%s>", rule->db_name, - rule->user_name); + od_log(logger, "rules", NULL, NULL, "<%s.%s %s>", rule->db_name, + rule->user_name, rule->address_range.string_value); od_log(logger, "rules", NULL, NULL, " authentication %s", rule->auth); if (rule->auth_common_name_default) diff --git a/sources/rules.h b/sources/rules.h index 7095c0c1e..c0b10da73 100644 --- a/sources/rules.h +++ b/sources/rules.h @@ -38,6 +38,7 @@ typedef struct od_rule_key od_rule_key_t; struct od_rule_key { char *usr_name; char *db_name; + od_address_range_t address_range; od_list_t link; }; @@ -67,6 +68,7 @@ struct od_rule { char *user_name; int user_name_len; int user_is_default; + od_address_range_t address_range; od_rule_role_type_t user_role; /* auth */ @@ -171,12 +173,13 @@ void od_rules_ref(od_rule_t *); void od_rules_unref(od_rule_t *); int od_rules_compare(od_rule_t *, od_rule_t *); -od_rule_t *od_rules_forward(od_rules_t *, char *, char *, int); +od_rule_t *od_rules_forward(od_rules_t *, char *, char *, + struct sockaddr_storage *, int); /* search rule with desored characteristik */ od_rule_t *od_rules_match(od_rules_t *rules, char *db_name, char *user_name, - int db_is_default, int user_is_default, - int pool_internal); + od_address_range_t *address_range, int db_is_default, + int user_is_default, int pool_internal); void od_rules_rule_free(od_rule_t *rule); diff --git a/sources/storage.c b/sources/storage.c index 82327cb6b..483de724b 100644 --- a/sources/storage.c +++ b/sources/storage.c @@ -259,6 +259,7 @@ void od_storage_watchdog_watch(void *arg) return; } + watchdog_client->is_watchdog = true; watchdog_client->global = global; watchdog_client->type = OD_POOL_CLIENT_INTERNAL; od_id_generate(&watchdog_client->id, "a"); diff --git a/sources/util.h b/sources/util.h index 1a4f6d76c..5847e4aee 100644 --- a/sources/util.h +++ b/sources/util.h @@ -98,4 +98,10 @@ static inline long od_memtol(char *data, size_t data_size, char **end_ptr, return result; } +static inline uint32 od_bswap32(uint32 x) +{ + return ((x << 24) & 0xff000000) | ((x << 8) & 0x00ff0000) | + ((x >> 8) & 0x0000ff00) | ((x >> 24) & 0x000000ff); +} + #endif /* ODYSSEY_UTIL_H */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index af25bb9ca..4c5b41954 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -65,6 +65,7 @@ set(od_test_src ../sources/util.h ../sources/build.h ../sources/debugprintf.h + ../sources/address.c ../sources/hba.c ../sources/hba_rule.c ../sources/hba_reader.c diff --git a/test/odyssey/test_hba_parse.c b/test/odyssey/test_hba_parse.c index 41102b1d4..31f485f5a 100644 --- a/test/odyssey/test_hba_parse.c +++ b/test/odyssey/test_hba_parse.c @@ -7,13 +7,15 @@ void test_od_hba_reader_prefix(sa_family_t net, char *prefix, char *value) od_hba_rule_t *hba = NULL; char buffer[INET6_ADDRSTRLEN]; hba = od_hba_rule_create(); - hba->addr.ss_family = net; - test(od_hba_reader_prefix(hba, prefix) == 0); + hba->address_range.addr.ss_family = net; + test(od_address_range_read_prefix(&hba->address_range, prefix) == 0); if (net == AF_INET) { - struct sockaddr_in *addr = (struct sockaddr_in *)&hba->mask; + struct sockaddr_in *addr = + (struct sockaddr_in *)&hba->address_range.mask; inet_ntop(net, &addr->sin_addr, buffer, sizeof(buffer)); } else { - struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&hba->mask; + struct sockaddr_in6 *addr = + (struct sockaddr_in6 *)&hba->address_range.mask; inet_ntop(net, &addr->sin6_addr, buffer, sizeof(buffer)); } test(memcmp(value, buffer, strlen(buffer)) == 0);