xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/ppm/ppm.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: ppm.c,v 1.2 2021/08/14 16:14:53 christos Exp $	*/
2 
3 /*
4  * ppm.c for OpenLDAP
5  *
6  * See LICENSE, README and INSTALL files
7  */
8 
9 
10 /*
11   password policy module is called with:
12   int check_password (char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
13 
14   *pPasswd: new password
15   **ppErrStr: pointer to the string containing the error message
16   *e: pointer to the current user entry
17   *pArg: pointer to a struct berval holding the value of pwdCheckModuleArg attr
18 */
19 
20 #include <stdlib.h>             // for type conversion, such as atoi...
21 #include <regex.h>              // for matching allowedParameters / conf file
22 #include <string.h>
23 #include <ctype.h>
24 #include <portable.h>
25 #include <slap.h>
26 #include <stdarg.h>             // for variable nb of arguments functions
27 #include "ppm.h"
28 
29 #ifdef CRACKLIB
30 #include "crack.h"              // use cracklib to check password
31 #endif
32 
33 void
ppm_log(int priority,const char * format,...)34 ppm_log(int priority, const char *format, ...)
35 {
36   // if DEBUG flag is set
37   // logs into syslog (for OpenLDAP) or to stdout (for tests)
38 #if defined(DEBUG)
39   if(ppm_test != 1)
40   {
41     va_list syslog_args;
42     va_start(syslog_args, format);
43     vsyslog(priority, format, syslog_args);
44     va_end(syslog_args);
45   }
46   else
47   {
48     va_list stdout_args;
49     va_start(stdout_args, format);
50     vprintf(format, stdout_args);
51     printf("\n");
52     fflush(stdout);
53     va_end(stdout_args);
54   }
55 #endif
56 }
57 
58 void
strcpy_safe(char * dest,char * src,int length_dest)59 strcpy_safe(char *dest, char *src, int length_dest)
60 {
61     if(src == NULL)
62     {
63         dest[0] = '\0';
64     }
65     else
66     {
67         int length_src = strlen(src);
68         int n = (length_dest < length_src) ? length_dest : length_src;
69         // Copy the string — don’t copy too many bytes.
70         strncpy(dest, src, n);
71         // Ensure null-termination.
72         dest[n] = '\0';
73     }
74 }
75 
76 genValue*
getValue(conf * fileConf,int numParam,char * param)77 getValue(conf *fileConf, int numParam, char* param)
78 {
79     int i = 0;
80 
81     // First scan parameters
82     for (i = 0; i < numParam; i++) {
83         if ((strlen(param) == strlen(fileConf[i].param))
84             && (strncmp(param, fileConf[i].param, strlen(fileConf[i].param))
85                 == 0)) {
86             return &(fileConf[i].value);
87         }
88     }
89     return NULL;
90 }
91 
maxConsPerClass(char * password,char * charClass)92 int maxConsPerClass(char *password, char *charClass)
93 {
94   // find maximum number of consecutive class characters in the password
95 
96   int bestMax = 0;
97   int max = 0;
98   int i;
99 
100   for(i=0 ; i<strlen(password) ; i++)
101   {
102     if(strchr(charClass,password[i]) != NULL)
103     {
104       // current character is in class
105       max++;
106       // is the new max a better candidate to maxConsecutivePerClass?
107       if(max > bestMax)
108       {
109         // found a better maxConsecutivePerClass
110         bestMax = max;
111       }
112     }
113     else
114     {
115       // current character is not in class
116       // reinitialize max
117       max=0;
118     }
119   }
120   return bestMax;
121 }
122 
123 void
storeEntry(char * param,char * value,valueType valType,char * min,char * minForPoint,conf * fileConf,int * numParam)124 storeEntry(char *param, char *value, valueType valType,
125            char *min, char *minForPoint, conf * fileConf, int *numParam)
126 {
127     int i = 0;
128     int iMin;
129     int iMinForPoint;
130     if (min == NULL || strcmp(min,"") == 0)
131       iMin = 0;
132     else
133       iMin = atoi(min);
134 
135     if (minForPoint == NULL || strcmp(minForPoint,"") == 0)
136       iMinForPoint = 0;
137     else
138       iMinForPoint = atoi(minForPoint);
139 
140     // First scan parameters
141     for (i = 0; i < *numParam; i++) {
142         if ((strlen(param) == strlen(fileConf[i].param))
143             && (strncmp(param, fileConf[i].param, strlen(fileConf[i].param))
144                 == 0)) {
145             // entry found, replace values
146             if(valType == typeInt)
147                 fileConf[i].value.iVal = atoi(value);
148             else
149                 strcpy_safe(fileConf[i].value.sVal, value, VALUE_MAX_LEN);
150             fileConf[i].min = iMin;
151             fileConf[i].minForPoint = iMinForPoint;
152             if(valType == typeInt)
153                 ppm_log(LOG_NOTICE, "ppm:  Accepted replaced value: %d",
154                                fileConf[i].value.iVal);
155             else
156                 ppm_log(LOG_NOTICE, "ppm:  Accepted replaced value: %s",
157                                fileConf[i].value.sVal);
158             return;
159         }
160     }
161     // entry not found, add values
162     strcpy_safe(fileConf[*numParam].param, param, PARAM_MAX_LEN);
163     fileConf[*numParam].iType = valType;
164     if(valType == typeInt)
165         fileConf[i].value.iVal = atoi(value);
166     else
167         strcpy_safe(fileConf[i].value.sVal, value, VALUE_MAX_LEN);
168     fileConf[*numParam].min = iMin;
169     fileConf[*numParam].minForPoint = iMinForPoint;
170     ++(*numParam);
171             if(valType == typeInt)
172                 ppm_log(LOG_NOTICE, "ppm:  Accepted new value: %d",
173                                fileConf[*numParam].value.iVal);
174             else
175                 ppm_log(LOG_NOTICE, "ppm:  Accepted new value: %s",
176                                fileConf[*numParam].value.sVal);
177 }
178 
179 int
typeParam(char * param)180 typeParam(char* param)
181 {
182     int i;
183     int n = sizeof(allowedParameters)/sizeof(params);
184 
185     regex_t regex;
186     int reti;
187 
188     for(i = 0 ; i < n ; i++ )
189     {
190         // Compile regular expression
191         reti = regcomp(&regex, allowedParameters[i].param, 0);
192         if (reti) {
193             ppm_log(LOG_ERR, "ppm: Cannot compile regex: %s",
194                    allowedParameters[i].param);
195             return n;
196         }
197 
198         // Execute regular expression
199         reti = regexec(&regex, param, 0, NULL, 0);
200         if (!reti)
201         {
202             regfree(&regex);
203             return i;
204         }
205         regfree(&regex);
206     }
207     return n;
208 }
209 
210 #ifndef PPM_READ_FILE
211 
212   /*
213    * read configuration into pwdCheckModuleArg attribute
214    * */
215   static void
read_config_attr(conf * fileConf,int * numParam,char * ppm_config_attr)216   read_config_attr(conf * fileConf, int *numParam, char *ppm_config_attr)
217   {
218     int nParam = 0;       // position of found parameter in allowedParameters
219     int sAllowedParameters = sizeof(allowedParameters)/sizeof(params);
220     char arg[260*256];
221     char *token;
222     char *saveptr1;
223     char *saveptr2;
224 
225     strcpy_safe(arg, ppm_config_attr, 260*256);
226     ppm_log(LOG_NOTICE, "ppm: Parsing pwdCheckModuleArg attribute");
227     token = strtok_r(arg, "\n", &saveptr1);
228 
229     while (token != NULL) {
230         ppm_log(LOG_NOTICE, "ppm: get line: %s",token);
231         char *start = token;
232         char *word, *value;
233         char *min, *minForPoint;;
234 
235         while (isspace(*start) && isascii(*start))
236             start++;
237 
238         if (!isascii(*start))
239         {
240             token = strtok_r(NULL, "\n", &saveptr1);
241             continue;
242         }
243         if (start[0] == '#')
244         {
245             token = strtok_r(NULL, "\n", &saveptr1);
246             continue;
247         }
248 
249         if ((word = strtok_r(start, " \t", &saveptr2))) {
250             if ((value = strtok_r(NULL, " \t", &saveptr2)) == NULL)
251             {
252                 saveptr2 = NULL;
253                 ppm_log(LOG_NOTICE, "ppm: No value, goto next parameter");
254                 token = strtok_r(NULL, "\n", &saveptr1);
255                 continue;
256             }
257             if (strchr(value, '\n') != NULL)
258                 strchr(value, '\n')[0] = '\0';
259             min = strtok_r(NULL, " \t", &saveptr2);
260             if (min != NULL)
261                 if (strchr(min, '\n') != NULL)
262                     strchr(min, '\n')[0] = '\0';
263             minForPoint = strtok_r(NULL, " \t", &saveptr2);
264             if (minForPoint != NULL)
265                 if (strchr(minForPoint, '\n') != NULL)
266                     strchr(minForPoint, '\n')[0] = '\0';
267 
268 
269             nParam = typeParam(word); // search for param in allowedParameters
270             if (nParam != sAllowedParameters) // param has been found
271             {
272                 ppm_log(LOG_NOTICE,
273                    "ppm: Param = %s, value = %s, min = %s, minForPoint= %s",
274                    word, value, min, minForPoint);
275 
276                 storeEntry(word, value, allowedParameters[nParam].iType,
277                            min, minForPoint, fileConf, numParam);
278             }
279             else
280             {
281                 ppm_log(LOG_NOTICE,
282                    "ppm: Parameter '%s' rejected", word);
283             }
284 
285         }
286         token = strtok_r(NULL, "\n", &saveptr1);
287     }
288 
289   }
290 
291 #endif
292 
293 #ifdef PPM_READ_FILE
294 
295   /*
296    * read configuration file (DEPRECATED)
297    * */
298   static void
read_config_file(conf * fileConf,int * numParam,char * ppm_config_file)299   read_config_file(conf * fileConf, int *numParam, char *ppm_config_file)
300   {
301     FILE *config;
302     char line[260] = "";
303     int nParam = 0;       // position of found parameter in allowedParameters
304     int sAllowedParameters = sizeof(allowedParameters)/sizeof(params);
305 
306     ppm_log(LOG_NOTICE, "ppm: Opening file %s", ppm_config_file);
307     if ((config = fopen(ppm_config_file, "r")) == NULL) {
308         ppm_log(LOG_ERR, "ppm: Opening file %s failed", ppm_config_file);
309         exit(EXIT_FAILURE);
310     }
311 
312     while (fgets(line, 256, config) != NULL) {
313         char *start = line;
314         char *word, *value;
315         char *min, *minForPoint;;
316 
317         while (isspace(*start) && isascii(*start))
318             start++;
319 
320         if (!isascii(*start))
321             continue;
322         if (start[0] == '#')
323             continue;
324 
325         if ((word = strtok(start, " \t"))) {
326             if ((value = strtok(NULL, " \t")) == NULL)
327                 continue;
328             if (strchr(value, '\n') != NULL)
329                 strchr(value, '\n')[0] = '\0';
330             min = strtok(NULL, " \t");
331             if (min != NULL)
332                 if (strchr(min, '\n') != NULL)
333                     strchr(min, '\n')[0] = '\0';
334             minForPoint = strtok(NULL, " \t");
335             if (minForPoint != NULL)
336                 if (strchr(minForPoint, '\n') != NULL)
337                     strchr(minForPoint, '\n')[0] = '\0';
338 
339 
340             nParam = typeParam(word); // search for param in allowedParameters
341             if (nParam != sAllowedParameters) // param has been found
342             {
343                 ppm_log(LOG_NOTICE,
344                    "ppm: Param = %s, value = %s, min = %s, minForPoint= %s",
345                    word, value, min, minForPoint);
346 
347                 storeEntry(word, value, allowedParameters[nParam].iType,
348                            min, minForPoint, fileConf, numParam);
349             }
350             else
351             {
352                 ppm_log(LOG_NOTICE,
353                    "ppm: Parameter '%s' rejected", word);
354             }
355 
356         }
357     }
358 
359     fclose(config);
360   }
361 
362 #endif
363 
364 static int
realloc_error_message(char ** target,int curlen,int nextlen)365 realloc_error_message(char **target, int curlen, int nextlen)
366 {
367     if (curlen < nextlen + MEMORY_MARGIN) {
368         ppm_log(LOG_WARNING,
369                "ppm: Reallocating szErrStr from %d to %d", curlen,
370                nextlen + MEMORY_MARGIN);
371         ber_memfree(*target);
372         curlen = nextlen + MEMORY_MARGIN;
373         *target = (char *) ber_memalloc(curlen);
374     }
375 
376     return curlen;
377 }
378 
379 // Does the password contains a token from the RDN ?
380 int
containsRDN(char * passwd,char * DN)381 containsRDN(char* passwd, char* DN)
382 {
383     char lDN[DN_MAX_LEN];
384     char * tmpToken;
385     char * token;
386     regex_t regex;
387     int reti;
388 
389     strcpy_safe(lDN, DN, DN_MAX_LEN);
390 
391     // Extract the RDN from the DN
392     tmpToken = strtok(lDN, ",+");
393     tmpToken = strtok(tmpToken, "=");
394     tmpToken = strtok(NULL, "=");
395 
396     // Search for each token in the password */
397     token = strtok(tmpToken, TOKENS_DELIMITERS);
398 
399     while (token != NULL)
400     {
401       if (strlen(token) > 2)
402       {
403         ppm_log(LOG_NOTICE, "ppm: Checking if %s part of RDN matches the password", token);
404         // Compile regular expression
405         reti = regcomp(&regex, token, REG_ICASE);
406         if (reti) {
407           ppm_log(LOG_ERR, "ppm: Cannot compile regex: %s", token);
408           return 0;
409         }
410 
411         // Execute regular expression
412         reti = regexec(&regex, passwd, 0, NULL, 0);
413         if (!reti)
414         {
415           regfree(&regex);
416           return 1;
417         }
418 
419         regfree(&regex);
420       }
421       else
422       {
423         ppm_log(LOG_NOTICE, "ppm: %s part of RDN is too short to be checked", token);
424       }
425       token = strtok(NULL, TOKENS_DELIMITERS);
426     }
427 
428     return 0;
429 }
430 
431 
432 int
check_password(char * pPasswd,char ** ppErrStr,Entry * e,void * pArg)433 check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
434 {
435 
436     Entry *pEntry = e;
437     ppm_log(LOG_NOTICE, "ppm: entry %s", pEntry->e_nname.bv_val);
438 
439     struct berval *pwdCheckModuleArg = pArg;
440     /* Determine if config file is to be read (DEPRECATED) */
441     #ifdef PPM_READ_FILE
442       ppm_log(LOG_NOTICE, "ppm: Not reading pwdCheckModuleArg attribute");
443       ppm_log(LOG_NOTICE, "ppm: instead, read configuration file (deprecated)");
444     #else
445       ppm_log(LOG_NOTICE, "ppm: Reading pwdCheckModuleArg attribute");
446       ppm_log(LOG_NOTICE, "ppm: RAW configuration: %s",
447                           (*(struct berval*)pwdCheckModuleArg).bv_val);
448     #endif
449 
450     char *szErrStr = (char *) ber_memalloc(MEM_INIT_SZ);
451     int mem_len = MEM_INIT_SZ;
452     int numParam = 0; // Number of params in current configuration
453 
454     int useCracklib;
455     char cracklibDict[VALUE_MAX_LEN];
456     char cracklibDictFiles[3][(VALUE_MAX_LEN+5)];
457     char const* cracklibExt[] = { ".hwm", ".pwd", ".pwi" };
458     FILE* fd;
459     char* res;
460     int minQuality;
461     int checkRDN;
462     char forbiddenChars[VALUE_MAX_LEN];
463     int nForbiddenChars = 0;
464     int nQuality = 0;
465     int maxConsecutivePerClass;
466     int nbInClass[CONF_MAX_SIZE];
467     int i,j;
468 
469     /* Determine config file (DEPRECATED) */
470     #ifdef PPM_READ_FILE
471       char ppm_config_file[FILENAME_MAX_LEN];
472       strcpy_safe(ppm_config_file, getenv("PPM_CONFIG_FILE"), FILENAME_MAX_LEN);
473       if (ppm_config_file[0] == '\0') {
474         strcpy_safe(ppm_config_file, CONFIG_FILE, FILENAME_MAX_LEN);
475       }
476       ppm_log(LOG_NOTICE, "ppm: reading config file from %s", ppm_config_file);
477     #endif
478 
479     for (i = 0; i < CONF_MAX_SIZE; i++)
480         nbInClass[i] = 0;
481 
482     /* Set default values */
483     conf fileConf[CONF_MAX_SIZE] = {
484         {"minQuality", typeInt, {.iVal = DEFAULT_QUALITY}, 0, 0
485          }
486         ,
487         {"checkRDN", typeInt, {.iVal = 0}, 0, 0
488          }
489         ,
490         {"forbiddenChars", typeStr, {.sVal = ""}, 0, 0
491          }
492         ,
493         {"maxConsecutivePerClass", typeInt, {.iVal = 0}, 0, 0
494          }
495         ,
496         {"useCracklib", typeInt, {.iVal = 0}, 0, 0
497          }
498         ,
499         {"cracklibDict", typeStr, {.sVal = "/var/cache/cracklib/cracklib_dict"}, 0, 0
500          }
501         ,
502         {"class-upperCase", typeStr, {.sVal = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"}, 0, 1
503          }
504         ,
505         {"class-lowerCase", typeStr, {.sVal = "abcdefghijklmnopqrstuvwxyz"}, 0, 1
506          }
507         ,
508         {"class-digit", typeStr, {.sVal = "0123456789"}, 0, 1
509          }
510         ,
511         {"class-special", typeStr,
512          {.sVal = "<>,?;.:/!§ù%*µ^¨$£²&é~\"#'{([-|è`_\\ç^à@)]°=}+"}, 0, 1
513          }
514     };
515     numParam = 10;
516 
517     #ifdef PPM_READ_FILE
518       /* Read configuration file (DEPRECATED) */
519       read_config_file(fileConf, &numParam, ppm_config_file);
520     #else
521       /* Read configuration attribute (pwdCheckModuleArg) */
522       read_config_attr(fileConf, &numParam, (*(struct berval*)pwdCheckModuleArg).bv_val);
523     #endif
524 
525     minQuality = getValue(fileConf, numParam, "minQuality")->iVal;
526     checkRDN = getValue(fileConf, numParam, "checkRDN")->iVal;
527     strcpy_safe(forbiddenChars,
528                 getValue(fileConf, numParam, "forbiddenChars")->sVal,
529                 VALUE_MAX_LEN);
530     maxConsecutivePerClass = getValue(fileConf, numParam, "maxConsecutivePerClass")->iVal;
531     useCracklib = getValue(fileConf, numParam, "useCracklib")->iVal;
532     strcpy_safe(cracklibDict,
533                 getValue(fileConf, numParam, "cracklibDict")->sVal,
534                 VALUE_MAX_LEN);
535 
536 
537     /*The password must have at least minQuality strength points with one
538      * point granted if the password contains at least minForPoint characters for each class
539      * It must contains at least min chars of each class
540      * It must not contain any char in forbiddenChar */
541 
542     for (i = 0; i < strlen(pPasswd); i++) {
543 
544         int n;
545         for (n = 0; n < numParam; n++) {
546             if (strstr(fileConf[n].param, "class-") != NULL) {
547                 if (strchr(fileConf[n].value.sVal, pPasswd[i]) != NULL) {
548                     ++(nbInClass[n]);
549                 }
550             }
551         }
552         if (strchr(forbiddenChars, pPasswd[i]) != NULL) {
553             nForbiddenChars++;
554         }
555     }
556 
557     // Password checking done, now loocking for minForPoint criteria
558     for (i = 0; i < CONF_MAX_SIZE; i++) {
559         if (strstr(fileConf[i].param, "class-") != NULL) {
560             if ((nbInClass[i] >= fileConf[i].minForPoint)
561                 && strlen(fileConf[i].value.sVal) != 0) {
562                 // 1 point granted
563                 ++nQuality;
564                 ppm_log(LOG_NOTICE, "ppm: 1 point granted for class %s",
565                        fileConf[i].param);
566             }
567         }
568     }
569 
570     if (nQuality < minQuality) {
571         mem_len = realloc_error_message(&szErrStr, mem_len,
572                                         strlen(PASSWORD_QUALITY_SZ) +
573                                         strlen(pEntry->e_nname.bv_val) + 4);
574         sprintf(szErrStr, PASSWORD_QUALITY_SZ, pEntry->e_nname.bv_val,
575                 nQuality, minQuality);
576         goto fail;
577     }
578     // Password checking done, now loocking for constraintClass criteria
579     for (i = 0; i < CONF_MAX_SIZE; i++) {
580         if (strstr(fileConf[i].param, "class-") != NULL) {
581             if ((nbInClass[i] < fileConf[i].min) &&
582                  strlen(fileConf[i].value.sVal) != 0) {
583                 // constraint is not satisfied... goto fail
584                 mem_len = realloc_error_message(&szErrStr, mem_len,
585                                                 strlen(PASSWORD_CRITERIA) +
586                                                 strlen(pEntry->e_nname.bv_val) +
587                                                 2 + PARAM_MAX_LEN);
588                 sprintf(szErrStr, PASSWORD_CRITERIA, pEntry->e_nname.bv_val,
589                         fileConf[i].min, fileConf[i].param);
590                 goto fail;
591             }
592         }
593     }
594 
595     // Password checking done, now loocking for forbiddenChars criteria
596     if (nForbiddenChars > 0) {  // at least 1 forbidden char... goto fail
597         mem_len = realloc_error_message(&szErrStr, mem_len,
598                                         strlen(PASSWORD_FORBIDDENCHARS) +
599                                         strlen(pEntry->e_nname.bv_val) + 2 +
600                                         VALUE_MAX_LEN);
601         sprintf(szErrStr, PASSWORD_FORBIDDENCHARS, pEntry->e_nname.bv_val,
602                 nForbiddenChars, forbiddenChars);
603         goto fail;
604     }
605 
606     // Password checking done, now loocking for maxConsecutivePerClass criteria
607     for (i = 0; i < CONF_MAX_SIZE; i++) {
608         if (strstr(fileConf[i].param, "class-") != NULL) {
609             if ( maxConsecutivePerClass != 0 &&
610                 (maxConsPerClass(pPasswd,fileConf[i].value.sVal)
611                                                  > maxConsecutivePerClass)) {
612                 // Too much consecutive characters of the same class
613                 ppm_log(LOG_NOTICE, "ppm: Too much consecutive chars for class %s",
614                        fileConf[i].param);
615                 mem_len = realloc_error_message(&szErrStr, mem_len,
616                                         strlen(PASSWORD_MAXCONSECUTIVEPERCLASS) +
617                                         strlen(pEntry->e_nname.bv_val) + 2 +
618                                         PARAM_MAX_LEN);
619                 sprintf(szErrStr, PASSWORD_MAXCONSECUTIVEPERCLASS, pEntry->e_nname.bv_val,
620                         maxConsecutivePerClass, fileConf[i].param);
621                 goto fail;
622             }
623         }
624     }
625 #ifdef CRACKLIB
626     // Password checking done, now loocking for cracklib criteria
627     if ( useCracklib > 0 ) {
628 
629         for( j = 0 ; j < 3 ; j++) {
630             strcpy_safe(cracklibDictFiles[j], cracklibDict, VALUE_MAX_LEN);
631             strcat(cracklibDictFiles[j], cracklibExt[j]);
632             if (( fd = fopen ( cracklibDictFiles[j], "r")) == NULL ) {
633                 ppm_log(LOG_NOTICE, "ppm: Error while reading %s file",
634                        cracklibDictFiles[j]);
635                 mem_len = realloc_error_message(&szErrStr, mem_len,
636                                 strlen(GENERIC_ERROR));
637                 sprintf(szErrStr, GENERIC_ERROR);
638                 goto fail;
639 
640             }
641             else {
642                 fclose (fd);
643             }
644         }
645         res = (char *) FascistCheck (pPasswd, cracklibDict);
646         if ( res != NULL ) {
647                 ppm_log(LOG_NOTICE, "ppm: cracklib does not validate password for entry %s",
648                        pEntry->e_nname.bv_val);
649                 mem_len = realloc_error_message(&szErrStr, mem_len,
650                                         strlen(PASSWORD_CRACKLIB) +
651                                         strlen(pEntry->e_nname.bv_val));
652                 sprintf(szErrStr, PASSWORD_CRACKLIB, pEntry->e_nname.bv_val);
653                 goto fail;
654 
655         }
656 
657     }
658 #endif
659 
660     // Password checking done, now looking for checkRDN criteria
661     if (checkRDN == 1 && containsRDN(pPasswd, pEntry->e_nname.bv_val))
662     // RDN check enabled and a token from RDN is found in password: goto fail
663     {
664         mem_len = realloc_error_message(&szErrStr, mem_len,
665                                         strlen(RDN_TOKEN_FOUND) +
666                                         strlen(pEntry->e_nname.bv_val));
667         sprintf(szErrStr, RDN_TOKEN_FOUND, pEntry->e_nname.bv_val);
668 
669         goto fail;
670     }
671 
672     *ppErrStr = strdup("");
673     ber_memfree(szErrStr);
674     return (LDAP_SUCCESS);
675 
676   fail:
677     *ppErrStr = strdup(szErrStr);
678     ber_memfree(szErrStr);
679     return (EXIT_FAILURE);
680 
681 }
682