You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by sh...@apache.org on 2021/10/29 16:12:33 UTC

[trafficcontrol] branch master updated: Update gopkg.in/ldap.v2 dependency to github.com/go-ldap/ldap/v3 (#6314)

This is an automated email from the ASF dual-hosted git repository.

shamrick pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 4f057c6  Update gopkg.in/ldap.v2 dependency to github.com/go-ldap/ldap/v3 (#6314)
4f057c6 is described below

commit 4f057c690dadb1eb50423caae11b998bb38e5447
Author: ocket8888 <oc...@apache.org>
AuthorDate: Fri Oct 29 10:12:19 2021 -0600

    Update gopkg.in/ldap.v2 dependency to github.com/go-ldap/ldap/v3 (#6314)
    
    * Update gopkg.in/ldap.v2 dependency to github.com/go-ldap/ldap/v3
    
    (cherry picked from commit 3fc4a41610117bb1585bf995a2d60ff547a7855c)
    
    * Remove extra license declaration
    
    * Remove vendor entries for removed dependencies
---
 LICENSE                                            |  17 +-
 go.mod                                             |   6 +-
 go.sum                                             |  11 +-
 traffic_ops/traffic_ops_golang/auth/ldap.go        |   2 +-
 .../Azure/go-ntlmssp}/LICENSE                      |   3 +-
 vendor/github.com/Azure/go-ntlmssp/README.md       |  29 ++
 .../Azure/go-ntlmssp/authenticate_message.go       | 183 +++++++
 vendor/github.com/Azure/go-ntlmssp/authheader.go   |  37 ++
 vendor/github.com/Azure/go-ntlmssp/avids.go        |  17 +
 .../Azure/go-ntlmssp/challenge_message.go          |  82 ++++
 .../github.com/Azure/go-ntlmssp/messageheader.go   |  21 +
 .../github.com/Azure/go-ntlmssp/negotiate_flags.go |  52 ++
 .../Azure/go-ntlmssp/negotiate_message.go          |  64 +++
 vendor/github.com/Azure/go-ntlmssp/negotiator.go   | 144 ++++++
 vendor/github.com/Azure/go-ntlmssp/nlmp.go         |  51 ++
 vendor/github.com/Azure/go-ntlmssp/unicode.go      |  29 ++
 vendor/github.com/Azure/go-ntlmssp/varfield.go     |  40 ++
 vendor/github.com/Azure/go-ntlmssp/version.go      |  20 +
 .../go-asn1-ber/asn1-ber}/LICENSE                  |   0
 .../go-asn1-ber/asn1-ber}/README.md                |   0
 .../go-asn1-ber/asn1-ber}/ber.go                   | 224 ++++++---
 .../go-asn1-ber/asn1-ber}/content_int.go           |   2 +-
 .../go-asn1-ber/asn1-ber/generalizedTime.go        | 105 ++++
 .../go-asn1-ber/asn1-ber}/header.go                |  25 +-
 .../go-asn1-ber/asn1-ber}/identifier.go            |  43 +-
 .../go-asn1-ber/asn1-ber}/length.go                |  12 +-
 vendor/github.com/go-asn1-ber/asn1-ber/real.go     | 157 ++++++
 .../go-asn1-ber/asn1-ber}/util.go                  |   2 +-
 .../ldap.v2 => github.com/go-ldap/ldap/v3}/LICENSE |   0
 vendor/github.com/go-ldap/ldap/v3/add.go           |  91 ++++
 vendor/github.com/go-ldap/ldap/v3/bind.go          | 540 +++++++++++++++++++++
 vendor/github.com/go-ldap/ldap/v3/client.go        |  32 ++
 vendor/github.com/go-ldap/ldap/v3/compare.go       |  61 +++
 .../ldap.v2 => github.com/go-ldap/ldap/v3}/conn.go | 177 +++++--
 .../go-ldap/ldap/v3}/control.go                    | 184 +++++--
 .../go-ldap/ldap/v3}/debug.go                      |  12 +-
 vendor/github.com/go-ldap/ldap/v3/del.go           |  59 +++
 .../ldap.v2 => github.com/go-ldap/ldap/v3}/dn.go   | 139 +++---
 .../ldap.v2 => github.com/go-ldap/ldap/v3}/doc.go  |   0
 vendor/github.com/go-ldap/ldap/v3/error.go         | 253 ++++++++++
 .../go-ldap/ldap/v3}/filter.go                     | 180 +++----
 .../ldap.v2 => github.com/go-ldap/ldap/v3}/ldap.go | 121 +++--
 vendor/github.com/go-ldap/ldap/v3/moddn.go         | 100 ++++
 vendor/github.com/go-ldap/ldap/v3/modify.go        | 177 +++++++
 .../go-ldap/ldap/v3}/passwdmodify.go               |  88 ++--
 vendor/github.com/go-ldap/ldap/v3/request.go       |  71 +++
 .../go-ldap/ldap/v3}/search.go                     | 229 ++++-----
 vendor/github.com/go-ldap/ldap/v3/unbind.go        |  37 ++
 vendor/github.com/go-ldap/ldap/v3/whoami.go        |  91 ++++
 vendor/gopkg.in/ldap.v2/.travis.yml                |  31 --
 vendor/gopkg.in/ldap.v2/Makefile                   |  52 --
 vendor/gopkg.in/ldap.v2/README.md                  |  53 --
 vendor/gopkg.in/ldap.v2/add.go                     | 113 -----
 vendor/gopkg.in/ldap.v2/atomic_value.go            |  13 -
 vendor/gopkg.in/ldap.v2/atomic_value_go13.go       |  28 --
 vendor/gopkg.in/ldap.v2/bind.go                    | 143 ------
 vendor/gopkg.in/ldap.v2/client.go                  |  27 --
 vendor/gopkg.in/ldap.v2/compare.go                 |  85 ----
 vendor/gopkg.in/ldap.v2/del.go                     |  84 ----
 vendor/gopkg.in/ldap.v2/error.go                   | 155 ------
 vendor/gopkg.in/ldap.v2/modify.go                  | 170 -------
 vendor/modules.txt                                 |  16 +-
 62 files changed, 3483 insertions(+), 1507 deletions(-)

diff --git a/LICENSE b/LICENSE
index 739f10b..685395b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -472,14 +472,19 @@ This product bundles miekg/dns, which is available under a BSD-3-Clause license.
 ./vendor/github.com/miekg/dns/LICENSE
 Refer to the above license for the full text.
 
-This product bundles asn1-ber.v1, which is available under an MIT license.
-@vendor/gopkg.in/asn1-ber.v1/*
-./vendor/gopkg.in/asn1-ber.v1/LICENSE
+This product bundles ldap/v3 component, which is available under an MIT license.
+@vendor/github.com/go-ldap/ldap/v3/*
+./vendor/github.com/go-ldap/ldap/v3/LICENSE
 Refer to the above license for the full text.
 
-This product bundles ldap.v2 component, which is available under an MIT license.
-@vendor/gopkg.in/ldap.v2/*
-./vendor/gopkg.in/ldap.v2/LICENSE
+This product bundles go-ntlmssp component, which is available under an MIT license.
+@vendor/github.com/Azure/go-ntlmssp/*
+./vendor/github.com/Azure/go-ntlmssp/LICENSE
+Refer to the above license for the full text.
+
+This product bundles go-asn1-ber component, which is available under an MIT license.
+@vendor/github.com/go-asn1-ber/asn1-ber/*
+vendor/github.com/go-asn1-ber/asn1-ber/LICENSE
 Refer to the above license for the full text.
 
 This product bundles an example video for fakeOrigin, which is available under an CC-PDM-1.0 license.
diff --git a/go.mod b/go.mod
index 682729c..3951a06 100644
--- a/go.mod
+++ b/go.mod
@@ -65,13 +65,15 @@ require (
 	golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac
 	golang.org/x/text v0.3.7 // indirect
 	gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0
-	gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225 // indirect
-	gopkg.in/ldap.v2 v2.5.1
 	gopkg.in/square/go-jose.v2 v2.3.1 // indirect
 	gopkg.in/yaml.v2 v2.3.0
 )
 
+require github.com/go-ldap/ldap/v3 v3.4.1
+
 require (
+	github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
+	github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
 	github.com/golang/protobuf v1.4.3 // indirect
 	github.com/hashicorp/errwrap v1.0.0 // indirect
 	github.com/hashicorp/go-multierror v1.1.0 // indirect
diff --git a/go.sum b/go.sum
index 41feed7..630f373 100644
--- a/go.sum
+++ b/go.sum
@@ -37,6 +37,8 @@ code.cloudfoundry.org/bytefmt v0.0.0-20180108190415-b31f603f5e1e/go.mod h1:wN/zk
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
+github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
@@ -106,9 +108,13 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
 github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw=
 github.com/go-acme/lego v2.7.2+incompatible h1:ThhpPBgf6oa9X/vRd0kEmWOsX7+vmYdckmGZSb+FEp0=
 github.com/go-acme/lego v2.7.2+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
+github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
+github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-ldap/ldap/v3 v3.4.1 h1:fU/0xli6HY02ocbMuozHAYsaHLcnkLjvho2r5a34BUU=
+github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
 github.com/go-ozzo/ozzo-validation v3.0.3-0.20180119232150-44af65fe9adf+incompatible h1:Ew5aKU1f03D3fhlr9oUycwEwAu+/Z6HWVcyuU9vXZjM=
 github.com/go-ozzo/ozzo-validation v3.0.3-0.20180119232150-44af65fe9adf+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
 github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@@ -360,6 +366,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
@@ -643,8 +650,6 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY=
 gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw=
-gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225 h1:JBwmEvLfCqgPcIq8MjVMQxsF3LVL4XG/HH0qiG0+IFY=
-gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -652,8 +657,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU=
-gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
 gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
diff --git a/traffic_ops/traffic_ops_golang/auth/ldap.go b/traffic_ops/traffic_ops_golang/auth/ldap.go
index b294285..9b3118f 100644
--- a/traffic_ops/traffic_ops_golang/auth/ldap.go
+++ b/traffic_ops/traffic_ops_golang/auth/ldap.go
@@ -29,7 +29,7 @@ import (
 	"github.com/apache/trafficcontrol/lib/go-log"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
 
-	"gopkg.in/ldap.v2"
+	"github.com/go-ldap/ldap/v3"
 )
 
 var defaultSet bool
diff --git a/vendor/gopkg.in/ldap.v2/LICENSE b/vendor/github.com/Azure/go-ntlmssp/LICENSE
similarity index 90%
copy from vendor/gopkg.in/ldap.v2/LICENSE
copy to vendor/github.com/Azure/go-ntlmssp/LICENSE
index 6c0ed4b..dc1cf39 100644
--- a/vendor/gopkg.in/ldap.v2/LICENSE
+++ b/vendor/github.com/Azure/go-ntlmssp/LICENSE
@@ -1,7 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
-Portions copyright (c) 2015-2016 go-ldap Authors
+Copyright (c) 2016 Microsoft
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/github.com/Azure/go-ntlmssp/README.md b/vendor/github.com/Azure/go-ntlmssp/README.md
new file mode 100644
index 0000000..55cdcef
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/README.md
@@ -0,0 +1,29 @@
+# go-ntlmssp
+Golang package that provides NTLM/Negotiate authentication over HTTP
+
+[![GoDoc](https://godoc.org/github.com/Azure/go-ntlmssp?status.svg)](https://godoc.org/github.com/Azure/go-ntlmssp) [![Build Status](https://travis-ci.org/Azure/go-ntlmssp.svg?branch=dev)](https://travis-ci.org/Azure/go-ntlmssp)
+
+Protocol details from https://msdn.microsoft.com/en-us/library/cc236621.aspx
+Implementation hints from http://davenport.sourceforge.net/ntlm.html
+
+This package only implements authentication, no key exchange or encryption. It
+only supports Unicode (UTF16LE) encoding of protocol strings, no OEM encoding.
+This package implements NTLMv2.
+
+# Usage
+
+```
+url, user, password := "http://www.example.com/secrets", "robpike", "pw123"
+client := &http.Client{
+  Transport: ntlmssp.Negotiator{
+    RoundTripper:&http.Transport{},
+  },
+}
+
+req, _ := http.NewRequest("GET", url, nil)
+req.SetBasicAuth(user, password)
+res, _ := client.Do(req)
+```
+
+-----
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
diff --git a/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go b/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go
new file mode 100644
index 0000000..c893068
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/authenticate_message.go
@@ -0,0 +1,183 @@
+package ntlmssp
+
+import (
+	"bytes"
+	"crypto/rand"
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"strings"
+	"time"
+)
+
+type authenicateMessage struct {
+	LmChallengeResponse []byte
+	NtChallengeResponse []byte
+
+	TargetName string
+	UserName   string
+
+	// only set if negotiateFlag_NTLMSSP_NEGOTIATE_KEY_EXCH
+	EncryptedRandomSessionKey []byte
+
+	NegotiateFlags negotiateFlags
+
+	MIC []byte
+}
+
+type authenticateMessageFields struct {
+	messageHeader
+	LmChallengeResponse varField
+	NtChallengeResponse varField
+	TargetName          varField
+	UserName            varField
+	Workstation         varField
+	_                   [8]byte
+	NegotiateFlags      negotiateFlags
+}
+
+func (m authenicateMessage) MarshalBinary() ([]byte, error) {
+	if !m.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEUNICODE) {
+		return nil, errors.New("Only unicode is supported")
+	}
+
+	target, user := toUnicode(m.TargetName), toUnicode(m.UserName)
+	workstation := toUnicode("go-ntlmssp")
+
+	ptr := binary.Size(&authenticateMessageFields{})
+	f := authenticateMessageFields{
+		messageHeader:       newMessageHeader(3),
+		NegotiateFlags:      m.NegotiateFlags,
+		LmChallengeResponse: newVarField(&ptr, len(m.LmChallengeResponse)),
+		NtChallengeResponse: newVarField(&ptr, len(m.NtChallengeResponse)),
+		TargetName:          newVarField(&ptr, len(target)),
+		UserName:            newVarField(&ptr, len(user)),
+		Workstation:         newVarField(&ptr, len(workstation)),
+	}
+
+	f.NegotiateFlags.Unset(negotiateFlagNTLMSSPNEGOTIATEVERSION)
+
+	b := bytes.Buffer{}
+	if err := binary.Write(&b, binary.LittleEndian, &f); err != nil {
+		return nil, err
+	}
+	if err := binary.Write(&b, binary.LittleEndian, &m.LmChallengeResponse); err != nil {
+		return nil, err
+	}
+	if err := binary.Write(&b, binary.LittleEndian, &m.NtChallengeResponse); err != nil {
+		return nil, err
+	}
+	if err := binary.Write(&b, binary.LittleEndian, &target); err != nil {
+		return nil, err
+	}
+	if err := binary.Write(&b, binary.LittleEndian, &user); err != nil {
+		return nil, err
+	}
+	if err := binary.Write(&b, binary.LittleEndian, &workstation); err != nil {
+		return nil, err
+	}
+
+	return b.Bytes(), nil
+}
+
+//ProcessChallenge crafts an AUTHENTICATE message in response to the CHALLENGE message
+//that was received from the server
+func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byte, error) {
+	if user == "" && password == "" {
+		return nil, errors.New("Anonymous authentication not supported")
+	}
+
+	var cm challengeMessage
+	if err := cm.UnmarshalBinary(challengeMessageData); err != nil {
+		return nil, err
+	}
+
+	if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) {
+		return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)")
+	}
+	if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) {
+		return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)")
+	}
+
+	am := authenicateMessage{
+		UserName:       user,
+		TargetName:     cm.TargetName,
+		NegotiateFlags: cm.NegotiateFlags,
+	}
+
+	timestamp := cm.TargetInfo[avIDMsvAvTimestamp]
+	if timestamp == nil { // no time sent, take current time
+		ft := uint64(time.Now().UnixNano()) / 100
+		ft += 116444736000000000 // add time between unix & windows offset
+		timestamp = make([]byte, 8)
+		binary.LittleEndian.PutUint64(timestamp, ft)
+	}
+
+	clientChallenge := make([]byte, 8)
+	rand.Reader.Read(clientChallenge)
+
+	ntlmV2Hash := getNtlmV2Hash(password, user, cm.TargetName)
+
+	am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash,
+		cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw)
+
+	if cm.TargetInfoRaw == nil {
+		am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash,
+			cm.ServerChallenge[:], clientChallenge)
+	}
+	return am.MarshalBinary()
+}
+
+func ProcessChallengeWithHash(challengeMessageData []byte, user, hash string) ([]byte, error) {
+	if user == "" && hash == "" {
+		return nil, errors.New("Anonymous authentication not supported")
+	}
+
+	var cm challengeMessage
+	if err := cm.UnmarshalBinary(challengeMessageData); err != nil {
+		return nil, err
+	}
+
+	if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) {
+		return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)")
+	}
+	if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) {
+		return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)")
+	}
+
+	am := authenicateMessage{
+		UserName:       user,
+		TargetName:     cm.TargetName,
+		NegotiateFlags: cm.NegotiateFlags,
+	}
+
+	timestamp := cm.TargetInfo[avIDMsvAvTimestamp]
+	if timestamp == nil { // no time sent, take current time
+		ft := uint64(time.Now().UnixNano()) / 100
+		ft += 116444736000000000 // add time between unix & windows offset
+		timestamp = make([]byte, 8)
+		binary.LittleEndian.PutUint64(timestamp, ft)
+	}
+
+	clientChallenge := make([]byte, 8)
+	rand.Reader.Read(clientChallenge)
+
+	hashParts := strings.Split(hash, ":")
+	if len(hashParts) > 1 {
+		hash = hashParts[1]
+	}
+	hashBytes, err := hex.DecodeString(hash)
+	if err != nil {
+		return nil, err
+	}
+	ntlmV2Hash := hmacMd5(hashBytes, toUnicode(strings.ToUpper(user)+cm.TargetName))
+
+	am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash,
+		cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw)
+
+	if cm.TargetInfoRaw == nil {
+		am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash,
+			cm.ServerChallenge[:], clientChallenge)
+	}
+	return am.MarshalBinary()
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/authheader.go b/vendor/github.com/Azure/go-ntlmssp/authheader.go
new file mode 100644
index 0000000..aac3f77
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/authheader.go
@@ -0,0 +1,37 @@
+package ntlmssp
+
+import (
+	"encoding/base64"
+	"strings"
+)
+
+type authheader string
+
+func (h authheader) IsBasic() bool {
+	return strings.HasPrefix(string(h), "Basic ")
+}
+
+func (h authheader) IsNegotiate() bool {
+	return strings.HasPrefix(string(h), "Negotiate")
+}
+
+func (h authheader) IsNTLM() bool {
+	return strings.HasPrefix(string(h), "NTLM")
+}
+
+func (h authheader) GetData() ([]byte, error) {
+	p := strings.Split(string(h), " ")
+	if len(p) < 2 {
+		return nil, nil
+	}
+	return base64.StdEncoding.DecodeString(string(p[1]))
+}
+
+func (h authheader) GetBasicCreds() (username, password string, err error) {
+	d, err := h.GetData()
+	if err != nil {
+		return "", "", err
+	}
+	parts := strings.SplitN(string(d), ":", 2)
+	return parts[0], parts[1], nil
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/avids.go b/vendor/github.com/Azure/go-ntlmssp/avids.go
new file mode 100644
index 0000000..196b5f1
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/avids.go
@@ -0,0 +1,17 @@
+package ntlmssp
+
+type avID uint16
+
+const (
+	avIDMsvAvEOL avID = iota
+	avIDMsvAvNbComputerName
+	avIDMsvAvNbDomainName
+	avIDMsvAvDNSComputerName
+	avIDMsvAvDNSDomainName
+	avIDMsvAvDNSTreeName
+	avIDMsvAvFlags
+	avIDMsvAvTimestamp
+	avIDMsvAvSingleHost
+	avIDMsvAvTargetName
+	avIDMsvChannelBindings
+)
diff --git a/vendor/github.com/Azure/go-ntlmssp/challenge_message.go b/vendor/github.com/Azure/go-ntlmssp/challenge_message.go
new file mode 100644
index 0000000..053b55e
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/challenge_message.go
@@ -0,0 +1,82 @@
+package ntlmssp
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+)
+
+type challengeMessageFields struct {
+	messageHeader
+	TargetName      varField
+	NegotiateFlags  negotiateFlags
+	ServerChallenge [8]byte
+	_               [8]byte
+	TargetInfo      varField
+}
+
+func (m challengeMessageFields) IsValid() bool {
+	return m.messageHeader.IsValid() && m.MessageType == 2
+}
+
+type challengeMessage struct {
+	challengeMessageFields
+	TargetName    string
+	TargetInfo    map[avID][]byte
+	TargetInfoRaw []byte
+}
+
+func (m *challengeMessage) UnmarshalBinary(data []byte) error {
+	r := bytes.NewReader(data)
+	err := binary.Read(r, binary.LittleEndian, &m.challengeMessageFields)
+	if err != nil {
+		return err
+	}
+	if !m.challengeMessageFields.IsValid() {
+		return fmt.Errorf("Message is not a valid challenge message: %+v", m.challengeMessageFields.messageHeader)
+	}
+
+	if m.challengeMessageFields.TargetName.Len > 0 {
+		m.TargetName, err = m.challengeMessageFields.TargetName.ReadStringFrom(data, m.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEUNICODE))
+		if err != nil {
+			return err
+		}
+	}
+
+	if m.challengeMessageFields.TargetInfo.Len > 0 {
+		d, err := m.challengeMessageFields.TargetInfo.ReadFrom(data)
+		m.TargetInfoRaw = d
+		if err != nil {
+			return err
+		}
+		m.TargetInfo = make(map[avID][]byte)
+		r := bytes.NewReader(d)
+		for {
+			var id avID
+			var l uint16
+			err = binary.Read(r, binary.LittleEndian, &id)
+			if err != nil {
+				return err
+			}
+			if id == avIDMsvAvEOL {
+				break
+			}
+
+			err = binary.Read(r, binary.LittleEndian, &l)
+			if err != nil {
+				return err
+			}
+			value := make([]byte, l)
+			n, err := r.Read(value)
+			if err != nil {
+				return err
+			}
+			if n != int(l) {
+				return fmt.Errorf("Expected to read %d bytes, got only %d", l, n)
+			}
+			m.TargetInfo[id] = value
+		}
+	}
+
+	return nil
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/messageheader.go b/vendor/github.com/Azure/go-ntlmssp/messageheader.go
new file mode 100644
index 0000000..247e284
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/messageheader.go
@@ -0,0 +1,21 @@
+package ntlmssp
+
+import (
+	"bytes"
+)
+
+var signature = [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0}
+
+type messageHeader struct {
+	Signature   [8]byte
+	MessageType uint32
+}
+
+func (h messageHeader) IsValid() bool {
+	return bytes.Equal(h.Signature[:], signature[:]) &&
+		h.MessageType > 0 && h.MessageType < 4
+}
+
+func newMessageHeader(messageType uint32) messageHeader {
+	return messageHeader{signature, messageType}
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go b/vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go
new file mode 100644
index 0000000..5905c02
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/negotiate_flags.go
@@ -0,0 +1,52 @@
+package ntlmssp
+
+type negotiateFlags uint32
+
+const (
+	/*A*/ negotiateFlagNTLMSSPNEGOTIATEUNICODE negotiateFlags = 1 << 0
+	/*B*/ negotiateFlagNTLMNEGOTIATEOEM = 1 << 1
+	/*C*/ negotiateFlagNTLMSSPREQUESTTARGET = 1 << 2
+
+	/*D*/
+	negotiateFlagNTLMSSPNEGOTIATESIGN = 1 << 4
+	/*E*/ negotiateFlagNTLMSSPNEGOTIATESEAL = 1 << 5
+	/*F*/ negotiateFlagNTLMSSPNEGOTIATEDATAGRAM = 1 << 6
+	/*G*/ negotiateFlagNTLMSSPNEGOTIATELMKEY = 1 << 7
+
+	/*H*/
+	negotiateFlagNTLMSSPNEGOTIATENTLM = 1 << 9
+
+	/*J*/
+	negotiateFlagANONYMOUS = 1 << 11
+	/*K*/ negotiateFlagNTLMSSPNEGOTIATEOEMDOMAINSUPPLIED = 1 << 12
+	/*L*/ negotiateFlagNTLMSSPNEGOTIATEOEMWORKSTATIONSUPPLIED = 1 << 13
+
+	/*M*/
+	negotiateFlagNTLMSSPNEGOTIATEALWAYSSIGN = 1 << 15
+	/*N*/ negotiateFlagNTLMSSPTARGETTYPEDOMAIN = 1 << 16
+	/*O*/ negotiateFlagNTLMSSPTARGETTYPESERVER = 1 << 17
+
+	/*P*/
+	negotiateFlagNTLMSSPNEGOTIATEEXTENDEDSESSIONSECURITY = 1 << 19
+	/*Q*/ negotiateFlagNTLMSSPNEGOTIATEIDENTIFY = 1 << 20
+
+	/*R*/
+	negotiateFlagNTLMSSPREQUESTNONNTSESSIONKEY = 1 << 22
+	/*S*/ negotiateFlagNTLMSSPNEGOTIATETARGETINFO = 1 << 23
+
+	/*T*/
+	negotiateFlagNTLMSSPNEGOTIATEVERSION = 1 << 25
+
+	/*U*/
+	negotiateFlagNTLMSSPNEGOTIATE128 = 1 << 29
+	/*V*/ negotiateFlagNTLMSSPNEGOTIATEKEYEXCH = 1 << 30
+	/*W*/ negotiateFlagNTLMSSPNEGOTIATE56 = 1 << 31
+)
+
+func (field negotiateFlags) Has(flags negotiateFlags) bool {
+	return field&flags == flags
+}
+
+func (field *negotiateFlags) Unset(flags negotiateFlags) {
+	*field = *field ^ (*field & flags)
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiate_message.go b/vendor/github.com/Azure/go-ntlmssp/negotiate_message.go
new file mode 100644
index 0000000..e466a98
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/negotiate_message.go
@@ -0,0 +1,64 @@
+package ntlmssp
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"strings"
+)
+
+const expMsgBodyLen = 40
+
+type negotiateMessageFields struct {
+	messageHeader
+	NegotiateFlags negotiateFlags
+
+	Domain      varField
+	Workstation varField
+
+	Version
+}
+
+var defaultFlags = negotiateFlagNTLMSSPNEGOTIATETARGETINFO |
+	negotiateFlagNTLMSSPNEGOTIATE56 |
+	negotiateFlagNTLMSSPNEGOTIATE128 |
+	negotiateFlagNTLMSSPNEGOTIATEUNICODE |
+	negotiateFlagNTLMSSPNEGOTIATEEXTENDEDSESSIONSECURITY
+
+//NewNegotiateMessage creates a new NEGOTIATE message with the
+//flags that this package supports.
+func NewNegotiateMessage(domainName, workstationName string) ([]byte, error) {
+	payloadOffset := expMsgBodyLen
+	flags := defaultFlags
+
+	if domainName != "" {
+		flags |= negotiateFlagNTLMSSPNEGOTIATEOEMDOMAINSUPPLIED
+	}
+
+	if workstationName != "" {
+		flags |= negotiateFlagNTLMSSPNEGOTIATEOEMWORKSTATIONSUPPLIED
+	}
+
+	msg := negotiateMessageFields{
+		messageHeader:  newMessageHeader(1),
+		NegotiateFlags: flags,
+		Domain:         newVarField(&payloadOffset, len(domainName)),
+		Workstation:    newVarField(&payloadOffset, len(workstationName)),
+		Version:        DefaultVersion(),
+	}
+
+	b := bytes.Buffer{}
+	if err := binary.Write(&b, binary.LittleEndian, &msg); err != nil {
+		return nil, err
+	}
+	if b.Len() != expMsgBodyLen {
+		return nil, errors.New("incorrect body length")
+	}
+
+	payload := strings.ToUpper(domainName + workstationName)
+	if _, err := b.WriteString(payload); err != nil {
+		return nil, err
+	}
+
+	return b.Bytes(), nil
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/negotiator.go b/vendor/github.com/Azure/go-ntlmssp/negotiator.go
new file mode 100644
index 0000000..7705eae
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/negotiator.go
@@ -0,0 +1,144 @@
+package ntlmssp
+
+import (
+	"bytes"
+	"encoding/base64"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+// GetDomain : parse domain name from based on slashes in the input
+func GetDomain(user string) (string, string) {
+	domain := ""
+
+	if strings.Contains(user, "\\") {
+		ucomponents := strings.SplitN(user, "\\", 2)
+		domain = ucomponents[0]
+		user = ucomponents[1]
+	}
+	return user, domain
+}
+
+//Negotiator is a http.Roundtripper decorator that automatically
+//converts basic authentication to NTLM/Negotiate authentication when appropriate.
+type Negotiator struct{ http.RoundTripper }
+
+//RoundTrip sends the request to the server, handling any authentication
+//re-sends as needed.
+func (l Negotiator) RoundTrip(req *http.Request) (res *http.Response, err error) {
+	// Use default round tripper if not provided
+	rt := l.RoundTripper
+	if rt == nil {
+		rt = http.DefaultTransport
+	}
+	// If it is not basic auth, just round trip the request as usual
+	reqauth := authheader(req.Header.Get("Authorization"))
+	if !reqauth.IsBasic() {
+		return rt.RoundTrip(req)
+	}
+	// Save request body
+	body := bytes.Buffer{}
+	if req.Body != nil {
+		_, err = body.ReadFrom(req.Body)
+		if err != nil {
+			return nil, err
+		}
+
+		req.Body.Close()
+		req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
+	}
+	// first try anonymous, in case the server still finds us
+	// authenticated from previous traffic
+	req.Header.Del("Authorization")
+	res, err = rt.RoundTrip(req)
+	if err != nil {
+		return nil, err
+	}
+	if res.StatusCode != http.StatusUnauthorized {
+		return res, err
+	}
+
+	resauth := authheader(res.Header.Get("Www-Authenticate"))
+	if !resauth.IsNegotiate() && !resauth.IsNTLM() {
+		// Unauthorized, Negotiate not requested, let's try with basic auth
+		req.Header.Set("Authorization", string(reqauth))
+		io.Copy(ioutil.Discard, res.Body)
+		res.Body.Close()
+		req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
+
+		res, err = rt.RoundTrip(req)
+		if err != nil {
+			return nil, err
+		}
+		if res.StatusCode != http.StatusUnauthorized {
+			return res, err
+		}
+		resauth = authheader(res.Header.Get("Www-Authenticate"))
+	}
+
+	if resauth.IsNegotiate() || resauth.IsNTLM() {
+		// 401 with request:Basic and response:Negotiate
+		io.Copy(ioutil.Discard, res.Body)
+		res.Body.Close()
+
+		// recycle credentials
+		u, p, err := reqauth.GetBasicCreds()
+		if err != nil {
+			return nil, err
+		}
+
+		// get domain from username
+		domain := ""
+		u, domain = GetDomain(u)
+
+		// send negotiate
+		negotiateMessage, err := NewNegotiateMessage(domain, "")
+		if err != nil {
+			return nil, err
+		}
+		if resauth.IsNTLM() {
+			req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(negotiateMessage))
+		} else {
+			req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(negotiateMessage))
+		}
+
+		req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
+
+		res, err = rt.RoundTrip(req)
+		if err != nil {
+			return nil, err
+		}
+
+		// receive challenge?
+		resauth = authheader(res.Header.Get("Www-Authenticate"))
+		challengeMessage, err := resauth.GetData()
+		if err != nil {
+			return nil, err
+		}
+		if !(resauth.IsNegotiate() || resauth.IsNTLM()) || len(challengeMessage) == 0 {
+			// Negotiation failed, let client deal with response
+			return res, nil
+		}
+		io.Copy(ioutil.Discard, res.Body)
+		res.Body.Close()
+
+		// send authenticate
+		authenticateMessage, err := ProcessChallenge(challengeMessage, u, p)
+		if err != nil {
+			return nil, err
+		}
+		if resauth.IsNTLM() {
+			req.Header.Set("Authorization", "NTLM "+base64.StdEncoding.EncodeToString(authenticateMessage))
+		} else {
+			req.Header.Set("Authorization", "Negotiate "+base64.StdEncoding.EncodeToString(authenticateMessage))
+		}
+
+		req.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
+
+		return rt.RoundTrip(req)
+	}
+
+	return res, err
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/nlmp.go b/vendor/github.com/Azure/go-ntlmssp/nlmp.go
new file mode 100644
index 0000000..1e65abe
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/nlmp.go
@@ -0,0 +1,51 @@
+// Package ntlmssp provides NTLM/Negotiate authentication over HTTP
+//
+// Protocol details from https://msdn.microsoft.com/en-us/library/cc236621.aspx,
+// implementation hints from http://davenport.sourceforge.net/ntlm.html .
+// This package only implements authentication, no key exchange or encryption. It
+// only supports Unicode (UTF16LE) encoding of protocol strings, no OEM encoding.
+// This package implements NTLMv2.
+package ntlmssp
+
+import (
+	"crypto/hmac"
+	"crypto/md5"
+	"golang.org/x/crypto/md4"
+	"strings"
+)
+
+func getNtlmV2Hash(password, username, target string) []byte {
+	return hmacMd5(getNtlmHash(password), toUnicode(strings.ToUpper(username)+target))
+}
+
+func getNtlmHash(password string) []byte {
+	hash := md4.New()
+	hash.Write(toUnicode(password))
+	return hash.Sum(nil)
+}
+
+func computeNtlmV2Response(ntlmV2Hash, serverChallenge, clientChallenge,
+	timestamp, targetInfo []byte) []byte {
+
+	temp := []byte{1, 1, 0, 0, 0, 0, 0, 0}
+	temp = append(temp, timestamp...)
+	temp = append(temp, clientChallenge...)
+	temp = append(temp, 0, 0, 0, 0)
+	temp = append(temp, targetInfo...)
+	temp = append(temp, 0, 0, 0, 0)
+
+	NTProofStr := hmacMd5(ntlmV2Hash, serverChallenge, temp)
+	return append(NTProofStr, temp...)
+}
+
+func computeLmV2Response(ntlmV2Hash, serverChallenge, clientChallenge []byte) []byte {
+	return append(hmacMd5(ntlmV2Hash, serverChallenge, clientChallenge), clientChallenge...)
+}
+
+func hmacMd5(key []byte, data ...[]byte) []byte {
+	mac := hmac.New(md5.New, key)
+	for _, d := range data {
+		mac.Write(d)
+	}
+	return mac.Sum(nil)
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/unicode.go b/vendor/github.com/Azure/go-ntlmssp/unicode.go
new file mode 100644
index 0000000..7b4f471
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/unicode.go
@@ -0,0 +1,29 @@
+package ntlmssp
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"unicode/utf16"
+)
+
+// helper func's for dealing with Windows Unicode (UTF16LE)
+
+func fromUnicode(d []byte) (string, error) {
+	if len(d)%2 > 0 {
+		return "", errors.New("Unicode (UTF 16 LE) specified, but uneven data length")
+	}
+	s := make([]uint16, len(d)/2)
+	err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &s)
+	if err != nil {
+		return "", err
+	}
+	return string(utf16.Decode(s)), nil
+}
+
+func toUnicode(s string) []byte {
+	uints := utf16.Encode([]rune(s))
+	b := bytes.Buffer{}
+	binary.Write(&b, binary.LittleEndian, &uints)
+	return b.Bytes()
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/varfield.go b/vendor/github.com/Azure/go-ntlmssp/varfield.go
new file mode 100644
index 0000000..15f9aa1
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/varfield.go
@@ -0,0 +1,40 @@
+package ntlmssp
+
+import (
+	"errors"
+)
+
+type varField struct {
+	Len          uint16
+	MaxLen       uint16
+	BufferOffset uint32
+}
+
+func (f varField) ReadFrom(buffer []byte) ([]byte, error) {
+	if len(buffer) < int(f.BufferOffset+uint32(f.Len)) {
+		return nil, errors.New("Error reading data, varField extends beyond buffer")
+	}
+	return buffer[f.BufferOffset : f.BufferOffset+uint32(f.Len)], nil
+}
+
+func (f varField) ReadStringFrom(buffer []byte, unicode bool) (string, error) {
+	d, err := f.ReadFrom(buffer)
+	if err != nil {
+		return "", err
+	}
+	if unicode { // UTF-16LE encoding scheme
+		return fromUnicode(d)
+	}
+	// OEM encoding, close enough to ASCII, since no code page is specified
+	return string(d), err
+}
+
+func newVarField(ptr *int, fieldsize int) varField {
+	f := varField{
+		Len:          uint16(fieldsize),
+		MaxLen:       uint16(fieldsize),
+		BufferOffset: uint32(*ptr),
+	}
+	*ptr += fieldsize
+	return f
+}
diff --git a/vendor/github.com/Azure/go-ntlmssp/version.go b/vendor/github.com/Azure/go-ntlmssp/version.go
new file mode 100644
index 0000000..6d84892
--- /dev/null
+++ b/vendor/github.com/Azure/go-ntlmssp/version.go
@@ -0,0 +1,20 @@
+package ntlmssp
+
+// Version is a struct representing https://msdn.microsoft.com/en-us/library/cc236654.aspx
+type Version struct {
+	ProductMajorVersion uint8
+	ProductMinorVersion uint8
+	ProductBuild        uint16
+	_                   [3]byte
+	NTLMRevisionCurrent uint8
+}
+
+// DefaultVersion returns a Version with "sensible" defaults (Windows 7)
+func DefaultVersion() Version {
+	return Version{
+		ProductMajorVersion: 6,
+		ProductMinorVersion: 1,
+		ProductBuild:        7601,
+		NTLMRevisionCurrent: 15,
+	}
+}
diff --git a/vendor/gopkg.in/asn1-ber.v1/LICENSE b/vendor/github.com/go-asn1-ber/asn1-ber/LICENSE
similarity index 100%
rename from vendor/gopkg.in/asn1-ber.v1/LICENSE
rename to vendor/github.com/go-asn1-ber/asn1-ber/LICENSE
diff --git a/vendor/gopkg.in/asn1-ber.v1/README.md b/vendor/github.com/go-asn1-ber/asn1-ber/README.md
similarity index 100%
rename from vendor/gopkg.in/asn1-ber.v1/README.md
rename to vendor/github.com/go-asn1-ber/asn1-ber/README.md
diff --git a/vendor/gopkg.in/asn1-ber.v1/ber.go b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go
similarity index 66%
rename from vendor/gopkg.in/asn1-ber.v1/ber.go
rename to vendor/github.com/go-asn1-ber/asn1-ber/ber.go
index 25cc921..4fd7a66 100644
--- a/vendor/gopkg.in/asn1-ber.v1/ber.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go
@@ -5,10 +5,17 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"math"
 	"os"
 	"reflect"
+	"time"
+	"unicode/utf8"
 )
 
+// MaxPacketLengthBytes specifies the maximum allowed packet size when calling ReadPacket or DecodePacket. Set to 0 for
+// no limit.
+var MaxPacketLengthBytes int64 = math.MaxInt32
+
 type Packet struct {
 	Identifier
 	Value       interface{}
@@ -138,42 +145,46 @@ var TypeMap = map[Type]string{
 	TypeConstructed: "Constructed",
 }
 
-var Debug bool = false
+var Debug = false
 
 func PrintBytes(out io.Writer, buf []byte, indent string) {
-	data_lines := make([]string, (len(buf)/30)+1)
-	num_lines := make([]string, (len(buf)/30)+1)
+	dataLines := make([]string, (len(buf)/30)+1)
+	numLines := make([]string, (len(buf)/30)+1)
 
 	for i, b := range buf {
-		data_lines[i/30] += fmt.Sprintf("%02x ", b)
-		num_lines[i/30] += fmt.Sprintf("%02d ", (i+1)%100)
+		dataLines[i/30] += fmt.Sprintf("%02x ", b)
+		numLines[i/30] += fmt.Sprintf("%02d ", (i+1)%100)
 	}
 
-	for i := 0; i < len(data_lines); i++ {
-		out.Write([]byte(indent + data_lines[i] + "\n"))
-		out.Write([]byte(indent + num_lines[i] + "\n\n"))
+	for i := 0; i < len(dataLines); i++ {
+		_, _ = out.Write([]byte(indent + dataLines[i] + "\n"))
+		_, _ = out.Write([]byte(indent + numLines[i] + "\n\n"))
 	}
 }
 
+func WritePacket(out io.Writer, p *Packet) {
+	printPacket(out, p, 0, false)
+}
+
 func PrintPacket(p *Packet) {
 	printPacket(os.Stdout, p, 0, false)
 }
 
 func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
-	indent_str := ""
+	indentStr := ""
 
-	for len(indent_str) != indent {
-		indent_str += " "
+	for len(indentStr) != indent {
+		indentStr += " "
 	}
 
-	class_str := ClassMap[p.ClassType]
+	classStr := ClassMap[p.ClassType]
 
-	tagtype_str := TypeMap[p.TagType]
+	tagTypeStr := TypeMap[p.TagType]
 
-	tag_str := fmt.Sprintf("0x%02X", p.Tag)
+	tagStr := fmt.Sprintf("0x%02X", p.Tag)
 
 	if p.ClassType == ClassUniversal {
-		tag_str = tagMap[p.Tag]
+		tagStr = tagMap[p.Tag]
 	}
 
 	value := fmt.Sprint(p.Value)
@@ -183,10 +194,10 @@ func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
 		description = p.Description + ": "
 	}
 
-	fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indent_str, description, class_str, tagtype_str, tag_str, p.Data.Len(), value)
+	_, _ = fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indentStr, description, classStr, tagTypeStr, tagStr, p.Data.Len(), value)
 
 	if printBytes {
-		PrintBytes(out, p.Bytes(), indent_str)
+		PrintBytes(out, p.Bytes(), indentStr)
 	}
 
 	for _, child := range p.Children {
@@ -194,7 +205,7 @@ func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
 	}
 }
 
-// ReadPacket reads a single Packet from the reader
+// ReadPacket reads a single Packet from the reader.
 func ReadPacket(reader io.Reader) (*Packet, error) {
 	p, _, err := readPacket(reader)
 	if err != nil {
@@ -207,7 +218,7 @@ func DecodeString(data []byte) string {
 	return string(data)
 }
 
-func parseInt64(bytes []byte) (ret int64, err error) {
+func ParseInt64(bytes []byte) (ret int64, err error) {
 	if len(bytes) > 8 {
 		// We'll overflow an int64 in this case.
 		err = fmt.Errorf("integer too large")
@@ -230,7 +241,7 @@ func encodeInteger(i int64) []byte {
 
 	var j int
 	for ; n > 0; n-- {
-		out[j] = (byte(i >> uint((n-1)*8)))
+		out[j] = byte(i >> uint((n-1)*8))
 		j++
 	}
 
@@ -262,7 +273,7 @@ func DecodePacket(data []byte) *Packet {
 }
 
 // DecodePacketErr decodes the given bytes into a single Packet
-// If a decode error is encountered, nil is returned
+// If a decode error is encountered, nil is returned.
 func DecodePacketErr(data []byte) (*Packet, error) {
 	p, _, err := readPacket(bytes.NewBuffer(data))
 	if err != nil {
@@ -271,7 +282,7 @@ func DecodePacketErr(data []byte) (*Packet, error) {
 	return p, nil
 }
 
-// readPacket reads a single Packet from the reader, returning the number of bytes read
+// readPacket reads a single Packet from the reader, returning the number of bytes read.
 func readPacket(reader io.Reader) (*Packet, int, error) {
 	identifier, length, read, err := readHeader(reader)
 	if err != nil {
@@ -330,7 +341,10 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
 	}
 
 	// Read definite-length content
-	content := make([]byte, length, length)
+	if MaxPacketLengthBytes > 0 && int64(length) > MaxPacketLengthBytes {
+		return nil, read, fmt.Errorf("length %d greater than maximum %d", length, MaxPacketLengthBytes)
+	}
+	content := make([]byte, length)
 	if length > 0 {
 		_, err := io.ReadFull(reader, content)
 		if err != nil {
@@ -349,11 +363,11 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
 		switch p.Tag {
 		case TagEOC:
 		case TagBoolean:
-			val, _ := parseInt64(content)
+			val, _ := ParseInt64(content)
 
 			p.Value = val != 0
 		case TagInteger:
-			p.Value, _ = parseInt64(content)
+			p.Value, _ = ParseInt64(content)
 		case TagBitString:
 		case TagOctetString:
 			// the actual string encoding is not known here
@@ -365,22 +379,42 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
 		case TagObjectDescriptor:
 		case TagExternal:
 		case TagRealFloat:
+			p.Value, err = ParseReal(content)
 		case TagEnumerated:
-			p.Value, _ = parseInt64(content)
+			p.Value, _ = ParseInt64(content)
 		case TagEmbeddedPDV:
 		case TagUTF8String:
-			p.Value = DecodeString(content)
+			val := DecodeString(content)
+			if !utf8.Valid([]byte(val)) {
+				err = errors.New("invalid UTF-8 string")
+			} else {
+				p.Value = val
+			}
 		case TagRelativeOID:
 		case TagSequence:
 		case TagSet:
 		case TagNumericString:
 		case TagPrintableString:
-			p.Value = DecodeString(content)
+			val := DecodeString(content)
+			if err = isPrintableString(val); err == nil {
+				p.Value = val
+			}
 		case TagT61String:
 		case TagVideotexString:
 		case TagIA5String:
+			val := DecodeString(content)
+			for i, c := range val {
+				if c >= 0x7F {
+					err = fmt.Errorf("invalid character for IA5String at pos %d: %c", i, c)
+					break
+				}
+			}
+			if err == nil {
+				p.Value = val
+			}
 		case TagUTCTime:
 		case TagGeneralizedTime:
+			p.Value, err = ParseGeneralizedTime(content)
 		case TagGraphicString:
 		case TagVisibleString:
 		case TagGeneralString:
@@ -392,7 +426,24 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
 		p.Data.Write(content)
 	}
 
-	return p, read, nil
+	return p, read, err
+}
+
+func isPrintableString(val string) error {
+	for i, c := range val {
+		switch {
+		case c >= 'a' && c <= 'z':
+		case c >= 'A' && c <= 'Z':
+		case c >= '0' && c <= '9':
+		default:
+			switch c {
+			case '\'', '(', ')', '+', ',', '-', '.', '=', '/', ':', '?', ' ':
+			default:
+				return fmt.Errorf("invalid character in position %d", i)
+			}
+		}
+	}
+	return nil
 }
 
 func (p *Packet) Bytes() []byte {
@@ -410,61 +461,99 @@ func (p *Packet) AppendChild(child *Packet) {
 	p.Children = append(p.Children, child)
 }
 
-func Encode(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet {
+func Encode(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet {
 	p := new(Packet)
 
-	p.ClassType = ClassType
-	p.TagType = TagType
-	p.Tag = Tag
+	p.ClassType = classType
+	p.TagType = tagType
+	p.Tag = tag
 	p.Data = new(bytes.Buffer)
 
 	p.Children = make([]*Packet, 0, 2)
 
-	p.Value = Value
-	p.Description = Description
+	p.Value = value
+	p.Description = description
 
-	if Value != nil {
-		v := reflect.ValueOf(Value)
+	if value != nil {
+		v := reflect.ValueOf(value)
 
-		if ClassType == ClassUniversal {
-			switch Tag {
+		if classType == ClassUniversal {
+			switch tag {
 			case TagOctetString:
 				sv, ok := v.Interface().(string)
 
 				if ok {
 					p.Data.Write([]byte(sv))
 				}
+			case TagEnumerated:
+				bv, ok := v.Interface().([]byte)
+				if ok {
+					p.Data.Write(bv)
+				}
+			case TagEmbeddedPDV:
+				bv, ok := v.Interface().([]byte)
+				if ok {
+					p.Data.Write(bv)
+				}
+			}
+		} else if classType == ClassContext {
+			switch tag {
+			case TagEnumerated:
+				bv, ok := v.Interface().([]byte)
+				if ok {
+					p.Data.Write(bv)
+				}
+			case TagEmbeddedPDV:
+				bv, ok := v.Interface().([]byte)
+				if ok {
+					p.Data.Write(bv)
+				}
 			}
 		}
 	}
-
 	return p
 }
 
-func NewSequence(Description string) *Packet {
-	return Encode(ClassUniversal, TypeConstructed, TagSequence, nil, Description)
+func NewSequence(description string) *Packet {
+	return Encode(ClassUniversal, TypeConstructed, TagSequence, nil, description)
 }
 
-func NewBoolean(ClassType Class, TagType Type, Tag Tag, Value bool, Description string) *Packet {
+func NewBoolean(classType Class, tagType Type, tag Tag, value bool, description string) *Packet {
 	intValue := int64(0)
 
-	if Value {
+	if value {
 		intValue = 1
 	}
 
-	p := Encode(ClassType, TagType, Tag, nil, Description)
+	p := Encode(classType, tagType, tag, nil, description)
+
+	p.Value = value
+	p.Data.Write(encodeInteger(intValue))
+
+	return p
+}
+
+// NewLDAPBoolean returns a RFC 4511-compliant Boolean packet.
+func NewLDAPBoolean(classType Class, tagType Type, tag Tag, value bool, description string) *Packet {
+	intValue := int64(0)
+
+	if value {
+		intValue = 255
+	}
+
+	p := Encode(classType, tagType, tag, nil, description)
 
-	p.Value = Value
+	p.Value = value
 	p.Data.Write(encodeInteger(intValue))
 
 	return p
 }
 
-func NewInteger(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet {
-	p := Encode(ClassType, TagType, Tag, nil, Description)
+func NewInteger(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet {
+	p := Encode(classType, tagType, tag, nil, description)
 
-	p.Value = Value
-	switch v := Value.(type) {
+	p.Value = value
+	switch v := value.(type) {
 	case int:
 		p.Data.Write(encodeInteger(int64(v)))
 	case uint:
@@ -494,11 +583,38 @@ func NewInteger(ClassType Class, TagType Type, Tag Tag, Value interface{}, Descr
 	return p
 }
 
-func NewString(ClassType Class, TagType Type, Tag Tag, Value, Description string) *Packet {
-	p := Encode(ClassType, TagType, Tag, nil, Description)
+func NewString(classType Class, tagType Type, tag Tag, value, description string) *Packet {
+	p := Encode(classType, tagType, tag, nil, description)
+
+	p.Value = value
+	p.Data.Write([]byte(value))
+
+	return p
+}
+
+func NewGeneralizedTime(classType Class, tagType Type, tag Tag, value time.Time, description string) *Packet {
+	p := Encode(classType, tagType, tag, nil, description)
+	var s string
+	if value.Nanosecond() != 0 {
+		s = value.Format(`20060102150405.000000000Z`)
+	} else {
+		s = value.Format(`20060102150405Z`)
+	}
+	p.Value = s
+	p.Data.Write([]byte(s))
+	return p
+}
 
-	p.Value = Value
-	p.Data.Write([]byte(Value))
+func NewReal(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet {
+	p := Encode(classType, tagType, tag, nil, description)
 
+	switch v := value.(type) {
+	case float64:
+		p.Data.Write(encodeFloat(v))
+	case float32:
+		p.Data.Write(encodeFloat(float64(v)))
+	default:
+		panic(fmt.Sprintf("Invalid type %T, expected float{64|32}", v))
+	}
 	return p
 }
diff --git a/vendor/gopkg.in/asn1-ber.v1/content_int.go b/vendor/github.com/go-asn1-ber/asn1-ber/content_int.go
similarity index 87%
rename from vendor/gopkg.in/asn1-ber.v1/content_int.go
rename to vendor/github.com/go-asn1-ber/asn1-ber/content_int.go
index 1858b74..20b500f 100644
--- a/vendor/gopkg.in/asn1-ber.v1/content_int.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/content_int.go
@@ -6,7 +6,7 @@ func encodeUnsignedInteger(i uint64) []byte {
 
 	var j int
 	for ; n > 0; n-- {
-		out[j] = (byte(i >> uint((n-1)*8)))
+		out[j] = byte(i >> uint((n-1)*8))
 		j++
 	}
 
diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/generalizedTime.go b/vendor/github.com/go-asn1-ber/asn1-ber/generalizedTime.go
new file mode 100644
index 0000000..51215f0
--- /dev/null
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/generalizedTime.go
@@ -0,0 +1,105 @@
+package ber
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"strconv"
+	"time"
+)
+
+// ErrInvalidTimeFormat is returned when the generalizedTime string was not correct.
+var ErrInvalidTimeFormat = errors.New("invalid time format")
+
+var zeroTime = time.Time{}
+
+// ParseGeneralizedTime parses a string value and if it conforms to
+// GeneralizedTime[^0] format, will return a time.Time for that value.
+//
+// [^0]: https://www.itu.int/rec/T-REC-X.690-201508-I/en Section 11.7
+func ParseGeneralizedTime(v []byte) (time.Time, error) {
+	var format string
+	var fract time.Duration
+
+	str := []byte(DecodeString(v))
+	tzIndex := bytes.IndexAny(str, "Z+-")
+	if tzIndex < 0 {
+		return zeroTime, ErrInvalidTimeFormat
+	}
+
+	dot := bytes.IndexAny(str, ".,")
+	switch dot {
+	case -1:
+		switch tzIndex {
+		case 10:
+			format = `2006010215Z`
+		case 12:
+			format = `200601021504Z`
+		case 14:
+			format = `20060102150405Z`
+		default:
+			return zeroTime, ErrInvalidTimeFormat
+		}
+
+	case 10, 12:
+		if tzIndex < dot {
+			return zeroTime, ErrInvalidTimeFormat
+		}
+		// a "," is also allowed, but would not be parsed by time.Parse():
+		str[dot] = '.'
+
+		// If <minute> is omitted, then <fraction> represents a fraction of an
+		// hour; otherwise, if <second> and <leap-second> are omitted, then
+		// <fraction> represents a fraction of a minute; otherwise, <fraction>
+		// represents a fraction of a second.
+
+		// parse as float from dot to timezone
+		f, err := strconv.ParseFloat(string(str[dot:tzIndex]), 64)
+		if err != nil {
+			return zeroTime, fmt.Errorf("failed to parse float: %s", err)
+		}
+		// ...and strip that part
+		str = append(str[:dot], str[tzIndex:]...)
+		tzIndex = dot
+
+		if dot == 10 {
+			fract = time.Duration(int64(f * float64(time.Hour)))
+			format = `2006010215Z`
+		} else {
+			fract = time.Duration(int64(f * float64(time.Minute)))
+			format = `200601021504Z`
+		}
+
+	case 14:
+		if tzIndex < dot {
+			return zeroTime, ErrInvalidTimeFormat
+		}
+		str[dot] = '.'
+		// no need for fractional seconds, time.Parse() handles that
+		format = `20060102150405Z`
+
+	default:
+		return zeroTime, ErrInvalidTimeFormat
+	}
+
+	l := len(str)
+	switch l - tzIndex {
+	case 1:
+		if str[l-1] != 'Z' {
+			return zeroTime, ErrInvalidTimeFormat
+		}
+	case 3:
+		format += `0700`
+		str = append(str, []byte("00")...)
+	case 5:
+		format += `0700`
+	default:
+		return zeroTime, ErrInvalidTimeFormat
+	}
+
+	t, err := time.Parse(format, string(str))
+	if err != nil {
+		return zeroTime, fmt.Errorf("%s: %s", ErrInvalidTimeFormat, err)
+	}
+	return t.Add(fract), nil
+}
diff --git a/vendor/gopkg.in/asn1-ber.v1/header.go b/vendor/github.com/go-asn1-ber/asn1-ber/header.go
similarity index 60%
rename from vendor/gopkg.in/asn1-ber.v1/header.go
rename to vendor/github.com/go-asn1-ber/asn1-ber/header.go
index 123744e..7dfa6b9 100644
--- a/vendor/gopkg.in/asn1-ber.v1/header.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/header.go
@@ -2,28 +2,37 @@ package ber
 
 import (
 	"errors"
+	"fmt"
 	"io"
 )
 
 func readHeader(reader io.Reader) (identifier Identifier, length int, read int, err error) {
-	if i, c, err := readIdentifier(reader); err != nil {
+	var (
+		c, l int
+		i    Identifier
+	)
+
+	if i, c, err = readIdentifier(reader); err != nil {
 		return Identifier{}, 0, read, err
-	} else {
-		identifier = i
-		read += c
 	}
+	identifier = i
+	read += c
 
-	if l, c, err := readLength(reader); err != nil {
+	if l, c, err = readLength(reader); err != nil {
 		return Identifier{}, 0, read, err
-	} else {
-		length = l
-		read += c
 	}
+	length = l
+	read += c
 
 	// Validate length type with identifier (x.600, 8.1.3.2.a)
 	if length == LengthIndefinite && identifier.TagType == TypePrimitive {
 		return Identifier{}, 0, read, errors.New("indefinite length used with primitive type")
 	}
 
+	if length < LengthIndefinite {
+		err = fmt.Errorf("length cannot be less than %d", LengthIndefinite)
+		return
+	}
+
 	return identifier, length, read, nil
 }
diff --git a/vendor/gopkg.in/asn1-ber.v1/identifier.go b/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
similarity index 69%
rename from vendor/gopkg.in/asn1-ber.v1/identifier.go
rename to vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
index f7672a8..e8c4357 100644
--- a/vendor/gopkg.in/asn1-ber.v1/identifier.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
@@ -4,7 +4,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"math"
 )
 
 func readIdentifier(reader io.Reader) (Identifier, int, error) {
@@ -80,24 +79,34 @@ func encodeIdentifier(identifier Identifier) []byte {
 
 		tag := identifier.Tag
 
-		highBit := uint(63)
-		for {
-			if tag&(1<<highBit) != 0 {
-				break
-			}
-			highBit--
-		}
+		b = append(b, encodeHighTag(tag)...)
+	}
+	return b
+}
 
-		tagBytes := int(math.Ceil(float64(highBit) / 7.0))
-		for i := tagBytes - 1; i >= 0; i-- {
-			offset := uint(i) * 7
-			mask := Tag(0x7f) << offset
-			tagByte := (tag & mask) >> offset
-			if i != 0 {
-				tagByte |= 0x80
-			}
-			b = append(b, byte(tagByte))
+func encodeHighTag(tag Tag) []byte {
+	// set cap=4 to hopefully avoid additional allocations
+	b := make([]byte, 0, 4)
+	for tag != 0 {
+		// t := last 7 bits of tag (HighTagValueBitmask = 0x7F)
+		t := tag & HighTagValueBitmask
+
+		// right shift tag 7 to remove what was just pulled off
+		tag >>= 7
+
+		// if b already has entries this entry needs a continuation bit (0x80)
+		if len(b) != 0 {
+			t |= HighTagContinueBitmask
 		}
+
+		b = append(b, byte(t))
+	}
+	// reverse
+	// since bits were pulled off 'tag' small to high the byte slice is in reverse order.
+	// example: tag = 0xFF results in {0x7F, 0x01 + 0x80 (continuation bit)}
+	// this needs to be reversed into 0x81 0x7F
+	for i, j := 0, len(b)-1; i < len(b)/2; i++ {
+		b[i], b[j-i] = b[j-i], b[i]
 	}
 	return b
 }
diff --git a/vendor/gopkg.in/asn1-ber.v1/length.go b/vendor/github.com/go-asn1-ber/asn1-ber/length.go
similarity index 85%
rename from vendor/gopkg.in/asn1-ber.v1/length.go
rename to vendor/github.com/go-asn1-ber/asn1-ber/length.go
index 750e8f4..9cc195d 100644
--- a/vendor/gopkg.in/asn1-ber.v1/length.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/length.go
@@ -71,11 +71,11 @@ func readLength(reader io.Reader) (length int, read int, err error) {
 }
 
 func encodeLength(length int) []byte {
-	length_bytes := encodeUnsignedInteger(uint64(length))
-	if length > 127 || len(length_bytes) > 1 {
-		longFormBytes := []byte{(LengthLongFormBitmask | byte(len(length_bytes)))}
-		longFormBytes = append(longFormBytes, length_bytes...)
-		length_bytes = longFormBytes
+	lengthBytes := encodeUnsignedInteger(uint64(length))
+	if length > 127 || len(lengthBytes) > 1 {
+		longFormBytes := []byte{LengthLongFormBitmask | byte(len(lengthBytes))}
+		longFormBytes = append(longFormBytes, lengthBytes...)
+		lengthBytes = longFormBytes
 	}
-	return length_bytes
+	return lengthBytes
 }
diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/real.go b/vendor/github.com/go-asn1-ber/asn1-ber/real.go
new file mode 100644
index 0000000..610a003
--- /dev/null
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/real.go
@@ -0,0 +1,157 @@
+package ber
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"math"
+	"strconv"
+	"strings"
+)
+
+func encodeFloat(v float64) []byte {
+	switch {
+	case math.IsInf(v, 1):
+		return []byte{0x40}
+	case math.IsInf(v, -1):
+		return []byte{0x41}
+	case math.IsNaN(v):
+		return []byte{0x42}
+	case v == 0.0:
+		if math.Signbit(v) {
+			return []byte{0x43}
+		}
+		return []byte{}
+	default:
+		// we take the easy part ;-)
+		value := []byte(strconv.FormatFloat(v, 'G', -1, 64))
+		var ret []byte
+		if bytes.Contains(value, []byte{'E'}) {
+			ret = []byte{0x03}
+		} else {
+			ret = []byte{0x02}
+		}
+		ret = append(ret, value...)
+		return ret
+	}
+}
+
+func ParseReal(v []byte) (val float64, err error) {
+	if len(v) == 0 {
+		return 0.0, nil
+	}
+	switch {
+	case v[0]&0x80 == 0x80:
+		val, err = parseBinaryFloat(v)
+	case v[0]&0xC0 == 0x40:
+		val, err = parseSpecialFloat(v)
+	case v[0]&0xC0 == 0x0:
+		val, err = parseDecimalFloat(v)
+	default:
+		return 0.0, fmt.Errorf("invalid info block")
+	}
+	if err != nil {
+		return 0.0, err
+	}
+
+	if val == 0.0 && !math.Signbit(val) {
+		return 0.0, errors.New("REAL value +0 must be encoded with zero-length value block")
+	}
+	return val, nil
+}
+
+func parseBinaryFloat(v []byte) (float64, error) {
+	var info byte
+	var buf []byte
+
+	info, v = v[0], v[1:]
+
+	var base int
+	switch info & 0x30 {
+	case 0x00:
+		base = 2
+	case 0x10:
+		base = 8
+	case 0x20:
+		base = 16
+	case 0x30:
+		return 0.0, errors.New("bits 6 and 5 of information octet for REAL are equal to 11")
+	}
+
+	scale := uint((info & 0x0c) >> 2)
+
+	var expLen int
+	switch info & 0x03 {
+	case 0x00:
+		expLen = 1
+	case 0x01:
+		expLen = 2
+	case 0x02:
+		expLen = 3
+	case 0x03:
+		expLen = int(v[0])
+		if expLen > 8 {
+			return 0.0, errors.New("too big value of exponent")
+		}
+		v = v[1:]
+	}
+	buf, v = v[:expLen], v[expLen:]
+	exponent, err := ParseInt64(buf)
+	if err != nil {
+		return 0.0, err
+	}
+
+	if len(v) > 8 {
+		return 0.0, errors.New("too big value of mantissa")
+	}
+
+	mant, err := ParseInt64(v)
+	if err != nil {
+		return 0.0, err
+	}
+	mantissa := mant << scale
+
+	if info&0x40 == 0x40 {
+		mantissa = -mantissa
+	}
+
+	return float64(mantissa) * math.Pow(float64(base), float64(exponent)), nil
+}
+
+func parseDecimalFloat(v []byte) (val float64, err error) {
+	switch v[0] & 0x3F {
+	case 0x01: // NR form 1
+		var iVal int64
+		iVal, err = strconv.ParseInt(strings.TrimLeft(string(v[1:]), " "), 10, 64)
+		val = float64(iVal)
+	case 0x02, 0x03: // NR form 2, 3
+		val, err = strconv.ParseFloat(strings.Replace(strings.TrimLeft(string(v[1:]), " "), ",", ".", -1), 64)
+	default:
+		err = errors.New("incorrect NR form")
+	}
+	if err != nil {
+		return 0.0, err
+	}
+
+	if val == 0.0 && math.Signbit(val) {
+		return 0.0, errors.New("REAL value -0 must be encoded as a special value")
+	}
+	return val, nil
+}
+
+func parseSpecialFloat(v []byte) (float64, error) {
+	if len(v) != 1 {
+		return 0.0, errors.New(`encoding of "special value" must not contain exponent and mantissa`)
+	}
+	switch v[0] {
+	case 0x40:
+		return math.Inf(1), nil
+	case 0x41:
+		return math.Inf(-1), nil
+	case 0x42:
+		return math.NaN(), nil
+	case 0x43:
+		return math.Copysign(0, -1), nil
+	}
+	return 0.0, errors.New(`encoding of "special value" not from ASN.1 standard`)
+}
diff --git a/vendor/gopkg.in/asn1-ber.v1/util.go b/vendor/github.com/go-asn1-ber/asn1-ber/util.go
similarity index 93%
rename from vendor/gopkg.in/asn1-ber.v1/util.go
rename to vendor/github.com/go-asn1-ber/asn1-ber/util.go
index 3e56b66..14dc87d 100644
--- a/vendor/gopkg.in/asn1-ber.v1/util.go
+++ b/vendor/github.com/go-asn1-ber/asn1-ber/util.go
@@ -3,7 +3,7 @@ package ber
 import "io"
 
 func readByte(reader io.Reader) (byte, error) {
-	bytes := make([]byte, 1, 1)
+	bytes := make([]byte, 1)
 	_, err := io.ReadFull(reader, bytes)
 	if err != nil {
 		if err == io.EOF {
diff --git a/vendor/gopkg.in/ldap.v2/LICENSE b/vendor/github.com/go-ldap/ldap/v3/LICENSE
similarity index 100%
rename from vendor/gopkg.in/ldap.v2/LICENSE
rename to vendor/github.com/go-ldap/ldap/v3/LICENSE
diff --git a/vendor/github.com/go-ldap/ldap/v3/add.go b/vendor/github.com/go-ldap/ldap/v3/add.go
new file mode 100644
index 0000000..baecd78
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/add.go
@@ -0,0 +1,91 @@
+package ldap
+
+import (
+	"log"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// Attribute represents an LDAP attribute
+type Attribute struct {
+	// Type is the name of the LDAP attribute
+	Type string
+	// Vals are the LDAP attribute values
+	Vals []string
+}
+
+func (a *Attribute) encode() *ber.Packet {
+	seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
+	seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type"))
+	set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
+	for _, value := range a.Vals {
+		set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
+	}
+	seq.AppendChild(set)
+	return seq
+}
+
+// AddRequest represents an LDAP AddRequest operation
+type AddRequest struct {
+	// DN identifies the entry being added
+	DN string
+	// Attributes list the attributes of the new entry
+	Attributes []Attribute
+	// Controls hold optional controls to send with the request
+	Controls []Control
+}
+
+func (req *AddRequest) appendTo(envelope *ber.Packet) error {
+	pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
+	pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN"))
+	attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
+	for _, attribute := range req.Attributes {
+		attributes.AppendChild(attribute.encode())
+	}
+	pkt.AppendChild(attributes)
+
+	envelope.AppendChild(pkt)
+	if len(req.Controls) > 0 {
+		envelope.AppendChild(encodeControls(req.Controls))
+	}
+
+	return nil
+}
+
+// Attribute adds an attribute with the given type and values
+func (req *AddRequest) Attribute(attrType string, attrVals []string) {
+	req.Attributes = append(req.Attributes, Attribute{Type: attrType, Vals: attrVals})
+}
+
+// NewAddRequest returns an AddRequest for the given DN, with no attributes
+func NewAddRequest(dn string, controls []Control) *AddRequest {
+	return &AddRequest{
+		DN:       dn,
+		Controls: controls,
+	}
+
+}
+
+// Add performs the given AddRequest
+func (l *Conn) Add(addRequest *AddRequest) error {
+	msgCtx, err := l.doRequest(addRequest)
+	if err != nil {
+		return err
+	}
+	defer l.finishMessage(msgCtx)
+
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return err
+	}
+
+	if packet.Children[1].Tag == ApplicationAddResponse {
+		err := GetLDAPError(packet)
+		if err != nil {
+			return err
+		}
+	} else {
+		log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
+	}
+	return nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/bind.go b/vendor/github.com/go-ldap/ldap/v3/bind.go
new file mode 100644
index 0000000..9bc5748
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/bind.go
@@ -0,0 +1,540 @@
+package ldap
+
+import (
+	"bytes"
+	"crypto/md5"
+	enchex "encoding/hex"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"math/rand"
+	"strings"
+
+	"github.com/Azure/go-ntlmssp"
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// SimpleBindRequest represents a username/password bind operation
+type SimpleBindRequest struct {
+	// Username is the name of the Directory object that the client wishes to bind as
+	Username string
+	// Password is the credentials to bind with
+	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
+type SimpleBindResult struct {
+	Controls []Control
+}
+
+// NewSimpleBindRequest returns a bind request
+func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
+	return &SimpleBindRequest{
+		Username:           username,
+		Password:           password,
+		Controls:           controls,
+		AllowEmptyPassword: false,
+	}
+}
+
+func (req *SimpleBindRequest) appendTo(envelope *ber.Packet) error {
+	pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
+	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
+	pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Username, "User Name"))
+	pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.Password, "Password"))
+
+	envelope.AppendChild(pkt)
+	if len(req.Controls) > 0 {
+		envelope.AppendChild(encodeControls(req.Controls))
+	}
+
+	return nil
+}
+
+// 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"))
+	}
+
+	msgCtx, err := l.doRequest(simpleBindRequest)
+	if err != nil {
+		return nil, err
+	}
+	defer l.finishMessage(msgCtx)
+
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return nil, err
+	}
+
+	result := &SimpleBindResult{
+		Controls: make([]Control, 0),
+	}
+
+	if len(packet.Children) == 3 {
+		for _, child := range packet.Children[2].Children {
+			decodedChild, decodeErr := DecodeControl(child)
+			if decodeErr != nil {
+				return nil, fmt.Errorf("failed to decode child control: %s", decodeErr)
+			}
+			result.Controls = append(result.Controls, decodedChild)
+		}
+	}
+
+	err = GetLDAPError(packet)
+	return result, err
+}
+
+// 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 {
+	req := &SimpleBindRequest{
+		Username:           username,
+		Password:           password,
+		AllowEmptyPassword: false,
+	}
+	_, err := l.SimpleBind(req)
+	return err
+}
+
+// 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,
+	}
+	_, err := l.SimpleBind(req)
+	return err
+}
+
+// DigestMD5BindRequest represents a digest-md5 bind operation
+type DigestMD5BindRequest struct {
+	Host string
+	// Username is the name of the Directory object that the client wishes to bind as
+	Username string
+	// Password is the credentials to bind with
+	Password string
+	// Controls are optional controls to send with the bind request
+	Controls []Control
+}
+
+func (req *DigestMD5BindRequest) appendTo(envelope *ber.Packet) error {
+	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
+	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
+	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
+
+	auth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
+	auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "DIGEST-MD5", "SASL Mech"))
+	request.AppendChild(auth)
+	envelope.AppendChild(request)
+	if len(req.Controls) > 0 {
+		envelope.AppendChild(encodeControls(req.Controls))
+	}
+	return nil
+}
+
+// DigestMD5BindResult contains the response from the server
+type DigestMD5BindResult struct {
+	Controls []Control
+}
+
+// MD5Bind performs a digest-md5 bind with the given host, username and password.
+func (l *Conn) MD5Bind(host, username, password string) error {
+	req := &DigestMD5BindRequest{
+		Host:     host,
+		Username: username,
+		Password: password,
+	}
+	_, err := l.DigestMD5Bind(req)
+	return err
+}
+
+// DigestMD5Bind performs the digest-md5 bind operation defined in the given request
+func (l *Conn) DigestMD5Bind(digestMD5BindRequest *DigestMD5BindRequest) (*DigestMD5BindResult, error) {
+	if digestMD5BindRequest.Password == "" {
+		return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
+	}
+
+	msgCtx, err := l.doRequest(digestMD5BindRequest)
+	if err != nil {
+		return nil, err
+	}
+	defer l.finishMessage(msgCtx)
+
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return nil, err
+	}
+	l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
+	if l.Debug {
+		if err = addLDAPDescriptions(packet); err != nil {
+			return nil, err
+		}
+		ber.PrintPacket(packet)
+	}
+
+	result := &DigestMD5BindResult{
+		Controls: make([]Control, 0),
+	}
+	var params map[string]string
+	if len(packet.Children) == 2 {
+		if len(packet.Children[1].Children) == 4 {
+			child := packet.Children[1].Children[0]
+			if child.Tag != ber.TagEnumerated {
+				return result, GetLDAPError(packet)
+			}
+			if child.Value.(int64) != 14 {
+				return result, GetLDAPError(packet)
+			}
+			child = packet.Children[1].Children[3]
+			if child.Tag != ber.TagObjectDescriptor {
+				return result, GetLDAPError(packet)
+			}
+			if child.Data == nil {
+				return result, GetLDAPError(packet)
+			}
+			data, _ := ioutil.ReadAll(child.Data)
+			params, err = parseParams(string(data))
+			if err != nil {
+				return result, fmt.Errorf("parsing digest-challenge: %s", err)
+			}
+		}
+	}
+
+	if params != nil {
+		resp := computeResponse(
+			params,
+			"ldap/"+strings.ToLower(digestMD5BindRequest.Host),
+			digestMD5BindRequest.Username,
+			digestMD5BindRequest.Password,
+		)
+		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"))
+
+		request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
+		request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
+		request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
+
+		auth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
+		auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "DIGEST-MD5", "SASL Mech"))
+		auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, resp, "Credentials"))
+		request.AppendChild(auth)
+		packet.AppendChild(request)
+		msgCtx, err = l.sendMessage(packet)
+		if err != nil {
+			return nil, fmt.Errorf("send message: %s", err)
+		}
+		defer l.finishMessage(msgCtx)
+		packetResponse, ok := <-msgCtx.responses
+		if !ok {
+			return nil, 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 nil, fmt.Errorf("read packet: %s", err)
+		}
+	}
+
+	err = GetLDAPError(packet)
+	return result, err
+}
+
+func parseParams(str string) (map[string]string, error) {
+	m := make(map[string]string)
+	var key, value string
+	var state int
+	for i := 0; i <= len(str); i++ {
+		switch state {
+		case 0: //reading key
+			if i == len(str) {
+				return nil, fmt.Errorf("syntax error on %d", i)
+			}
+			if str[i] != '=' {
+				key += string(str[i])
+				continue
+			}
+			state = 1
+		case 1: //reading value
+			if i == len(str) {
+				m[key] = value
+				break
+			}
+			switch str[i] {
+			case ',':
+				m[key] = value
+				state = 0
+				key = ""
+				value = ""
+			case '"':
+				if value != "" {
+					return nil, fmt.Errorf("syntax error on %d", i)
+				}
+				state = 2
+			default:
+				value += string(str[i])
+			}
+		case 2: //inside quotes
+			if i == len(str) {
+				return nil, fmt.Errorf("syntax error on %d", i)
+			}
+			if str[i] != '"' {
+				value += string(str[i])
+			} else {
+				state = 1
+			}
+		}
+	}
+	return m, nil
+}
+
+func computeResponse(params map[string]string, uri, username, password string) string {
+	nc := "00000001"
+	qop := "auth"
+	cnonce := enchex.EncodeToString(randomBytes(16))
+	x := username + ":" + params["realm"] + ":" + password
+	y := md5Hash([]byte(x))
+
+	a1 := bytes.NewBuffer(y)
+	a1.WriteString(":" + params["nonce"] + ":" + cnonce)
+	if len(params["authzid"]) > 0 {
+		a1.WriteString(":" + params["authzid"])
+	}
+	a2 := bytes.NewBuffer([]byte("AUTHENTICATE"))
+	a2.WriteString(":" + uri)
+	ha1 := enchex.EncodeToString(md5Hash(a1.Bytes()))
+	ha2 := enchex.EncodeToString(md5Hash(a2.Bytes()))
+
+	kd := ha1
+	kd += ":" + params["nonce"]
+	kd += ":" + nc
+	kd += ":" + cnonce
+	kd += ":" + qop
+	kd += ":" + ha2
+	resp := enchex.EncodeToString(md5Hash([]byte(kd)))
+	return fmt.Sprintf(
+		`username="%s",realm="%s",nonce="%s",cnonce="%s",nc=00000001,qop=%s,digest-uri="%s",response=%s`,
+		username,
+		params["realm"],
+		params["nonce"],
+		cnonce,
+		qop,
+		uri,
+		resp,
+	)
+}
+
+func md5Hash(b []byte) []byte {
+	hasher := md5.New()
+	hasher.Write(b)
+	return hasher.Sum(nil)
+}
+
+func randomBytes(len int) []byte {
+	b := make([]byte, len)
+	for i := 0; i < len; i++ {
+		b[i] = byte(rand.Intn(256))
+	}
+	return b
+}
+
+var externalBindRequest = requestFunc(func(envelope *ber.Packet) error {
+	pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
+	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
+	pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
+
+	saslAuth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
+	saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "EXTERNAL", "SASL Mech"))
+	saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "SASL Cred"))
+
+	pkt.AppendChild(saslAuth)
+
+	envelope.AppendChild(pkt)
+
+	return nil
+})
+
+// ExternalBind performs SASL/EXTERNAL authentication.
+//
+// Use ldap.DialURL("ldapi://") to connect to the Unix socket before ExternalBind.
+//
+// See https://tools.ietf.org/html/rfc4422#appendix-A
+func (l *Conn) ExternalBind() error {
+	msgCtx, err := l.doRequest(externalBindRequest)
+	if err != nil {
+		return err
+	}
+	defer l.finishMessage(msgCtx)
+
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return err
+	}
+
+	return GetLDAPError(packet)
+}
+
+// NTLMBind performs an NTLMSSP bind leveraging https://github.com/Azure/go-ntlmssp
+
+// NTLMBindRequest represents an NTLMSSP bind operation
+type NTLMBindRequest struct {
+	// Domain is the AD Domain to authenticate too. If not specified, it will be grabbed from the NTLMSSP Challenge
+	Domain string
+	// Username is the name of the Directory object that the client wishes to bind as
+	Username string
+	// Password is the credentials to bind with
+	Password string
+	// Hash is the hex NTLM hash to bind with. Password or hash must be provided
+	Hash string
+	// Controls are optional controls to send with the bind request
+	Controls []Control
+}
+
+func (req *NTLMBindRequest) appendTo(envelope *ber.Packet) error {
+	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
+	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
+	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
+
+	// generate an NTLMSSP Negotiation message for the  specified domain (it can be blank)
+	negMessage, err := ntlmssp.NewNegotiateMessage(req.Domain, "")
+	if err != nil {
+		return fmt.Errorf("err creating negmessage: %s", err)
+	}
+
+	// append the generated NTLMSSP message as a TagEnumerated BER value
+	auth := ber.Encode(ber.ClassContext, ber.TypePrimitive, ber.TagEnumerated, negMessage, "authentication")
+	request.AppendChild(auth)
+	envelope.AppendChild(request)
+	if len(req.Controls) > 0 {
+		envelope.AppendChild(encodeControls(req.Controls))
+	}
+	return nil
+}
+
+// NTLMBindResult contains the response from the server
+type NTLMBindResult struct {
+	Controls []Control
+}
+
+// NTLMBind performs an NTLMSSP Bind with the given domain, username and password
+func (l *Conn) NTLMBind(domain, username, password string) error {
+	req := &NTLMBindRequest{
+		Domain:   domain,
+		Username: username,
+		Password: password,
+	}
+	_, err := l.NTLMChallengeBind(req)
+	return err
+}
+
+// NTLMBindWithHash performs an NTLM Bind with an NTLM hash instead of plaintext password (pass-the-hash)
+func (l *Conn) NTLMBindWithHash(domain, username, hash string) error {
+	req := &NTLMBindRequest{
+		Domain:   domain,
+		Username: username,
+		Hash:     hash,
+	}
+	_, err := l.NTLMChallengeBind(req)
+	return err
+}
+
+// NTLMChallengeBind performs the NTLMSSP bind operation defined in the given request
+func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindResult, error) {
+	if ntlmBindRequest.Password == "" && ntlmBindRequest.Hash == "" {
+		return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
+	}
+
+	msgCtx, err := l.doRequest(ntlmBindRequest)
+	if err != nil {
+		return nil, err
+	}
+	defer l.finishMessage(msgCtx)
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return nil, err
+	}
+	l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
+	if l.Debug {
+		if err = addLDAPDescriptions(packet); err != nil {
+			return nil, err
+		}
+		ber.PrintPacket(packet)
+	}
+	result := &NTLMBindResult{
+		Controls: make([]Control, 0),
+	}
+	var ntlmsspChallenge []byte
+
+	// now find the NTLM Response Message
+	if len(packet.Children) == 2 {
+		if len(packet.Children[1].Children) == 3 {
+			child := packet.Children[1].Children[1]
+			ntlmsspChallenge = child.ByteValue
+			// Check to make sure we got the right message. It will always start with NTLMSSP
+			if len(ntlmsspChallenge) < 7 || !bytes.Equal(ntlmsspChallenge[:7], []byte("NTLMSSP")) {
+				return result, GetLDAPError(packet)
+			}
+			l.Debug.Printf("%d: found ntlmssp challenge", msgCtx.id)
+		}
+	}
+	if ntlmsspChallenge != nil {
+		var err error
+		var responseMessage []byte
+		// generate a response message to the challenge with the given Username/Password if password is provided
+		if ntlmBindRequest.Password != "" {
+			responseMessage, err = ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password)
+		} else if ntlmBindRequest.Hash != "" {
+			responseMessage, err = ntlmssp.ProcessChallengeWithHash(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Hash)
+		} else {
+			err = fmt.Errorf("need a password or hash to generate reply")
+		}
+		if err != nil {
+			return result, fmt.Errorf("parsing ntlm-challenge: %s", err)
+		}
+		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"))
+
+		request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
+		request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
+		request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
+
+		// append the challenge response message as a TagEmbeddedPDV BER value
+		auth := ber.Encode(ber.ClassContext, ber.TypePrimitive, ber.TagEmbeddedPDV, responseMessage, "authentication")
+
+		request.AppendChild(auth)
+		packet.AppendChild(request)
+		msgCtx, err = l.sendMessage(packet)
+		if err != nil {
+			return nil, fmt.Errorf("send message: %s", err)
+		}
+		defer l.finishMessage(msgCtx)
+		packetResponse, ok := <-msgCtx.responses
+		if !ok {
+			return nil, 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 nil, fmt.Errorf("read packet: %s", err)
+		}
+
+	}
+
+	err = GetLDAPError(packet)
+	return result, err
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/client.go b/vendor/github.com/go-ldap/ldap/v3/client.go
new file mode 100644
index 0000000..1fa4ad5
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/client.go
@@ -0,0 +1,32 @@
+package ldap
+
+import (
+	"crypto/tls"
+	"time"
+)
+
+// Client knows how to interact with an LDAP server
+type Client interface {
+	Start()
+	StartTLS(*tls.Config) error
+	Close()
+	IsClosing() bool
+	SetTimeout(time.Duration)
+
+	Bind(username, password string) error
+	UnauthenticatedBind(username string) error
+	SimpleBind(*SimpleBindRequest) (*SimpleBindResult, error)
+	ExternalBind() error
+
+	Add(*AddRequest) error
+	Del(*DelRequest) error
+	Modify(*ModifyRequest) error
+	ModifyDN(*ModifyDNRequest) error
+	ModifyWithResult(*ModifyRequest) (*ModifyResult, error)
+
+	Compare(dn, attribute, value string) (bool, error)
+	PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error)
+
+	Search(*SearchRequest) (*SearchResult, error)
+	SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/compare.go b/vendor/github.com/go-ldap/ldap/v3/compare.go
new file mode 100644
index 0000000..cd43e4c
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/compare.go
@@ -0,0 +1,61 @@
+package ldap
+
+import (
+	"fmt"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// CompareRequest represents an LDAP CompareRequest operation.
+type CompareRequest struct {
+	DN        string
+	Attribute string
+	Value     string
+}
+
+func (req *CompareRequest) appendTo(envelope *ber.Packet) error {
+	pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request")
+	pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN"))
+
+	ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion")
+	ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Attribute, "AttributeDesc"))
+	ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Value, "AssertionValue"))
+
+	pkt.AppendChild(ava)
+
+	envelope.AppendChild(pkt)
+
+	return nil
+}
+
+// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise
+// false with any error that occurs if any.
+func (l *Conn) Compare(dn, attribute, value string) (bool, error) {
+	msgCtx, err := l.doRequest(&CompareRequest{
+		DN:        dn,
+		Attribute: attribute,
+		Value:     value})
+	if err != nil {
+		return false, err
+	}
+	defer l.finishMessage(msgCtx)
+
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return false, err
+	}
+
+	if packet.Children[1].Tag == ApplicationCompareResponse {
+		err := GetLDAPError(packet)
+
+		switch {
+		case IsErrorWithCode(err, LDAPResultCompareTrue):
+			return true, nil
+		case IsErrorWithCode(err, LDAPResultCompareFalse):
+			return false, nil
+		default:
+			return false, err
+		}
+	}
+	return false, fmt.Errorf("unexpected Response: %d", packet.Children[1].Tag)
+}
diff --git a/vendor/gopkg.in/ldap.v2/conn.go b/vendor/github.com/go-ldap/ldap/v3/conn.go
similarity index 75%
rename from vendor/gopkg.in/ldap.v2/conn.go
rename to vendor/github.com/go-ldap/ldap/v3/conn.go
index eb28eb4..ae5e19a 100644
--- a/vendor/gopkg.in/ldap.v2/conn.go
+++ b/vendor/github.com/go-ldap/ldap/v3/conn.go
@@ -1,20 +1,18 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package ldap
 
 import (
+	"bufio"
 	"crypto/tls"
 	"errors"
 	"fmt"
 	"log"
 	"net"
+	"net/url"
 	"sync"
 	"sync/atomic"
 	"time"
 
-	"gopkg.in/asn1-ber.v1"
+	ber "github.com/go-asn1-ber/asn1-ber"
 )
 
 const (
@@ -30,6 +28,13 @@ const (
 	MessageTimeout = 4
 )
 
+const (
+	// DefaultLdapPort default ldap port for pure TCP connection
+	DefaultLdapPort = "389"
+	// DefaultLdapsPort default ldap port for SSL connection
+	DefaultLdapsPort = "636"
+)
+
 // PacketResponse contains the packet or error encountered reading a response
 type PacketResponse struct {
 	// Packet is the packet read from the server
@@ -81,10 +86,13 @@ const (
 
 // Conn represents an LDAP Connection
 type Conn struct {
+	// requestTimeout is loaded atomically
+	// so we need to ensure 64-bit alignment on 32-bit platforms.
+	requestTimeout      int64
 	conn                net.Conn
 	isTLS               bool
 	closing             uint32
-	closeErr            atomicValue
+	closeErr            atomic.Value
 	isStartingTLS       bool
 	Debug               debugging
 	chanConfirm         chan struct{}
@@ -94,7 +102,6 @@ type Conn struct {
 	wgClose             sync.WaitGroup
 	outstandingRequests uint
 	messageMutex        sync.Mutex
-	requestTimeout      int64
 }
 
 var _ Client = &Conn{}
@@ -106,8 +113,72 @@ var _ Client = &Conn{}
 // multiple places will probably result in undesired behaviour.
 var DefaultTimeout = 60 * time.Second
 
+// DialOpt configures DialContext.
+type DialOpt func(*DialContext)
+
+// DialWithDialer updates net.Dialer in DialContext.
+func DialWithDialer(d *net.Dialer) DialOpt {
+	return func(dc *DialContext) {
+		dc.d = d
+	}
+}
+
+// DialWithTLSConfig updates tls.Config in DialContext.
+func DialWithTLSConfig(tc *tls.Config) DialOpt {
+	return func(dc *DialContext) {
+		dc.tc = tc
+	}
+}
+
+// DialWithTLSDialer is a wrapper for DialWithTLSConfig with the option to
+// specify a net.Dialer to for example define a timeout or a custom resolver.
+func DialWithTLSDialer(tlsConfig *tls.Config, dialer *net.Dialer) DialOpt {
+	return func(dc *DialContext) {
+		dc.tc = tlsConfig
+		dc.d = dialer
+	}
+}
+
+// DialContext contains necessary parameters to dial the given ldap URL.
+type DialContext struct {
+	d  *net.Dialer
+	tc *tls.Config
+}
+
+func (dc *DialContext) dial(u *url.URL) (net.Conn, error) {
+	if u.Scheme == "ldapi" {
+		if u.Path == "" || u.Path == "/" {
+			u.Path = "/var/run/slapd/ldapi"
+		}
+		return dc.d.Dial("unix", u.Path)
+	}
+
+	host, port, err := net.SplitHostPort(u.Host)
+	if err != nil {
+		// we assume that error is due to missing port
+		host = u.Host
+		port = ""
+	}
+
+	switch u.Scheme {
+	case "ldap":
+		if port == "" {
+			port = DefaultLdapPort
+		}
+		return dc.d.Dial("tcp", net.JoinHostPort(host, port))
+	case "ldaps":
+		if port == "" {
+			port = DefaultLdapsPort
+		}
+		return tls.DialWithDialer(dc.d, "tcp", net.JoinHostPort(host, port), dc.tc)
+	}
+
+	return nil, fmt.Errorf("Unknown scheme '%s'", u.Scheme)
+}
+
 // Dial connects to the given address on the given network using net.Dial
 // and then returns a new Conn for the connection.
+// @deprecated Use DialURL instead.
 func Dial(network, addr string) (*Conn, error) {
 	c, err := net.DialTimeout(network, addr, DefaultTimeout)
 	if err != nil {
@@ -120,19 +191,40 @@ func Dial(network, addr string) (*Conn, error) {
 
 // DialTLS connects to the given address on the given network using tls.Dial
 // and then returns a new Conn for the connection.
+// @deprecated Use DialURL instead.
 func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
-	dc, err := net.DialTimeout(network, addr, DefaultTimeout)
+	c, err := tls.DialWithDialer(&net.Dialer{Timeout: DefaultTimeout}, network, addr, config)
 	if err != nil {
 		return nil, NewError(ErrorNetwork, err)
 	}
-	c := tls.Client(dc, config)
-	err = c.Handshake()
+	conn := NewConn(c, true)
+	conn.Start()
+	return conn, nil
+}
+
+// DialURL connects to the given ldap URL.
+// The following schemas are supported: ldap://, ldaps://, ldapi://.
+// On success a new Conn for the connection is returned.
+func DialURL(addr string, opts ...DialOpt) (*Conn, error) {
+	u, err := url.Parse(addr)
 	if err != nil {
-		// Handshake error, close the established connection before we return an error
-		dc.Close()
 		return nil, NewError(ErrorNetwork, err)
 	}
-	conn := NewConn(c, true)
+
+	var dc DialContext
+	for _, opt := range opts {
+		opt(&dc)
+	}
+	if dc.d == nil {
+		dc.d = &net.Dialer{Timeout: DefaultTimeout}
+	}
+
+	c, err := dc.dial(u)
+	if err != nil {
+		return nil, NewError(ErrorNetwork, err)
+	}
+
+	conn := NewConn(c, u.Scheme == "ldaps")
 	conn.Start()
 	return conn, nil
 }
@@ -152,13 +244,13 @@ func NewConn(conn net.Conn, isTLS bool) *Conn {
 
 // Start initializes goroutines to read responses and process messages
 func (l *Conn) Start() {
+	l.wgClose.Add(1)
 	go l.reader()
 	go l.processMessages()
-	l.wgClose.Add(1)
 }
 
-// isClosing returns whether or not we're currently closing.
-func (l *Conn) isClosing() bool {
+// IsClosing returns whether or not we're currently closing.
+func (l *Conn) IsClosing() bool {
 	return atomic.LoadUint32(&l.closing) == 1
 }
 
@@ -239,33 +331,44 @@ func (l *Conn) StartTLS(config *tls.Config) error {
 			l.Close()
 			return err
 		}
-		ber.PrintPacket(packet)
+		l.Debug.PrintPacket(packet)
 	}
 
-	if resultCode, message := getLDAPResultCode(packet); resultCode == LDAPResultSuccess {
+	if err := GetLDAPError(packet); err == nil {
 		conn := tls.Client(l.conn, config)
 
-		if err := conn.Handshake(); err != nil {
+		if connErr := conn.Handshake(); connErr != nil {
 			l.Close()
-			return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", err))
+			return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", connErr))
 		}
 
 		l.isTLS = true
 		l.conn = conn
 	} else {
-		return NewError(resultCode, fmt.Errorf("ldap: cannot StartTLS (%s)", message))
+		return err
 	}
 	go l.reader()
 
 	return nil
 }
 
+// TLSConnectionState returns the client's TLS connection state.
+// The return values are their zero values if StartTLS did
+// not succeed.
+func (l *Conn) TLSConnectionState() (state tls.ConnectionState, ok bool) {
+	tc, ok := l.conn.(*tls.Conn)
+	if !ok {
+		return
+	}
+	return tc.ConnectionState(), true
+}
+
 func (l *Conn) sendMessage(packet *ber.Packet) (*messageContext, error) {
 	return l.sendMessageWithFlags(packet, 0)
 }
 
 func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (*messageContext, error) {
-	if l.isClosing() {
+	if l.IsClosing() {
 		return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
 	}
 	l.messageMutex.Lock()
@@ -297,14 +400,19 @@ func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags)
 			responses: responses,
 		},
 	}
-	l.sendProcessMessage(message)
+	if !l.sendProcessMessage(message) {
+		if l.IsClosing() {
+			return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
+		}
+		return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message for unknown reason"))
+	}
 	return message.Context, nil
 }
 
 func (l *Conn) finishMessage(msgCtx *messageContext) {
 	close(msgCtx.done)
 
-	if l.isClosing() {
+	if l.IsClosing() {
 		return
 	}
 
@@ -325,7 +433,7 @@ func (l *Conn) finishMessage(msgCtx *messageContext) {
 func (l *Conn) sendProcessMessage(message *messagePacket) bool {
 	l.messageMutex.Lock()
 	defer l.messageMutex.Unlock()
-	if l.isClosing() {
+	if l.IsClosing() {
 		return false
 	}
 	l.chanMessage <- message
@@ -340,7 +448,7 @@ func (l *Conn) processMessages() {
 		for messageID, msgCtx := range l.messageContexts {
 			// If we are closing due to an error, inform anyone who
 			// is waiting about the error.
-			if l.isClosing() && l.closeErr.Load() != nil {
+			if l.IsClosing() && l.closeErr.Load() != nil {
 				msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)})
 			}
 			l.Debug.Printf("Closing channel for MessageID %d", messageID)
@@ -400,15 +508,15 @@ func (l *Conn) processMessages() {
 				if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
 					msgCtx.sendResponse(&PacketResponse{message.Packet, nil})
 				} else {
-					log.Printf("Received unexpected message %d, %v", message.MessageID, l.isClosing())
-					ber.PrintPacket(message.Packet)
+					log.Printf("Received unexpected message %d, %v", message.MessageID, l.IsClosing())
+					l.Debug.PrintPacket(message.Packet)
 				}
 			case MessageTimeout:
 				// Handle the timeout by closing the channel
 				// All reads will return immediately
 				if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
 					l.Debug.Printf("Receiving message timeout for %d", message.MessageID)
-					msgCtx.sendResponse(&PacketResponse{message.Packet, errors.New("ldap: connection timed out")})
+					msgCtx.sendResponse(&PacketResponse{message.Packet, NewError(ErrorNetwork, errors.New("ldap: connection timed out"))})
 					delete(l.messageContexts, message.MessageID)
 					close(msgCtx.responses)
 				}
@@ -434,21 +542,24 @@ func (l *Conn) reader() {
 		}
 	}()
 
+	bufConn := bufio.NewReader(l.conn)
 	for {
 		if cleanstop {
 			l.Debug.Printf("reader clean stopping (without closing the connection)")
 			return
 		}
-		packet, err := ber.ReadPacket(l.conn)
+		packet, err := ber.ReadPacket(bufConn)
 		if err != nil {
 			// A read error is expected here if we are closing the connection...
-			if !l.isClosing() {
+			if !l.IsClosing() {
 				l.closeErr.Store(fmt.Errorf("unable to read LDAP response packet: %s", err))
-				l.Debug.Printf("reader error: %s", err.Error())
+				l.Debug.Printf("reader error: %s", err)
 			}
 			return
 		}
-		addLDAPDescriptions(packet)
+		if err := addLDAPDescriptions(packet); err != nil {
+			l.Debug.Printf("descriptions error: %s", err)
+		}
 		if len(packet.Children) == 0 {
 			l.Debug.Printf("Received bad ldap packet")
 			continue
diff --git a/vendor/gopkg.in/ldap.v2/control.go b/vendor/github.com/go-ldap/ldap/v3/control.go
similarity index 66%
rename from vendor/gopkg.in/ldap.v2/control.go
rename to vendor/github.com/go-ldap/ldap/v3/control.go
index 342f325..64fb002 100644
--- a/vendor/gopkg.in/ldap.v2/control.go
+++ b/vendor/github.com/go-ldap/ldap/v3/control.go
@@ -1,14 +1,10 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package ldap
 
 import (
 	"fmt"
 	"strconv"
 
-	"gopkg.in/asn1-ber.v1"
+	ber "github.com/go-asn1-ber/asn1-ber"
 )
 
 const (
@@ -22,13 +18,25 @@ const (
 	ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
 	// ControlTypeManageDsaIT - https://tools.ietf.org/html/rfc3296
 	ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
+	// ControlTypeWhoAmI - https://tools.ietf.org/html/rfc4532
+	ControlTypeWhoAmI = "1.3.6.1.4.1.4203.1.11.3"
+
+	// ControlTypeMicrosoftNotification - https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
+	ControlTypeMicrosoftNotification = "1.2.840.113556.1.4.528"
+	// ControlTypeMicrosoftShowDeleted - https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
+	ControlTypeMicrosoftShowDeleted = "1.2.840.113556.1.4.417"
+	// ControlTypeMicrosoftServerLinkTTL - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/f4f523a8-abc0-4b3a-a471-6b2fef135481?redirectedfrom=MSDN
+	ControlTypeMicrosoftServerLinkTTL = "1.2.840.113556.1.4.2309"
 )
 
 // ControlTypeMap maps controls to text descriptions
 var ControlTypeMap = map[string]string{
-	ControlTypePaging:               "Paging",
-	ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
-	ControlTypeManageDsaIT:          "Manage DSA IT",
+	ControlTypePaging:                 "Paging",
+	ControlTypeBeheraPasswordPolicy:   "Password Policy - Behera Draft",
+	ControlTypeManageDsaIT:            "Manage DSA IT",
+	ControlTypeMicrosoftNotification:  "Change Notification - Microsoft",
+	ControlTypeMicrosoftShowDeleted:   "Show Deleted Objects - Microsoft",
+	ControlTypeMicrosoftServerLinkTTL: "Return TTL-DNs for link values with associated expiry times - Microsoft",
 }
 
 // Control defines an interface controls provide to encode and describe themselves
@@ -60,7 +68,9 @@ func (c *ControlString) Encode() *ber.Packet {
 	if c.Criticality {
 		packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
 	}
-	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value"))
+	if c.ControlValue != "" {
+		packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value"))
+	}
 	return packet
 }
 
@@ -242,6 +252,93 @@ func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {
 	return &ControlManageDsaIT{Criticality: Criticality}
 }
 
+// ControlMicrosoftNotification implements the control described in https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
+type ControlMicrosoftNotification struct{}
+
+// GetControlType returns the OID
+func (c *ControlMicrosoftNotification) GetControlType() string {
+	return ControlTypeMicrosoftNotification
+}
+
+// Encode returns the ber packet representation
+func (c *ControlMicrosoftNotification) Encode() *ber.Packet {
+	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftNotification, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftNotification]+")"))
+
+	return packet
+}
+
+// String returns a human-readable description
+func (c *ControlMicrosoftNotification) String() string {
+	return fmt.Sprintf(
+		"Control Type: %s (%q)",
+		ControlTypeMap[ControlTypeMicrosoftNotification],
+		ControlTypeMicrosoftNotification)
+}
+
+// NewControlMicrosoftNotification returns a ControlMicrosoftNotification control
+func NewControlMicrosoftNotification() *ControlMicrosoftNotification {
+	return &ControlMicrosoftNotification{}
+}
+
+// ControlMicrosoftShowDeleted implements the control described in https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
+type ControlMicrosoftShowDeleted struct{}
+
+// GetControlType returns the OID
+func (c *ControlMicrosoftShowDeleted) GetControlType() string {
+	return ControlTypeMicrosoftShowDeleted
+}
+
+// Encode returns the ber packet representation
+func (c *ControlMicrosoftShowDeleted) Encode() *ber.Packet {
+	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftShowDeleted, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftShowDeleted]+")"))
+
+	return packet
+}
+
+// String returns a human-readable description
+func (c *ControlMicrosoftShowDeleted) String() string {
+	return fmt.Sprintf(
+		"Control Type: %s (%q)",
+		ControlTypeMap[ControlTypeMicrosoftShowDeleted],
+		ControlTypeMicrosoftShowDeleted)
+}
+
+// NewControlMicrosoftShowDeleted returns a ControlMicrosoftShowDeleted control
+func NewControlMicrosoftShowDeleted() *ControlMicrosoftShowDeleted {
+	return &ControlMicrosoftShowDeleted{}
+}
+
+// ControlMicrosoftServerLinkTTL implements the control described in https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/f4f523a8-abc0-4b3a-a471-6b2fef135481?redirectedfrom=MSDN
+type ControlMicrosoftServerLinkTTL struct{}
+
+// GetControlType returns the OID
+func (c *ControlMicrosoftServerLinkTTL) GetControlType() string {
+	return ControlTypeMicrosoftServerLinkTTL
+}
+
+// Encode returns the ber packet representation
+func (c *ControlMicrosoftServerLinkTTL) Encode() *ber.Packet {
+	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftServerLinkTTL, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftServerLinkTTL]+")"))
+
+	return packet
+}
+
+// String returns a human-readable description
+func (c *ControlMicrosoftServerLinkTTL) String() string {
+	return fmt.Sprintf(
+		"Control Type: %s (%q)",
+		ControlTypeMap[ControlTypeMicrosoftServerLinkTTL],
+		ControlTypeMicrosoftServerLinkTTL)
+}
+
+// NewControlMicrosoftServerLinkTTL returns a ControlMicrosoftServerLinkTTL control
+func NewControlMicrosoftServerLinkTTL() *ControlMicrosoftServerLinkTTL {
+	return &ControlMicrosoftServerLinkTTL{}
+}
+
 // FindControl returns the first control of the given type in the list, or nil
 func FindControl(controls []Control, controlType string) Control {
 	for _, c := range controls {
@@ -253,7 +350,7 @@ func FindControl(controls []Control, controlType string) Control {
 }
 
 // DecodeControl returns a control read from the given packet, or nil if no recognized control can be made
-func DecodeControl(packet *ber.Packet) Control {
+func DecodeControl(packet *ber.Packet) (Control, error) {
 	var (
 		ControlType = ""
 		Criticality = false
@@ -263,7 +360,7 @@ func DecodeControl(packet *ber.Packet) Control {
 	switch len(packet.Children) {
 	case 0:
 		// at least one child is required for control type
-		return nil
+		return nil, fmt.Errorf("at least one child is required for control type")
 
 	case 1:
 		// just type, no criticality or value
@@ -296,17 +393,20 @@ func DecodeControl(packet *ber.Packet) Control {
 
 	default:
 		// more than 3 children is invalid
-		return nil
+		return nil, fmt.Errorf("more than 3 children is invalid for controls")
 	}
 
 	switch ControlType {
 	case ControlTypeManageDsaIT:
-		return NewControlManageDsaIT(Criticality)
+		return NewControlManageDsaIT(Criticality), nil
 	case ControlTypePaging:
 		value.Description += " (Paging)"
 		c := new(ControlPaging)
 		if value.Value != nil {
-			valueChildren := ber.DecodePacket(value.Data.Bytes())
+			valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+			if err != nil {
+				return nil, fmt.Errorf("failed to decode data bytes: %s", err)
+			}
 			value.Data.Truncate(0)
 			value.Value = nil
 			value.AppendChild(valueChildren)
@@ -318,12 +418,15 @@ func DecodeControl(packet *ber.Packet) Control {
 		c.PagingSize = uint32(value.Children[0].Value.(int64))
 		c.Cookie = value.Children[1].Data.Bytes()
 		value.Children[1].Value = c.Cookie
-		return c
+		return c, nil
 	case ControlTypeBeheraPasswordPolicy:
 		value.Description += " (Password Policy - Behera)"
 		c := NewControlBeheraPasswordPolicy()
 		if value.Value != nil {
-			valueChildren := ber.DecodePacket(value.Data.Bytes())
+			valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+			if err != nil {
+				return nil, fmt.Errorf("failed to decode data bytes: %s", err)
+			}
 			value.Data.Truncate(0)
 			value.Value = nil
 			value.AppendChild(valueChildren)
@@ -335,48 +438,53 @@ func DecodeControl(packet *ber.Packet) Control {
 			if child.Tag == 0 {
 				//Warning
 				warningPacket := child.Children[0]
-				packet := ber.DecodePacket(warningPacket.Data.Bytes())
-				val, ok := packet.Value.(int64)
-				if ok {
-					if warningPacket.Tag == 0 {
-						//timeBeforeExpiration
-						c.Expire = val
-						warningPacket.Value = c.Expire
-					} else if warningPacket.Tag == 1 {
-						//graceAuthNsRemaining
-						c.Grace = val
-						warningPacket.Value = c.Grace
-					}
+				val, err := ber.ParseInt64(warningPacket.Data.Bytes())
+				if err != nil {
+					return nil, fmt.Errorf("failed to decode data bytes: %s", err)
+				}
+				if warningPacket.Tag == 0 {
+					//timeBeforeExpiration
+					c.Expire = val
+					warningPacket.Value = c.Expire
+				} else if warningPacket.Tag == 1 {
+					//graceAuthNsRemaining
+					c.Grace = val
+					warningPacket.Value = c.Grace
 				}
 			} else if child.Tag == 1 {
 				// Error
-				packet := ber.DecodePacket(child.Data.Bytes())
-				val, ok := packet.Value.(int8)
-				if !ok {
-					// what to do?
-					val = -1
+				bs := child.Data.Bytes()
+				if len(bs) != 1 || bs[0] > 8 {
+					return nil, fmt.Errorf("failed to decode data bytes: %s", "invalid PasswordPolicyResponse enum value")
 				}
+				val := int8(bs[0])
 				c.Error = val
 				child.Value = c.Error
 				c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error]
 			}
 		}
-		return c
+		return c, nil
 	case ControlTypeVChuPasswordMustChange:
 		c := &ControlVChuPasswordMustChange{MustChange: true}
-		return c
+		return c, nil
 	case ControlTypeVChuPasswordWarning:
 		c := &ControlVChuPasswordWarning{Expire: -1}
 		expireStr := ber.DecodeString(value.Data.Bytes())
 
 		expire, err := strconv.ParseInt(expireStr, 10, 64)
 		if err != nil {
-			return nil
+			return nil, fmt.Errorf("failed to parse value as int: %s", err)
 		}
 		c.Expire = expire
 		value.Value = c.Expire
 
-		return c
+		return c, nil
+	case ControlTypeMicrosoftNotification:
+		return NewControlMicrosoftNotification(), nil
+	case ControlTypeMicrosoftShowDeleted:
+		return NewControlMicrosoftShowDeleted(), nil
+	case ControlTypeMicrosoftServerLinkTTL:
+		return NewControlMicrosoftServerLinkTTL(), nil
 	default:
 		c := new(ControlString)
 		c.ControlType = ControlType
@@ -384,7 +492,7 @@ func DecodeControl(packet *ber.Packet) Control {
 		if value != nil {
 			c.ControlValue = value.Value.(string)
 		}
-		return c
+		return c, nil
 	}
 }
 
diff --git a/vendor/gopkg.in/ldap.v2/debug.go b/vendor/github.com/go-ldap/ldap/v3/debug.go
similarity index 56%
rename from vendor/gopkg.in/ldap.v2/debug.go
rename to vendor/github.com/go-ldap/ldap/v3/debug.go
index 7279fc2..d0a8fc1 100644
--- a/vendor/gopkg.in/ldap.v2/debug.go
+++ b/vendor/github.com/go-ldap/ldap/v3/debug.go
@@ -3,22 +3,28 @@ package ldap
 import (
 	"log"
 
-	"gopkg.in/asn1-ber.v1"
+	ber "github.com/go-asn1-ber/asn1-ber"
 )
 
 // debugging type
 //     - has a Printf method to write the debug output
 type debugging bool
 
-// write debug output
+// Enable controls debugging mode.
+func (debug *debugging) Enable(b bool) {
+	*debug = debugging(b)
+}
+
+// Printf writes debug output.
 func (debug debugging) Printf(format string, args ...interface{}) {
 	if debug {
 		log.Printf(format, args...)
 	}
 }
 
+// PrintPacket dumps a packet.
 func (debug debugging) PrintPacket(packet *ber.Packet) {
 	if debug {
-		ber.PrintPacket(packet)
+		ber.WritePacket(log.Writer(), packet)
 	}
 }
diff --git a/vendor/github.com/go-ldap/ldap/v3/del.go b/vendor/github.com/go-ldap/ldap/v3/del.go
new file mode 100644
index 0000000..6e98726
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/del.go
@@ -0,0 +1,59 @@
+package ldap
+
+import (
+	"log"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// DelRequest implements an LDAP deletion request
+type DelRequest struct {
+	// DN is the name of the directory entry to delete
+	DN string
+	// Controls hold optional controls to send with the request
+	Controls []Control
+}
+
+func (req *DelRequest) appendTo(envelope *ber.Packet) error {
+	pkt := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, req.DN, "Del Request")
+	pkt.Data.Write([]byte(req.DN))
+
+	envelope.AppendChild(pkt)
+	if len(req.Controls) > 0 {
+		envelope.AppendChild(encodeControls(req.Controls))
+	}
+
+	return nil
+}
+
+// NewDelRequest creates a delete request for the given DN and controls
+func NewDelRequest(DN string, Controls []Control) *DelRequest {
+	return &DelRequest{
+		DN:       DN,
+		Controls: Controls,
+	}
+}
+
+// Del executes the given delete request
+func (l *Conn) Del(delRequest *DelRequest) error {
+	msgCtx, err := l.doRequest(delRequest)
+	if err != nil {
+		return err
+	}
+	defer l.finishMessage(msgCtx)
+
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return err
+	}
+
+	if packet.Children[1].Tag == ApplicationDelResponse {
+		err := GetLDAPError(packet)
+		if err != nil {
+			return err
+		}
+	} else {
+		log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
+	}
+	return nil
+}
diff --git a/vendor/gopkg.in/ldap.v2/dn.go b/vendor/github.com/go-ldap/ldap/v3/dn.go
similarity index 66%
rename from vendor/gopkg.in/ldap.v2/dn.go
rename to vendor/github.com/go-ldap/ldap/v3/dn.go
index 34e9023..2b4cede 100644
--- a/vendor/gopkg.in/ldap.v2/dn.go
+++ b/vendor/github.com/go-ldap/ldap/v3/dn.go
@@ -1,48 +1,3 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-//
-// File contains DN parsing functionality
-//
-// https://tools.ietf.org/html/rfc4514
-//
-//   distinguishedName = [ relativeDistinguishedName
-//         *( COMMA relativeDistinguishedName ) ]
-//     relativeDistinguishedName = attributeTypeAndValue
-//         *( PLUS attributeTypeAndValue )
-//     attributeTypeAndValue = attributeType EQUALS attributeValue
-//     attributeType = descr / numericoid
-//     attributeValue = string / hexstring
-//
-//     ; The following characters are to be escaped when they appear
-//     ; in the value to be encoded: ESC, one of <escaped>, leading
-//     ; SHARP or SPACE, trailing SPACE, and NULL.
-//     string =   [ ( leadchar / pair ) [ *( stringchar / pair )
-//        ( trailchar / pair ) ] ]
-//
-//     leadchar = LUTF1 / UTFMB
-//     LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A /
-//        %x3D / %x3F-5B / %x5D-7F
-//
-//     trailchar  = TUTF1 / UTFMB
-//     TUTF1 = %x01-1F / %x21 / %x23-2A / %x2D-3A /
-//        %x3D / %x3F-5B / %x5D-7F
-//
-//     stringchar = SUTF1 / UTFMB
-//     SUTF1 = %x01-21 / %x23-2A / %x2D-3A /
-//        %x3D / %x3F-5B / %x5D-7F
-//
-//     pair = ESC ( ESC / special / hexpair )
-//     special = escaped / SPACE / SHARP / EQUALS
-//     escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
-//     hexstring = SHARP 1*hexpair
-//     hexpair = HEX HEX
-//
-//  where the productions <descr>, <numericoid>, <COMMA>, <DQUOTE>,
-//  <EQUALS>, <ESC>, <HEX>, <LANGLE>, <NULL>, <PLUS>, <RANGLE>, <SEMI>,
-//  <SPACE>, <SHARP>, and <UTFMB> are defined in [RFC4512].
-//
-
 package ldap
 
 import (
@@ -52,7 +7,7 @@ import (
 	"fmt"
 	"strings"
 
-	"gopkg.in/asn1-ber.v1"
+	ber "github.com/go-asn1-ber/asn1-ber"
 )
 
 // AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514
@@ -73,7 +28,8 @@ type DN struct {
 	RDNs []*RelativeDN
 }
 
-// ParseDN returns a distinguishedName or an error
+// ParseDN returns a distinguishedName or an error.
+// The function respects https://tools.ietf.org/html/rfc4514
 func ParseDN(str string) (*DN, error) {
 	dn := new(DN)
 	dn.RDNs = make([]*RelativeDN, 0)
@@ -94,7 +50,8 @@ func ParseDN(str string) (*DN, error) {
 
 	for i := 0; i < len(str); i++ {
 		char := str[i]
-		if escaping {
+		switch {
+		case escaping:
 			unescapedTrailingSpaces = 0
 			escaping = false
 			switch char {
@@ -104,22 +61,22 @@ func ParseDN(str string) (*DN, error) {
 			}
 			// Not a special character, assume hex encoded octet
 			if len(str) == i+1 {
-				return nil, errors.New("Got corrupted escaped character")
+				return nil, errors.New("got corrupted escaped character")
 			}
 
 			dst := []byte{0}
 			n, err := enchex.Decode([]byte(dst), []byte(str[i:i+2]))
 			if err != nil {
-				return nil, fmt.Errorf("Failed to decode escaped character: %s", err)
+				return nil, fmt.Errorf("failed to decode escaped character: %s", err)
 			} else if n != 1 {
-				return nil, fmt.Errorf("Expected 1 byte when un-escaping, got %d", n)
+				return nil, fmt.Errorf("expected 1 byte when un-escaping, got %d", n)
 			}
 			buffer.WriteByte(dst[0])
 			i++
-		} else if char == '\\' {
+		case char == '\\':
 			unescapedTrailingSpaces = 0
 			escaping = true
-		} else if char == '=' {
+		case char == '=':
 			attribute.Type = stringFromBuffer()
 			// Special case: If the first character in the value is # the
 			// following data is BER encoded so we can just fast forward
@@ -135,13 +92,16 @@ func ParseDN(str string) (*DN, error) {
 				}
 				rawBER, err := enchex.DecodeString(data)
 				if err != nil {
-					return nil, fmt.Errorf("Failed to decode BER encoding: %s", err)
+					return nil, fmt.Errorf("failed to decode BER encoding: %s", err)
+				}
+				packet, err := ber.DecodePacketErr(rawBER)
+				if err != nil {
+					return nil, fmt.Errorf("failed to decode BER packet: %s", err)
 				}
-				packet := ber.DecodePacket(rawBER)
 				buffer.WriteString(packet.Data.String())
 				i += len(data) - 1
 			}
-		} else if char == ',' || char == '+' {
+		case char == ',' || char == '+':
 			// We're done with this RDN or value, push it
 			if len(attribute.Type) == 0 {
 				return nil, errors.New("incomplete type, value pair")
@@ -154,10 +114,10 @@ func ParseDN(str string) (*DN, error) {
 				rdn = new(RelativeDN)
 				rdn.Attributes = make([]*AttributeTypeAndValue, 0)
 			}
-		} else if char == ' ' && buffer.Len() == 0 {
+		case char == ' ' && buffer.Len() == 0:
 			// ignore unescaped leading spaces
 			continue
-		} else {
+		default:
 			if char == ' ' {
 				// Track unescaped spaces in case they are trailing and we need to remove them
 				unescapedTrailingSpaces++
@@ -245,3 +205,66 @@ func (r *RelativeDN) hasAllAttributes(attrs []*AttributeTypeAndValue) bool {
 func (a *AttributeTypeAndValue) Equal(other *AttributeTypeAndValue) bool {
 	return strings.EqualFold(a.Type, other.Type) && a.Value == other.Value
 }
+
+// Equal returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
+// Returns true if they have the same number of relative distinguished names
+// and corresponding relative distinguished names (by position) are the same.
+// Case of the attribute type and value is not significant
+func (d *DN) EqualFold(other *DN) bool {
+	if len(d.RDNs) != len(other.RDNs) {
+		return false
+	}
+	for i := range d.RDNs {
+		if !d.RDNs[i].EqualFold(other.RDNs[i]) {
+			return false
+		}
+	}
+	return true
+}
+
+// AncestorOfFold returns true if the other DN consists of at least one RDN followed by all the RDNs of the current DN.
+// Case of the attribute type and value is not significant
+func (d *DN) AncestorOfFold(other *DN) bool {
+	if len(d.RDNs) >= len(other.RDNs) {
+		return false
+	}
+	// Take the last `len(d.RDNs)` RDNs from the other DN to compare against
+	otherRDNs := other.RDNs[len(other.RDNs)-len(d.RDNs):]
+	for i := range d.RDNs {
+		if !d.RDNs[i].EqualFold(otherRDNs[i]) {
+			return false
+		}
+	}
+	return true
+}
+
+// Equal returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
+// Case of the attribute type is not significant
+func (r *RelativeDN) EqualFold(other *RelativeDN) bool {
+	if len(r.Attributes) != len(other.Attributes) {
+		return false
+	}
+	return r.hasAllAttributesFold(other.Attributes) && other.hasAllAttributesFold(r.Attributes)
+}
+
+func (r *RelativeDN) hasAllAttributesFold(attrs []*AttributeTypeAndValue) bool {
+	for _, attr := range attrs {
+		found := false
+		for _, myattr := range r.Attributes {
+			if myattr.EqualFold(attr) {
+				found = true
+				break
+			}
+		}
+		if !found {
+			return false
+		}
+	}
+	return true
+}
+
+// EqualFold returns true if the AttributeTypeAndValue is equivalent to the specified AttributeTypeAndValue
+// Case of the attribute type and value is not significant
+func (a *AttributeTypeAndValue) EqualFold(other *AttributeTypeAndValue) bool {
+	return strings.EqualFold(a.Type, other.Type) && strings.EqualFold(a.Value, other.Value)
+}
diff --git a/vendor/gopkg.in/ldap.v2/doc.go b/vendor/github.com/go-ldap/ldap/v3/doc.go
similarity index 100%
rename from vendor/gopkg.in/ldap.v2/doc.go
rename to vendor/github.com/go-ldap/ldap/v3/doc.go
diff --git a/vendor/github.com/go-ldap/ldap/v3/error.go b/vendor/github.com/go-ldap/ldap/v3/error.go
new file mode 100644
index 0000000..3cdb7b3
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/error.go
@@ -0,0 +1,253 @@
+package ldap
+
+import (
+	"fmt"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// LDAP Result Codes
+const (
+	LDAPResultSuccess                            = 0
+	LDAPResultOperationsError                    = 1
+	LDAPResultProtocolError                      = 2
+	LDAPResultTimeLimitExceeded                  = 3
+	LDAPResultSizeLimitExceeded                  = 4
+	LDAPResultCompareFalse                       = 5
+	LDAPResultCompareTrue                        = 6
+	LDAPResultAuthMethodNotSupported             = 7
+	LDAPResultStrongAuthRequired                 = 8
+	LDAPResultReferral                           = 10
+	LDAPResultAdminLimitExceeded                 = 11
+	LDAPResultUnavailableCriticalExtension       = 12
+	LDAPResultConfidentialityRequired            = 13
+	LDAPResultSaslBindInProgress                 = 14
+	LDAPResultNoSuchAttribute                    = 16
+	LDAPResultUndefinedAttributeType             = 17
+	LDAPResultInappropriateMatching              = 18
+	LDAPResultConstraintViolation                = 19
+	LDAPResultAttributeOrValueExists             = 20
+	LDAPResultInvalidAttributeSyntax             = 21
+	LDAPResultNoSuchObject                       = 32
+	LDAPResultAliasProblem                       = 33
+	LDAPResultInvalidDNSyntax                    = 34
+	LDAPResultIsLeaf                             = 35
+	LDAPResultAliasDereferencingProblem          = 36
+	LDAPResultInappropriateAuthentication        = 48
+	LDAPResultInvalidCredentials                 = 49
+	LDAPResultInsufficientAccessRights           = 50
+	LDAPResultBusy                               = 51
+	LDAPResultUnavailable                        = 52
+	LDAPResultUnwillingToPerform                 = 53
+	LDAPResultLoopDetect                         = 54
+	LDAPResultSortControlMissing                 = 60
+	LDAPResultOffsetRangeError                   = 61
+	LDAPResultNamingViolation                    = 64
+	LDAPResultObjectClassViolation               = 65
+	LDAPResultNotAllowedOnNonLeaf                = 66
+	LDAPResultNotAllowedOnRDN                    = 67
+	LDAPResultEntryAlreadyExists                 = 68
+	LDAPResultObjectClassModsProhibited          = 69
+	LDAPResultResultsTooLarge                    = 70
+	LDAPResultAffectsMultipleDSAs                = 71
+	LDAPResultVirtualListViewErrorOrControlError = 76
+	LDAPResultOther                              = 80
+	LDAPResultServerDown                         = 81
+	LDAPResultLocalError                         = 82
+	LDAPResultEncodingError                      = 83
+	LDAPResultDecodingError                      = 84
+	LDAPResultTimeout                            = 85
+	LDAPResultAuthUnknown                        = 86
+	LDAPResultFilterError                        = 87
+	LDAPResultUserCanceled                       = 88
+	LDAPResultParamError                         = 89
+	LDAPResultNoMemory                           = 90
+	LDAPResultConnectError                       = 91
+	LDAPResultNotSupported                       = 92
+	LDAPResultControlNotFound                    = 93
+	LDAPResultNoResultsReturned                  = 94
+	LDAPResultMoreResultsToReturn                = 95
+	LDAPResultClientLoop                         = 96
+	LDAPResultReferralLimitExceeded              = 97
+	LDAPResultInvalidResponse                    = 100
+	LDAPResultAmbiguousResponse                  = 101
+	LDAPResultTLSNotSupported                    = 112
+	LDAPResultIntermediateResponse               = 113
+	LDAPResultUnknownType                        = 114
+	LDAPResultCanceled                           = 118
+	LDAPResultNoSuchOperation                    = 119
+	LDAPResultTooLate                            = 120
+	LDAPResultCannotCancel                       = 121
+	LDAPResultAssertionFailed                    = 122
+	LDAPResultAuthorizationDenied                = 123
+	LDAPResultSyncRefreshRequired                = 4096
+
+	ErrorNetwork            = 200
+	ErrorFilterCompile      = 201
+	ErrorFilterDecompile    = 202
+	ErrorDebugging          = 203
+	ErrorUnexpectedMessage  = 204
+	ErrorUnexpectedResponse = 205
+	ErrorEmptyPassword      = 206
+)
+
+// LDAPResultCodeMap contains string descriptions for LDAP error codes
+var LDAPResultCodeMap = map[uint16]string{
+	LDAPResultSuccess:                            "Success",
+	LDAPResultOperationsError:                    "Operations Error",
+	LDAPResultProtocolError:                      "Protocol Error",
+	LDAPResultTimeLimitExceeded:                  "Time Limit Exceeded",
+	LDAPResultSizeLimitExceeded:                  "Size Limit Exceeded",
+	LDAPResultCompareFalse:                       "Compare False",
+	LDAPResultCompareTrue:                        "Compare True",
+	LDAPResultAuthMethodNotSupported:             "Auth Method Not Supported",
+	LDAPResultStrongAuthRequired:                 "Strong Auth Required",
+	LDAPResultReferral:                           "Referral",
+	LDAPResultAdminLimitExceeded:                 "Admin Limit Exceeded",
+	LDAPResultUnavailableCriticalExtension:       "Unavailable Critical Extension",
+	LDAPResultConfidentialityRequired:            "Confidentiality Required",
+	LDAPResultSaslBindInProgress:                 "Sasl Bind In Progress",
+	LDAPResultNoSuchAttribute:                    "No Such Attribute",
+	LDAPResultUndefinedAttributeType:             "Undefined Attribute Type",
+	LDAPResultInappropriateMatching:              "Inappropriate Matching",
+	LDAPResultConstraintViolation:                "Constraint Violation",
+	LDAPResultAttributeOrValueExists:             "Attribute Or Value Exists",
+	LDAPResultInvalidAttributeSyntax:             "Invalid Attribute Syntax",
+	LDAPResultNoSuchObject:                       "No Such Object",
+	LDAPResultAliasProblem:                       "Alias Problem",
+	LDAPResultInvalidDNSyntax:                    "Invalid DN Syntax",
+	LDAPResultIsLeaf:                             "Is Leaf",
+	LDAPResultAliasDereferencingProblem:          "Alias Dereferencing Problem",
+	LDAPResultInappropriateAuthentication:        "Inappropriate Authentication",
+	LDAPResultInvalidCredentials:                 "Invalid Credentials",
+	LDAPResultInsufficientAccessRights:           "Insufficient Access Rights",
+	LDAPResultBusy:                               "Busy",
+	LDAPResultUnavailable:                        "Unavailable",
+	LDAPResultUnwillingToPerform:                 "Unwilling To Perform",
+	LDAPResultLoopDetect:                         "Loop Detect",
+	LDAPResultSortControlMissing:                 "Sort Control Missing",
+	LDAPResultOffsetRangeError:                   "Result Offset Range Error",
+	LDAPResultNamingViolation:                    "Naming Violation",
+	LDAPResultObjectClassViolation:               "Object Class Violation",
+	LDAPResultResultsTooLarge:                    "Results Too Large",
+	LDAPResultNotAllowedOnNonLeaf:                "Not Allowed On Non Leaf",
+	LDAPResultNotAllowedOnRDN:                    "Not Allowed On RDN",
+	LDAPResultEntryAlreadyExists:                 "Entry Already Exists",
+	LDAPResultObjectClassModsProhibited:          "Object Class Mods Prohibited",
+	LDAPResultAffectsMultipleDSAs:                "Affects Multiple DSAs",
+	LDAPResultVirtualListViewErrorOrControlError: "Failed because of a problem related to the virtual list view",
+	LDAPResultOther:                              "Other",
+	LDAPResultServerDown:                         "Cannot establish a connection",
+	LDAPResultLocalError:                         "An error occurred",
+	LDAPResultEncodingError:                      "LDAP encountered an error while encoding",
+	LDAPResultDecodingError:                      "LDAP encountered an error while decoding",
+	LDAPResultTimeout:                            "LDAP timeout while waiting for a response from the server",
+	LDAPResultAuthUnknown:                        "The auth method requested in a bind request is unknown",
+	LDAPResultFilterError:                        "An error occurred while encoding the given search filter",
+	LDAPResultUserCanceled:                       "The user canceled the operation",
+	LDAPResultParamError:                         "An invalid parameter was specified",
+	LDAPResultNoMemory:                           "Out of memory error",
+	LDAPResultConnectError:                       "A connection to the server could not be established",
+	LDAPResultNotSupported:                       "An attempt has been made to use a feature not supported LDAP",
+	LDAPResultControlNotFound:                    "The controls required to perform the requested operation were not found",
+	LDAPResultNoResultsReturned:                  "No results were returned from the server",
+	LDAPResultMoreResultsToReturn:                "There are more results in the chain of results",
+	LDAPResultClientLoop:                         "A loop has been detected. For example when following referrals",
+	LDAPResultReferralLimitExceeded:              "The referral hop limit has been exceeded",
+	LDAPResultCanceled:                           "Operation was canceled",
+	LDAPResultNoSuchOperation:                    "Server has no knowledge of the operation requested for cancellation",
+	LDAPResultTooLate:                            "Too late to cancel the outstanding operation",
+	LDAPResultCannotCancel:                       "The identified operation does not support cancellation or the cancel operation cannot be performed",
+	LDAPResultAssertionFailed:                    "An assertion control given in the LDAP operation evaluated to false causing the operation to not be performed",
+	LDAPResultSyncRefreshRequired:                "Refresh Required",
+	LDAPResultInvalidResponse:                    "Invalid Response",
+	LDAPResultAmbiguousResponse:                  "Ambiguous Response",
+	LDAPResultTLSNotSupported:                    "Tls Not Supported",
+	LDAPResultIntermediateResponse:               "Intermediate Response",
+	LDAPResultUnknownType:                        "Unknown Type",
+	LDAPResultAuthorizationDenied:                "Authorization Denied",
+
+	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",
+}
+
+// Error holds LDAP error information
+type Error struct {
+	// Err is the underlying error
+	Err error
+	// ResultCode is the LDAP error code
+	ResultCode uint16
+	// MatchedDN is the matchedDN returned if any
+	MatchedDN string
+	// Packet is the returned packet if any
+	Packet *ber.Packet
+}
+
+func (e *Error) Error() string {
+	return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
+}
+
+// GetLDAPError creates an Error out of a BER packet representing a LDAPResult
+// The return is an error object. It can be casted to a Error structure.
+// This function returns nil if resultCode in the LDAPResult sequence is success(0).
+func GetLDAPError(packet *ber.Packet) error {
+	if packet == nil {
+		return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty packet")}
+	}
+
+	if len(packet.Children) >= 2 {
+		response := packet.Children[1]
+		if response == nil {
+			return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty response in packet"), Packet: packet}
+		}
+		if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
+			resultCode := uint16(response.Children[0].Value.(int64))
+			if resultCode == 0 { // No error
+				return nil
+			}
+			return &Error{
+				ResultCode: resultCode,
+				MatchedDN:  response.Children[1].Value.(string),
+				Err:        fmt.Errorf("%s", response.Children[2].Value.(string)),
+				Packet:     packet,
+			}
+		}
+	}
+
+	return &Error{ResultCode: ErrorNetwork, Err: fmt.Errorf("Invalid packet format"), Packet: packet}
+}
+
+// NewError creates an LDAP error with the given code and underlying error
+func NewError(resultCode uint16, err error) error {
+	return &Error{ResultCode: resultCode, Err: err}
+}
+
+// IsErrorAnyOf returns true if the given error is an LDAP error with any one of the given result codes
+func IsErrorAnyOf(err error, codes ...uint16) bool {
+	if err == nil {
+		return false
+	}
+
+	serverError, ok := err.(*Error)
+	if !ok {
+		return false
+	}
+
+	for _, code := range codes {
+		if serverError.ResultCode == code {
+			return true
+		}
+	}
+
+	return false
+}
+
+// IsErrorWithCode returns true if the given error is an LDAP error with the given result code
+func IsErrorWithCode(err error, desiredResultCode uint16) bool {
+	return IsErrorAnyOf(err, desiredResultCode)
+}
diff --git a/vendor/gopkg.in/ldap.v2/filter.go b/vendor/github.com/go-ldap/ldap/v3/filter.go
similarity index 75%
rename from vendor/gopkg.in/ldap.v2/filter.go
rename to vendor/github.com/go-ldap/ldap/v3/filter.go
index 3858a28..73505e7 100644
--- a/vendor/gopkg.in/ldap.v2/filter.go
+++ b/vendor/github.com/go-ldap/ldap/v3/filter.go
@@ -1,7 +1,3 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package ldap
 
 import (
@@ -9,10 +5,12 @@ import (
 	hexpac "encoding/hex"
 	"errors"
 	"fmt"
+	"io"
 	"strings"
+	"unicode"
 	"unicode/utf8"
 
-	"gopkg.in/asn1-ber.v1"
+	ber "github.com/go-asn1-ber/asn1-ber"
 )
 
 // Filter choices
@@ -73,6 +71,8 @@ var MatchingRuleAssertionMap = map[uint64]string{
 	MatchingRuleAssertionDNAttributes: "Matching Rule Assertion DN Attributes",
 }
 
+var _SymbolAny = []byte{'*'}
+
 // CompileFilter converts a string representation of a filter into a BER-encoded packet
 func CompileFilter(filter string) (*ber.Packet, error) {
 	if len(filter) == 0 || filter[0] != '(' {
@@ -92,74 +92,75 @@ func CompileFilter(filter string) (*ber.Packet, error) {
 }
 
 // DecompileFilter converts a packet representation of a filter into a string representation
-func DecompileFilter(packet *ber.Packet) (ret string, err error) {
+func DecompileFilter(packet *ber.Packet) (_ string, err error) {
 	defer func() {
 		if r := recover(); r != nil {
 			err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter"))
 		}
 	}()
-	ret = "("
-	err = nil
+
+	buf := bytes.NewBuffer(nil)
+	buf.WriteByte('(')
 	childStr := ""
 
 	switch packet.Tag {
 	case FilterAnd:
-		ret += "&"
+		buf.WriteByte('&')
 		for _, child := range packet.Children {
 			childStr, err = DecompileFilter(child)
 			if err != nil {
 				return
 			}
-			ret += childStr
+			buf.WriteString(childStr)
 		}
 	case FilterOr:
-		ret += "|"
+		buf.WriteByte('|')
 		for _, child := range packet.Children {
 			childStr, err = DecompileFilter(child)
 			if err != nil {
 				return
 			}
-			ret += childStr
+			buf.WriteString(childStr)
 		}
 	case FilterNot:
-		ret += "!"
+		buf.WriteByte('!')
 		childStr, err = DecompileFilter(packet.Children[0])
 		if err != nil {
 			return
 		}
-		ret += childStr
+		buf.WriteString(childStr)
 
 	case FilterSubstrings:
-		ret += ber.DecodeString(packet.Children[0].Data.Bytes())
-		ret += "="
+		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
+		buf.WriteByte('=')
 		for i, child := range packet.Children[1].Children {
 			if i == 0 && child.Tag != FilterSubstringsInitial {
-				ret += "*"
+				buf.Write(_SymbolAny)
 			}
-			ret += EscapeFilter(ber.DecodeString(child.Data.Bytes()))
+			buf.WriteString(EscapeFilter(ber.DecodeString(child.Data.Bytes())))
 			if child.Tag != FilterSubstringsFinal {
-				ret += "*"
+				buf.Write(_SymbolAny)
 			}
 		}
 	case FilterEqualityMatch:
-		ret += ber.DecodeString(packet.Children[0].Data.Bytes())
-		ret += "="
-		ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
+		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
+		buf.WriteByte('=')
+		buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
 	case FilterGreaterOrEqual:
-		ret += ber.DecodeString(packet.Children[0].Data.Bytes())
-		ret += ">="
-		ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
+		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
+		buf.WriteString(">=")
+		buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
 	case FilterLessOrEqual:
-		ret += ber.DecodeString(packet.Children[0].Data.Bytes())
-		ret += "<="
-		ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
+		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
+		buf.WriteString("<=")
+		buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
 	case FilterPresent:
-		ret += ber.DecodeString(packet.Data.Bytes())
-		ret += "=*"
+		buf.WriteString(ber.DecodeString(packet.Data.Bytes()))
+		buf.WriteString("=*")
 	case FilterApproxMatch:
-		ret += ber.DecodeString(packet.Children[0].Data.Bytes())
-		ret += "~="
-		ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
+		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
+		buf.WriteString("~=")
+		buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
 	case FilterExtensibleMatch:
 		attr := ""
 		dnAttributes := false
@@ -180,21 +181,22 @@ func DecompileFilter(packet *ber.Packet) (ret string, err error) {
 		}
 
 		if len(attr) > 0 {
-			ret += attr
+			buf.WriteString(attr)
 		}
 		if dnAttributes {
-			ret += ":dn"
+			buf.WriteString(":dn")
 		}
 		if len(matchingRule) > 0 {
-			ret += ":"
-			ret += matchingRule
+			buf.WriteString(":")
+			buf.WriteString(matchingRule)
 		}
-		ret += ":="
-		ret += EscapeFilter(value)
+		buf.WriteString(":=")
+		buf.WriteString(EscapeFilter(value))
 	}
 
-	ret += ")"
-	return
+	buf.WriteByte(')')
+
+	return buf.String(), nil
 }
 
 func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) {
@@ -257,11 +259,10 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
 		)
 
 		state := stateReadingAttr
-
-		attribute := ""
+		attribute := bytes.NewBuffer(nil)
 		extensibleDNAttributes := false
-		extensibleMatchingRule := ""
-		condition := ""
+		extensibleMatchingRule := bytes.NewBuffer(nil)
+		condition := bytes.NewBuffer(nil)
 
 		for newPos < len(filter) {
 			remainingFilter := filter[newPos:]
@@ -328,7 +329,7 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
 
 				// Still reading the attribute name
 				default:
-					attribute += fmt.Sprintf("%c", currentRune)
+					attribute.WriteRune(currentRune)
 					newPos += currentWidth
 				}
 
@@ -342,13 +343,13 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
 
 				// Still reading the matching rule oid
 				default:
-					extensibleMatchingRule += fmt.Sprintf("%c", currentRune)
+					extensibleMatchingRule.WriteRune(currentRune)
 					newPos += currentWidth
 				}
 
 			case stateReadingCondition:
 				// append to the condition
-				condition += fmt.Sprintf("%c", currentRune)
+				condition.WriteRune(currentRune)
 				newPos += currentWidth
 			}
 		}
@@ -372,17 +373,17 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
 			// }
 
 			// Include the matching rule oid, if specified
-			if len(extensibleMatchingRule) > 0 {
-				packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule, MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule]))
+			if extensibleMatchingRule.Len() > 0 {
+				packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule.String(), MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule]))
 			}
 
 			// Include the attribute, if specified
-			if len(attribute) > 0 {
-				packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute, MatchingRuleAssertionMap[MatchingRuleAssertionType]))
+			if attribute.Len() > 0 {
+				packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute.String(), MatchingRuleAssertionMap[MatchingRuleAssertionType]))
 			}
 
 			// Add the value (only required child)
-			encodedString, encodeErr := escapedStringToEncodedBytes(condition)
+			encodedString, encodeErr := decodeEscapedSymbols(condition.Bytes())
 			if encodeErr != nil {
 				return packet, newPos, encodeErr
 			}
@@ -393,16 +394,16 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
 				packet.AppendChild(ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionDNAttributes, extensibleDNAttributes, MatchingRuleAssertionMap[MatchingRuleAssertionDNAttributes]))
 			}
 
-		case packet.Tag == FilterEqualityMatch && condition == "*":
-			packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute, FilterMap[FilterPresent])
-		case packet.Tag == FilterEqualityMatch && strings.Contains(condition, "*"):
-			packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
+		case packet.Tag == FilterEqualityMatch && bytes.Equal(condition.Bytes(), _SymbolAny):
+			packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute.String(), FilterMap[FilterPresent])
+		case packet.Tag == FilterEqualityMatch && bytes.Index(condition.Bytes(), _SymbolAny) > -1:
+			packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute.String(), "Attribute"))
 			packet.Tag = FilterSubstrings
 			packet.Description = FilterMap[uint64(packet.Tag)]
 			seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
-			parts := strings.Split(condition, "*")
+			parts := bytes.Split(condition.Bytes(), _SymbolAny)
 			for i, part := range parts {
-				if part == "" {
+				if len(part) == 0 {
 					continue
 				}
 				var tag ber.Tag
@@ -414,7 +415,7 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
 				default:
 					tag = FilterSubstringsAny
 				}
-				encodedString, encodeErr := escapedStringToEncodedBytes(part)
+				encodedString, encodeErr := decodeEscapedSymbols(part)
 				if encodeErr != nil {
 					return packet, newPos, encodeErr
 				}
@@ -422,11 +423,11 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
 			}
 			packet.AppendChild(seq)
 		default:
-			encodedString, encodeErr := escapedStringToEncodedBytes(condition)
+			encodedString, encodeErr := decodeEscapedSymbols(condition.Bytes())
 			if encodeErr != nil {
 				return packet, newPos, encodeErr
 			}
-			packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
+			packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute.String(), "Attribute"))
 			packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, encodedString, "Condition"))
 		}
 
@@ -436,34 +437,51 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
 }
 
 // Convert from "ABC\xx\xx\xx" form to literal bytes for transport
-func escapedStringToEncodedBytes(escapedString string) (string, error) {
-	var buffer bytes.Buffer
-	i := 0
-	for i < len(escapedString) {
-		currentRune, currentWidth := utf8.DecodeRuneInString(escapedString[i:])
-		if currentRune == utf8.RuneError {
-			return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", i))
+func decodeEscapedSymbols(src []byte) (string, error) {
+
+	var (
+		buffer  bytes.Buffer
+		offset  int
+		reader  = bytes.NewReader(src)
+		byteHex []byte
+		byteVal []byte
+	)
+
+	for {
+		runeVal, runeSize, err := reader.ReadRune()
+		if err == io.EOF {
+			return buffer.String(), nil
+		} else if err != nil {
+			return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: failed to read filter: %v", err))
+		} else if runeVal == unicode.ReplacementChar {
+			return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", offset))
 		}
 
-		// Check for escaped hex characters and convert them to their literal value for transport.
-		if currentRune == '\\' {
+		if runeVal == '\\' {
 			// http://tools.ietf.org/search/rfc4515
 			// \ (%x5C) is not a valid character unless it is followed by two HEX characters due to not
 			// being a member of UTF1SUBSET.
-			if i+2 > len(escapedString) {
-				return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter"))
+			if byteHex == nil {
+				byteHex = make([]byte, 2)
+				byteVal = make([]byte, 1)
 			}
-			escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3])
-			if decodeErr != nil {
-				return "", NewError(ErrorFilterCompile, errors.New("ldap: invalid characters for escape in filter"))
+
+			if _, err := io.ReadFull(reader, byteHex); err != nil {
+				if err == io.ErrUnexpectedEOF {
+					return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter"))
+				}
+				return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: invalid characters for escape in filter: %v", err))
 			}
-			buffer.WriteByte(escByte[0])
-			i += 2 // +1 from end of loop, so 3 total for \xx.
+
+			if _, err := hexpac.Decode(byteVal, byteHex); err != nil {
+				return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: invalid characters for escape in filter: %v", err))
+			}
+
+			buffer.Write(byteVal)
 		} else {
-			buffer.WriteRune(currentRune)
+			buffer.WriteRune(runeVal)
 		}
 
-		i += currentWidth
+		offset += runeSize
 	}
-	return buffer.String(), nil
 }
diff --git a/vendor/gopkg.in/ldap.v2/ldap.go b/vendor/github.com/go-ldap/ldap/v3/ldap.go
similarity index 76%
rename from vendor/gopkg.in/ldap.v2/ldap.go
rename to vendor/github.com/go-ldap/ldap/v3/ldap.go
index 4969247..7ae6dfe 100644
--- a/vendor/gopkg.in/ldap.v2/ldap.go
+++ b/vendor/github.com/go-ldap/ldap/v3/ldap.go
@@ -1,15 +1,11 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package ldap
 
 import (
-	"errors"
+	"fmt"
 	"io/ioutil"
 	"os"
 
-	"gopkg.in/asn1-ber.v1"
+	ber "github.com/go-asn1-ber/asn1-ber"
 )
 
 // LDAP Application Codes
@@ -90,7 +86,7 @@ var BeheraPasswordPolicyErrorMap = map[int8]string{
 func addLDAPDescriptions(packet *ber.Packet) (err error) {
 	defer func() {
 		if r := recover(); r != nil {
-			err = NewError(ErrorDebugging, errors.New("ldap: cannot process packet to add descriptions"))
+			err = NewError(ErrorDebugging, fmt.Errorf("ldap: cannot process packet to add descriptions: %s", r))
 		}
 	}()
 	packet.Description = "LDAP Response"
@@ -101,13 +97,13 @@ func addLDAPDescriptions(packet *ber.Packet) (err error) {
 
 	switch application {
 	case ApplicationBindRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationBindResponse:
-		addDefaultLDAPResponseDescriptions(packet)
+		err = addDefaultLDAPResponseDescriptions(packet)
 	case ApplicationUnbindRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationSearchRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationSearchResultEntry:
 		packet.Children[1].Children[0].Description = "Object Name"
 		packet.Children[1].Children[1].Description = "Attributes"
@@ -120,37 +116,37 @@ func addLDAPDescriptions(packet *ber.Packet) (err error) {
 			}
 		}
 		if len(packet.Children) == 3 {
-			addControlDescriptions(packet.Children[2])
+			err = addControlDescriptions(packet.Children[2])
 		}
 	case ApplicationSearchResultDone:
-		addDefaultLDAPResponseDescriptions(packet)
+		err = addDefaultLDAPResponseDescriptions(packet)
 	case ApplicationModifyRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationModifyResponse:
 	case ApplicationAddRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationAddResponse:
 	case ApplicationDelRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationDelResponse:
 	case ApplicationModifyDNRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationModifyDNResponse:
 	case ApplicationCompareRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationCompareResponse:
 	case ApplicationAbandonRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationSearchResultReference:
 	case ApplicationExtendedRequest:
-		addRequestDescriptions(packet)
+		err = addRequestDescriptions(packet)
 	case ApplicationExtendedResponse:
 	}
 
-	return nil
+	return err
 }
 
-func addControlDescriptions(packet *ber.Packet) {
+func addControlDescriptions(packet *ber.Packet) error {
 	packet.Description = "Controls"
 	for _, child := range packet.Children {
 		var value *ber.Packet
@@ -159,7 +155,7 @@ func addControlDescriptions(packet *ber.Packet) {
 		switch len(child.Children) {
 		case 0:
 			// at least one child is required for control type
-			continue
+			return fmt.Errorf("at least one child is required for control type")
 
 		case 1:
 			// just type, no criticality or value
@@ -188,8 +184,9 @@ func addControlDescriptions(packet *ber.Packet) {
 
 		default:
 			// more than 3 children is invalid
-			continue
+			return fmt.Errorf("more than 3 children for control packet found")
 		}
+
 		if value == nil {
 			continue
 		}
@@ -197,7 +194,10 @@ func addControlDescriptions(packet *ber.Packet) {
 		case ControlTypePaging:
 			value.Description += " (Paging)"
 			if value.Value != nil {
-				valueChildren := ber.DecodePacket(value.Data.Bytes())
+				valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+				if err != nil {
+					return fmt.Errorf("failed to decode data bytes: %s", err)
+				}
 				value.Data.Truncate(0)
 				value.Value = nil
 				valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes()
@@ -210,7 +210,10 @@ func addControlDescriptions(packet *ber.Packet) {
 		case ControlTypeBeheraPasswordPolicy:
 			value.Description += " (Password Policy - Behera Draft)"
 			if value.Value != nil {
-				valueChildren := ber.DecodePacket(value.Data.Bytes())
+				valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+				if err != nil {
+					return fmt.Errorf("failed to decode data bytes: %s", err)
+				}
 				value.Data.Truncate(0)
 				value.Value = nil
 				value.AppendChild(valueChildren)
@@ -220,54 +223,65 @@ func addControlDescriptions(packet *ber.Packet) {
 				if child.Tag == 0 {
 					//Warning
 					warningPacket := child.Children[0]
-					packet := ber.DecodePacket(warningPacket.Data.Bytes())
-					val, ok := packet.Value.(int64)
-					if ok {
-						if warningPacket.Tag == 0 {
-							//timeBeforeExpiration
-							value.Description += " (TimeBeforeExpiration)"
-							warningPacket.Value = val
-						} else if warningPacket.Tag == 1 {
-							//graceAuthNsRemaining
-							value.Description += " (GraceAuthNsRemaining)"
-							warningPacket.Value = val
-						}
+					val, err := ber.ParseInt64(warningPacket.Data.Bytes())
+					if err != nil {
+						return fmt.Errorf("failed to decode data bytes: %s", err)
+					}
+					if warningPacket.Tag == 0 {
+						//timeBeforeExpiration
+						value.Description += " (TimeBeforeExpiration)"
+						warningPacket.Value = val
+					} else if warningPacket.Tag == 1 {
+						//graceAuthNsRemaining
+						value.Description += " (GraceAuthNsRemaining)"
+						warningPacket.Value = val
 					}
 				} else if child.Tag == 1 {
 					// Error
-					packet := ber.DecodePacket(child.Data.Bytes())
-					val, ok := packet.Value.(int8)
-					if !ok {
-						val = -1
+					bs := child.Data.Bytes()
+					if len(bs) != 1 || bs[0] > 8 {
+						return fmt.Errorf("failed to decode data bytes: %s", "invalid PasswordPolicyResponse enum value")
 					}
+					val := int8(bs[0])
 					child.Description = "Error"
 					child.Value = val
 				}
 			}
 		}
 	}
+	return nil
 }
 
-func addRequestDescriptions(packet *ber.Packet) {
+func addRequestDescriptions(packet *ber.Packet) error {
 	packet.Description = "LDAP Request"
 	packet.Children[0].Description = "Message ID"
 	packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)]
 	if len(packet.Children) == 3 {
-		addControlDescriptions(packet.Children[2])
+		return addControlDescriptions(packet.Children[2])
 	}
+	return nil
 }
 
-func addDefaultLDAPResponseDescriptions(packet *ber.Packet) {
-	resultCode, _ := getLDAPResultCode(packet)
+func addDefaultLDAPResponseDescriptions(packet *ber.Packet) error {
+	resultCode := uint16(LDAPResultSuccess)
+	matchedDN := ""
+	description := "Success"
+	if err := GetLDAPError(packet); err != nil {
+		resultCode = err.(*Error).ResultCode
+		matchedDN = err.(*Error).MatchedDN
+		description = "Error Message"
+	}
+
 	packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")"
-	packet.Children[1].Children[1].Description = "Matched DN"
-	packet.Children[1].Children[2].Description = "Error Message"
+	packet.Children[1].Children[1].Description = "Matched DN (" + matchedDN + ")"
+	packet.Children[1].Children[2].Description = description
 	if len(packet.Children[1].Children) > 3 {
 		packet.Children[1].Children[3].Description = "Referral"
 	}
 	if len(packet.Children) == 3 {
-		addControlDescriptions(packet.Children[2])
+		return addControlDescriptions(packet.Children[2])
 	}
+	return nil
 }
 
 // DebugBinaryFile reads and prints packets from the given filename
@@ -277,8 +291,13 @@ func DebugBinaryFile(fileName string) error {
 		return NewError(ErrorDebugging, err)
 	}
 	ber.PrintBytes(os.Stdout, file, "")
-	packet := ber.DecodePacket(file)
-	addLDAPDescriptions(packet)
+	packet, err := ber.DecodePacketErr(file)
+	if err != nil {
+		return fmt.Errorf("failed to decode packet: %s", err)
+	}
+	if err := addLDAPDescriptions(packet); err != nil {
+		return err
+	}
 	ber.PrintPacket(packet)
 
 	return nil
diff --git a/vendor/github.com/go-ldap/ldap/v3/moddn.go b/vendor/github.com/go-ldap/ldap/v3/moddn.go
new file mode 100644
index 0000000..71cdcd0
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/moddn.go
@@ -0,0 +1,100 @@
+package ldap
+
+import (
+	"log"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// ModifyDNRequest holds the request to modify a DN
+type ModifyDNRequest struct {
+	DN           string
+	NewRDN       string
+	DeleteOldRDN bool
+	NewSuperior  string
+	// Controls hold optional controls to send with the request
+	Controls []Control
+}
+
+// NewModifyDNRequest creates a new request which can be passed to ModifyDN().
+//
+// To move an object in the tree, set the "newSup" to the new parent entry DN. Use an
+// empty string for just changing the object's RDN.
+//
+// For moving the object without renaming, the "rdn" must be the first
+// RDN of the given DN.
+//
+// A call like
+//   mdnReq := NewModifyDNRequest("uid=someone,dc=example,dc=org", "uid=newname", true, "")
+// will setup the request to just rename uid=someone,dc=example,dc=org to
+// uid=newname,dc=example,dc=org.
+func NewModifyDNRequest(dn string, rdn string, delOld bool, newSup string) *ModifyDNRequest {
+	return &ModifyDNRequest{
+		DN:           dn,
+		NewRDN:       rdn,
+		DeleteOldRDN: delOld,
+		NewSuperior:  newSup,
+	}
+}
+
+// NewModifyDNWithControlsRequest creates a new request which can be passed to ModifyDN()
+// and also allows setting LDAP request controls.
+//
+// Refer NewModifyDNRequest for other parameters
+func NewModifyDNWithControlsRequest(dn string, rdn string, delOld bool,
+	newSup string, controls []Control) *ModifyDNRequest {
+	return &ModifyDNRequest{
+		DN:           dn,
+		NewRDN:       rdn,
+		DeleteOldRDN: delOld,
+		NewSuperior:  newSup,
+		Controls:     controls,
+	}
+}
+
+func (req *ModifyDNRequest) appendTo(envelope *ber.Packet) error {
+	pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyDNRequest, nil, "Modify DN Request")
+	pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN"))
+	pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.NewRDN, "New RDN"))
+	if req.DeleteOldRDN {
+		buf := []byte{0xff}
+		pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, string(buf), "Delete old RDN"))
+	} else {
+		pkt.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, req.DeleteOldRDN, "Delete old RDN"))
+	}
+	if req.NewSuperior != "" {
+		pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.NewSuperior, "New Superior"))
+	}
+
+	envelope.AppendChild(pkt)
+	if len(req.Controls) > 0 {
+		envelope.AppendChild(encodeControls(req.Controls))
+	}
+
+	return nil
+}
+
+// ModifyDN renames the given DN and optionally move to another base (when the "newSup" argument
+// to NewModifyDNRequest() is not "").
+func (l *Conn) ModifyDN(m *ModifyDNRequest) error {
+	msgCtx, err := l.doRequest(m)
+	if err != nil {
+		return err
+	}
+	defer l.finishMessage(msgCtx)
+
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return err
+	}
+
+	if packet.Children[1].Tag == ApplicationModifyDNResponse {
+		err := GetLDAPError(packet)
+		if err != nil {
+			return err
+		}
+	} else {
+		log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
+	}
+	return nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/modify.go b/vendor/github.com/go-ldap/ldap/v3/modify.go
new file mode 100644
index 0000000..1821413
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/modify.go
@@ -0,0 +1,177 @@
+package ldap
+
+import (
+	"errors"
+	"log"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// Change operation choices
+const (
+	AddAttribute       = 0
+	DeleteAttribute    = 1
+	ReplaceAttribute   = 2
+	IncrementAttribute = 3 // (https://tools.ietf.org/html/rfc4525)
+)
+
+// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
+type PartialAttribute struct {
+	// Type is the type of the partial attribute
+	Type string
+	// Vals are the values of the partial attribute
+	Vals []string
+}
+
+func (p *PartialAttribute) encode() *ber.Packet {
+	seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute")
+	seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type"))
+	set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
+	for _, value := range p.Vals {
+		set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
+	}
+	seq.AppendChild(set)
+	return seq
+}
+
+// Change for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
+type Change struct {
+	// Operation is the type of change to be made
+	Operation uint
+	// Modification is the attribute to be modified
+	Modification PartialAttribute
+}
+
+func (c *Change) encode() *ber.Packet {
+	change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
+	change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(c.Operation), "Operation"))
+	change.AppendChild(c.Modification.encode())
+	return change
+}
+
+// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
+type ModifyRequest struct {
+	// DN is the distinguishedName of the directory entry to modify
+	DN string
+	// Changes contain the attributes to modify
+	Changes []Change
+	// Controls hold optional controls to send with the request
+	Controls []Control
+}
+
+// Add appends the given attribute to the list of changes to be made
+func (req *ModifyRequest) Add(attrType string, attrVals []string) {
+	req.appendChange(AddAttribute, attrType, attrVals)
+}
+
+// Delete appends the given attribute to the list of changes to be made
+func (req *ModifyRequest) Delete(attrType string, attrVals []string) {
+	req.appendChange(DeleteAttribute, attrType, attrVals)
+}
+
+// Replace appends the given attribute to the list of changes to be made
+func (req *ModifyRequest) Replace(attrType string, attrVals []string) {
+	req.appendChange(ReplaceAttribute, attrType, attrVals)
+}
+
+// Increment appends the given attribute to the list of changes to be made
+func (req *ModifyRequest) Increment(attrType string, attrVal string) {
+	req.appendChange(IncrementAttribute, attrType, []string{attrVal})
+}
+
+func (req *ModifyRequest) appendChange(operation uint, attrType string, attrVals []string) {
+	req.Changes = append(req.Changes, Change{operation, PartialAttribute{Type: attrType, Vals: attrVals}})
+}
+
+func (req *ModifyRequest) appendTo(envelope *ber.Packet) error {
+	pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request")
+	pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN"))
+	changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes")
+	for _, change := range req.Changes {
+		changes.AppendChild(change.encode())
+	}
+	pkt.AppendChild(changes)
+
+	envelope.AppendChild(pkt)
+	if len(req.Controls) > 0 {
+		envelope.AppendChild(encodeControls(req.Controls))
+	}
+
+	return nil
+}
+
+// NewModifyRequest creates a modify request for the given DN
+func NewModifyRequest(dn string, controls []Control) *ModifyRequest {
+	return &ModifyRequest{
+		DN:       dn,
+		Controls: controls,
+	}
+}
+
+// Modify performs the ModifyRequest
+func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
+	msgCtx, err := l.doRequest(modifyRequest)
+	if err != nil {
+		return err
+	}
+	defer l.finishMessage(msgCtx)
+
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return err
+	}
+
+	if packet.Children[1].Tag == ApplicationModifyResponse {
+		err := GetLDAPError(packet)
+		if err != nil {
+			return err
+		}
+	} else {
+		log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
+	}
+	return nil
+}
+
+// ModifyResult holds the server's response to a modify request
+type ModifyResult struct {
+	// Controls are the returned controls
+	Controls []Control
+}
+
+// ModifyWithResult performs the ModifyRequest and returns the result
+func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, error) {
+	msgCtx, err := l.doRequest(modifyRequest)
+	if err != nil {
+		return nil, err
+	}
+	defer l.finishMessage(msgCtx)
+
+	result := &ModifyResult{
+		Controls: make([]Control, 0),
+	}
+
+	l.Debug.Printf("%d: waiting for response", msgCtx.id)
+	packet, err := l.readPacket(msgCtx)
+	if err != nil {
+		return nil, err
+	}
+
+	switch packet.Children[1].Tag {
+	case ApplicationModifyResponse:
+		err := GetLDAPError(packet)
+		if err != nil {
+			return nil, err
+		}
+		if len(packet.Children) == 3 {
+			for _, child := range packet.Children[2].Children {
+				decodedChild, err := DecodeControl(child)
+				if err != nil {
+					return nil, errors.New("failed to decode child control: " + err.Error())
+				}
+				result.Controls = append(result.Controls, decodedChild)
+			}
+		}
+	}
+	l.Debug.Printf("%d: returning", msgCtx.id)
+	return result, nil
+}
diff --git a/vendor/gopkg.in/ldap.v2/passwdmodify.go b/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
similarity index 62%
rename from vendor/gopkg.in/ldap.v2/passwdmodify.go
rename to vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
index 7d8246f..62a1108 100644
--- a/vendor/gopkg.in/ldap.v2/passwdmodify.go
+++ b/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
@@ -1,15 +1,9 @@
-// This file contains the password modify extended operation as specified in rfc 3062
-//
-// https://tools.ietf.org/html/rfc3062
-//
-
 package ldap
 
 import (
-	"errors"
 	"fmt"
 
-	"gopkg.in/asn1-ber.v1"
+	ber "github.com/go-asn1-ber/asn1-ber"
 )
 
 const (
@@ -32,32 +26,37 @@ type PasswordModifyRequest struct {
 type PasswordModifyResult struct {
 	// GeneratedPassword holds a password generated by the server, if present
 	GeneratedPassword string
+	// Referral are the returned referral
+	Referral string
 }
 
-func (r *PasswordModifyRequest) encode() (*ber.Packet, error) {
-	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation")
-	request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID"))
+func (req *PasswordModifyRequest) appendTo(envelope *ber.Packet) error {
+	pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation")
+	pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID"))
+
 	extendedRequestValue := ber.Encode(ber.ClassContext, ber.TypePrimitive, 1, nil, "Extended Request Value: Password Modify Request")
 	passwordModifyRequestValue := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Password Modify Request")
-	if r.UserIdentity != "" {
-		passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, r.UserIdentity, "User Identity"))
+	if req.UserIdentity != "" {
+		passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.UserIdentity, "User Identity"))
 	}
-	if r.OldPassword != "" {
-		passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, r.OldPassword, "Old Password"))
+	if req.OldPassword != "" {
+		passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, req.OldPassword, "Old Password"))
 	}
-	if r.NewPassword != "" {
-		passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, r.NewPassword, "New Password"))
+	if req.NewPassword != "" {
+		passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, req.NewPassword, "New Password"))
 	}
-
 	extendedRequestValue.AppendChild(passwordModifyRequestValue)
-	request.AppendChild(extendedRequestValue)
 
-	return request, nil
+	pkt.AppendChild(extendedRequestValue)
+
+	envelope.AppendChild(pkt)
+
+	return nil
 }
 
 // NewPasswordModifyRequest creates a new PasswordModifyRequest
 //
-// According to the RFC 3602:
+// According to the RFC 3602 (https://tools.ietf.org/html/rfc3062):
 // userIdentity is a string representing the user associated with the request.
 // This string may or may not be an LDAPDN (RFC 2253).
 // If userIdentity is empty then the operation will act on the user associated
@@ -82,54 +81,33 @@ func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPasswo
 
 // PasswordModify performs the modification request
 func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, 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"))
-
-	encodedPasswordModifyRequest, err := passwordModifyRequest.encode()
-	if err != nil {
-		return nil, err
-	}
-	packet.AppendChild(encodedPasswordModifyRequest)
-
-	l.Debug.PrintPacket(packet)
-
-	msgCtx, err := l.sendMessage(packet)
+	msgCtx, err := l.doRequest(passwordModifyRequest)
 	if err != nil {
 		return nil, err
 	}
 	defer l.finishMessage(msgCtx)
 
-	result := &PasswordModifyResult{}
-
-	l.Debug.Printf("%d: waiting for response", msgCtx.id)
-	packetResponse, ok := <-msgCtx.responses
-	if !ok {
-		return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
-	}
-	packet, err = packetResponse.ReadPacket()
-	l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
+	packet, err := l.readPacket(msgCtx)
 	if err != nil {
 		return nil, err
 	}
 
-	if packet == nil {
-		return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
-	}
-
-	if l.Debug {
-		if err := addLDAPDescriptions(packet); err != nil {
-			return nil, err
-		}
-		ber.PrintPacket(packet)
-	}
+	result := &PasswordModifyResult{}
 
 	if packet.Children[1].Tag == ApplicationExtendedResponse {
-		resultCode, resultDescription := getLDAPResultCode(packet)
-		if resultCode != 0 {
-			return nil, NewError(resultCode, errors.New(resultDescription))
+		err := GetLDAPError(packet)
+		if err != nil {
+			if IsErrorWithCode(err, LDAPResultReferral) {
+				for _, child := range packet.Children[1].Children {
+					if child.Tag == 3 {
+						result.Referral = child.Children[0].Value.(string)
+					}
+				}
+			}
+			return result, err
 		}
 	} else {
-		return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag))
+		return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("unexpected Response: %d", packet.Children[1].Tag))
 	}
 
 	extendedResponse := packet.Children[1]
diff --git a/vendor/github.com/go-ldap/ldap/v3/request.go b/vendor/github.com/go-ldap/ldap/v3/request.go
new file mode 100644
index 0000000..4ea31e9
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/request.go
@@ -0,0 +1,71 @@
+package ldap
+
+import (
+	"errors"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+var (
+	errRespChanClosed = errors.New("ldap: response channel closed")
+	errCouldNotRetMsg = errors.New("ldap: could not retrieve message")
+	ErrNilConnection  = errors.New("ldap: conn is nil, expected net.Conn")
+)
+
+type request interface {
+	appendTo(*ber.Packet) error
+}
+
+type requestFunc func(*ber.Packet) error
+
+func (f requestFunc) appendTo(p *ber.Packet) error {
+	return f(p)
+}
+
+func (l *Conn) doRequest(req request) (*messageContext, error) {
+	if l == nil || l.conn == nil {
+		return nil, ErrNilConnection
+	}
+
+	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"))
+	if err := req.appendTo(packet); err != nil {
+		return nil, err
+	}
+
+	if l.Debug {
+		l.Debug.PrintPacket(packet)
+	}
+
+	msgCtx, err := l.sendMessage(packet)
+	if err != nil {
+		return nil, err
+	}
+	l.Debug.Printf("%d: returning", msgCtx.id)
+	return msgCtx, nil
+}
+
+func (l *Conn) readPacket(msgCtx *messageContext) (*ber.Packet, error) {
+	l.Debug.Printf("%d: waiting for response", msgCtx.id)
+	packetResponse, ok := <-msgCtx.responses
+	if !ok {
+		return nil, NewError(ErrorNetwork, errRespChanClosed)
+	}
+	packet, err := packetResponse.ReadPacket()
+	l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
+	if err != nil {
+		return nil, err
+	}
+
+	if packet == nil {
+		return nil, NewError(ErrorNetwork, errCouldNotRetMsg)
+	}
+
+	if l.Debug {
+		if err = addLDAPDescriptions(packet); err != nil {
+			return nil, err
+		}
+		l.Debug.PrintPacket(packet)
+	}
+	return packet, nil
+}
diff --git a/vendor/gopkg.in/ldap.v2/search.go b/vendor/github.com/go-ldap/ldap/v3/search.go
similarity index 61%
rename from vendor/gopkg.in/ldap.v2/search.go
rename to vendor/github.com/go-ldap/ldap/v3/search.go
index 2a99894..915e420 100644
--- a/vendor/gopkg.in/ldap.v2/search.go
+++ b/vendor/github.com/go-ldap/ldap/v3/search.go
@@ -1,62 +1,3 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-//
-// File contains Search functionality
-//
-// https://tools.ietf.org/html/rfc4511
-//
-//         SearchRequest ::= [APPLICATION 3] SEQUENCE {
-//              baseObject      LDAPDN,
-//              scope           ENUMERATED {
-//                   baseObject              (0),
-//                   singleLevel             (1),
-//                   wholeSubtree            (2),
-//                   ...  },
-//              derefAliases    ENUMERATED {
-//                   neverDerefAliases       (0),
-//                   derefInSearching        (1),
-//                   derefFindingBaseObj     (2),
-//                   derefAlways             (3) },
-//              sizeLimit       INTEGER (0 ..  maxInt),
-//              timeLimit       INTEGER (0 ..  maxInt),
-//              typesOnly       BOOLEAN,
-//              filter          Filter,
-//              attributes      AttributeSelection }
-//
-//         AttributeSelection ::= SEQUENCE OF selector LDAPString
-//                         -- The LDAPString is constrained to
-//                         -- <attributeSelector> in Section 4.5.1.8
-//
-//         Filter ::= CHOICE {
-//              and             [0] SET SIZE (1..MAX) OF filter Filter,
-//              or              [1] SET SIZE (1..MAX) OF filter Filter,
-//              not             [2] Filter,
-//              equalityMatch   [3] AttributeValueAssertion,
-//              substrings      [4] SubstringFilter,
-//              greaterOrEqual  [5] AttributeValueAssertion,
-//              lessOrEqual     [6] AttributeValueAssertion,
-//              present         [7] AttributeDescription,
-//              approxMatch     [8] AttributeValueAssertion,
-//              extensibleMatch [9] MatchingRuleAssertion,
-//              ...  }
-//
-//         SubstringFilter ::= SEQUENCE {
-//              type           AttributeDescription,
-//              substrings     SEQUENCE SIZE (1..MAX) OF substring CHOICE {
-//                   initial [0] AssertionValue,  -- can occur at most once
-//                   any     [1] AssertionValue,
-//                   final   [2] AssertionValue } -- can occur at most once
-//              }
-//
-//         MatchingRuleAssertion ::= SEQUENCE {
-//              matchingRule    [1] MatchingRuleId OPTIONAL,
-//              type            [2] AttributeDescription OPTIONAL,
-//              matchValue      [3] AssertionValue,
-//              dnAttributes    [4] BOOLEAN DEFAULT FALSE }
-//
-//
-
 package ldap
 
 import (
@@ -65,7 +6,7 @@ import (
 	"sort"
 	"strings"
 
-	"gopkg.in/asn1-ber.v1"
+	ber "github.com/go-asn1-ber/asn1-ber"
 )
 
 // scope choices
@@ -136,6 +77,17 @@ func (e *Entry) GetAttributeValues(attribute string) []string {
 	return []string{}
 }
 
+// GetEqualFoldAttributeValues returns the values for the named attribute, or an
+// empty list. Attribute matching is done with strings.EqualFold.
+func (e *Entry) GetEqualFoldAttributeValues(attribute string) []string {
+	for _, attr := range e.Attributes {
+		if strings.EqualFold(attribute, attr.Name) {
+			return attr.Values
+		}
+	}
+	return []string{}
+}
+
 // GetRawAttributeValues returns the byte values for the named attribute, or an empty list
 func (e *Entry) GetRawAttributeValues(attribute string) [][]byte {
 	for _, attr := range e.Attributes {
@@ -146,6 +98,16 @@ func (e *Entry) GetRawAttributeValues(attribute string) [][]byte {
 	return [][]byte{}
 }
 
+// GetEqualFoldRawAttributeValues returns the byte values for the named attribute, or an empty list
+func (e *Entry) GetEqualFoldRawAttributeValues(attribute string) [][]byte {
+	for _, attr := range e.Attributes {
+		if strings.EqualFold(attr.Name, attribute) {
+			return attr.ByteValues
+		}
+	}
+	return [][]byte{}
+}
+
 // GetAttributeValue returns the first value for the named attribute, or ""
 func (e *Entry) GetAttributeValue(attribute string) string {
 	values := e.GetAttributeValues(attribute)
@@ -155,6 +117,16 @@ func (e *Entry) GetAttributeValue(attribute string) string {
 	return values[0]
 }
 
+// GetEqualFoldAttributeValue returns the first value for the named attribute, or "".
+// Attribute comparison is done with strings.EqualFold.
+func (e *Entry) GetEqualFoldAttributeValue(attribute string) string {
+	values := e.GetEqualFoldAttributeValues(attribute)
+	if len(values) == 0 {
+		return ""
+	}
+	return values[0]
+}
+
 // GetRawAttributeValue returns the first value for the named attribute, or an empty slice
 func (e *Entry) GetRawAttributeValue(attribute string) []byte {
 	values := e.GetRawAttributeValues(attribute)
@@ -164,6 +136,15 @@ func (e *Entry) GetRawAttributeValue(attribute string) []byte {
 	return values[0]
 }
 
+// GetEqualFoldRawAttributeValue returns the first value for the named attribute, or an empty slice
+func (e *Entry) GetEqualFoldRawAttributeValue(attribute string) []byte {
+	values := e.GetEqualFoldRawAttributeValues(attribute)
+	if len(values) == 0 {
+		return []byte{}
+	}
+	return values[0]
+}
+
 // Print outputs a human-readable description
 func (e *Entry) Print() {
 	fmt.Printf("DN: %s\n", e.DN)
@@ -250,27 +231,33 @@ type SearchRequest struct {
 	Controls     []Control
 }
 
-func (s *SearchRequest) encode() (*ber.Packet, error) {
-	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request")
-	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, s.BaseDN, "Base DN"))
-	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.Scope), "Scope"))
-	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.DerefAliases), "Deref Aliases"))
-	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.SizeLimit), "Size Limit"))
-	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.TimeLimit), "Time Limit"))
-	request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, s.TypesOnly, "Types Only"))
+func (req *SearchRequest) appendTo(envelope *ber.Packet) error {
+	pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request")
+	pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.BaseDN, "Base DN"))
+	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(req.Scope), "Scope"))
+	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(req.DerefAliases), "Deref Aliases"))
+	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(req.SizeLimit), "Size Limit"))
+	pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(req.TimeLimit), "Time Limit"))
+	pkt.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, req.TypesOnly, "Types Only"))
 	// compile and encode filter
-	filterPacket, err := CompileFilter(s.Filter)
+	filterPacket, err := CompileFilter(req.Filter)
 	if err != nil {
-		return nil, err
+		return err
 	}
-	request.AppendChild(filterPacket)
+	pkt.AppendChild(filterPacket)
 	// encode attributes
 	attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
-	for _, attribute := range s.Attributes {
+	for _, attribute := range req.Attributes {
 		attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
 	}
-	request.AppendChild(attributesPacket)
-	return request, nil
+	pkt.AppendChild(attributesPacket)
+
+	envelope.AppendChild(pkt)
+	if len(req.Controls) > 0 {
+		envelope.AppendChild(encodeControls(req.Controls))
+	}
+
+	return nil
 }
 
 // NewSearchRequest creates a new search request
@@ -313,10 +300,10 @@ func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32)
 	} else {
 		castControl, ok := control.(*ControlPaging)
 		if !ok {
-			return nil, fmt.Errorf("Expected paging control to be of type *ControlPaging, got %v", control)
+			return nil, fmt.Errorf("expected paging control to be of type *ControlPaging, got %v", control)
 		}
 		if castControl.PagingSize != pagingSize {
-			return nil, fmt.Errorf("Paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize)
+			return nil, fmt.Errorf("paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize)
 		}
 		pagingControl = castControl
 	}
@@ -370,22 +357,7 @@ func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32)
 
 // Search performs the given search request
 func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, 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"))
-	// encode search request
-	encodedSearchRequest, err := searchRequest.encode()
-	if err != nil {
-		return nil, err
-	}
-	packet.AppendChild(encodedSearchRequest)
-	// encode search controls
-	if searchRequest.Controls != nil {
-		packet.AppendChild(encodeControls(searchRequest.Controls))
-	}
-
-	l.Debug.PrintPacket(packet)
-
-	msgCtx, err := l.sendMessage(packet)
+	msgCtx, err := l.doRequest(searchRequest)
 	if err != nil {
 		return nil, err
 	}
@@ -396,55 +368,60 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
 		Referrals: make([]string, 0),
 		Controls:  make([]Control, 0)}
 
-	foundSearchResultDone := false
-	for !foundSearchResultDone {
-		l.Debug.Printf("%d: waiting for response", msgCtx.id)
-		packetResponse, ok := <-msgCtx.responses
-		if !ok {
-			return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
-		}
-		packet, err = packetResponse.ReadPacket()
-		l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
+	for {
+		packet, err := l.readPacket(msgCtx)
 		if err != nil {
-			return nil, err
-		}
-
-		if l.Debug {
-			if err := addLDAPDescriptions(packet); err != nil {
-				return nil, err
-			}
-			ber.PrintPacket(packet)
+			return result, err
 		}
 
 		switch packet.Children[1].Tag {
 		case 4:
-			entry := new(Entry)
-			entry.DN = packet.Children[1].Children[0].Value.(string)
-			for _, child := range packet.Children[1].Children[1].Children {
-				attr := new(EntryAttribute)
-				attr.Name = child.Children[0].Value.(string)
-				for _, value := range child.Children[1].Children {
-					attr.Values = append(attr.Values, value.Value.(string))
-					attr.ByteValues = append(attr.ByteValues, value.ByteValue)
-				}
-				entry.Attributes = append(entry.Attributes, attr)
+			entry := &Entry{
+				DN:         packet.Children[1].Children[0].Value.(string),
+				Attributes: unpackAttributes(packet.Children[1].Children[1].Children),
 			}
 			result.Entries = append(result.Entries, entry)
 		case 5:
-			resultCode, resultDescription := getLDAPResultCode(packet)
-			if resultCode != 0 {
-				return result, NewError(resultCode, errors.New(resultDescription))
+			err := GetLDAPError(packet)
+			if err != nil {
+				return result, err
 			}
 			if len(packet.Children) == 3 {
 				for _, child := range packet.Children[2].Children {
-					result.Controls = append(result.Controls, DecodeControl(child))
+					decodedChild, err := DecodeControl(child)
+					if err != nil {
+						return result, fmt.Errorf("failed to decode child control: %s", err)
+					}
+					result.Controls = append(result.Controls, decodedChild)
 				}
 			}
-			foundSearchResultDone = true
+			return result, nil
 		case 19:
 			result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string))
 		}
 	}
-	l.Debug.Printf("%d: returning", msgCtx.id)
-	return result, nil
+}
+
+// unpackAttributes will extract all given LDAP attributes and it's values
+// from the ber.Packet
+func unpackAttributes(children []*ber.Packet) []*EntryAttribute {
+	entries := make([]*EntryAttribute, len(children))
+	for i, child := range children {
+		length := len(child.Children[1].Children)
+		entry := &EntryAttribute{
+			Name: child.Children[0].Value.(string),
+			// pre-allocate the slice since we can determine
+			// the number of attributes at this point
+			Values:     make([]string, length),
+			ByteValues: make([][]byte, length),
+		}
+
+		for i, value := range child.Children[1].Children {
+			entry.ByteValues[i] = value.ByteValue
+			entry.Values[i] = value.Value.(string)
+		}
+		entries[i] = entry
+	}
+
+	return entries
 }
diff --git a/vendor/github.com/go-ldap/ldap/v3/unbind.go b/vendor/github.com/go-ldap/ldap/v3/unbind.go
new file mode 100644
index 0000000..6c411cd
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/unbind.go
@@ -0,0 +1,37 @@
+package ldap
+
+import (
+	"errors"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+var ErrConnUnbound = NewError(ErrorNetwork, errors.New("ldap: connection is closed"))
+
+type unbindRequest struct{}
+
+func (unbindRequest) appendTo(envelope *ber.Packet) error {
+	envelope.AppendChild(ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationUnbindRequest, nil, ApplicationMap[ApplicationUnbindRequest]))
+	return nil
+}
+
+// Unbind will perform an unbind request. The Unbind operation
+// should be thought of as the "quit" operation.
+// See https://datatracker.ietf.org/doc/html/rfc4511#section-4.3
+func (l *Conn) Unbind() error {
+	if l.IsClosing() {
+		return ErrConnUnbound
+	}
+
+	_, err := l.doRequest(unbindRequest{})
+	if err != nil {
+		return err
+	}
+
+	// Sending an unbindRequest will make the connection unusable.
+	// Pending requests will fail with:
+	// LDAP Result Code 200 "Network Error": ldap: response channel closed
+	l.Close()
+
+	return nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/v3/whoami.go b/vendor/github.com/go-ldap/ldap/v3/whoami.go
new file mode 100644
index 0000000..10c523d
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/v3/whoami.go
@@ -0,0 +1,91 @@
+package ldap
+
+// This file contains the "Who Am I?" extended operation as specified in rfc 4532
+//
+// https://tools.ietf.org/html/rfc4532
+
+import (
+	"errors"
+	"fmt"
+
+	ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+type whoAmIRequest bool
+
+// WhoAmIResult is returned by the WhoAmI() call
+type WhoAmIResult struct {
+	AuthzID string
+}
+
+func (r whoAmIRequest) encode() (*ber.Packet, error) {
+	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Who Am I? Extended Operation")
+	request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, ControlTypeWhoAmI, "Extended Request Name: Who Am I? OID"))
+	return request, nil
+}
+
+// WhoAmI returns the authzId the server thinks we are, you may pass controls
+// like a Proxied Authorization control
+func (l *Conn) WhoAmI(controls []Control) (*WhoAmIResult, 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"))
+	req := whoAmIRequest(true)
+	encodedWhoAmIRequest, err := req.encode()
+	if err != nil {
+		return nil, err
+	}
+	packet.AppendChild(encodedWhoAmIRequest)
+
+	if len(controls) != 0 {
+		packet.AppendChild(encodeControls(controls))
+	}
+
+	l.Debug.PrintPacket(packet)
+
+	msgCtx, err := l.sendMessage(packet)
+	if err != nil {
+		return nil, err
+	}
+	defer l.finishMessage(msgCtx)
+
+	result := &WhoAmIResult{}
+
+	l.Debug.Printf("%d: waiting for response", msgCtx.id)
+	packetResponse, ok := <-msgCtx.responses
+	if !ok {
+		return nil, 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 nil, err
+	}
+
+	if packet == nil {
+		return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
+	}
+
+	if l.Debug {
+		if err := addLDAPDescriptions(packet); err != nil {
+			return nil, err
+		}
+		ber.PrintPacket(packet)
+	}
+
+	if packet.Children[1].Tag == ApplicationExtendedResponse {
+		if err := GetLDAPError(packet); err != nil {
+			return nil, err
+		}
+	} else {
+		return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag))
+	}
+
+	extendedResponse := packet.Children[1]
+	for _, child := range extendedResponse.Children {
+		if child.Tag == 11 {
+			result.AuthzID = ber.DecodeString(child.Data.Bytes())
+		}
+	}
+
+	return result, nil
+}
diff --git a/vendor/gopkg.in/ldap.v2/.travis.yml b/vendor/gopkg.in/ldap.v2/.travis.yml
deleted file mode 100644
index 9782c9b..0000000
--- a/vendor/gopkg.in/ldap.v2/.travis.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-language: go
-env:
-    global:
-        - VET_VERSIONS="1.6 1.7 1.8 1.9 tip"
-        - LINT_VERSIONS="1.6 1.7 1.8 1.9 tip"
-go:
-    - 1.2
-    - 1.3
-    - 1.4
-    - 1.5
-    - 1.6
-    - 1.7
-    - 1.8
-    - 1.9
-    - tip
-matrix:
-    fast_finish: true
-    allow_failures:
-        - go: tip
-go_import_path: gopkg.in/ldap.v2
-install:
-    - go get gopkg.in/asn1-ber.v1
-    - go get gopkg.in/ldap.v2
-    - go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover
-    - go get github.com/golang/lint/golint || true
-    - go build -v ./...
-script:
-    - make test
-    - make fmt
-    - if [[ "$VET_VERSIONS"  == *"$TRAVIS_GO_VERSION"* ]]; then make vet; fi
-    - if [[ "$LINT_VERSIONS" == *"$TRAVIS_GO_VERSION"* ]]; then make lint; fi
diff --git a/vendor/gopkg.in/ldap.v2/Makefile b/vendor/gopkg.in/ldap.v2/Makefile
deleted file mode 100644
index a9d351c..0000000
--- a/vendor/gopkg.in/ldap.v2/Makefile
+++ /dev/null
@@ -1,52 +0,0 @@
-.PHONY: default install build test quicktest fmt vet lint 
-
-GO_VERSION := $(shell go version | cut -d' ' -f3 | cut -d. -f2)
-
-# Only use the `-race` flag on newer versions of Go
-IS_OLD_GO := $(shell test $(GO_VERSION) -le 2 && echo true)
-ifeq ($(IS_OLD_GO),true)
-	RACE_FLAG :=
-else
-	RACE_FLAG := -race -cpu 1,2,4
-endif
-
-default: fmt vet lint build quicktest
-
-install:
-	go get -t -v ./...
-
-build:
-	go build -v ./...
-
-test:
-	go test -v $(RACE_FLAG) -cover ./...
-
-quicktest:
-	go test ./...
-
-# Capture output and force failure when there is non-empty output
-fmt:
-	@echo gofmt -l .
-	@OUTPUT=`gofmt -l . 2>&1`; \
-	if [ "$$OUTPUT" ]; then \
-		echo "gofmt must be run on the following files:"; \
-		echo "$$OUTPUT"; \
-		exit 1; \
-	fi
-
-# Only run on go1.5+
-vet:
-	go tool vet -atomic -bool -copylocks -nilfunc -printf -shadow -rangeloops -unreachable -unsafeptr -unusedresult .
-
-# https://github.com/golang/lint
-# go get github.com/golang/lint/golint
-# Capture output and force failure when there is non-empty output
-# Only run on go1.5+
-lint:
-	@echo golint ./...
-	@OUTPUT=`golint ./... 2>&1`; \
-	if [ "$$OUTPUT" ]; then \
-		echo "golint errors:"; \
-		echo "$$OUTPUT"; \
-		exit 1; \
-	fi
diff --git a/vendor/gopkg.in/ldap.v2/README.md b/vendor/gopkg.in/ldap.v2/README.md
deleted file mode 100644
index a26ed2d..0000000
--- a/vendor/gopkg.in/ldap.v2/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-[![GoDoc](https://godoc.org/gopkg.in/ldap.v2?status.svg)](https://godoc.org/gopkg.in/ldap.v2)
-[![Build Status](https://travis-ci.org/go-ldap/ldap.svg)](https://travis-ci.org/go-ldap/ldap)
-
-# Basic LDAP v3 functionality for the GO programming language.
-
-## Install
-
-For the latest version use:
-
-    go get gopkg.in/ldap.v2
-
-Import the latest version with:
-
-    import "gopkg.in/ldap.v2"
-
-## Required Libraries:
-
- - gopkg.in/asn1-ber.v1
-
-## Features:
-
- - Connecting to LDAP server (non-TLS, TLS, STARTTLS)
- - Binding to LDAP server
- - Searching for entries
- - Filter Compile / Decompile
- - Paging Search Results
- - Modify Requests / Responses
- - Add Requests / Responses
- - Delete Requests / Responses
-
-## Examples:
-
- - search
- - modify
-
-## Contributing:
-
-Bug reports and pull requests are welcome!
-
-Before submitting a pull request, please make sure tests and verification scripts pass:
-```
-make all
-```
-
-To set up a pre-push hook to run the tests and verify scripts before pushing:
-```
-ln -s ../../.githooks/pre-push .git/hooks/pre-push
-```
-
----
-The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
-The design is licensed under the Creative Commons 3.0 Attributions license.
-Read this article for more details: http://blog.golang.org/gopher
diff --git a/vendor/gopkg.in/ldap.v2/add.go b/vendor/gopkg.in/ldap.v2/add.go
deleted file mode 100644
index 0e5f6cd..0000000
--- a/vendor/gopkg.in/ldap.v2/add.go
+++ /dev/null
@@ -1,113 +0,0 @@
-//
-// https://tools.ietf.org/html/rfc4511
-//
-// AddRequest ::= [APPLICATION 8] SEQUENCE {
-//      entry           LDAPDN,
-//      attributes      AttributeList }
-//
-// AttributeList ::= SEQUENCE OF attribute Attribute
-
-package ldap
-
-import (
-	"errors"
-	"log"
-
-	"gopkg.in/asn1-ber.v1"
-)
-
-// Attribute represents an LDAP attribute
-type Attribute struct {
-	// Type is the name of the LDAP attribute
-	Type string
-	// Vals are the LDAP attribute values
-	Vals []string
-}
-
-func (a *Attribute) encode() *ber.Packet {
-	seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
-	seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type"))
-	set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
-	for _, value := range a.Vals {
-		set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
-	}
-	seq.AppendChild(set)
-	return seq
-}
-
-// AddRequest represents an LDAP AddRequest operation
-type AddRequest struct {
-	// DN identifies the entry being added
-	DN string
-	// Attributes list the attributes of the new entry
-	Attributes []Attribute
-}
-
-func (a AddRequest) encode() *ber.Packet {
-	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
-	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN"))
-	attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
-	for _, attribute := range a.Attributes {
-		attributes.AppendChild(attribute.encode())
-	}
-	request.AppendChild(attributes)
-	return request
-}
-
-// Attribute adds an attribute with the given type and values
-func (a *AddRequest) Attribute(attrType string, attrVals []string) {
-	a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals})
-}
-
-// NewAddRequest returns an AddRequest for the given DN, with no attributes
-func NewAddRequest(dn string) *AddRequest {
-	return &AddRequest{
-		DN: dn,
-	}
-
-}
-
-// Add performs the given AddRequest
-func (l *Conn) Add(addRequest *AddRequest) 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"))
-	packet.AppendChild(addRequest.encode())
-
-	l.Debug.PrintPacket(packet)
-
-	msgCtx, err := l.sendMessage(packet)
-	if err != nil {
-		return err
-	}
-	defer l.finishMessage(msgCtx)
-
-	l.Debug.Printf("%d: waiting for response", msgCtx.id)
-	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)
-	}
-
-	if packet.Children[1].Tag == ApplicationAddResponse {
-		resultCode, resultDescription := getLDAPResultCode(packet)
-		if resultCode != 0 {
-			return NewError(resultCode, errors.New(resultDescription))
-		}
-	} else {
-		log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
-	}
-
-	l.Debug.Printf("%d: returning", msgCtx.id)
-	return nil
-}
diff --git a/vendor/gopkg.in/ldap.v2/atomic_value.go b/vendor/gopkg.in/ldap.v2/atomic_value.go
deleted file mode 100644
index bccf757..0000000
--- a/vendor/gopkg.in/ldap.v2/atomic_value.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// +build go1.4
-
-package ldap
-
-import (
-	"sync/atomic"
-)
-
-// For compilers that support it, we just use the underlying sync/atomic.Value
-// type.
-type atomicValue struct {
-	atomic.Value
-}
diff --git a/vendor/gopkg.in/ldap.v2/atomic_value_go13.go b/vendor/gopkg.in/ldap.v2/atomic_value_go13.go
deleted file mode 100644
index 04920bb..0000000
--- a/vendor/gopkg.in/ldap.v2/atomic_value_go13.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// +build !go1.4
-
-package ldap
-
-import (
-	"sync"
-)
-
-// This is a helper type that emulates the use of the "sync/atomic.Value"
-// struct that's available in Go 1.4 and up.
-type atomicValue struct {
-	value interface{}
-	lock  sync.RWMutex
-}
-
-func (av *atomicValue) Store(val interface{}) {
-	av.lock.Lock()
-	av.value = val
-	av.lock.Unlock()
-}
-
-func (av *atomicValue) Load() interface{} {
-	av.lock.RLock()
-	ret := av.value
-	av.lock.RUnlock()
-
-	return ret
-}
diff --git a/vendor/gopkg.in/ldap.v2/bind.go b/vendor/gopkg.in/ldap.v2/bind.go
deleted file mode 100644
index 26b3cc7..0000000
--- a/vendor/gopkg.in/ldap.v2/bind.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package ldap
-
-import (
-	"errors"
-
-	"gopkg.in/asn1-ber.v1"
-)
-
-// SimpleBindRequest represents a username/password bind operation
-type SimpleBindRequest struct {
-	// Username is the name of the Directory object that the client wishes to bind as
-	Username string
-	// Password is the credentials to bind with
-	Password string
-	// Controls are optional controls to send with the bind request
-	Controls []Control
-}
-
-// SimpleBindResult contains the response from the server
-type SimpleBindResult struct {
-	Controls []Control
-}
-
-// NewSimpleBindRequest returns a bind request
-func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
-	return &SimpleBindRequest{
-		Username: username,
-		Password: password,
-		Controls: controls,
-	}
-}
-
-func (bindRequest *SimpleBindRequest) encode() *ber.Packet {
-	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
-	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
-	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, bindRequest.Username, "User Name"))
-	request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, bindRequest.Password, "Password"))
-
-	request.AppendChild(encodeControls(bindRequest.Controls))
-
-	return request
-}
-
-// SimpleBind performs the simple bind operation defined in the given request
-func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, 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"))
-	encodedBindRequest := simpleBindRequest.encode()
-	packet.AppendChild(encodedBindRequest)
-
-	if l.Debug {
-		ber.PrintPacket(packet)
-	}
-
-	msgCtx, err := l.sendMessage(packet)
-	if err != nil {
-		return nil, err
-	}
-	defer l.finishMessage(msgCtx)
-
-	packetResponse, ok := <-msgCtx.responses
-	if !ok {
-		return nil, 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 nil, err
-	}
-
-	if l.Debug {
-		if err := addLDAPDescriptions(packet); err != nil {
-			return nil, err
-		}
-		ber.PrintPacket(packet)
-	}
-
-	result := &SimpleBindResult{
-		Controls: make([]Control, 0),
-	}
-
-	if len(packet.Children) == 3 {
-		for _, child := range packet.Children[2].Children {
-			result.Controls = append(result.Controls, DecodeControl(child))
-		}
-	}
-
-	resultCode, resultDescription := getLDAPResultCode(packet)
-	if resultCode != 0 {
-		return result, NewError(resultCode, errors.New(resultDescription))
-	}
-
-	return result, nil
-}
-
-// Bind performs a bind with the given username and password
-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)
-	}
-
-	resultCode, resultDescription := getLDAPResultCode(packet)
-	if resultCode != 0 {
-		return NewError(resultCode, errors.New(resultDescription))
-	}
-
-	return nil
-}
diff --git a/vendor/gopkg.in/ldap.v2/client.go b/vendor/gopkg.in/ldap.v2/client.go
deleted file mode 100644
index 055b27b..0000000
--- a/vendor/gopkg.in/ldap.v2/client.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package ldap
-
-import (
-	"crypto/tls"
-	"time"
-)
-
-// Client knows how to interact with an LDAP server
-type Client interface {
-	Start()
-	StartTLS(config *tls.Config) error
-	Close()
-	SetTimeout(time.Duration)
-
-	Bind(username, password string) error
-	SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error)
-
-	Add(addRequest *AddRequest) error
-	Del(delRequest *DelRequest) error
-	Modify(modifyRequest *ModifyRequest) error
-
-	Compare(dn, attribute, value string) (bool, error)
-	PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error)
-
-	Search(searchRequest *SearchRequest) (*SearchResult, error)
-	SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
-}
diff --git a/vendor/gopkg.in/ldap.v2/compare.go b/vendor/gopkg.in/ldap.v2/compare.go
deleted file mode 100644
index cc6d2af..0000000
--- a/vendor/gopkg.in/ldap.v2/compare.go
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-//
-// File contains Compare functionality
-//
-// https://tools.ietf.org/html/rfc4511
-//
-// CompareRequest ::= [APPLICATION 14] SEQUENCE {
-//              entry           LDAPDN,
-//              ava             AttributeValueAssertion }
-//
-// AttributeValueAssertion ::= SEQUENCE {
-//              attributeDesc   AttributeDescription,
-//              assertionValue  AssertionValue }
-//
-// AttributeDescription ::= LDAPString
-//                         -- Constrained to <attributedescription>
-//                         -- [RFC4512]
-//
-// AttributeValue ::= OCTET STRING
-//
-
-package ldap
-
-import (
-	"errors"
-	"fmt"
-
-	"gopkg.in/asn1-ber.v1"
-)
-
-// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise
-// false with any error that occurs if any.
-func (l *Conn) Compare(dn, attribute, value string) (bool, 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"))
-
-	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request")
-	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN"))
-
-	ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion")
-	ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "AttributeDesc"))
-	ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagOctetString, value, "AssertionValue"))
-	request.AppendChild(ava)
-	packet.AppendChild(request)
-
-	l.Debug.PrintPacket(packet)
-
-	msgCtx, err := l.sendMessage(packet)
-	if err != nil {
-		return false, err
-	}
-	defer l.finishMessage(msgCtx)
-
-	l.Debug.Printf("%d: waiting for response", msgCtx.id)
-	packetResponse, ok := <-msgCtx.responses
-	if !ok {
-		return false, 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 false, err
-	}
-
-	if l.Debug {
-		if err := addLDAPDescriptions(packet); err != nil {
-			return false, err
-		}
-		ber.PrintPacket(packet)
-	}
-
-	if packet.Children[1].Tag == ApplicationCompareResponse {
-		resultCode, resultDescription := getLDAPResultCode(packet)
-		if resultCode == LDAPResultCompareTrue {
-			return true, nil
-		} else if resultCode == LDAPResultCompareFalse {
-			return false, nil
-		} else {
-			return false, NewError(resultCode, errors.New(resultDescription))
-		}
-	}
-	return false, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag)
-}
diff --git a/vendor/gopkg.in/ldap.v2/del.go b/vendor/gopkg.in/ldap.v2/del.go
deleted file mode 100644
index 4fd63dc..0000000
--- a/vendor/gopkg.in/ldap.v2/del.go
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-// https://tools.ietf.org/html/rfc4511
-//
-// DelRequest ::= [APPLICATION 10] LDAPDN
-
-package ldap
-
-import (
-	"errors"
-	"log"
-
-	"gopkg.in/asn1-ber.v1"
-)
-
-// DelRequest implements an LDAP deletion request
-type DelRequest struct {
-	// DN is the name of the directory entry to delete
-	DN string
-	// Controls hold optional controls to send with the request
-	Controls []Control
-}
-
-func (d DelRequest) encode() *ber.Packet {
-	request := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, d.DN, "Del Request")
-	request.Data.Write([]byte(d.DN))
-	return request
-}
-
-// NewDelRequest creates a delete request for the given DN and controls
-func NewDelRequest(DN string,
-	Controls []Control) *DelRequest {
-	return &DelRequest{
-		DN:       DN,
-		Controls: Controls,
-	}
-}
-
-// Del executes the given delete request
-func (l *Conn) Del(delRequest *DelRequest) 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"))
-	packet.AppendChild(delRequest.encode())
-	if delRequest.Controls != nil {
-		packet.AppendChild(encodeControls(delRequest.Controls))
-	}
-
-	l.Debug.PrintPacket(packet)
-
-	msgCtx, err := l.sendMessage(packet)
-	if err != nil {
-		return err
-	}
-	defer l.finishMessage(msgCtx)
-
-	l.Debug.Printf("%d: waiting for response", msgCtx.id)
-	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)
-	}
-
-	if packet.Children[1].Tag == ApplicationDelResponse {
-		resultCode, resultDescription := getLDAPResultCode(packet)
-		if resultCode != 0 {
-			return NewError(resultCode, errors.New(resultDescription))
-		}
-	} else {
-		log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
-	}
-
-	l.Debug.Printf("%d: returning", msgCtx.id)
-	return nil
-}
diff --git a/vendor/gopkg.in/ldap.v2/error.go b/vendor/gopkg.in/ldap.v2/error.go
deleted file mode 100644
index 4cccb53..0000000
--- a/vendor/gopkg.in/ldap.v2/error.go
+++ /dev/null
@@ -1,155 +0,0 @@
-package ldap
-
-import (
-	"fmt"
-
-	"gopkg.in/asn1-ber.v1"
-)
-
-// LDAP Result Codes
-const (
-	LDAPResultSuccess                      = 0
-	LDAPResultOperationsError              = 1
-	LDAPResultProtocolError                = 2
-	LDAPResultTimeLimitExceeded            = 3
-	LDAPResultSizeLimitExceeded            = 4
-	LDAPResultCompareFalse                 = 5
-	LDAPResultCompareTrue                  = 6
-	LDAPResultAuthMethodNotSupported       = 7
-	LDAPResultStrongAuthRequired           = 8
-	LDAPResultReferral                     = 10
-	LDAPResultAdminLimitExceeded           = 11
-	LDAPResultUnavailableCriticalExtension = 12
-	LDAPResultConfidentialityRequired      = 13
-	LDAPResultSaslBindInProgress           = 14
-	LDAPResultNoSuchAttribute              = 16
-	LDAPResultUndefinedAttributeType       = 17
-	LDAPResultInappropriateMatching        = 18
-	LDAPResultConstraintViolation          = 19
-	LDAPResultAttributeOrValueExists       = 20
-	LDAPResultInvalidAttributeSyntax       = 21
-	LDAPResultNoSuchObject                 = 32
-	LDAPResultAliasProblem                 = 33
-	LDAPResultInvalidDNSyntax              = 34
-	LDAPResultAliasDereferencingProblem    = 36
-	LDAPResultInappropriateAuthentication  = 48
-	LDAPResultInvalidCredentials           = 49
-	LDAPResultInsufficientAccessRights     = 50
-	LDAPResultBusy                         = 51
-	LDAPResultUnavailable                  = 52
-	LDAPResultUnwillingToPerform           = 53
-	LDAPResultLoopDetect                   = 54
-	LDAPResultNamingViolation              = 64
-	LDAPResultObjectClassViolation         = 65
-	LDAPResultNotAllowedOnNonLeaf          = 66
-	LDAPResultNotAllowedOnRDN              = 67
-	LDAPResultEntryAlreadyExists           = 68
-	LDAPResultObjectClassModsProhibited    = 69
-	LDAPResultAffectsMultipleDSAs          = 71
-	LDAPResultOther                        = 80
-
-	ErrorNetwork            = 200
-	ErrorFilterCompile      = 201
-	ErrorFilterDecompile    = 202
-	ErrorDebugging          = 203
-	ErrorUnexpectedMessage  = 204
-	ErrorUnexpectedResponse = 205
-)
-
-// LDAPResultCodeMap contains string descriptions for LDAP error codes
-var LDAPResultCodeMap = map[uint8]string{
-	LDAPResultSuccess:                      "Success",
-	LDAPResultOperationsError:              "Operations Error",
-	LDAPResultProtocolError:                "Protocol Error",
-	LDAPResultTimeLimitExceeded:            "Time Limit Exceeded",
-	LDAPResultSizeLimitExceeded:            "Size Limit Exceeded",
-	LDAPResultCompareFalse:                 "Compare False",
-	LDAPResultCompareTrue:                  "Compare True",
-	LDAPResultAuthMethodNotSupported:       "Auth Method Not Supported",
-	LDAPResultStrongAuthRequired:           "Strong Auth Required",
-	LDAPResultReferral:                     "Referral",
-	LDAPResultAdminLimitExceeded:           "Admin Limit Exceeded",
-	LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension",
-	LDAPResultConfidentialityRequired:      "Confidentiality Required",
-	LDAPResultSaslBindInProgress:           "Sasl Bind In Progress",
-	LDAPResultNoSuchAttribute:              "No Such Attribute",
-	LDAPResultUndefinedAttributeType:       "Undefined Attribute Type",
-	LDAPResultInappropriateMatching:        "Inappropriate Matching",
-	LDAPResultConstraintViolation:          "Constraint Violation",
-	LDAPResultAttributeOrValueExists:       "Attribute Or Value Exists",
-	LDAPResultInvalidAttributeSyntax:       "Invalid Attribute Syntax",
-	LDAPResultNoSuchObject:                 "No Such Object",
-	LDAPResultAliasProblem:                 "Alias Problem",
-	LDAPResultInvalidDNSyntax:              "Invalid DN Syntax",
-	LDAPResultAliasDereferencingProblem:    "Alias Dereferencing Problem",
-	LDAPResultInappropriateAuthentication:  "Inappropriate Authentication",
-	LDAPResultInvalidCredentials:           "Invalid Credentials",
-	LDAPResultInsufficientAccessRights:     "Insufficient Access Rights",
-	LDAPResultBusy:                         "Busy",
-	LDAPResultUnavailable:                  "Unavailable",
-	LDAPResultUnwillingToPerform:           "Unwilling To Perform",
-	LDAPResultLoopDetect:                   "Loop Detect",
-	LDAPResultNamingViolation:              "Naming Violation",
-	LDAPResultObjectClassViolation:         "Object Class Violation",
-	LDAPResultNotAllowedOnNonLeaf:          "Not Allowed On Non Leaf",
-	LDAPResultNotAllowedOnRDN:              "Not Allowed On RDN",
-	LDAPResultEntryAlreadyExists:           "Entry Already Exists",
-	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",
-}
-
-func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) {
-	if packet == nil {
-		return ErrorUnexpectedResponse, "Empty packet"
-	} else if len(packet.Children) >= 2 {
-		response := packet.Children[1]
-		if response == nil {
-			return ErrorUnexpectedResponse, "Empty response in packet"
-		}
-		if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
-			// Children[1].Children[2] is the diagnosticMessage which is guaranteed to exist as seen here: https://tools.ietf.org/html/rfc4511#section-4.1.9
-			return uint8(response.Children[0].Value.(int64)), response.Children[2].Value.(string)
-		}
-	}
-
-	return ErrorNetwork, "Invalid packet format"
-}
-
-// Error holds LDAP error information
-type Error struct {
-	// Err is the underlying error
-	Err error
-	// ResultCode is the LDAP error code
-	ResultCode uint8
-}
-
-func (e *Error) Error() string {
-	return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
-}
-
-// NewError creates an LDAP error with the given code and underlying error
-func NewError(resultCode uint8, err error) error {
-	return &Error{ResultCode: resultCode, Err: err}
-}
-
-// IsErrorWithCode returns true if the given error is an LDAP error with the given result code
-func IsErrorWithCode(err error, desiredResultCode uint8) bool {
-	if err == nil {
-		return false
-	}
-
-	serverError, ok := err.(*Error)
-	if !ok {
-		return false
-	}
-
-	return serverError.ResultCode == desiredResultCode
-}
diff --git a/vendor/gopkg.in/ldap.v2/modify.go b/vendor/gopkg.in/ldap.v2/modify.go
deleted file mode 100644
index e4ab6ce..0000000
--- a/vendor/gopkg.in/ldap.v2/modify.go
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-//
-// File contains Modify functionality
-//
-// https://tools.ietf.org/html/rfc4511
-//
-// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
-//      object          LDAPDN,
-//      changes         SEQUENCE OF change SEQUENCE {
-//           operation       ENUMERATED {
-//                add     (0),
-//                delete  (1),
-//                replace (2),
-//                ...  },
-//           modification    PartialAttribute } }
-//
-// PartialAttribute ::= SEQUENCE {
-//      type       AttributeDescription,
-//      vals       SET OF value AttributeValue }
-//
-// AttributeDescription ::= LDAPString
-//                         -- Constrained to <attributedescription>
-//                         -- [RFC4512]
-//
-// AttributeValue ::= OCTET STRING
-//
-
-package ldap
-
-import (
-	"errors"
-	"log"
-
-	"gopkg.in/asn1-ber.v1"
-)
-
-// Change operation choices
-const (
-	AddAttribute     = 0
-	DeleteAttribute  = 1
-	ReplaceAttribute = 2
-)
-
-// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
-type PartialAttribute struct {
-	// Type is the type of the partial attribute
-	Type string
-	// Vals are the values of the partial attribute
-	Vals []string
-}
-
-func (p *PartialAttribute) encode() *ber.Packet {
-	seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute")
-	seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type"))
-	set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
-	for _, value := range p.Vals {
-		set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
-	}
-	seq.AppendChild(set)
-	return seq
-}
-
-// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
-type ModifyRequest struct {
-	// DN is the distinguishedName of the directory entry to modify
-	DN string
-	// AddAttributes contain the attributes to add
-	AddAttributes []PartialAttribute
-	// DeleteAttributes contain the attributes to delete
-	DeleteAttributes []PartialAttribute
-	// ReplaceAttributes contain the attributes to replace
-	ReplaceAttributes []PartialAttribute
-}
-
-// Add inserts the given attribute to the list of attributes to add
-func (m *ModifyRequest) Add(attrType string, attrVals []string) {
-	m.AddAttributes = append(m.AddAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
-}
-
-// Delete inserts the given attribute to the list of attributes to delete
-func (m *ModifyRequest) Delete(attrType string, attrVals []string) {
-	m.DeleteAttributes = append(m.DeleteAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
-}
-
-// Replace inserts the given attribute to the list of attributes to replace
-func (m *ModifyRequest) Replace(attrType string, attrVals []string) {
-	m.ReplaceAttributes = append(m.ReplaceAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
-}
-
-func (m ModifyRequest) encode() *ber.Packet {
-	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request")
-	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN"))
-	changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes")
-	for _, attribute := range m.AddAttributes {
-		change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
-		change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(AddAttribute), "Operation"))
-		change.AppendChild(attribute.encode())
-		changes.AppendChild(change)
-	}
-	for _, attribute := range m.DeleteAttributes {
-		change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
-		change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(DeleteAttribute), "Operation"))
-		change.AppendChild(attribute.encode())
-		changes.AppendChild(change)
-	}
-	for _, attribute := range m.ReplaceAttributes {
-		change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
-		change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ReplaceAttribute), "Operation"))
-		change.AppendChild(attribute.encode())
-		changes.AppendChild(change)
-	}
-	request.AppendChild(changes)
-	return request
-}
-
-// NewModifyRequest creates a modify request for the given DN
-func NewModifyRequest(
-	dn string,
-) *ModifyRequest {
-	return &ModifyRequest{
-		DN: dn,
-	}
-}
-
-// Modify performs the ModifyRequest
-func (l *Conn) Modify(modifyRequest *ModifyRequest) 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"))
-	packet.AppendChild(modifyRequest.encode())
-
-	l.Debug.PrintPacket(packet)
-
-	msgCtx, err := l.sendMessage(packet)
-	if err != nil {
-		return err
-	}
-	defer l.finishMessage(msgCtx)
-
-	l.Debug.Printf("%d: waiting for response", msgCtx.id)
-	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)
-	}
-
-	if packet.Children[1].Tag == ApplicationModifyResponse {
-		resultCode, resultDescription := getLDAPResultCode(packet)
-		if resultCode != 0 {
-			return NewError(resultCode, errors.New(resultDescription))
-		}
-	} else {
-		log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
-	}
-
-	l.Debug.Printf("%d: returning", msgCtx.id)
-	return nil
-}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 3b11c78..8361571 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,6 +1,9 @@
 # code.cloudfoundry.org/bytefmt v0.0.0-20180108190415-b31f603f5e1e
 ## explicit
 code.cloudfoundry.org/bytefmt
+# github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c
+## explicit
+github.com/Azure/go-ntlmssp
 # github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46
 ## explicit
 github.com/GehirnInc/crypt
@@ -59,6 +62,12 @@ github.com/go-acme/lego/lego
 github.com/go-acme/lego/log
 github.com/go-acme/lego/platform/wait
 github.com/go-acme/lego/registration
+# github.com/go-asn1-ber/asn1-ber v1.5.1
+## explicit; go 1.13
+github.com/go-asn1-ber/asn1-ber
+# github.com/go-ldap/ldap/v3 v3.4.1
+## explicit; go 1.13
+github.com/go-ldap/ldap/v3
 # github.com/go-ozzo/ozzo-validation v3.0.3-0.20180119232150-44af65fe9adf+incompatible
 ## explicit
 github.com/go-ozzo/ozzo-validation
@@ -193,6 +202,7 @@ go.etcd.io/bbolt
 ## explicit; go 1.17
 golang.org/x/crypto/ed25519
 golang.org/x/crypto/ed25519/internal/edwards25519
+golang.org/x/crypto/md4
 golang.org/x/crypto/ocsp
 golang.org/x/crypto/pbkdf2
 golang.org/x/crypto/scrypt
@@ -273,12 +283,6 @@ google.golang.org/protobuf/runtime/protoimpl
 # gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0
 ## explicit
 gopkg.in/DATA-DOG/go-sqlmock.v1
-# gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225
-## explicit
-gopkg.in/asn1-ber.v1
-# gopkg.in/ldap.v2 v2.5.1
-## explicit
-gopkg.in/ldap.v2
 # gopkg.in/square/go-jose.v2 v2.3.1
 ## explicit
 gopkg.in/square/go-jose.v2