diff --git a/dlg_specific.c b/dlg_specific.c index 7f8ad79e..c252ee06 100644 --- a/dlg_specific.c +++ b/dlg_specific.c @@ -687,9 +687,19 @@ copyConnAttributes(ConnInfo *ci, const char *attribute, const char *value) } else if (stricmp(attribute, INI_PQOPT) == 0 || stricmp(attribute, ABBR_PQOPT) == 0) { + char *hide_str = hide_password(value); + NULL_THE_NAME(ci->pqopt); ci->pqopt_in_str = TRUE; ci->pqopt = decode_or_remove_braces(value); + /* + * A pqopt value can embed a libpq 'password=...', so log a copy + * with the password masked and skip the generic logging below. + */ + MYLOG(0, "key='%s' value='%s'\n", attribute, hide_str ? hide_str : ""); + if (hide_str) + free(hide_str); + printed = TRUE; } else if (stricmp(attribute, INI_UPDATABLECURSORS) == 0 || stricmp(attribute, ABBR_UPDATABLECURSORS) == 0) ci->allow_keyset = pg_atoi(value); diff --git a/drvconn.c b/drvconn.c index 58ff2d86..35d6258e 100644 --- a/drvconn.c +++ b/drvconn.c @@ -37,30 +37,8 @@ #include "dlg_specific.h" -#define FORCE_PASSWORD_DISPLAY #define NULL_IF_NULL(a) (a ? a : "(NULL)") -#ifndef FORCE_PASSWORD_DISPLAY -static char * hide_password(const char *str) -{ - char *outstr, *pwdp; - - if (!str) return NULL; - outstr = strdup(str); - if (!outstr) return NULL; - if (pwdp = strstr(outstr, "PWD="), !pwdp) - pwdp = strstr(outstr, "pwd="); - if (pwdp) - { - char *p; - - for (p=pwdp + 4; *p && *p != ';'; p++) - *p = 'x'; - } - return outstr; -} -#endif - /* prototypes */ static BOOL dconn_get_DSN_or_Driver(const char *connect_string, ConnInfo *ci); static BOOL dconn_get_connect_attributes(const char *connect_string, ConnInfo *ci); @@ -308,8 +286,8 @@ MYLOG(DETAIL_LOG_LEVEL, "before CC_connect\n"); char *hide_str = NULL; if (cbConnStrOutMax > 0) - hide_str = hide_password(szConnStrOut); - MYLOG(0, "szConnStrOut = '%s' len=%d,%d\n", NULL_IF_NULL(hide_str), len, cbConnStrOutMax); + hide_str = hide_password((char *) szConnStrOut); + MYLOG(0, "szConnStrOut = '%s' len=" FORMAT_SSIZE_T ",%d\n", NULL_IF_NULL(hide_str), len, cbConnStrOutMax); if (hide_str) free(hide_str); } diff --git a/misc.c b/misc.c index 66a88573..7dd24508 100644 --- a/misc.c +++ b/misc.c @@ -312,3 +312,59 @@ quote_table(const pgNAME schema, const pgNAME table, char *buf, int buf_size) return buf; } + +/* + * Return a malloc'd copy of a connection string with any password value + * masked, so that debug logs (MyLog/CommLog) can be shared without leaking + * the database credentials. Both keywords are matched case-insensitively: + * + * PWD= the ODBC password attribute; its value runs to the next ';' + * password= the libpq password keyword, e.g. inside a pqopt={...} value; + * its value is whitespace-delimited (or single-quoted) and ends + * at the closing brace of the pqopt block. + * + * The caller is responsible for free()ing the returned string. + */ +char * +hide_password(const char *str) +{ + char *outstr, *p; + + if (!str) + return NULL; + outstr = strdup(str); + if (!outstr) + return NULL; + for (p = outstr; *p; ) + { + if (strnicmp(p, "PWD=", 4) == 0) + { + for (p += 4; *p && *p != ';'; p++) + *p = 'x'; + } + else if (strnicmp(p, "password=", 9) == 0) + { + p += 9; + if (*p == '\'') /* libpq single-quoted value */ + { + for (p++; *p && *p != '\''; p++) + { + if (*p == '\\' && p[1]) + *p++ = 'x'; + *p = 'x'; + } + if (*p == '\'') + p++; + } + else + { + for (; *p && *p != ';' && *p != '}' && + *p != ' ' && *p != '\t'; p++) + *p = 'x'; + } + } + else + p++; + } + return outstr; +} diff --git a/misc.h b/misc.h index da8cdfce..ff76d9b6 100644 --- a/misc.h +++ b/misc.h @@ -98,6 +98,12 @@ FUNCTION_BEGIN_MACRO \ FUNCTION_END_MACRO +/* + * Return a malloc'd copy of a connection string with password values masked, + * for safe logging. The caller must free() the result. + */ +char *hide_password(const char *str); + #ifdef __cplusplus } #endif