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