mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-26 13:59:44 +01:00
Merge pull request #1142 from slingamn/removeldap
remove LDAP support from core
This commit is contained in:
commit
7af1f20223
@ -3,6 +3,10 @@
|
|||||||
# exclude vendor/
|
# exclude vendor/
|
||||||
SOURCES="./oragono.go ./irc"
|
SOURCES="./oragono.go ./irc"
|
||||||
|
|
||||||
|
if [ "$1" = "--fix" ]; then
|
||||||
|
exec gofmt -s -w $SOURCES
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -n "$(gofmt -s -l $SOURCES)" ]; then
|
if [ -n "$(gofmt -s -l $SOURCES)" ]; then
|
||||||
echo "Go code is not formatted correctly with \`gofmt -s\`:"
|
echo "Go code is not formatted correctly with \`gofmt -s\`:"
|
||||||
gofmt -s -d $SOURCES
|
gofmt -s -d $SOURCES
|
||||||
|
5
Makefile
5
Makefile
@ -1,4 +1,4 @@
|
|||||||
.PHONY: all install build release capdefs test smoke
|
.PHONY: all install build release capdefs test smoke gofmt
|
||||||
|
|
||||||
GIT_COMMIT := $(shell git rev-parse HEAD 2> /dev/null)
|
GIT_COMMIT := $(shell git rev-parse HEAD 2> /dev/null)
|
||||||
|
|
||||||
@ -36,3 +36,6 @@ test:
|
|||||||
smoke:
|
smoke:
|
||||||
oragono mkcerts --conf ./default.yaml || true
|
oragono mkcerts --conf ./default.yaml || true
|
||||||
oragono run --conf ./default.yaml --smoke
|
oragono run --conf ./default.yaml --smoke
|
||||||
|
|
||||||
|
gofmt:
|
||||||
|
./.check-gofmt.sh --fix
|
||||||
|
@ -456,41 +456,6 @@ accounts:
|
|||||||
# see /QUOTE HELP umodes for more user modes
|
# see /QUOTE HELP umodes for more user modes
|
||||||
# default-user-modes: +i
|
# default-user-modes: +i
|
||||||
|
|
||||||
# support for deferring password checking to an external LDAP server
|
|
||||||
# you should probably ignore this section! consult the grafana docs for details:
|
|
||||||
# https://grafana.com/docs/grafana/latest/auth/ldap/
|
|
||||||
# you will probably want to set require-sasl and disable accounts.registration.enabled
|
|
||||||
# ldap:
|
|
||||||
# enabled: true
|
|
||||||
# # should we automatically create users if their LDAP login succeeds?
|
|
||||||
# autocreate: true
|
|
||||||
# # example configuration that works with Forum Systems's testing server:
|
|
||||||
# # https://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
|
|
||||||
# host: "ldap.forumsys.com"
|
|
||||||
# port: 389
|
|
||||||
# timeout: 30s
|
|
||||||
# # example "single-bind" configuration, where we bind directly to the user's entry:
|
|
||||||
# bind-dn: "uid=%s,dc=example,dc=com"
|
|
||||||
# # example "admin bind" configuration, where we bind to an initial admin user,
|
|
||||||
# # then search for the user's entry with a search filter:
|
|
||||||
# #search-base-dns:
|
|
||||||
# # - "dc=example,dc=com"
|
|
||||||
# #bind-dn: "cn=read-only-admin,dc=example,dc=com"
|
|
||||||
# #bind-password: "password"
|
|
||||||
# #search-filter: "(uid=%s)"
|
|
||||||
# # example of requiring that users be in a particular group
|
|
||||||
# # (note that this is an OR over the listed groups, not an AND):
|
|
||||||
# #require-groups:
|
|
||||||
# # - "ou=mathematicians,dc=example,dc=com"
|
|
||||||
# #group-search-filter-user-attribute: "dn"
|
|
||||||
# #group-search-filter: "(uniqueMember=%s)"
|
|
||||||
# #group-search-base-dns:
|
|
||||||
# # - "dc=example,dc=com"
|
|
||||||
# # example of group membership testing via user attributes, as in AD
|
|
||||||
# # or with OpenLDAP's "memberOf overlay" (overrides group-search-filter):
|
|
||||||
# attributes:
|
|
||||||
# member-of: "memberOf"
|
|
||||||
|
|
||||||
# pluggable authentication mechanism, via subprocess invocation
|
# pluggable authentication mechanism, via subprocess invocation
|
||||||
# see the manual for details on how to write an authentication plugin script
|
# see the manual for details on how to write an authentication plugin script
|
||||||
auth-script:
|
auth-script:
|
||||||
|
35
default.yaml
35
default.yaml
@ -482,41 +482,6 @@ accounts:
|
|||||||
# see /QUOTE HELP umodes for more user modes
|
# see /QUOTE HELP umodes for more user modes
|
||||||
default-user-modes: +i
|
default-user-modes: +i
|
||||||
|
|
||||||
# support for deferring password checking to an external LDAP server
|
|
||||||
# you should probably ignore this section! consult the grafana docs for details:
|
|
||||||
# https://grafana.com/docs/grafana/latest/auth/ldap/
|
|
||||||
# you will probably want to set require-sasl and disable accounts.registration.enabled
|
|
||||||
# ldap:
|
|
||||||
# enabled: true
|
|
||||||
# # should we automatically create users if their LDAP login succeeds?
|
|
||||||
# autocreate: true
|
|
||||||
# # example configuration that works with Forum Systems's testing server:
|
|
||||||
# # https://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
|
|
||||||
# host: "ldap.forumsys.com"
|
|
||||||
# port: 389
|
|
||||||
# timeout: 30s
|
|
||||||
# # example "single-bind" configuration, where we bind directly to the user's entry:
|
|
||||||
# bind-dn: "uid=%s,dc=example,dc=com"
|
|
||||||
# # example "admin bind" configuration, where we bind to an initial admin user,
|
|
||||||
# # then search for the user's entry with a search filter:
|
|
||||||
# #search-base-dns:
|
|
||||||
# # - "dc=example,dc=com"
|
|
||||||
# #bind-dn: "cn=read-only-admin,dc=example,dc=com"
|
|
||||||
# #bind-password: "password"
|
|
||||||
# #search-filter: "(uid=%s)"
|
|
||||||
# # example of requiring that users be in a particular group
|
|
||||||
# # (note that this is an OR over the listed groups, not an AND):
|
|
||||||
# #require-groups:
|
|
||||||
# # - "ou=mathematicians,dc=example,dc=com"
|
|
||||||
# #group-search-filter-user-attribute: "dn"
|
|
||||||
# #group-search-filter: "(uniqueMember=%s)"
|
|
||||||
# #group-search-base-dns:
|
|
||||||
# # - "dc=example,dc=com"
|
|
||||||
# # example of group membership testing via user attributes, as in AD
|
|
||||||
# # or with OpenLDAP's "memberOf overlay" (overrides group-search-filter):
|
|
||||||
# attributes:
|
|
||||||
# member-of: "memberOf"
|
|
||||||
|
|
||||||
# pluggable authentication mechanism, via subprocess invocation
|
# pluggable authentication mechanism, via subprocess invocation
|
||||||
# see the manual for details on how to write an authentication plugin script
|
# see the manual for details on how to write an authentication plugin script
|
||||||
auth-script:
|
auth-script:
|
||||||
|
1
go.mod
1
go.mod
@ -6,7 +6,6 @@ require (
|
|||||||
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48
|
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||||
github.com/go-ldap/ldap/v3 v3.1.10
|
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
github.com/go-test/deep v1.0.6 // indirect
|
github.com/go-test/deep v1.0.6 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
|
4
go.sum
4
go.sum
@ -9,10 +9,6 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh
|
|||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.3.1 h1:gvPdv/Hr++TRFCl0UbPFHC54P9N9jgsRPnmnr419Uck=
|
|
||||||
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
|
||||||
github.com/go-ldap/ldap/v3 v3.1.10 h1:7WsKqasmPThNvdl0Q5GPpbTDD/ZD98CfuawrMIuh7qQ=
|
|
||||||
github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
|
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-test/deep v1.0.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8=
|
github.com/go-test/deep v1.0.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8=
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
|
|
||||||
"github.com/oragono/oragono/irc/connection_limits"
|
"github.com/oragono/oragono/irc/connection_limits"
|
||||||
"github.com/oragono/oragono/irc/email"
|
"github.com/oragono/oragono/irc/email"
|
||||||
"github.com/oragono/oragono/irc/ldap"
|
|
||||||
"github.com/oragono/oragono/irc/modes"
|
"github.com/oragono/oragono/irc/modes"
|
||||||
"github.com/oragono/oragono/irc/passwd"
|
"github.com/oragono/oragono/irc/passwd"
|
||||||
"github.com/oragono/oragono/irc/utils"
|
"github.com/oragono/oragono/irc/utils"
|
||||||
@ -1065,24 +1064,13 @@ func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName s
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
config := am.server.Config()
|
config := am.server.Config()
|
||||||
if config.Accounts.LDAP.Enabled {
|
|
||||||
ldapConf := am.server.Config().Accounts.LDAP
|
|
||||||
err = ldap.CheckLDAPPassphrase(ldapConf, accountName, passphrase, am.server.logger)
|
|
||||||
if err != nil {
|
|
||||||
account, err = am.loadWithAutocreation(accountName, ldapConf.Autocreate)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Accounts.AuthScript.Enabled {
|
if config.Accounts.AuthScript.Enabled {
|
||||||
var output AuthScriptOutput
|
var output AuthScriptOutput
|
||||||
output, err = CheckAuthScript(config.Accounts.AuthScript,
|
output, err = CheckAuthScript(config.Accounts.AuthScript,
|
||||||
AuthScriptInput{AccountName: accountName, Passphrase: passphrase, IP: client.IP().String()})
|
AuthScriptInput{AccountName: accountName, Passphrase: passphrase, IP: client.IP().String()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
|
am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
|
||||||
return err
|
} else if output.Success {
|
||||||
}
|
|
||||||
if output.Success {
|
|
||||||
if output.AccountName != "" {
|
if output.AccountName != "" {
|
||||||
accountName = output.AccountName
|
accountName = output.AccountName
|
||||||
}
|
}
|
||||||
@ -1419,9 +1407,7 @@ func (am *AccountManager) AuthenticateByCertFP(client *Client, certfp, authzid s
|
|||||||
AuthScriptInput{Certfp: certfp, IP: client.IP().String()})
|
AuthScriptInput{Certfp: certfp, IP: client.IP().String()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
|
am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
|
||||||
return err
|
} else if output.Success && output.AccountName != "" {
|
||||||
}
|
|
||||||
if output.Success && output.AccountName != "" {
|
|
||||||
clientAccount, err = am.loadWithAutocreation(output.AccountName, config.Accounts.AuthScript.Autocreate)
|
clientAccount, err = am.loadWithAutocreation(output.AccountName, config.Accounts.AuthScript.Autocreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
"github.com/oragono/oragono/irc/isupport"
|
"github.com/oragono/oragono/irc/isupport"
|
||||||
"github.com/oragono/oragono/irc/jwt"
|
"github.com/oragono/oragono/irc/jwt"
|
||||||
"github.com/oragono/oragono/irc/languages"
|
"github.com/oragono/oragono/irc/languages"
|
||||||
"github.com/oragono/oragono/irc/ldap"
|
|
||||||
"github.com/oragono/oragono/irc/logger"
|
"github.com/oragono/oragono/irc/logger"
|
||||||
"github.com/oragono/oragono/irc/modes"
|
"github.com/oragono/oragono/irc/modes"
|
||||||
"github.com/oragono/oragono/irc/mysql"
|
"github.com/oragono/oragono/irc/mysql"
|
||||||
@ -258,7 +257,6 @@ type AccountConfig struct {
|
|||||||
} `yaml:"require-sasl"`
|
} `yaml:"require-sasl"`
|
||||||
DefaultUserModes *string `yaml:"default-user-modes"`
|
DefaultUserModes *string `yaml:"default-user-modes"`
|
||||||
defaultUserModes modes.Modes
|
defaultUserModes modes.Modes
|
||||||
LDAP ldap.ServerConfig
|
|
||||||
LoginThrottling ThrottleConfig `yaml:"login-throttling"`
|
LoginThrottling ThrottleConfig `yaml:"login-throttling"`
|
||||||
SkipServerPassword bool `yaml:"skip-server-password"`
|
SkipServerPassword bool `yaml:"skip-server-password"`
|
||||||
LoginViaPassCommand bool `yaml:"login-via-pass-command"`
|
LoginViaPassCommand bool `yaml:"login-via-pass-command"`
|
||||||
|
202
irc/ldap/LICENSE
202
irc/ldap/LICENSE
@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2015 Grafana Labs
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
@ -1,62 +0,0 @@
|
|||||||
// Copyright 2014-2018 Grafana Labs
|
|
||||||
// Released under the Apache 2.0 license
|
|
||||||
|
|
||||||
// Modification notice:
|
|
||||||
// 1. All field names were changed from toml and snake case to yaml and kebab case,
|
|
||||||
// matching the Oragono project conventions
|
|
||||||
// 2. Four fields were added:
|
|
||||||
// 2.1 `Enabled`
|
|
||||||
// 2.2 `Autocreate`
|
|
||||||
// 2.3 `Timeout`
|
|
||||||
// 2.4 `RequireGroups`
|
|
||||||
|
|
||||||
// XXX: none of AttributeMap does anything in oragono, except MemberOf,
|
|
||||||
// which can be used to retrieve group memberships
|
|
||||||
|
|
||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ServerConfig struct {
|
|
||||||
Enabled bool
|
|
||||||
Autocreate bool
|
|
||||||
|
|
||||||
Host string
|
|
||||||
Port int
|
|
||||||
Timeout time.Duration
|
|
||||||
UseSSL bool `yaml:"use-ssl"`
|
|
||||||
StartTLS bool `yaml:"start-tls"`
|
|
||||||
SkipVerifySSL bool `yaml:"ssl-skip-verify"`
|
|
||||||
RootCACert string `yaml:"root-ca-cert"`
|
|
||||||
ClientCert string `yaml:"client-cert"`
|
|
||||||
ClientKey string `yaml:"client-key"`
|
|
||||||
|
|
||||||
BindDN string `yaml:"bind-dn"`
|
|
||||||
BindPassword string `yaml:"bind-password"`
|
|
||||||
SearchFilter string `yaml:"search-filter"`
|
|
||||||
SearchBaseDNs []string `yaml:"search-base-dns"`
|
|
||||||
|
|
||||||
// user validation: require them to be in any one of these groups
|
|
||||||
RequireGroups []string `yaml:"require-groups"`
|
|
||||||
|
|
||||||
// two ways of testing group membership:
|
|
||||||
// either by searching for groups that match the user's DN
|
|
||||||
// and testing their names:
|
|
||||||
GroupSearchFilter string `yaml:"group-search-filter"`
|
|
||||||
GroupSearchFilterUserAttribute string `yaml:"group-search-filter-user-attribute"`
|
|
||||||
GroupSearchBaseDNs []string `yaml:"group-search-base-dns"`
|
|
||||||
|
|
||||||
// or by an attribute on the user's DN, typically named 'memberOf', but customizable:
|
|
||||||
Attr AttributeMap `yaml:"attributes"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttributeMap is a struct representation for LDAP "attributes" setting
|
|
||||||
type AttributeMap struct {
|
|
||||||
Username string
|
|
||||||
Name string
|
|
||||||
Surname string
|
|
||||||
Email string
|
|
||||||
MemberOf string `yaml:"member-of"`
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
// Copyright 2014-2018 Grafana Labs
|
|
||||||
// Released under the Apache 2.0 license
|
|
||||||
|
|
||||||
// Modification notice:
|
|
||||||
// 1. `serverConn` was substituted for `Server` as the type of the server object
|
|
||||||
// 2. Debug loglines were altered to work with Oragono's logging system
|
|
||||||
|
|
||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
ldap "github.com/go-ldap/ldap/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrInvalidCredentials is returned if username and password do not match
|
|
||||||
ErrInvalidCredentials = errors.New("Invalid Username or Password")
|
|
||||||
|
|
||||||
// ErrCouldNotFindUser is returned when username hasn't been found (not username+password)
|
|
||||||
ErrCouldNotFindUser = errors.New("Can't find user in LDAP")
|
|
||||||
)
|
|
||||||
|
|
||||||
// shouldAdminBind checks if we should use
|
|
||||||
// admin username & password for LDAP bind
|
|
||||||
func (server *serverConn) shouldAdminBind() bool {
|
|
||||||
return server.Config.BindPassword != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// singleBindDN combines the bind with the username
|
|
||||||
// in order to get the proper path
|
|
||||||
func (server *serverConn) singleBindDN(username string) string {
|
|
||||||
return fmt.Sprintf(server.Config.BindDN, username)
|
|
||||||
}
|
|
||||||
|
|
||||||
// shouldSingleBind checks if we can use "single bind" approach
|
|
||||||
func (server *serverConn) shouldSingleBind() bool {
|
|
||||||
return strings.Contains(server.Config.BindDN, "%s")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial dials in the LDAP
|
|
||||||
// TODO: decrease cyclomatic complexity
|
|
||||||
func (server *serverConn) Dial() error {
|
|
||||||
var err error
|
|
||||||
var certPool *x509.CertPool
|
|
||||||
if server.Config.RootCACert != "" {
|
|
||||||
certPool = x509.NewCertPool()
|
|
||||||
for _, caCertFile := range strings.Split(server.Config.RootCACert, " ") {
|
|
||||||
pem, err := ioutil.ReadFile(caCertFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !certPool.AppendCertsFromPEM(pem) {
|
|
||||||
return errors.New("Failed to append CA certificate " + caCertFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var clientCert tls.Certificate
|
|
||||||
if server.Config.ClientCert != "" && server.Config.ClientKey != "" {
|
|
||||||
clientCert, err = tls.LoadX509KeyPair(server.Config.ClientCert, server.Config.ClientKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, host := range strings.Split(server.Config.Host, " ") {
|
|
||||||
address := fmt.Sprintf("%s:%d", host, server.Config.Port)
|
|
||||||
if server.Config.UseSSL {
|
|
||||||
tlsCfg := &tls.Config{
|
|
||||||
InsecureSkipVerify: server.Config.SkipVerifySSL,
|
|
||||||
ServerName: host,
|
|
||||||
RootCAs: certPool,
|
|
||||||
}
|
|
||||||
if len(clientCert.Certificate) > 0 {
|
|
||||||
tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
|
|
||||||
}
|
|
||||||
if server.Config.StartTLS {
|
|
||||||
server.Connection, err = ldap.Dial("tcp", address)
|
|
||||||
if err == nil {
|
|
||||||
if err = server.Connection.StartTLS(tlsCfg); err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
server.Connection, err = ldap.DialTLS("tcp", address, tlsCfg)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
server.Connection, err = ldap.Dial("tcp", address)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the LDAP connection
|
|
||||||
// Dial() sets the connection with the server for this Struct. Therefore, we require a
|
|
||||||
// call to Dial() before being able to execute this function.
|
|
||||||
func (server *serverConn) Close() {
|
|
||||||
server.Connection.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// userBind binds the user with the LDAP server
|
|
||||||
func (server *serverConn) userBind(path, password string) error {
|
|
||||||
err := server.Connection.Bind(path, password)
|
|
||||||
if err != nil {
|
|
||||||
if ldapErr, ok := err.(*ldap.Error); ok {
|
|
||||||
if ldapErr.ResultCode == 49 {
|
|
||||||
return ErrInvalidCredentials
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// users is helper method for the Users()
|
|
||||||
func (server *serverConn) users(logins []string) (
|
|
||||||
[]*ldap.Entry,
|
|
||||||
error,
|
|
||||||
) {
|
|
||||||
var result *ldap.SearchResult
|
|
||||||
var Config = server.Config
|
|
||||||
var err error
|
|
||||||
|
|
||||||
for _, base := range Config.SearchBaseDNs {
|
|
||||||
result, err = server.Connection.Search(
|
|
||||||
server.getSearchRequest(base, logins),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(result.Entries) > 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.Entries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getSearchRequest returns LDAP search request for users
|
|
||||||
func (server *serverConn) getSearchRequest(
|
|
||||||
base string,
|
|
||||||
logins []string,
|
|
||||||
) *ldap.SearchRequest {
|
|
||||||
attributes := []string{}
|
|
||||||
|
|
||||||
inputs := server.Config.Attr
|
|
||||||
attributes = appendIfNotEmpty(
|
|
||||||
attributes,
|
|
||||||
inputs.Username,
|
|
||||||
inputs.Surname,
|
|
||||||
inputs.Email,
|
|
||||||
inputs.Name,
|
|
||||||
inputs.MemberOf,
|
|
||||||
|
|
||||||
// In case for the POSIX LDAP schema server
|
|
||||||
server.Config.GroupSearchFilterUserAttribute,
|
|
||||||
)
|
|
||||||
|
|
||||||
search := ""
|
|
||||||
for _, login := range logins {
|
|
||||||
query := strings.Replace(
|
|
||||||
server.Config.SearchFilter,
|
|
||||||
"%s", ldap.EscapeFilter(login),
|
|
||||||
-1,
|
|
||||||
)
|
|
||||||
|
|
||||||
search = search + query
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := fmt.Sprintf("(|%s)", search)
|
|
||||||
|
|
||||||
return &ldap.SearchRequest{
|
|
||||||
BaseDN: base,
|
|
||||||
Scope: ldap.ScopeWholeSubtree,
|
|
||||||
DerefAliases: ldap.NeverDerefAliases,
|
|
||||||
Attributes: attributes,
|
|
||||||
Filter: filter,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// requestMemberOf use this function when POSIX LDAP
|
|
||||||
// schema does not support memberOf, so it manually search the groups
|
|
||||||
func (server *serverConn) requestMemberOf(entry *ldap.Entry) ([]string, error) {
|
|
||||||
var memberOf []string
|
|
||||||
var config = server.Config
|
|
||||||
|
|
||||||
for _, groupSearchBase := range config.GroupSearchBaseDNs {
|
|
||||||
var filterReplace string
|
|
||||||
if config.GroupSearchFilterUserAttribute == "" {
|
|
||||||
filterReplace = getAttribute(config.Attr.Username, entry)
|
|
||||||
} else {
|
|
||||||
filterReplace = getAttribute(
|
|
||||||
config.GroupSearchFilterUserAttribute,
|
|
||||||
entry,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := strings.Replace(
|
|
||||||
config.GroupSearchFilter, "%s",
|
|
||||||
ldap.EscapeFilter(filterReplace),
|
|
||||||
-1,
|
|
||||||
)
|
|
||||||
|
|
||||||
server.logger.Debug("ldap", "Searching for groups with filter", filter)
|
|
||||||
|
|
||||||
// support old way of reading settings
|
|
||||||
groupIDAttribute := config.Attr.MemberOf
|
|
||||||
// but prefer dn attribute if default settings are used
|
|
||||||
if groupIDAttribute == "" || groupIDAttribute == "memberOf" {
|
|
||||||
groupIDAttribute = "dn"
|
|
||||||
}
|
|
||||||
|
|
||||||
groupSearchReq := ldap.SearchRequest{
|
|
||||||
BaseDN: groupSearchBase,
|
|
||||||
Scope: ldap.ScopeWholeSubtree,
|
|
||||||
DerefAliases: ldap.NeverDerefAliases,
|
|
||||||
Attributes: []string{groupIDAttribute},
|
|
||||||
Filter: filter,
|
|
||||||
}
|
|
||||||
|
|
||||||
groupSearchResult, err := server.Connection.Search(&groupSearchReq)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(groupSearchResult.Entries) > 0 {
|
|
||||||
for _, group := range groupSearchResult.Entries {
|
|
||||||
|
|
||||||
memberOf = append(
|
|
||||||
memberOf,
|
|
||||||
getAttribute(groupIDAttribute, group),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return memberOf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMemberOf finds memberOf property or request it
|
|
||||||
func (server *serverConn) getMemberOf(result *ldap.Entry) (
|
|
||||||
[]string, error,
|
|
||||||
) {
|
|
||||||
if server.Config.GroupSearchFilter == "" {
|
|
||||||
memberOf := getArrayAttribute(server.Config.Attr.MemberOf, result)
|
|
||||||
|
|
||||||
return memberOf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
memberOf, err := server.requestMemberOf(result)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return memberOf, nil
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
// Copyright 2014-2018 Grafana Labs
|
|
||||||
// Released under the Apache 2.0 license
|
|
||||||
|
|
||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
ldap "github.com/go-ldap/ldap/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func isMemberOf(memberOf []string, group string) bool {
|
|
||||||
if group == "*" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, member := range memberOf {
|
|
||||||
if strings.EqualFold(member, group) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func getArrayAttribute(name string, entry *ldap.Entry) []string {
|
|
||||||
if strings.ToLower(name) == "dn" {
|
|
||||||
return []string{entry.DN}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, attr := range entry.Attributes {
|
|
||||||
if attr.Name == name && len(attr.Values) > 0 {
|
|
||||||
return attr.Values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAttribute(name string, entry *ldap.Entry) string {
|
|
||||||
if strings.ToLower(name) == "dn" {
|
|
||||||
return entry.DN
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, attr := range entry.Attributes {
|
|
||||||
if attr.Name == name {
|
|
||||||
if len(attr.Values) > 0 {
|
|
||||||
return attr.Values[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendIfNotEmpty(slice []string, values ...string) []string {
|
|
||||||
for _, v := range values {
|
|
||||||
if v != "" {
|
|
||||||
slice = append(slice, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return slice
|
|
||||||
}
|
|
@ -1,152 +0,0 @@
|
|||||||
// Copyright (c) 2020 Matt Ouille
|
|
||||||
// Copyright (c) 2020 Shivaram Lingamneni
|
|
||||||
// released under the MIT license
|
|
||||||
|
|
||||||
// Portions of this code copyright Grafana Labs and contributors
|
|
||||||
// and released under the Apache 2.0 license
|
|
||||||
|
|
||||||
// Copying Grafana's original comment on the different cases for LDAP:
|
|
||||||
// There are several cases -
|
|
||||||
// 1. "admin" user
|
|
||||||
// Bind the "admin" user (defined in Grafana config file) which has the search privileges
|
|
||||||
// in LDAP server, then we search the targeted user through that bind, then the second
|
|
||||||
// perform the bind via passed login/password.
|
|
||||||
// 2. Single bind
|
|
||||||
// // If all the users meant to be used with Grafana have the ability to search in LDAP server
|
|
||||||
// then we bind with LDAP server with targeted login/password
|
|
||||||
// and then search for the said user in order to retrive all the information about them
|
|
||||||
// 3. Unauthenticated bind
|
|
||||||
// For some LDAP configurations it is allowed to search the
|
|
||||||
// user without login/password binding with LDAP server, in such case
|
|
||||||
// we will perform "unauthenticated bind", then search for the
|
|
||||||
// targeted user and then perform the bind with passed login/password.
|
|
||||||
|
|
||||||
// Note: the only validation we do on users is to check RequiredGroups.
|
|
||||||
// If RequiredGroups is not set and we can do a single bind, we don't
|
|
||||||
// even need to search. So our case 2 is not restricted
|
|
||||||
// to setups where all the users have search privileges: we only need to
|
|
||||||
// be able to do DN resolution via pure string substitution.
|
|
||||||
|
|
||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
ldap "github.com/go-ldap/ldap/v3"
|
|
||||||
|
|
||||||
"github.com/oragono/oragono/irc/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrUserNotInRequiredGroup = errors.New("User is not a member of any required groups")
|
|
||||||
)
|
|
||||||
|
|
||||||
// equivalent of Grafana's `Server`, but unexported
|
|
||||||
// also, `log` was renamed to `logger`, since the APIs are slightly different
|
|
||||||
// and this way the compiler will catch any unchanged references to Grafana's `Server.log`
|
|
||||||
type serverConn struct {
|
|
||||||
Config *ServerConfig
|
|
||||||
Connection *ldap.Conn
|
|
||||||
logger *logger.Manager
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckLDAPPassphrase(config ServerConfig, accountName, passphrase string, log *logger.Manager) (err error) {
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("ldap", "failed passphrase check", err.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
server := serverConn{
|
|
||||||
Config: &config,
|
|
||||||
logger: log,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = server.Dial()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
server.Connection.SetTimeout(config.Timeout)
|
|
||||||
|
|
||||||
passphraseChecked := false
|
|
||||||
|
|
||||||
if server.shouldSingleBind() {
|
|
||||||
log.Debug("ldap", "attempting single bind to", accountName)
|
|
||||||
err = server.userBind(server.singleBindDN(accountName), passphrase)
|
|
||||||
passphraseChecked = (err == nil)
|
|
||||||
} else if server.shouldAdminBind() {
|
|
||||||
log.Debug("ldap", "attempting admin bind to", config.BindDN)
|
|
||||||
err = server.userBind(config.BindDN, config.BindPassword)
|
|
||||||
} else {
|
|
||||||
log.Debug("ldap", "attempting unauthenticated bind")
|
|
||||||
err = server.Connection.UnauthenticatedBind(config.BindDN)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if passphraseChecked && len(config.RequireGroups) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
users, err := server.users([]string{accountName})
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("ldap", "failed user lookup")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(users) == 0 {
|
|
||||||
return ErrCouldNotFindUser
|
|
||||||
}
|
|
||||||
|
|
||||||
user := users[0]
|
|
||||||
|
|
||||||
log.Debug("ldap", "looked up user", user.DN)
|
|
||||||
|
|
||||||
err = server.validateGroupMembership(user)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !passphraseChecked {
|
|
||||||
log.Debug("ldap", "rebinding", user.DN)
|
|
||||||
err = server.userBind(user.DN, passphrase)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (server *serverConn) validateGroupMembership(user *ldap.Entry) (err error) {
|
|
||||||
if len(server.Config.RequireGroups) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var memberOf []string
|
|
||||||
memberOf, err = server.getMemberOf(user)
|
|
||||||
if err != nil {
|
|
||||||
server.logger.Debug("ldap", "could not retrieve group memberships", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
server.logger.Debug("ldap", fmt.Sprintf("found group memberships: %v", memberOf))
|
|
||||||
foundGroup := false
|
|
||||||
for _, inGroup := range memberOf {
|
|
||||||
for _, acceptableGroup := range server.Config.RequireGroups {
|
|
||||||
if inGroup == acceptableGroup {
|
|
||||||
foundGroup = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if foundGroup {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if foundGroup {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return ErrUserNotInRequiredGroup
|
|
||||||
}
|
|
||||||
}
|
|
38
vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml
generated
vendored
38
vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml
generated
vendored
@ -1,38 +0,0 @@
|
|||||||
language: go
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- go: 1.2.x
|
|
||||||
env: GOOS=linux GOARCH=amd64
|
|
||||||
- go: 1.2.x
|
|
||||||
env: GOOS=linux GOARCH=386
|
|
||||||
- go: 1.2.x
|
|
||||||
env: GOOS=windows GOARCH=amd64
|
|
||||||
- go: 1.2.x
|
|
||||||
env: GOOS=windows GOARCH=386
|
|
||||||
- go: 1.3.x
|
|
||||||
- go: 1.4.x
|
|
||||||
- go: 1.5.x
|
|
||||||
- go: 1.6.x
|
|
||||||
- go: 1.7.x
|
|
||||||
- go: 1.8.x
|
|
||||||
- go: 1.9.x
|
|
||||||
- go: 1.10.x
|
|
||||||
- go: 1.11.x
|
|
||||||
- go: 1.12.x
|
|
||||||
- go: 1.13.x
|
|
||||||
env: GOOS=linux GOARCH=amd64
|
|
||||||
- go: 1.13.x
|
|
||||||
env: GOOS=linux GOARCH=386
|
|
||||||
- go: 1.13.x
|
|
||||||
env: GOOS=windows GOARCH=amd64
|
|
||||||
- go: 1.13.x
|
|
||||||
env: GOOS=windows GOARCH=386
|
|
||||||
- go: tip
|
|
||||||
go_import_path: gopkg.in/asn-ber.v1
|
|
||||||
install:
|
|
||||||
- go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
|
|
||||||
- go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
|
|
||||||
- go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover
|
|
||||||
- go build -v ./...
|
|
||||||
script:
|
|
||||||
- go test -v -cover ./... || go test -v ./...
|
|
22
vendor/github.com/go-asn1-ber/asn1-ber/LICENSE
generated
vendored
22
vendor/github.com/go-asn1-ber/asn1-ber/LICENSE
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
|
|
||||||
Portions copyright (c) 2015-2016 go-asn1-ber Authors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
24
vendor/github.com/go-asn1-ber/asn1-ber/README.md
generated
vendored
24
vendor/github.com/go-asn1-ber/asn1-ber/README.md
generated
vendored
@ -1,24 +0,0 @@
|
|||||||
[![GoDoc](https://godoc.org/gopkg.in/asn1-ber.v1?status.svg)](https://godoc.org/gopkg.in/asn1-ber.v1) [![Build Status](https://travis-ci.org/go-asn1-ber/asn1-ber.svg)](https://travis-ci.org/go-asn1-ber/asn1-ber)
|
|
||||||
|
|
||||||
|
|
||||||
ASN1 BER Encoding / Decoding Library for the GO programming language.
|
|
||||||
---------------------------------------------------------------------
|
|
||||||
|
|
||||||
Required libraries:
|
|
||||||
None
|
|
||||||
|
|
||||||
Working:
|
|
||||||
Very basic encoding / decoding needed for LDAP protocol
|
|
||||||
|
|
||||||
Tests Implemented:
|
|
||||||
A few
|
|
||||||
|
|
||||||
TODO:
|
|
||||||
Fix all encoding / decoding to conform to ASN1 BER spec
|
|
||||||
Implement Tests / Benchmarks
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
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
|
|
512
vendor/github.com/go-asn1-ber/asn1-ber/ber.go
generated
vendored
512
vendor/github.com/go-asn1-ber/asn1-ber/ber.go
generated
vendored
@ -1,512 +0,0 @@
|
|||||||
package ber
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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{}
|
|
||||||
ByteValue []byte
|
|
||||||
Data *bytes.Buffer
|
|
||||||
Children []*Packet
|
|
||||||
Description string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Identifier struct {
|
|
||||||
ClassType Class
|
|
||||||
TagType Type
|
|
||||||
Tag Tag
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tag uint64
|
|
||||||
|
|
||||||
const (
|
|
||||||
TagEOC Tag = 0x00
|
|
||||||
TagBoolean Tag = 0x01
|
|
||||||
TagInteger Tag = 0x02
|
|
||||||
TagBitString Tag = 0x03
|
|
||||||
TagOctetString Tag = 0x04
|
|
||||||
TagNULL Tag = 0x05
|
|
||||||
TagObjectIdentifier Tag = 0x06
|
|
||||||
TagObjectDescriptor Tag = 0x07
|
|
||||||
TagExternal Tag = 0x08
|
|
||||||
TagRealFloat Tag = 0x09
|
|
||||||
TagEnumerated Tag = 0x0a
|
|
||||||
TagEmbeddedPDV Tag = 0x0b
|
|
||||||
TagUTF8String Tag = 0x0c
|
|
||||||
TagRelativeOID Tag = 0x0d
|
|
||||||
TagSequence Tag = 0x10
|
|
||||||
TagSet Tag = 0x11
|
|
||||||
TagNumericString Tag = 0x12
|
|
||||||
TagPrintableString Tag = 0x13
|
|
||||||
TagT61String Tag = 0x14
|
|
||||||
TagVideotexString Tag = 0x15
|
|
||||||
TagIA5String Tag = 0x16
|
|
||||||
TagUTCTime Tag = 0x17
|
|
||||||
TagGeneralizedTime Tag = 0x18
|
|
||||||
TagGraphicString Tag = 0x19
|
|
||||||
TagVisibleString Tag = 0x1a
|
|
||||||
TagGeneralString Tag = 0x1b
|
|
||||||
TagUniversalString Tag = 0x1c
|
|
||||||
TagCharacterString Tag = 0x1d
|
|
||||||
TagBMPString Tag = 0x1e
|
|
||||||
TagBitmask Tag = 0x1f // xxx11111b
|
|
||||||
|
|
||||||
// HighTag indicates the start of a high-tag byte sequence
|
|
||||||
HighTag Tag = 0x1f // xxx11111b
|
|
||||||
// HighTagContinueBitmask indicates the high-tag byte sequence should continue
|
|
||||||
HighTagContinueBitmask Tag = 0x80 // 10000000b
|
|
||||||
// HighTagValueBitmask obtains the tag value from a high-tag byte sequence byte
|
|
||||||
HighTagValueBitmask Tag = 0x7f // 01111111b
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// LengthLongFormBitmask is the mask to apply to the length byte to see if a long-form byte sequence is used
|
|
||||||
LengthLongFormBitmask = 0x80
|
|
||||||
// LengthValueBitmask is the mask to apply to the length byte to get the number of bytes in the long-form byte sequence
|
|
||||||
LengthValueBitmask = 0x7f
|
|
||||||
|
|
||||||
// LengthIndefinite is returned from readLength to indicate an indefinite length
|
|
||||||
LengthIndefinite = -1
|
|
||||||
)
|
|
||||||
|
|
||||||
var tagMap = map[Tag]string{
|
|
||||||
TagEOC: "EOC (End-of-Content)",
|
|
||||||
TagBoolean: "Boolean",
|
|
||||||
TagInteger: "Integer",
|
|
||||||
TagBitString: "Bit String",
|
|
||||||
TagOctetString: "Octet String",
|
|
||||||
TagNULL: "NULL",
|
|
||||||
TagObjectIdentifier: "Object Identifier",
|
|
||||||
TagObjectDescriptor: "Object Descriptor",
|
|
||||||
TagExternal: "External",
|
|
||||||
TagRealFloat: "Real (float)",
|
|
||||||
TagEnumerated: "Enumerated",
|
|
||||||
TagEmbeddedPDV: "Embedded PDV",
|
|
||||||
TagUTF8String: "UTF8 String",
|
|
||||||
TagRelativeOID: "Relative-OID",
|
|
||||||
TagSequence: "Sequence and Sequence of",
|
|
||||||
TagSet: "Set and Set OF",
|
|
||||||
TagNumericString: "Numeric String",
|
|
||||||
TagPrintableString: "Printable String",
|
|
||||||
TagT61String: "T61 String",
|
|
||||||
TagVideotexString: "Videotex String",
|
|
||||||
TagIA5String: "IA5 String",
|
|
||||||
TagUTCTime: "UTC Time",
|
|
||||||
TagGeneralizedTime: "Generalized Time",
|
|
||||||
TagGraphicString: "Graphic String",
|
|
||||||
TagVisibleString: "Visible String",
|
|
||||||
TagGeneralString: "General String",
|
|
||||||
TagUniversalString: "Universal String",
|
|
||||||
TagCharacterString: "Character String",
|
|
||||||
TagBMPString: "BMP String",
|
|
||||||
}
|
|
||||||
|
|
||||||
type Class uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
ClassUniversal Class = 0 // 00xxxxxxb
|
|
||||||
ClassApplication Class = 64 // 01xxxxxxb
|
|
||||||
ClassContext Class = 128 // 10xxxxxxb
|
|
||||||
ClassPrivate Class = 192 // 11xxxxxxb
|
|
||||||
ClassBitmask Class = 192 // 11xxxxxxb
|
|
||||||
)
|
|
||||||
|
|
||||||
var ClassMap = map[Class]string{
|
|
||||||
ClassUniversal: "Universal",
|
|
||||||
ClassApplication: "Application",
|
|
||||||
ClassContext: "Context",
|
|
||||||
ClassPrivate: "Private",
|
|
||||||
}
|
|
||||||
|
|
||||||
type Type uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
TypePrimitive Type = 0 // xx0xxxxxb
|
|
||||||
TypeConstructed Type = 32 // xx1xxxxxb
|
|
||||||
TypeBitmask Type = 32 // xx1xxxxxb
|
|
||||||
)
|
|
||||||
|
|
||||||
var TypeMap = map[Type]string{
|
|
||||||
TypePrimitive: "Primitive",
|
|
||||||
TypeConstructed: "Constructed",
|
|
||||||
}
|
|
||||||
|
|
||||||
var Debug bool = 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)
|
|
||||||
|
|
||||||
for i, b := range buf {
|
|
||||||
data_lines[i/30] += fmt.Sprintf("%02x ", b)
|
|
||||||
num_lines[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"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func PrintPacket(p *Packet) {
|
|
||||||
printPacket(os.Stdout, p, 0, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
|
|
||||||
indent_str := ""
|
|
||||||
|
|
||||||
for len(indent_str) != indent {
|
|
||||||
indent_str += " "
|
|
||||||
}
|
|
||||||
|
|
||||||
class_str := ClassMap[p.ClassType]
|
|
||||||
|
|
||||||
tagtype_str := TypeMap[p.TagType]
|
|
||||||
|
|
||||||
tag_str := fmt.Sprintf("0x%02X", p.Tag)
|
|
||||||
|
|
||||||
if p.ClassType == ClassUniversal {
|
|
||||||
tag_str = tagMap[p.Tag]
|
|
||||||
}
|
|
||||||
|
|
||||||
value := fmt.Sprint(p.Value)
|
|
||||||
description := ""
|
|
||||||
|
|
||||||
if p.Description != "" {
|
|
||||||
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)
|
|
||||||
|
|
||||||
if printBytes {
|
|
||||||
PrintBytes(out, p.Bytes(), indent_str)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range p.Children {
|
|
||||||
printPacket(out, child, indent+1, printBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPacket reads a single Packet from the reader
|
|
||||||
func ReadPacket(reader io.Reader) (*Packet, error) {
|
|
||||||
p, _, err := readPacket(reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DecodeString(data []byte) string {
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
|
|
||||||
ret <<= 8
|
|
||||||
ret |= int64(bytes[bytesRead])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shift up and down in order to sign extend the result.
|
|
||||||
ret <<= 64 - uint8(len(bytes))*8
|
|
||||||
ret >>= 64 - uint8(len(bytes))*8
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeInteger(i int64) []byte {
|
|
||||||
n := int64Length(i)
|
|
||||||
out := make([]byte, n)
|
|
||||||
|
|
||||||
var j int
|
|
||||||
for ; n > 0; n-- {
|
|
||||||
out[j] = (byte(i >> uint((n-1)*8)))
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func int64Length(i int64) (numBytes int) {
|
|
||||||
numBytes = 1
|
|
||||||
|
|
||||||
for i > 127 {
|
|
||||||
numBytes++
|
|
||||||
i >>= 8
|
|
||||||
}
|
|
||||||
|
|
||||||
for i < -128 {
|
|
||||||
numBytes++
|
|
||||||
i >>= 8
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodePacket decodes the given bytes into a single Packet
|
|
||||||
// If a decode error is encountered, nil is returned.
|
|
||||||
func DecodePacket(data []byte) *Packet {
|
|
||||||
p, _, _ := readPacket(bytes.NewBuffer(data))
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodePacketErr decodes the given bytes into a single Packet
|
|
||||||
// If a decode error is encountered, nil is returned
|
|
||||||
func DecodePacketErr(data []byte) (*Packet, error) {
|
|
||||||
p, _, err := readPacket(bytes.NewBuffer(data))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
return nil, read, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p := &Packet{
|
|
||||||
Identifier: identifier,
|
|
||||||
}
|
|
||||||
|
|
||||||
p.Data = new(bytes.Buffer)
|
|
||||||
p.Children = make([]*Packet, 0, 2)
|
|
||||||
p.Value = nil
|
|
||||||
|
|
||||||
if p.TagType == TypeConstructed {
|
|
||||||
// TODO: if universal, ensure tag type is allowed to be constructed
|
|
||||||
|
|
||||||
// Track how much content we've read
|
|
||||||
contentRead := 0
|
|
||||||
for {
|
|
||||||
if length != LengthIndefinite {
|
|
||||||
// End if we've read what we've been told to
|
|
||||||
if contentRead == length {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Detect if a packet boundary didn't fall on the expected length
|
|
||||||
if contentRead > length {
|
|
||||||
return nil, read, fmt.Errorf("expected to read %d bytes, read %d", length, contentRead)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the next packet
|
|
||||||
child, r, err := readPacket(reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, read, err
|
|
||||||
}
|
|
||||||
contentRead += r
|
|
||||||
read += r
|
|
||||||
|
|
||||||
// Test is this is the EOC marker for our packet
|
|
||||||
if isEOCPacket(child) {
|
|
||||||
if length == LengthIndefinite {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return nil, read, errors.New("eoc child not allowed with definite length")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append and continue
|
|
||||||
p.AppendChild(child)
|
|
||||||
}
|
|
||||||
return p, read, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if length == LengthIndefinite {
|
|
||||||
return nil, read, errors.New("indefinite length used with primitive type")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read definite-length content
|
|
||||||
if MaxPacketLengthBytes > 0 && int64(length) > MaxPacketLengthBytes {
|
|
||||||
return nil, read, fmt.Errorf("length %d greater than maximum %d", length, MaxPacketLengthBytes)
|
|
||||||
}
|
|
||||||
content := make([]byte, length, length)
|
|
||||||
if length > 0 {
|
|
||||||
_, err := io.ReadFull(reader, content)
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil, read, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return nil, read, err
|
|
||||||
}
|
|
||||||
read += length
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.ClassType == ClassUniversal {
|
|
||||||
p.Data.Write(content)
|
|
||||||
p.ByteValue = content
|
|
||||||
|
|
||||||
switch p.Tag {
|
|
||||||
case TagEOC:
|
|
||||||
case TagBoolean:
|
|
||||||
val, _ := ParseInt64(content)
|
|
||||||
|
|
||||||
p.Value = val != 0
|
|
||||||
case TagInteger:
|
|
||||||
p.Value, _ = ParseInt64(content)
|
|
||||||
case TagBitString:
|
|
||||||
case TagOctetString:
|
|
||||||
// the actual string encoding is not known here
|
|
||||||
// (e.g. for LDAP content is already an UTF8-encoded
|
|
||||||
// string). Return the data without further processing
|
|
||||||
p.Value = DecodeString(content)
|
|
||||||
case TagNULL:
|
|
||||||
case TagObjectIdentifier:
|
|
||||||
case TagObjectDescriptor:
|
|
||||||
case TagExternal:
|
|
||||||
case TagRealFloat:
|
|
||||||
case TagEnumerated:
|
|
||||||
p.Value, _ = ParseInt64(content)
|
|
||||||
case TagEmbeddedPDV:
|
|
||||||
case TagUTF8String:
|
|
||||||
p.Value = DecodeString(content)
|
|
||||||
case TagRelativeOID:
|
|
||||||
case TagSequence:
|
|
||||||
case TagSet:
|
|
||||||
case TagNumericString:
|
|
||||||
case TagPrintableString:
|
|
||||||
p.Value = DecodeString(content)
|
|
||||||
case TagT61String:
|
|
||||||
case TagVideotexString:
|
|
||||||
case TagIA5String:
|
|
||||||
case TagUTCTime:
|
|
||||||
case TagGeneralizedTime:
|
|
||||||
case TagGraphicString:
|
|
||||||
case TagVisibleString:
|
|
||||||
case TagGeneralString:
|
|
||||||
case TagUniversalString:
|
|
||||||
case TagCharacterString:
|
|
||||||
case TagBMPString:
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p.Data.Write(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
return p, read, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Packet) Bytes() []byte {
|
|
||||||
var out bytes.Buffer
|
|
||||||
|
|
||||||
out.Write(encodeIdentifier(p.Identifier))
|
|
||||||
out.Write(encodeLength(p.Data.Len()))
|
|
||||||
out.Write(p.Data.Bytes())
|
|
||||||
|
|
||||||
return out.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Packet) AppendChild(child *Packet) {
|
|
||||||
p.Data.Write(child.Bytes())
|
|
||||||
p.Children = append(p.Children, child)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.Data = new(bytes.Buffer)
|
|
||||||
|
|
||||||
p.Children = make([]*Packet, 0, 2)
|
|
||||||
|
|
||||||
p.Value = Value
|
|
||||||
p.Description = Description
|
|
||||||
|
|
||||||
if Value != nil {
|
|
||||||
v := reflect.ValueOf(Value)
|
|
||||||
|
|
||||||
if ClassType == ClassUniversal {
|
|
||||||
switch Tag {
|
|
||||||
case TagOctetString:
|
|
||||||
sv, ok := v.Interface().(string)
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
p.Data.Write([]byte(sv))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
intValue := int64(0)
|
|
||||||
|
|
||||||
if Value {
|
|
||||||
intValue = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
p := Encode(ClassType, TagType, Tag, nil, Description)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
p.Value = Value
|
|
||||||
switch v := Value.(type) {
|
|
||||||
case int:
|
|
||||||
p.Data.Write(encodeInteger(int64(v)))
|
|
||||||
case uint:
|
|
||||||
p.Data.Write(encodeInteger(int64(v)))
|
|
||||||
case int64:
|
|
||||||
p.Data.Write(encodeInteger(v))
|
|
||||||
case uint64:
|
|
||||||
// TODO : check range or add encodeUInt...
|
|
||||||
p.Data.Write(encodeInteger(int64(v)))
|
|
||||||
case int32:
|
|
||||||
p.Data.Write(encodeInteger(int64(v)))
|
|
||||||
case uint32:
|
|
||||||
p.Data.Write(encodeInteger(int64(v)))
|
|
||||||
case int16:
|
|
||||||
p.Data.Write(encodeInteger(int64(v)))
|
|
||||||
case uint16:
|
|
||||||
p.Data.Write(encodeInteger(int64(v)))
|
|
||||||
case int8:
|
|
||||||
p.Data.Write(encodeInteger(int64(v)))
|
|
||||||
case uint8:
|
|
||||||
p.Data.Write(encodeInteger(int64(v)))
|
|
||||||
default:
|
|
||||||
// TODO : add support for big.Int ?
|
|
||||||
panic(fmt.Sprintf("Invalid type %T, expected {u|}int{64|32|16|8}", v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
25
vendor/github.com/go-asn1-ber/asn1-ber/content_int.go
generated
vendored
25
vendor/github.com/go-asn1-ber/asn1-ber/content_int.go
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
package ber
|
|
||||||
|
|
||||||
func encodeUnsignedInteger(i uint64) []byte {
|
|
||||||
n := uint64Length(i)
|
|
||||||
out := make([]byte, n)
|
|
||||||
|
|
||||||
var j int
|
|
||||||
for ; n > 0; n-- {
|
|
||||||
out[j] = (byte(i >> uint((n-1)*8)))
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func uint64Length(i uint64) (numBytes int) {
|
|
||||||
numBytes = 1
|
|
||||||
|
|
||||||
for i > 255 {
|
|
||||||
numBytes++
|
|
||||||
i >>= 8
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
3
vendor/github.com/go-asn1-ber/asn1-ber/go.mod
generated
vendored
3
vendor/github.com/go-asn1-ber/asn1-ber/go.mod
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
module github.com/go-asn1-ber/asn1-ber
|
|
||||||
|
|
||||||
go 1.13
|
|
35
vendor/github.com/go-asn1-ber/asn1-ber/header.go
generated
vendored
35
vendor/github.com/go-asn1-ber/asn1-ber/header.go
generated
vendored
@ -1,35 +0,0 @@
|
|||||||
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 {
|
|
||||||
return Identifier{}, 0, read, err
|
|
||||||
} else {
|
|
||||||
identifier = i
|
|
||||||
read += c
|
|
||||||
}
|
|
||||||
|
|
||||||
if l, c, err := readLength(reader); err != nil {
|
|
||||||
return Identifier{}, 0, read, err
|
|
||||||
} else {
|
|
||||||
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
|
|
||||||
}
|
|
112
vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
generated
vendored
112
vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
generated
vendored
@ -1,112 +0,0 @@
|
|||||||
package ber
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func readIdentifier(reader io.Reader) (Identifier, int, error) {
|
|
||||||
identifier := Identifier{}
|
|
||||||
read := 0
|
|
||||||
|
|
||||||
// identifier byte
|
|
||||||
b, err := readByte(reader)
|
|
||||||
if err != nil {
|
|
||||||
if Debug {
|
|
||||||
fmt.Printf("error reading identifier byte: %v\n", err)
|
|
||||||
}
|
|
||||||
return Identifier{}, read, err
|
|
||||||
}
|
|
||||||
read++
|
|
||||||
|
|
||||||
identifier.ClassType = Class(b) & ClassBitmask
|
|
||||||
identifier.TagType = Type(b) & TypeBitmask
|
|
||||||
|
|
||||||
if tag := Tag(b) & TagBitmask; tag != HighTag {
|
|
||||||
// short-form tag
|
|
||||||
identifier.Tag = tag
|
|
||||||
return identifier, read, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// high-tag-number tag
|
|
||||||
tagBytes := 0
|
|
||||||
for {
|
|
||||||
b, err := readByte(reader)
|
|
||||||
if err != nil {
|
|
||||||
if Debug {
|
|
||||||
fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err)
|
|
||||||
}
|
|
||||||
return Identifier{}, read, err
|
|
||||||
}
|
|
||||||
tagBytes++
|
|
||||||
read++
|
|
||||||
|
|
||||||
// Lowest 7 bits get appended to the tag value (x.690, 8.1.2.4.2.b)
|
|
||||||
identifier.Tag <<= 7
|
|
||||||
identifier.Tag |= Tag(b) & HighTagValueBitmask
|
|
||||||
|
|
||||||
// First byte may not be all zeros (x.690, 8.1.2.4.2.c)
|
|
||||||
if tagBytes == 1 && identifier.Tag == 0 {
|
|
||||||
return Identifier{}, read, errors.New("invalid first high-tag-number tag byte")
|
|
||||||
}
|
|
||||||
// Overflow of int64
|
|
||||||
// TODO: support big int tags?
|
|
||||||
if tagBytes > 9 {
|
|
||||||
return Identifier{}, read, errors.New("high-tag-number tag overflow")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Top bit of 0 means this is the last byte in the high-tag-number tag (x.690, 8.1.2.4.2.a)
|
|
||||||
if Tag(b)&HighTagContinueBitmask == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return identifier, read, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeIdentifier(identifier Identifier) []byte {
|
|
||||||
b := []byte{0x0}
|
|
||||||
b[0] |= byte(identifier.ClassType)
|
|
||||||
b[0] |= byte(identifier.TagType)
|
|
||||||
|
|
||||||
if identifier.Tag < HighTag {
|
|
||||||
// Short-form
|
|
||||||
b[0] |= byte(identifier.Tag)
|
|
||||||
} else {
|
|
||||||
// high-tag-number
|
|
||||||
b[0] |= byte(HighTag)
|
|
||||||
|
|
||||||
tag := identifier.Tag
|
|
||||||
|
|
||||||
b = append(b, encodeHighTag(tag)...)
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
81
vendor/github.com/go-asn1-ber/asn1-ber/length.go
generated
vendored
81
vendor/github.com/go-asn1-ber/asn1-ber/length.go
generated
vendored
@ -1,81 +0,0 @@
|
|||||||
package ber
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func readLength(reader io.Reader) (length int, read int, err error) {
|
|
||||||
// length byte
|
|
||||||
b, err := readByte(reader)
|
|
||||||
if err != nil {
|
|
||||||
if Debug {
|
|
||||||
fmt.Printf("error reading length byte: %v\n", err)
|
|
||||||
}
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
read++
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case b == 0xFF:
|
|
||||||
// Invalid 0xFF (x.600, 8.1.3.5.c)
|
|
||||||
return 0, read, errors.New("invalid length byte 0xff")
|
|
||||||
|
|
||||||
case b == LengthLongFormBitmask:
|
|
||||||
// Indefinite form, we have to decode packets until we encounter an EOC packet (x.600, 8.1.3.6)
|
|
||||||
length = LengthIndefinite
|
|
||||||
|
|
||||||
case b&LengthLongFormBitmask == 0:
|
|
||||||
// Short definite form, extract the length from the bottom 7 bits (x.600, 8.1.3.4)
|
|
||||||
length = int(b) & LengthValueBitmask
|
|
||||||
|
|
||||||
case b&LengthLongFormBitmask != 0:
|
|
||||||
// Long definite form, extract the number of length bytes to follow from the bottom 7 bits (x.600, 8.1.3.5.b)
|
|
||||||
lengthBytes := int(b) & LengthValueBitmask
|
|
||||||
// Protect against overflow
|
|
||||||
// TODO: support big int length?
|
|
||||||
if lengthBytes > 8 {
|
|
||||||
return 0, read, errors.New("long-form length overflow")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate into a 64-bit variable
|
|
||||||
var length64 int64
|
|
||||||
for i := 0; i < lengthBytes; i++ {
|
|
||||||
b, err = readByte(reader)
|
|
||||||
if err != nil {
|
|
||||||
if Debug {
|
|
||||||
fmt.Printf("error reading long-form length byte %d: %v\n", i, err)
|
|
||||||
}
|
|
||||||
return 0, read, err
|
|
||||||
}
|
|
||||||
read++
|
|
||||||
|
|
||||||
// x.600, 8.1.3.5
|
|
||||||
length64 <<= 8
|
|
||||||
length64 |= int64(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cast to a platform-specific integer
|
|
||||||
length = int(length64)
|
|
||||||
// Ensure we didn't overflow
|
|
||||||
if int64(length) != length64 {
|
|
||||||
return 0, read, errors.New("long-form length overflow")
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0, read, errors.New("invalid length byte")
|
|
||||||
}
|
|
||||||
|
|
||||||
return length, read, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
return length_bytes
|
|
||||||
}
|
|
24
vendor/github.com/go-asn1-ber/asn1-ber/util.go
generated
vendored
24
vendor/github.com/go-asn1-ber/asn1-ber/util.go
generated
vendored
@ -1,24 +0,0 @@
|
|||||||
package ber
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
func readByte(reader io.Reader) (byte, error) {
|
|
||||||
bytes := make([]byte, 1, 1)
|
|
||||||
_, err := io.ReadFull(reader, bytes)
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return bytes[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isEOCPacket(p *Packet) bool {
|
|
||||||
return p != nil &&
|
|
||||||
p.Tag == TagEOC &&
|
|
||||||
p.ClassType == ClassUniversal &&
|
|
||||||
p.TagType == TypePrimitive &&
|
|
||||||
len(p.ByteValue) == 0 &&
|
|
||||||
len(p.Children) == 0
|
|
||||||
}
|
|
22
vendor/github.com/go-ldap/ldap/v3/LICENSE
generated
vendored
22
vendor/github.com/go-ldap/ldap/v3/LICENSE
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
|
|
||||||
Portions copyright (c) 2015-2016 go-ldap Authors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
91
vendor/github.com/go-ldap/ldap/v3/add.go
generated
vendored
91
vendor/github.com/go-ldap/ldap/v3/add.go
generated
vendored
@ -1,91 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
389
vendor/github.com/go-ldap/ldap/v3/bind.go
generated
vendored
389
vendor/github.com/go-ldap/ldap/v3/bind.go
generated
vendored
@ -1,389 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
|
||||||
enchex "encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
30
vendor/github.com/go-ldap/ldap/v3/client.go
generated
vendored
30
vendor/github.com/go-ldap/ldap/v3/client.go
generated
vendored
@ -1,30 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Client knows how to interact with an LDAP server
|
|
||||||
type Client interface {
|
|
||||||
Start()
|
|
||||||
StartTLS(*tls.Config) error
|
|
||||||
Close()
|
|
||||||
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
|
|
||||||
|
|
||||||
Compare(dn, attribute, value string) (bool, error)
|
|
||||||
PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error)
|
|
||||||
|
|
||||||
Search(*SearchRequest) (*SearchResult, error)
|
|
||||||
SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
|
|
||||||
}
|
|
61
vendor/github.com/go-ldap/ldap/v3/compare.go
generated
vendored
61
vendor/github.com/go-ldap/ldap/v3/compare.go
generated
vendored
@ -1,61 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
570
vendor/github.com/go-ldap/ldap/v3/conn.go
generated
vendored
570
vendor/github.com/go-ldap/ldap/v3/conn.go
generated
vendored
@ -1,570 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
ber "github.com/go-asn1-ber/asn1-ber"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// MessageQuit causes the processMessages loop to exit
|
|
||||||
MessageQuit = 0
|
|
||||||
// MessageRequest sends a request to the server
|
|
||||||
MessageRequest = 1
|
|
||||||
// MessageResponse receives a response from the server
|
|
||||||
MessageResponse = 2
|
|
||||||
// MessageFinish indicates the client considers a particular message ID to be finished
|
|
||||||
MessageFinish = 3
|
|
||||||
// MessageTimeout indicates the client-specified timeout for a particular message ID has been reached
|
|
||||||
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
|
|
||||||
Packet *ber.Packet
|
|
||||||
// Error is an error encountered while reading
|
|
||||||
Error error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPacket returns the packet or an error
|
|
||||||
func (pr *PacketResponse) ReadPacket() (*ber.Packet, error) {
|
|
||||||
if (pr == nil) || (pr.Packet == nil && pr.Error == nil) {
|
|
||||||
return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve response"))
|
|
||||||
}
|
|
||||||
return pr.Packet, pr.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
type messageContext struct {
|
|
||||||
id int64
|
|
||||||
// close(done) should only be called from finishMessage()
|
|
||||||
done chan struct{}
|
|
||||||
// close(responses) should only be called from processMessages(), and only sent to from sendResponse()
|
|
||||||
responses chan *PacketResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendResponse should only be called within the processMessages() loop which
|
|
||||||
// is also responsible for closing the responses channel.
|
|
||||||
func (msgCtx *messageContext) sendResponse(packet *PacketResponse) {
|
|
||||||
select {
|
|
||||||
case msgCtx.responses <- packet:
|
|
||||||
// Successfully sent packet to message handler.
|
|
||||||
case <-msgCtx.done:
|
|
||||||
// The request handler is done and will not receive more
|
|
||||||
// packets.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type messagePacket struct {
|
|
||||||
Op int
|
|
||||||
MessageID int64
|
|
||||||
Packet *ber.Packet
|
|
||||||
Context *messageContext
|
|
||||||
}
|
|
||||||
|
|
||||||
type sendMessageFlags uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
startTLS sendMessageFlags = 1 << iota
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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 atomic.Value
|
|
||||||
isStartingTLS bool
|
|
||||||
Debug debugging
|
|
||||||
chanConfirm chan struct{}
|
|
||||||
messageContexts map[int64]*messageContext
|
|
||||||
chanMessage chan *messagePacket
|
|
||||||
chanMessageID chan int64
|
|
||||||
wgClose sync.WaitGroup
|
|
||||||
outstandingRequests uint
|
|
||||||
messageMutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Client = &Conn{}
|
|
||||||
|
|
||||||
// DefaultTimeout is a package-level variable that sets the timeout value
|
|
||||||
// used for the Dial and DialTLS methods.
|
|
||||||
//
|
|
||||||
// WARNING: since this is a package-level variable, setting this value from
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 asume 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 {
|
|
||||||
return nil, NewError(ErrorNetwork, err)
|
|
||||||
}
|
|
||||||
conn := NewConn(c, false)
|
|
||||||
conn.Start()
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
c, err := tls.DialWithDialer(&net.Dialer{Timeout: DefaultTimeout}, network, addr, config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewError(ErrorNetwork, err)
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
return nil, NewError(ErrorNetwork, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConn returns a new Conn using conn for network I/O.
|
|
||||||
func NewConn(conn net.Conn, isTLS bool) *Conn {
|
|
||||||
return &Conn{
|
|
||||||
conn: conn,
|
|
||||||
chanConfirm: make(chan struct{}),
|
|
||||||
chanMessageID: make(chan int64),
|
|
||||||
chanMessage: make(chan *messagePacket, 10),
|
|
||||||
messageContexts: map[int64]*messageContext{},
|
|
||||||
requestTimeout: 0,
|
|
||||||
isTLS: isTLS,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start initializes goroutines to read responses and process messages
|
|
||||||
func (l *Conn) Start() {
|
|
||||||
l.wgClose.Add(1)
|
|
||||||
go l.reader()
|
|
||||||
go l.processMessages()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsClosing returns whether or not we're currently closing.
|
|
||||||
func (l *Conn) IsClosing() bool {
|
|
||||||
return atomic.LoadUint32(&l.closing) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// setClosing sets the closing value to true
|
|
||||||
func (l *Conn) setClosing() bool {
|
|
||||||
return atomic.CompareAndSwapUint32(&l.closing, 0, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the connection.
|
|
||||||
func (l *Conn) Close() {
|
|
||||||
l.messageMutex.Lock()
|
|
||||||
defer l.messageMutex.Unlock()
|
|
||||||
|
|
||||||
if l.setClosing() {
|
|
||||||
l.Debug.Printf("Sending quit message and waiting for confirmation")
|
|
||||||
l.chanMessage <- &messagePacket{Op: MessageQuit}
|
|
||||||
<-l.chanConfirm
|
|
||||||
close(l.chanMessage)
|
|
||||||
|
|
||||||
l.Debug.Printf("Closing network connection")
|
|
||||||
if err := l.conn.Close(); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.wgClose.Done()
|
|
||||||
}
|
|
||||||
l.wgClose.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTimeout sets the time after a request is sent that a MessageTimeout triggers
|
|
||||||
func (l *Conn) SetTimeout(timeout time.Duration) {
|
|
||||||
if timeout > 0 {
|
|
||||||
atomic.StoreInt64(&l.requestTimeout, int64(timeout))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the next available messageID
|
|
||||||
func (l *Conn) nextMessageID() int64 {
|
|
||||||
if messageID, ok := <-l.chanMessageID; ok {
|
|
||||||
return messageID
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartTLS sends the command to start a TLS session and then creates a new TLS Client
|
|
||||||
func (l *Conn) StartTLS(config *tls.Config) error {
|
|
||||||
if l.isTLS {
|
|
||||||
return NewError(ErrorNetwork, errors.New("ldap: already encrypted"))
|
|
||||||
}
|
|
||||||
|
|
||||||
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, ApplicationExtendedRequest, nil, "Start TLS")
|
|
||||||
request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
|
|
||||||
packet.AppendChild(request)
|
|
||||||
l.Debug.PrintPacket(packet)
|
|
||||||
|
|
||||||
msgCtx, err := l.sendMessageWithFlags(packet, startTLS)
|
|
||||||
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 {
|
|
||||||
l.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
l.Debug.PrintPacket(packet)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := GetLDAPError(packet); err == nil {
|
|
||||||
conn := tls.Client(l.conn, config)
|
|
||||||
|
|
||||||
if connErr := conn.Handshake(); connErr != nil {
|
|
||||||
l.Close()
|
|
||||||
return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", connErr))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.isTLS = true
|
|
||||||
l.conn = conn
|
|
||||||
} else {
|
|
||||||
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() {
|
|
||||||
return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
|
|
||||||
}
|
|
||||||
l.messageMutex.Lock()
|
|
||||||
l.Debug.Printf("flags&startTLS = %d", flags&startTLS)
|
|
||||||
if l.isStartingTLS {
|
|
||||||
l.messageMutex.Unlock()
|
|
||||||
return nil, NewError(ErrorNetwork, errors.New("ldap: connection is in startls phase"))
|
|
||||||
}
|
|
||||||
if flags&startTLS != 0 {
|
|
||||||
if l.outstandingRequests != 0 {
|
|
||||||
l.messageMutex.Unlock()
|
|
||||||
return nil, NewError(ErrorNetwork, errors.New("ldap: cannot StartTLS with outstanding requests"))
|
|
||||||
}
|
|
||||||
l.isStartingTLS = true
|
|
||||||
}
|
|
||||||
l.outstandingRequests++
|
|
||||||
|
|
||||||
l.messageMutex.Unlock()
|
|
||||||
|
|
||||||
responses := make(chan *PacketResponse)
|
|
||||||
messageID := packet.Children[0].Value.(int64)
|
|
||||||
message := &messagePacket{
|
|
||||||
Op: MessageRequest,
|
|
||||||
MessageID: messageID,
|
|
||||||
Packet: packet,
|
|
||||||
Context: &messageContext{
|
|
||||||
id: messageID,
|
|
||||||
done: make(chan struct{}),
|
|
||||||
responses: responses,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
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() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
l.messageMutex.Lock()
|
|
||||||
l.outstandingRequests--
|
|
||||||
if l.isStartingTLS {
|
|
||||||
l.isStartingTLS = false
|
|
||||||
}
|
|
||||||
l.messageMutex.Unlock()
|
|
||||||
|
|
||||||
message := &messagePacket{
|
|
||||||
Op: MessageFinish,
|
|
||||||
MessageID: msgCtx.id,
|
|
||||||
}
|
|
||||||
l.sendProcessMessage(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Conn) sendProcessMessage(message *messagePacket) bool {
|
|
||||||
l.messageMutex.Lock()
|
|
||||||
defer l.messageMutex.Unlock()
|
|
||||||
if l.IsClosing() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
l.chanMessage <- message
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Conn) processMessages() {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
log.Printf("ldap: recovered panic in processMessages: %v", err)
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)})
|
|
||||||
}
|
|
||||||
l.Debug.Printf("Closing channel for MessageID %d", messageID)
|
|
||||||
close(msgCtx.responses)
|
|
||||||
delete(l.messageContexts, messageID)
|
|
||||||
}
|
|
||||||
close(l.chanMessageID)
|
|
||||||
close(l.chanConfirm)
|
|
||||||
}()
|
|
||||||
|
|
||||||
var messageID int64 = 1
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case l.chanMessageID <- messageID:
|
|
||||||
messageID++
|
|
||||||
case message := <-l.chanMessage:
|
|
||||||
switch message.Op {
|
|
||||||
case MessageQuit:
|
|
||||||
l.Debug.Printf("Shutting down - quit message received")
|
|
||||||
return
|
|
||||||
case MessageRequest:
|
|
||||||
// Add to message list and write to network
|
|
||||||
l.Debug.Printf("Sending message %d", message.MessageID)
|
|
||||||
|
|
||||||
buf := message.Packet.Bytes()
|
|
||||||
_, err := l.conn.Write(buf)
|
|
||||||
if err != nil {
|
|
||||||
l.Debug.Printf("Error Sending Message: %s", err.Error())
|
|
||||||
message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)})
|
|
||||||
close(message.Context.responses)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only add to messageContexts if we were able to
|
|
||||||
// successfully write the message.
|
|
||||||
l.messageContexts[message.MessageID] = message.Context
|
|
||||||
|
|
||||||
// Add timeout if defined
|
|
||||||
requestTimeout := time.Duration(atomic.LoadInt64(&l.requestTimeout))
|
|
||||||
if requestTimeout > 0 {
|
|
||||||
go func() {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
log.Printf("ldap: recovered panic in RequestTimeout: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
time.Sleep(requestTimeout)
|
|
||||||
timeoutMessage := &messagePacket{
|
|
||||||
Op: MessageTimeout,
|
|
||||||
MessageID: message.MessageID,
|
|
||||||
}
|
|
||||||
l.sendProcessMessage(timeoutMessage)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
case MessageResponse:
|
|
||||||
l.Debug.Printf("Receiving message %d", message.MessageID)
|
|
||||||
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())
|
|
||||||
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")})
|
|
||||||
delete(l.messageContexts, message.MessageID)
|
|
||||||
close(msgCtx.responses)
|
|
||||||
}
|
|
||||||
case MessageFinish:
|
|
||||||
l.Debug.Printf("Finished message %d", message.MessageID)
|
|
||||||
if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
|
|
||||||
delete(l.messageContexts, message.MessageID)
|
|
||||||
close(msgCtx.responses)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Conn) reader() {
|
|
||||||
cleanstop := false
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
log.Printf("ldap: recovered panic in reader: %v", err)
|
|
||||||
}
|
|
||||||
if !cleanstop {
|
|
||||||
l.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
if cleanstop {
|
|
||||||
l.Debug.Printf("reader clean stopping (without closing the connection)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
packet, err := ber.ReadPacket(l.conn)
|
|
||||||
if err != nil {
|
|
||||||
// A read error is expected here if we are closing the connection...
|
|
||||||
if !l.IsClosing() {
|
|
||||||
l.closeErr.Store(fmt.Errorf("unable to read LDAP response packet: %s", err))
|
|
||||||
l.Debug.Printf("reader error: %s", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
l.messageMutex.Lock()
|
|
||||||
if l.isStartingTLS {
|
|
||||||
cleanstop = true
|
|
||||||
}
|
|
||||||
l.messageMutex.Unlock()
|
|
||||||
message := &messagePacket{
|
|
||||||
Op: MessageResponse,
|
|
||||||
MessageID: packet.Children[0].Value.(int64),
|
|
||||||
Packet: packet,
|
|
||||||
}
|
|
||||||
if !l.sendProcessMessage(message) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
499
vendor/github.com/go-ldap/ldap/v3/control.go
generated
vendored
499
vendor/github.com/go-ldap/ldap/v3/control.go
generated
vendored
@ -1,499 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/go-asn1-ber/asn1-ber"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ControlTypePaging - https://www.ietf.org/rfc/rfc2696.txt
|
|
||||||
ControlTypePaging = "1.2.840.113556.1.4.319"
|
|
||||||
// ControlTypeBeheraPasswordPolicy - https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
|
|
||||||
ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
|
|
||||||
// ControlTypeVChuPasswordMustChange - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
|
|
||||||
ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
|
|
||||||
// ControlTypeVChuPasswordWarning - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
|
|
||||||
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"
|
|
||||||
|
|
||||||
// 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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ControlTypeMap maps controls to text descriptions
|
|
||||||
var ControlTypeMap = map[string]string{
|
|
||||||
ControlTypePaging: "Paging",
|
|
||||||
ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
|
|
||||||
ControlTypeManageDsaIT: "Manage DSA IT",
|
|
||||||
ControlTypeMicrosoftNotification: "Change Notification - Microsoft",
|
|
||||||
ControlTypeMicrosoftShowDeleted: "Show Deleted Objects - Microsoft",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Control defines an interface controls provide to encode and describe themselves
|
|
||||||
type Control interface {
|
|
||||||
// GetControlType returns the OID
|
|
||||||
GetControlType() string
|
|
||||||
// Encode returns the ber packet representation
|
|
||||||
Encode() *ber.Packet
|
|
||||||
// String returns a human-readable description
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ControlString implements the Control interface for simple controls
|
|
||||||
type ControlString struct {
|
|
||||||
ControlType string
|
|
||||||
Criticality bool
|
|
||||||
ControlValue string
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetControlType returns the OID
|
|
||||||
func (c *ControlString) GetControlType() string {
|
|
||||||
return c.ControlType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode returns the ber packet representation
|
|
||||||
func (c *ControlString) Encode() *ber.Packet {
|
|
||||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
|
||||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")"))
|
|
||||||
if c.Criticality {
|
|
||||||
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
|
|
||||||
}
|
|
||||||
if c.ControlValue != "" {
|
|
||||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value"))
|
|
||||||
}
|
|
||||||
return packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a human-readable description
|
|
||||||
func (c *ControlString) String() string {
|
|
||||||
return fmt.Sprintf("Control Type: %s (%q) Criticality: %t Control Value: %s", ControlTypeMap[c.ControlType], c.ControlType, c.Criticality, c.ControlValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ControlPaging implements the paging control described in https://www.ietf.org/rfc/rfc2696.txt
|
|
||||||
type ControlPaging struct {
|
|
||||||
// PagingSize indicates the page size
|
|
||||||
PagingSize uint32
|
|
||||||
// Cookie is an opaque value returned by the server to track a paging cursor
|
|
||||||
Cookie []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetControlType returns the OID
|
|
||||||
func (c *ControlPaging) GetControlType() string {
|
|
||||||
return ControlTypePaging
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode returns the ber packet representation
|
|
||||||
func (c *ControlPaging) Encode() *ber.Packet {
|
|
||||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
|
||||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypePaging, "Control Type ("+ControlTypeMap[ControlTypePaging]+")"))
|
|
||||||
|
|
||||||
p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Paging)")
|
|
||||||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Search Control Value")
|
|
||||||
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.PagingSize), "Paging Size"))
|
|
||||||
cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
|
|
||||||
cookie.Value = c.Cookie
|
|
||||||
cookie.Data.Write(c.Cookie)
|
|
||||||
seq.AppendChild(cookie)
|
|
||||||
p2.AppendChild(seq)
|
|
||||||
|
|
||||||
packet.AppendChild(p2)
|
|
||||||
return packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a human-readable description
|
|
||||||
func (c *ControlPaging) String() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"Control Type: %s (%q) Criticality: %t PagingSize: %d Cookie: %q",
|
|
||||||
ControlTypeMap[ControlTypePaging],
|
|
||||||
ControlTypePaging,
|
|
||||||
false,
|
|
||||||
c.PagingSize,
|
|
||||||
c.Cookie)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCookie stores the given cookie in the paging control
|
|
||||||
func (c *ControlPaging) SetCookie(cookie []byte) {
|
|
||||||
c.Cookie = cookie
|
|
||||||
}
|
|
||||||
|
|
||||||
// ControlBeheraPasswordPolicy implements the control described in https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
|
|
||||||
type ControlBeheraPasswordPolicy struct {
|
|
||||||
// Expire contains the number of seconds before a password will expire
|
|
||||||
Expire int64
|
|
||||||
// Grace indicates the remaining number of times a user will be allowed to authenticate with an expired password
|
|
||||||
Grace int64
|
|
||||||
// Error indicates the error code
|
|
||||||
Error int8
|
|
||||||
// ErrorString is a human readable error
|
|
||||||
ErrorString string
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetControlType returns the OID
|
|
||||||
func (c *ControlBeheraPasswordPolicy) GetControlType() string {
|
|
||||||
return ControlTypeBeheraPasswordPolicy
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode returns the ber packet representation
|
|
||||||
func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet {
|
|
||||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
|
||||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeBeheraPasswordPolicy, "Control Type ("+ControlTypeMap[ControlTypeBeheraPasswordPolicy]+")"))
|
|
||||||
|
|
||||||
return packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a human-readable description
|
|
||||||
func (c *ControlBeheraPasswordPolicy) String() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"Control Type: %s (%q) Criticality: %t Expire: %d Grace: %d Error: %d, ErrorString: %s",
|
|
||||||
ControlTypeMap[ControlTypeBeheraPasswordPolicy],
|
|
||||||
ControlTypeBeheraPasswordPolicy,
|
|
||||||
false,
|
|
||||||
c.Expire,
|
|
||||||
c.Grace,
|
|
||||||
c.Error,
|
|
||||||
c.ErrorString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ControlVChuPasswordMustChange implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
|
|
||||||
type ControlVChuPasswordMustChange struct {
|
|
||||||
// MustChange indicates if the password is required to be changed
|
|
||||||
MustChange bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetControlType returns the OID
|
|
||||||
func (c *ControlVChuPasswordMustChange) GetControlType() string {
|
|
||||||
return ControlTypeVChuPasswordMustChange
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode returns the ber packet representation
|
|
||||||
func (c *ControlVChuPasswordMustChange) Encode() *ber.Packet {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a human-readable description
|
|
||||||
func (c *ControlVChuPasswordMustChange) String() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"Control Type: %s (%q) Criticality: %t MustChange: %v",
|
|
||||||
ControlTypeMap[ControlTypeVChuPasswordMustChange],
|
|
||||||
ControlTypeVChuPasswordMustChange,
|
|
||||||
false,
|
|
||||||
c.MustChange)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ControlVChuPasswordWarning implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
|
|
||||||
type ControlVChuPasswordWarning struct {
|
|
||||||
// Expire indicates the time in seconds until the password expires
|
|
||||||
Expire int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetControlType returns the OID
|
|
||||||
func (c *ControlVChuPasswordWarning) GetControlType() string {
|
|
||||||
return ControlTypeVChuPasswordWarning
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode returns the ber packet representation
|
|
||||||
func (c *ControlVChuPasswordWarning) Encode() *ber.Packet {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a human-readable description
|
|
||||||
func (c *ControlVChuPasswordWarning) String() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"Control Type: %s (%q) Criticality: %t Expire: %b",
|
|
||||||
ControlTypeMap[ControlTypeVChuPasswordWarning],
|
|
||||||
ControlTypeVChuPasswordWarning,
|
|
||||||
false,
|
|
||||||
c.Expire)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ControlManageDsaIT implements the control described in https://tools.ietf.org/html/rfc3296
|
|
||||||
type ControlManageDsaIT struct {
|
|
||||||
// Criticality indicates if this control is required
|
|
||||||
Criticality bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetControlType returns the OID
|
|
||||||
func (c *ControlManageDsaIT) GetControlType() string {
|
|
||||||
return ControlTypeManageDsaIT
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode returns the ber packet representation
|
|
||||||
func (c *ControlManageDsaIT) Encode() *ber.Packet {
|
|
||||||
//FIXME
|
|
||||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
|
||||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeManageDsaIT, "Control Type ("+ControlTypeMap[ControlTypeManageDsaIT]+")"))
|
|
||||||
if c.Criticality {
|
|
||||||
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
|
|
||||||
}
|
|
||||||
return packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a human-readable description
|
|
||||||
func (c *ControlManageDsaIT) String() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"Control Type: %s (%q) Criticality: %t",
|
|
||||||
ControlTypeMap[ControlTypeManageDsaIT],
|
|
||||||
ControlTypeManageDsaIT,
|
|
||||||
c.Criticality)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewControlManageDsaIT returns a ControlManageDsaIT control
|
|
||||||
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{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
if c.GetControlType() == controlType {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeControl returns a control read from the given packet, or nil if no recognized control can be made
|
|
||||||
func DecodeControl(packet *ber.Packet) (Control, error) {
|
|
||||||
var (
|
|
||||||
ControlType = ""
|
|
||||||
Criticality = false
|
|
||||||
value *ber.Packet
|
|
||||||
)
|
|
||||||
|
|
||||||
switch len(packet.Children) {
|
|
||||||
case 0:
|
|
||||||
// at least one child is required for control type
|
|
||||||
return nil, fmt.Errorf("at least one child is required for control type")
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
// just type, no criticality or value
|
|
||||||
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
|
|
||||||
ControlType = packet.Children[0].Value.(string)
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
|
|
||||||
ControlType = packet.Children[0].Value.(string)
|
|
||||||
|
|
||||||
// Children[1] could be criticality or value (both are optional)
|
|
||||||
// duck-type on whether this is a boolean
|
|
||||||
if _, ok := packet.Children[1].Value.(bool); ok {
|
|
||||||
packet.Children[1].Description = "Criticality"
|
|
||||||
Criticality = packet.Children[1].Value.(bool)
|
|
||||||
} else {
|
|
||||||
packet.Children[1].Description = "Control Value"
|
|
||||||
value = packet.Children[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
|
|
||||||
ControlType = packet.Children[0].Value.(string)
|
|
||||||
|
|
||||||
packet.Children[1].Description = "Criticality"
|
|
||||||
Criticality = packet.Children[1].Value.(bool)
|
|
||||||
|
|
||||||
packet.Children[2].Description = "Control Value"
|
|
||||||
value = packet.Children[2]
|
|
||||||
|
|
||||||
default:
|
|
||||||
// more than 3 children is invalid
|
|
||||||
return nil, fmt.Errorf("more than 3 children is invalid for controls")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ControlType {
|
|
||||||
case ControlTypeManageDsaIT:
|
|
||||||
return NewControlManageDsaIT(Criticality), nil
|
|
||||||
case ControlTypePaging:
|
|
||||||
value.Description += " (Paging)"
|
|
||||||
c := new(ControlPaging)
|
|
||||||
if value.Value != nil {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
value = value.Children[0]
|
|
||||||
value.Description = "Search Control Value"
|
|
||||||
value.Children[0].Description = "Paging Size"
|
|
||||||
value.Children[1].Description = "Cookie"
|
|
||||||
c.PagingSize = uint32(value.Children[0].Value.(int64))
|
|
||||||
c.Cookie = value.Children[1].Data.Bytes()
|
|
||||||
value.Children[1].Value = c.Cookie
|
|
||||||
return c, nil
|
|
||||||
case ControlTypeBeheraPasswordPolicy:
|
|
||||||
value.Description += " (Password Policy - Behera)"
|
|
||||||
c := NewControlBeheraPasswordPolicy()
|
|
||||||
if value.Value != nil {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
sequence := value.Children[0]
|
|
||||||
|
|
||||||
for _, child := range sequence.Children {
|
|
||||||
if child.Tag == 0 {
|
|
||||||
//Warning
|
|
||||||
warningPacket := child.Children[0]
|
|
||||||
packet, err := ber.DecodePacketErr(warningPacket.Data.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if child.Tag == 1 {
|
|
||||||
// Error
|
|
||||||
packet, err := ber.DecodePacketErr(child.Data.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
|
||||||
}
|
|
||||||
val, ok := packet.Value.(int8)
|
|
||||||
if !ok {
|
|
||||||
// what to do?
|
|
||||||
val = -1
|
|
||||||
}
|
|
||||||
c.Error = val
|
|
||||||
child.Value = c.Error
|
|
||||||
c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
case ControlTypeVChuPasswordMustChange:
|
|
||||||
c := &ControlVChuPasswordMustChange{MustChange: true}
|
|
||||||
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, fmt.Errorf("failed to parse value as int: %s", err)
|
|
||||||
}
|
|
||||||
c.Expire = expire
|
|
||||||
value.Value = c.Expire
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
case ControlTypeMicrosoftNotification:
|
|
||||||
return NewControlMicrosoftNotification(), nil
|
|
||||||
case ControlTypeMicrosoftShowDeleted:
|
|
||||||
return NewControlMicrosoftShowDeleted(), nil
|
|
||||||
default:
|
|
||||||
c := new(ControlString)
|
|
||||||
c.ControlType = ControlType
|
|
||||||
c.Criticality = Criticality
|
|
||||||
if value != nil {
|
|
||||||
c.ControlValue = value.Value.(string)
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewControlString returns a generic control
|
|
||||||
func NewControlString(controlType string, criticality bool, controlValue string) *ControlString {
|
|
||||||
return &ControlString{
|
|
||||||
ControlType: controlType,
|
|
||||||
Criticality: criticality,
|
|
||||||
ControlValue: controlValue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewControlPaging returns a paging control
|
|
||||||
func NewControlPaging(pagingSize uint32) *ControlPaging {
|
|
||||||
return &ControlPaging{PagingSize: pagingSize}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewControlBeheraPasswordPolicy returns a ControlBeheraPasswordPolicy
|
|
||||||
func NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy {
|
|
||||||
return &ControlBeheraPasswordPolicy{
|
|
||||||
Expire: -1,
|
|
||||||
Grace: -1,
|
|
||||||
Error: -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeControls(controls []Control) *ber.Packet {
|
|
||||||
packet := ber.Encode(ber.ClassContext, ber.TypeConstructed, 0, nil, "Controls")
|
|
||||||
for _, control := range controls {
|
|
||||||
packet.AppendChild(control.Encode())
|
|
||||||
}
|
|
||||||
return packet
|
|
||||||
}
|
|
30
vendor/github.com/go-ldap/ldap/v3/debug.go
generated
vendored
30
vendor/github.com/go-ldap/ldap/v3/debug.go
generated
vendored
@ -1,30 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
ber "github.com/go-asn1-ber/asn1-ber"
|
|
||||||
)
|
|
||||||
|
|
||||||
// debugging type
|
|
||||||
// - has a Printf method to write the debug output
|
|
||||||
type debugging bool
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
59
vendor/github.com/go-ldap/ldap/v3/del.go
generated
vendored
59
vendor/github.com/go-ldap/ldap/v3/del.go
generated
vendored
@ -1,59 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
207
vendor/github.com/go-ldap/ldap/v3/dn.go
generated
vendored
207
vendor/github.com/go-ldap/ldap/v3/dn.go
generated
vendored
@ -1,207 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
enchex "encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
ber "github.com/go-asn1-ber/asn1-ber"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514
|
|
||||||
type AttributeTypeAndValue struct {
|
|
||||||
// Type is the attribute type
|
|
||||||
Type string
|
|
||||||
// Value is the attribute value
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// RelativeDN represents a relativeDistinguishedName from https://tools.ietf.org/html/rfc4514
|
|
||||||
type RelativeDN struct {
|
|
||||||
Attributes []*AttributeTypeAndValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// DN represents a distinguishedName from https://tools.ietf.org/html/rfc4514
|
|
||||||
type DN struct {
|
|
||||||
RDNs []*RelativeDN
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
rdn := new(RelativeDN)
|
|
||||||
rdn.Attributes = make([]*AttributeTypeAndValue, 0)
|
|
||||||
buffer := bytes.Buffer{}
|
|
||||||
attribute := new(AttributeTypeAndValue)
|
|
||||||
escaping := false
|
|
||||||
|
|
||||||
unescapedTrailingSpaces := 0
|
|
||||||
stringFromBuffer := func() string {
|
|
||||||
s := buffer.String()
|
|
||||||
s = s[0 : len(s)-unescapedTrailingSpaces]
|
|
||||||
buffer.Reset()
|
|
||||||
unescapedTrailingSpaces = 0
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(str); i++ {
|
|
||||||
char := str[i]
|
|
||||||
switch {
|
|
||||||
case escaping:
|
|
||||||
unescapedTrailingSpaces = 0
|
|
||||||
escaping = false
|
|
||||||
switch char {
|
|
||||||
case ' ', '"', '#', '+', ',', ';', '<', '=', '>', '\\':
|
|
||||||
buffer.WriteByte(char)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Not a special character, assume hex encoded octet
|
|
||||||
if len(str) == i+1 {
|
|
||||||
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)
|
|
||||||
} else if n != 1 {
|
|
||||||
return nil, fmt.Errorf("expected 1 byte when un-escaping, got %d", n)
|
|
||||||
}
|
|
||||||
buffer.WriteByte(dst[0])
|
|
||||||
i++
|
|
||||||
case char == '\\':
|
|
||||||
unescapedTrailingSpaces = 0
|
|
||||||
escaping = true
|
|
||||||
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
|
|
||||||
// and decode.
|
|
||||||
if len(str) > i+1 && str[i+1] == '#' {
|
|
||||||
i += 2
|
|
||||||
index := strings.IndexAny(str[i:], ",+")
|
|
||||||
data := str
|
|
||||||
if index > 0 {
|
|
||||||
data = str[i : i+index]
|
|
||||||
} else {
|
|
||||||
data = str[i:]
|
|
||||||
}
|
|
||||||
rawBER, err := enchex.DecodeString(data)
|
|
||||||
if err != nil {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
buffer.WriteString(packet.Data.String())
|
|
||||||
i += len(data) - 1
|
|
||||||
}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
attribute.Value = stringFromBuffer()
|
|
||||||
rdn.Attributes = append(rdn.Attributes, attribute)
|
|
||||||
attribute = new(AttributeTypeAndValue)
|
|
||||||
if char == ',' {
|
|
||||||
dn.RDNs = append(dn.RDNs, rdn)
|
|
||||||
rdn = new(RelativeDN)
|
|
||||||
rdn.Attributes = make([]*AttributeTypeAndValue, 0)
|
|
||||||
}
|
|
||||||
case char == ' ' && buffer.Len() == 0:
|
|
||||||
// ignore unescaped leading spaces
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
if char == ' ' {
|
|
||||||
// Track unescaped spaces in case they are trailing and we need to remove them
|
|
||||||
unescapedTrailingSpaces++
|
|
||||||
} else {
|
|
||||||
// Reset if we see a non-space char
|
|
||||||
unescapedTrailingSpaces = 0
|
|
||||||
}
|
|
||||||
buffer.WriteByte(char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if buffer.Len() > 0 {
|
|
||||||
if len(attribute.Type) == 0 {
|
|
||||||
return nil, errors.New("DN ended with incomplete type, value pair")
|
|
||||||
}
|
|
||||||
attribute.Value = stringFromBuffer()
|
|
||||||
rdn.Attributes = append(rdn.Attributes, attribute)
|
|
||||||
dn.RDNs = append(dn.RDNs, rdn)
|
|
||||||
}
|
|
||||||
return dn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
func (d *DN) Equal(other *DN) bool {
|
|
||||||
if len(d.RDNs) != len(other.RDNs) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := range d.RDNs {
|
|
||||||
if !d.RDNs[i].Equal(other.RDNs[i]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AncestorOf returns true if the other DN consists of at least one RDN followed by all the RDNs of the current DN.
|
|
||||||
// "ou=widgets,o=acme.com" is an ancestor of "ou=sprockets,ou=widgets,o=acme.com"
|
|
||||||
// "ou=widgets,o=acme.com" is not an ancestor of "ou=sprockets,ou=widgets,o=foo.com"
|
|
||||||
// "ou=widgets,o=acme.com" is not an ancestor of "ou=widgets,o=acme.com"
|
|
||||||
func (d *DN) AncestorOf(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].Equal(otherRDNs[i]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
|
|
||||||
// Relative distinguished names are the same if and only if they have the same number of AttributeTypeAndValues
|
|
||||||
// and each attribute of the first RDN is the same as the attribute of the second RDN with the same attribute type.
|
|
||||||
// The order of attributes is not significant.
|
|
||||||
// Case of attribute types is not significant.
|
|
||||||
func (r *RelativeDN) Equal(other *RelativeDN) bool {
|
|
||||||
if len(r.Attributes) != len(other.Attributes) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return r.hasAllAttributes(other.Attributes) && other.hasAllAttributes(r.Attributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RelativeDN) hasAllAttributes(attrs []*AttributeTypeAndValue) bool {
|
|
||||||
for _, attr := range attrs {
|
|
||||||
found := false
|
|
||||||
for _, myattr := range r.Attributes {
|
|
||||||
if myattr.Equal(attr) {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal returns true if the AttributeTypeAndValue is equivalent to the specified AttributeTypeAndValue
|
|
||||||
// Case of the attribute type is not significant
|
|
||||||
func (a *AttributeTypeAndValue) Equal(other *AttributeTypeAndValue) bool {
|
|
||||||
return strings.EqualFold(a.Type, other.Type) && a.Value == other.Value
|
|
||||||
}
|
|
4
vendor/github.com/go-ldap/ldap/v3/doc.go
generated
vendored
4
vendor/github.com/go-ldap/ldap/v3/doc.go
generated
vendored
@ -1,4 +0,0 @@
|
|||||||
/*
|
|
||||||
Package ldap provides basic LDAP v3 functionality.
|
|
||||||
*/
|
|
||||||
package ldap
|
|
236
vendor/github.com/go-ldap/ldap/v3/error.go
generated
vendored
236
vendor/github.com/go-ldap/ldap/v3/error.go
generated
vendored
@ -1,236 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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")}
|
|
||||||
}
|
|
||||||
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))}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Error{ResultCode: ErrorNetwork, Err: fmt.Errorf("Invalid packet format")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrorWithCode returns true if the given error is an LDAP error with the given result code
|
|
||||||
func IsErrorWithCode(err error, desiredResultCode uint16) bool {
|
|
||||||
if err == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
serverError, ok := err.(*Error)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return serverError.ResultCode == desiredResultCode
|
|
||||||
}
|
|
487
vendor/github.com/go-ldap/ldap/v3/filter.go
generated
vendored
487
vendor/github.com/go-ldap/ldap/v3/filter.go
generated
vendored
@ -1,487 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
hexpac "encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
ber "github.com/go-asn1-ber/asn1-ber"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Filter choices
|
|
||||||
const (
|
|
||||||
FilterAnd = 0
|
|
||||||
FilterOr = 1
|
|
||||||
FilterNot = 2
|
|
||||||
FilterEqualityMatch = 3
|
|
||||||
FilterSubstrings = 4
|
|
||||||
FilterGreaterOrEqual = 5
|
|
||||||
FilterLessOrEqual = 6
|
|
||||||
FilterPresent = 7
|
|
||||||
FilterApproxMatch = 8
|
|
||||||
FilterExtensibleMatch = 9
|
|
||||||
)
|
|
||||||
|
|
||||||
// FilterMap contains human readable descriptions of Filter choices
|
|
||||||
var FilterMap = map[uint64]string{
|
|
||||||
FilterAnd: "And",
|
|
||||||
FilterOr: "Or",
|
|
||||||
FilterNot: "Not",
|
|
||||||
FilterEqualityMatch: "Equality Match",
|
|
||||||
FilterSubstrings: "Substrings",
|
|
||||||
FilterGreaterOrEqual: "Greater Or Equal",
|
|
||||||
FilterLessOrEqual: "Less Or Equal",
|
|
||||||
FilterPresent: "Present",
|
|
||||||
FilterApproxMatch: "Approx Match",
|
|
||||||
FilterExtensibleMatch: "Extensible Match",
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubstringFilter options
|
|
||||||
const (
|
|
||||||
FilterSubstringsInitial = 0
|
|
||||||
FilterSubstringsAny = 1
|
|
||||||
FilterSubstringsFinal = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// FilterSubstringsMap contains human readable descriptions of SubstringFilter choices
|
|
||||||
var FilterSubstringsMap = map[uint64]string{
|
|
||||||
FilterSubstringsInitial: "Substrings Initial",
|
|
||||||
FilterSubstringsAny: "Substrings Any",
|
|
||||||
FilterSubstringsFinal: "Substrings Final",
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchingRuleAssertion choices
|
|
||||||
const (
|
|
||||||
MatchingRuleAssertionMatchingRule = 1
|
|
||||||
MatchingRuleAssertionType = 2
|
|
||||||
MatchingRuleAssertionMatchValue = 3
|
|
||||||
MatchingRuleAssertionDNAttributes = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// MatchingRuleAssertionMap contains human readable descriptions of MatchingRuleAssertion choices
|
|
||||||
var MatchingRuleAssertionMap = map[uint64]string{
|
|
||||||
MatchingRuleAssertionMatchingRule: "Matching Rule Assertion Matching Rule",
|
|
||||||
MatchingRuleAssertionType: "Matching Rule Assertion Type",
|
|
||||||
MatchingRuleAssertionMatchValue: "Matching Rule Assertion Match Value",
|
|
||||||
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] != '(' {
|
|
||||||
return nil, NewError(ErrorFilterCompile, errors.New("ldap: filter does not start with an '('"))
|
|
||||||
}
|
|
||||||
packet, pos, err := compileFilter(filter, 1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case pos > len(filter):
|
|
||||||
return nil, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
|
|
||||||
case pos < len(filter):
|
|
||||||
return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:])))
|
|
||||||
}
|
|
||||||
return packet, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecompileFilter converts a packet representation of a filter into a string representation
|
|
||||||
func DecompileFilter(packet *ber.Packet) (_ string, err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter"))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
buf.WriteByte('(')
|
|
||||||
childStr := ""
|
|
||||||
|
|
||||||
switch packet.Tag {
|
|
||||||
case FilterAnd:
|
|
||||||
buf.WriteByte('&')
|
|
||||||
for _, child := range packet.Children {
|
|
||||||
childStr, err = DecompileFilter(child)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf.WriteString(childStr)
|
|
||||||
}
|
|
||||||
case FilterOr:
|
|
||||||
buf.WriteByte('|')
|
|
||||||
for _, child := range packet.Children {
|
|
||||||
childStr, err = DecompileFilter(child)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf.WriteString(childStr)
|
|
||||||
}
|
|
||||||
case FilterNot:
|
|
||||||
buf.WriteByte('!')
|
|
||||||
childStr, err = DecompileFilter(packet.Children[0])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf.WriteString(childStr)
|
|
||||||
|
|
||||||
case FilterSubstrings:
|
|
||||||
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 {
|
|
||||||
buf.Write(_SymbolAny)
|
|
||||||
}
|
|
||||||
buf.WriteString(EscapeFilter(ber.DecodeString(child.Data.Bytes())))
|
|
||||||
if child.Tag != FilterSubstringsFinal {
|
|
||||||
buf.Write(_SymbolAny)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case FilterEqualityMatch:
|
|
||||||
buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
|
|
||||||
buf.WriteByte('=')
|
|
||||||
buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
|
|
||||||
case FilterGreaterOrEqual:
|
|
||||||
buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
|
|
||||||
buf.WriteString(">=")
|
|
||||||
buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
|
|
||||||
case FilterLessOrEqual:
|
|
||||||
buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
|
|
||||||
buf.WriteString("<=")
|
|
||||||
buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
|
|
||||||
case FilterPresent:
|
|
||||||
buf.WriteString(ber.DecodeString(packet.Data.Bytes()))
|
|
||||||
buf.WriteString("=*")
|
|
||||||
case FilterApproxMatch:
|
|
||||||
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
|
|
||||||
matchingRule := ""
|
|
||||||
value := ""
|
|
||||||
|
|
||||||
for _, child := range packet.Children {
|
|
||||||
switch child.Tag {
|
|
||||||
case MatchingRuleAssertionMatchingRule:
|
|
||||||
matchingRule = ber.DecodeString(child.Data.Bytes())
|
|
||||||
case MatchingRuleAssertionType:
|
|
||||||
attr = ber.DecodeString(child.Data.Bytes())
|
|
||||||
case MatchingRuleAssertionMatchValue:
|
|
||||||
value = ber.DecodeString(child.Data.Bytes())
|
|
||||||
case MatchingRuleAssertionDNAttributes:
|
|
||||||
dnAttributes = child.Value.(bool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(attr) > 0 {
|
|
||||||
buf.WriteString(attr)
|
|
||||||
}
|
|
||||||
if dnAttributes {
|
|
||||||
buf.WriteString(":dn")
|
|
||||||
}
|
|
||||||
if len(matchingRule) > 0 {
|
|
||||||
buf.WriteString(":")
|
|
||||||
buf.WriteString(matchingRule)
|
|
||||||
}
|
|
||||||
buf.WriteString(":=")
|
|
||||||
buf.WriteString(EscapeFilter(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteByte(')')
|
|
||||||
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) {
|
|
||||||
for pos < len(filter) && filter[pos] == '(' {
|
|
||||||
child, newPos, err := compileFilter(filter, pos+1)
|
|
||||||
if err != nil {
|
|
||||||
return pos, err
|
|
||||||
}
|
|
||||||
pos = newPos
|
|
||||||
parent.AppendChild(child)
|
|
||||||
}
|
|
||||||
if pos == len(filter) {
|
|
||||||
return pos, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return pos + 1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
|
|
||||||
var (
|
|
||||||
packet *ber.Packet
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
err = NewError(ErrorFilterCompile, errors.New("ldap: error compiling filter"))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
newPos := pos
|
|
||||||
|
|
||||||
currentRune, currentWidth := utf8.DecodeRuneInString(filter[newPos:])
|
|
||||||
|
|
||||||
switch currentRune {
|
|
||||||
case utf8.RuneError:
|
|
||||||
return nil, 0, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
|
|
||||||
case '(':
|
|
||||||
packet, newPos, err = compileFilter(filter, pos+currentWidth)
|
|
||||||
newPos++
|
|
||||||
return packet, newPos, err
|
|
||||||
case '&':
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterAnd, nil, FilterMap[FilterAnd])
|
|
||||||
newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
|
|
||||||
return packet, newPos, err
|
|
||||||
case '|':
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterOr, nil, FilterMap[FilterOr])
|
|
||||||
newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
|
|
||||||
return packet, newPos, err
|
|
||||||
case '!':
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterNot, nil, FilterMap[FilterNot])
|
|
||||||
var child *ber.Packet
|
|
||||||
child, newPos, err = compileFilter(filter, pos+currentWidth)
|
|
||||||
packet.AppendChild(child)
|
|
||||||
return packet, newPos, err
|
|
||||||
default:
|
|
||||||
const (
|
|
||||||
stateReadingAttr = 0
|
|
||||||
stateReadingExtensibleMatchingRule = 1
|
|
||||||
stateReadingCondition = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
state := stateReadingAttr
|
|
||||||
attribute := bytes.NewBuffer(nil)
|
|
||||||
extensibleDNAttributes := false
|
|
||||||
extensibleMatchingRule := bytes.NewBuffer(nil)
|
|
||||||
condition := bytes.NewBuffer(nil)
|
|
||||||
|
|
||||||
for newPos < len(filter) {
|
|
||||||
remainingFilter := filter[newPos:]
|
|
||||||
currentRune, currentWidth = utf8.DecodeRuneInString(remainingFilter)
|
|
||||||
if currentRune == ')' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if currentRune == utf8.RuneError {
|
|
||||||
return packet, newPos, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch state {
|
|
||||||
case stateReadingAttr:
|
|
||||||
switch {
|
|
||||||
// Extensible rule, with only DN-matching
|
|
||||||
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:="):
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
|
|
||||||
extensibleDNAttributes = true
|
|
||||||
state = stateReadingCondition
|
|
||||||
newPos += 5
|
|
||||||
|
|
||||||
// Extensible rule, with DN-matching and a matching OID
|
|
||||||
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:"):
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
|
|
||||||
extensibleDNAttributes = true
|
|
||||||
state = stateReadingExtensibleMatchingRule
|
|
||||||
newPos += 4
|
|
||||||
|
|
||||||
// Extensible rule, with attr only
|
|
||||||
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
|
|
||||||
state = stateReadingCondition
|
|
||||||
newPos += 2
|
|
||||||
|
|
||||||
// Extensible rule, with no DN attribute matching
|
|
||||||
case currentRune == ':':
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
|
|
||||||
state = stateReadingExtensibleMatchingRule
|
|
||||||
newPos++
|
|
||||||
|
|
||||||
// Equality condition
|
|
||||||
case currentRune == '=':
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
|
|
||||||
state = stateReadingCondition
|
|
||||||
newPos++
|
|
||||||
|
|
||||||
// Greater-than or equal
|
|
||||||
case currentRune == '>' && strings.HasPrefix(remainingFilter, ">="):
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
|
|
||||||
state = stateReadingCondition
|
|
||||||
newPos += 2
|
|
||||||
|
|
||||||
// Less-than or equal
|
|
||||||
case currentRune == '<' && strings.HasPrefix(remainingFilter, "<="):
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
|
|
||||||
state = stateReadingCondition
|
|
||||||
newPos += 2
|
|
||||||
|
|
||||||
// Approx
|
|
||||||
case currentRune == '~' && strings.HasPrefix(remainingFilter, "~="):
|
|
||||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterApproxMatch])
|
|
||||||
state = stateReadingCondition
|
|
||||||
newPos += 2
|
|
||||||
|
|
||||||
// Still reading the attribute name
|
|
||||||
default:
|
|
||||||
attribute.WriteRune(currentRune)
|
|
||||||
newPos += currentWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
case stateReadingExtensibleMatchingRule:
|
|
||||||
switch {
|
|
||||||
|
|
||||||
// Matching rule OID is done
|
|
||||||
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
|
|
||||||
state = stateReadingCondition
|
|
||||||
newPos += 2
|
|
||||||
|
|
||||||
// Still reading the matching rule oid
|
|
||||||
default:
|
|
||||||
extensibleMatchingRule.WriteRune(currentRune)
|
|
||||||
newPos += currentWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
case stateReadingCondition:
|
|
||||||
// append to the condition
|
|
||||||
condition.WriteRune(currentRune)
|
|
||||||
newPos += currentWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if newPos == len(filter) {
|
|
||||||
err = NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
|
|
||||||
return packet, newPos, err
|
|
||||||
}
|
|
||||||
if packet == nil {
|
|
||||||
err = NewError(ErrorFilterCompile, errors.New("ldap: error parsing filter"))
|
|
||||||
return packet, newPos, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case packet.Tag == FilterExtensibleMatch:
|
|
||||||
// MatchingRuleAssertion ::= SEQUENCE {
|
|
||||||
// matchingRule [1] MatchingRuleID OPTIONAL,
|
|
||||||
// type [2] AttributeDescription OPTIONAL,
|
|
||||||
// matchValue [3] AssertionValue,
|
|
||||||
// dnAttributes [4] BOOLEAN DEFAULT FALSE
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Include the matching rule oid, if specified
|
|
||||||
if extensibleMatchingRule.Len() > 0 {
|
|
||||||
packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule.String(), MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include the attribute, if specified
|
|
||||||
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 := decodeEscapedSymbols(condition.Bytes())
|
|
||||||
if encodeErr != nil {
|
|
||||||
return packet, newPos, encodeErr
|
|
||||||
}
|
|
||||||
packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchValue, encodedString, MatchingRuleAssertionMap[MatchingRuleAssertionMatchValue]))
|
|
||||||
|
|
||||||
// Defaults to false, so only include in the sequence if true
|
|
||||||
if extensibleDNAttributes {
|
|
||||||
packet.AppendChild(ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionDNAttributes, extensibleDNAttributes, MatchingRuleAssertionMap[MatchingRuleAssertionDNAttributes]))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 := bytes.Split(condition.Bytes(), _SymbolAny)
|
|
||||||
for i, part := range parts {
|
|
||||||
if len(part) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var tag ber.Tag
|
|
||||||
switch i {
|
|
||||||
case 0:
|
|
||||||
tag = FilterSubstringsInitial
|
|
||||||
case len(parts) - 1:
|
|
||||||
tag = FilterSubstringsFinal
|
|
||||||
default:
|
|
||||||
tag = FilterSubstringsAny
|
|
||||||
}
|
|
||||||
encodedString, encodeErr := decodeEscapedSymbols(part)
|
|
||||||
if encodeErr != nil {
|
|
||||||
return packet, newPos, encodeErr
|
|
||||||
}
|
|
||||||
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, tag, encodedString, FilterSubstringsMap[uint64(tag)]))
|
|
||||||
}
|
|
||||||
packet.AppendChild(seq)
|
|
||||||
default:
|
|
||||||
encodedString, encodeErr := decodeEscapedSymbols(condition.Bytes())
|
|
||||||
if encodeErr != nil {
|
|
||||||
return packet, newPos, encodeErr
|
|
||||||
}
|
|
||||||
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"))
|
|
||||||
}
|
|
||||||
|
|
||||||
newPos += currentWidth
|
|
||||||
return packet, newPos, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert from "ABC\xx\xx\xx" form to literal bytes for transport
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 byteHex == nil {
|
|
||||||
byteHex = make([]byte, 2)
|
|
||||||
byteVal = make([]byte, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
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(runeVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += runeSize
|
|
||||||
}
|
|
||||||
}
|
|
5
vendor/github.com/go-ldap/ldap/v3/go.mod
generated
vendored
5
vendor/github.com/go-ldap/ldap/v3/go.mod
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
module github.com/go-ldap/ldap/v3
|
|
||||||
|
|
||||||
go 1.13
|
|
||||||
|
|
||||||
require github.com/go-asn1-ber/asn1-ber v1.3.1
|
|
2
vendor/github.com/go-ldap/ldap/v3/go.sum
generated
vendored
2
vendor/github.com/go-ldap/ldap/v3/go.sum
generated
vendored
@ -1,2 +0,0 @@
|
|||||||
github.com/go-asn1-ber/asn1-ber v1.3.1 h1:gvPdv/Hr++TRFCl0UbPFHC54P9N9jgsRPnmnr419Uck=
|
|
||||||
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
|
345
vendor/github.com/go-ldap/ldap/v3/ldap.go
generated
vendored
345
vendor/github.com/go-ldap/ldap/v3/ldap.go
generated
vendored
@ -1,345 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
ber "github.com/go-asn1-ber/asn1-ber"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LDAP Application Codes
|
|
||||||
const (
|
|
||||||
ApplicationBindRequest = 0
|
|
||||||
ApplicationBindResponse = 1
|
|
||||||
ApplicationUnbindRequest = 2
|
|
||||||
ApplicationSearchRequest = 3
|
|
||||||
ApplicationSearchResultEntry = 4
|
|
||||||
ApplicationSearchResultDone = 5
|
|
||||||
ApplicationModifyRequest = 6
|
|
||||||
ApplicationModifyResponse = 7
|
|
||||||
ApplicationAddRequest = 8
|
|
||||||
ApplicationAddResponse = 9
|
|
||||||
ApplicationDelRequest = 10
|
|
||||||
ApplicationDelResponse = 11
|
|
||||||
ApplicationModifyDNRequest = 12
|
|
||||||
ApplicationModifyDNResponse = 13
|
|
||||||
ApplicationCompareRequest = 14
|
|
||||||
ApplicationCompareResponse = 15
|
|
||||||
ApplicationAbandonRequest = 16
|
|
||||||
ApplicationSearchResultReference = 19
|
|
||||||
ApplicationExtendedRequest = 23
|
|
||||||
ApplicationExtendedResponse = 24
|
|
||||||
)
|
|
||||||
|
|
||||||
// ApplicationMap contains human readable descriptions of LDAP Application Codes
|
|
||||||
var ApplicationMap = map[uint8]string{
|
|
||||||
ApplicationBindRequest: "Bind Request",
|
|
||||||
ApplicationBindResponse: "Bind Response",
|
|
||||||
ApplicationUnbindRequest: "Unbind Request",
|
|
||||||
ApplicationSearchRequest: "Search Request",
|
|
||||||
ApplicationSearchResultEntry: "Search Result Entry",
|
|
||||||
ApplicationSearchResultDone: "Search Result Done",
|
|
||||||
ApplicationModifyRequest: "Modify Request",
|
|
||||||
ApplicationModifyResponse: "Modify Response",
|
|
||||||
ApplicationAddRequest: "Add Request",
|
|
||||||
ApplicationAddResponse: "Add Response",
|
|
||||||
ApplicationDelRequest: "Del Request",
|
|
||||||
ApplicationDelResponse: "Del Response",
|
|
||||||
ApplicationModifyDNRequest: "Modify DN Request",
|
|
||||||
ApplicationModifyDNResponse: "Modify DN Response",
|
|
||||||
ApplicationCompareRequest: "Compare Request",
|
|
||||||
ApplicationCompareResponse: "Compare Response",
|
|
||||||
ApplicationAbandonRequest: "Abandon Request",
|
|
||||||
ApplicationSearchResultReference: "Search Result Reference",
|
|
||||||
ApplicationExtendedRequest: "Extended Request",
|
|
||||||
ApplicationExtendedResponse: "Extended Response",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)
|
|
||||||
const (
|
|
||||||
BeheraPasswordExpired = 0
|
|
||||||
BeheraAccountLocked = 1
|
|
||||||
BeheraChangeAfterReset = 2
|
|
||||||
BeheraPasswordModNotAllowed = 3
|
|
||||||
BeheraMustSupplyOldPassword = 4
|
|
||||||
BeheraInsufficientPasswordQuality = 5
|
|
||||||
BeheraPasswordTooShort = 6
|
|
||||||
BeheraPasswordTooYoung = 7
|
|
||||||
BeheraPasswordInHistory = 8
|
|
||||||
)
|
|
||||||
|
|
||||||
// BeheraPasswordPolicyErrorMap contains human readable descriptions of Behera Password Policy error codes
|
|
||||||
var BeheraPasswordPolicyErrorMap = map[int8]string{
|
|
||||||
BeheraPasswordExpired: "Password expired",
|
|
||||||
BeheraAccountLocked: "Account locked",
|
|
||||||
BeheraChangeAfterReset: "Password must be changed",
|
|
||||||
BeheraPasswordModNotAllowed: "Policy prevents password modification",
|
|
||||||
BeheraMustSupplyOldPassword: "Policy requires old password in order to change password",
|
|
||||||
BeheraInsufficientPasswordQuality: "Password fails quality checks",
|
|
||||||
BeheraPasswordTooShort: "Password is too short for policy",
|
|
||||||
BeheraPasswordTooYoung: "Password has been changed too recently",
|
|
||||||
BeheraPasswordInHistory: "New password is in list of old passwords",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds descriptions to an LDAP Response packet for debugging
|
|
||||||
func addLDAPDescriptions(packet *ber.Packet) (err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
err = NewError(ErrorDebugging, fmt.Errorf("ldap: cannot process packet to add descriptions: %s", r))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
packet.Description = "LDAP Response"
|
|
||||||
packet.Children[0].Description = "Message ID"
|
|
||||||
|
|
||||||
application := uint8(packet.Children[1].Tag)
|
|
||||||
packet.Children[1].Description = ApplicationMap[application]
|
|
||||||
|
|
||||||
switch application {
|
|
||||||
case ApplicationBindRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationBindResponse:
|
|
||||||
err = addDefaultLDAPResponseDescriptions(packet)
|
|
||||||
case ApplicationUnbindRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationSearchRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationSearchResultEntry:
|
|
||||||
packet.Children[1].Children[0].Description = "Object Name"
|
|
||||||
packet.Children[1].Children[1].Description = "Attributes"
|
|
||||||
for _, child := range packet.Children[1].Children[1].Children {
|
|
||||||
child.Description = "Attribute"
|
|
||||||
child.Children[0].Description = "Attribute Name"
|
|
||||||
child.Children[1].Description = "Attribute Values"
|
|
||||||
for _, grandchild := range child.Children[1].Children {
|
|
||||||
grandchild.Description = "Attribute Value"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(packet.Children) == 3 {
|
|
||||||
err = addControlDescriptions(packet.Children[2])
|
|
||||||
}
|
|
||||||
case ApplicationSearchResultDone:
|
|
||||||
err = addDefaultLDAPResponseDescriptions(packet)
|
|
||||||
case ApplicationModifyRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationModifyResponse:
|
|
||||||
case ApplicationAddRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationAddResponse:
|
|
||||||
case ApplicationDelRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationDelResponse:
|
|
||||||
case ApplicationModifyDNRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationModifyDNResponse:
|
|
||||||
case ApplicationCompareRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationCompareResponse:
|
|
||||||
case ApplicationAbandonRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationSearchResultReference:
|
|
||||||
case ApplicationExtendedRequest:
|
|
||||||
err = addRequestDescriptions(packet)
|
|
||||||
case ApplicationExtendedResponse:
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func addControlDescriptions(packet *ber.Packet) error {
|
|
||||||
packet.Description = "Controls"
|
|
||||||
for _, child := range packet.Children {
|
|
||||||
var value *ber.Packet
|
|
||||||
controlType := ""
|
|
||||||
child.Description = "Control"
|
|
||||||
switch len(child.Children) {
|
|
||||||
case 0:
|
|
||||||
// at least one child is required for control type
|
|
||||||
return fmt.Errorf("at least one child is required for control type")
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
// just type, no criticality or value
|
|
||||||
controlType = child.Children[0].Value.(string)
|
|
||||||
child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
controlType = child.Children[0].Value.(string)
|
|
||||||
child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
|
|
||||||
// Children[1] could be criticality or value (both are optional)
|
|
||||||
// duck-type on whether this is a boolean
|
|
||||||
if _, ok := child.Children[1].Value.(bool); ok {
|
|
||||||
child.Children[1].Description = "Criticality"
|
|
||||||
} else {
|
|
||||||
child.Children[1].Description = "Control Value"
|
|
||||||
value = child.Children[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
// criticality and value present
|
|
||||||
controlType = child.Children[0].Value.(string)
|
|
||||||
child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
|
|
||||||
child.Children[1].Description = "Criticality"
|
|
||||||
child.Children[2].Description = "Control Value"
|
|
||||||
value = child.Children[2]
|
|
||||||
|
|
||||||
default:
|
|
||||||
// more than 3 children is invalid
|
|
||||||
return fmt.Errorf("more than 3 children for control packet found")
|
|
||||||
}
|
|
||||||
|
|
||||||
if value == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch controlType {
|
|
||||||
case ControlTypePaging:
|
|
||||||
value.Description += " (Paging)"
|
|
||||||
if value.Value != nil {
|
|
||||||
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()
|
|
||||||
value.AppendChild(valueChildren)
|
|
||||||
}
|
|
||||||
value.Children[0].Description = "Real Search Control Value"
|
|
||||||
value.Children[0].Children[0].Description = "Paging Size"
|
|
||||||
value.Children[0].Children[1].Description = "Cookie"
|
|
||||||
|
|
||||||
case ControlTypeBeheraPasswordPolicy:
|
|
||||||
value.Description += " (Password Policy - Behera Draft)"
|
|
||||||
if value.Value != nil {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
sequence := value.Children[0]
|
|
||||||
for _, child := range sequence.Children {
|
|
||||||
if child.Tag == 0 {
|
|
||||||
//Warning
|
|
||||||
warningPacket := child.Children[0]
|
|
||||||
packet, err := ber.DecodePacketErr(warningPacket.Data.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to decode data bytes: %s", err)
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if child.Tag == 1 {
|
|
||||||
// Error
|
|
||||||
packet, err := ber.DecodePacketErr(child.Data.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to decode data bytes: %s", err)
|
|
||||||
}
|
|
||||||
val, ok := packet.Value.(int8)
|
|
||||||
if !ok {
|
|
||||||
val = -1
|
|
||||||
}
|
|
||||||
child.Description = "Error"
|
|
||||||
child.Value = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return addControlDescriptions(packet.Children[2])
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (" + 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 {
|
|
||||||
return addControlDescriptions(packet.Children[2])
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DebugBinaryFile reads and prints packets from the given filename
|
|
||||||
func DebugBinaryFile(fileName string) error {
|
|
||||||
file, err := ioutil.ReadFile(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return NewError(ErrorDebugging, err)
|
|
||||||
}
|
|
||||||
ber.PrintBytes(os.Stdout, file, "")
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
var hex = "0123456789abcdef"
|
|
||||||
|
|
||||||
func mustEscape(c byte) bool {
|
|
||||||
return c > 0x7f || c == '(' || c == ')' || c == '\\' || c == '*' || c == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// EscapeFilter escapes from the provided LDAP filter string the special
|
|
||||||
// characters in the set `()*\` and those out of the range 0 < c < 0x80,
|
|
||||||
// as defined in RFC4515.
|
|
||||||
func EscapeFilter(filter string) string {
|
|
||||||
escape := 0
|
|
||||||
for i := 0; i < len(filter); i++ {
|
|
||||||
if mustEscape(filter[i]) {
|
|
||||||
escape++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if escape == 0 {
|
|
||||||
return filter
|
|
||||||
}
|
|
||||||
buf := make([]byte, len(filter)+escape*2)
|
|
||||||
for i, j := 0, 0; i < len(filter); i++ {
|
|
||||||
c := filter[i]
|
|
||||||
if mustEscape(c) {
|
|
||||||
buf[j+0] = '\\'
|
|
||||||
buf[j+1] = hex[c>>4]
|
|
||||||
buf[j+2] = hex[c&0xf]
|
|
||||||
j += 3
|
|
||||||
} else {
|
|
||||||
buf[j] = c
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(buf)
|
|
||||||
}
|
|
75
vendor/github.com/go-ldap/ldap/v3/moddn.go
generated
vendored
75
vendor/github.com/go-ldap/ldap/v3/moddn.go
generated
vendored
@ -1,75 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"))
|
|
||||||
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)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
132
vendor/github.com/go-ldap/ldap/v3/modify.go
generated
vendored
132
vendor/github.com/go-ldap/ldap/v3/modify.go
generated
vendored
@ -1,132 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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
|
|
||||||
}
|
|
126
vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
generated
vendored
126
vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
generated
vendored
@ -1,126 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
ber "github.com/go-asn1-ber/asn1-ber"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
passwordModifyOID = "1.3.6.1.4.1.4203.1.11.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PasswordModifyRequest implements the Password Modify Extended Operation as defined in https://www.ietf.org/rfc/rfc3062.txt
|
|
||||||
type PasswordModifyRequest struct {
|
|
||||||
// UserIdentity is an optional string representation of the user associated with the request.
|
|
||||||
// This string may or may not be an LDAPDN [RFC2253].
|
|
||||||
// If no UserIdentity field is present, the request acts up upon the password of the user currently associated with the LDAP session
|
|
||||||
UserIdentity string
|
|
||||||
// OldPassword, if present, contains the user's current password
|
|
||||||
OldPassword string
|
|
||||||
// NewPassword, if present, contains the desired password for this user
|
|
||||||
NewPassword string
|
|
||||||
}
|
|
||||||
|
|
||||||
// PasswordModifyResult holds the server response to a PasswordModifyRequest
|
|
||||||
type PasswordModifyResult struct {
|
|
||||||
// GeneratedPassword holds a password generated by the server, if present
|
|
||||||
GeneratedPassword string
|
|
||||||
// Referral are the returned referral
|
|
||||||
Referral string
|
|
||||||
}
|
|
||||||
|
|
||||||
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 req.UserIdentity != "" {
|
|
||||||
passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.UserIdentity, "User Identity"))
|
|
||||||
}
|
|
||||||
if req.OldPassword != "" {
|
|
||||||
passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, req.OldPassword, "Old Password"))
|
|
||||||
}
|
|
||||||
if req.NewPassword != "" {
|
|
||||||
passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, req.NewPassword, "New Password"))
|
|
||||||
}
|
|
||||||
extendedRequestValue.AppendChild(passwordModifyRequestValue)
|
|
||||||
|
|
||||||
pkt.AppendChild(extendedRequestValue)
|
|
||||||
|
|
||||||
envelope.AppendChild(pkt)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPasswordModifyRequest creates a new PasswordModifyRequest
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
// with the session.
|
|
||||||
//
|
|
||||||
// oldPassword is the current user's password, it can be empty or it can be
|
|
||||||
// needed depending on the session user access rights (usually an administrator
|
|
||||||
// can change a user's password without knowing the current one) and the
|
|
||||||
// password policy (see pwdSafeModify password policy's attribute)
|
|
||||||
//
|
|
||||||
// newPassword is the desired user's password. If empty the server can return
|
|
||||||
// an error or generate a new password that will be available in the
|
|
||||||
// PasswordModifyResult.GeneratedPassword
|
|
||||||
//
|
|
||||||
func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
|
|
||||||
return &PasswordModifyRequest{
|
|
||||||
UserIdentity: userIdentity,
|
|
||||||
OldPassword: oldPassword,
|
|
||||||
NewPassword: newPassword,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PasswordModify performs the modification request
|
|
||||||
func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) {
|
|
||||||
msgCtx, err := l.doRequest(passwordModifyRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer l.finishMessage(msgCtx)
|
|
||||||
|
|
||||||
packet, err := l.readPacket(msgCtx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &PasswordModifyResult{}
|
|
||||||
|
|
||||||
if packet.Children[1].Tag == ApplicationExtendedResponse {
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
extendedResponse := packet.Children[1]
|
|
||||||
for _, child := range extendedResponse.Children {
|
|
||||||
if child.Tag == 11 {
|
|
||||||
passwordModifyResponseValue := ber.DecodePacket(child.Data.Bytes())
|
|
||||||
if len(passwordModifyResponseValue.Children) == 1 {
|
|
||||||
if passwordModifyResponseValue.Children[0].Tag == 0 {
|
|
||||||
result.GeneratedPassword = ber.DecodeString(passwordModifyResponseValue.Children[0].Data.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
66
vendor/github.com/go-ldap/ldap/v3/request.go
generated
vendored
66
vendor/github.com/go-ldap/ldap/v3/request.go
generated
vendored
@ -1,66 +0,0 @@
|
|||||||
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")
|
|
||||||
)
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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
|
|
||||||
}
|
|
370
vendor/github.com/go-ldap/ldap/v3/search.go
generated
vendored
370
vendor/github.com/go-ldap/ldap/v3/search.go
generated
vendored
@ -1,370 +0,0 @@
|
|||||||
package ldap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
ber "github.com/go-asn1-ber/asn1-ber"
|
|
||||||
)
|
|
||||||
|
|
||||||
// scope choices
|
|
||||||
const (
|
|
||||||
ScopeBaseObject = 0
|
|
||||||
ScopeSingleLevel = 1
|
|
||||||
ScopeWholeSubtree = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// ScopeMap contains human readable descriptions of scope choices
|
|
||||||
var ScopeMap = map[int]string{
|
|
||||||
ScopeBaseObject: "Base Object",
|
|
||||||
ScopeSingleLevel: "Single Level",
|
|
||||||
ScopeWholeSubtree: "Whole Subtree",
|
|
||||||
}
|
|
||||||
|
|
||||||
// derefAliases
|
|
||||||
const (
|
|
||||||
NeverDerefAliases = 0
|
|
||||||
DerefInSearching = 1
|
|
||||||
DerefFindingBaseObj = 2
|
|
||||||
DerefAlways = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
// DerefMap contains human readable descriptions of derefAliases choices
|
|
||||||
var DerefMap = map[int]string{
|
|
||||||
NeverDerefAliases: "NeverDerefAliases",
|
|
||||||
DerefInSearching: "DerefInSearching",
|
|
||||||
DerefFindingBaseObj: "DerefFindingBaseObj",
|
|
||||||
DerefAlways: "DerefAlways",
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEntry returns an Entry object with the specified distinguished name and attribute key-value pairs.
|
|
||||||
// The map of attributes is accessed in alphabetical order of the keys in order to ensure that, for the
|
|
||||||
// same input map of attributes, the output entry will contain the same order of attributes
|
|
||||||
func NewEntry(dn string, attributes map[string][]string) *Entry {
|
|
||||||
var attributeNames []string
|
|
||||||
for attributeName := range attributes {
|
|
||||||
attributeNames = append(attributeNames, attributeName)
|
|
||||||
}
|
|
||||||
sort.Strings(attributeNames)
|
|
||||||
|
|
||||||
var encodedAttributes []*EntryAttribute
|
|
||||||
for _, attributeName := range attributeNames {
|
|
||||||
encodedAttributes = append(encodedAttributes, NewEntryAttribute(attributeName, attributes[attributeName]))
|
|
||||||
}
|
|
||||||
return &Entry{
|
|
||||||
DN: dn,
|
|
||||||
Attributes: encodedAttributes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry represents a single search result entry
|
|
||||||
type Entry struct {
|
|
||||||
// DN is the distinguished name of the entry
|
|
||||||
DN string
|
|
||||||
// Attributes are the returned attributes for the entry
|
|
||||||
Attributes []*EntryAttribute
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAttributeValues returns the values for the named attribute, or an empty list
|
|
||||||
func (e *Entry) GetAttributeValues(attribute string) []string {
|
|
||||||
for _, attr := range e.Attributes {
|
|
||||||
if attr.Name == attribute {
|
|
||||||
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 {
|
|
||||||
if 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)
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
for _, attr := range e.Attributes {
|
|
||||||
attr.Print()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrettyPrint outputs a human-readable description indenting
|
|
||||||
func (e *Entry) PrettyPrint(indent int) {
|
|
||||||
fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN)
|
|
||||||
for _, attr := range e.Attributes {
|
|
||||||
attr.PrettyPrint(indent + 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEntryAttribute returns a new EntryAttribute with the desired key-value pair
|
|
||||||
func NewEntryAttribute(name string, values []string) *EntryAttribute {
|
|
||||||
var bytes [][]byte
|
|
||||||
for _, value := range values {
|
|
||||||
bytes = append(bytes, []byte(value))
|
|
||||||
}
|
|
||||||
return &EntryAttribute{
|
|
||||||
Name: name,
|
|
||||||
Values: values,
|
|
||||||
ByteValues: bytes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EntryAttribute holds a single attribute
|
|
||||||
type EntryAttribute struct {
|
|
||||||
// Name is the name of the attribute
|
|
||||||
Name string
|
|
||||||
// Values contain the string values of the attribute
|
|
||||||
Values []string
|
|
||||||
// ByteValues contain the raw values of the attribute
|
|
||||||
ByteValues [][]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print outputs a human-readable description
|
|
||||||
func (e *EntryAttribute) Print() {
|
|
||||||
fmt.Printf("%s: %s\n", e.Name, e.Values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrettyPrint outputs a human-readable description with indenting
|
|
||||||
func (e *EntryAttribute) PrettyPrint(indent int) {
|
|
||||||
fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchResult holds the server's response to a search request
|
|
||||||
type SearchResult struct {
|
|
||||||
// Entries are the returned entries
|
|
||||||
Entries []*Entry
|
|
||||||
// Referrals are the returned referrals
|
|
||||||
Referrals []string
|
|
||||||
// Controls are the returned controls
|
|
||||||
Controls []Control
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print outputs a human-readable description
|
|
||||||
func (s *SearchResult) Print() {
|
|
||||||
for _, entry := range s.Entries {
|
|
||||||
entry.Print()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrettyPrint outputs a human-readable description with indenting
|
|
||||||
func (s *SearchResult) PrettyPrint(indent int) {
|
|
||||||
for _, entry := range s.Entries {
|
|
||||||
entry.PrettyPrint(indent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchRequest represents a search request to send to the server
|
|
||||||
type SearchRequest struct {
|
|
||||||
BaseDN string
|
|
||||||
Scope int
|
|
||||||
DerefAliases int
|
|
||||||
SizeLimit int
|
|
||||||
TimeLimit int
|
|
||||||
TypesOnly bool
|
|
||||||
Filter string
|
|
||||||
Attributes []string
|
|
||||||
Controls []Control
|
|
||||||
}
|
|
||||||
|
|
||||||
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(req.Filter)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pkt.AppendChild(filterPacket)
|
|
||||||
// encode attributes
|
|
||||||
attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
|
|
||||||
for _, attribute := range req.Attributes {
|
|
||||||
attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
|
|
||||||
}
|
|
||||||
pkt.AppendChild(attributesPacket)
|
|
||||||
|
|
||||||
envelope.AppendChild(pkt)
|
|
||||||
if len(req.Controls) > 0 {
|
|
||||||
envelope.AppendChild(encodeControls(req.Controls))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSearchRequest creates a new search request
|
|
||||||
func NewSearchRequest(
|
|
||||||
BaseDN string,
|
|
||||||
Scope, DerefAliases, SizeLimit, TimeLimit int,
|
|
||||||
TypesOnly bool,
|
|
||||||
Filter string,
|
|
||||||
Attributes []string,
|
|
||||||
Controls []Control,
|
|
||||||
) *SearchRequest {
|
|
||||||
return &SearchRequest{
|
|
||||||
BaseDN: BaseDN,
|
|
||||||
Scope: Scope,
|
|
||||||
DerefAliases: DerefAliases,
|
|
||||||
SizeLimit: SizeLimit,
|
|
||||||
TimeLimit: TimeLimit,
|
|
||||||
TypesOnly: TypesOnly,
|
|
||||||
Filter: Filter,
|
|
||||||
Attributes: Attributes,
|
|
||||||
Controls: Controls,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchWithPaging accepts a search request and desired page size in order to execute LDAP queries to fulfill the
|
|
||||||
// search request. All paged LDAP query responses will be buffered and the final result will be returned atomically.
|
|
||||||
// The following four cases are possible given the arguments:
|
|
||||||
// - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size
|
|
||||||
// - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries
|
|
||||||
// - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request
|
|
||||||
// - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries
|
|
||||||
// A requested pagingSize of 0 is interpreted as no limit by LDAP servers.
|
|
||||||
func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) {
|
|
||||||
var pagingControl *ControlPaging
|
|
||||||
|
|
||||||
control := FindControl(searchRequest.Controls, ControlTypePaging)
|
|
||||||
if control == nil {
|
|
||||||
pagingControl = NewControlPaging(pagingSize)
|
|
||||||
searchRequest.Controls = append(searchRequest.Controls, pagingControl)
|
|
||||||
} else {
|
|
||||||
castControl, ok := control.(*ControlPaging)
|
|
||||||
if !ok {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
pagingControl = castControl
|
|
||||||
}
|
|
||||||
|
|
||||||
searchResult := new(SearchResult)
|
|
||||||
for {
|
|
||||||
result, err := l.Search(searchRequest)
|
|
||||||
l.Debug.Printf("Looking for Paging Control...")
|
|
||||||
if err != nil {
|
|
||||||
return searchResult, err
|
|
||||||
}
|
|
||||||
if result == nil {
|
|
||||||
return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range result.Entries {
|
|
||||||
searchResult.Entries = append(searchResult.Entries, entry)
|
|
||||||
}
|
|
||||||
for _, referral := range result.Referrals {
|
|
||||||
searchResult.Referrals = append(searchResult.Referrals, referral)
|
|
||||||
}
|
|
||||||
for _, control := range result.Controls {
|
|
||||||
searchResult.Controls = append(searchResult.Controls, control)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Debug.Printf("Looking for Paging Control...")
|
|
||||||
pagingResult := FindControl(result.Controls, ControlTypePaging)
|
|
||||||
if pagingResult == nil {
|
|
||||||
pagingControl = nil
|
|
||||||
l.Debug.Printf("Could not find paging control. Breaking...")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
cookie := pagingResult.(*ControlPaging).Cookie
|
|
||||||
if len(cookie) == 0 {
|
|
||||||
pagingControl = nil
|
|
||||||
l.Debug.Printf("Could not find cookie. Breaking...")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
pagingControl.SetCookie(cookie)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pagingControl != nil {
|
|
||||||
l.Debug.Printf("Abandoning Paging...")
|
|
||||||
pagingControl.PagingSize = 0
|
|
||||||
l.Search(searchRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchResult, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search performs the given search request
|
|
||||||
func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
|
|
||||||
msgCtx, err := l.doRequest(searchRequest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer l.finishMessage(msgCtx)
|
|
||||||
|
|
||||||
result := &SearchResult{
|
|
||||||
Entries: make([]*Entry, 0),
|
|
||||||
Referrals: make([]string, 0),
|
|
||||||
Controls: make([]Control, 0)}
|
|
||||||
|
|
||||||
for {
|
|
||||||
packet, err := l.readPacket(msgCtx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 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)
|
|
||||||
}
|
|
||||||
result.Entries = append(result.Entries, entry)
|
|
||||||
case 5:
|
|
||||||
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, fmt.Errorf("failed to decode child control: %s", err)
|
|
||||||
}
|
|
||||||
result.Controls = append(result.Controls, decodedChild)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
case 19:
|
|
||||||
result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
5
vendor/modules.txt
vendored
5
vendor/modules.txt
vendored
@ -7,11 +7,6 @@ github.com/dgrijalva/jwt-go
|
|||||||
# github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
# github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||||
## explicit
|
## explicit
|
||||||
github.com/docopt/docopt-go
|
github.com/docopt/docopt-go
|
||||||
# github.com/go-asn1-ber/asn1-ber v1.3.1
|
|
||||||
github.com/go-asn1-ber/asn1-ber
|
|
||||||
# github.com/go-ldap/ldap/v3 v3.1.10
|
|
||||||
## explicit
|
|
||||||
github.com/go-ldap/ldap/v3
|
|
||||||
# github.com/go-sql-driver/mysql v1.5.0
|
# github.com/go-sql-driver/mysql v1.5.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/go-sql-driver/mysql
|
github.com/go-sql-driver/mysql
|
||||||
|
Loading…
Reference in New Issue
Block a user