Index: nselib/sslcert.lua
===================================================================
--- nselib/sslcert.lua	(revision 33038)
+++ nselib/sslcert.lua	(working copy)
@@ -10,12 +10,16 @@
 --
 -- * FTP
 -- * IMAP
+-- * LDAP
 -- * POP3
 -- * SMTP
 -- * XMPP
 --
 -- @author "Patrik Karlsson <patrik@cqure.net>"
 
+local asn1 = require "asn1"
+local bin = require "bin"
+local ldap = require "ldap"
 local nmap = require "nmap"
 local stdnse = require "stdnse"
 local string = require "string"
@@ -24,7 +28,7 @@
 
 StartTLS = {
 
-  -- TODO: Implement STARTTLS for LDAP, NNTP
+  -- TODO: Implement STARTTLS for NNTP
 
   ftp_prepare_tls_without_reconnect = function(host, port)
     local s = nmap.new_socket()
@@ -145,6 +149,85 @@
     return false, "Failed to connect to IMAP server"
   end,
 
+  ldap_prepare_tls_without_reconnect = function(host, port)
+    local s = nmap.new_socket()
+    -- Attempt to negotiate TLS over LDAP for services that support it
+    -- Works for LDAP (389)
+
+    -- Open a standard TCP socket
+    local status, error = s:connect(host, port, "tcp")
+    if not status then
+      return false, "Failed to connect to LDAP server"
+    end
+
+    -- Create an ExtendedRequest and specify the OID for the
+    -- Start TTLS operation (see http://www.ietf.org/rfc/rfc2830.txt)
+    local ExtendedRequest = 23
+    local ExtendedResponse = 24
+    local oid, ldapRequest, ldapRequestId
+    oid = ldap.encode("1.3.6.1.4.1.1466.20037")
+    ldapRequest = ldap.encodeLDAPOp(ExtendedRequest, true, oid)
+    ldapRequestId = ldap.encode(1)
+
+    -- Send the STARTTLS request
+    local encoder = asn1.ASN1Encoder:new()
+    local data = encoder:encodeSeq(ldapRequestId .. ldapRequest)
+    status = s:send(data)
+    if not status then
+      return false, "STARTTLS failed"
+    end
+
+    -- Decode the response
+    local response
+    status, response = s:receive()
+    if not status then
+      return false, "STARTTLS failed"
+    end
+
+    local decoder = asn1.ASN1Decoder:new()
+    local len, pos, messageId, ldapOp, tmp = ""
+    pos, len = decoder.decodeLength(response, 2)
+    pos, messageId = ldap.decode(response, pos)
+    pos, tmp = bin.unpack("C", response, pos)
+    ldapOp = asn1.intToBER(tmp)
+
+    if ldapOp.number ~= ExtendedResponse then
+      stdnse.print_debug(1, string.format(
+        "STARTTLS failed (got wrong op number: %d)", ldapOp.number))
+      return false, "STARTTLS failed"
+    end
+
+    local resultCode
+    pos, len = decoder.decodeLength(response, pos)
+    pos, resultCode = ldap.decode(response, pos)
+
+    if resultCode ~= 0 then
+      stdnse.print_debug(1, string.format(
+        "STARTTLS failed (LDAP error code is: %d)", resultCode))
+      return false, "STARTTLS failed"
+    end
+
+    -- Should have a solid TLS over LDAP session now...
+    return true,s
+  end,
+
+  ldap_prepare_tls = function(host, port)
+    local err
+    local status, s = StartTLS.ldat_prepare_tls_without_reconnect(host, port)
+    if status then
+      status,err = s:reconnect_ssl()
+      if not status then
+        stdnse.print_debug(
+          1,"Could not establish SSL session after STARTTLS command.")
+        s:close()
+        return false, "Failed to connect to LDAP server"
+      else
+        return true,s
+      end
+    end
+    return false, "Failed to connect to LDAP server"
+  end,
+
   pop3_prepare_tls_without_reconnect = function(host, port)
     local s = nmap.new_socket()
     -- Attempt to negotiate TLS over POP3 for services that support it
@@ -345,6 +428,8 @@
   [21] = StartTLS.ftp_prepare_tls,
   imap = StartTLS.imap_prepare_tls,
   [143] = StartTLS.imap_prepare_tls,
+  [ldap] = StartTLS.ldap_prepare_tls,
+  [389] = StartTLS.ldap_prepare_tls,
   pop3 = StartTLS.pop3_prepare_tls,
   [110] = StartTLS.pop3_prepare_tls,
   smtp = StartTLS.smtp_prepare_tls,
@@ -360,6 +445,8 @@
   [21] = StartTLS.ftp_prepare_tls_without_reconnect,
   imap = StartTLS.imap_prepare_tls_without_reconnect,
   [143] = StartTLS.imap_prepare_tls_without_reconnect,
+  ldap = StartTLS.ldap_prepare_tls_without_reconnect,
+  [389] = StartTLS.ldap_prepare_tls_without_reconnect,
   pop3 = StartTLS.pop3_prepare_tls_without_reconnect,
   [110] = StartTLS.pop3_prepare_tls_without_reconnect,
   smtp = StartTLS.smtp_prepare_tls_without_reconnect,
