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