File: 0002-Require-explicit-intention-for-empty-password.patch

package info (click to toggle)
golang-github-go-ldap-ldap 2.4.1-1%2Bdeb9u1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 316 kB
  • sloc: makefile: 30; sh: 1
file content (169 lines) | stat: -rw-r--r-- 6,202 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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) {