From b209a3992d46f2dad39788512e576c7863f53fe0 Mon Sep 17 00:00:00 2001 From: Brian Boru Date: Tue, 26 May 2020 22:45:50 +0300 Subject: [PATCH] auth, not tested --- Jamrules.configure | 1 + include/libpgcli/pgconn.h | 1 + include/libpgcli/pgprov3.h | 12 +++++++++ libpgcli.pc.in | 2 +- src/pgconn.c | 63 ++++++++++++++++++++++++++++++++++++++-------- src/pgprov3.c | 59 ++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 126 insertions(+), 12 deletions(-) diff --git a/Jamrules.configure b/Jamrules.configure index 8bff240..b64516c 100644 --- a/Jamrules.configure +++ b/Jamrules.configure @@ -18,6 +18,7 @@ rule -configure- { -configure-pkg-config-necessary- "libex" "libex" "uuid" "uuid" + "libssl" "libssl" ; local pp = [ Command "echo $(PREFIX) | sed \"s/\\//\\\\\\\\\\\//g\"" : dummy ] ; Command "sed -e \"s/@prefix/\\$(pp)/\" ./libpgcli.pc.in > ./libpgcli.pc" ; diff --git a/include/libpgcli/pgconn.h b/include/libpgcli/pgconn.h index 7a7e23f..0da2409 100644 --- a/include/libpgcli/pgconn.h +++ b/include/libpgcli/pgconn.h @@ -16,6 +16,7 @@ #define EPG_PORT 0x80000000 #define EPG_ERRNO 0x40000000 +#define EPG_PROTO 0x20000000 #define EPG_HOST 0x0fffffff #define PG_NRECS(C) C->row_list->len diff --git a/include/libpgcli/pgprov3.h b/include/libpgcli/pgprov3.h index 7996122..0344d17 100644 --- a/include/libpgcli/pgprov3.h +++ b/include/libpgcli/pgprov3.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #define PG_MINOR_VER 0 #define PG_AUTHOK 'R' +#define PG_PASS 'p' #define PG_PARAMSTATUS 'S' #define PG_BACKENDKEYDATA 'K' #define PG_READY 'Z' @@ -65,6 +67,12 @@ #define PG_FULL 0 +#define PG_OK 0 +#define PG_REQMD5 5 +#define PG_REQPASS 3 + +#define PG_MD5PASS_LEN 35 + typedef int32_t oid_t; typedef struct { @@ -87,6 +95,7 @@ int pgmsg_set_param (pgmsg_t **msg, const char *name, size_t name_len, const cha pgmsg_t *pgmsg_create (char type); pgmsg_t *pgmsg_create_startup (const char *user, size_t user_len, const char *database, size_t database_len); pgmsg_t *pgmsg_create_startup_params (const char *conn_info); +pgmsg_t *pgmsg_create_pass (int req, const char *salt, size_t salt_len, const char *user, const char *pass); pgmsg_t *pgmsg_create_simple_query (const char *sql, size_t sql_len); pgmsg_t *pgmsg_create_parse (const char *name, size_t name_len, const char *sql, size_t sql_len, int fld_len, pgfld_t **flds); pgmsg_t *pgmsg_create_bind (const char *portal, size_t portal_len, const char *stmt, size_t stmt_len, @@ -103,6 +112,9 @@ int pgmsg_recv (int fd, pgmsg_t **msg); typedef struct { char type; int32_t success; + union { + uint8_t md5_auth [4]; + } kind; } pgmsg_auth_t; typedef struct { diff --git a/libpgcli.pc.in b/libpgcli.pc.in index 60fe661..a01014a 100644 --- a/libpgcli.pc.in +++ b/libpgcli.pc.in @@ -6,5 +6,5 @@ includedir=${prefix}/include Name: libpgcli Description: libpgcli Version: 0.1.0 -Libs: -L${libdir} -lpgcli -lex -luuid +Libs: -L${libdir} -lpgcli -lex -luuid -lssl Cflags: -I${includedir} diff --git a/src/pgconn.c b/src/pgconn.c index a5951b6..e4f3674 100644 --- a/src/pgconn.c +++ b/src/pgconn.c @@ -113,6 +113,11 @@ const int pg_error (pgconn_t *conn, pgerror_t *e) { e->msg = strerror(conn->intr_error & EPG_HOST); return -1; } + if ((conn->intr_error & EPG_PROTO)) { + e->code = EPG_PROTO; + e->msg = "Protocol error"; + return -1; + } switch ((conn->intr_error & EPG_HOST)) { case HOST_NOT_FOUND: e->code = HOST_NOT_FOUND; @@ -141,7 +146,14 @@ const int pg_error (pgconn_t *conn, pgerror_t *e) { return 0; } -static void _pg_startup (pgconn_t *conn, const char *conn_info) { +typedef struct { + struct sockaddr_in in_addr; + char *user; + char *pass; + char salt [4]; +} conninfo_t; + +static void _pg_startup (pgconn_t *conn, const char *conn_info, conninfo_t *cinfo) { pgmsg_t *msg = pgmsg_create_startup_params(conn_info); if (-1 == pgmsg_send(conn->fd, msg)) goto done; @@ -196,7 +208,24 @@ static void _pg_startup (pgconn_t *conn, const char *conn_info) { break; case PG_AUTHOK: resp = pgmsg_parse(msg); - conn->authok = resp->msg_auth.success; + switch (conn->authok = resp->msg_auth.success) { + case PG_OK: + break; + case PG_REQMD5: + case PG_REQPASS: + memcpy(cinfo->salt, resp->msg_auth.kind.md5_auth, sizeof(uint8_t) * 4); + free(resp); + free(msg); + msg = pgmsg_create_pass(conn->authok, cinfo->salt, sizeof(uint8_t) * 4, cinfo->user, cinfo->pass); + pgmsg_send(conn->fd, msg); + free(msg); + msg = NULL; + break; + default: + free(resp); + conn->intr_error = EPG_PROTO; + goto done; + } free(resp); free(msg); msg = NULL; @@ -213,7 +242,7 @@ done: } static void *_parse_param (void *data, const char *begin, const char *end, unsigned int *flags) { - struct sockaddr_in *in_addr = (struct sockaddr_in*)data; + conninfo_t *cinfo = (conninfo_t*)data; const char *p = begin, *p1; while (p < end && '=' != *p) ++p; if (p == end) @@ -228,10 +257,10 @@ static void *_parse_param (void *data, const char *begin, const char *end, unsig return data; if (0 == strncmp(begin, "host", (uintptr_t)p1 - (uintptr_t)begin)) { char *addr = strndup(p, (uintptr_t)end - (uintptr_t)p); - if (!inet_aton(addr, &in_addr->sin_addr)) { + if (!inet_aton(addr, &cinfo->in_addr.sin_addr)) { struct hostent *host = gethostbyname(addr); if (host) - in_addr->sin_addr = *(struct in_addr*)host->h_addr_list; + cinfo->in_addr.sin_addr = *(struct in_addr*)host->h_addr_list; else *flags |= h_errno; } @@ -241,32 +270,46 @@ static void *_parse_param (void *data, const char *begin, const char *end, unsig char *port_s = strndup(p, (uintptr_t)end - (uintptr_t)p), *tail; int port = strtol(port_s, &tail, 0); if ('\0' == *tail && ERANGE != errno) - in_addr->sin_port = htons(port); + cinfo->in_addr.sin_port = htons(port); else *flags |= EPG_PORT; + } else + if (0 == strncmp(begin, "user", (uintptr_t)p1 - (uintptr_t)begin)) { + if (cinfo->user) + free(cinfo->user); + cinfo->user = strndup(p, (uintptr_t)end - (uintptr_t)p); + } else + if (0 == strncmp(begin, "password", (uintptr_t)p1 - (uintptr_t)begin)) { + if (cinfo->pass) + free(cinfo->pass); + cinfo->pass = strndup(p, (uintptr_t)end - (uintptr_t)p); } return data; } pgconn_t *pg_connect (const char *conn_info) { pgconn_t *conn; - struct sockaddr_in in_addr = { .sin_family = AF_INET, .sin_port = pg_port, .sin_addr.s_addr = pg_addr }; + conninfo_t cinfo = { .in_addr ={ .sin_family = AF_INET, .sin_port = pg_port, .sin_addr.s_addr = pg_addr }, .user = NULL, .pass = NULL }; int fd; uint32_t flags = 0; conn = calloc(1, sizeof(pgconn_t)); conn->msgs = lst_alloc(on_default_free_item); conn->row_list = lst_alloc(on_default_free_item); - parse_conninfo((void*)&in_addr, conn_info, _parse_param, &flags); + parse_conninfo((void*)&cinfo, conn_info, _parse_param, &flags); if (0 != (conn->intr_error = flags)) return conn; fd = socket(AF_INET, SOCK_STREAM, 0); - if (-1 == connect(fd, (struct sockaddr*)&in_addr, sizeof in_addr)) { + if (-1 == connect(fd, (struct sockaddr*)&cinfo.in_addr, sizeof cinfo.in_addr)) { close(fd); conn->intr_error = EPG_ERRNO | errno; return conn; } conn->fd = fd; - _pg_startup(conn, conn_info); + _pg_startup(conn, conn_info, &cinfo); + if (cinfo.user) + free(cinfo.user); + if (cinfo.pass) + free(cinfo.pass); return conn; } diff --git a/src/pgprov3.c b/src/pgprov3.c index fd22ad3..ff1d961 100644 --- a/src/pgprov3.c +++ b/src/pgprov3.c @@ -272,6 +272,53 @@ pgmsg_t *pgmsg_create_close(char what, const char *str, size_t slen) { return msg; } +static void _pg_md5_hash (const void *buff, size_t len, char *out) { + uint8_t digest [16]; + MD5_CTX ctx; + MD5_Init(&ctx); + while (len > 0) { + if (len > 512) + MD5_Update(&ctx, buff, 512); + else + MD5_Update(&ctx, buff, len); + buff += 512; + len -= 512; + } + MD5_Final(digest, &ctx); + for (int i = 0; i < 16; ++i) + snprintf(&(out[i*2]), 16*2, "%02x", (uint8_t)digest[i]); +} + +static void _pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, char *buf) { + size_t passwd_len = strlen(passwd); + char *crypt_buf = malloc(passwd_len + salt_len + 1); + memcpy(crypt_buf, passwd, passwd_len); + memcpy(crypt_buf + passwd_len, salt, salt_len); + strcpy(buf, "md5"); + _pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3); + free(crypt_buf); +} + +pgmsg_t *pgmsg_create_pass (int req, const char *salt, size_t salt_len, const char *user, const char *pass) { + pgmsg_t *msg = pgmsg_create(PG_PASS); + char *pwd = malloc(2 * (PG_MD5PASS_LEN + 1)), + *pwd2 = pwd + PG_MD5PASS_LEN + 1, + *pwd_to_send; + _pg_md5_encrypt(pass, user, strlen(user), pwd2); + _pg_md5_encrypt(pwd2 + sizeof("md5")-1, salt, 4, pwd); + pwd_to_send = pwd; + switch (req) { + case PG_REQMD5: + pwd_to_send = pwd; + break; + case PG_REQPASS: + pwd_to_send = (char*)pass; + } + _setstr(&msg, pwd_to_send, strlen(pwd_to_send)); + _seti8(&msg, 0); + return msg; +} + int pgmsg_send (int fd, pgmsg_t *msg) { void *buf; size_t size; @@ -414,7 +461,17 @@ pgmsg_resp_t *pgmsg_parse (pgmsg_t *msg) { case PG_AUTHOK: resp = malloc(sizeof(pgmsg_auth_t)); resp->type = msg->body.type; - resp->msg_auth.success = be32toh(*((int32_t*)msg->body.ptr)); + switch (resp->msg_auth.success = be32toh(*((int32_t*)msg->body.ptr))) { + case PG_OK: + break; + case PG_REQMD5: + memcpy(resp->msg_auth.kind.md5_auth, msg->body.ptr + sizeof(int32_t), sizeof(uint8_t)*4); + break; + default: + free(resp); + resp = NULL; + break; + } break; case PG_PARAMSTATUS: resp = malloc(sizeof(pgmsg_param_status_t)); -- 2.11.4.GIT