From 5e45211a64149b3c659b90ff2de6fa982a5a93ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:17:33 +0200 Subject: Adding upstream version 15.5. Signed-off-by: Daniel Baumann --- contrib/passwordcheck/passwordcheck.c | 150 ++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 contrib/passwordcheck/passwordcheck.c (limited to 'contrib/passwordcheck/passwordcheck.c') diff --git a/contrib/passwordcheck/passwordcheck.c b/contrib/passwordcheck/passwordcheck.c new file mode 100644 index 0000000..9d8c58d --- /dev/null +++ b/contrib/passwordcheck/passwordcheck.c @@ -0,0 +1,150 @@ +/*------------------------------------------------------------------------- + * + * passwordcheck.c + * + * + * Copyright (c) 2009-2022, PostgreSQL Global Development Group + * + * Author: Laurenz Albe + * + * IDENTIFICATION + * contrib/passwordcheck/passwordcheck.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include + +#ifdef USE_CRACKLIB +#include +#endif + +#include "commands/user.h" +#include "fmgr.h" +#include "libpq/crypt.h" + +PG_MODULE_MAGIC; + +/* Saved hook value in case of unload */ +static check_password_hook_type prev_check_password_hook = NULL; + +/* passwords shorter than this will be rejected */ +#define MIN_PWD_LENGTH 8 + +extern void _PG_init(void); + +/* + * check_password + * + * performs checks on an encrypted or unencrypted password + * ereport's if not acceptable + * + * username: name of role being created or changed + * password: new password (possibly already encrypted) + * password_type: PASSWORD_TYPE_* code, to indicate if the password is + * in plaintext or encrypted form. + * validuntil_time: password expiration time, as a timestamptz Datum + * validuntil_null: true if password expiration time is NULL + * + * This sample implementation doesn't pay any attention to the password + * expiration time, but you might wish to insist that it be non-null and + * not too far in the future. + */ +static void +check_password(const char *username, + const char *shadow_pass, + PasswordType password_type, + Datum validuntil_time, + bool validuntil_null) +{ + if (prev_check_password_hook) + prev_check_password_hook(username, shadow_pass, + password_type, validuntil_time, + validuntil_null); + + if (password_type != PASSWORD_TYPE_PLAINTEXT) + { + /* + * Unfortunately we cannot perform exhaustive checks on encrypted + * passwords - we are restricted to guessing. (Alternatively, we could + * insist on the password being presented non-encrypted, but that has + * its own security disadvantages.) + * + * We only check for username = password. + */ + const char *logdetail = NULL; + + if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password must not equal user name"))); + } + else + { + /* + * For unencrypted passwords we can perform better checks + */ + const char *password = shadow_pass; + int pwdlen = strlen(password); + int i; + bool pwd_has_letter, + pwd_has_nonletter; +#ifdef USE_CRACKLIB + const char *reason; +#endif + + /* enforce minimum length */ + if (pwdlen < MIN_PWD_LENGTH) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password is too short"))); + + /* check if the password contains the username */ + if (strstr(password, username)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password must not contain user name"))); + + /* check if the password contains both letters and non-letters */ + pwd_has_letter = false; + pwd_has_nonletter = false; + for (i = 0; i < pwdlen; i++) + { + /* + * isalpha() does not work for multibyte encodings but let's + * consider non-ASCII characters non-letters + */ + if (isalpha((unsigned char) password[i])) + pwd_has_letter = true; + else + pwd_has_nonletter = true; + } + if (!pwd_has_letter || !pwd_has_nonletter) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password must contain both letters and nonletters"))); + +#ifdef USE_CRACKLIB + /* call cracklib to check password */ + if ((reason = FascistCheck(password, CRACKLIB_DICTPATH))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password is easily cracked"), + errdetail_log("cracklib diagnostic: %s", reason))); +#endif + } + + /* all checks passed, password is ok */ +} + +/* + * Module initialization function + */ +void +_PG_init(void) +{ + /* activate password checks when the module is loaded */ + prev_check_password_hook = check_password_hook; + check_password_hook = check_password; +} -- cgit v1.2.3