LCOV - code coverage report
Current view: top level - contrib/passwordcheck - passwordcheck.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 23 23 100.0 %
Date: 2019-06-19 14:06:47 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13