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(®ex, 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(®ex, param, 0, NULL, 0);
198 if (!reti)
199 {
200 regfree(®ex);
201 return i;
202 }
203 regfree(®ex);
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(®ex, 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(®ex, passwd, 0, NULL, 0);
412 if (!reti)
413 {
414 regfree(®ex);
415 return 1;
416 }
417
418 regfree(®ex);
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