LCOV - differential code coverage report
Current view: top level - contrib/passwordcheck - passwordcheck.c (source / functions) Coverage Total Hit UIC GIC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 96.3 % 27 26 1 20 6 1 20
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 3 3 2 1 2
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * passwordcheck.c
       4                 :  *
       5                 :  *
       6                 :  * Copyright (c) 2009-2023, 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                 : 
      19                 : #ifdef USE_CRACKLIB
      20                 : #include <crack.h>
      21                 : #endif
      22                 : 
      23                 : #include "commands/user.h"
      24                 : #include "fmgr.h"
      25                 : #include "libpq/crypt.h"
      26                 : 
      27 CBC           1 : PG_MODULE_MAGIC;
      28                 : 
      29                 : /* Saved hook value in case of unload */
      30                 : static check_password_hook_type prev_check_password_hook = NULL;
      31                 : 
      32                 : /* passwords shorter than this will be rejected */
      33                 : #define MIN_PWD_LENGTH 8
      34                 : 
      35                 : /*
      36                 :  * check_password
      37                 :  *
      38                 :  * performs checks on an encrypted or unencrypted password
      39                 :  * ereport's if not acceptable
      40                 :  *
      41                 :  * username: name of role being created or changed
      42                 :  * password: new password (possibly already encrypted)
      43                 :  * password_type: PASSWORD_TYPE_* code, to indicate if the password is
      44                 :  *          in plaintext or encrypted form.
      45                 :  * validuntil_time: password expiration time, as a timestamptz Datum
      46                 :  * validuntil_null: true if password expiration time is NULL
      47                 :  *
      48                 :  * This sample implementation doesn't pay any attention to the password
      49                 :  * expiration time, but you might wish to insist that it be non-null and
      50                 :  * not too far in the future.
      51 ECB             :  */
      52                 : static void
      53 GIC           6 : check_password(const char *username,
      54                 :                const char *shadow_pass,
      55                 :                PasswordType password_type,
      56                 :                Datum validuntil_time,
      57 ECB             :                bool validuntil_null)
      58 EUB             : {
      59 GIC           6 :     if (prev_check_password_hook)
      60 UIC           0 :         prev_check_password_hook(username, shadow_pass,
      61                 :                                  password_type, validuntil_time,
      62 ECB             :                                  validuntil_null);
      63                 : 
      64 GIC           6 :     if (password_type != PASSWORD_TYPE_PLAINTEXT)
      65                 :     {
      66                 :         /*
      67                 :          * Unfortunately we cannot perform exhaustive checks on encrypted
      68                 :          * passwords - we are restricted to guessing. (Alternatively, we could
      69                 :          * insist on the password being presented non-encrypted, but that has
      70                 :          * its own security disadvantages.)
      71                 :          *
      72 ECB             :          * We only check for username = password.
      73                 :          */
      74 CBC           2 :         const char *logdetail = NULL;
      75 ECB             : 
      76 GIC           2 :         if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK)
      77               1 :             ereport(ERROR,
      78                 :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      79                 :                      errmsg("password must not equal user name")));
      80                 :     }
      81                 :     else
      82                 :     {
      83                 :         /*
      84 ECB             :          * For unencrypted passwords we can perform better checks
      85                 :          */
      86 GIC           4 :         const char *password = shadow_pass;
      87               4 :         int         pwdlen = strlen(password);
      88                 :         int         i;
      89                 :         bool        pwd_has_letter,
      90                 :                     pwd_has_nonletter;
      91                 : #ifdef USE_CRACKLIB
      92                 :         const char *reason;
      93                 : #endif
      94 ECB             : 
      95                 :         /* enforce minimum length */
      96 GIC           4 :         if (pwdlen < MIN_PWD_LENGTH)
      97               1 :             ereport(ERROR,
      98                 :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      99                 :                      errmsg("password is too short")));
     100 ECB             : 
     101                 :         /* check if the password contains the username */
     102 GIC           3 :         if (strstr(password, username))
     103               1 :             ereport(ERROR,
     104                 :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     105                 :                      errmsg("password must not contain user name")));
     106 ECB             : 
     107                 :         /* check if the password contains both letters and non-letters */
     108 CBC           2 :         pwd_has_letter = false;
     109 GIC           2 :         pwd_has_nonletter = false;
     110              43 :         for (i = 0; i < pwdlen; i++)
     111                 :         {
     112                 :             /*
     113                 :              * isalpha() does not work for multibyte encodings but let's
     114 ECB             :              * consider non-ASCII characters non-letters
     115                 :              */
     116 GIC          41 :             if (isalpha((unsigned char) password[i]))
     117 CBC          38 :                 pwd_has_letter = true;
     118                 :             else
     119               3 :                 pwd_has_nonletter = true;
     120 ECB             :         }
     121 GIC           2 :         if (!pwd_has_letter || !pwd_has_nonletter)
     122               1 :             ereport(ERROR,
     123                 :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     124                 :                      errmsg("password must contain both letters and nonletters")));
     125                 : 
     126                 : #ifdef USE_CRACKLIB
     127                 :         /* call cracklib to check password */
     128                 :         if ((reason = FascistCheck(password, CRACKLIB_DICTPATH)))
     129                 :             ereport(ERROR,
     130                 :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     131                 :                      errmsg("password is easily cracked"),
     132                 :                      errdetail_log("cracklib diagnostic: %s", reason)));
     133                 : #endif
     134                 :     }
     135 ECB             : 
     136                 :     /* all checks passed, password is ok */
     137 GIC           2 : }
     138                 : 
     139                 : /*
     140                 :  * Module initialization function
     141 ECB             :  */
     142                 : void
     143 GIC           1 : _PG_init(void)
     144 ECB             : {
     145                 :     /* activate password checks when the module is loaded */
     146 CBC           1 :     prev_check_password_hook = check_password_hook;
     147 GIC           1 :     check_password_hook = check_password;
     148               1 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a