From: "Dr. Tobias Quathamer" <toddy@debian.org>
Date: Wed, 29 Nov 2017 14:34:16 +0100
Subject: Require explicit intention for empty password.

This is normally used for unauthenticated bind, and
https://tools.ietf.org/html/rfc4513#section-5.1.2 recommends:

> Clients SHOULD disallow an empty password input to a Name/Password
> Authentication user interface

This is (mostly) a cherry-pick of 95ede12 from upstream. I've removed
the bit in ldap_test.go, which is unrelated to the security issue.

This fixes CVE-2017-14623.

https://github.com/go-ldap/ldap/commit/95ede1266b237bf8e9aa5dce0b3250e51bfefe66
---
 bind.go  | 80 ++++++++++++++++++++++++++++++----------------------------------
 error.go |  9 ++++++++
 2 files changed, 46 insertions(+), 43 deletions(-)

diff --git a/bind.go b/bind.go
index 26b3cc7..432efa7 100644
--- a/bind.go
+++ b/bind.go
@@ -7,7 +7,7 @@ package ldap
 import (
 	"errors"
 
-	"gopkg.in/asn1-ber.v1"
+	ber "gopkg.in/asn1-ber.v1"
 )
 
 // SimpleBindRequest represents a username/password bind operation
@@ -18,6 +18,9 @@ type SimpleBindRequest struct {
 	Password string
 	// Controls are optional controls to send with the bind request
 	Controls []Control
+	// AllowEmptyPassword sets whether the client allows binding with an empty password
+	// (normally used for unauthenticated bind).
+	AllowEmptyPassword bool
 }
 
 // SimpleBindResult contains the response from the server
@@ -28,9 +31,10 @@ type SimpleBindResult struct {
 // NewSimpleBindRequest returns a bind request
 func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
 	return &SimpleBindRequest{
-		Username: username,
-		Password: password,
-		Controls: controls,
+		Username:           username,
+		Password:           password,
+		Controls:           controls,
+		AllowEmptyPassword: false,
 	}
 }
 
@@ -47,6 +51,10 @@ func (bindRequest *SimpleBindRequest) encode() *ber.Packet {
 
 // SimpleBind performs the simple bind operation defined in the given request
 func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) {
+	if simpleBindRequest.Password == "" && !simpleBindRequest.AllowEmptyPassword {
+		return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
+	}
+
 	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
 	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
 	encodedBindRequest := simpleBindRequest.encode()
@@ -97,47 +105,33 @@ func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResu
 	return result, nil
 }
 
-// Bind performs a bind with the given username and password
+// Bind performs a bind with the given username and password.
+//
+// It does not allow unauthenticated bind (i.e. empty password). Use the UnauthenticatedBind method
+// for that.
 func (l *Conn) Bind(username, password string) error {
-	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
-	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
-	bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
-	bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
-	bindRequest.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, username, "User Name"))
-	bindRequest.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, password, "Password"))
-	packet.AppendChild(bindRequest)
-
-	if l.Debug {
-		ber.PrintPacket(packet)
-	}
-
-	msgCtx, err := l.sendMessage(packet)
-	if err != nil {
-		return err
-	}
-	defer l.finishMessage(msgCtx)
-
-	packetResponse, ok := <-msgCtx.responses
-	if !ok {
-		return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
-	}
-	packet, err = packetResponse.ReadPacket()
-	l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
-	if err != nil {
-		return err
-	}
-
-	if l.Debug {
-		if err := addLDAPDescriptions(packet); err != nil {
-			return err
-		}
-		ber.PrintPacket(packet)
+	req := &SimpleBindRequest{
+		Username:           username,
+		Password:           password,
+		AllowEmptyPassword: false,
 	}
+	_, err := l.SimpleBind(req)
+	return err
+}
 
-	resultCode, resultDescription := getLDAPResultCode(packet)
-	if resultCode != 0 {
-		return NewError(resultCode, errors.New(resultDescription))
+// UnauthenticatedBind performs an unauthenticated bind.
+//
+// A username may be provided for trace (e.g. logging) purpose only, but it is normally not
+// authenticated or otherwise validated by the LDAP server.
+//
+// See https://tools.ietf.org/html/rfc4513#section-5.1.2 .
+// See https://tools.ietf.org/html/rfc4513#section-6.3.1 .
+func (l *Conn) UnauthenticatedBind(username string) error {
+	req := &SimpleBindRequest{
+		Username:           username,
+		Password:           "",
+		AllowEmptyPassword: true,
 	}
-
-	return nil
+	_, err := l.SimpleBind(req)
+	return err
 }
diff --git a/error.go b/error.go
index ff69787..6e1277f 100644
--- a/error.go
+++ b/error.go
@@ -54,6 +54,7 @@ const (
 	ErrorDebugging          = 203
 	ErrorUnexpectedMessage  = 204
 	ErrorUnexpectedResponse = 205
+	ErrorEmptyPassword      = 206
 )
 
 // LDAPResultCodeMap contains string descriptions for LDAP error codes
@@ -97,6 +98,14 @@ var LDAPResultCodeMap = map[uint8]string{
 	LDAPResultObjectClassModsProhibited:    "Object Class Mods Prohibited",
 	LDAPResultAffectsMultipleDSAs:          "Affects Multiple DSAs",
 	LDAPResultOther:                        "Other",
+
+	ErrorNetwork:            "Network Error",
+	ErrorFilterCompile:      "Filter Compile Error",
+	ErrorFilterDecompile:    "Filter Decompile Error",
+	ErrorDebugging:          "Debugging Error",
+	ErrorUnexpectedMessage:  "Unexpected Message",
+	ErrorUnexpectedResponse: "Unexpected Response",
+	ErrorEmptyPassword:      "Empty password not allowed by the client",
 }
 
 func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) {
