/contrib/passwordcheck/passwordcheck.c
C | 157 lines | 79 code | 17 blank | 61 comment | 12 complexity | 86a9aea4e63d3eea305d02ef70926104 MD5 | raw file
Possible License(s): AGPL-3.0
- /*-------------------------------------------------------------------------
- *
- * passwordcheck.c
- *
- *
- * Copyright (c) 2009-2020, PostgreSQL Global Development Group
- *
- * Author: Laurenz Albe <laurenz.albe@wien.gv.at>
- *
- * IDENTIFICATION
- * contrib/passwordcheck/passwordcheck.c
- *
- *-------------------------------------------------------------------------
- */
- #include "postgres.h"
- #include <ctype.h>
- #ifdef USE_CRACKLIB
- #include <crack.h>
- #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);
- extern void _PG_fini(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.
- */
- char *logdetail;
- 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;
- /* 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 (FascistCheck(password, CRACKLIB_DICTPATH))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("password is easily cracked")));
- #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;
- }
- /*
- * Module unload function
- */
- void
- _PG_fini(void)
- {
- /* uninstall hook */
- check_password_hook = prev_check_password_hook;
- }