LCOV - code coverage report
Current view: top level - contrib/passwordcheck - passwordcheck.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 25 29 86.2 %
Date: 2019-09-19 02:07:14 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-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             : /* 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             : 
      95             :         /* enforce minimum length */
      96           8 :         if (pwdlen < MIN_PWD_LENGTH)
      97           2 :             ereport(ERROR,
      98             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      99             :                      errmsg("password is too short")));
     100             : 
     101             :         /* check if the password contains the username */
     102           6 :         if (strstr(password, username))
     103           2 :             ereport(ERROR,
     104             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     105             :                      errmsg("password must not contain user name")));
     106             : 
     107             :         /* check if the password contains both letters and non-letters */
     108           4 :         pwd_has_letter = false;
     109           4 :         pwd_has_nonletter = false;
     110          86 :         for (i = 0; i < pwdlen; i++)
     111             :         {
     112             :             /*
     113             :              * isalpha() does not work for multibyte encodings but let's
     114             :              * consider non-ASCII characters non-letters
     115             :              */
     116          82 :             if (isalpha((unsigned char) password[i]))
     117          76 :                 pwd_has_letter = true;
     118             :             else
     119           6 :                 pwd_has_nonletter = true;
     120             :         }
     121           4 :         if (!pwd_has_letter || !pwd_has_nonletter)
     122           2 :             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 (FascistCheck(password, CRACKLIB_DICTPATH))
     129             :             ereport(ERROR,
     130             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     131             :                      errmsg("password is easily cracked")));
     132             : #endif
     133             :     }
     134             : 
     135             :     /* all checks passed, password is ok */
     136           4 : }
     137             : 
     138             : /*
     139             :  * Module initialization function
     140             :  */
     141             : void
     142           2 : _PG_init(void)
     143             : {
     144             :     /* activate password checks when the module is loaded */
     145           2 :     prev_check_password_hook = check_password_hook;
     146           2 :     check_password_hook = check_password;
     147           2 : }
     148             : 
     149             : /*
     150             :  * Module unload function
     151             :  */
     152             : void
     153           0 : _PG_fini(void)
     154             : {
     155             :     /* uninstall hook */
     156           0 :     check_password_hook = prev_check_password_hook;
     157           0 : }

Generated by: LCOV version 1.13