2009-11-19 05:57:56 +08:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* passwordcheck.c
|
|
|
|
*
|
|
|
|
*
|
2011-01-02 02:18:15 +08:00
|
|
|
* Copyright (c) 2009-2011, PostgreSQL Global Development Group
|
2009-11-19 05:57:56 +08:00
|
|
|
*
|
|
|
|
* Author: Laurenz Albe <laurenz.albe@wien.gv.at>
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-21 04:08:53 +08:00
|
|
|
* contrib/passwordcheck/passwordcheck.c
|
2009-11-19 05:57:56 +08:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#ifdef USE_CRACKLIB
|
|
|
|
#include <crack.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "commands/user.h"
|
|
|
|
#include "fmgr.h"
|
|
|
|
#include "libpq/md5.h"
|
|
|
|
|
|
|
|
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
|
|
|
|
/* passwords shorter than this will be rejected */
|
|
|
|
#define MIN_PWD_LENGTH 8
|
|
|
|
|
|
|
|
extern void _PG_init(void);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check_password
|
|
|
|
*
|
|
|
|
* performs checks on an encrypted or unencrypted password
|
|
|
|
* ereport's if not acceptable
|
|
|
|
*
|
|
|
|
* username: name of role being created or changed
|
|
|
|
* password: new password (possibly already encrypted)
|
|
|
|
* password_type: PASSWORD_TYPE_PLAINTEXT or PASSWORD_TYPE_MD5 (there
|
|
|
|
* could be other encryption schemes in future)
|
|
|
|
* validuntil_time: password expiration time, as a timestamptz Datum
|
|
|
|
* validuntil_null: true if password expiration time is NULL
|
|
|
|
*
|
|
|
|
* This sample implementation doesn't pay any attention to the password
|
|
|
|
* expiration time, but you might wish to insist that it be non-null and
|
|
|
|
* not too far in the future.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
check_password(const char *username,
|
|
|
|
const char *password,
|
|
|
|
int password_type,
|
|
|
|
Datum validuntil_time,
|
|
|
|
bool validuntil_null)
|
|
|
|
{
|
|
|
|
int namelen = strlen(username);
|
|
|
|
int pwdlen = strlen(password);
|
|
|
|
char encrypted[MD5_PASSWD_LEN + 1];
|
|
|
|
int i;
|
|
|
|
bool pwd_has_letter,
|
|
|
|
pwd_has_nonletter;
|
|
|
|
|
|
|
|
switch (password_type)
|
|
|
|
{
|
|
|
|
case PASSWORD_TYPE_MD5:
|
2010-02-26 10:01:40 +08:00
|
|
|
|
2009-11-19 05:57:56 +08:00
|
|
|
/*
|
2010-02-26 10:01:40 +08:00
|
|
|
* Unfortunately we cannot perform exhaustive checks on encrypted
|
|
|
|
* passwords - we are restricted to guessing. (Alternatively, we
|
|
|
|
* could insist on the password being presented non-encrypted, but
|
|
|
|
* that has its own security disadvantages.)
|
2009-11-19 05:57:56 +08:00
|
|
|
*
|
|
|
|
* We only check for username = password.
|
|
|
|
*/
|
|
|
|
if (!pg_md5_encrypt(username, username, namelen, encrypted))
|
|
|
|
elog(ERROR, "password encryption failed");
|
|
|
|
if (strcmp(password, encrypted) == 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("password must not contain user name")));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PASSWORD_TYPE_PLAINTEXT:
|
2010-02-26 10:01:40 +08:00
|
|
|
|
2009-11-19 05:57:56 +08:00
|
|
|
/*
|
|
|
|
* For unencrypted passwords we can perform better checks
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* enforce minimum length */
|
|
|
|
if (pwdlen < MIN_PWD_LENGTH)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("password is too short")));
|
|
|
|
|
|
|
|
/* check if the password contains the username */
|
|
|
|
if (strstr(password, username))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("password must not contain user name")));
|
|
|
|
|
|
|
|
/* check if the password contains both letters and non-letters */
|
|
|
|
pwd_has_letter = false;
|
|
|
|
pwd_has_nonletter = false;
|
|
|
|
for (i = 0; i < pwdlen; i++)
|
|
|
|
{
|
|
|
|
/*
|
2010-02-26 10:01:40 +08:00
|
|
|
* isalpha() does not work for multibyte encodings but let's
|
|
|
|
* consider non-ASCII characters non-letters
|
2009-11-19 05:57:56 +08:00
|
|
|
*/
|
|
|
|
if (isalpha((unsigned char) password[i]))
|
|
|
|
pwd_has_letter = true;
|
|
|
|
else
|
|
|
|
pwd_has_nonletter = true;
|
|
|
|
}
|
|
|
|
if (!pwd_has_letter || !pwd_has_nonletter)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
2010-02-26 10:01:40 +08:00
|
|
|
errmsg("password must contain both letters and nonletters")));
|
2009-11-19 05:57:56 +08:00
|
|
|
|
|
|
|
#ifdef USE_CRACKLIB
|
|
|
|
/* call cracklib to check password */
|
|
|
|
if (FascistCheck(password, CRACKLIB_DICTPATH))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("password is easily cracked")));
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized password type: %d", password_type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* all checks passed, password is ok */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Module initialization function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
_PG_init(void)
|
|
|
|
{
|
|
|
|
/* activate password checks when the module is loaded */
|
|
|
|
check_password_hook = check_password;
|
|
|
|
}
|