/* * odissey. * * PostgreSQL connection pooler and request router. */ #include #include #include #include #include #include #include #include "od_macro.h" #include "od_list.h" #include "od_pid.h" #include "od_syslog.h" #include "od_log.h" #include "od_scheme.h" #include "od_lex.h" #include "od_config.h" #include "od_stat.h" #include "od_server.h" #include "od_server_pool.h" #include "od_client.h" #include "od_client_list.h" #include "od_client_pool.h" #include "od_route_id.h" #include "od_route.h" #include "od_route_pool.h" #include "od.h" #include "od_io.h" #include "od_pooler.h" #include "od_fe.h" #include "od_auth.h" static inline int od_authfe_cleartext(od_client_t *client) { od_pooler_t *pooler = client->pooler; /* AuthenticationCleartextPassword */ so_stream_t *stream = &client->stream; so_stream_reset(stream); int rc; rc = so_bewrite_authentication_clear_text(stream); if (rc == -1) return -1; rc = od_write(client->io, stream); if (rc == -1) { od_error(&pooler->od->log, client->io, "C (auth): write error: %s", machine_error(client->io)); return -1; } /* wait for password response */ while (1) { so_stream_reset(stream); rc = od_read(client->io, stream, INT_MAX); if (rc == -1) { od_error(&pooler->od->log, client->io, "C (auth): read error: %s", machine_error(client->io)); return -1; } uint8_t type = *stream->s; od_debug(&pooler->od->log, client->io, "C (auth): %c", *stream->s); /* PasswordMessage */ if (type == 'p') break; } /* read password message */ so_password_t client_token; so_password_init(&client_token); rc = so_beread_password(&client_token, stream->s, so_stream_used(stream)); if (rc == -1) { od_error(&pooler->od->log, client->io, "C (auth): password read error"); so_password_free(&client_token); return -1; } /* set user password */ so_password_t client_password = { .password_len = client->scheme->password_len + 1, .password = client->scheme->password, }; /* authenticate */ int check = so_password_compare(&client_password, &client_token); so_password_free(&client_token); if (! check) { od_log(&pooler->od->log, client->io, "C (auth): user '%s' incorrect password", client->startup.user); return -1; } return 0; } static inline int od_authfe_md5(od_client_t *client) { od_pooler_t *pooler = client->pooler; /* generate salt */ uint32_t salt = so_password_salt(&client->key); /* AuthenticationMD5Password */ so_stream_t *stream = &client->stream; so_stream_reset(stream); int rc; rc = so_bewrite_authentication_md5(stream, (uint8_t*)&salt); if (rc == -1) return -1; rc = od_write(client->io, stream); if (rc == -1) { od_error(&pooler->od->log, client->io, "C (auth): write error: %s", machine_error(client->io)); return -1; } /* wait for password response */ while (1) { int rc; so_stream_reset(stream); rc = od_read(client->io, stream, INT_MAX); if (rc == -1) { od_error(&pooler->od->log, client->io, "C (auth): read error: %s", machine_error(client->io)); return -1; } uint8_t type = *stream->s; od_debug(&pooler->od->log, client->io, "C (auth): %c", *stream->s); /* PasswordMessage */ if (type == 'p') break; } /* read password message */ so_password_t client_token; so_password_init(&client_token); rc = so_beread_password(&client_token, stream->s, so_stream_used(stream)); if (rc == -1) { od_error(&pooler->od->log, client->io, "C (auth): password read error"); so_password_free(&client_token); return -1; } /* set user password */ so_password_t client_password; so_password_init(&client_password); rc = so_password_md5(&client_password, so_parameter_value(client->startup.user), client->startup.user->value_len - 1, client->scheme->password, client->scheme->password_len, (uint8_t*)&salt); if (rc == -1) { od_error(&pooler->od->log, NULL, "memory allocation error"); so_password_free(&client_password); so_password_free(&client_token); return -1; } /* authenticate */ int check = so_password_compare(&client_password, &client_token); so_password_free(&client_password); so_password_free(&client_token); if (! check) { od_log(&pooler->od->log, client->io, "C (auth): user '%s' incorrect password", client->startup.user); return -1; } return 0; } int od_authfe(od_client_t *client) { od_pooler_t *pooler = client->pooler; /* match user scheme */ od_schemeuser_t *user_scheme = od_schemeuser_match(&pooler->od->scheme, so_parameter_value(client->startup.user)); if (user_scheme == NULL) { /* try to use default user */ user_scheme = pooler->od->scheme.users_default; if (user_scheme == NULL) { od_error(&pooler->od->log, client->io, "C (auth): user '%s' not found", so_parameter_value(client->startup.user)); return -1; } } client->scheme = user_scheme; /* is user access denied */ if (user_scheme->is_deny) { od_log(&pooler->od->log, client->io, "C (auth): user '%s' access denied", so_parameter_value(client->startup.user)); return -1; } /* authentication mode */ int rc; switch (user_scheme->auth_mode) { case OD_ACLEAR_TEXT: rc = od_authfe_cleartext(client); if (rc == -1) return -1; break; case OD_AMD5: rc = od_authfe_md5(client); if (rc == -1) return -1; break; case OD_ANONE: break; default: assert(0); break; } /* pass */ so_stream_t *stream = &client->stream; so_stream_reset(stream); rc = so_bewrite_authentication_ok(stream); if (rc == -1) return -1; rc = od_write(client->io, stream); if (rc == -1) { od_error(&pooler->od->log, client->io, "C (auth): write error: %s", machine_error(client->io)); return -1; } return 0; } static inline int od_authbe_cleartext(od_server_t *server) { od_pooler_t *pooler = server->pooler; od_route_t *route = server->route; assert(route != NULL); od_debug(&pooler->od->log, server->io, "S (auth): requested clear-text authentication"); /* validate route scheme */ if (route->scheme->password == NULL) { od_error(&pooler->od->log, server->io, "S (auth): password required for route '%s'", route->scheme->target); return -1; } /* PasswordMessage */ so_stream_t *stream = &server->stream; so_stream_reset(stream); int rc; rc = so_fewrite_password(stream, route->scheme->password, route->scheme->password_len + 1); if (rc == -1) { od_error(&pooler->od->log, NULL, "memory allocation error"); return -1; } rc = od_write(server->io, stream); if (rc == -1) { od_error(&pooler->od->log, server->io, "S (auth): write error: %s", machine_error(server->io)); return -1; } return 0; } static inline int od_authbe_md5(od_server_t *server, uint8_t salt[4]) { od_pooler_t *pooler = server->pooler; od_route_t *route = server->route; assert(route != NULL); od_debug(&pooler->od->log, server->io, "S (auth): requested md5 authentication"); /* validate route scheme */ if (route->scheme->user == NULL || route->scheme->password == NULL) { od_error(&pooler->od->log, server->io, "S (auth): user and password required for route '%s'", route->scheme->target); return -1; } /* prepare md5 password using server supplied salt */ so_password_t client_password; so_password_init(&client_password); int rc; rc = so_password_md5(&client_password, route->scheme->user, route->scheme->user_len, route->scheme->password, route->scheme->password_len, (uint8_t*)salt); if (rc == -1) { od_error(&pooler->od->log, NULL, "memory allocation error"); so_password_free(&client_password); return -1; } /* PasswordMessage */ so_stream_t *stream = &server->stream; so_stream_reset(stream); rc = so_fewrite_password(stream, client_password.password, client_password.password_len); so_password_free(&client_password); if (rc == -1) { od_error(&pooler->od->log, NULL, "memory allocation error"); return -1; } rc = od_write(server->io, stream); if (rc == -1) { od_error(&pooler->od->log, server->io, "S (auth): write error: %s", machine_error(server->io)); return -1; } return 0; } int od_authbe(od_server_t *server) { od_pooler_t *pooler = server->pooler; so_stream_t *stream = &server->stream; assert(*stream->s == 'R'); uint32_t auth_type; uint8_t salt[4]; int rc; rc = so_feread_auth(&auth_type, salt, stream->s, so_stream_used(stream)); if (rc == -1) { od_error(&pooler->od->log, server->io, "S (auth): failed to parse authentication message"); return -1; } switch (auth_type) { /* AuthenticationOk */ case 0: return 0; /* AuthenticationCleartextPassword */ case 3: rc = od_authbe_cleartext(server); if (rc == -1) return -1; break; /* AuthenticationMD5Password */ case 5: rc = od_authbe_md5(server, salt); if (rc == -1) return -1; break; /* unsupported */ default: od_error(&pooler->od->log, server->io, "S (auth): unuspported authentication method"); return -1; } /* wait for authentication response */ while (1) { int rc; so_stream_reset(stream); rc = od_read(server->io, &server->stream, INT_MAX); if (rc == -1) { od_error(&pooler->od->log, server->io, "S (auth): read error: %s", machine_error(server->io)); return -1; } char type = *server->stream.s; od_debug(&pooler->od->log, server->io, "S (auth): %c", type); switch (type) { case 'R': { rc = so_feread_auth(&auth_type, salt, stream->s, so_stream_used(stream)); if (rc == -1) { od_error(&pooler->od->log, server->io, "S (auth): failed to parse authentication message"); return -1; } if (auth_type != 0) { od_error(&pooler->od->log, server->io, "S (auth): incorrect authentication flow"); return 0; } return 0; } case 'E': od_error(&pooler->od->log, server->io, "S (auth): authentication error"); return -1; } } return 0; }