static bool decide_startup_pool(PgSocket *client, PktHdr *pkt) { const char *username = NULL, *dbname = NULL; const char *key, *val; bool ok; while (1) { ok = mbuf_get_string(&pkt->data, &key); if (!ok || *key == 0) break; ok = mbuf_get_string(&pkt->data, &val); if (!ok) break; if (strcmp(key, "database") == 0) { slog_debug(client, "got var: %s=%s", key, val); dbname = val; } else if (strcmp(key, "user") == 0) { slog_debug(client, "got var: %s=%s", key, val); username = val; } else if (varcache_set(&client->vars, key, val)) { slog_debug(client, "got var: %s=%s", key, val); } else if (strlist_contains(cf_ignore_startup_params, key)) { slog_debug(client, "ignoring startup parameter: %s=%s", key, val); } else { slog_warning(client, "unsupported startup parameter: %s=%s", key, val); disconnect_client(client, true, "Unsupported startup parameter: %s", key); return false; } } if (!username || !username[0]) { disconnect_client(client, true, "No username supplied"); return false; } /* if missing dbname, default to username */ if (!dbname || !dbname[0]) dbname = username; /* check if limit allows, dont limit admin db nb: new incoming conn will be attached to PgSocket, thus get_active_client_count() counts it */ if (get_active_client_count() > cf_max_client_conn) { if (strcmp(dbname, "pgbouncer") != 0) { disconnect_client(client, true, "no more connections allowed (max_client_conn)"); return false; } } /* find pool and log about it */ if (set_pool(client, dbname, username)) { if (cf_log_connections) slog_info(client, "login attempt: db=%s user=%s", dbname, username); return true; } else { if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } }
bool set_pool(PgSocket *client, const char *dbname, const char *username, const char *password, bool takeover) { /* find database */ client->db = find_database(dbname); if (!client->db) { client->db = register_auto_database(dbname); if (!client->db) { disconnect_client(client, true, "No such database: %s", dbname); if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } else { slog_info(client, "registered new auto-database: db = %s", dbname ); } } /* are new connections allowed? */ if (client->db->db_disabled) { disconnect_client(client, true, "database does not allow connections: %s", dbname); return false; } if (client->db->admin) { if (admin_pre_login(client, username)) return finish_set_pool(client, takeover); } /* find user */ if (cf_auth_type == AUTH_ANY) { /* ignore requested user */ if (client->db->forced_user == NULL) { slog_error(client, "auth_type=any requires forced user"); disconnect_client(client, true, "bouncer config error"); return false; } client->auth_user = client->db->forced_user; } else { /* the user clients wants to log in as */ client->auth_user = find_user(username); if (!client->auth_user && client->db->auth_user) { if (takeover) { client->auth_user = add_db_user(client->db, username, password); return finish_set_pool(client, takeover); } start_auth_request(client, username); return false; } if (!client->auth_user) { disconnect_client(client, true, "No such user: %s", username); if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } } return finish_set_pool(client, takeover); }
bool set_pool(PgSocket *client, const char *dbname, const char *username) { PgDatabase *db; PgUser *user; /* find database */ db = find_database(dbname); if (!db) { db = register_auto_database(dbname); if (!db) { disconnect_client(client, true, "No such database: %s", dbname); return false; } else { slog_info(client, "registered new auto-database: db = %s", dbname ); } } /* are new connections allowed? */ if (db->db_disabled) { disconnect_client(client, true, "database does not allow connections: %s", dbname); return false; } /* find user */ if (cf_auth_type == AUTH_ANY) { /* ignore requested user */ user = NULL; if (db->forced_user == NULL) { slog_error(client, "auth_type=any requires forced user"); disconnect_client(client, true, "bouncer config error"); return false; } client->auth_user = db->forced_user; } else { /* the user clients wants to log in as */ user = find_user(username); if (!user) { disconnect_client(client, true, "No such user: %s", username); return false; } client->auth_user = user; } /* pool user may be forced */ if (db->forced_user) user = db->forced_user; client->pool = get_pool(db, user); if (!client->pool) { disconnect_client(client, true, "no memory for pool"); return false; } return check_fast_fail(client); }
static bool finish_set_pool(PgSocket *client, bool takeover) { PgUser *user = client->auth_user; /* pool user may be forced */ if (client->db->forced_user) { user = client->db->forced_user; } client->pool = get_pool(client->db, user); if (!client->pool) { disconnect_client(client, true, "no memory for pool"); return false; } if (cf_log_connections) slog_info(client, "login attempt: db=%s user=%s", client->db->name, client->auth_user->name); if (!check_fast_fail(client)) return false; if (takeover) return true; if (client->pool->db->admin) { if (!admin_post_login(client)) return false; } if (cf_auth_type <= AUTH_TRUST || client->own_user) { if (!finish_client_login(client)) return false; } else { if (!send_client_authreq(client)) { disconnect_client(client, false, "failed to send auth req"); return false; } } return true; }
bool handle_auth_response(PgSocket *client, PktHdr *pkt) { uint16_t columns; uint32_t length; const char *username, *password; PgUser user; PgSocket *server = client->link; switch(pkt->type) { case 'T': /* RowDescription */ if (!mbuf_get_uint16be(&pkt->data, &columns)) { disconnect_server(server, false, "bad packet"); return false; } if (columns != 2u) { disconnect_server(server, false, "expected 1 column from login query, not %hu", columns); return false; } break; case 'D': /* DataRow */ memset(&user, 0, sizeof(user)); if (!mbuf_get_uint16be(&pkt->data, &columns)) { disconnect_server(server, false, "bad packet"); return false; } if (columns != 2u) { disconnect_server(server, false, "expected 1 column from login query, not %hu", columns); return false; } if (!mbuf_get_uint32be(&pkt->data, &length)) { disconnect_server(server, false, "bad packet"); return false; } if (!mbuf_get_chars(&pkt->data, length, &username)) { disconnect_server(server, false, "bad packet"); return false; } if (sizeof(user.name) - 1 < length) length = sizeof(user.name) - 1; memcpy(user.name, username, length); if (!mbuf_get_uint32be(&pkt->data, &length)) { disconnect_server(server, false, "bad packet"); return false; } if (length == (uint32_t)-1) { // NULL - set an md5 password with an impossible value, // so that nothing will ever match password = "******"; length = 3; } else { if (!mbuf_get_chars(&pkt->data, length, &password)) { disconnect_server(server, false, "bad packet"); return false; } } if (sizeof(user.passwd) - 1 < length) length = sizeof(user.passwd) - 1; memcpy(user.passwd, password, length); client->auth_user = add_db_user(client->db, user.name, user.passwd); if (!client->auth_user) { disconnect_server(server, false, "unable to allocate new user for auth"); return false; } break; case 'C': /* CommandComplete */ break; case 'Z': /* ReadyForQuery */ sbuf_prepare_skip(&client->link->sbuf, pkt->len); if (!client->auth_user) { if (cf_log_connections) slog_info(client, "login failed: db=%s", client->db->name); disconnect_client(client, true, "No such user"); } else { slog_noise(client, "auth query complete"); client->link->resetting = true; sbuf_continue(&client->sbuf); } // either sbuf_continue or disconnect_client could disconnect the server // way down in their bowels of other callbacks. so check that, and // return appropriately (similar to reuse_on_release) if (server->state == SV_FREE || server->state == SV_JUSTFREE) return false; return true; default: disconnect_server(server, false, "unexpected response from login query"); return false; } sbuf_prepare_skip(&server->sbuf, pkt->len); return true; }
bool set_pool(PgSocket *client, const char *dbname, const char *username, const char *password, bool takeover) { /* find database */ client->db = find_database(dbname); if (!client->db) { client->db = register_auto_database(dbname); if (!client->db) { disconnect_client(client, true, "no such database: %s", dbname); if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } else { slog_info(client, "registered new auto-database: db=%s", dbname); } } /* are new connections allowed? */ if (client->db->db_disabled) { disconnect_client(client, true, "database does not allow connections: %s", dbname); return false; } if (client->db->admin) { if (admin_pre_login(client, username)) return finish_set_pool(client, takeover); } /* find user */ if (cf_auth_type == AUTH_ANY) { /* ignore requested user */ if (client->db->forced_user == NULL) { slog_error(client, "auth_type=any requires forced user"); disconnect_client(client, true, "bouncer config error"); return false; } client->auth_user = client->db->forced_user; } else if (cf_auth_type == AUTH_PAM) { if (client->db->auth_user) { slog_error(client, "PAM can't be used together with database authorization"); disconnect_client(client, true, "bouncer config error"); return false; } /* Password will be set after successful authorization when not in takeover mode */ client->auth_user = add_pam_user(username, password); if (!client->auth_user) { slog_error(client, "set_pool(): failed to allocate new PAM user"); disconnect_client(client, true, "bouncer resources exhaustion"); return false; } } else { /* the user clients wants to log in as */ client->auth_user = find_user(username); if (!client->auth_user && client->db->auth_user) { if (takeover) { client->auth_user = add_db_user(client->db, username, password); return finish_set_pool(client, takeover); } start_auth_request(client, username); return false; } if (!client->auth_user) { disconnect_client(client, true, "no such user: %s", username); if (cf_log_connections) slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } } return finish_set_pool(client, takeover); }
static bool finish_set_pool(PgSocket *client, bool takeover) { PgUser *user = client->auth_user; bool ok = false; int auth; /* pool user may be forced */ if (client->db->forced_user) { user = client->db->forced_user; } client->pool = get_pool(client->db, user); if (!client->pool) { disconnect_client(client, true, "no memory for pool"); return false; } if (cf_log_connections) { if (client->sbuf.tls) { char infobuf[96] = ""; tls_get_connection_info(client->sbuf.tls, infobuf, sizeof infobuf); slog_info(client, "login attempt: db=%s user=%s tls=%s", client->db->name, client->auth_user->name, infobuf); } else { slog_info(client, "login attempt: db=%s user=%s tls=no", client->db->name, client->auth_user->name); } } if (!check_fast_fail(client)) return false; if (takeover) return true; if (client->pool->db->admin) { if (!admin_post_login(client)) return false; } if (client->own_user) return finish_client_login(client); auth = cf_auth_type; if (auth == AUTH_HBA) { auth = hba_eval(parsed_hba, &client->remote_addr, !!client->sbuf.tls, client->db->name, client->auth_user->name); } /* remember method */ client->client_auth_type = auth; switch (auth) { case AUTH_ANY: case AUTH_TRUST: ok = finish_client_login(client); break; case AUTH_PLAIN: case AUTH_MD5: case AUTH_PAM: ok = send_client_authreq(client); break; case AUTH_CERT: ok = login_via_cert(client); break; case AUTH_PEER: ok = login_as_unix_peer(client); break; default: disconnect_client(client, true, "login rejected"); ok = false; } return ok; }