LCOV - code coverage report
Current view: top level - contrib/passwordcheck - passwordcheck.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 25 29 86.2 %
Date: 2020-12-05 18:06:08 Functions: 3 4 75.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13