/* * Finishes the handshake after successful or unsuccessful authorization. * The function is only called from the main thread. */ static void pam_auth_finish(struct pam_auth_request *request) { PgSocket *client = request->client; bool authenticated = (request->status == PAM_STATUS_SUCCESS); if (authenticated) { safe_strcpy(client->auth_user->passwd, request->password, sizeof(client->auth_user->passwd)); sbuf_continue(&client->sbuf); } else { disconnect_client(client, true, "auth failed"); } }
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; }
/* callback from SBuf */ bool client_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *data) { bool res = false; PgSocket *client = container_of(sbuf, PgSocket, sbuf); PktHdr pkt; Assert(!is_server_socket(client)); Assert(client->sbuf.sock); Assert(client->state != CL_FREE); /* may happen if close failed */ if (client->state == CL_JUSTFREE) return false; switch (evtype) { case SBUF_EV_CONNECT_OK: case SBUF_EV_CONNECT_FAILED: /* ^ those should not happen */ case SBUF_EV_RECV_FAILED: disconnect_client(client, false, "client unexpected eof"); break; case SBUF_EV_SEND_FAILED: disconnect_server(client->link, false, "server connection closed"); break; case SBUF_EV_READ: /* Wait until full packet headers is available. */ if (incomplete_header(data)) { slog_noise(client, "C: got partial header, trying to wait a bit"); return false; } if (!get_header(data, &pkt)) { char hex[8*2 + 1]; disconnect_client(client, true, "bad packet header: '%s'", hdr2hex(data, hex, sizeof(hex))); return false; } slog_noise(client, "pkt='%c' len=%d", pkt_desc(&pkt), pkt.len); client->request_time = get_cached_time(); switch (client->state) { case CL_LOGIN: res = handle_client_startup(client, &pkt); break; case CL_ACTIVE: if (client->wait_for_welcome) res = handle_client_startup(client, &pkt); else res = handle_client_work(client, &pkt); break; case CL_WAITING: fatal("why waiting client in client_proto()"); default: fatal("bad client state: %d", client->state); } break; case SBUF_EV_FLUSH: /* client is not interested in it */ break; case SBUF_EV_PKT_CALLBACK: /* unused ATM */ break; case SBUF_EV_TLS_READY: sbuf_continue(&client->sbuf); res = true; break; } return res; }