From 3c7e434b7ee4d4c141a1b2c05293856fcdc4d92b Mon Sep 17 00:00:00 2001
From: Julius Kriukas <julius.kriukas@gmail.com>
Date: Tue, 18 Sep 2012 11:54:29 +0300
Subject: [PATCH] LDAPv3, ldaps, starttls support

---
 README.md       | 11 +++++++++++
 data/header.txt |  2 +-
 doc/gnarwl.man  |  9 +++++++++
 src/config.c    | 20 +++++++++++++++++---
 src/config.h    |  3 +++
 src/dbaccess.c  | 34 ++++++++++++++++++++++------------
 6 files changed, 63 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md
index c9f9237..a106a6b 100644
--- a/README.md
+++ b/README.md
@@ -45,3 +45,14 @@ Gnarwl features:
     object in the outgoing mail.
 *   Unicode support.
 *   GPL software.
+
+Improvements
+------------
+
+
+*   LDAP protocol version 3 is used by default.
+*   New config option *server_uri* allows using more than one LDAP server for 
+    failover.
+*   New config option *ca_cert* allow specifying CA certificate when ldaps or 
+    StartTLS is used.
+*   New config option *starttls*.
diff --git a/data/header.txt b/data/header.txt
index 7f04140..3c1937f 100644
--- a/data/header.txt
+++ b/data/header.txt
@@ -2,7 +2,7 @@ From: $fullname <$recepient>
 To: $sender
 X-mailer: GNARWL
 MIME-Version: 1.0
-Content-Type: text/plain
+Content-Type: text/plain; charset=UTF-8
 Content-Transfer-Encoding: 8bit
 Subject: Re: $subject
 
diff --git a/doc/gnarwl.man b/doc/gnarwl.man
index 4f6aea9..bdb8198 100644
--- a/doc/gnarwl.man
+++ b/doc/gnarwl.man
@@ -75,6 +75,15 @@ email. Defaults to "$subject".
 Binds a macroname (case insensitive), refering to a field in the resultset, 
 returned by the database. There are no defaults for this directive.
 
+.IP "server_uri <ldap_uri [ldap_uri ...]>"
+URI of the databaseserver to query. Multiple URIs can be specified. No default value.
+
+.IP "starttls <0|1>"
+Enforce StartTLS on connect. Defaults to 0.
+
+.IP "ca_cert </path/to/certificate>"
+Use specified CA cert to validate ldap server certificate. No default value.
+
 .IP "server <address>"
 Address of the databaseserver to query. Defaults to localhost.
 
diff --git a/src/config.c b/src/config.c
index 29096a9..bdc18bd 100644
--- a/src/config.c
+++ b/src/config.c
@@ -80,6 +80,18 @@ void putEntry(char* key, char* val) {
     return;
   }
   
+  if (!strcasecmp(key,"server_uri")) {
+    cfg.uri=val;
+    return;
+  }
+  if (!strcasecmp(key,"ca_cert")) {
+    cfg.ca_cert=val;
+    return;
+  }
+  if (!strcasecmp(key,"starttls")) {
+    cfg.starttls = atoi(val);
+    return;
+  }
   if (!strcasecmp(key,"server")) {
     cfg.server=val;
     return;
@@ -174,8 +186,8 @@ void putEntry(char* key, char* val) {
   }
   if (!strcasecmp(key,"protocol")) {
     switch(atoi(val)) {
-      case 2: {cfg.protver=PROTVER2; break;}
-      case 3: {cfg.protver=PROTVER3; break;}
+      case 2: {cfg.protver = LDAP_VERSION2; break;}
+      default: {cfg.protver = LDAP_VERSION3; break;}
     }
     return;
   }
@@ -186,9 +198,11 @@ void putEntry(char* key, char* val) {
 void setDefaults(void) {
   cfg.umask=UMASK;
   cfg.base=DEFAULT_BASE;
-  cfg.protver=LDAP_PROTOCOL_DETECT;
+  cfg.protver=LDAP_VERSION3;
   cfg.uid=NULL;
   cfg.pwd=NULL;
+  cfg.uri=NULL;
+  cfg.starttls=0;
   cfg.server=DEFAULT_SERVER;
   cfg.qfilter=DEFAULT_QFILTER;
   cfg.mfilter=NULL;
diff --git a/src/config.h b/src/config.h
index c9db0f9..82ccb76 100644
--- a/src/config.h
+++ b/src/config.h
@@ -2,6 +2,9 @@
  * Holds the data of the config file
  */
 struct conf {
+  char *uri;		// LDAP uri
+  char *ca_cert;	// LDAP TLS_CACERTFILE
+  int starttls;     //
   char *base;		// LDAP base
   char *uid;		// LDAP bind dn
   char *pwd;		// LDAP bind password
diff --git a/src/dbaccess.c b/src/dbaccess.c
index 8fc57c2..4bda734 100644
--- a/src/dbaccess.c
+++ b/src/dbaccess.c
@@ -176,24 +176,34 @@ int dbContains(const char* str_ptr, GDBM_FILE dbf_ptr) {
 
 void dbConnect() {
   int rc;
-  
-  ldcon=ldap_init(cfg.server,cfg.port);
+
+  if (cfg.ca_cert) {
+    rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, cfg.ca_cert);
+    if (rc != LDAP_SUCCESS) {
+        syslog(LOG_MAIL|LOG_ERR,"CRIT/LDAP Set option TLS_CACERTFILE failed: %s",ldap_err2string(rc));
+        exit(EXIT_FAILURE);
+    }
+  }
+
+  if (cfg.uri)
+    rc = ldap_initialize(&ldcon, cfg.uri);
+  else
+    ldcon=ldap_init(cfg.server,cfg.port);
+
   if (ldcon==NULL) {
     syslog(LOG_MAIL|LOG_ERR,"CRIT/LDAP Connection failed");
     exit(EXIT_FAILURE);
   }
 
-#ifdef HAVE_LDAP_SET_OPTION
-  if (cfg.protver==LDAP_PROTOCOL_DETECT) {
-    int prot=LDAP_VERSION2;
-    ldap_set_option(ldcon,LDAP_OPT_PROTOCOL_VERSION, &prot);
-    if (ldap_simple_bind_s(ldcon,cfg.uid,cfg.pwd)==LDAP_SUCCESS) return;
-    prot=LDAP_VERSION3;
-    ldap_set_option(ldcon,LDAP_OPT_PROTOCOL_VERSION, &prot);
-    if (ldap_simple_bind_s(ldcon,cfg.uid,cfg.pwd)==LDAP_SUCCESS) return;
+  ldap_set_option(ldcon, LDAP_OPT_PROTOCOL_VERSION, &cfg.protver);
+
+  if (cfg.starttls) {
+    rc = ldap_start_tls_s(ldcon, NULL, NULL);
+    if (rc != LDAP_SUCCESS) {
+        syslog(LOG_MAIL|LOG_ERR,"CRIT/LDAP StartTLS failed: %s",ldap_err2string(rc));
+        exit(EXIT_FAILURE);
+    }
   }
-  else ldap_set_option(ldcon,LDAP_OPT_PROTOCOL_VERSION, &cfg.protver);
-#endif
 
   rc=ldap_simple_bind_s(ldcon,cfg.uid,cfg.pwd);
   if (rc!=LDAP_SUCCESS) {
-- 
2.9.3

