/*------------------------------------------------------------------------- * * disable_set_password is a PostgreSQL module which forbids usage of * CREATE/ALTER USER ... PASSWORD ... statements. * * This program is open source, licensed under the PostgreSQL license. * For license terms, see the LICENSE file. * * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * Copyright (c) 2026 Pierre Forstmann. * *------------------------------------------------------------------------- */ #include "postgres.h" #include "commands/user.h" #include "tcop/utility.h" #include "utils/guc.h" PG_MODULE_MAGIC; void _PG_init(void); void _PG_fini(void); static void raise_log_min_messages() { /* ** Raise log_min_messages to avoid password display on error in log. ** We use PGC_SUSET because we are not sure current user has been granted ** SET privilege on log_min_messages setting. */ SetConfigOption("log_min_messages", "FATAL", PGC_SUSET, PGC_S_SESSION); } static void check_password_in_statement(bool isCreateRoleStmt, List *options) { ListCell *cell; foreach(cell, options) { DefElem *defel = (DefElem *) lfirst(cell); if (strcmp(defel->defname, "password") == 0) { raise_log_min_messages(); if (isCreateRoleStmt) ereport(ERROR, (errmsg("CREATE USER ... PASSWORD is not allowed with non encrypted password."))); else ereport(ERROR, (errmsg("ALTER USER ... PASSWORD is not allowed with non encrypted password: try \\password."))); } } } static void my_ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *completionTag); static ProcessUtility_hook_type prev_ProcessUtility = NULL; static void my_ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *completionTag) { bool isCreateRoleStmt = false, isAlterRoleStmt = false; CreateRoleStmt *createStmt; AlterRoleStmt *alterStmt; isCreateRoleStmt = pstmt->utilityStmt && IsA(pstmt->utilityStmt, CreateRoleStmt); isAlterRoleStmt = pstmt->utilityStmt && IsA(pstmt->utilityStmt, AlterRoleStmt); if (isCreateRoleStmt) createStmt = (CreateRoleStmt *) pstmt->utilityStmt; if (isAlterRoleStmt) alterStmt = (AlterRoleStmt *) pstmt->utilityStmt; if (isCreateRoleStmt || isAlterRoleStmt) { if ((isCreateRoleStmt && createStmt->options) || (isAlterRoleStmt && alterStmt->options)) { if (isCreateRoleStmt) check_password_in_statement(isCreateRoleStmt, createStmt->options); else check_password_in_statement(isCreateRoleStmt, alterStmt->options); } } if (prev_ProcessUtility) prev_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, completionTag); else standard_ProcessUtility(pstmt, queryString, readOnlyTree,context, params, queryEnv, dest, completionTag); } void _PG_init(void) { prev_ProcessUtility = ProcessUtility_hook; ProcessUtility_hook = my_ProcessUtility; } void _PG_fini(void) { ProcessUtility_hook = prev_ProcessUtility; }