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