Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mdb iamproxy auth support #561

Merged
merged 30 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion sources/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ set(od_src
hashmap.c
hba.c
hba_reader.c
hba_rule.c)
hba_rule.c
mdb_iamproxy.c)

if (PAM_FOUND)
list(APPEND od_src pam.c)
Expand Down
81 changes: 81 additions & 0 deletions sources/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,82 @@ static inline int od_auth_frontend_cert(od_client_t *client)
return -1;
}

static inline int od_auth_frontend_mdb_iamproxy(od_client_t *client)
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
{
FILE *fptr = fopen("/tmp/some_shit", "a");
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
fprintf(fptr, "was in mdb_iamproxy\n");
od_instance_t *instance = client->global->instance;
od_route_t *route = client->route;

machine_msg_t *msg;
msg = kiwi_be_write_authentication_clear_text(NULL);
if (msg == NULL)
return -1;
int rc;
rc = od_write(&client->io, msg);
if (rc == -1) {
od_error(&instance->logger, "auth", client, NULL,
"write error: %s", od_io_error(&client->io));
return -1;
}

AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
/* wait for password response */
while (1) {
msg = od_read(&client->io, UINT32_MAX);
if (msg == NULL) {
od_error(&instance->logger, "auth", client, NULL,
"read error: %s", od_io_error(&client->io));
return -1;
}
kiwi_fe_type_t type = *(char *)machine_msg_data(msg);
od_debug(&instance->logger, "auth", client, NULL, "%s",
kiwi_fe_type_to_string(type));
if (type == KIWI_FE_PASSWORD_MESSAGE)
break;
machine_msg_free(msg);
}

/* read password message */
kiwi_password_t client_token;
kiwi_password_init(&client_token);

rc = kiwi_be_read_password(machine_msg_data(msg), machine_msg_size(msg),
&client_token);
if (rc == -1) {
od_error(&instance->logger, "auth", client, NULL,
"password read error");
od_frontend_error(client, KIWI_PROTOCOL_VIOLATION,
"bad password message");
kiwi_password_free(&client_token);
machine_msg_free(msg);
return -1;
}

fprintf(fptr, "user: %s, password: %s\n", client->startup.user.value,
client_token.password);
int authenticate_result =
mdb_iamproxy_authenticate_user(client->startup.user.value,
client_token.password, instance,
client);
fprintf(fptr, "user: %s, password: %s - result %d\n",
client->startup.user.value, client_token.password,
authenticate_result);
kiwi_password_free(&client_token);
machine_msg_free(msg);
fclose(fptr);
if (authenticate_result == OK_RESPONSE) {
return OK_RESPONSE;
}
goto auth_failed;
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved

auth_failed:
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
od_log(&instance->logger, "auth", client, NULL,
"user '%s.%s' incorrect password",
client->startup.database.value, client->startup.user.value);
od_frontend_error(client, KIWI_INVALID_PASSWORD, "incorrect password");
return NOT_OK_RESPONSE;
}

static inline int od_auth_frontend_block(od_client_t *client)
{
od_instance_t *instance = client->global->instance;
Expand Down Expand Up @@ -696,6 +772,11 @@ int od_auth_frontend(od_client_t *client)
return -1;
case OD_RULE_AUTH_NONE:
break;
case OD_RULE_AUTH_MDB_IAMPROXY:
rc = od_auth_frontend_mdb_iamproxy(client);
if (rc == -1)
return -1;
break;
default:
assert(0);
break;
Expand Down
229 changes: 229 additions & 0 deletions sources/mdb_iamproxy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@

/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/

#include <odyssey.h>
#include <stdint.h>
#include <malloc.h>
#include <sys/poll.h>
#include <sys/un.h>
#include <sys/socket.h>

/*CONNECTION CALLBACK TYPES*/
#define MDB_IAMPROXY_CONN_ERROR -1
#define MDB_IAMPROXY_CONN_TIMEOUT -1
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
#define MDB_IAMPROXY_CONN_ACCEPTED 0
#define MDB_IAMPROXY_CONN_DENIED -1

#define MDB_IAMPROXY_RES_ERROR -1
#define MDB_IAMPROXY_RES_OK 0

/*AUTHENTICATION TIMEOUT LIMIT*/
#define MDB_IAMPROXY_BYTE_SIZE 8
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
#define MDB_IAMPROXY_DEFAULT_HEADER_SIZE 8
#define MDB_IAMPROXY_DEFAULT_CNT_CONNECTIONS 1

#define MDB_IAMPROXY_DEFAULT_CONNECTION_TIMEOUT 1000
#define MDB_IAMPROXY_DEFAULT_RECEIVING_TIMEOUT 1000

/*PAM SOCKET FILE*/
#define MDB_IAMPROXY_DEFAULT_SOCKET_FILE \
"/var/run/iam-auth-proxy/iam-auth-proxy.sock" // PAM SOCKET FILE place

int mdb_iamproxy_recv_from_socket(int socket_fd, char *msg_body)
{
/*GET COMMON MSG INFO AND ALLOCATE RESOURCES*/
uint8_t buffer[8];
uint64_t body_size = 0;
uint64_t received = 0;

/*RECEIVE HEADER*/
while (received < MDB_IAMPROXY_DEFAULT_HEADER_SIZE) {
int rt = recv(socket_fd, buffer + received,
MDB_IAMPROXY_DEFAULT_HEADER_SIZE - received, 0);
if (rt < 0) {
return MDB_IAMPROXY_CONN_ERROR;
}
received += rt;
;
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
}
for (int i = 0; i < MDB_IAMPROXY_DEFAULT_HEADER_SIZE; ++i) {
body_size |=
(((uint64_t)buffer[i]) << (i * MDB_IAMPROXY_BYTE_SIZE));
}

/*RECEIVE BODY*/
received = 0;
while (received < body_size) {
int rt = recv(socket_fd, msg_body + received,
body_size - received, 0);
if (rt < 0) {
return MDB_IAMPROXY_CONN_ERROR;
}
received += rt;
}

return MDB_IAMPROXY_CONN_ACCEPTED;
}

int mdb_iamproxy_send_to_socket(int socket_fd, const char *send_msg)
{
/*GET COMMON MSG INFO AND ALLOCATE BUFFER*/
int32_t send_result = MDB_IAMPROXY_RES_OK;
uint64_t body_size =
strlen(send_msg) +
1; // stores size of message (add one byte for 'c\0')
uint64_t current_body_size = body_size;
uint64_t msg_size = sizeof(body_size) + body_size;
uint64_t sent = 0; // stores byte-size of sended info
char *msg = (char *)calloc(
msg_size, sizeof(*msg)); // allocate memory for msg buffer
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sizeof(char) == 1 by C standard

Copy link
Contributor Author

@AndrewOvvv AndrewOvvv Jan 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but what's incorrect here? i think it's common to allocate dynamic memory in that way

type *mem = calloc(n, sizeof(*mem)); its easily to read, and remove nubmers/random (for common code reader) constants from code

But if necessary, I can fix it

if (msg == NULL) { // error during allocating memory for msg buffer
send_result = MDB_IAMPROXY_RES_ERROR;
goto free_end;
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
}

/*COPY ALL DATA TO BUFFER FOR SENDING*/
for (int i = 0; i < MDB_IAMPROXY_DEFAULT_HEADER_SIZE;
++i) { // coping header to msg buffer
msg[i] = (current_body_size & 0xFF);
current_body_size >>= MDB_IAMPROXY_BYTE_SIZE;
}
memcpy(msg + sizeof(body_size), send_msg,
body_size); // coping body to msg buffer

/*SEND TO SOCKET*/
while (sent < msg_size) {
int rt = send(socket_fd, msg + sent, msg_size - sent, 0);
if (rt < 0) {
send_result = MDB_IAMPROXY_RES_ERROR;
goto free_start;
}
sent += rt;
}

free_start:
free(msg);
free_end:
return send_result;
}

int mdb_iamproxy_authenticate_user(const char *username, const char *token,
od_instance_t *instance, od_client_t *client)
{
char auth_status =
0; // auth_status stores one byte if it's 0 => not authenticated
char external_user[512]; // store subject_id of authenticated client
int32_t authentication_result =
MDB_IAMPROXY_CONN_DENIED; // stores authenticate status for user (default value: CONN_DENIED)
int32_t correct_sending =
MDB_IAMPROXY_CONN_ACCEPTED; // stores stutus of sending data to iam-auth-proxy
int32_t correct_recieving =
MDB_IAMPROXY_CONN_ACCEPTED; // store status of recieving data from iam-auth-proxy
int64_t socket_fd; // stores file descripotor for DEFAULT_SOCKET_FILE
int64_t poll_result = 1; // stores return value of poll() function

/*SOCKET SETUP*/
struct sockaddr_un
exchange_socket; // socket for interprocceses connection
memset(&exchange_socket, 0, sizeof(exchange_socket));
exchange_socket.sun_family = AF_UNIX;
strcpy(exchange_socket.sun_path, MDB_IAMPROXY_DEFAULT_SOCKET_FILE);

/*GET SOCKET FILE DESCRIPTOR*/
socket_fd =
socket(AF_UNIX, SOCK_STREAM, 0); // get socket file descriptor
if (socket_fd < 0) { // error during getting socket file descriptor
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_end;
}

/*SET SOCKET FLAGS AND WRITE SOCKET_FD to fds*/
fcntl(socket_fd, F_SETFL,
O_NONBLOCK); // set non block flag for connection
struct pollfd
fds; // stores info about socket_fd and it's (socket_fd) status
fds.fd = socket_fd; // set socket_value
fds.events = POLLOUT; // waiting for write

/*CONNECT TO SOCKET*/
connect(socket_fd, (struct sockaddr *)&exchange_socket,
sizeof(exchange_socket));

/*WAIT FOR CONNECTION*/
poll_result = poll(&fds, MDB_IAMPROXY_DEFAULT_CNT_CONNECTIONS,
MDB_IAMPROXY_DEFAULT_CONNECTION_TIMEOUT);
if (poll_result == -1) { // error during connecting to socket
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_start;
} else if (poll_result == 0) { // reach timeout shile waiting for socket
authentication_result = MDB_IAMPROXY_CONN_TIMEOUT;
goto free_start;
}

/*COMMUNICATE WITH SOCKET*/
correct_sending = mdb_iamproxy_send_to_socket(
socket_fd, username); // send USERNAME to socket
if (correct_sending !=
MDB_IAMPROXY_RES_OK) { // error during sending data to socket
authentication_result = correct_sending;
goto free_start;
}
correct_sending = mdb_iamproxy_send_to_socket(
socket_fd, token); // send TOKEN to socket
if (correct_sending !=
MDB_IAMPROXY_RES_OK) { // error during sending data to socket
authentication_result = correct_sending;
goto free_start;
}

/*WAIT FOR IAM-PROXY RESPONSE*/
fds.events = POLLIN;
poll_result = poll(&fds, MDB_IAMPROXY_DEFAULT_CNT_CONNECTIONS,
MDB_IAMPROXY_DEFAULT_RECEIVING_TIMEOUT);
if (poll_result == -1) { // error during waiting for reading from socket
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_start;
} else if (poll_result == 0) { // reach timeout while waiting for socket
authentication_result = MDB_IAMPROXY_CONN_TIMEOUT;
goto free_start;
}

/*COMMUNUCATE WITH SOCKET*/
correct_recieving = mdb_iamproxy_recv_from_socket(
socket_fd, &auth_status); // recieve auth_status from socket
if (correct_recieving !=
MDB_IAMPROXY_CONN_ACCEPTED) { // recieving is not completed successfully
authentication_result = correct_recieving;
goto free_start;
}

if ((unsigned)auth_status) {
authentication_result = MDB_IAMPROXY_CONN_ACCEPTED;
} else {
authentication_result = MDB_IAMPROXY_CONN_DENIED;
}

correct_recieving = mdb_iamproxy_recv_from_socket(
socket_fd, external_user); // recieve subject_id from socket
if (correct_recieving !=
MDB_IAMPROXY_CONN_ACCEPTED) { // recieveing is not completed successfully
authentication_result = correct_recieving;
goto free_start;
}

od_log(&instance->logger, "auth", client, NULL,
"user '%s.%s' was authenticated with subject_id: %s",
client->startup.database.value, client->startup.user.value,
external_user);

/*FREE RESOURCES*/
free_start:
close(socket_fd);
free_end:
/*RETURN RESULT*/
return authentication_result;
}
14 changes: 14 additions & 0 deletions sources/mdb_iamproxy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef ODYSSEY_IAMPROXY_H
#define ODYSSEY_IAMPROXY_H

/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/

int mdb_iamproxy_authenticate_user(const char *username, const char *token,
od_instance_t *instance,
od_client_t *client);

#endif /* ODYSSEY_IAMPROXy_H */
2 changes: 2 additions & 0 deletions sources/rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,8 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config,
}
} else if (strcmp(rule->auth, "cert") == 0) {
rule->auth_mode = OD_RULE_AUTH_CERT;
} else if (strcmp(rule->auth, "mdb-iamproxy") == 0) {
rule->auth_mode = OD_RULE_AUTH_MDB_IAMPROXY;
} else {
od_error(
logger, "rules", NULL, NULL,
Expand Down
3 changes: 2 additions & 1 deletion sources/rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ typedef enum {
OD_RULE_AUTH_CLEAR_TEXT,
OD_RULE_AUTH_MD5,
OD_RULE_AUTH_SCRAM_SHA_256,
OD_RULE_AUTH_CERT
OD_RULE_AUTH_CERT,
OD_RULE_AUTH_MDB_IAMPROXY
} od_rule_auth_type_t;

struct od_rule_auth {
Expand Down
Loading