Introduction to Password Verification

The passwordcheck module checks whether the USER’s password conforms to the specified rules during the CREATE ROLE or CREATE USER session. If the password is weak, it will refuse to execute the password during this period and return an error. This module is located in the SRCPKG /contrib directory and after installation is located in the $libdir directory. It takes effect after loading with shared_preload_libraries and restarting the server. In this module, there are mainly two rule judgments, one is the judgment of the user name itself, one is the judgment of the password length less than 8 bits, and one is the judgment of whether the user name itself is included.

Password authentication enhancements

Password verification enhancement is mainly in the original password check module on the basis of the increase of the password contains at least one upper and lower case letter, a number and a special character judgment.

implementation

​

/*-------------------------------------------------------------------------
 *
 * passwordcheck_enchance.c
 *
 * Author: Sungsasong
 *
 * IDENTIFICATION
 *      contrib/passwordcheck_enhance/passwordcheck_enhance.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"
#include <ctype.h>
#ifdef USE_CRACKLIB
#include <crack.h>
#endif
#include "commands/user.h"
#include "catalog/namespace.h"
#include "libpq/crypt.h"
#include "fmgr.h"
#include "utils/guc.h"
#if PG_VERSION_NUM < 100000
#include "libpq/md5.h"
#endif
PG_MODULE_MAGIC;
/* passwords shorter than this will be rejected */
#define MIN_PWD_LENGTH 8
#define MIN_UPPER_LETTER  1
#define MIN_LOWER_LETTER  1
#define MIN_DIGIT_CHAR    1
#define MIN_SPECIAL_CHAR  1
extern void _PG_init(void);
/**********************************************************************
 *Function:passwordcheck_enhance                                      *
 *Verifying the password at least need contains one upper letter,lower* 
 *letter,digit and specital character                                 *
 *********************************************************************/
#if PG_VERSION_NUM >= 100000
 
static void
    check_password(const char *username,
                   const char *shadow_pass,
                   PasswordType password_type,
                   Datum validuntil_time,
                   bool 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 contain 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;
            
int PWD_UPPER_LETTER_COUNT  = 0;            
int PWD_LOWER_LETTER_COUNT  = 0;
int PWD_SPECIAL_CHAR_COUNT  = 0;            
int PWD_DIGIT_COUNT         = 0;            
int PWD_CONTAINS_LETTER_COUNT = 0;               
//如果满足至少8位密码的条件,那么判断密码中是否包含至少一个大小写字母和特殊字符
for(i = 0; i < pwdlen; i++)
                
{                    
/* enforce minimum length */
                    
if(pwdlen < MIN_PWD_LENGTH)
                    
{
                       ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("密码长度至少需要 %d 位,并且至少需要包含一个大小写字母和特殊字符 ",MIN_PWD_LENGTH)));
                    
}
                    
//判断是否包含字母
if(isalpha((unsigned char) password[i]))
 {                 
       PWD_CONTAINS_LETTER_COUNT++;                        
       if(islower((unsigned char) password[i]))
       {
                            PWD_LOWER_LETTER_COUNT++;
                        }else if(isupper((unsigned char) password[i]))
                        {
                            PWD_UPPER_LETTER_COUNT++;
                        }
                    }else if(isdigit((unsigned char) password[i]))
                    {
                        PWD_DIGIT_COUNT++;
                    }else
                    {
                        PWD_SPECIAL_CHAR_COUNT++;
                    }
                }
   //判断是否至少包含了一个数字,大小写字母和特殊字符
                if(PWD_LOWER_LETTER_COUNT < MIN_LOWER_LETTER)
                {
                    ereport(ERROR,
                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                            errmsg("密码至少需要包含 %d 个小写字母",
                                   MIN_LOWER_LETTER)));
                }else if(PWD_UPPER_LETTER_COUNT < MIN_UPPER_LETTER)
                {
                    ereport(ERROR,
                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                            errmsg("密码至少需要包含 %d 个大写字母",
                                   MIN_UPPER_LETTER))); 
               }else if(PWD_DIGIT_COUNT < MIN_DIGIT_CHAR)
                {
                    ereport(ERROR,
                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                            errmsg("密码至少需要包含 %d 个数字",
                                   MIN_DIGIT_CHAR)));
                }else if(PWD_SPECIAL_CHAR_COUNT < MIN_SPECIAL_CHAR)
                {
                    ereport(ERROR,
                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                            errmsg("密码至少需要包含 %d 个特殊字符",
                                   MIN_DIGIT_CHAR)));                       
                }



            /* check if the password contains the username */
            if (strstr(password, username))
            {
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("密码不能与用户名同名")));
            }
        }
 /* all checks passed, password is ok */
    }
#else
    static void
    check_password(const char *username,
                   const char *password,
                   int password_type,
                   Datum validuntil_time,
                   bool validuntil_null)
    {
        int            namelen = strlen(username);
        int            pwdlen = strlen(password);
        char           encrypted[MD5_PASSWD_LEN + 1];
        int            i;
        bool        pwd_has_letter,
                    pwd_has_nonletter;
        int PWD_UPPER_LETTER_COUNT  = 0;
        int PWD_LOWER_LETTER_COUNT  = 0;
        int PWD_SPECIAL_CHAR_COUNT  = 0;
        int PWD_DIGIT_COUNT         = 0;
        int PWD_CONTAINS_LETTER_COUNT = 0;

        switch (password_type)
        {
            case PASSWORD_TYPE_MD5:
                /*
                 * 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.
                 */
  if (!pg_md5_encrypt(username, username, namelen, encrypted))
                {
                    elog(ERROR, "password encryption failed");
                }
                if (strcmp(password, encrypted) == 0)
                {
                    ereport(ERROR,
                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                             errmsg("password must not contain user name")));
                }
                break;

            case PASSWORD_TYPE_PLAINTEXT:
                /*
                 * For unencrypted passwords we can perform better checks
                 */

                /* enforce minimum length */
                //如果满足至少8位密码的条件,那么判断密码中是否包含至少一个大小写字母和特殊字符
                for(i = 0; i < pwdlen; i++)
                {
                    /* enforce minimum length */
                    if(pwdlen < MIN_PWD_LENGTH)
                    {
                       ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("密码长度至少需要 %d 位,并且至少需要包含一个大小写字母和特殊字符 ",MIN_PWD_LENGTH)));
                    }

 //判断是否包含字母
                    if(isalpha((unsigned char) password[i]))
                    {
                        PWD_CONTAINS_LETTER_COUNT++;
                        if(islower((unsigned char) password[i]))
                        {
                            PWD_LOWER_LETTER_COUNT++;
                        }else if(isupper((unsigned char) password[i]))
                        {
                            PWD_UPPER_LETTER_COUNT++;
                        }
                    }else if(isdigit((unsigned char) password[i]))
                    {
                        PWD_DIGIT_COUNT++;
                    }else
                    {
                        PWD_SPECIAL_CHAR_COUNT++;
                    }
                }
 //判断是否至少包含了一个数字,大小写字母和特殊字符
                if(PWD_LOWER_LETTER_COUNT < MIN_LOWER_LETTER)
                {
                    ereport(ERROR,
                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                            errmsg("密码至少需要包含 %d 个小写字母",
                                   MIN_LOWER_LETTER)));
                }else if(PWD_UPPER_LETTER_COUNT < MIN_UPPER_LETTER)
                {
                    ereport(ERROR,
                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                            errmsg("密码至少需要包含 %d 个大写字母",
                                   MIN_UPPER_LETTER))); 
                }else if(PWD_DIGIT_COUNT < MIN_DIGIT_CHAR)
                {
                    ereport(ERROR,
                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                            errmsg("密码至少需要包含 %d 个数字",
                                   MIN_DIGIT_CHAR)));
                }else if(PWD_SPECIAL_CHAR_COUNT < MIN_SPECIAL_CHAR)
                {
                    ereport(ERROR,
                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                            errmsg("密码至少需要包含 %d 个特殊字符",
                                   MIN_DIGIT_CHAR)));                       
                }

            /* check if the password contains the username */
            if (strstr(password, username))
            {
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("密码不能与用户名同名")));
            }
            
                break;

            default:
                elog(ERROR, "unrecognized password type: %d", password_type);
                break;
        }

        /* all checks passed, password is ok */
    }

#endif
/*
 * Module initialization function
 */

void
_PG_init(void)
{
    /* activate password checks when the module is loaded */
    check_password_hook = check_password;
}

​

Get the source code

​

[passwordcheck_enhance](https://github.com/DeveloperHonor/passwordkcheck-enhance-for-postgresql.git "passwordcheck_enhance module")

The installation

* '[postgres@sungsasong contrib]$unzip' * '[postgres@sungsasong contrib]$unzip Passwordkcheck - enhance - for - postgresql - the main ` * to switch to extract directory * * execution make && make install * * in the $PGDATA/postgresql. Auto. Conf or $PGDATA/ postgreSQL. Conf * 'shared_preload_libraries = 'passwordcheck_enhance' * Restart the PostgreSQL server *

validation

postgres=# CREATE USER user_test WITH PASSWORD 'user'; Postgres =# CREATE USER user_test WITH PASSWORD 'useruser'; postgres=# CREATE USER user_test WITH PASSWORD 'useruser'; Postgres =# CREATE USER user_test WITH PASSWORD 'useruseA'; postgres=# CREATE USER user_test WITH PASSWORD 'useruseA' Postgres =# CREATE USER user_test WITH PASSWORD 'useruseA1'; postgres=# CREATE USER user_test WITH PASSWORD 'useruseA1'; Postgres =# CREATE USER user_test WITH PASSWORD 'useruseA1! Postgres =# CREATE USER user_test WITH PASSWORD 'useruseA1! '; CREATE ROLE