LCOV - differential code coverage report
Current view: top level - contrib/passwordcheck - passwordcheck.c (source / functions) Coverage Total Hit UBC CBC
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 96.6 % 29 28 1 28
Current Date: 2025-09-06 07:49:51 +0900 Functions: 100.0 % 3 3 3
Baseline: lcov-20250906-005545-baseline Branches: 76.9 % 26 20 6 20
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 100.0 % 4 4 4
(360..) days: 96.0 % 25 24 1 24
Function coverage date bins:
(30,360] days: 100.0 % 1 1 1
(360..) days: 100.0 % 2 2 2
Branch coverage date bins:
(30,360] days: 100.0 % 2 2 2
(360..) days: 75.0 % 24 18 6 18

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * passwordcheck.c
                                  4                 :                :  *
                                  5                 :                :  *
                                  6                 :                :  * Copyright (c) 2009-2025, PostgreSQL Global Development Group
                                  7                 :                :  *
                                  8                 :                :  * Author: Laurenz Albe <laurenz.albe@wien.gv.at>
                                  9                 :                :  *
                                 10                 :                :  * IDENTIFICATION
                                 11                 :                :  *    contrib/passwordcheck/passwordcheck.c
                                 12                 :                :  *
                                 13                 :                :  *-------------------------------------------------------------------------
                                 14                 :                :  */
                                 15                 :                : #include "postgres.h"
                                 16                 :                : 
                                 17                 :                : #include <ctype.h>
                                 18                 :                : #include <limits.h>
                                 19                 :                : 
                                 20                 :                : #ifdef USE_CRACKLIB
                                 21                 :                : #include <crack.h>
                                 22                 :                : #endif
                                 23                 :                : 
                                 24                 :                : #include "commands/user.h"
                                 25                 :                : #include "fmgr.h"
                                 26                 :                : #include "libpq/crypt.h"
                                 27                 :                : 
  164 tgl@sss.pgh.pa.us          28                 :CBC           1 : PG_MODULE_MAGIC_EXT(
                                 29                 :                :                     .name = "passwordcheck",
                                 30                 :                :                     .version = PG_VERSION
                                 31                 :                : );
                                 32                 :                : 
                                 33                 :                : /* Saved hook value */
                                 34                 :                : static check_password_hook_type prev_check_password_hook = NULL;
                                 35                 :                : 
                                 36                 :                : /* GUC variables */
                                 37                 :                : static int  min_password_length = 8;
                                 38                 :                : 
                                 39                 :                : /*
                                 40                 :                :  * check_password
                                 41                 :                :  *
                                 42                 :                :  * performs checks on an encrypted or unencrypted password
                                 43                 :                :  * ereport's if not acceptable
                                 44                 :                :  *
                                 45                 :                :  * username: name of role being created or changed
                                 46                 :                :  * password: new password (possibly already encrypted)
                                 47                 :                :  * password_type: PASSWORD_TYPE_* code, to indicate if the password is
                                 48                 :                :  *          in plaintext or encrypted form.
                                 49                 :                :  * validuntil_time: password expiration time, as a timestamptz Datum
                                 50                 :                :  * validuntil_null: true if password expiration time is NULL
                                 51                 :                :  *
                                 52                 :                :  * This sample implementation doesn't pay any attention to the password
                                 53                 :                :  * expiration time, but you might wish to insist that it be non-null and
                                 54                 :                :  * not too far in the future.
                                 55                 :                :  */
                                 56                 :                : static void
 5771                            57                 :              7 : check_password(const char *username,
                                 58                 :                :                const char *shadow_pass,
                                 59                 :                :                PasswordType password_type,
                                 60                 :                :                Datum validuntil_time,
                                 61                 :                :                bool validuntil_null)
                                 62                 :                : {
 2228 michael@paquier.xyz        63         [ -  + ]:              7 :     if (prev_check_password_hook)
 2228 michael@paquier.xyz        64                 :UBC           0 :         prev_check_password_hook(username, shadow_pass,
                                 65                 :                :                                  password_type, validuntil_time,
                                 66                 :                :                                  validuntil_null);
                                 67                 :                : 
 3139 heikki.linnakangas@i       68         [ +  + ]:CBC           7 :     if (password_type != PASSWORD_TYPE_PLAINTEXT)
                                 69                 :                :     {
                                 70                 :                :         /*
                                 71                 :                :          * Unfortunately we cannot perform exhaustive checks on encrypted
                                 72                 :                :          * passwords - we are restricted to guessing. (Alternatively, we could
                                 73                 :                :          * insist on the password being presented non-encrypted, but that has
                                 74                 :                :          * its own security disadvantages.)
                                 75                 :                :          *
                                 76                 :                :          * We only check for username = password.
                                 77                 :                :          */
 1334 michael@paquier.xyz        78                 :              2 :         const char *logdetail = NULL;
                                 79                 :                : 
 3139 heikki.linnakangas@i       80         [ +  + ]:              2 :         if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK)
                                 81         [ +  - ]:              1 :             ereport(ERROR,
                                 82                 :                :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 83                 :                :                      errmsg("password must not equal user name")));
                                 84                 :                :     }
                                 85                 :                :     else
                                 86                 :                :     {
                                 87                 :                :         /*
                                 88                 :                :          * For unencrypted passwords we can perform better checks
                                 89                 :                :          */
                                 90                 :              5 :         const char *password = shadow_pass;
                                 91                 :              5 :         int         pwdlen = strlen(password);
                                 92                 :                :         int         i;
                                 93                 :                :         bool        pwd_has_letter,
                                 94                 :                :                     pwd_has_nonletter;
                                 95                 :                : #ifdef USE_CRACKLIB
                                 96                 :                :         const char *reason;
                                 97                 :                : #endif
                                 98                 :                : 
                                 99                 :                :         /* enforce minimum length */
  242 nathan@postgresql.or      100         [ +  + ]:              5 :         if (pwdlen < min_password_length)
 3139 heikki.linnakangas@i      101         [ +  - ]:              1 :             ereport(ERROR,
                                102                 :                :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                103                 :                :                      errmsg("password is too short"),
                                104                 :                :                      errdetail("password must be at least \"passwordcheck.min_password_length\" (%d) bytes long",
                                105                 :                :                                min_password_length)));
                                106                 :                : 
                                107                 :                :         /* check if the password contains the username */
                                108         [ +  + ]:              4 :         if (strstr(password, username))
                                109         [ +  - ]:              1 :             ereport(ERROR,
                                110                 :                :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                111                 :                :                      errmsg("password must not contain user name")));
                                112                 :                : 
                                113                 :                :         /* check if the password contains both letters and non-letters */
                                114                 :              3 :         pwd_has_letter = false;
                                115                 :              3 :         pwd_has_nonletter = false;
                                116         [ +  + ]:             50 :         for (i = 0; i < pwdlen; i++)
                                117                 :                :         {
                                118                 :                :             /*
                                119                 :                :              * isalpha() does not work for multibyte encodings but let's
                                120                 :                :              * consider non-ASCII characters non-letters
                                121                 :                :              */
                                122         [ +  + ]:             47 :             if (isalpha((unsigned char) password[i]))
                                123                 :             43 :                 pwd_has_letter = true;
                                124                 :                :             else
                                125                 :              4 :                 pwd_has_nonletter = true;
                                126                 :                :         }
                                127   [ +  -  +  + ]:              3 :         if (!pwd_has_letter || !pwd_has_nonletter)
                                128         [ +  - ]:              1 :             ereport(ERROR,
                                129                 :                :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                130                 :                :                      errmsg("password must contain both letters and nonletters")));
                                131                 :                : 
                                132                 :                : #ifdef USE_CRACKLIB
                                133                 :                :         /* call cracklib to check password */
                                134                 :                :         if ((reason = FascistCheck(password, CRACKLIB_DICTPATH)))
                                135                 :                :             ereport(ERROR,
                                136                 :                :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                137                 :                :                      errmsg("password is easily cracked"),
                                138                 :                :                      errdetail_log("cracklib diagnostic: %s", reason)));
                                139                 :                : #endif
                                140                 :                :     }
                                141                 :                : 
                                142                 :                :     /* all checks passed, password is ok */
 5771 tgl@sss.pgh.pa.us         143                 :              3 : }
                                144                 :                : 
                                145                 :                : /*
                                146                 :                :  * Module initialization function
                                147                 :                :  */
                                148                 :                : void
                                149                 :              1 : _PG_init(void)
                                150                 :                : {
                                151                 :                :     /* Define custom GUC variables. */
  242 nathan@postgresql.or      152                 :              1 :     DefineCustomIntVariable("passwordcheck.min_password_length",
                                153                 :                :                             "Minimum allowed password length.",
                                154                 :                :                             NULL,
                                155                 :                :                             &min_password_length,
                                156                 :                :                             8,
                                157                 :                :                             0, INT_MAX,
                                158                 :                :                             PGC_SUSET,
                                159                 :                :                             GUC_UNIT_BYTE,
                                160                 :                :                             NULL, NULL, NULL);
                                161                 :                : 
                                162                 :              1 :     MarkGUCPrefixReserved("passwordcheck");
                                163                 :                : 
                                164                 :                :     /* activate password checks when the module is loaded */
 2228 michael@paquier.xyz       165                 :              1 :     prev_check_password_hook = check_password_hook;
 5771 tgl@sss.pgh.pa.us         166                 :              1 :     check_password_hook = check_password;
                                167                 :              1 : }
        

Generated by: LCOV version 2.4-beta