1 /*
2 ** Copyright 2016 Double Precision, Inc.  See COPYING for
3 ** distribution information.
4 */
5 
6 #if HAVE_CONFIG_H
7 #include "courier_auth_config.h"
8 #endif
9 #include <string>
10 #include <sstream>
11 #include <map>
12 #include <set>
13 #include <vector>
14 #include <algorithm>
15 #include <cctype>
16 
17 #if HAVE_LBER_H
18 #include <lber.h>
19 #endif
20 #if HAVE_LDAP_H
21 #include <ldap.h>
22 #endif
23 #include <pwd.h>
24 #include <grp.h>
25 #include <errno.h>
26 #include <time.h>
27 #if HAVE_SYS_TIME_H
28 #include <sys/time.h>
29 #endif
30 #if HAVE_SYS_STAT_H
31 #include <sys/stat.h>
32 #endif
33 
34 extern "C" {
35 #include "authldap.h"
36 #include "auth.h"
37 #include "authldaprc.h"
38 #include "courierauthdebug.h"
39 };
40 
41 #include "authconfigfile.h"
42 #include <stdio.h>
43 
44 #ifndef	LDAP_OPT_SUCCESS
45 #define LDAP_OPT_SUCCESS LDAP_SUCCESS
46 #endif
47 
48 /*
49 ** Main connection to the LDAP server, and a separate connection for the
50 ** LDAP_AUTHBIND option.
51 */
52 
53 class ldap_connection {
54 
55 public:
56 	LDAP *connection;
57 	bool bound;
58 
ldap_connection()59 	ldap_connection() : connection(0), bound(false) {}
~ldap_connection()60 	~ldap_connection() { disconnect(); }
61 
connected() const62 	bool connected() const { return connection != 0; }
63 
64 	bool connect();
65 	void disconnect();
66 	void close();
67 
68 private:
69 	bool enable_tls();
70 
71 public:
72 
ok(const char * method,int rc)73 	static bool ok(const char *method, int rc)
74 	{
75 		if (rc == 0 || LDAP_NAME_ERROR(rc))
76 			return true;
77 
78 		courier_auth_err("%s failed: %s", method,
79 				 ldap_err2string(rc));
80 		return false;
81 	}
82 
bind(const std::string & userid,const std::string & password)83 	bool bind(const std::string &userid,
84 		  const std::string &password)
85 	{
86 		if (do_bind(userid, password))
87 		{
88 			bound=true;
89 			return true;
90 		}
91 
92 		return false;
93 	}
94 
95 private:
do_bind(const std::string & userid,const std::string & password)96 	bool do_bind(const std::string &userid,
97 		  const std::string &password)
98 	{
99 		std::vector<char> buffer(password.begin(), password.end());
100 		struct berval cred;
101 
102 		cred.bv_len=buffer.size();
103 		cred.bv_val=&buffer[0];
104 
105 		if (connect() &&
106 		    ok("ldap_sasl_bind_s",
107 		       ldap_sasl_bind_s(connection, userid.c_str(),
108 					NULL, &cred,
109 					NULL, NULL, NULL)))
110 			return true;
111 
112 		disconnect();
113 		return connect() &&
114 			ok("ldap_sasl_bind_s",
115 			   ldap_sasl_bind_s(connection, userid.c_str(),
116 					    NULL, &cred,
117 					    NULL, NULL, NULL));
118 	}
119 };
120 
121 ldap_connection main_connection, bind_connection;
122 
123 
124 // Loaded and parsed authldaprc configuration file.
125 
126 class authldaprc_file : public courier::auth::config_file {
127 
128 public:
129 
130 	int protocol_version;
131 	int timeout;
132 	int authbind;
133 	int initbind;
134 	int tls;
135 	uid_t uid;
136 	gid_t gid;
137 
138 	std::string ldap_uri, ldap_binddn, ldap_bindpw, ldap_basedn;
139 	int ldap_deref;
140 
141 	std::vector<std::string> auxoptions, auxnames;
142 
143 	authldaprc_file();
144 
145 private:
146 	bool do_load();
147 	void do_reload();
148 public:
149 };
150 
151 /*
152 ** There's a memory leak in OpenLDAP 1.2.11, presumably in earlier versions
153 ** too.  See http://www.OpenLDAP.org/its/index.cgi?findid=864 for more
154 ** information.  To work around the bug, the first time a connection fails
155 ** we stop trying for 60 seconds.  After 60 seconds we kill the process,
156 ** and let the parent process restart it.
157 **
158 ** We'll control this behavior via LDAP_MEMORY_LEAK.  Set it to ZERO to turn
159 ** off this behavior (whenever OpenLDAP gets fixed).
160 */
161 
162 static time_t ldapfailflag=0;
163 
ldapconnfailure()164 static void ldapconnfailure()
165 {
166 	const char *p=getenv("LDAP_MEMORY_LEAK");
167 
168 	if (!p)
169 	{
170 #ifdef LDAP_VENDOR_NAME
171 #ifdef LDAP_VENDOR_VERSION
172 #define DO_OPENLDAP_CHECK
173 #endif
174 #endif
175 
176 #ifdef DO_OPENLDAP_CHECK
177 
178 		/* It's supposed to be fixed in 20019 */
179 
180 		if (strcmp(LDAP_VENDOR_NAME, "OpenLDAP") == 0 &&
181 		    LDAP_VENDOR_VERSION < 20019)
182 			p="1";
183 		else
184 			p="0";
185 #else
186 		p="0";
187 #endif
188 	}
189 
190 	if (atoi(p) && !ldapfailflag)
191 	{
192 		time(&ldapfailflag);
193 		ldapfailflag += 60;
194 	}
195 }
196 
ldapconncheck()197 static int ldapconncheck()
198 {
199 	time_t t;
200 
201 	if (!ldapfailflag)
202 		return (0);
203 
204 	time(&t);
205 
206 	if (t >= ldapfailflag)
207 		exit(0);
208 	return (1);
209 }
210 
authldaprc_file()211 authldaprc_file::authldaprc_file()
212 	: config_file(AUTHLDAPRC)
213 {
214 }
215 
do_load()216 bool authldaprc_file::do_load()
217 {
218 	bool loaded=true;
219 
220 	// Frequently-accessed variables.
221 
222 	if (!config("LDAP_TIMEOUT", timeout, false, "5") ||
223 	    !config("LDAP_TLS", tls, false, "0"))
224 	{
225 		loaded=false;
226 	}
227 
228 	if (!config("LDAP_URI", ldap_uri, true))
229 	{
230 		loaded=false;
231 	}
232 
233 	ldap_deref=0;
234 
235 	std::string deref_setting;
236 
237 	config("LDAP_DEREF", deref_setting, false, "");
238 
239 	for (std::string::iterator p=deref_setting.begin();
240 	     p != deref_setting.end(); ++p)
241 		*p=std::tolower(*p);
242 
243 #ifdef LDAP_OPT_DEREF
244 
245 	ldap_deref=LDAP_DEREF_NEVER;
246 
247 	if (deref_setting == "never")
248 		ldap_deref = LDAP_DEREF_NEVER;
249 	else if (deref_setting == "searching")
250 		ldap_deref = LDAP_DEREF_SEARCHING;
251 	else if (deref_setting == "finding")
252 		ldap_deref = LDAP_DEREF_FINDING;
253 	else if (deref_setting == "always")
254 		ldap_deref = LDAP_DEREF_ALWAYS;
255 	else if (deref_setting != "")
256 	{
257 		loaded=false;
258 		courier_auth_err("authldap: INVALID LDAP_OPT_DEREF");
259 	}
260 #endif
261 	uid=0;
262 	gid=0;
263 
264 	std::string uid_str, gid_str;
265 
266 	config("LDAP_GLOB_UID", uid_str, false);
267 	config("LDAP_GLOB_GID", gid_str, false);
268 
269 	if (!uid_str.empty())
270 	{
271 		std::istringstream i(uid_str);
272 
273 		i >> uid;
274 
275 		if (i.fail())
276 		{
277 			struct passwd *pwent=getpwnam(uid_str.c_str());
278 
279 			if (!pwent)
280 			{
281 				courier_auth_err("authldap: INVALID LDAP_GLOB_UID");
282 				loaded=false;
283 			}
284 			else
285 			{
286 				uid=pwent->pw_uid;
287 			}
288 		}
289 	}
290 
291 	if (!gid_str.empty())
292 	{
293 		std::istringstream i(uid_str);
294 
295 		i >> gid;
296 
297 		if (i.fail())
298 		{
299 			struct group  *grent=getgrnam(gid_str.c_str());
300 
301 			if (!grent)
302 			{
303 				courier_auth_err("authldap: INVALID LDAP_GLOB_GID");
304 				loaded=false;
305 			}
306 			else
307 			{
308 				gid=grent->gr_gid;
309 			}
310 		}
311 	}
312 
313 	if (!config("LDAP_AUTHBIND", authbind, false, "0")
314 	    || !config("LDAP_INITBIND", initbind, false, "0")
315 	    || !config("LDAP_BASEDN", ldap_basedn, true))
316 		loaded=false;
317 
318 	if (initbind)
319 	{
320 		if (!config("LDAP_BINDDN", ldap_binddn, true) ||
321 		    !config("LDAP_BINDPW", ldap_bindpw, true))
322 			loaded=false;
323 	}
324 
325 	if (!config("LDAP_PROTOCOL_VERSION", protocol_version, false, "0"))
326 		loaded=false;
327 
328 	if (protocol_version)
329 	{
330 #ifndef LDAP_OPT_PROTOCOL_VERSION
331 		courier_auth_err("authldaplib: LDAP_OPT_PROTOCOL_VERSION ignored");
332 		loaded=false;
333 #endif
334 		if (0
335 #ifdef LDAP_VERSION_MIN
336 			|| protocol_version < LDAP_VERSION_MIN
337 #endif
338 #ifdef LDAP_VERSION_MAX
339 			|| protocol_version > LDAP_VERSION_MAX
340 #endif
341 		)
342 		{
343 			protocol_version=0;
344 			courier_auth_err("authldaplib: LDAP_PROTOCOL_VERSION not supported");
345 			loaded=false;
346 		}
347 	}
348 
349 	std::string auxoptions_str;
350 
351 	config("LDAP_AUXOPTIONS", auxoptions_str, "");
352 
353 	std::string::iterator p=auxoptions_str.begin();
354 
355 	while (p != auxoptions_str.end())
356 	{
357 		if (*p == ',')
358 		{
359 			++p;
360 			continue;
361 		}
362 
363 		std::string::iterator q=p;
364 
365 		p=std::find(p, auxoptions_str.end(), ',');
366 
367 		std::string auxoption(q, p);
368 		std::string auxname;
369 
370 		q=std::find(auxoption.begin(), auxoption.end(), '=');
371 
372 		if (q != auxoption.end())
373 		{
374 			auxname=std::string(q+1, auxoption.end());
375 			auxoption=std::string(auxoption.begin(), q);
376 		}
377 
378 		auxoptions.push_back(auxoption);
379 		auxnames.push_back(auxname);
380 	}
381 
382 	return loaded;
383 }
384 
do_reload()385 void authldaprc_file::do_reload()
386 {
387 	// File changed, try to load it again.
388 
389 	authldaprc_file new_file;
390 
391 	if (new_file.load(true))
392 	{
393 		*this=new_file;
394 		DPRINTF("authldap: reloaded %s", filename);
395 
396 		// If we reloaded the file, close the
397 		// connections, so they can be reopened using
398 		// the new config.
399 
400 		main_connection.close();
401 		bind_connection.close();
402 	}
403 }
404 
405 static authldaprc_file authldaprc;
406 
407 #if HAVE_LDAP_TLS
enable_tls()408 bool ldap_connection::enable_tls()
409 {
410 	int version;
411 
412 	if (!ok("ldap_get_option",
413 		ldap_get_option(connection, LDAP_OPT_PROTOCOL_VERSION,
414 				&version)))
415 		return false;
416 
417 	if (version < LDAP_VERSION3)
418 	{
419 		version = LDAP_VERSION3;
420 		(void)ldap_set_option (connection,
421 				       LDAP_OPT_PROTOCOL_VERSION,
422 				       &version);
423 	}
424 
425 	if (!ok("ldap_start_tls_s",
426 		ldap_start_tls_s(connection, NULL, NULL)))
427 		return false;
428 
429 	return true;
430 }
431 
432 #else
433 
enable_tls()434 bool ldap_connection::enable_tls()
435 {
436 	courier_auth_err("authldaplib: TLS not available");
437 	return (-1);
438 }
439 #endif
440 
connect()441 bool ldap_connection::connect()
442 {
443 	if (connected()) return true;
444 
445 	bound=false;
446 
447 	DPRINTF("authldaplib: connecting to %s", authldaprc.ldap_uri.c_str());
448 
449 	if (ldapconncheck())
450 	{
451 		DPRINTF("authldaplib: timing out after failed connection");
452 		return (false);
453 	}
454 
455 	ldap_initialize(&connection, authldaprc.ldap_uri.c_str());
456 
457 	if (connection==NULL)
458 	{
459 		courier_auth_err("cannot connect to LDAP server (%s): %s",
460 				 authldaprc.ldap_uri.c_str(), strerror(errno));
461 		ldapconnfailure();
462 	}
463 #ifdef LDAP_OPT_NETWORK_TIMEOUT
464 	else if (authldaprc.timeout > 0)
465 	{
466 		DPRINTF("timeout set to %d", authldaprc.timeout);
467 		ldap_set_option (connection, LDAP_OPT_NETWORK_TIMEOUT, &authldaprc.timeout);
468 #endif
469 	}
470 
471 #ifdef LDAP_OPT_PROTOCOL_VERSION
472 
473 	if (authldaprc.protocol_version &&
474 	    !ok("ldap_set_option",
475 		ldap_set_option(connection,
476 				LDAP_OPT_PROTOCOL_VERSION,
477 				(void *) &authldaprc.protocol_version)))
478 	{
479 		disconnect();
480 		return false;
481 	}
482 
483 	if (authldaprc.protocol_version)
484 	{
485 		DPRINTF("selected ldap protocol version %d", authldaprc.protocol_version);
486 	}
487 #endif
488 
489 	if (authldaprc.tls && !enable_tls())
490 	{
491 		disconnect();
492 		return false;
493 	}
494 
495 #ifdef LDAP_OPT_DEREF
496 	if (!ok("ldap_set_option",
497 		ldap_set_option(connection, LDAP_OPT_DEREF,
498 				(void *)&authldaprc.ldap_deref)))
499 	{
500 		disconnect();
501 		return (false);
502 	}
503 #else
504 	if (!deref_setting.empty())
505 	{
506 		courier_auth_err("authldaplib: LDAP_OPT_DEREF not available, ignored");
507 	}
508 #endif
509 
510 	return true;
511 }
512 
disconnect()513 void ldap_connection::disconnect()
514 {
515 	close();
516 	ldapconnfailure();
517 }
518 
close()519 void ldap_connection::close()
520 {
521 	if (connection == NULL)
522 		return;
523 
524 	ldap_unbind_ext(connection, 0, 0);
525 	connection=NULL;
526 }
527 
ldapopen()528 static int ldapopen()
529 {
530 	if (!main_connection.connected())
531 	{
532 		if (!main_connection.connect())
533 			return 1;
534 	}
535 
536 	if (authldaprc.initbind && !main_connection.bound)
537 	{
538 		/* Bind to server */
539 		if (courier_authdebug_login_level >= 2)
540 		{
541 			DPRINTF("binding to LDAP server as DN '%s', password '%s'",
542 				authldaprc.ldap_binddn.empty() ? "<null>"
543 				: authldaprc.ldap_binddn.c_str(),
544 				authldaprc.ldap_bindpw.empty() ? "<null>"
545 				: authldaprc.ldap_bindpw.c_str());
546 		}
547 		else
548 		{
549 			DPRINTF("binding to LDAP server as DN '%s'",
550 				authldaprc.ldap_binddn.empty() ? "<null>"
551 				: authldaprc.ldap_binddn.c_str());
552 		}
553 
554 		if (!main_connection.bind(authldaprc.ldap_binddn,
555 					  authldaprc.ldap_bindpw))
556 		{
557 			authldapclose();
558 			ldapconnfailure();
559 			return (-1);
560 		}
561 	}
562 	return (0);
563 }
564 
565 class authldaprc_attributes {
566 
567 public:
568 
569 	std::map<std::string, std::vector<std::string *> > attributes;
570 
attribute(const char * name,const char * default_value,std::string & return_value)571 	std::string attribute(const char *name,
572 			      const char *default_value,
573 			      std::string &return_value)
574 	{
575 		std::string value;
576 
577 		authldaprc.config(name, value, false, default_value);
578 
579 		if (!value.empty())
580 			attributes[value].push_back(&return_value);
581 		return value;
582 	}
583 };
584 
585 class authldaprc_attribute_vector : public std::vector<std::string> {
586 
587 public:
588 
authldaprc_attribute_vector(const std::map<std::string,std::vector<std::string * >> & attributes)589 	authldaprc_attribute_vector(const std::map<std::string,
590 				    std::vector<std::string *> > &attributes)
591 	{
592 		for (std::map<std::string, std::vector<std::string *> >
593 			     ::const_iterator
594 			     p=attributes.begin(); p != attributes.end(); ++p)
595 		{
596 			push_back(p->first);
597 		}
598 	}
599 };
600 
601 class authldaprc_search_attributes {
602 
603 	std::vector<std::string> copy_buffer;
604 
605 public:
606 
607 	std::vector<char *> all_attributes_ptr;
608 
authldaprc_search_attributes(const std::vector<std::string> & attributes)609 	authldaprc_search_attributes(const std::vector<std::string> &attributes)
610 		: copy_buffer(attributes)
611 	{
612 		std::set<std::string> dupes;
613 
614 		for (std::vector<std::string>::iterator
615 			     p=copy_buffer.begin();
616 		     p != copy_buffer.end(); ++p)
617 		{
618 			if (p->empty())
619 				continue;
620 
621 			if (dupes.find(*p) != dupes.end())
622 				continue;
623 			dupes.insert(*p);
624 			p->push_back(0);
625 			all_attributes_ptr.push_back(& (*p)[0]);
626 		}
627 
628 		all_attributes_ptr.push_back(0);
629 	}
630 
search_attributes()631 	char **search_attributes()
632 	{
633 		return &all_attributes_ptr[0];
634 	}
635 };
636 
637 class authldaprc_search_result : authldaprc_search_attributes {
638 
639 public:
640 
641 	LDAPMessage *ptr;
642 	bool finished;
643 
authldaprc_search_result(ldap_connection & conn,const std::string & basedn,const std::string & query,const std::vector<std::string> & attributes,const struct timeval & timeout)644 	authldaprc_search_result(ldap_connection &conn,
645 				 const std::string &basedn,
646 				 const std::string &query,
647 				 const std::vector<std::string> &attributes,
648 				 const struct timeval &timeout)
649 		: authldaprc_search_attributes(attributes),
650 		  ptr(NULL), finished(false)
651 	{
652 		struct timeval timeout_copy=timeout;
653 
654 		if (!conn.connect() ||
655 		    !conn.ok("ldap_search_ext_s",
656 			     ldap_search_ext_s(conn.connection,
657 					       basedn.c_str(),
658 					       LDAP_SCOPE_SUBTREE,
659 					       query.c_str(),
660 					       search_attributes(),
661 					       0,
662 					       NULL, NULL,
663 					       &timeout_copy,
664 					       100, &ptr)))
665 		{
666 			ptr=NULL;
667 			conn.disconnect();
668 			if (!conn.connect()
669 			    || !conn.ok("ldap_search_ext_s",
670 				     ldap_search_ext_s(conn.connection,
671 						       basedn.c_str(),
672 						       LDAP_SCOPE_SUBTREE,
673 						       query.c_str(),
674 						       search_attributes(),
675 						       0,
676 						       NULL, NULL,
677 						       &timeout_copy,
678 						       100, &ptr)))
679 			{
680 				ptr=NULL;
681 			}
682 		}
683 	}
684 
authldaprc_search_result(ldap_connection & conn,int msgid,bool all,const struct timeval & timeout)685 	authldaprc_search_result(ldap_connection &conn,
686 				 int msgid,
687 				 bool all,
688 				 const struct timeval &timeout)
689 		:authldaprc_search_attributes(std::vector<std::string>()),
690 		 ptr(NULL), finished(false)
691 	{
692 		while (1)
693 		{
694 			struct timeval timeout_copy=timeout;
695 
696 			int rc=ldap_result(conn.connection, msgid, all ? 1:0,
697 					   &timeout_copy,
698 					   &ptr);
699 
700 			switch (rc)
701 			{
702 			case -1:
703 				DPRINTF("ldap_result() failed");
704 				ldap_msgfree(ptr);
705 				ptr=NULL;
706 				return;
707 			case 0:
708 				DPRINTF("ldap_result() timed out");
709 				ldap_msgfree(ptr);
710 				ptr=NULL;
711 				return;
712 			case LDAP_RES_SEARCH_ENTRY:
713 				break; /* deal with below */
714 			case LDAP_RES_SEARCH_RESULT:
715 				if (ldap_parse_result(conn.connection, ptr,
716 						      &rc,
717 						      NULL, NULL, NULL, NULL,
718 						      0) != LDAP_SUCCESS)
719 				{
720 					DPRINTF("ldap_parse_result failed");
721 					ldap_msgfree(ptr);
722 					ptr=NULL;
723 					return;
724 				}
725 				ldap_msgfree(ptr);
726 				ptr=NULL;
727 				if (rc != LDAP_SUCCESS)
728 				{
729 					DPRINTF("search failed: %s",
730 						ldap_err2string(rc));
731 					return;
732 				}
733 				finished=true;
734 				return;
735 			default:
736 				DPRINTF("ldap_result(): ignored 0x%02X status",
737 					rc);
738 				ldap_msgfree(ptr);
739 				ptr=NULL;
740 				continue;
741 			}
742 			break;
743 		}
744 	}
745 
operator !() const746 	bool operator!() const
747 	{
748 		return ptr == NULL;
749 	}
750 
operator bool() const751 	operator bool() const
752 	{
753 		return ptr != NULL;
754 	}
755 
~authldaprc_search_result()756 	~authldaprc_search_result()
757 	{
758 		if (ptr)
759 			ldap_msgfree(ptr);
760 	}
761 };
762 
763 static std::vector<std::string>
authldap_entry_values(LDAP * connection,LDAPMessage * msg,const std::string & attrname)764 authldap_entry_values(LDAP *connection, LDAPMessage *msg,
765 		      const std::string &attrname)
766 {
767 	std::vector<std::string> values;
768 
769 	struct berval **p=ldap_get_values_len(connection, msg,
770 					      attrname.c_str());
771 
772 	if (p)
773 	{
774 
775 		size_t n=ldap_count_values_len(p);
776 
777 		values.reserve(n);
778 
779 		for (size_t i=0; i<n; i++)
780 		{
781 			const char *ptr=
782 				reinterpret_cast<const char *>(p[i]->bv_val);
783 
784 			values.push_back(std::string(ptr, ptr+p[i]->bv_len));
785 		}
786 
787 		ldap_value_free_len(p);
788 	}
789 	return values;
790 }
791 
792 class authldap_get_values {
793 
794 	LDAP *connection;
795 	LDAPMessage *entry;
796 	std::string context;
797 
798 public:
799 
authldap_get_values(LDAP * connectionArg,LDAPMessage * entryArg,const std::string & contextArg)800 	authldap_get_values(LDAP *connectionArg,
801 			    LDAPMessage *entryArg,
802 			    const std::string &contextArg)
803 		: connection(connectionArg),
804 		  entry(entryArg),
805 		  context(contextArg)
806 	{
807 	}
808 
operator ()(const std::string & attrname,std::string & value)809 	bool operator()(const std::string &attrname,
810 			std::string &value)
811 	{
812 		std::vector<std::string> values=
813 			authldap_entry_values(connection, entry, attrname);
814 
815 		if (values.empty())
816 			return false;
817 
818 		if (values.size() > 1)
819 		{
820 			fprintf(stderr,
821 				"WARN: authldaplib: duplicate attribute %s for %s\n",
822 				attrname.c_str(),
823 				context.c_str());
824 		}
825 
826 		value=values[0];
827 		return true;
828 	}
829 
operator ()(const std::string & attrname,const std::vector<std::string * > & values)830 	bool operator()(const std::string &attrname,
831 			const std::vector<std::string *> &values)
832 	{
833 		bool found=true;
834 
835 		for (std::vector<std::string *>::const_iterator
836 			     b=values.begin();
837 		     b != values.end(); ++b)
838 		{
839 			found=operator()(attrname, **b);
840 		}
841 
842 		return found;
843 	}
844 
options()845 	std::string options()
846 	{
847 		size_t i;
848 
849 		std::ostringstream options;
850 		const char *options_sep="";
851 
852 		for (i=0; i<authldaprc.auxoptions.size(); ++i)
853 		{
854 			std::string value;
855 
856 			if (operator()(authldaprc.auxoptions[i], value)
857 			    && !value.empty())
858 			{
859 				options << options_sep
860 					<< authldaprc.auxnames[i]
861 					<< "="
862 					<< value;
863 				options_sep=",";
864 			}
865 		}
866 		return options.str();
867 	}
868 };
869 
cpp_auth_ldap_enumerate(void (* cb_func)(const char * name,uid_t uid,gid_t gid,const char * homedir,const char * maildir,const char * options,void * void_arg),void * void_arg)870 static void cpp_auth_ldap_enumerate( void(*cb_func)(const char *name,
871 						    uid_t uid,
872 						    gid_t gid,
873 						    const char *homedir,
874 						    const char *maildir,
875 						    const char *options,
876 						    void *void_arg),
877 				     void *void_arg)
878 {
879 	int msgid;
880 
881 	if (ldapopen())
882 	{
883 		(*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
884 		return;
885 	}
886 
887 	std::string mail_field, uid_field, gid_field,
888 		homedir_field, maildir_field;
889 
890 	authldaprc.config("LDAP_MAIL", mail_field, false, "mail");
891 	authldaprc.config("LDAP_UID", uid_field, false);
892 	authldaprc.config("LDAP_GID", gid_field, false);
893 	authldaprc.config("LDAP_HOMEDIR", homedir_field, false, "homeDir");
894 	authldaprc.config("LDAP_MAILDIR", maildir_field, false);
895 
896 	std::vector<std::string> attribute_vector;
897 
898 	attribute_vector.push_back(mail_field);
899 	attribute_vector.push_back(uid_field);
900 	attribute_vector.push_back(gid_field);
901 	attribute_vector.push_back(homedir_field);
902 	attribute_vector.push_back(maildir_field);
903 
904 	for (size_t i=0; i<authldaprc.auxoptions.size(); i++)
905 	{
906 		attribute_vector.push_back(authldaprc.auxoptions[i]);
907 	}
908 
909 	std::string enumerate_filter;
910 
911 	authldaprc.config("LDAP_ENUMERATE_FILTER", enumerate_filter, false);
912 
913 	if (enumerate_filter.empty())
914 	{
915 		std::string filter;
916 
917 		authldaprc.config("LDAP_FILTER", filter, false);
918 
919 		if (!filter.empty())
920 		{
921 			enumerate_filter=filter;
922 		}
923 		else
924 		{
925 			std::string s;
926 
927 			authldaprc.config("LDAP_MAIL", s, false);
928 
929 			enumerate_filter = s + "=*";
930 		}
931 	}
932 
933 	DPRINTF("ldap_search: basedn='%s', filter='%s'",
934 		authldaprc.ldap_basedn.c_str(), enumerate_filter.c_str());
935 
936 	authldaprc_search_attributes search_attributes(attribute_vector);
937 
938 	struct timeval tv;
939 	tv.tv_sec=60*60;
940 	tv.tv_usec=0;
941 
942 	if (ldap_search_ext(main_connection.connection,
943 			    authldaprc.ldap_basedn.c_str(),
944 			    LDAP_SCOPE_SUBTREE,
945 			    enumerate_filter.c_str(),
946 			    search_attributes.search_attributes(),
947 			    0,
948 			    NULL, NULL, &tv, 1000000, &msgid) < 0)
949 	{
950 		DPRINTF("ldap_search_ext failed");
951 		return;
952 	}
953 
954 	/* Process results */
955 
956 	while (1)
957 	{
958 		struct timeval timeout;
959 		LDAPMessage *entry;
960 
961 		timeout.tv_sec=authldaprc.timeout;
962 		timeout.tv_usec=0;
963 
964 		authldaprc_search_result search_result(main_connection,
965 						       msgid,
966 						       false,
967 						       timeout);
968 
969 		if (search_result.finished)
970 			break;
971 
972 		if (!search_result)
973 			return;
974 
975 		entry = ldap_first_entry(main_connection.connection, search_result.ptr);
976 
977 		while (entry)
978 		{
979 			std::vector<std::string>
980 				names=authldap_entry_values(main_connection.connection,
981 							    entry,
982 							    mail_field);
983 
984 			if (names.empty())
985 			{
986 				entry = ldap_next_entry(main_connection.connection, entry);
987 				continue;
988 			}
989 
990 			size_t n=names.size();
991 
992 			if (n > 0)
993 			{
994 				std::string uid_s, gid_s, homedir, maildir;
995 				uid_t uid=authldaprc.uid;
996 				gid_t gid=authldaprc.gid;
997 
998 				authldap_get_values
999 					get_value(main_connection.connection, entry,
1000 						  names[0]);
1001 
1002 				if (!uid_field.empty())
1003 					get_value(uid_field, uid_s);
1004 
1005 				if (!gid_field.empty())
1006 				{
1007 					get_value(gid_field, gid_s);
1008 				}
1009 
1010 				get_value(homedir_field, homedir);
1011 				get_value(maildir_field, maildir);
1012 
1013 				if (!uid_s.empty())
1014 					std::istringstream(uid_s)
1015 						>> uid;
1016 
1017 				if (!gid_s.empty())
1018 					std::istringstream(gid_s)
1019 						>> gid;
1020 
1021 				std::string options=get_value.options();
1022 
1023 				for (size_t j=0; j < n; j++)
1024 				{
1025 					if (!homedir.empty())
1026 						(*cb_func)(names[j].c_str(),
1027 							   uid, gid,
1028 							   homedir.c_str(),
1029 							   maildir.empty()
1030 							   ? 0:maildir.c_str(),
1031 							   options.empty()
1032 							   ? 0:options.c_str(),
1033 							   void_arg);
1034 				}
1035 			}
1036 
1037 			entry = ldap_next_entry(main_connection.connection, entry);
1038 		}
1039 	}
1040 
1041 	/* Success */
1042 	(*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
1043 }
1044 
cpp_authldapclose()1045 static void cpp_authldapclose()
1046 {
1047 	main_connection.disconnect();
1048 	bind_connection.disconnect();
1049 }
1050 
1051 class authldap_lookup : private authldaprc_attributes {
1052 
1053 	struct authinfo auth;
1054 	const char *service;
1055 	std::string attrname;
1056 	std::string user;
1057 	const char *pass;
1058 	const char *newpass;
1059 	const char *authaddr;
1060 
1061 public:
1062 	authldap_lookup(const char *serviceArg,
1063 			const std::string &attrnameArg,
1064 			const std::string &userArg,
1065 			const char *passArg,
1066 			const char *newpassArg,
1067 			const char *authaddrArg);
1068 
1069 	int operator()(int (*callback)(struct authinfo *, void *), void *arg);
1070 
1071 private:
1072 	int verify_password(const std::string &dn);
1073 	int verify_password_myself(const std::string &dn);
1074 	int verify_password_authbind(const std::string &dn);
1075 };
1076 
authldap_lookup(const char * serviceArg,const std::string & attrnameArg,const std::string & userArg,const char * passArg,const char * newpassArg,const char * authaddrArg)1077 authldap_lookup::authldap_lookup(const char *serviceArg,
1078 				 const std::string &attrnameArg,
1079 				 const std::string &userArg,
1080 				 const char *passArg,
1081 				 const char *newpassArg,
1082 				 const char *authaddrArg)
1083 	: service(serviceArg),
1084 	  attrname(attrnameArg),
1085 	  user(userArg),
1086 	  pass(passArg),
1087 	  newpass(newpassArg),
1088 	  authaddr(authaddrArg)
1089 {
1090 }
1091 
operator ()(int (* callback)(struct authinfo *,void *),void * arg)1092 int authldap_lookup::operator()(int (*callback)(struct authinfo *, void *),
1093 				void *arg)
1094 {
1095 	struct timeval timeout;
1096 
1097 	LDAPMessage *entry;
1098 
1099 	std::string dn;
1100 
1101 	std::string homeDir, mailDir, userPassword,
1102 		cryptPassword,
1103 		cn,
1104 		uidNumber,
1105 		gidNumber, quota;
1106 
1107 	uid_t au;
1108 	gid_t ag;
1109 	int rc;
1110 
1111 	std::ostringstream query;
1112 
1113 	std::string filter;
1114 
1115 	authldaprc.config("LDAP_FILTER", filter, false);
1116 
1117 	if (!filter.empty())
1118 	{
1119 		query << "(&" << filter;
1120 	}
1121 
1122 	query << "(" << attrname << "=" << user;
1123 
1124 	std::string domain;
1125 
1126 	authldaprc.config("LDAP_DOMAIN", domain, false);
1127 
1128 	if (!domain.empty() &&
1129 	     std::find(user.begin(), user.end(), '@') == user.end())
1130 		query << "@" << domain;
1131 	query << ")";
1132 
1133 	if (!filter.empty())
1134 		query << ")";
1135 
1136 	std::string query_str=query.str();
1137 
1138 	DPRINTF("Query: %s", query_str.c_str());
1139 
1140 	timeout.tv_sec=authldaprc.timeout;
1141 	timeout.tv_usec=0;
1142 
1143 	attribute("LDAP_HOMEDIR", "homeDir", homeDir);
1144 	attribute(service && strcmp(service, "courier") == 0
1145 		  ? "LDAP_DEFAULTDELIVERY":"LDAP_MAILDIR", 0, mailDir);
1146 	attribute("LDAP_FULLNAME", 0, cn);
1147 	std::string clearpw_value=attribute("LDAP_CLEARPW", 0, userPassword);
1148 	std::string cryptpw_value=attribute("LDAP_CRYPTPW", 0, cryptPassword);
1149 	attribute("LDAP_UID", 0, uidNumber);
1150 	attribute("LDAP_GID", 0, gidNumber);
1151 	attribute("LDAP_MAILDIRQUOTA", 0, quota);
1152 
1153 	authldaprc_attribute_vector all_attributes(attributes);
1154 
1155 	for (size_t i=0; i<authldaprc.auxoptions.size(); i++)
1156 	{
1157 		all_attributes.push_back(authldaprc.auxoptions[i]);
1158 	}
1159 
1160 	authldaprc_search_result result(main_connection,
1161 					authldaprc.ldap_basedn,
1162 					query_str,
1163 					all_attributes,
1164 					timeout);
1165 
1166 	int n_entries=ldap_count_entries(main_connection.connection, result.ptr);
1167 
1168 	if (n_entries != 1)
1169 	{
1170 		if (n_entries == 0)
1171 		{
1172 			DPRINTF("Not found");
1173 		}
1174 		else
1175 		{
1176 			DPRINTF("Returned multiple entries (%d)", n_entries);
1177 		}
1178 		return -1;
1179 	}
1180 
1181 	char *dn_p = ldap_get_dn(main_connection.connection, result.ptr);
1182 
1183 	DPRINTF("Returned DN: %s", dn_p ? dn_p : "<null>");
1184 
1185 	if (dn_p == NULL)
1186 	{
1187 		DPRINTF("ldap_get_dn failed");
1188 		return -1;
1189 	}
1190 
1191 	dn=dn_p;
1192 	free(dn_p);
1193 
1194 	/* Get the pointer on this result */
1195 	entry=ldap_first_entry(main_connection.connection, result.ptr);
1196 	if (entry==NULL)
1197 	{
1198 		DPRINTF("ldap_first_entry failed");
1199 		return -1;
1200 	}
1201 
1202 	/* print all the raw attributes */
1203 	if (courier_authdebug_login_level >= 2)
1204 	{
1205 		BerElement *berptr = 0;
1206 		char *attr=
1207 			ldap_first_attribute(main_connection.connection, entry, &berptr);
1208 
1209 		while (attr)
1210 		{
1211 			std::vector<std::string>
1212 				values=authldap_entry_values(main_connection.connection,
1213 							     entry,
1214 							     attr);
1215 			for (size_t i=0; i<values.size(); ++i)
1216 				DPRINTF("    %s: %s", attr, values[i].c_str());
1217 
1218 			ldap_memfree(attr);
1219 			attr = ldap_next_attribute(main_connection.connection, entry,
1220 						   berptr);
1221 		}
1222 
1223 		ber_free(berptr, 0);
1224 	}
1225 
1226 	authldap_get_values get_value(main_connection.connection, entry, dn.c_str());
1227 
1228 	for (std::map<std::string, std::vector<std::string *> >::iterator
1229 		     p=attributes.begin(); p != attributes.end(); ++p)
1230 	{
1231 		get_value(p->first, p->second);
1232 	}
1233 
1234 	au=authldaprc.uid;
1235 	ag=authldaprc.gid;
1236 	if (!uidNumber.empty())
1237 	{
1238 		std::istringstream(uidNumber) >> au;
1239 	}
1240 
1241 	if (!gidNumber.empty())
1242 	{
1243 		std::istringstream(gidNumber) >> ag;
1244 	}
1245 
1246 	std::string mailroot;
1247 
1248 	authldaprc.config("LDAP_MAILROOT", mailroot, false);
1249 
1250 	if (!homeDir.empty() && !mailroot.empty())
1251 	{
1252 		homeDir = mailroot + "/" + homeDir;
1253 	}
1254 
1255 	std::string options=get_value.options();
1256 
1257 	memset(&auth, 0, sizeof(auth));
1258 
1259 	auth.sysuserid= &au;
1260 	auth.sysgroupid= ag;
1261 	auth.homedir=homeDir.c_str();
1262 	auth.address=authaddr;
1263 	auth.fullname=cn.c_str();
1264 
1265 	if (!mailDir.empty())
1266 		auth.maildir=mailDir.c_str();
1267 
1268 	if (!userPassword.empty())
1269 		auth.clearpasswd=userPassword.c_str();
1270 
1271 	if (!cryptPassword.empty())
1272 		auth.passwd=cryptPassword.c_str();
1273 
1274 	if (!quota.empty())
1275 		auth.quota=quota.c_str();
1276 
1277 	if (!options.empty())
1278 		auth.options=options.c_str();
1279 
1280 	rc=0;
1281 
1282 	if (au == 0 || ag == 0)
1283 	{
1284 		courier_auth_err("authldaplib: refuse to authenticate %s: uid=%d, gid=%d (zero uid or gid not permitted)",
1285 				 user.c_str(), au, ag);
1286 		rc= 1;
1287 	}
1288 
1289 	courier_authdebug_authinfo("DEBUG: authldaplib: ", &auth,
1290 				   userPassword.c_str(),
1291 				   cryptPassword.c_str());
1292 
1293 	if (rc == 0)
1294 		rc=verify_password(dn);
1295 
1296 	std::string newpass_crypt;
1297 
1298 	if (rc == 0 && newpass)
1299 	{
1300 		char *p=authcryptpasswd(newpass, auth.passwd);
1301 
1302 		if (!p)
1303 			rc=-1;
1304 		else
1305 		{
1306 			newpass_crypt=p;
1307 			free(p);
1308 		}
1309 
1310 		LDAPMod *mods[3];
1311 		int mod_index=0;
1312 
1313 		LDAPMod mod_clear, mod_crypt;
1314 		char *mod_clear_vals[2], *mod_crypt_vals[2];
1315 
1316 		std::vector<char> clearpw_buffer, cryptpw_buffer;
1317 
1318 		if (!clearpw_value.empty() && auth.clearpasswd)
1319 		{
1320 			clearpw_buffer.insert(clearpw_buffer.end(),
1321 					      clearpw_value.begin(),
1322 					      clearpw_value.end());
1323 
1324 			clearpw_buffer.push_back(0);
1325 
1326 			mods[mod_index]= &mod_clear;
1327 			mod_clear.mod_op=LDAP_MOD_REPLACE;
1328 			mod_clear.mod_type=&clearpw_buffer[0];
1329 			mod_clear.mod_values=mod_clear_vals;
1330 
1331 			DPRINTF("Modify: %s", mod_clear.mod_type);
1332 
1333 			mod_clear_vals[0]=(char *)newpass;
1334 			mod_clear_vals[1]=NULL;
1335 			++mod_index;
1336 		}
1337 
1338 		if (!cryptpw_value.empty() &&
1339 		    !newpass_crypt.empty() && auth.passwd)
1340 		{
1341 			cryptpw_buffer.insert(cryptpw_buffer.end(),
1342 					      cryptpw_value.begin(),
1343 					      cryptpw_value.end());
1344 
1345 			cryptpw_buffer.push_back(0);
1346 
1347 			mods[mod_index]= &mod_crypt;
1348 			mod_crypt.mod_op=LDAP_MOD_REPLACE;
1349 			mod_crypt.mod_type=&cryptpw_buffer[0];
1350 			mod_crypt.mod_values=mod_crypt_vals;
1351 
1352 			DPRINTF("Modify: %s", mod_crypt.mod_type);
1353 
1354 			newpass_crypt.push_back(0);
1355 			mod_crypt_vals[0]=&newpass_crypt[0];
1356 			mod_crypt_vals[1]=NULL;
1357 			++mod_index;
1358 		}
1359 		if (mod_index == 0)
1360 			rc= -1;
1361 		else
1362 		{
1363 			mods[mod_index]=0;
1364 
1365 			/*
1366 			** Use the user connection for updating the password.
1367 			*/
1368 
1369 			int ld_errno=
1370 				ldap_modify_ext_s(bind_connection.connected()
1371 						  ? bind_connection.connection
1372 						  : main_connection.connection,
1373 						  dn.c_str(), mods,
1374 						  NULL, NULL);
1375 
1376 			if (ld_errno != LDAP_SUCCESS)
1377 			{
1378 				rc= -1;
1379 				DPRINTF("Password update failed: %s",
1380 					ldap_err2string(ld_errno));
1381 			}
1382 		}
1383 	}
1384 
1385 	if (rc == 0 && callback)
1386 	{
1387 		if (!auth.clearpasswd)
1388 			auth.clearpasswd=pass;
1389 		rc= (*callback)(&auth, arg);
1390 	}
1391 
1392 	return (rc);
1393 }
1394 
verify_password(const std::string & dn)1395 int authldap_lookup::verify_password(const std::string &dn)
1396 {
1397 	if (!pass)
1398 		return 0;
1399 
1400 	if (authldaprc.authbind)
1401 		return verify_password_authbind(dn);
1402 
1403 	return verify_password_myself(dn);
1404 }
1405 
verify_password_authbind(const std::string & dn)1406 int authldap_lookup::verify_password_authbind(const std::string &dn)
1407 {
1408 	if (!bind_connection.connect())
1409 		return 1;
1410 
1411 	if (!bind_connection.bind(dn, pass))
1412 	{
1413 		bind_connection.close();
1414 		return -1;
1415 	}
1416 
1417 	if (authldaprc.protocol_version == 2)
1418 	{
1419 		// protocol version 2 does not allow rebinds.
1420 
1421 		bind_connection.close();
1422 	}
1423 
1424 	return 0;
1425 }
1426 
verify_password_myself(const std::string & dn)1427 int authldap_lookup::verify_password_myself(const std::string &dn)
1428 {
1429 	if (auth.clearpasswd)
1430 	{
1431 		if (strcmp(pass, auth.clearpasswd))
1432 		{
1433 			if (courier_authdebug_login_level >= 2)
1434 			{
1435 				DPRINTF("Password for %s: '%s' does not match clearpasswd '%s'",
1436 					dn.c_str(), pass, auth.clearpasswd);
1437 			}
1438 			else
1439 			{
1440 				DPRINTF("Password for %s does not match",
1441 					dn.c_str());
1442 			}
1443 			return -1;
1444 		}
1445 	}
1446 	else
1447 	{
1448 		const char *p=auth.passwd;
1449 
1450 		if (!p)
1451 		{
1452 			DPRINTF("Missing password in LDAP!");
1453 			return -1;
1454 		}
1455 
1456 		if (authcheckpassword(pass, p))
1457 		{
1458 			DPRINTF("Password for %s does not match",
1459 				dn.c_str());
1460 			return -1;
1461 		}
1462         }
1463 
1464 	return 0;
1465 }
1466 
1467 /*
1468 ** Replace keywords in the emailmap string.
1469 */
1470 
emailmap_replace(const char * str,const std::string & user_value,const std::string & realm_value)1471 static std::string emailmap_replace(const char *str,
1472 				    const std::string &user_value,
1473 				    const std::string &realm_value)
1474 {
1475 	std::ostringstream o;
1476 
1477 	while (*str)
1478 	{
1479 		const char *p=str;
1480 
1481 		while (*str && *str != '@')
1482 			++str;
1483 
1484 		o << std::string(p, str);
1485 
1486 		if (*str != '@')
1487 			continue;
1488 
1489 		++str;
1490 
1491 		p=str;
1492 		while (*str && *str != '@')
1493 			++str;
1494 
1495 		std::string key(p, str);
1496 
1497 		if (*str)
1498 			++str;
1499 
1500 		if (key == "user")
1501 			o << user_value;
1502 		else if (key == "realm")
1503 			o << realm_value;
1504 	}
1505 
1506 	return o.str();
1507 }
1508 
auth_ldap_try(const char * service,const char * unquoted_user,const char * pass,int (* callback)(struct authinfo *,void *),void * arg,const char * newpass)1509 static int auth_ldap_try(const char *service,
1510 			 const char *unquoted_user, const char *pass,
1511 			 int (*callback)(struct authinfo *, void *),
1512 			 void *arg, const char *newpass)
1513 {
1514 	std::string user;
1515 
1516 	{
1517 		char *q=courier_auth_ldap_escape(unquoted_user);
1518 
1519 		user=q;
1520 		free(q);
1521 	}
1522 
1523 	if (ldapopen())	return (-1);
1524 
1525 	std::string::iterator at=std::find(user.begin(), user.end(), '@');
1526 
1527 	std::string emailmap;
1528 
1529 	authldaprc.config("LDAP_EMAILMAP", emailmap, false);
1530 
1531 	std::string mail;
1532 
1533 	if (!authldaprc.config("LDAP_MAIL", mail, false, "mail"))
1534 		return -1;
1535 
1536 	if (emailmap.empty() || at == user.end())
1537 	{
1538 		authldap_lookup real_lookup(service, mail, user, pass,
1539 					    newpass, user.c_str());
1540 
1541 		return real_lookup(callback, arg);
1542 	}
1543 
1544 	std::string user_value(user.begin(), at);
1545 	std::string realm_value(++at, user.end());
1546 
1547 	std::string query=
1548 		emailmap_replace(emailmap.c_str(), user_value, realm_value);
1549 
1550 	DPRINTF("using emailmap search: %s", query.c_str());
1551 
1552 	struct timeval tv;
1553 
1554 	tv.tv_sec=authldaprc.timeout;
1555 	tv.tv_usec=0;
1556 
1557 	std::vector<std::string> attributes;
1558 
1559 	attributes.push_back("");
1560 
1561 	authldaprc.config("LDAP_EMAILMAP_ATTRIBUTE", attributes[0], false);
1562 
1563 	if (attributes[0].empty())
1564 		attributes[0]="handle";
1565 
1566 	std::string basedn;
1567 
1568 	if (!authldaprc.config("LDAP_EMAILMAP_BASEDN", basedn, true))
1569 		return -1;
1570 
1571 	authldaprc_search_result
1572 		lookup(main_connection,
1573 		       basedn,
1574 		       query,
1575 		       attributes,
1576 		       tv);
1577 
1578 	if (!lookup)
1579 	{
1580 		if (main_connection.connection)	return (-1);
1581 		return (1);
1582 	}
1583 
1584 	int cnt=ldap_count_entries(main_connection.connection, lookup.ptr);
1585 
1586 	if (cnt != 1)
1587 	{
1588 		courier_auth_err("emailmap: %d entries returned from search %s (but we need exactly 1)",
1589 		    cnt, query.c_str());
1590 		return -1;
1591 	}
1592 
1593 	LDAPMessage *entry=ldap_first_entry(main_connection.connection, lookup.ptr);
1594 
1595 	if (!entry)
1596 	{
1597 		courier_auth_err("authldap: unexpected NULL from ldap_first_entry");
1598 		return -1;
1599 	}
1600 
1601 	authldap_get_values get_value(main_connection.connection, entry, query);
1602 
1603 	std::string v;
1604 
1605 	get_value(attributes[0], v);
1606 
1607 	if (v.empty())
1608 	{
1609 		DPRINTF("emailmap: empty attribute");
1610 		return (-1);
1611 	}
1612 
1613 
1614 	std::string attrname;
1615 
1616 	authldaprc.config("LDAP_EMAILMAP_MAIL", attrname, false);
1617 
1618 	if (attrname.empty())
1619 	{
1620 		attrname=mail;
1621 	}
1622 
1623 	DPRINTF("emailmap: attribute=%s, value=%s", attrname.c_str(),
1624 		v.c_str());
1625 
1626 	authldap_lookup real_lookup(service, attrname, v.c_str(), pass,
1627 				    newpass, user.c_str());
1628 
1629 	return real_lookup(callback, arg);
1630 }
1631 
1632 // Try the query once. If there's a connection-level error, try again.
1633 
auth_ldap_retry(const char * service,const char * unquoted_user,const char * pass,int (* callback)(struct authinfo *,void *),void * arg,const char * newpass)1634 static int auth_ldap_retry(const char *service,
1635 			   const char *unquoted_user,
1636 			   const char *pass,
1637 			   int (*callback)(struct authinfo *, void *),
1638 			   void *arg, const char *newpass)
1639 {
1640 	int rc=auth_ldap_try(service, unquoted_user, pass, callback, arg,
1641 			     newpass);
1642 
1643 	if (rc > 0)
1644 		rc=auth_ldap_try(service, unquoted_user, pass, callback, arg,
1645 				   newpass);
1646 
1647 	return rc;
1648 }
1649 
1650 extern "C" {
1651 
1652 #if 0
1653 };
1654 #endif
1655 
auth_ldap_changepw(const char * dummy,const char * user,const char * pass,const char * newpass)1656 int auth_ldap_changepw(const char *dummy, const char *user,
1657 		       const char *pass,
1658 		       const char *newpass)
1659 {
1660 	if (!authldaprc.load())
1661 		return 1;
1662 
1663 	return auth_ldap_retry("authlib", user, pass, NULL, NULL, newpass);
1664 }
1665 
authldapcommon(const char * service,const char * user,const char * pass,int (* callback)(struct authinfo *,void *),void * arg)1666 int authldapcommon(const char *service,
1667 		   const char *user, const char *pass,
1668 		   int (*callback)(struct authinfo *, void *),
1669 		   void *arg)
1670 {
1671 	if (!authldaprc.load())
1672 		return 1;
1673 	return (auth_ldap_retry(service, user, pass, callback, arg, NULL));
1674 }
1675 
authldapclose()1676 void authldapclose()
1677 {
1678 	cpp_authldapclose();
1679 }
1680 
auth_ldap_enumerate(void (* cb_func)(const char * name,uid_t uid,gid_t gid,const char * homedir,const char * maildir,const char * options,void * void_arg),void * void_arg)1681 void auth_ldap_enumerate( void(*cb_func)(const char *name,
1682 					 uid_t uid,
1683 					 gid_t gid,
1684 					 const char *homedir,
1685 					 const char *maildir,
1686 					 const char *options,
1687 					 void *void_arg),
1688 			   void *void_arg)
1689 {
1690 	if (!authldaprc.load())
1691 		return;
1692 
1693 	cpp_auth_ldap_enumerate(cb_func, void_arg);
1694 }
1695 
1696 #if 0
1697 {
1698 #endif
1699 }
1700