Header And Logo

PostgreSQL
| The world's most advanced open source database.

passwordcheck.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * passwordcheck.c
00004  *
00005  *
00006  * Copyright (c) 2009-2013, PostgreSQL Global Development Group
00007  *
00008  * Author: Laurenz Albe <[email protected]>
00009  *
00010  * IDENTIFICATION
00011  *    contrib/passwordcheck/passwordcheck.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include <ctype.h>
00018 
00019 #ifdef USE_CRACKLIB
00020 #include <crack.h>
00021 #endif
00022 
00023 #include "commands/user.h"
00024 #include "fmgr.h"
00025 #include "libpq/md5.h"
00026 
00027 
00028 PG_MODULE_MAGIC;
00029 
00030 /* passwords shorter than this will be rejected */
00031 #define MIN_PWD_LENGTH 8
00032 
00033 extern void _PG_init(void);
00034 
00035 /*
00036  * check_password
00037  *
00038  * performs checks on an encrypted or unencrypted password
00039  * ereport's if not acceptable
00040  *
00041  * username: name of role being created or changed
00042  * password: new password (possibly already encrypted)
00043  * password_type: PASSWORD_TYPE_PLAINTEXT or PASSWORD_TYPE_MD5 (there
00044  *          could be other encryption schemes in future)
00045  * validuntil_time: password expiration time, as a timestamptz Datum
00046  * validuntil_null: true if password expiration time is NULL
00047  *
00048  * This sample implementation doesn't pay any attention to the password
00049  * expiration time, but you might wish to insist that it be non-null and
00050  * not too far in the future.
00051  */
00052 static void
00053 check_password(const char *username,
00054                const char *password,
00055                int password_type,
00056                Datum validuntil_time,
00057                bool validuntil_null)
00058 {
00059     int         namelen = strlen(username);
00060     int         pwdlen = strlen(password);
00061     char        encrypted[MD5_PASSWD_LEN + 1];
00062     int         i;
00063     bool        pwd_has_letter,
00064                 pwd_has_nonletter;
00065 
00066     switch (password_type)
00067     {
00068         case PASSWORD_TYPE_MD5:
00069 
00070             /*
00071              * Unfortunately we cannot perform exhaustive checks on encrypted
00072              * passwords - we are restricted to guessing. (Alternatively, we
00073              * could insist on the password being presented non-encrypted, but
00074              * that has its own security disadvantages.)
00075              *
00076              * We only check for username = password.
00077              */
00078             if (!pg_md5_encrypt(username, username, namelen, encrypted))
00079                 elog(ERROR, "password encryption failed");
00080             if (strcmp(password, encrypted) == 0)
00081                 ereport(ERROR,
00082                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00083                          errmsg("password must not contain user name")));
00084             break;
00085 
00086         case PASSWORD_TYPE_PLAINTEXT:
00087 
00088             /*
00089              * For unencrypted passwords we can perform better checks
00090              */
00091 
00092             /* enforce minimum length */
00093             if (pwdlen < MIN_PWD_LENGTH)
00094                 ereport(ERROR,
00095                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00096                          errmsg("password is too short")));
00097 
00098             /* check if the password contains the username */
00099             if (strstr(password, username))
00100                 ereport(ERROR,
00101                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00102                          errmsg("password must not contain user name")));
00103 
00104             /* check if the password contains both letters and non-letters */
00105             pwd_has_letter = false;
00106             pwd_has_nonletter = false;
00107             for (i = 0; i < pwdlen; i++)
00108             {
00109                 /*
00110                  * isalpha() does not work for multibyte encodings but let's
00111                  * consider non-ASCII characters non-letters
00112                  */
00113                 if (isalpha((unsigned char) password[i]))
00114                     pwd_has_letter = true;
00115                 else
00116                     pwd_has_nonletter = true;
00117             }
00118             if (!pwd_has_letter || !pwd_has_nonletter)
00119                 ereport(ERROR,
00120                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00121                 errmsg("password must contain both letters and nonletters")));
00122 
00123 #ifdef USE_CRACKLIB
00124             /* call cracklib to check password */
00125             if (FascistCheck(password, CRACKLIB_DICTPATH))
00126                 ereport(ERROR,
00127                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00128                          errmsg("password is easily cracked")));
00129 #endif
00130             break;
00131 
00132         default:
00133             elog(ERROR, "unrecognized password type: %d", password_type);
00134             break;
00135     }
00136 
00137     /* all checks passed, password is ok */
00138 }
00139 
00140 /*
00141  * Module initialization function
00142  */
00143 void
00144 _PG_init(void)
00145 {
00146     /* activate password checks when the module is loaded */
00147     check_password_hook = check_password;
00148 }