From d302b6f4d5d8e4beea00496ea83bb0ffbc923008 Mon Sep 17 00:00:00 2001 From: Hiroshi Inoue Date: Sun, 17 Jan 2010 13:10:41 +0000 Subject: [PATCH] 1) Allow password which contains special characters like {,},=,;. 2) Add a new data source option which makes it possible to use Kerberos for Windows library to reply to GSSAPI authentication request. 3) Native support for SSPI Kerberos or Negaotiate service. It may be useful for the 64-bit drivers. 4) Fix an oversight of Memory overflow handling. --- connection.c | 240 +++++++++++++++++++++++---- connection.h | 5 + convert.c | 2 + dlg_specific.c | 312 +++++++++++++++++++++++++++++++--- dlg_specific.h | 10 +- dlg_wingui.c | 5 + loadlib.c | 48 +++++- psqlodbc.h | 5 +- psqlodbc.rc | 4 + resource.h | 3 +- socket.c | 12 +- socket.h | 16 +- sspisvcs.c | 443 ++++++++++++++++++++++++++++++++++++++++++++----- sspisvcs.h | 6 +- statement.c | 41 ++++- version.h | 8 +- 16 files changed, 1034 insertions(+), 126 deletions(-) diff --git a/connection.c b/connection.c index dfbfe5f..53c1555 100644 --- a/connection.c +++ b/connection.c @@ -14,7 +14,6 @@ */ /* Multibyte support Eiji Tokuya 2001-03-15 */ -#include "loadlib.h" #include "connection.h" #ifndef NOT_USE_LIBPQ #include @@ -30,6 +29,12 @@ #else #include #endif /* WIN32 */ +#ifdef USE_KRB5 +#include "krb5svcs.h" +#endif /* USE_KRB5 */ +#ifdef USE_GSS +#include "gsssvcs.h" +#endif /* USE_GSS */ #include "environ.h" #include "socket.h" @@ -273,6 +278,7 @@ CC_conninfo_init(ConnInfo *conninfo) conninfo->cvt_null_date_string = -1; conninfo->autocommit_public = SQL_AUTOCOMMIT_ON; conninfo->accessible_only = -1; + conninfo->gssauth_use_gssapi = -1; #ifdef _HANDLE_ENLIST_IN_DTC_ conninfo->xa_opt = -1; #endif /* _HANDLE_ENLIST_IN_DTC_ */ @@ -447,6 +453,21 @@ CC_clear_error(ConnectionClass *self) CONNLOCK_RELEASE(self); } +static void +CC_endup_authentication(ConnectionClass *self) +{ + SocketClass *sock = CC_get_socket(self); +#ifdef USE_SSPI + if (0 != self->auth_svcs) + { + ReleaseSvcSpecData(sock, self->auth_svcs); + self->auth_svcs = 0; + } +#endif /* USE_SSPI */ +#ifdef USE_GSS + pg_GSS_cleanup(sock); +#endif /* USE_GSS */ +} CSTR bgncmd = "BEGIN"; CSTR cmtcmd = "COMMIT"; @@ -1077,9 +1098,23 @@ static int protocol3_opts_array(ConnectionClass *self, const char *opts[][2], BO opts[cnt][0] = "database"; opts[cnt++][1] = ci->database; } } - if (ci->username[0]) + if (ci->username[0] || !libpqopt) { - opts[cnt][0] = "user"; opts[cnt++][1] = ci->username; + char *usrname = ci->username; +#ifdef WIN32 + DWORD namesize = sizeof(ci->username) - 2; +#endif /* WIN32 */ + + opts[cnt][0] = "user"; + if (!usrname[0]) + { +#ifdef WIN32 + if (GetUserName(ci->username + 1, &namesize)) + usrname = ci->username + 1; +#endif /* WIN32 */ + } +mylog("!!! usrname=%s server=%s\n", usrname, ci->server); + opts[cnt++][1] = usrname; } if (libpqopt) { @@ -1120,6 +1155,10 @@ static int protocol3_opts_array(ConnectionClass *self, const char *opts[][2], BO { opts[cnt][0] = "password"; opts[cnt++][1] = ci->password; } + if (ci->gssauth_use_gssapi) + { + opts[cnt][0] = "gsslib"; opts[cnt++][1] = "gssapi"; + } } else { @@ -1379,6 +1418,38 @@ LIBPQ_CC_connect(ConnectionClass *self, char password_req, char *salt_para) } #endif /* NOT_USE_LIBPQ */ +#if defined(USE_SSPI) || defined(USE_GSS) +/* + * Callee should free hte returned pointer. + */ +static char *MakePrincHint(ConnInfo *ci, BOOL sspi) +{ + size_t len; + char *svcprinc; + char *svcname; + BOOL attrFound = FALSE; + + svcname = extract_extra_attribute_setting(ci->conn_settings, "krbsrvname"); + +mylog("!!! %s settings=%s svcname=%p\n", __FUNCTION__, ci->conn_settings, svcname); + if (svcname) + attrFound = TRUE; + else + svcname = "postgres"; + len = strlen(svcname) + 1 + strlen(ci->server) + 1; + if (NULL != (svcprinc = malloc(len))) + { + if (sspi) + snprintf(svcprinc, len, "%s/%s", svcname, ci->server); + else + snprintf(svcprinc, len, "%s@%s", svcname, ci->server); + } + if (attrFound) + free(svcname); + return svcprinc; +} +#endif /* USE_SSPI */ + static char original_CC_connect(ConnectionClass *self, char password_req, char *salt_para) { @@ -1387,8 +1458,8 @@ original_CC_connect(ConnectionClass *self, char password_req, char *salt_para) QResultClass *res; SocketClass *sock; ConnInfo *ci = &(self->connInfo); - int areq = -1; - int beresp, sockerr; + int areq = -1; + int ret = 0, beresp, sockerr; char msgbuffer[ERROR_MSG_LENGTH]; char salt[5], notice[512]; CSTR func = "original_CC_connect"; @@ -1400,6 +1471,10 @@ original_CC_connect(ConnectionClass *self, char password_req, char *salt_para) mylog("%s: entering...\n", func); + /* lock the connetion during authentication */ + ENTER_CONN_CS(self); +#define return DONT_CALL_RETURN_FROM_HERE???? + if (password_req != AUTH_REQ_OK) { sock = self->sock; /* already connected, just authenticate */ @@ -1408,7 +1483,7 @@ original_CC_connect(ConnectionClass *self, char password_req, char *salt_para) else { if (0 == CC_initial_log(self, func)) - return 0; + goto error_proc; #ifdef USE_SSPI ssl_try_count = 0; @@ -1453,7 +1528,7 @@ another_version_retry: if (PROTOCOL_62(ci)) { CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not construct a socket to the server", func); - return 0; + goto error_proc; } if (PROTOCOL_63(ci)) strncpy_null(ci->protocol, PG62, sizeof(ci->protocol)); @@ -1484,7 +1559,7 @@ another_version_retry: if (!self->sock) { CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not construct a socket to the server", func); - return 0; + goto error_proc; } } @@ -1496,7 +1571,7 @@ another_version_retry: if (SOCK_get_errcode(sock) != 0) { CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not connect to the server", func); - return 0; + goto error_proc; } mylog("connection to the server socket succeeded.\n"); @@ -1528,8 +1603,8 @@ inolog("protocol=%s version=%d,%d\n", ci->protocol, self->pg_version_major, self case 'S': if (!StartupSspiService(sock, SchannelService, NULL)) { - CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Service negotation failed", func); - return 0; + CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service negotation failed", func); + goto error_proc; } break; case 'N': @@ -1543,8 +1618,8 @@ inolog("protocol=%s version=%d,%d\n", ci->protocol, self->pg_version_major, self goto another_version_retry; } default: - CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Service negotation failed", func); - return 0; + CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service negotation failed", func); + goto error_proc; } } #endif /* USE_SSPI */ @@ -1565,7 +1640,7 @@ inolog("protocol=%s version=%d,%d\n", ci->protocol, self->pg_version_major, self else if (PROTOCOL_74(ci)) { if (!protocol3_packet_build(self)) - return 0; + goto error_proc; } else { @@ -1592,7 +1667,7 @@ inolog("protocol=%s version=%d,%d\n", ci->protocol, self->pg_version_major, self if (SOCK_get_errcode(sock) != 0) { CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Failed to send the authentication packet", func); - return 0; + goto error_proc; } mylog("sent the authentication block successfully.\n"); } @@ -1609,7 +1684,8 @@ inolog("protocol=%s version=%d,%d\n", ci->protocol, self->pg_version_major, self { BOOL beforeV2 = PG_VERSION_LT(self, 6.4), ReadyForQuery = FALSE, retry = FALSE; - uint32 leng; + uint32 leng = 0; + int authRet; do { @@ -1664,7 +1740,7 @@ inolog("Ekita retry=%d\n", retry); if (retry) break; - return 0; + goto error_proc; case 'R': if (password_req != AUTH_REQ_OK) @@ -1695,11 +1771,22 @@ inolog("Ekita retry=%d\n", retry); case AUTH_REQ_KRB4: CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Kerberos 4 authentication not supported", func); - return 0; + goto error_proc; case AUTH_REQ_KRB5: +#ifdef USE_KRB5 + // pglock_thread(); + authRet = pg_krb5_sendauth(self); + // pgunlock_thread(); + if (authRet != 0) + { + /* Error message already filled in */ + goto error_proc; + } + break; +#endif /* USE_KRB5 */ CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Kerberos 5 authentication not supported", func); - return 0; + goto error_proc; case AUTH_REQ_PASSWORD: mylog("in AUTH_REQ_PASSWORD\n"); @@ -1707,7 +1794,8 @@ inolog("Ekita retry=%d\n", retry); if (ci->password[0] == '\0') { CC_set_error(self, CONNECTION_NEED_PASSWORD, "A password is required for this connection.", func); - return -areq; /* need password */ + ret = -areq; /* need password */ + goto error_proc; } mylog("past need password\n"); @@ -1723,7 +1811,7 @@ inolog("Ekita retry=%d\n", retry); case AUTH_REQ_CRYPT: CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Password crypt authentication not supported", func); - return 0; + goto error_proc; case AUTH_REQ_MD5: mylog("in AUTH_REQ_MD5\n"); if (ci->password[0] == '\0') @@ -1731,22 +1819,101 @@ inolog("Ekita retry=%d\n", retry); CC_set_error(self, CONNECTION_NEED_PASSWORD, "A password is required for this connection.", func); if (salt_para) memcpy(salt_para, salt, sizeof(salt)); - return -areq; /* need password */ + ret = -areq; /* need password */ + goto error_proc; } if (md5_auth_send(self, salt)) { CC_set_error(self, CONN_INVALID_AUTHENTICATION, "md5 hashing failed", func); - return 0; + goto error_proc; } break; case AUTH_REQ_SCM_CREDS: CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Unix socket credential authentication not supported", func); - return 0; + goto error_proc; + + case AUTH_REQ_GSS: + mylog("in AUTH_REQ_GSS\n"); +#if defined(USE_SSPI) + if (!ci->gssauth_use_gssapi) + { + self->auth_svcs = KerberosService; + authRet = StartupSspiService(sock, self->auth_svcs, MakePrincHint(ci, TRUE)); + if (!authRet) + { + CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service negotation failed", func); + goto error_proc; + } + break; + } +#endif /* USE_SSPI */ +#ifdef USE_GSS +#ifdef USE_SSPI + if (ci->gssauth_use_gssapi) +#endif /* USE_SSPI */ + { + // pglock_thread(); + authRet = pg_GSS_startup(self, MakePrincHint(ci, FALSE)); + // pgunlock_thread(); + if (authRet != 0) + { + CC_set_errornumber(self, CONN_INVALID_AUTHENTICATION); + goto error_proc; + } + break; + } +#endif /* USE_GSS */ + CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "GSS authentication not supported", func); + goto error_proc; + + case AUTH_REQ_GSS_CONT: + mylog("in AUTH_REQ_GSS_CONT\n"); +#if defined(USE_SSPI) + if (0 != self->auth_svcs) + { + authRet = ContinueSspiService(sock, self->auth_svcs, (void *) (leng - 4)); + if (!authRet) + { + CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service continuation failed", func); + goto error_proc; + } + break; + } +#endif /* USE_SSPI */ +#ifdef USE_GSS + // pglock_thread(); + authRet = pg_GSS_continue(self, leng - 4); + // pgunlock_thread(); + if (authRet != 0) + { + CC_set_errornumber(self, CONN_INVALID_AUTHENTICATION); + goto error_proc; + } + break; +#else + CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "GSS authentication not supported", func); + goto error_proc; +#endif + + case AUTH_REQ_SSPI: + mylog("in AUTH_REQ_SSPI\n"); +#if defined(USE_SSPI) + self->auth_svcs = ci->gssauth_use_gssapi ? KerberosService : NegotiateService; + if (!StartupSspiService(sock, self->auth_svcs, MakePrincHint(ci, TRUE))) + { + CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Service negotation failed", func); + goto error_proc; + } + break; +#else + CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "SSPI authentication not supported", func); + goto error_proc; +#endif default: CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Unknown authentication type", func); - return 0; + goto error_proc; } break; case 'S': /* parameter status */ @@ -1767,7 +1934,7 @@ inolog("Ekita retry=%d\n", retry); default: snprintf(notice, sizeof(notice), "Unexpected protocol character='%c' during authentication", beresp); CC_set_error(self, CONN_INVALID_AUTHENTICATION, notice, func); - return 0; + goto error_proc; } if (retry) { @@ -1784,6 +1951,13 @@ inolog("Ekita retry=%d\n", retry); } sockerr_proc: + ret = 1; +error_proc: +#undef return + CC_endup_authentication(self); + LEAVE_CONN_CS(self); + if (0 >= ret) + return ret; if (0 != (sockerr = SOCK_get_errcode(sock))) { if (0 == CC_get_errornumber(self)) @@ -1846,17 +2020,15 @@ CC_connect(ConnectionClass *self, char password_req, char *salt_para) mylog("sslmode=%s\n", self->connInfo.sslmode); #ifndef NOT_USE_LIBPQ +#ifdef USE_SSPI + if (0 != self->svcs_allowed) + ; + else +#endif /* USE_SSPI */ if (self->connInfo.username[0] == '\0') call_libpq = TRUE; else if (self->connInfo.sslmode[0] != SSLLBYTE_DISABLE) -#ifdef USE_SSPI - { - if (0 == (SchannelService & self->svcs_allowed)) - call_libpq = TRUE; - } -#else call_libpq = TRUE; -#endif /* USE_SSPI */ if (call_libpq) { ret = LIBPQ_CC_connect(self, password_req, salt_para); @@ -1866,7 +2038,7 @@ CC_connect(ConnectionClass *self, char password_req, char *salt_para) */ if (0 == ret && CONN_UNABLE_TO_LOAD_DLL == CC_get_errornumber(self)) { - self->svcs_allowed |= SchannelService; + self->svcs_allowed |= (SchannelService | KerberosService | NegotiateService); ret = original_CC_connect(self, password_req, salt_para); } #endif /* USE_SSPI */ diff --git a/connection.h b/connection.h index 51044f8..c625248 100644 --- a/connection.h +++ b/connection.h @@ -209,6 +209,9 @@ do { \ #define AUTH_REQ_CRYPT 4 #define AUTH_REQ_MD5 5 #define AUTH_REQ_SCM_CREDS 6 +#define AUTH_REQ_GSS 7 +#define AUTH_REQ_GSS_CONT 8 +#define AUTH_REQ_SSPI 9 /* Startup Packet sizes */ #define SM_DATABASE 64 @@ -311,6 +314,7 @@ typedef struct signed char cvt_null_date_string; signed char autocommit_public; signed char accessible_only; + signed char gssauth_use_gssapi; UInt4 extra_opts; #ifdef _HANDLE_ENLIST_IN_DTC_ signed char xa_opt; @@ -474,6 +478,7 @@ struct ConnectionClass_ pgNAME tableIns; #ifdef USE_SSPI UInt4 svcs_allowed; + UInt4 auth_svcs; #endif /* USE_SSPI */ #if defined(WIN_MULTITHREAD_SUPPORT) CRITICAL_SECTION cs; diff --git a/convert.c b/convert.c index 5d8723c..5fac790 100644 --- a/convert.c +++ b/convert.c @@ -1097,6 +1097,7 @@ inolog("2stime fr=%d\n", std_time.fr); new_string = malloc(cbValueMax); lc = localeconv(); for (i = 0, j = 0; ptr[i]; i++) + { if (ptr[i] == '.') { strncpy_null(&new_string[j], lc->decimal_point, cbValueMax - j); @@ -1104,6 +1105,7 @@ inolog("2stime fr=%d\n", std_time.fr); } else new_string[j++] = ptr[i]; + } new_string[j] = '\0'; strncpy_null(rgbValueBindRow, new_string, copy_len + 1); free(new_string); diff --git a/dlg_specific.c b/dlg_specific.c index c5a3523..9e00952 100644 --- a/dlg_specific.c +++ b/dlg_specific.c @@ -27,8 +27,8 @@ extern GLOBAL_VALUES globals; -static void encode(const UCHAR *in, UCHAR *out); -static void decode(const UCHAR *in, UCHAR *out); +static void encode(const UCHAR *in, UCHAR *out, int outlen); +static void decode(const UCHAR *in, UCHAR *out, int outlen); #define OVR_EXTRA_BITS (BIT_FORCEABBREVCONNSTR | BIT_FAKE_MSS | BIT_BDE_ENVIRONMENT | BIT_CVT_NULL_DATE | BIT_ACCESSIBLE_ONLY) UInt4 getExtraOptions(const ConnInfo *ci) @@ -167,13 +167,14 @@ void makeConnectString(char *connect_string, const ConnInfo *ci, UWORD len) { char got_dsn = (ci->dsn[0] != '\0'); - char encoded_conn_settings[LARGE_REGISTRY_LEN]; + char encoded_item[LARGE_REGISTRY_LEN]; ssize_t hlen, nlen, olen; /*BOOL abbrev = (len <= 400);*/ BOOL abbrev = (len < 1024) || 0 < ci->force_abbrev_connstr; UInt4 flag; inolog("force_abbrev=%d abbrev=%d\n", ci->force_abbrev_connstr, abbrev); + encode(ci->password, encoded_item, sizeof(encoded_item)); /* fundamental info */ nlen = MAX_CONNECT_STRING; olen = snprintf(connect_string, nlen, "%s=%s;DATABASE=%s;SERVER=%s;PORT=%s;UID=%s;PWD=%s", @@ -183,14 +184,14 @@ inolog("force_abbrev=%d abbrev=%d\n", ci->force_abbrev_connstr, abbrev); ci->server, ci->port, ci->username, - ci->password); + encoded_item); if (olen < 0 || olen >= nlen) { connect_string[0] = '\0'; return; } - encode(ci->conn_settings, encoded_conn_settings); + encode(ci->conn_settings, encoded_item, sizeof(encoded_item)); /* extra info */ hlen = strlen(connect_string); @@ -237,6 +238,9 @@ inolog("hlen=%d", hlen); INI_BYTEAASLONGVARBINARY "=%d;" INI_USESERVERSIDEPREPARE "=%d;" INI_LOWERCASEIDENTIFIER "=%d;" +#ifdef WIN32 + INI_GSSAUTHUSEGSSAPI "=%d" +#endif /* WIN32 */ #ifdef _HANDLE_ENLIST_IN_DTC_ INI_XAOPT "=%d" /* XAOPT */ #endif /* _HANDLE_ENLIST_IN_DTC_ */ @@ -247,7 +251,7 @@ inolog("hlen=%d", hlen); ,ci->show_oid_column ,ci->row_versioning ,ci->show_system_tables - ,encoded_conn_settings + ,encoded_item ,ci->drivers.fetch_max ,ci->drivers.socket_buffersize ,ci->drivers.unknown_sizes @@ -272,6 +276,9 @@ inolog("hlen=%d", hlen); ,ci->bytea_as_longvarbinary ,ci->use_server_side_prepare ,ci->lower_case_identifier +#ifdef WIN32 + ,ci->gssauth_use_gssapi +#endif /* WIN32 */ #ifdef _HANDLE_ENLIST_IN_DTC_ ,ci->xa_opt #endif /* _HANDLE_ENLIST_IN_DTC_ */ @@ -342,6 +349,8 @@ inolog("hlen=%d", hlen); flag |= BIT_USESERVERSIDEPREPARE; if (ci->lower_case_identifier) flag |= BIT_LOWERCASEIDENTIFIER; + if (ci->gssauth_use_gssapi) + flag |= BIT_GSSAUTHUSEGSSAPI; if (ci->sslmode[0]) { @@ -361,7 +370,7 @@ inolog("hlen=%d", hlen); INI_INT8AS "=%d;" ABBR_EXTRASYSTABLEPREFIXES "=%s;" INI_ABBREVIATE "=%02x%x", - encoded_conn_settings, + encoded_item, ci->drivers.fetch_max, ci->drivers.socket_buffersize, ci->drivers.max_varchar_size, @@ -464,6 +473,7 @@ unfoldCXAttribute(ConnInfo *ci, const char *value) ci->bytea_as_longvarbinary = (char)((flag & BIT_BYTEAASLONGVARBINARY) != 0); ci->use_server_side_prepare = (char)((flag & BIT_USESERVERSIDEPREPARE) != 0); ci->lower_case_identifier = (char)((flag & BIT_LOWERCASEIDENTIFIER) != 0); + ci->gssauth_use_gssapi = (char)((flag & BIT_GSSAUTHUSEGSSAPI) != 0); } BOOL copyAttributes(ConnInfo *ci, const char *attribute, const char *value) @@ -483,11 +493,11 @@ copyAttributes(ConnInfo *ci, const char *attribute, const char *value) else if (stricmp(attribute, INI_SERVER) == 0 || stricmp(attribute, SPEC_SERVER) == 0) strcpy(ci->server, value); - else if (stricmp(attribute, INI_USER) == 0 || stricmp(attribute, INI_UID) == 0) + else if (stricmp(attribute, INI_USERNAME) == 0 || stricmp(attribute, INI_UID) == 0) strcpy(ci->username, value); else if (stricmp(attribute, INI_PASSWORD) == 0 || stricmp(attribute, "pwd") == 0) - strcpy(ci->password, value); + decode(value, ci->password, sizeof(ci->password)); else if (stricmp(attribute, INI_PORT) == 0) strcpy(ci->port, value); @@ -538,7 +548,7 @@ copyAttributes(ConnInfo *ci, const char *attribute, const char *value) ci->conn_settings[len - 1] = '\0'; } else - decode(value, ci->conn_settings); + decode(value, ci->conn_settings, sizeof(ci->conn_settings)); } else if (stricmp(attribute, INI_DISALLOWPREMATURE) == 0 || stricmp(attribute, ABBR_DISALLOWPREMATURE) == 0) ci->disallow_premature = atoi(value); @@ -556,6 +566,8 @@ copyAttributes(ConnInfo *ci, const char *attribute, const char *value) ci->use_server_side_prepare = atoi(value); else if (stricmp(attribute, INI_LOWERCASEIDENTIFIER) == 0 || stricmp(attribute, ABBR_LOWERCASEIDENTIFIER) == 0) ci->lower_case_identifier = atoi(value); + else if (stricmp(attribute, INI_GSSAUTHUSEGSSAPI) == 0 || stricmp(attribute, ABBR_GSSAUTHUSEGSSAPI) == 0) + ci->gssauth_use_gssapi = atoi(value); else if (stricmp(attribute, INI_SSLMODE) == 0 || stricmp(attribute, ABBR_SSLMODE) == 0) { switch (value[0]) @@ -735,6 +747,8 @@ getDSNdefaults(ConnInfo *ci) ci->use_server_side_prepare = DEFAULT_USESERVERSIDEPREPARE; if (ci->lower_case_identifier < 0) ci->lower_case_identifier = DEFAULT_LOWERCASEIDENTIFIER; + if (ci->gssauth_use_gssapi < 0) + ci->gssauth_use_gssapi = DEFAULT_GSSAUTHUSEGSSAPI; if (ci->sslmode[0] == '\0') strcpy(ci->sslmode, DEFAULT_SSLMODE); if (ci->force_abbrev_connstr < 0) @@ -774,7 +788,7 @@ getDSNinfo(ConnInfo *ci, char overwrite) { CSTR func = "getDSNinfo"; char *DSN = ci->dsn; - char encoded_conn_settings[LARGE_REGISTRY_LEN], + char encoded_item[LARGE_REGISTRY_LEN], temp[SMALL_REGISTRY_LEN]; /* @@ -813,10 +827,13 @@ getDSNinfo(ConnInfo *ci, char overwrite) SQLGetPrivateProfileString(DSN, INI_DATABASE, "", ci->database, sizeof(ci->database), ODBC_INI); if (ci->username[0] == '\0' || overwrite) - SQLGetPrivateProfileString(DSN, INI_USER, "", ci->username, sizeof(ci->username), ODBC_INI); + SQLGetPrivateProfileString(DSN, INI_USERNAME, "", ci->username, sizeof(ci->username), ODBC_INI); if (ci->password[0] == '\0' || overwrite) - SQLGetPrivateProfileString(DSN, INI_PASSWORD, "", ci->password, sizeof(ci->password), ODBC_INI); + { + SQLGetPrivateProfileString(DSN, INI_PASSWORD, "", encoded_item, sizeof(encoded_item), ODBC_INI); + decode(encoded_item, ci->password, sizeof(ci->password)); + } if (ci->port[0] == '\0' || overwrite) SQLGetPrivateProfileString(DSN, INI_PORT, "", ci->port, sizeof(ci->port), ODBC_INI); @@ -853,8 +870,8 @@ getDSNinfo(ConnInfo *ci, char overwrite) if (ci->conn_settings[0] == '\0' || overwrite) { - SQLGetPrivateProfileString(DSN, INI_CONNSETTINGS, "", encoded_conn_settings, sizeof(encoded_conn_settings), ODBC_INI); - decode(encoded_conn_settings, ci->conn_settings); + SQLGetPrivateProfileString(DSN, INI_CONNSETTINGS, "", encoded_item, sizeof(encoded_item), ODBC_INI); + decode(encoded_item, ci->conn_settings, sizeof(ci->conn_settings)); } if (ci->translation_dll[0] == '\0' || overwrite) @@ -919,6 +936,13 @@ getDSNinfo(ConnInfo *ci, char overwrite) ci->lower_case_identifier = atoi(temp); } + if (ci->gssauth_use_gssapi < 0 || overwrite) + { + SQLGetPrivateProfileString(DSN, INI_GSSAUTHUSEGSSAPI, "", temp, sizeof(temp), ODBC_INI); + if (temp[0]) + ci->gssauth_use_gssapi = atoi(temp); + } + if (ci->sslmode[0] == '\0' || overwrite) SQLGetPrivateProfileString(DSN, INI_SSLMODE, "", ci->sslmode, sizeof(ci->sslmode), ODBC_INI); @@ -1080,10 +1104,9 @@ void writeDSNinfo(const ConnInfo *ci) { const char *DSN = ci->dsn; - char encoded_conn_settings[LARGE_REGISTRY_LEN], + char encoded_item[LARGE_REGISTRY_LEN], temp[SMALL_REGISTRY_LEN]; - encode(ci->conn_settings, encoded_conn_settings); SQLWritePrivateProfileString(DSN, INI_KDESC, @@ -1106,14 +1129,15 @@ writeDSNinfo(const ConnInfo *ci) ODBC_INI); SQLWritePrivateProfileString(DSN, - INI_USER, + INI_USERNAME, ci->username, ODBC_INI); SQLWritePrivateProfileString(DSN, INI_UID, ci->username, ODBC_INI); + encode(ci->password, encoded_item, sizeof(encoded_item)); SQLWritePrivateProfileString(DSN, INI_PASSWORD, - ci->password, + encoded_item, ODBC_INI); SQLWritePrivateProfileString(DSN, @@ -1150,9 +1174,10 @@ writeDSNinfo(const ConnInfo *ci) temp, ODBC_INI); + encode(ci->conn_settings, encoded_item, sizeof(encoded_item)); SQLWritePrivateProfileString(DSN, INI_CONNSETTINGS, - encoded_conn_settings, + encoded_item, ODBC_INI); sprintf(temp, "%d", ci->disallow_premature); @@ -1200,6 +1225,11 @@ writeDSNinfo(const ConnInfo *ci) INI_LOWERCASEIDENTIFIER, temp, ODBC_INI); + sprintf(temp, "%d", ci->gssauth_use_gssapi); + SQLWritePrivateProfileString(DSN, + INI_GSSAUTHUSEGSSAPI, + temp, + ODBC_INI); SQLWritePrivateProfileString(DSN, INI_SSLMODE, ci->sslmode, @@ -1420,17 +1450,19 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci) } static void -encode(const UCHAR *in, UCHAR *out) +encode(const UCHAR *in, UCHAR *out, int outlen) { size_t i, ilen = strlen(in), o = 0; UCHAR inc; - for (i = 0; i < ilen; i++) + for (i = 0; i < ilen && o < outlen - 1; i++) { inc = in[i]; if (inc == '+') { + if (o + 2 >= outlen) + break; sprintf(&out[o], "%%2B"); o += 3; } @@ -1438,6 +1470,8 @@ encode(const UCHAR *in, UCHAR *out) out[o++] = '+'; else if (!isalnum(inc)) { + if (o + 2 >= outlen) + break; sprintf(&out[o], "%%%02x", inc); o += 3; } @@ -1470,13 +1504,13 @@ conv_from_hex(const UCHAR *s) } static void -decode(const UCHAR *in, UCHAR *out) +decode(const UCHAR *in, UCHAR *out, int outlen) { size_t i, ilen = strlen(in), o = 0; UCHAR inc; - for (i = 0; i < ilen; i++) + for (i = 0; i < ilen && o < outlen - 1; i++) { inc = in[i]; if (inc == '+') @@ -1491,3 +1525,233 @@ decode(const UCHAR *in, UCHAR *out) } out[o++] = '\0'; } + +char *extract_attribute_setting(const char *str, const char *attr, BOOL ref_comment) +{ + const UCHAR *cptr, *sptr = NULL; + UCHAR *rptr; + BOOL allowed_cmd = TRUE, in_quote = FALSE, in_comment = FALSE; + int step = 0, skiplen; + size_t len = 0, attrlen = strlen(attr); + + for (cptr = (UCHAR *) str; *cptr; cptr++) + { + if (in_quote) + { + if (LITERAL_QUOTE == *cptr) + { + if (4 == step) + { + len = cptr - sptr; + step++; + } + in_quote = FALSE; + } + continue; + } + else if (in_comment) + { + if ('*' == *cptr && + '/' == cptr[1]) + { + if (4 == step) + { + len = cptr - sptr; + step++; + } + in_comment = FALSE; + cptr++; + continue; + } + if (!ref_comment) + continue; + } + else if ('/' == *cptr && + '*' == cptr[1]) + { + in_comment = TRUE; + cptr++; + continue; + } + if (';' == *cptr) + { + if (4 == step) + { + len = cptr - sptr; + } + allowed_cmd = TRUE; + step = 0; + continue; + } + if (!allowed_cmd) + continue; + if (isspace(*cptr)) + { + if (4 == step) + { + len = cptr - sptr; + step++; + } + continue; + } + switch (step) + { + case 0: + if (0 != strnicmp(cptr, "set", 3)) + { + allowed_cmd = FALSE; + continue; + } + step++; + cptr += 3; + break; + case 1: + if (0 != strnicmp(cptr, attr, attrlen)) + { + allowed_cmd = FALSE; + continue; + } + step++; + cptr += (attrlen - 1); + break; + case 2: + skiplen = 0; + if (0 != strnicmp(cptr, "=", 1)) + { + skiplen = strlen("to"); + if (0 != strnicmp(cptr, "to", 2)) + { + allowed_cmd = FALSE; + continue; + } + } + step++; + cptr += skiplen; + break; + case 3: + if (LITERAL_QUOTE == *cptr) + { + cptr++; + sptr = cptr; + } + else + sptr = cptr; + step++; + break; + } + } + if (!sptr) + return NULL; + rptr = malloc(len + 1); + memcpy(rptr, sptr, len); + rptr[len] = '\0'; + mylog("extracted a %s '%s' from %s\n", attr, rptr, str); + return rptr; +} + +/* + * extract the specified attribute from the comment part. + * attribute=[']value['] + */ +char *extract_extra_attribute_setting(const char *str, const char *attr) +{ + const UCHAR *cptr, *sptr = NULL; + UCHAR *rptr; + BOOL allowed_cmd = FALSE, in_quote = FALSE, in_comment = FALSE; + int step = 0, step_last = 2; + size_t len = 0, attrlen = strlen(attr); + + for (cptr = (UCHAR *) str; *cptr; cptr++) + { + if (in_quote) + { + if (LITERAL_QUOTE == *cptr) + { + if (step_last == step) + { + len = cptr - sptr; + step = 0; + } + in_quote = FALSE; + } + continue; + } + else if (in_comment) + { + if ('*' == *cptr && + '/' == cptr[1]) + { + if (step_last == step) + { + len = cptr - sptr; + step = 0; + } + in_comment = FALSE; + allowed_cmd = FALSE; + cptr++; + continue; + } + } + else if ('/' == *cptr && + '*' == cptr[1]) + { + in_comment = TRUE; + allowed_cmd = TRUE; + cptr++; + continue; + } + else + { + if (LITERAL_QUOTE == *cptr) + in_quote = TRUE; + continue; + } + /* now in comment */ + if (';' == *cptr || + isspace(*cptr)) + { + if (step_last == step) + len = cptr - sptr; + allowed_cmd = TRUE; + step = 0; + continue; + } + if (!allowed_cmd) + continue; + switch (step) + { + case 0: + if (0 != strnicmp(cptr, attr, attrlen)) + { + allowed_cmd = FALSE; + continue; + } + if (cptr[attrlen] != '=') + { + allowed_cmd = FALSE; + continue; + } + step++; + cptr += attrlen; + break; + case 1: + if (LITERAL_QUOTE == *cptr) + { + in_quote = TRUE; + cptr++; + sptr = cptr; + } + else + sptr = cptr; + step++; + break; + } + } + if (!sptr) + return NULL; + rptr = malloc(len + 1); + memcpy(rptr, sptr, len); + rptr[len] = '\0'; + mylog("extracted a %s '%s' from %s\n", attr, rptr, str); + return rptr; +} diff --git a/dlg_specific.h b/dlg_specific.h index 84e09f4..033b7f7 100644 --- a/dlg_specific.h +++ b/dlg_specific.h @@ -57,7 +57,7 @@ extern "C" { * Postmaster is listening */ #define INI_DATABASE "Database" /* Database Name */ #define INI_UID "UID" /* Default User Name */ -#define INI_USER "Username" /* Default User Name */ +#define INI_USERNAME "Username" /* Default User Name */ #define INI_PASSWORD "Password" /* Default Password */ #define INI_ABBREVIATE "CX" @@ -144,6 +144,8 @@ extern "C" { #define ABBR_SSLMODE "CA" #define INI_EXTRAOPTIONS "AB" #define INI_LOGDIR "Logdir" +#define INI_GSSAUTHUSEGSSAPI "GssAuthUseGSS" +#define ABBR_GSSAUTHUSEGSSAPI "D0" #define SSLMODE_DISABLE "disable" #define SSLMODE_ALLOW "allow" @@ -189,8 +191,9 @@ const char *GetXaLibPath(); #define BIT_BYTEAASLONGVARBINARY (1L<<24) #define BIT_USESERVERSIDEPREPARE (1L<<25) #define BIT_LOWERCASEIDENTIFIER (1L<<26) +#define BIT_GSSAUTHUSEGSSAPI (1L<<27) -#define EFFECTIVE_BIT_COUNT 27 +#define EFFECTIVE_BIT_COUNT 28 /* Mask for extra options */ #define BIT_FORCEABBREVCONNSTR 1L @@ -241,6 +244,7 @@ const char *GetXaLibPath(); #define DEFAULT_USESERVERSIDEPREPARE 0 #define DEFAULT_LOWERCASEIDENTIFIER 0 #define DEFAULT_SSLMODE SSLMODE_DISABLE +#define DEFAULT_GSSAUTHUSEGSSAPI 0 #ifdef _HANDLE_ENLIST_IN_DTC_ #define DEFAULT_XAOPT 1 @@ -290,6 +294,8 @@ int setLogDir(const char *dir); int changeDriverNameOfaDSN(const char *dsn, const char *driver_name, DWORD *errcode); UInt4 getExtraOptions(const ConnInfo *); BOOL setExtraOptions(ConnInfo *, const char *str, const char *format); +char *extract_attribute_setting(const char *str, const char *attr, BOOL ref_comment); +char *extract_extra_attribute_setting(const char *str, const char *attr); #ifdef __cplusplus } diff --git a/dlg_wingui.c b/dlg_wingui.c index 6405ab5..0fe65cb 100644 --- a/dlg_wingui.c +++ b/dlg_wingui.c @@ -590,7 +590,11 @@ ds_options2Proc(HWND hdlg, CheckDlgButton(hdlg, DS_SERVERSIDEPREPARE, ci->use_server_side_prepare); CheckDlgButton(hdlg, DS_BYTEAASLONGVARBINARY, ci->bytea_as_longvarbinary); /*CheckDlgButton(hdlg, DS_LOWERCASEIDENTIFIER, ci->lower_case_identifier);*/ + CheckDlgButton(hdlg, DS_GSSAUTHUSEGSSAPI, ci->gssauth_use_gssapi); +#if defined(NOT_USE_LIBPQ) && !defined(USE_SSPI) && !defined(USE_GSS) + EnableWindow(GetDlgItem(hdlg, DS_GSSAUTHUSEGSSAPI), FALSE); +#endif EnableWindow(GetDlgItem(hdlg, DS_FAKEOIDINDEX), atoi(ci->show_oid_column)); /* Datasource Connection Settings */ @@ -662,6 +666,7 @@ ds_options2Proc(HWND hdlg, ci->use_server_side_prepare = IsDlgButtonChecked(hdlg, DS_SERVERSIDEPREPARE); ci->bytea_as_longvarbinary = IsDlgButtonChecked(hdlg, DS_BYTEAASLONGVARBINARY); /*ci->lower_case_identifier = IsDlgButtonChecked(hdlg, DS_LOWERCASEIDENTIFIER);*/ + ci->gssauth_use_gssapi = IsDlgButtonChecked(hdlg, DS_GSSAUTHUSEGSSAPI); /* OID Options */ sprintf(ci->fake_oid_index, "%d", IsDlgButtonChecked(hdlg, DS_FAKEOIDINDEX)); diff --git a/loadlib.c b/loadlib.c index c1aa565..4d05d93 100644 --- a/loadlib.c +++ b/loadlib.c @@ -63,6 +63,11 @@ #if defined(DYNAMIC_LOAD) #define WIN_DYN_LOAD CSTR libpqdll = "LIBPQ.dll"; +#ifdef _WIN64 +CSTR gssapidll = "GSSAPI64.dll"; +#else +CSTR gssapidll = "GSSAPI32.dll"; +#endif /* _WIN64 */ #ifdef UNICODE_SUPPORT CSTR pgenlist = "pgenlist"; CSTR pgenlistdll = "PGENLIST.dll"; @@ -76,7 +81,12 @@ CSTR pgenlistdll = "PGENLISTA.dll"; #endif /* DYNAMIC_LOAD */ #endif /* WIN32 */ -CSTR libpq = "libpq"; +CSTR libpqlib = "libpq"; +#ifdef _WIN64 +CSTR gssapilib = "gssapi64"; +#else +CSTR gssapilib = "gssapi32"; +#endif /* _WIN64 */ #ifndef NOT_USE_LIBPQ CSTR checkproc = "PQconninfoParse"; static int sslverify_available = -1; @@ -84,7 +94,7 @@ static int sslverify_available = -1; #if defined(_MSC_DELAY_LOAD_IMPORT) static BOOL loaded_libpq = FALSE, loaded_ssllib = FALSE; -static BOOL loaded_pgenlist = FALSE; +static BOOL loaded_pgenlist = FALSE, loaded_gssapi = FALSE; /* * Load psqlodbc path based libpq dll. */ @@ -141,10 +151,10 @@ DliErrorHook(unsigned dliNotify, #else __pfnDliNotifyHook2 = NULL; #endif /* _MSC_VER */ - if (_strnicmp(pdli->szDll, libpq, 5) == 0) + if (_strnicmp(pdli->szDll, libpqlib, strlen(libpqlib)) == 0) { - if (hmodule = MODULE_load_from_psqlodbc_path(libpq), NULL == hmodule) - hmodule = LoadLibrary(libpq); + if (hmodule = MODULE_load_from_psqlodbc_path(libpqlib), NULL == hmodule) + hmodule = LoadLibrary(libpqlib); #ifndef NOT_USE_LIBPQ if (sslverify_available < 0) { @@ -162,6 +172,23 @@ DliErrorHook(unsigned dliNotify, if (hmodule = MODULE_load_from_psqlodbc_path(pgenlist), NULL == hmodule) hmodule = LoadLibrary(pgenlist); } +#ifdef USE_GSS + else if (_strnicmp(pdli->szDll, gssapilib, strlen(gssapilib)) == 0) + { +#ifndef NOT_USE_LIBPQ + if (hmodule = GetModuleHandle(gssapilib), NULL == hmodule) +#endif + { + if (hmodule = MODULE_load_from_psqlodbc_path(gssapilib), NULL == hmodule) + { + if (hmodule = LoadLibrary(gssapilib), NULL != hmodule) + loaded_gssapi = TRUE; + } + else + loaded_gssapi = TRUE; + } + } +#endif /* USE_GSS */ else if (0 == _stricmp(pdli->szDll, libarray[0]) || 0 == _stricmp(pdli->szDll, libarray[1])) { @@ -223,6 +250,11 @@ void CleanupDelayLoadedDLLs(void) success = (*func)(pgenlistdll); mylog("%s unload success=%d\n", pgenlistdll, success); } + if (loaded_gssapi) + { + success = (*func)(gssapidll); + mylog("%s unload success=%d\n", gssapidll, success); + } return; } #else @@ -267,7 +299,7 @@ BOOL ssl_verify_available(void) sslverify_available = 0; #else #ifdef HAVE_LIBLTDL - lt_dlhandle dlhandle = lt_dlopenext(libpq); + lt_dlhandle dlhandle = lt_dlopenext(libpqlib); sslverify_available = 1; if (NULL != dlhandle) @@ -382,9 +414,9 @@ BOOL SSLLIB_check() mylog("checking libpq library\n"); /* First search the driver's folder */ #ifndef NOT_USE_LIBPQ - if (NULL == (hmodule = MODULE_load_from_psqlodbc_path(libpq))) + if (NULL == (hmodule = MODULE_load_from_psqlodbc_path(libpqlib))) /* Second try the PATH ordinarily */ - hmodule = LoadLibrary(libpq); + hmodule = LoadLibrary(libpqlib); mylog("libpq hmodule=%p\n", hmodule); #endif /* NOT_USE_LIBPQ */ #ifdef USE_SSPI diff --git a/psqlodbc.h b/psqlodbc.h index 5440ecd..1c806a3 100644 --- a/psqlodbc.h +++ b/psqlodbc.h @@ -5,7 +5,7 @@ * * Comments: See "notice.txt" for copyright and license information. * - * $Id: psqlodbc.h,v 1.132 2009/10/25 13:36:31 hinoue Exp $ + * $Id: psqlodbc.h,v 1.133 2010/01/17 13:10:41 hinoue Exp $ * */ @@ -102,7 +102,10 @@ typedef UInt4 OID; #define FORMAT_UINT4 "%u" /* UInt4 */ #ifdef WIN32 +#ifndef SSIZE_T_DEFINED #define ssize_t SSIZE_T +#define SSIZE_T_DEFINED +#endif #define FORMAT_SIZE_T "%Iu" /* size_t */ #define FORMAT_SSIZE_T "%Id" /* ssize_t */ #define FORMAT_INTEGER "%ld" /* SQLINTEGER */ diff --git a/psqlodbc.rc b/psqlodbc.rc index 1ac1c6d..3cafa66 100644 --- a/psqlodbc.rc +++ b/psqlodbc.rc @@ -199,6 +199,8 @@ BEGIN PUSHBUTTON "“K—p",IDAPPLY,128,224,50,14 CONTROL "bytea‚ðLO‚Æ‚µ‚Ĉµ‚¤",DS_BYTEAASLONGVARBINARY,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,15,85,87,10 + CONTROL "Kerberos‰ž“š‚ÉGSSAPI—˜—p",DS_GSSAUTHUSEGSSAPI,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,139,85,110,10 END DLG_OPTIONS_GLOBAL DIALOG DISCARDABLE 0, 0, 306, 115 @@ -547,6 +549,8 @@ BEGIN BS_AUTOCHECKBOX | WS_TABSTOP,163,71,90,10 CONTROL "bytea as LO",DS_BYTEAASLONGVARBINARY,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,16,84,87,10 + CONTROL "use gssapi for GSS request",DS_GSSAUTHUSEGSSAPI,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,163,84,110,10 GROUPBOX "Int8 As",IDC_STATIC,5,97,256,25 CONTROL "default",DS_INT8_AS_DEFAULT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,12,107,40,10 diff --git a/resource.h b/resource.h index 1239fac..a2fc09c 100644 --- a/resource.h +++ b/resource.h @@ -97,6 +97,7 @@ #define DS_EXTRA_OPTIONS 1084 #define IDC_TEST 1085 #define DS_LOGDIR 1086 +#define DS_GSSAUTHUSEGSSAPI 1087 // Next default values for new objects // @@ -104,7 +105,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1087 +#define _APS_NEXT_CONTROL_VALUE 1088 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/socket.c b/socket.c index 2e3e1cc..f5d17b5 100644 --- a/socket.c +++ b/socket.c @@ -17,6 +17,9 @@ #ifdef USE_SSPI #include "sspisvcs.h" #endif /* USE_SSPI */ +#ifdef USE_GSS +#include "gsssvcs.h" +#endif /* USE_GSS */ #ifndef NOT_USE_LIBPQ #include #ifdef USE_SSL @@ -84,6 +87,10 @@ SOCK_Constructor(const ConnectionClass *conn) rv->ssd = NULL; rv->sspisvcs = 0; #endif /* USE_SSPI */ +#ifdef USE_GSS + rv->gctx = NULL; + rv->gtarg_nam = NULL; +#endif /* USE_GSS */ #ifndef NOT_USE_LIBPQ rv->via_libpq = FALSE; #ifdef USE_SSL @@ -156,12 +163,15 @@ SOCK_Destructor(SocketClass *self) #ifdef USE_SSPI if (self->ssd) { - ReleaseSvcSpecData(self); + ReleaseSvcSpecData(self, (UInt4) -1); free(self->ssd); self->ssd = NULL; } self->sspisvcs = 0; #endif /* USE_SSPI */ +#ifdef USE_GSS + pg_GSS_cleanup(self); +#endif /* USE_GSS */ } if (self->buffer_in) diff --git a/socket.h b/socket.h index 0cf7d47..1559968 100644 --- a/socket.h +++ b/socket.h @@ -10,6 +10,13 @@ #define __SOCKET_H__ #include "psqlodbc.h" +#if defined (USE_GSS) +#ifdef HAVE_GSSAPI_H +#include +#else +#include +#endif +#endif #include #ifndef WIN32 @@ -160,10 +167,13 @@ struct SocketClass_ void *pqconn; /* libpq PGConn */ BOOL via_libpq; /* using libpq library ? */ #endif /* NOT_USE_LIBPQ */ +#ifdef USE_GSS + gss_ctx_id_t gctx; /* GSS context */ + gss_name_t gtarg_nam; /* GSS target name */ +#endif /* USE_GSS */ - char reverse; /* used to handle Postgres 6.2 protocol - * (reverse byte order) */ - + char reverse; /* used to handle Postgres 6.2 protocol + * (reverse byte order) */ }; #define SOCK_get_char(self) (SOCK_get_next_byte(self, FALSE)) diff --git a/sspisvcs.c b/sspisvcs.c index 3ac01d4..e13a3ce 100644 --- a/sspisvcs.c +++ b/sspisvcs.c @@ -122,21 +122,121 @@ static int recvall(SOCKET sock, void *buf, int len) return ttllen; } -CSTR SCHANNEL = "sChannel"; -static int DoSchannelNegotiation(SocketClass *, const char *opt); -int StartupSspiService(SocketClass *self, SSPI_Service svc, const char *opt) +/* + * service specific data + */ + +/* Schannel specific data */ +typedef struct { + CredHandle hCred; + CtxtHandle hCtxt; + PBYTE ioovrbuf; + size_t ioovrlen; + PBYTE iobuf; + size_t iobuflen; + size_t ioread; +} SchannelSpec; + +/* Kerberos/Negotiate common specific data */ +typedef struct { + LPTSTR svcprinc; + CredHandle hKerbEtcCred; + BOOL ValidCtxt; + CtxtHandle hKerbEtcCtxt; +} KerberosEtcSpec; + +typedef struct { + SchannelSpec sdata; + KerberosEtcSpec kdata; +} SspiData; + +static int DoSchannelNegotiation(SocketClass *, SspiData *, const void *opt); +static int DoKerberosNegotiation(SocketClass *, SspiData *, const void *opt); +static int DoNegotiateNegotiation(SocketClass *, SspiData *, const void *opt); +static int DoKerberosEtcProcessAuthentication(SocketClass *, const void *opt); + +static SspiData *SspiDataAlloc(SocketClass *self) +{ + SspiData *sspidata; + + if (sspidata = self->ssd, !sspidata) + sspidata = calloc(sizeof(SspiData), 1); + return sspidata; +} + +int StartupSspiService(SocketClass *self, SSPI_Service svc, const void *opt) { CSTR func = "DoServicelNegotiation"; + SspiData *sspidata; + if (NULL == (sspidata = SspiDataAlloc(self))) + return -1; switch (svc) { case SchannelService: - return DoSchannelNegotiation(self, opt); + return DoSchannelNegotiation(self, sspidata, opt); + case KerberosService: + return DoKerberosNegotiation(self, sspidata, opt); + case NegotiateService: + return DoNegotiateNegotiation(self, sspidata, opt); } + free(sspidata); return -1; } +int ContinueSspiService(SocketClass *self, SSPI_Service svc, const void *opt) +{ + CSTR func = "ContinueSspiService"; + + switch (svc) + { + case KerberosService: + case NegotiateService: + return DoKerberosEtcProcessAuthentication(self, opt); + } + + return -1; +} + +static BOOL format_sspierr(char *errmsg, size_t buflen, SECURITY_STATUS r, const char *cmd, const char *cmd2) +{ + BOOL ret = FALSE; + + if (!cmd2) + cmd2 = ""; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, + r, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + errmsg, (DWORD)buflen, NULL)) + ret = TRUE; + if (ret) + { + size_t tlen = strlen(errmsg); + errmsg += tlen; + buflen -= tlen; + snprintf(errmsg, buflen, " in %s:%s", cmd, cmd2); + } + else + snprintf(errmsg, buflen, "%s:%s failed ", cmd, cmd2); + return ret; +} + +static void SSPI_set_error(SocketClass *s, SECURITY_STATUS r, const char *cmd, const char *cmd2) +{ + int gerrno = SOCK_ERRNO; + char emsg[256]; + + format_sspierr(emsg, sizeof(emsg), r, cmd, cmd2); + s->errornumber = r; + if (NULL != s->_errormsg_) + free(s->_errormsg_); + if (NULL != emsg) + s->_errormsg_ = strdup(emsg); + else + s->_errormsg_ = NULL; + mylog("(%d)%s ERRNO=%d\n", r, emsg, gerrno); +} + /* * Stuff for Schannel service */ @@ -145,16 +245,6 @@ int StartupSspiService(SocketClass *self, SSPI_Service svc, const char *opt) #define UNI_SCHANNEL TEXT("sChannel") #define IO_BUFFER_SIZE 0x10000 -typedef struct { - CredHandle hCred; - CtxtHandle hCtxt; - PBYTE ioovrbuf; - size_t ioovrlen; - PBYTE iobuf; - size_t iobuflen; - size_t ioread; -} SchannelSpec; - static SECURITY_STATUS CreateSchannelCredentials(LPCTSTR, LPSTR, PCredHandle); static SECURITY_STATUS PerformSchannelClientHandshake(SOCKET, PCredHandle, LPSTR, CtxtHandle *, SecBuffer *); static SECURITY_STATUS SchannelClientHandshakeLoop(SOCKET, PCredHandle, CtxtHandle *, BOOL, SecBuffer *); @@ -162,26 +252,26 @@ static void GetNewSchannelClientCredentials(PCredHandle, CtxtHandle *); static HCERTSTORE hMyCertStore = NULL; -static int DoSchannelNegotiation(SocketClass *self, const char *opt) +static int DoSchannelNegotiation(SocketClass *self, SspiData *sspidata, const void *opt) { CSTR func = "DoSchannelNegotiation"; + SECURITY_STATUS r = SEC_E_OK; + const char *cmd = NULL; SecBuffer ExtraData; BOOL ret = 0, cCreds = FALSE, cCtxt = FALSE; - SchannelSpec *ssd = NULL; + SchannelSpec *ssd = &(sspidata->sdata); - if (NULL == (ssd = calloc(sizeof(SchannelSpec), 1))) - { - return 0; - } - if (SEC_E_OK != CreateSchannelCredentials(NULL, NULL, &ssd->hCred)) + if (SEC_E_OK != (r = CreateSchannelCredentials(NULL, NULL, &ssd->hCred))) { - mylog("%s:CreateSchannel failed\n", func); + cmd = "CreateSchannelCredentials"; + mylog("%s:%s failed\n", func, cmd); goto cleanup; } cCreds = TRUE; - if (SEC_E_OK != PerformSchannelClientHandshake(self->socket, &ssd->hCred, NULL, &ssd->hCtxt, &ExtraData)) + if (SEC_E_OK != (r = PerformSchannelClientHandshake(self->socket, &ssd->hCred, NULL, &ssd->hCtxt, &ExtraData))) { - mylog("%s:PerformSchannelClientHandshake failed\n", func); + cmd = "PerformSchannelClientHandshake"; + mylog("%s:%s failed\n", func, cmd); goto cleanup; } cCtxt = TRUE; @@ -198,17 +288,19 @@ cleanup: if (ret) { self->sspisvcs |= SchannelService; - self->ssd = ssd; + self->ssd = sspidata; } else { + SSPI_set_error(self, r, __FUNCTION__, cmd); if (cCreds) FreeCredentialHandle(&ssd->hCred); if (cCtxt) DeleteSecurityContext(&ssd->hCtxt); if (ssd->iobuf) free(ssd->iobuf); - free(ssd); + if (!self->ssd) + free(sspidata); } return ret; } @@ -303,15 +395,15 @@ CreateSchannelCredentials( */ Status = AcquireCredentialsHandle( - NULL, /* Name of principal */ - UNI_SCHANNEL, /* Name of package */ - SECPKG_CRED_OUTBOUND, /* Flags indicating use */ - NULL, /* Pointer to logon ID */ - &SchannelCred, /* Package specific data */ - NULL, /* Pointer to GetKey() func */ - NULL, /* Value to pass to GetKey() */ - phCreds, /* (out) Cred Handle */ - &tsExpiry); /* (out) Lifetime (optional) */ + NULL, /* Name of principal */ + UNI_SCHANNEL, /* Name of package */ + SECPKG_CRED_OUTBOUND, /* Flags indicating use */ + NULL, /* Pointer to logon ID */ + &SchannelCred, /* Package specific data */ + NULL, /* Pointer to GetKey() func */ + NULL, /* Value to pass to GetKey() */ + phCreds, /* (out) Cred Handle */ + &tsExpiry); /* (out) Lifetime (optional) */ if (Status != SEC_E_OK) { mylog("**** Error 0x%p returned by AcquireCredentialsHandle\n", Status); @@ -385,7 +477,7 @@ PerformSchannelClientHandshake( if (scRet != SEC_I_CONTINUE_NEEDED) { - mylog("**** Error %d returned by InitializeSecurityContext (1)\n", scRet); + mylog("**** Error %x returned by InitializeSecurityContext (1)\n", scRet); return scRet; } @@ -397,7 +489,7 @@ PerformSchannelClientHandshake( OutBuffers[0].cbBuffer); if (cbData <= 0) { - mylog("**** Error %d sending data to server (1)\n", SOCK_ERRNO); + mylog("**** Error %x sending data to server\n", SOCK_ERRNO); FreeContextBuffer(OutBuffers[0].pvBuffer); DeleteSecurityContext(phContext); return SEC_E_INTERNAL_ERROR; @@ -496,6 +588,7 @@ SchannelClientHandshakeLoop( continue; default: scRet = SEC_E_INTERNAL_ERROR; + SOCK_ERRNO_SET(gerrno); break; } break; @@ -830,6 +923,252 @@ GetNewSchannelClientCredentials( } } +/* + * Stuff for Kerberos etc service + */ +#define UNI_KERBEROS TEXT("Kerberos") +#define UNI_NEGOTIATE TEXT("Negotiate") +#define IO_BUFFER_SIZE 0x10000 + + +static SECURITY_STATUS CreateKerberosEtcCredentials(LPCTSTR, SEC_CHAR *, LPCTSTR, PCredHandle); +static SECURITY_STATUS PerformKerberosEtcClientHandshake(SocketClass *, KerberosEtcSpec *ssd, size_t); + +static int DoKerberosNegotiation(SocketClass *self, SspiData *sspidata, const void *opt) +{ + CSTR func = "DoKerberosNegotiation"; + SECURITY_STATUS r = SEC_E_OK; + const char * cmd = NULL; + BOOL ret = 0; + KerberosEtcSpec *ssd = &(sspidata->kdata); + +mylog("!!! %s in\n", __FUNCTION__); + if (SEC_E_OK != (r = CreateKerberosEtcCredentials(NULL, UNI_KERBEROS, (LPCTSTR) opt, &ssd->hKerbEtcCred))) + { + cmd = "CreateKerberosCredentials"; + mylog("%s:%s failed\n", func, cmd); + SSPI_set_error(self, r, __FUNCTION__, cmd); + return 0; + } +mylog("!!! CreateKerberosCredentials passed\n"); + + ssd->svcprinc = (LPTSTR) opt; + self->sspisvcs |= KerberosService; + self->ssd = sspidata; + return DoKerberosEtcProcessAuthentication(self, NULL); +} + +static int DoNegotiateNegotiation(SocketClass *self, SspiData *sspidata, const void *opt) +{ + CSTR func = "DoNegotiateNegotiation"; + SECURITY_STATUS r = SEC_E_OK; + const char * cmd = NULL; + BOOL ret = 0; + KerberosEtcSpec *ssd = &(sspidata->kdata); + +mylog("!!! %s in\n", __FUNCTION__); + if (SEC_E_OK != (r = CreateKerberosEtcCredentials(NULL, UNI_NEGOTIATE, (LPCTSTR) opt, &ssd->hKerbEtcCred))) + { + cmd = "CreateNegotiateCredentials"; + mylog("%s:%s failed\n", func, cmd); + SSPI_set_error(self, r, __FUNCTION__, cmd); + return 0; + } +mylog("!!! CreateNegotiateCredentials passed\n"); + + ssd->svcprinc = (LPTSTR) opt; + self->sspisvcs |= NegotiateService; + self->ssd = sspidata; + return DoKerberosEtcProcessAuthentication(self, NULL); +} + +static int DoKerberosEtcProcessAuthentication(SocketClass *self, const void *opt) +{ + CSTR func = "DoKerberosEtcProcessAuthentication"; + SECURITY_STATUS r = SEC_E_OK; + const char * cmd = NULL; + BOOL ret = 0, cCtxt = FALSE; + KerberosEtcSpec *ssd; + +mylog("!!! %s in\n", __FUNCTION__); + ssd = &(((SspiData *)(self->ssd))->kdata); + if (SEC_E_OK != (r = PerformKerberosEtcClientHandshake(self, ssd, (size_t) opt))) + { + cmd = "PerformKerberosEtcClientHandshake"; + mylog("%s:%s failed\n", func, cmd); + goto cleanup; + } +mylog("!!! PerformKerberosEtcClientHandshake passed\n"); + cCtxt = TRUE; + ret = TRUE; +cleanup: + if (!ret) + { + SSPI_set_error(self, r, __FUNCTION__, cmd); + FreeCredentialHandle(&ssd->hKerbEtcCred); + if (cCtxt) + { + DeleteSecurityContext(&ssd->hKerbEtcCtxt); + } + self->sspisvcs &= (~(KerberosService | NegotiateService)); + } + return ret; +} + +static +SECURITY_STATUS +CreateKerberosEtcCredentials( + LPCTSTR opt, /* in */ + SEC_CHAR *packname, /* in */ + LPCTSTR pszUserName, /* in */ + PCredHandle phCreds) /* out */ +{ + TimeStamp tsExpiry; + SECURITY_STATUS Status; + + /* + * Create an SSPI credential. + */ + + Status = AcquireCredentialsHandle( + NULL, /* Name of principal */ + packname, /* Name of package */ + SECPKG_CRED_OUTBOUND, /* Flags indicating use */ + NULL, /* Pointer to logon ID */ + NULL, /* Package specific data */ + NULL, /* Pointer to GetKey() func */ + NULL, /* Value to pass to GetKey() */ + phCreds, /* (out) Cred Handle */ + &tsExpiry); /* (out) Lifetime (optional) */ + if (Status != SEC_E_OK) + { + mylog("**** Error 0x%p returned by AcquireCredentialsHandle\n", Status); + goto cleanup; + } + +cleanup: + + return Status; +} + +static +SECURITY_STATUS +PerformKerberosEtcClientHandshake( + SocketClass *sock, /* in */ + KerberosEtcSpec *ssd, /* i-o */ + size_t inlen) +{ + SecBufferDesc InBuffer; + SecBuffer InBuffers[1]; + SecBufferDesc OutBuffer; + SecBuffer OutBuffers[1]; + DWORD dwSSPIFlags; + DWORD dwSSPIOutFlags; + TimeStamp tsExpiry; + SECURITY_STATUS scRet; + CtxtHandle hContext; + PBYTE inbuf = NULL; + +mylog("!!! inlen=%u svcprinc=%s\n", inlen, ssd->svcprinc); + if (ssd->ValidCtxt && inlen > 0) + { + if (NULL == (inbuf = malloc(inlen + 1))) + { + return SEC_E_INTERNAL_ERROR; + } + SOCK_get_n_char(sock, inbuf, inlen); + if (SOCK_get_errcode(sock) != 0) + { + mylog("**** Error %d receiving data from server (1)\n", SOCK_ERRNO); + free(inbuf); + return SEC_E_INTERNAL_ERROR; + } + + InBuffer.ulVersion = SECBUFFER_VERSION; + InBuffer.cBuffers = 1; + InBuffer.pBuffers = InBuffers; + InBuffers[0].pvBuffer = inbuf; + InBuffers[0].cbBuffer = inlen; + InBuffers[0].BufferType = SECBUFFER_TOKEN; + } + + dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM; + + /* + * Initiate a ClientHello message and generate a token. + */ + + OutBuffers[0].pvBuffer = NULL; + OutBuffers[0].BufferType = SECBUFFER_TOKEN; + OutBuffers[0].cbBuffer = 0; + + OutBuffer.cBuffers = 1; + OutBuffer.pBuffers = OutBuffers; + OutBuffer.ulVersion = SECBUFFER_VERSION; + +mylog("!!! before InitializeSecurityContext\n"); + scRet = InitializeSecurityContext( + &ssd->hKerbEtcCred, + ssd->ValidCtxt ? &ssd->hKerbEtcCtxt : NULL, + ssd->svcprinc, + dwSSPIFlags, + 0, + SECURITY_NATIVE_DREP, + ssd->ValidCtxt ? &InBuffer : NULL, + 0, + &hContext, + &OutBuffer, + &dwSSPIOutFlags, + &tsExpiry); +mylog("!!! %s:InitializeSecurityContext ret=%x\n", __FUNCTION__, scRet); + + if (inbuf) + free(inbuf); + if (SEC_E_OK != scRet && SEC_I_CONTINUE_NEEDED != scRet) + { + mylog("**** Error %x returned by InitializeSecurityContext\n", scRet); + return scRet; + } + if (!ssd->ValidCtxt) + { + memcpy(&ssd->hKerbEtcCtxt, &hContext, sizeof(CtxtHandle)); + ssd->ValidCtxt = TRUE; + } + +mylog("!!! cbBuffer=%d pvBuffer=%p\n", OutBuffers[0].cbBuffer, OutBuffers[0].pvBuffer); + /* Send response to server if there is one. */ + if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) + { + int reslen = OutBuffers[0].cbBuffer; +mylog("!!! responding 'p' + int(%d) + %dbytes of data\n", reslen + 4, reslen); + SOCK_put_char(sock, 'p'); + SOCK_put_int(sock, reslen + 4, 4); + SOCK_put_n_char(sock, OutBuffers[0].pvBuffer, reslen); + SOCK_flush_output(sock); + if (SOCK_get_errcode(sock) != 0) + { + mylog("**** Error %d sending data to server (1)\n", SOCK_ERRNO); + FreeContextBuffer(OutBuffers[0].pvBuffer); + return SEC_E_INTERNAL_ERROR; + } + + mylog("%d bytes of handshake data sent\n", OutBuffers[0].cbBuffer); + + /* Free output buffer. */ + FreeContextBuffer(OutBuffers[0].pvBuffer); + OutBuffers[0].pvBuffer = NULL; + } + + return SEC_E_OK; + // return KerberosEtcClientHandshakeLoop(Socket, ssd, TRUE, pExtraData); +} + + int SSPI_recv(SocketClass *self, void *buffer, int len) { CSTR func = "SSPI_recv"; @@ -847,7 +1186,7 @@ int SSPI_recv(SocketClass *self, void *buffer, int len) DWORD cbIoBuffer, cbIoBufferLength; DWORD cbData; - SchannelSpec *ssd = (SchannelSpec *) self->ssd; + SchannelSpec *ssd = &(((SspiData *)(self->ssd))->sdata); mylog("buflen=%d,%d ovrlen=%d\n", ssd->iobuflen, ssd->ioread, ssd->ioovrlen); if (ssd->ioovrlen > 0) @@ -914,13 +1253,12 @@ mylog("buf=%p read=%d req=%d\n", pbIoBuffer, cbIoBuffer, reqlen); { case EINTR: continue; - case ECONNRESET: - break; case EWOULDBLOCK: retry_count++; if (Socket_wait_for_ready(self->socket, FALSE, retry_count) >= 0) continue; default: + SOCK_ERRNO_SET(gerrno); scRet = SEC_E_INTERNAL_ERROR; break; } @@ -1091,7 +1429,7 @@ int SSPI_send(SocketClass *self, const void *buffer, int len) LPVOID lpTrail; SecBuffer sb[4]; SecBufferDesc sbd; - SchannelSpec *ssd = (SchannelSpec *) self->ssd; + SchannelSpec *ssd = &(((SspiData *)(self->ssd))->sdata); QueryContextAttributes(&ssd->hCtxt, SECPKG_ATTR_STREAM_SIZES, &sizes); slen = len; @@ -1142,13 +1480,13 @@ mylog("EMPTY=%p %d %d\n", sb[3].pvBuffer, sb[3].cbBuffer, sb[3].BufferType); return send(self->socket, (char *) buffer, len, SEND_FLAG); } -void ReleaseSvcSpecData(SocketClass *self) +void ReleaseSvcSpecData(SocketClass *self, UInt4 svc) { if (!self->ssd) return; - if (0 != (self->sspisvcs & SchannelService)) + if (0 != (self->sspisvcs & (svc & SchannelService))) { - SchannelSpec *ssd = (SchannelSpec *) self->ssd; + SchannelSpec *ssd = &(((SspiData *)(self->ssd))->sdata); if (ssd->iobuf) { @@ -1164,5 +1502,22 @@ void ReleaseSvcSpecData(SocketClass *self) DeleteSecurityContext(&ssd->hCtxt); self->sspisvcs &= (~SchannelService); } + if (0 != (self->sspisvcs & (svc & (KerberosService | NegotiateService)))) + { + KerberosEtcSpec *ssd = &(((SspiData *)(self->ssd))->kdata); + + if (ssd->svcprinc) + { + free(ssd->svcprinc); + ssd->svcprinc = NULL; + } + FreeCredentialHandle(&ssd->hKerbEtcCred); + if (ssd->ValidCtxt) + { + DeleteSecurityContext(&ssd->hKerbEtcCtxt); + ssd->ValidCtxt = FALSE; + } + self->sspisvcs &= (~(KerberosService | NegotiateService)); + } } #endif /* USE_SSPI */ diff --git a/sspisvcs.h b/sspisvcs.h index eeb8754..8a838b8 100755 --- a/sspisvcs.h +++ b/sspisvcs.h @@ -15,10 +15,12 @@ typedef enum { SchannelService = 1L ,KerberosService = (1L << 1) + ,NegotiateService = (1L << 2) } SSPI_Service; -void ReleaseSvcSpecData(SocketClass *self); -int StartupSspiService(SocketClass *self, SSPI_Service svc, const char *opt); +void ReleaseSvcSpecData(SocketClass *self, UInt4); +int StartupSspiService(SocketClass *self, SSPI_Service svc, const void *opt); +int ContinueSspiService(SocketClass *self, SSPI_Service svc, const void *opt); int SSPI_recv(SocketClass *self, void *buf, int len); int SSPI_send(SocketClass *self, const void *buf, int len); diff --git a/statement.c b/statement.c index 0e1e342..a670e05 100644 --- a/statement.c +++ b/statement.c @@ -852,7 +852,7 @@ inolog("%s statement=%p ommitted=0\n", func, self); } /* - * Scan the query wholly or partially (if specifed next_cmd). + * Scan the query wholly or partially (if the next_cmd param specified). * Also count the number of parameters respectviely. */ void @@ -867,6 +867,7 @@ SC_scanQueryAndCountParams(const char *query, const ConnectionClass *conn, char tchar, bchar, escape_in_literal = '\0'; char in_literal = FALSE, in_identifier = FALSE, in_dollar_quote = FALSE, in_escape = FALSE, + in_line_comment = FALSE, in_comment = FALSE, del_found = FALSE; po_ind_t multi = FALSE; SQLSMALLINT num_p; @@ -926,6 +927,20 @@ SC_scanQueryAndCountParams(const char *query, const ConnectionClass *conn, if (tchar == identifier_quote) in_identifier = FALSE; } + else if (in_line_comment) + { + if (PG_LINEFEED == tchar) + in_line_comment = FALSE; + } + else if (in_comment) + { + if ('*' == tchar && '/' == sptr[1]) + { + encoded_nextchar(&encstr); + sptr++; + in_comment = FALSE; + } + } else { if (tchar == '?') @@ -968,6 +983,24 @@ SC_scanQueryAndCountParams(const char *query, const ConnectionClass *conn, } else if (tchar == identifier_quote) in_identifier = TRUE; + else if ('-' == tchar) + { + if ('-' == sptr[1]) + { + encoded_nextchar(&encstr); + sptr++; + in_line_comment = TRUE; + } + } + else if ('/' == tchar) + { + if ('*' == sptr[1]) + { + encoded_nextchar(&encstr); + sptr++; + in_comment = TRUE; + } + } if (!isspace(tchar)) bchar = tchar; } @@ -2476,7 +2509,11 @@ inolog("!![%d].PGType %u->%u\n", i, PIC_get_pgtype(ipdopts->parameters[i]), CI_g break; case 'B': /* Binary data */ case 'D': /* ASCII data */ - QR_get_tupledata(res, id == 'B'); + if (!QR_get_tupledata(res, id == 'B')) + { + rcvend = TRUE; + res = NULL; + } break; case 'S': /* parameter status */ getParameterValues(conn); diff --git a/version.h b/version.h index e98843e..5abc975 100644 --- a/version.h +++ b/version.h @@ -9,9 +9,9 @@ #ifndef __VERSION_H__ #define __VERSION_H__ -#define POSTGRESDRIVERVERSION "08.04.0200" -#define POSTGRES_RESOURCE_VERSION "08.04.0200\0" -#define PG_DRVFILE_VERSION 8,4,02,00 -#define PG_BUILD_VERSION "200912260001" +#define POSTGRESDRIVERVERSION "08.04.0201" +#define POSTGRES_RESOURCE_VERSION "08.04.0201\0" +#define PG_DRVFILE_VERSION 8,4,02,01 +#define PG_BUILD_VERSION "201001130001" #endif -- 2.39.5