Commit c34d419f authored by David Coutadeur's avatar David Coutadeur
Browse files

provide ppm v2.2 (#9846)

parent cda12cd4
Pipeline #4339 passed with stage
in 44 minutes and 51 seconds
# CHANGELOG
* 2022-05-17 David Coutadeur <david.coutadeur@gmail.com>
implement a maximum number of characters for each class #18
upgrade documentation for new olcPPolicyCheckModule in OpenLDAP 2.6 #30
Make one unique code of development for 2.5 and 2.6 OpenLDAP versions #35
fix segmentation fault in ppm_test #36
various minor fixes and optimizations
Version 2.2
* 2022-03-22 David Coutadeur <david.coutadeur@gmail.com>
Reject password if it contains tokens from an attribute of the LDAP entry #17
Version 2.1
......
# CONTRIBUTIONS
* 2014 - 2021 - David Coutadeur <david.coutadeur@gmail.com> - maintainer
* 2014 - 2022 - David Coutadeur <david.coutadeur@gmail.com> - maintainer
* 2015 - Daly Chikhaoui - Janua <dchikhaoui@janua.fr> - contribution on RDN checks
* 2017 - tdb - Tim Bishop - contribution on some compilation improvements
......@@ -20,7 +20,7 @@ You can optionally customize some variables if you don't want the default ones:
- etcdir: used to compose default sysconfdir location (defaults to $prefix/etc)
- sysconfdir: where the ppm example policy is to be deployed (defaults to $prefix/$etcdir/$ldap_subdir)
- LDAP_SRC: path to OpenLDAP source directory
- Options in OPTS variable:
- Options in DEFS variable:
CONFIG_FILE: (DEPRECATED) path to a ppm configuration file (see PPM_READ_FILE in ppm.h)
note: ppm configuration now lies into pwdCheckModuleArg password policy attribute
provided example file is only helpful as an example or for testing
......@@ -43,7 +43,7 @@ Here is an illustrative example showing how to overload some options:
```
make clean
make LDAP_SRC=../../.. prefix=/usr/local libdir=/usr/local/lib
make LDAP_SRC=../../.. prefix=/usr/local libdir=/usr/local/lib
make test LDAP_SRC=../../..
make doc prefix=/usr/local
make install prefix=/usr/local libdir=/usr/local/lib
......
......@@ -65,7 +65,7 @@ MDDOC=ppm.md
all: ppm $(TEST)
$(TEST): ppm
$(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(LDFLAGS) $(INCS) $(LDAP_LIBS) -Wl,-rpath=. -o $(TEST) ppm_test.c $(PROGRAMS) $(LDAP_LIBS) $(CRACKLIB)
$(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(LDFLAGS) $(INCS) -Wl,-rpath=. -o $(TEST) ppm_test.c $(PROGRAMS) $(LDAP_LIBS) $(CRACKLIB)
ppm.o:
$(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) -c $(INCS) ppm.c
......
......@@ -6,10 +6,14 @@
/*
password policy module is called with:
password policy module is called with (openldap 2.6):
int check_password (char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
password policy module is called with (openldap 2.5):
int check_password (char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
*pPasswd: new password
**ppErrStr: pointer to the string containing the error message
*ppErrmsg: pointer to a struct berval containing space for an error message of length bv_len
*e: pointer to the current user entry
*pArg: pointer to a struct berval holding the value of pwdCheckModuleArg attr
......@@ -120,11 +124,13 @@ int maxConsPerClass(char *password, char *charClass)
void
storeEntry(char *param, char *value, valueType valType,
char *min, char *minForPoint, conf * fileConf, int *numParam)
char *min, char *minForPoint, char *max, conf * fileConf,
int *numParam)
{
int i = 0;
int iMin;
int iMinForPoint;
int iMax;
if (min == NULL || strcmp(min,"") == 0)
iMin = 0;
else
......@@ -135,6 +141,11 @@ storeEntry(char *param, char *value, valueType valType,
else
iMinForPoint = atoi(minForPoint);
if (max == NULL || strcmp(max,"") == 0)
iMax = 0;
else
iMax = atoi(max);
// First scan parameters
for (i = 0; i < *numParam; i++) {
if ((strlen(param) == strlen(fileConf[i].param))
......@@ -147,6 +158,7 @@ storeEntry(char *param, char *value, valueType valType,
strcpy_safe(fileConf[i].value.sVal, value, VALUE_MAX_LEN);
fileConf[i].min = iMin;
fileConf[i].minForPoint = iMinForPoint;
fileConf[i].max = iMax;
if(valType == typeInt)
ppm_log(LOG_NOTICE, "ppm: Accepted replaced value: %d",
fileConf[i].value.iVal);
......@@ -165,6 +177,7 @@ storeEntry(char *param, char *value, valueType valType,
strcpy_safe(fileConf[i].value.sVal, value, VALUE_MAX_LEN);
fileConf[*numParam].min = iMin;
fileConf[*numParam].minForPoint = iMinForPoint;
fileConf[*numParam].max = iMax;
++(*numParam);
if(valType == typeInt)
ppm_log(LOG_NOTICE, "ppm: Accepted new value: %d",
......@@ -228,7 +241,7 @@ typeParam(char* param)
ppm_log(LOG_NOTICE, "ppm: get line: %s",token);
char *start = token;
char *word, *value;
char *min, *minForPoint;;
char *min, *minForPoint, *max;
while (isspace(*start) && isascii(*start))
start++;
......@@ -262,17 +275,21 @@ typeParam(char* param)
if (minForPoint != NULL)
if (strchr(minForPoint, '\n') != NULL)
strchr(minForPoint, '\n')[0] = '\0';
max = strtok_r(NULL, " \t", &saveptr2);
if (max != NULL)
if (strchr(max, '\n') != NULL)
strchr(max, '\n')[0] = '\0';
nParam = typeParam(word); // search for param in allowedParameters
if (nParam != sAllowedParameters) // param has been found
{
ppm_log(LOG_NOTICE,
"ppm: Param = %s, value = %s, min = %s, minForPoint= %s",
word, value, min, minForPoint);
"ppm: Param = %s, value = %s, min = %s, minForPoint = %s, max = %s",
word, value, min, minForPoint, max);
storeEntry(word, value, allowedParameters[nParam].iType,
min, minForPoint, fileConf, numParam);
min, minForPoint, max, fileConf, numParam);
}
else
{
......@@ -310,7 +327,7 @@ typeParam(char* param)
while (fgets(line, 256, config) != NULL) {
char *start = line;
char *word, *value;
char *min, *minForPoint;;
char *min, *minForPoint, *max;
while (isspace(*start) && isascii(*start))
start++;
......@@ -333,17 +350,21 @@ typeParam(char* param)
if (minForPoint != NULL)
if (strchr(minForPoint, '\n') != NULL)
strchr(minForPoint, '\n')[0] = '\0';
max = strtok(NULL, " \t");
if (max != NULL)
if (strchr(max, '\n') != NULL)
strchr(max, '\n')[0] = '\0';
nParam = typeParam(word); // search for param in allowedParameters
if (nParam != sAllowedParameters) // param has been found
{
ppm_log(LOG_NOTICE,
"ppm: Param = %s, value = %s, min = %s, minForPoint= %s",
word, value, min, minForPoint);
"ppm: Param = %s, value = %s, min = %s, minForPoint = %s, max = %s",
word, value, min, minForPoint, max);
storeEntry(word, value, allowedParameters[nParam].iType,
min, minForPoint, fileConf, numParam);
min, minForPoint, max, fileConf, numParam);
}
else
{
......@@ -360,14 +381,22 @@ typeParam(char* param)
#endif
static int
#if OLDAP_VERSION == 0x0205
realloc_error_message(char **target, int curlen, int nextlen)
#else
realloc_error_message(const char *orig, char **target, int curlen, int nextlen)
#endif
{
if (curlen < nextlen + MEMORY_MARGIN) {
ppm_log(LOG_WARNING,
"ppm: Reallocating szErrStr from %d to %d", curlen,
nextlen + MEMORY_MARGIN);
#if OLDAP_VERSION == 0x0205
ber_memfree(*target);
#else
if (*target != orig)
ber_memfree(*target);
#endif
curlen = nextlen + MEMORY_MARGIN;
*target = (char *) ber_memalloc(curlen);
}
......@@ -511,14 +540,23 @@ containsAttributes(char* passwd, Entry* pEntry, char* checkAttributes)
int
#if OLDAP_VERSION == 0x0205
check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
#else
check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
#endif
{
Entry *pEntry = e;
struct berval *pwdCheckModuleArg = pArg;
#if OLDAP_VERSION == 0x0205
char *szErrStr = (char *) ber_memalloc(MEM_INIT_SZ);
int mem_len = MEM_INIT_SZ;
#else
char *origmsg = ppErrmsg->bv_val;
char *szErrStr = origmsg;
int mem_len = ppErrmsg->bv_len;
#endif
int numParam = 0; // Number of params in current configuration
int useCracklib;
......@@ -554,7 +592,11 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
#else
if ( !pwdCheckModuleArg || !pwdCheckModuleArg->bv_val ) {
ppm_log(LOG_ERR, "ppm: No config provided in pwdCheckModuleArg");
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
#endif
strlen(GENERIC_ERROR));
sprintf(szErrStr, GENERIC_ERROR);
goto fail;
......@@ -564,43 +606,40 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
ppm_log(LOG_NOTICE, "ppm: RAW configuration: %s", pwdCheckModuleArg->bv_val);
#endif
for (i = 0; i < CONF_MAX_SIZE; i++)
nbInClass[i] = 0;
/* Set default values */
conf fileConf[CONF_MAX_SIZE] = {
{"minQuality", typeInt, {.iVal = DEFAULT_QUALITY}, 0, 0
{"minQuality", typeInt, {.iVal = DEFAULT_QUALITY}, 0, 0, 0
}
,
{"checkRDN", typeInt, {.iVal = 0}, 0, 0
{"checkRDN", typeInt, {.iVal = 0}, 0, 0, 0
}
,
{"forbiddenChars", typeStr, {.sVal = ""}, 0, 0
{"forbiddenChars", typeStr, {.sVal = ""}, 0, 0, 0
}
,
{"maxConsecutivePerClass", typeInt, {.iVal = 0}, 0, 0
{"maxConsecutivePerClass", typeInt, {.iVal = 0}, 0, 0, 0
}
,
{"useCracklib", typeInt, {.iVal = 0}, 0, 0
{"useCracklib", typeInt, {.iVal = 0}, 0, 0, 0
}
,
{"cracklibDict", typeStr, {.sVal = "/var/cache/cracklib/cracklib_dict"}, 0, 0
{"cracklibDict", typeStr, {.sVal = "/var/cache/cracklib/cracklib_dict"}, 0, 0, 0
}
,
{"class-upperCase", typeStr, {.sVal = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"}, 0, 1
{"class-upperCase", typeStr, {.sVal = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"}, 0, 1, 0
}
,
{"class-lowerCase", typeStr, {.sVal = "abcdefghijklmnopqrstuvwxyz"}, 0, 1
{"class-lowerCase", typeStr, {.sVal = "abcdefghijklmnopqrstuvwxyz"}, 0, 1, 0
}
,
{"class-digit", typeStr, {.sVal = "0123456789"}, 0, 1
{"class-digit", typeStr, {.sVal = "0123456789"}, 0, 1, 0
}
,
{"class-special", typeStr,
{.sVal = "<>,?;.:/!§ù%*µ^¨$£²&é~\"#'{([-|è`_\\ç^à@)]°=}+"}, 0, 1
{.sVal = "<>,?;.:/!§ù%*µ^¨$£²&é~\"#'{([-|è`_\\ç^à@)]°=}+"}, 0, 1, 0
}
,
{"checkAttributes", typeStr, {.sVal = ""}, 0, 0
{"checkAttributes", typeStr, {.sVal = ""}, 0, 0, 0
}
};
numParam = 11;
......@@ -627,10 +666,14 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
getValue(fileConf, numParam, "checkAttributes")->sVal,
VALUE_MAX_LEN);
for (i = 0; i < numParam; i++)
nbInClass[i] = 0;
/*The password must have at least minQuality strength points with one
* point granted if the password contains at least minForPoint characters for each class
* It must contains at least min chars of each class
* It must contains at most max chars of each class
* It must not contain any char in forbiddenChar */
for (i = 0; i < strlen(pPasswd); i++) {
......@@ -649,7 +692,7 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
}
// Password checking done, now loocking for minForPoint criteria
for (i = 0; i < CONF_MAX_SIZE; i++) {
for (i = 0; i < numParam; i++) {
if (strstr(fileConf[i].param, "class-") != NULL) {
if ((nbInClass[i] >= fileConf[i].minForPoint)
&& strlen(fileConf[i].value.sVal) != 0) {
......@@ -662,33 +705,68 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
}
if (nQuality < minQuality) {
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
#endif
strlen(PASSWORD_QUALITY_SZ) +
strlen(pEntry->e_nname.bv_val) + 4);
sprintf(szErrStr, PASSWORD_QUALITY_SZ, pEntry->e_nname.bv_val,
nQuality, minQuality);
goto fail;
}
// Password checking done, now loocking for constraintClass criteria
for (i = 0; i < CONF_MAX_SIZE; i++) {
// Password checking done, now loocking for minimum criteria
for (i = 0; i < numParam; i++) {
if (strstr(fileConf[i].param, "class-") != NULL) {
if ((nbInClass[i] < fileConf[i].min) &&
strlen(fileConf[i].value.sVal) != 0) {
// constraint is not satisfied... goto fail
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
strlen(PASSWORD_CRITERIA) +
#endif
strlen(PASSWORD_MIN_CRITERIA) +
strlen(pEntry->e_nname.bv_val) +
2 + PARAM_MAX_LEN);
sprintf(szErrStr, PASSWORD_CRITERIA, pEntry->e_nname.bv_val,
sprintf(szErrStr, PASSWORD_MIN_CRITERIA, pEntry->e_nname.bv_val,
fileConf[i].min, fileConf[i].param);
goto fail;
}
}
}
// Password checking done, now loocking for maximum criteria
for (i = 0; i < numParam; i++) {
if (strstr(fileConf[i].param, "class-") != NULL) {
if ( (fileConf[i].max != 0) &&
(nbInClass[i] > fileConf[i].max) &&
strlen(fileConf[i].value.sVal) != 0) {
// constraint is not satisfied... goto fail
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
#endif
strlen(PASSWORD_MAX_CRITERIA) +
strlen(pEntry->e_nname.bv_val) +
2 + PARAM_MAX_LEN);
sprintf(szErrStr, PASSWORD_MAX_CRITERIA, pEntry->e_nname.bv_val,
fileConf[i].max, fileConf[i].param);
goto fail;
}
}
}
// Password checking done, now loocking for forbiddenChars criteria
if (nForbiddenChars > 0) { // at least 1 forbidden char... goto fail
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
#endif
strlen(PASSWORD_FORBIDDENCHARS) +
strlen(pEntry->e_nname.bv_val) + 2 +
VALUE_MAX_LEN);
......@@ -698,7 +776,7 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
}
// Password checking done, now loocking for maxConsecutivePerClass criteria
for (i = 0; i < CONF_MAX_SIZE; i++) {
for (i = 0; i < numParam; i++) {
if (strstr(fileConf[i].param, "class-") != NULL) {
if ( maxConsecutivePerClass != 0 &&
(maxConsPerClass(pPasswd,fileConf[i].value.sVal)
......@@ -706,7 +784,11 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
// Too much consecutive characters of the same class
ppm_log(LOG_NOTICE, "ppm: Too much consecutive chars for class %s",
fileConf[i].param);
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
#endif
strlen(PASSWORD_MAXCONSECUTIVEPERCLASS) +
strlen(pEntry->e_nname.bv_val) + 2 +
PARAM_MAX_LEN);
......@@ -726,7 +808,11 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
if (( fd = fopen ( cracklibDictFiles[j], "r")) == NULL ) {
ppm_log(LOG_NOTICE, "ppm: Error while reading %s file",
cracklibDictFiles[j]);
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
#endif
strlen(GENERIC_ERROR));
sprintf(szErrStr, GENERIC_ERROR);
goto fail;
......@@ -740,7 +826,11 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
if ( res != NULL ) {
ppm_log(LOG_NOTICE, "ppm: cracklib does not validate password for entry %s",
pEntry->e_nname.bv_val);
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
#endif
strlen(PASSWORD_CRACKLIB) +
strlen(pEntry->e_nname.bv_val));
sprintf(szErrStr, PASSWORD_CRACKLIB, pEntry->e_nname.bv_val);
......@@ -755,7 +845,11 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
if (checkRDN == 1 && containsRDN(pPasswd, pEntry->e_nname.bv_val))
// RDN check enabled and a token from RDN is found in password: goto fail
{
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
#endif
strlen(RDN_TOKEN_FOUND) +
strlen(pEntry->e_nname.bv_val));
sprintf(szErrStr, RDN_TOKEN_FOUND, pEntry->e_nname.bv_val);
......@@ -764,10 +858,15 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
}
// Password checking done, now looking for checkAttributes criteria
if (containsAttributes(pPasswd, pEntry, checkAttributes))
if ( strcmp(checkAttributes, "") !=0 &&
containsAttributes(pPasswd, pEntry, checkAttributes))
// A token from an attribute is found in password: goto fail
{
#if OLDAP_VERSION == 0x0205
mem_len = realloc_error_message(&szErrStr, mem_len,
#else
mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
#endif
strlen(ATTR_TOKEN_FOUND) +
strlen(pEntry->e_nname.bv_val));
sprintf(szErrStr, ATTR_TOKEN_FOUND, pEntry->e_nname.bv_val);
......@@ -775,12 +874,22 @@ check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
goto fail;
}
#if OLDAP_VERSION == 0x0205
*ppErrStr = strdup("");
ber_memfree(szErrStr);
#else
szErrStr[0] = '\0';
#endif
return (LDAP_SUCCESS);
fail:
#if OLDAP_VERSION == 0x0205
*ppErrStr = strdup(szErrStr);
ber_memfree(szErrStr);
#else
ppErrmsg->bv_val = szErrStr;
ppErrmsg->bv_len = mem_len;
#endif
return (EXIT_FAILURE);
}
......@@ -15,7 +15,7 @@
# cn: default
# pwdMinLength: 6
# pwdCheckModule: /usr/local/lib/ppm.so
# pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKY2hlY2tBdHRyaWJ1dGVzCmZvcmJpZGRlbkNoYXJzCm1heENvbnNlY3V0aXZlUGVyQ2xhc3MgMAp1c2VDcmFja2xpYiAwCmNyYWNrbGliRGljdCAvdmFyL2NhY2hlL2NyYWNrbGliL2NyYWNrbGliX2RpY3QKY2xhc3MtdXBwZXJDYXNlIEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaIDAgMQpjbGFzcy1sb3dlckNhc2UgYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogMCAxCmNsYXNzLWRpZ2l0IDAxMjM0NTY3ODkgMCAxCmNsYXNzLXNwZWNpYWwgPD4sPzsuOi8hwqfDuSUqwrVewqgkwqPCsibDqX4iIyd7KFstfMOoYF9cw6dew6BAKV3CsD19KyAwIDEK
# pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKY2hlY2tBdHRyaWJ1dGVzCmZvcmJpZGRlbkNoYXJzCm1heENvbnNlY3V0aXZlUGVyQ2xhc3MgMAp1c2VDcmFja2xpYiAwCmNyYWNrbGliRGljdCAvdmFyL2NhY2hlL2NyYWNrbGliL2NyYWNrbGliX2RpY3QKY2xhc3MtdXBwZXJDYXNlIEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaIDAgMSAwCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEgMApjbGFzcy1kaWdpdCAwMTIzNDU2Nzg5IDAgMSAwCmNsYXNzLXNwZWNpYWwgPD4sPzsuOi8hwqfDuSUqwrVewqgkwqPCsibDqX4iIyd7KFstfMOoYF9cw6dew6BAKV3CsD19KyAwIDEgMAo=
#
# Different parameters are separated by a linefeed (\n)
# Parameters starting with a # are ignored
......@@ -90,7 +90,8 @@ cracklibDict /var/cache/cracklib/cracklib_dict
# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
class-digit 0123456789 0 1
class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
# [MAX]: if > [MAX] occurrences of characters from this class are found, then the password is rejected (0 means no maximum)
class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1 0
class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1 0
class-digit 0123456789 0 1 0
class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1 0
......@@ -18,6 +18,11 @@
#include <syslog.h>
#endif
// Get OpenLDAP version
#define OLDAP_VERSION ((LDAP_VENDOR_VERSION_MAJOR << 8) | LDAP_VENDOR_VERSION_MINOR)
// OLDAP_VERSION = 0x0205 // (v2.5)
// OLDAP_VERSION = 0x0206 // (v2.6)
//#define PPM_READ_FILE 1 // old deprecated configuration mode
// 1: (deprecated) don't read pwdCheckModuleArg
// attribute, instead read config file
......@@ -31,6 +36,9 @@
#define DEFAULT_QUALITY 3
#define MEMORY_MARGIN 50
#if OLDAP_VERSION == 0x0205
#define MEM_INIT_SZ 64
#endif
#define DN_MAX_LEN 512
#define CONF_MAX_SIZE 50
......@@ -47,8 +55,10 @@
#define PASSWORD_QUALITY_SZ \
"Password for dn=\"%s\" does not pass required number of strength checks (%d of %d)"
#define PASSWORD_CRITERIA \
#define PASSWORD_MIN_CRITERIA \
"Password for dn=\"%s\" has not reached the minimum number of characters (%d) for class %s"
#define PASSWORD_MAX_CRITERIA \
"Password for dn=\"%s\" has reached the maximum number of characters (%d) for class %s"
#define PASSWORD_MAXCONSECUTIVEPERCLASS \
"Password for dn=\"%s\" has reached the maximum number of characters (%d) for class %s"
#define PASSWORD_FORBIDDENCHARS \
......@@ -104,6 +114,7 @@ typedef struct conf {
genValue value;
int min;
int minForPoint;
int max;
} conf;
void ppm_log(int priority, const char *format, ...);
......@@ -114,10 +125,16 @@ int min(char *str1, char *str2);
#ifdef PPM_READ_FILE
static void read_config_file(conf * fileConf, int *numParam, char *ppm_config_file);
#endif
int check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg);
#if OLDAP_VERSION == 0x0205
int check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg);
#else
int check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg);
#endif
int maxConsPerClass(char *password, char *charClass);
void storeEntry(char *param, char *value, valueType valType,
char *min, char *minForPoint, conf * fileConf, int *numParam);
char *min, char *minForPoint, char *max, conf * fileConf,
int *numParam);
int typeParam(char* param);
genValue* getValue(conf *fileConf, int numParam, char* param);
void strcpy_safe(char *dest, char *src, int length_dest);
......
......@@ -28,7 +28,9 @@ see slapo-ppolicy(5) section **pwdCheckModule**.
Create a password policy entry and indicate the path of the ppm.so library
and the content of the desired policy.
Use a base64 tool to code / decode the content of the policy stored into
**pwdCheckModuleArg**. Here is an example:
**pwdCheckModuleArg**.
Here is an example for OpenLDAP 2.6:
```
dn: cn=default,ou=policies,dc=my-domain,dc=com
......@@ -41,26 +43,35 @@ pwdAttribute: userPassword
sn: default
cn: default
pwdMinLength: 6
pwdCheckModule: /usr/local/lib/ppm.so
pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKY2hlY2tBdHRyaWJ1dGVzCmZvcmJpZGRlbkNoYXJzCm1heENvbnNlY3V0aXZlUGVyQ2xhc3MgMAp1c2VDcmFja2xpYiAwCmNyYWNrbGliRGljdCAvdmFyL2NhY2hlL2NyYWNrbGliL2NyYWNrbGliX2RpY3QKY2xhc3MtdXBwZXJDYXNlIEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaIDAgMQpjbGFzcy1sb3dlckNhc2UgYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogMCAxCmNsYXNzLWRpZ2l0IDAxMjM0NTY3ODkgMCAxCmNsYXNzLXNwZWNpYWwgPD4sPzsuOi8hwqfDuSUqwrVewqgkwqPCsibDqX4iIyd7KFstfMOoYF9cw6dew6BAKV3CsD19KyAwIDEK
pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKY2hlY2tBdHRyaWJ1dGVzCmZvcmJpZGRlbkNoYXJzCm1heENvbnNlY3V0aXZlUGVyQ2xhc3MgMAp1c2VDcmFja2xpYiAwCmNyYWNrbGliRGljdCAvdmFyL2NhY2hlL2NyYWNrbGliL2NyYWNrbGliX2RpY3QKY2xhc3MtdXBwZXJDYXNlIEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaIDAgMSAwCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEgMApjbGFzcy1kaWdpdCAwMTIzNDU2Nzg5IDAgMSAwCmNsYXNzLXNwZWNpYWwgPD4sPzsuOi8hwqfDuSUqwrVewqgkwqPCsibDqX4iIyd7KFstfMOoYF9cw6dew6BAKV3CsD19KyAwIDEgMAoK
pwdUseCheckModule: TRUE
```
For OpenLDAP 2.5, you must add a **pwdCheckModule** attribute pointing
to the ppm module (for example /usr/local/lib/ppm.so),
and remove the **pwdUseCheckModule** attribute.
See **slapo-ppolicy** for more information, but to sum up:
- enable ppolicy overlay in your database.
- define a default password policy in OpenLDAP configuration or use pwdPolicySubentry attribute to point to the given policy.
This example show the activation for a **slapd.conf** file
This example show the activation for a **slapd.conf** file for OpenLDAP 2.6
(see **slapd-config** and **slapo-ppolicy** for more information for
**cn=config** configuration)
```
overlay ppolicy
ppolicy_default "cn=default,ou=policies,dc=my-domain,dc=com"
ppolicy_check_module /usr/local/openldap/libexec/openldap/ppm.so
#ppolicy_use_lockout # for having more infos about the lockout
```
For OpenLDAP 2.5, you must remove **ppolicy_check_module** parameter as
it is managed in the password policy definition
# FEATURES
......@@ -78,7 +89,10 @@ character class are present in the password.
- passwords must have at least n of the corresponding character class
present, else they are rejected.
- the two previous criteria are checked against any specific character class
- passwords must have at the most x occurrences of characters from the
corresponding character class, else they are rejected.