mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-30 06:18:02 +02:00
Changes LDAP Library and other LDAP improvements (#1277)
* Changes LDAP library to go-ldap and creates new ldap timeout directive * Add support for connection on LDAP using TLS
This commit is contained in:
parent
226ab5a5f7
commit
160e22f0fe
@ -16,6 +16,7 @@ LDAP_BASE_DN=$ldap_basedn
|
|||||||
LDAP_FILTER=$ldap_filter
|
LDAP_FILTER=$ldap_filter
|
||||||
LDAP_UID=$ldap_uid
|
LDAP_UID=$ldap_uid
|
||||||
LDAP_SCOPE=$ldap_scope
|
LDAP_SCOPE=$ldap_scope
|
||||||
|
LDAP_CONNECT_TIMEOUT=$ldap_connect_timeout
|
||||||
UI_SECRET=$ui_secret
|
UI_SECRET=$ui_secret
|
||||||
SECRET_KEY=$secret_key
|
SECRET_KEY=$secret_key
|
||||||
SELF_REGISTRATION=$self_registration
|
SELF_REGISTRATION=$self_registration
|
||||||
|
@ -21,7 +21,7 @@ COPY src/favicon.ico /go/bin/favicon.ico
|
|||||||
COPY make/jsminify.sh /tmp/jsminify.sh
|
COPY make/jsminify.sh /tmp/jsminify.sh
|
||||||
|
|
||||||
RUN chmod u+x /go/bin/harbor_ui \
|
RUN chmod u+x /go/bin/harbor_ui \
|
||||||
&& sed -i 's/TLS_CACERT/#TLS_CAERT/g' /etc/ldap/ldap.conf \
|
&& sed -i 's/TLS_CACERT/#TLS_CACERT/g' /etc/ldap/ldap.conf \
|
||||||
&& sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf \
|
&& sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf \
|
||||||
&& timestamp=`date '+%s'` \
|
&& timestamp=`date '+%s'` \
|
||||||
&& /tmp/jsminify.sh /go/bin/views/sections/script-include.htm /go/bin/static/resources/js/harbor.app.min.$timestamp.js /go/bin/ \
|
&& /tmp/jsminify.sh /go/bin/views/sections/script-include.htm /go/bin/static/resources/js/harbor.app.min.$timestamp.js /go/bin/ \
|
||||||
|
@ -52,6 +52,9 @@ ldap_uid = uid
|
|||||||
#the scope to search for users, 1-LDAP_SCOPE_BASE, 2-LDAP_SCOPE_ONELEVEL, 3-LDAP_SCOPE_SUBTREE
|
#the scope to search for users, 1-LDAP_SCOPE_BASE, 2-LDAP_SCOPE_ONELEVEL, 3-LDAP_SCOPE_SUBTREE
|
||||||
ldap_scope = 3
|
ldap_scope = 3
|
||||||
|
|
||||||
|
#Timeout (in seconds) when connecting to an LDAP Server. The default value (and most reasonable) is 5 seconds.
|
||||||
|
ldap_connect_timeout = 5
|
||||||
|
|
||||||
#The password for the root user of mysql db, change this before any production use.
|
#The password for the root user of mysql db, change this before any production use.
|
||||||
db_password = root123
|
db_password = root123
|
||||||
|
|
||||||
|
@ -101,6 +101,7 @@ else:
|
|||||||
ldap_filter = ""
|
ldap_filter = ""
|
||||||
ldap_uid = rcp.get("configuration", "ldap_uid")
|
ldap_uid = rcp.get("configuration", "ldap_uid")
|
||||||
ldap_scope = rcp.get("configuration", "ldap_scope")
|
ldap_scope = rcp.get("configuration", "ldap_scope")
|
||||||
|
ldap_connect_timeout = rcp.get("configuration", "ldap_connect_timeout")
|
||||||
db_password = rcp.get("configuration", "db_password")
|
db_password = rcp.get("configuration", "db_password")
|
||||||
self_registration = rcp.get("configuration", "self_registration")
|
self_registration = rcp.get("configuration", "self_registration")
|
||||||
use_compressed_js = rcp.get("configuration", "use_compressed_js")
|
use_compressed_js = rcp.get("configuration", "use_compressed_js")
|
||||||
@ -201,6 +202,7 @@ render(os.path.join(templates_dir, "ui", "env"),
|
|||||||
ldap_filter=ldap_filter,
|
ldap_filter=ldap_filter,
|
||||||
ldap_uid=ldap_uid,
|
ldap_uid=ldap_uid,
|
||||||
ldap_scope=ldap_scope,
|
ldap_scope=ldap_scope,
|
||||||
|
ldap_connect_timeout=ldap_connect_timeout,
|
||||||
self_registration=self_registration,
|
self_registration=self_registration,
|
||||||
use_compressed_js=use_compressed_js,
|
use_compressed_js=use_compressed_js,
|
||||||
ui_secret=ui_secret,
|
ui_secret=ui_secret,
|
||||||
|
@ -18,7 +18,7 @@ COPY ./src/favicon.ico /harbor/favicon.ico
|
|||||||
COPY ./make/jsminify.sh /tmp/jsminify.sh
|
COPY ./make/jsminify.sh /tmp/jsminify.sh
|
||||||
|
|
||||||
RUN chmod u+x /harbor/harbor_ui \
|
RUN chmod u+x /harbor/harbor_ui \
|
||||||
&& sed -i 's/TLS_CACERT/#TLS_CAERT/g' /etc/ldap/ldap.conf \
|
&& sed -i 's/TLS_CACERT/#TLS_CACERT/g' /etc/ldap/ldap.conf \
|
||||||
&& sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf \
|
&& sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf \
|
||||||
&& timestamp=`date '+%s'` \
|
&& timestamp=`date '+%s'` \
|
||||||
&& /tmp/jsminify.sh /harbor/views/sections/script-include.htm /harbor/static/resources/js/harbor.app.min.$timestamp.js /harbor/ \
|
&& /tmp/jsminify.sh /harbor/views/sections/script-include.htm /harbor/static/resources/js/harbor.app.min.$timestamp.js /harbor/ \
|
||||||
|
@ -18,7 +18,11 @@ package ldap
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
|
|
||||||
@ -27,7 +31,7 @@ import (
|
|||||||
"github.com/vmware/harbor/src/ui/auth"
|
"github.com/vmware/harbor/src/ui/auth"
|
||||||
"github.com/vmware/harbor/src/ui/config"
|
"github.com/vmware/harbor/src/ui/config"
|
||||||
|
|
||||||
"github.com/mqu/openldap"
|
goldap "gopkg.in/ldap.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Auth implements Authenticator interface to authenticate against LDAP
|
// Auth implements Authenticator interface to authenticate against LDAP
|
||||||
@ -35,6 +39,59 @@ type Auth struct{}
|
|||||||
|
|
||||||
const metaChars = "&|!=~*<>()"
|
const metaChars = "&|!=~*<>()"
|
||||||
|
|
||||||
|
// Connect checks the LDAP configuration directives, and connects to the LDAP URL
|
||||||
|
// Returns an LDAP connection
|
||||||
|
func Connect() (*goldap.Conn, error) {
|
||||||
|
|
||||||
|
ldapURL := config.LDAP().URL
|
||||||
|
if ldapURL == "" {
|
||||||
|
return nil, errors.New("can not get any available LDAP_URL")
|
||||||
|
}
|
||||||
|
log.Debug("ldapURL:", ldapURL)
|
||||||
|
|
||||||
|
// This routine keeps compability with the old format used on harbor.cfg
|
||||||
|
splitLdapURL := strings.Split(ldapURL, "://")
|
||||||
|
protocol, hostport := splitLdapURL[0], splitLdapURL[1]
|
||||||
|
|
||||||
|
var host, port string
|
||||||
|
|
||||||
|
// This tries to detect the used port, if not defined
|
||||||
|
if strings.Contains(hostport, ":") {
|
||||||
|
splitHostPort := strings.Split(hostport, ":")
|
||||||
|
host, port = splitHostPort[0], splitHostPort[1]
|
||||||
|
} else {
|
||||||
|
host = hostport
|
||||||
|
switch protocol {
|
||||||
|
case "ldap":
|
||||||
|
port = "389"
|
||||||
|
case "ldaps":
|
||||||
|
port = "636"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets a Dial Timeout for LDAP
|
||||||
|
cTimeout := config.LDAP().ConnectTimeout
|
||||||
|
connectTimeout, _ := strconv.Atoi(cTimeout)
|
||||||
|
goldap.DefaultTimeout = time.Duration(connectTimeout) * time.Second
|
||||||
|
|
||||||
|
var ldap *goldap.Conn
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch protocol {
|
||||||
|
case "ldap":
|
||||||
|
ldap, err = goldap.Dial("tcp", fmt.Sprintf("%s:%s", host, port))
|
||||||
|
case "ldaps":
|
||||||
|
ldap, err = goldap.DialTLS("tcp", fmt.Sprintf("%s:%s", host, port), &tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ldap, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Authenticate checks user's credential against LDAP based on basedn template and LDAP URL,
|
// Authenticate checks user's credential against LDAP based on basedn template and LDAP URL,
|
||||||
// if the check is successful a dummy record will be inserted into DB, such that this user can
|
// if the check is successful a dummy record will be inserted into DB, such that this user can
|
||||||
// be associated to other entities in the system.
|
// be associated to other entities in the system.
|
||||||
@ -46,16 +103,11 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
|
|||||||
return nil, fmt.Errorf("the principal contains meta char: %q", c)
|
return nil, fmt.Errorf("the principal contains meta char: %q", c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ldapURL := config.LDAP().URL
|
|
||||||
if ldapURL == "" {
|
ldap, err := Connect()
|
||||||
return nil, errors.New("can not get any available LDAP_URL")
|
|
||||||
}
|
|
||||||
log.Debug("ldapURL:", ldapURL)
|
|
||||||
ldap, err := openldap.Initialize(ldapURL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ldap.SetOption(openldap.LDAP_OPT_PROTOCOL_VERSION, openldap.LDAP_VERSION3)
|
|
||||||
|
|
||||||
ldapBaseDn := config.LDAP().BaseDn
|
ldapBaseDn := config.LDAP().BaseDn
|
||||||
if ldapBaseDn == "" {
|
if ldapBaseDn == "" {
|
||||||
@ -86,27 +138,42 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
|
|||||||
ldapScope := config.LDAP().Scope
|
ldapScope := config.LDAP().Scope
|
||||||
var scope int
|
var scope int
|
||||||
if ldapScope == "1" {
|
if ldapScope == "1" {
|
||||||
scope = openldap.LDAP_SCOPE_BASE
|
scope = goldap.ScopeBaseObject
|
||||||
} else if ldapScope == "2" {
|
} else if ldapScope == "2" {
|
||||||
scope = openldap.LDAP_SCOPE_ONELEVEL
|
scope = goldap.ScopeSingleLevel
|
||||||
} else {
|
} else {
|
||||||
scope = openldap.LDAP_SCOPE_SUBTREE
|
scope = goldap.ScopeWholeSubtree
|
||||||
}
|
}
|
||||||
attributes := []string{"uid", "cn", "mail", "email"}
|
attributes := []string{"uid", "cn", "mail", "email"}
|
||||||
result, err := ldap.SearchAll(ldapBaseDn, scope, filter, attributes)
|
|
||||||
|
searchRequest := goldap.NewSearchRequest(
|
||||||
|
ldapBaseDn,
|
||||||
|
scope,
|
||||||
|
goldap.NeverDerefAliases,
|
||||||
|
0, // Unlimited results. TODO: Limit this (as we expect only one result)?
|
||||||
|
0, // Search Timeout. TODO: Limit this (check what is the unit of timeout) and make configurable
|
||||||
|
false, // Types Only
|
||||||
|
filter,
|
||||||
|
attributes,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
result, err := ldap.Search(searchRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(result.Entries()) == 0 {
|
|
||||||
|
if len(result.Entries) == 0 {
|
||||||
log.Warningf("Not found an entry.")
|
log.Warningf("Not found an entry.")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
} else if len(result.Entries()) != 1 {
|
} else if len(result.Entries) != 1 {
|
||||||
log.Warningf("Found more than one entry.")
|
log.Warningf("Found more than one entry.")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
en := result.Entries()[0]
|
|
||||||
bindDN := en.Dn()
|
entry := result.Entries[0]
|
||||||
log.Debug("found entry:", en)
|
bindDN := entry.DN
|
||||||
|
log.Debug("found entry:", bindDN)
|
||||||
err = ldap.Bind(bindDN, m.Password)
|
err = ldap.Bind(bindDN, m.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Bind user error", err)
|
log.Debug("Bind user error", err)
|
||||||
@ -115,9 +182,10 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
|
|||||||
defer ldap.Close()
|
defer ldap.Close()
|
||||||
|
|
||||||
u := models.User{}
|
u := models.User{}
|
||||||
for _, attr := range en.Attributes() {
|
|
||||||
val := attr.Values()[0]
|
for _, attr := range entry.Attributes {
|
||||||
switch attr.Name() {
|
val := attr.Values[0]
|
||||||
|
switch attr.Name {
|
||||||
case "uid":
|
case "uid":
|
||||||
u.Realname = val
|
u.Realname = val
|
||||||
case "cn":
|
case "cn":
|
||||||
|
@ -26,13 +26,14 @@ import (
|
|||||||
|
|
||||||
// LDAPSetting wraps the setting of an LDAP server
|
// LDAPSetting wraps the setting of an LDAP server
|
||||||
type LDAPSetting struct {
|
type LDAPSetting struct {
|
||||||
URL string
|
URL string
|
||||||
BaseDn string
|
BaseDn string
|
||||||
SearchDn string
|
SearchDn string
|
||||||
SearchPwd string
|
SearchPwd string
|
||||||
UID string
|
UID string
|
||||||
Filter string
|
Filter string
|
||||||
Scope string
|
Scope string
|
||||||
|
ConnectTimeout string
|
||||||
}
|
}
|
||||||
|
|
||||||
type uiParser struct{}
|
type uiParser struct{}
|
||||||
@ -42,13 +43,14 @@ func (up *uiParser) Parse(raw map[string]string, config map[string]interface{})
|
|||||||
mode := raw["AUTH_MODE"]
|
mode := raw["AUTH_MODE"]
|
||||||
if mode == "ldap_auth" {
|
if mode == "ldap_auth" {
|
||||||
setting := LDAPSetting{
|
setting := LDAPSetting{
|
||||||
URL: raw["LDAP_URL"],
|
URL: raw["LDAP_URL"],
|
||||||
BaseDn: raw["LDAP_BASE_DN"],
|
BaseDn: raw["LDAP_BASE_DN"],
|
||||||
SearchDn: raw["LDAP_SEARCH_DN"],
|
SearchDn: raw["LDAP_SEARCH_DN"],
|
||||||
SearchPwd: raw["LDAP_SEARCH_PWD"],
|
SearchPwd: raw["LDAP_SEARCH_PWD"],
|
||||||
UID: raw["LDAP_UID"],
|
UID: raw["LDAP_UID"],
|
||||||
Filter: raw["LDAP_FILTER"],
|
Filter: raw["LDAP_FILTER"],
|
||||||
Scope: raw["LDAP_SCOPE"],
|
Scope: raw["LDAP_SCOPE"],
|
||||||
|
ConnectTimeout: raw["LDAP_CONNECT_TIMEOUT"],
|
||||||
}
|
}
|
||||||
config["ldap"] = setting
|
config["ldap"] = setting
|
||||||
}
|
}
|
||||||
@ -83,7 +85,7 @@ func (up *uiParser) Parse(raw map[string]string, config map[string]interface{})
|
|||||||
var uiConfig *commonConfig.Config
|
var uiConfig *commonConfig.Config
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
uiKeys := []string{"AUTH_MODE", "LDAP_URL", "LDAP_BASE_DN", "LDAP_SEARCH_DN", "LDAP_SEARCH_PWD", "LDAP_UID", "LDAP_FILTER", "LDAP_SCOPE", "TOKEN_EXPIRATION", "HARBOR_ADMIN_PASSWORD", "EXT_REG_URL", "UI_SECRET", "SECRET_KEY", "SELF_REGISTRATION", "PROJECT_CREATION_RESTRICTION", "REGISTRY_URL", "JOB_SERVICE_URL"}
|
uiKeys := []string{"AUTH_MODE", "LDAP_URL", "LDAP_BASE_DN", "LDAP_SEARCH_DN", "LDAP_SEARCH_PWD", "LDAP_UID", "LDAP_FILTER", "LDAP_SCOPE", "LDAP_CONNECT_TIMEOUT", "TOKEN_EXPIRATION", "HARBOR_ADMIN_PASSWORD", "EXT_REG_URL", "UI_SECRET", "SECRET_KEY", "SELF_REGISTRATION", "PROJECT_CREATION_RESTRICTION", "REGISTRY_URL", "JOB_SERVICE_URL"}
|
||||||
uiConfig = &commonConfig.Config{
|
uiConfig = &commonConfig.Config{
|
||||||
Config: make(map[string]interface{}),
|
Config: make(map[string]interface{}),
|
||||||
Loader: &commonConfig.EnvConfigLoader{Keys: uiKeys},
|
Loader: &commonConfig.EnvConfigLoader{Keys: uiKeys},
|
||||||
|
@ -29,6 +29,7 @@ var (
|
|||||||
"cn",
|
"cn",
|
||||||
"uid",
|
"uid",
|
||||||
"2",
|
"2",
|
||||||
|
"5",
|
||||||
}
|
}
|
||||||
tokenExp = "3"
|
tokenExp = "3"
|
||||||
tokenExpRes = 3
|
tokenExpRes = 3
|
||||||
@ -52,6 +53,7 @@ func TestMain(m *testing.M) {
|
|||||||
os.Setenv("LDAP_UID", ldap.UID)
|
os.Setenv("LDAP_UID", ldap.UID)
|
||||||
os.Setenv("LDAP_SCOPE", ldap.Scope)
|
os.Setenv("LDAP_SCOPE", ldap.Scope)
|
||||||
os.Setenv("LDAP_FILTER", ldap.Filter)
|
os.Setenv("LDAP_FILTER", ldap.Filter)
|
||||||
|
os.Setenv("LDAP_CONNECT_TIMEOUT", ldap.ConnectTimeout)
|
||||||
os.Setenv("TOKEN_EXPIRATION", tokenExp)
|
os.Setenv("TOKEN_EXPIRATION", tokenExp)
|
||||||
os.Setenv("HARBOR_ADMIN_PASSWORD", adminPassword)
|
os.Setenv("HARBOR_ADMIN_PASSWORD", adminPassword)
|
||||||
os.Setenv("EXT_REG_URL", externalRegURL)
|
os.Setenv("EXT_REG_URL", externalRegURL)
|
||||||
@ -76,6 +78,7 @@ func TestMain(m *testing.M) {
|
|||||||
os.Unsetenv("LDAP_UID")
|
os.Unsetenv("LDAP_UID")
|
||||||
os.Unsetenv("LDAP_SCOPE")
|
os.Unsetenv("LDAP_SCOPE")
|
||||||
os.Unsetenv("LDAP_FILTER")
|
os.Unsetenv("LDAP_FILTER")
|
||||||
|
os.Unsetenv("LDAP_CONNECT_TIMEOUT")
|
||||||
os.Unsetenv("TOKEN_EXPIRATION")
|
os.Unsetenv("TOKEN_EXPIRATION")
|
||||||
os.Unsetenv("HARBOR_ADMIN_PASSWORD")
|
os.Unsetenv("HARBOR_ADMIN_PASSWORD")
|
||||||
os.Unsetenv("EXT_REG_URL")
|
os.Unsetenv("EXT_REG_URL")
|
||||||
|
24
src/vendor/github.com/mqu/openldap/LICENCE.txt
generated
vendored
24
src/vendor/github.com/mqu/openldap/LICENCE.txt
generated
vendored
@ -1,24 +0,0 @@
|
|||||||
Copyright (C) 2012 - Marc Quinton.
|
|
||||||
|
|
||||||
Use of this source code is governed by the MIT Licence :
|
|
||||||
http://opensource.org/licenses/mit-license.php
|
|
||||||
|
|
||||||
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 AUTHOR 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.
|
|
||||||
|
|
82
src/vendor/github.com/mqu/openldap/README.md
generated
vendored
82
src/vendor/github.com/mqu/openldap/README.md
generated
vendored
@ -1,82 +0,0 @@
|
|||||||
OpenLDAP
|
|
||||||
====
|
|
||||||
|
|
||||||
this is Openldap binding in GO language. I don't work any more with golang, so, please fork this project.
|
|
||||||
|
|
||||||
|
|
||||||
Installation :
|
|
||||||
-----
|
|
||||||
|
|
||||||
Installation is easy and very quick, as you can see :
|
|
||||||
|
|
||||||
# install openldap library and devel packages
|
|
||||||
sudo apt-get install libldap libldap2-dev # debian/ubuntu.
|
|
||||||
sudo urpmi openldap-devel # fedora, RH, ...
|
|
||||||
|
|
||||||
# install go
|
|
||||||
go get github.com/mqu/openldap
|
|
||||||
|
|
||||||
# verify you've got it :
|
|
||||||
(cd $GOPATH ; go list ./...) | grep openldap
|
|
||||||
|
|
||||||
Usage
|
|
||||||
----
|
|
||||||
|
|
||||||
- Look a this [exemple](https://github.com/mqu/openldap/blob/master/_examples/test-openldap.go).
|
|
||||||
- a more complex example making [LDAP search](https://github.com/mqu/openldap/blob/master/_examples/ldapsearch.go) that mimics ldapsearch command, printing out result on console.
|
|
||||||
|
|
||||||
Doc:
|
|
||||||
---
|
|
||||||
- run _go doc openldap_,
|
|
||||||
- will come soon, complete documentation in this [Wiki](https://github.com/mqu/openldap/wiki).
|
|
||||||
- look at [_examples/](https://github.com/mqu/openldap/blob/master/_examples/)*.go to see how to use this library.
|
|
||||||
|
|
||||||
Todo :
|
|
||||||
----
|
|
||||||
|
|
||||||
- thread-safe test,
|
|
||||||
- complete LDAP:GetOption() and LDAP:SetOption() method : now, they work only for integer values,
|
|
||||||
- avoid using deprecated function (see LDAP_DEPRECATED flag and "// DEPRECATED" comments in *.go sources),
|
|
||||||
- write some tests,
|
|
||||||
- verify memory leaks (Valgrind),
|
|
||||||
- support LDIF format (in, out),
|
|
||||||
- add support for external commands (ldapadd, ldapdelete)
|
|
||||||
- create an LDAP CLI (command line interface), like lftp, with commands like shell,
|
|
||||||
- a nice GUI with GTK,
|
|
||||||
- proxy, server,
|
|
||||||
- what else ?
|
|
||||||
|
|
||||||
|
|
||||||
Links :
|
|
||||||
----
|
|
||||||
|
|
||||||
- goc : http://code.google.com/p/go-wiki/wiki/cgo (how to bind native libraries to GO)
|
|
||||||
- Openldap library (and server) : http://www.openldap.org/
|
|
||||||
- Pure Go [LDAP](https://github.com/mmitton/ldap) library, with [ASN1](https://github.com/mmitton/asn1-ber) support.
|
|
||||||
|
|
||||||
Licence :
|
|
||||||
----
|
|
||||||
|
|
||||||
Copyright (C) 2012 - Marc Quinton.
|
|
||||||
|
|
||||||
Use of this source code is governed by the MIT Licence :
|
|
||||||
http://opensource.org/licenses/mit-license.php
|
|
||||||
|
|
||||||
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 AUTHOR 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.
|
|
230
src/vendor/github.com/mqu/openldap/add-modify-delete.go
generated
vendored
230
src/vendor/github.com/mqu/openldap/add-modify-delete.go
generated
vendored
@ -1,230 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 - Marc Quinton.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by the MIT Licence :
|
|
||||||
* http://opensource.org/licenses/mit-license.php
|
|
||||||
*
|
|
||||||
* 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 AUTHOR 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package openldap
|
|
||||||
|
|
||||||
/* #include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#define LDAP_DEPRECATED 1
|
|
||||||
#include <ldap.h>
|
|
||||||
|
|
||||||
// goc can not use union on structs ; so create a new type with same attributes and size
|
|
||||||
// fixme : support binary mods (mod_bvalues)
|
|
||||||
typedef struct ldapmod_str {
|
|
||||||
int mod_op;
|
|
||||||
char *mod_type;
|
|
||||||
char **mod_vals;
|
|
||||||
} LDAPModStr;
|
|
||||||
|
|
||||||
|
|
||||||
int _ldap_add(LDAP *ld, char* dn, LDAPModStr **attrs){
|
|
||||||
|
|
||||||
//API: int ldap_add_ext_s(LDAP *ld, char *dn, LDAPMod **attrs, LDAPControl *sctrls, LDAPControl *cctrls );
|
|
||||||
// nota : cast (LDAPMod **) is possible because structs have same size
|
|
||||||
return ldap_add_ext_s(ld, dn, (LDAPMod **)attrs, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _ldap_modify(LDAP *ld, char* dn, LDAPModStr **mods ){
|
|
||||||
|
|
||||||
// nota : cast (LDAPMod **) is possible because structs have same size
|
|
||||||
return ldap_modify_ext_s( ld, dn, (LDAPMod **)mods, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _ldap_rename (LDAP *ld, char *dn, char *newrdn, char *newSuperior, int deleteoldrdn){
|
|
||||||
//API: int ldap_rename_s( ld, dn, newrdn, newparent, deleteoldrdn, sctrls[], cctrls[])
|
|
||||||
|
|
||||||
return ldap_rename_s(ld, dn, newrdn, newSuperior, deleteoldrdn, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _ldap_mods_free (LDAPModStr **mods, int freemods){
|
|
||||||
//API: void ldap_mods_free(LDAPMod **mods, int freemods);
|
|
||||||
return ldap_mods_free((LDAPMod **)mods, freemods);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
// #cgo CFLAGS: -DLDAP_DEPRECATED=1
|
|
||||||
// #cgo linux CFLAGS: -DLINUX=1
|
|
||||||
// #cgo LDFLAGS: -lldap -llber
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"unsafe"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func (self *Ldap) doModify(dn string, attrs map[string][]string, changeType int, full_add bool) (int){
|
|
||||||
|
|
||||||
_dn := C.CString(dn)
|
|
||||||
defer C.free(unsafe.Pointer(_dn))
|
|
||||||
|
|
||||||
mods := make([]*C.LDAPModStr, len(attrs)+1)
|
|
||||||
// mods[len] = nil by default
|
|
||||||
|
|
||||||
count:= 0
|
|
||||||
for key, values := range attrs {
|
|
||||||
|
|
||||||
// transform []string to C.char** null terminated array (attributes argument)
|
|
||||||
_values := make([]*C.char, len(values)+1) // default set to nil (NULL in C)
|
|
||||||
|
|
||||||
for i, value := range values {
|
|
||||||
_values[i] = C.CString(value)
|
|
||||||
defer C.free(unsafe.Pointer(_values[i]))
|
|
||||||
}
|
|
||||||
|
|
||||||
var mod C.LDAPModStr
|
|
||||||
|
|
||||||
mod.mod_op = C.int(changeType)
|
|
||||||
mod.mod_type = C.CString(key)
|
|
||||||
mod.mod_vals = &_values[0]
|
|
||||||
|
|
||||||
defer C.free(unsafe.Pointer(mod.mod_type))
|
|
||||||
|
|
||||||
mods[count] = &mod
|
|
||||||
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
|
|
||||||
var rv int
|
|
||||||
|
|
||||||
if full_add {
|
|
||||||
// API: int ldap_add (LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods )
|
|
||||||
rv = int(C._ldap_add(self.conn, _dn, &mods[0]))
|
|
||||||
} else{
|
|
||||||
// API: int ldap_modify (LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods )
|
|
||||||
rv = int(C._ldap_modify(self.conn, _dn, &mods[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: need to call ldap_mods_free(&mods) some where.
|
|
||||||
// C._ldap_mods_free(&mods[0], 1) // not OK.
|
|
||||||
return rv
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Ldap) Modify(dn string, attrs map[string][]string) (error){
|
|
||||||
|
|
||||||
changeType := C.LDAP_MOD_REPLACE
|
|
||||||
full_add := false
|
|
||||||
rv := self.doModify(dn, attrs, changeType, full_add)
|
|
||||||
|
|
||||||
if rv != LDAP_OPT_SUCCESS {
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::Modify() error : %d (%s)", rv, ErrorToString(rv)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Ldap) ModifyDel(dn string, attrs map[string][]string) (error){
|
|
||||||
|
|
||||||
changeType := C.LDAP_MOD_DELETE
|
|
||||||
full_add := false
|
|
||||||
rv := self.doModify(dn, attrs, changeType, full_add)
|
|
||||||
|
|
||||||
if rv != LDAP_OPT_SUCCESS {
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::ModifyDel() error : %d (%s)", rv, ErrorToString(rv)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Ldap) ModifyAdd(dn string, attrs map[string][]string) (error){
|
|
||||||
|
|
||||||
changeType := C.LDAP_MOD_ADD
|
|
||||||
full_add := false
|
|
||||||
rv := self.doModify(dn, attrs, changeType, full_add)
|
|
||||||
if rv != LDAP_OPT_SUCCESS {
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::ModifyAdd() error : %d (%s)", rv, ErrorToString(rv)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Ldap) Add(dn string, attrs map[string][]string) (error){
|
|
||||||
|
|
||||||
changeType := C.LDAP_MOD_ADD
|
|
||||||
full_add := true
|
|
||||||
rv := self.doModify(dn, attrs, changeType, full_add)
|
|
||||||
if rv != LDAP_OPT_SUCCESS {
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::Add() error : %d (%s)", rv, ErrorToString(rv)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Ldap) Delete(dn string) (error){
|
|
||||||
|
|
||||||
_dn := C.CString(dn)
|
|
||||||
defer C.free(unsafe.Pointer(_dn))
|
|
||||||
|
|
||||||
// API: int ldap_delete (LDAP *ld, LDAP_CONST char *dn)
|
|
||||||
rv := C.ldap_delete_s(self.conn, _dn)
|
|
||||||
|
|
||||||
if rv != LDAP_OPT_SUCCESS {
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::Delete() error : %d (%s)", rv, ErrorToString(int(rv))))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rename() to rename LDAP entries.
|
|
||||||
//
|
|
||||||
// These routines are used to perform a LDAP rename operation. The function changes the leaf compo-
|
|
||||||
// nent of an entry's distinguished name and optionally moves the entry to a new parent container.
|
|
||||||
// The ldap_rename_s performs a rename operation synchronously. The method takes dn, which points to
|
|
||||||
// the distinguished name of the entry whose attribute is being compared, newparent,the distinguished
|
|
||||||
// name of the entry's new parent. If this parameter is NULL, only the RDN is changed. The root DN is
|
|
||||||
// specified by passing a zero length string, "". deleteoldrdn specifies whether the old RDN should
|
|
||||||
// be retained or deleted. Zero indicates that the old RDN should be retained. If you choose this
|
|
||||||
// option, the attribute will contain both names (the old and the new). Non-zero indicates that the
|
|
||||||
// old RDN should be deleted. serverctrls points to an array of LDAPControl structures that list the
|
|
||||||
// client controls to use with this extended operation. Use NULL to specify no client controls.
|
|
||||||
// clientctrls points to an array of LDAPControl structures that list the client controls to use with
|
|
||||||
// the search.
|
|
||||||
// FIXME: support NULL and "" values for newSuperior parameter.
|
|
||||||
//
|
|
||||||
func (self *Ldap) Rename(dn string, newrdn string, newSuperior string, deleteOld bool) (error){
|
|
||||||
|
|
||||||
_dn := C.CString(dn)
|
|
||||||
defer C.free(unsafe.Pointer(_dn))
|
|
||||||
|
|
||||||
_newrdn := C.CString(newrdn)
|
|
||||||
defer C.free(unsafe.Pointer(_newrdn))
|
|
||||||
|
|
||||||
_newSuperior := C.CString(newSuperior)
|
|
||||||
defer C.free(unsafe.Pointer(_newSuperior))
|
|
||||||
|
|
||||||
var _delete C.int = 0
|
|
||||||
if deleteOld {
|
|
||||||
_delete = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// API: int ldap_rename (LDAP *ld, char *newrdn, char *newSuperior, int deleteoldrdn)
|
|
||||||
rv := C._ldap_rename(self.conn, _dn, _newrdn, _newSuperior, _delete)
|
|
||||||
|
|
||||||
if rv != LDAP_OPT_SUCCESS {
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::Rename() error : %d (%s)", rv, ErrorToString(int(rv))))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
350
src/vendor/github.com/mqu/openldap/defines.go
generated
vendored
350
src/vendor/github.com/mqu/openldap/defines.go
generated
vendored
@ -1,350 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 - Marc Quinton.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by the MIT Licence :
|
|
||||||
* http://opensource.org/licenses/mit-license.php
|
|
||||||
*
|
|
||||||
* 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 AUTHOR 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package openldap
|
|
||||||
|
|
||||||
const (
|
|
||||||
// first version for this GO API binding
|
|
||||||
OPENLDAP_API_BINDING_VERSION = "0.2"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_VERSION1 = 1
|
|
||||||
LDAP_VERSION2 = 2
|
|
||||||
LDAP_VERSION3 = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_VERSION_MIN = LDAP_VERSION2
|
|
||||||
LDAP_VERSION = LDAP_VERSION2
|
|
||||||
LDAP_VERSION_MAX = LDAP_VERSION3
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_API_VERSION = 3001
|
|
||||||
LDAP_VENDOR_NAME = "OpenLDAP"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_PORT = 389
|
|
||||||
LDAPS_PORT = 636
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_OPT_SUCCESS = 0
|
|
||||||
LDAP_OPT_ERROR = -1
|
|
||||||
)
|
|
||||||
|
|
||||||
// search scopes
|
|
||||||
const (
|
|
||||||
LDAP_SCOPE_BASE = 0x0000
|
|
||||||
LDAP_SCOPE_ONELEVEL = 0x0001
|
|
||||||
LDAP_SCOPE_SUBTREE = 0x0002
|
|
||||||
LDAP_SCOPE_SUBORDINATE = 0x0003 // OpenLDAP extension
|
|
||||||
LDAP_SCOPE_DEFAULT = -1 // OpenLDAP extension
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_SCOPE_BASEOBJECT = LDAP_SCOPE_BASE
|
|
||||||
LDAP_SCOPE_ONE = LDAP_SCOPE_ONELEVEL
|
|
||||||
LDAP_SCOPE_SUB = LDAP_SCOPE_SUBTREE
|
|
||||||
LDAP_SCOPE_CHILDREN = LDAP_SCOPE_SUBORDINATE
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_RES_ANY = -1
|
|
||||||
LDAP_RES_UNSOLICITED = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
//const (
|
|
||||||
//LDAP_API_FEATURE_THREAD_SAFE = 1
|
|
||||||
//LDAP_API_FEATURE_SESSION_THREAD_SAFE = 1
|
|
||||||
//LDAP_API_FEATURE_OPERATION_THREAD_SAFE = 1
|
|
||||||
//)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_SUCCESS = 0x00
|
|
||||||
LDAP_OPERATIONS_ERROR = 0x01
|
|
||||||
LDAP_PROTOCOL_ERROR = 0x02
|
|
||||||
LDAP_TIMELIMIT_EXCEEDED = 0x03
|
|
||||||
LDAP_SIZELIMIT_EXCEEDED = 0x04
|
|
||||||
LDAP_COMPARE_FALSE = 0x05
|
|
||||||
LDAP_COMPARE_TRUE = 0x06
|
|
||||||
LDAP_AUTH_METHOD_NOT_SUPPORTED = 0x07
|
|
||||||
LDAP_STRONG_AUTH_REQUIRED = 0x08
|
|
||||||
// Not used in LDAPv3
|
|
||||||
LDAP_PARTIAL_RESULTS = 0x09
|
|
||||||
|
|
||||||
// Next 5 new in LDAPv3
|
|
||||||
LDAP_REFERRAL = 0x0a
|
|
||||||
LDAP_ADMINLIMIT_EXCEEDED = 0x0b
|
|
||||||
LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 0x0c
|
|
||||||
LDAP_CONFIDENTIALITY_REQUIRED = 0x0d
|
|
||||||
LDAP_SASL_BIND_INPROGRESS = 0x0e
|
|
||||||
|
|
||||||
LDAP_NO_SUCH_ATTRIBUTE = 0x10
|
|
||||||
LDAP_UNDEFINED_TYPE = 0x11
|
|
||||||
LDAP_INAPPROPRIATE_MATCHING = 0x12
|
|
||||||
LDAP_CONSTRAINT_VIOLATION = 0x13
|
|
||||||
LDAP_TYPE_OR_VALUE_EXISTS = 0x14
|
|
||||||
LDAP_INVALID_SYNTAX = 0x15
|
|
||||||
|
|
||||||
LDAP_NO_SUCH_OBJECT = 0x20 /* 32 */
|
|
||||||
LDAP_ALIAS_PROBLEM = 0x21
|
|
||||||
LDAP_INVALID_DN_SYNTAX = 0x22
|
|
||||||
// Next two not used in LDAPv3
|
|
||||||
LDAP_IS_LEAF = 0x23
|
|
||||||
LDAP_ALIAS_DEREF_PROBLEM = 0x24
|
|
||||||
|
|
||||||
LDAP_INAPPROPRIATE_AUTH = 0x30 /* 48 */
|
|
||||||
LDAP_INVALID_CREDENTIALS = 0x31 /* 49 */
|
|
||||||
LDAP_INSUFFICIENT_ACCESS = 0x32
|
|
||||||
LDAP_BUSY = 0x33
|
|
||||||
LDAP_UNAVAILABLE = 0x34
|
|
||||||
LDAP_UNWILLING_TO_PERFORM = 0x35
|
|
||||||
LDAP_LOOP_DETECT = 0x36
|
|
||||||
|
|
||||||
LDAP_SORT_CONTROL_MISSING = 0x3C /* 60 */
|
|
||||||
LDAP_INDEX_RANGE_ERROR = 0x3D /* 61 */
|
|
||||||
|
|
||||||
LDAP_NAMING_VIOLATION = 0x40
|
|
||||||
LDAP_OBJECT_CLASS_VIOLATION = 0x41
|
|
||||||
LDAP_NOT_ALLOWED_ON_NONLEAF = 0x42
|
|
||||||
LDAP_NOT_ALLOWED_ON_RDN = 0x43
|
|
||||||
LDAP_ALREADY_EXISTS = 0x44 /* 68 */
|
|
||||||
LDAP_NO_OBJECT_CLASS_MODS = 0x45
|
|
||||||
LDAP_RESULTS_TOO_LARGE = 0x46
|
|
||||||
// Next two for LDAPv3
|
|
||||||
LDAP_AFFECTS_MULTIPLE_DSAS = 0x47
|
|
||||||
LDAP_OTHER = 0x50
|
|
||||||
|
|
||||||
// Used by some APIs
|
|
||||||
LDAP_SERVER_DOWN = 0x51
|
|
||||||
LDAP_LOCAL_ERROR = 0x52
|
|
||||||
LDAP_ENCODING_ERROR = 0x53
|
|
||||||
LDAP_DECODING_ERROR = 0x54
|
|
||||||
LDAP_TIMEOUT = 0x55
|
|
||||||
LDAP_AUTH_UNKNOWN = 0x56
|
|
||||||
LDAP_FILTER_ERROR = 0x57 /* 87 */
|
|
||||||
LDAP_USER_CANCELLED = 0x58
|
|
||||||
LDAP_PARAM_ERROR = 0x59
|
|
||||||
LDAP_NO_MEMORY = 0x5a
|
|
||||||
|
|
||||||
// Preliminary LDAPv3 codes
|
|
||||||
LDAP_CONNECT_ERROR = 0x5b
|
|
||||||
LDAP_NOT_SUPPORTED = 0x5c
|
|
||||||
LDAP_CONTROL_NOT_FOUND = 0x5d
|
|
||||||
LDAP_NO_RESULTS_RETURNED = 0x5e
|
|
||||||
LDAP_MORE_RESULTS_TO_RETURN = 0x5f
|
|
||||||
LDAP_CLIENT_LOOP = 0x60
|
|
||||||
LDAP_REFERRAL_LIMIT_EXCEEDED = 0x61
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_DEREF_NEVER = 0
|
|
||||||
LDAP_DEREF_SEARCHING = 1
|
|
||||||
LDAP_DEREF_FINDING = 2
|
|
||||||
LDAP_DEREF_ALWAYS = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_NO_LIMIT = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_MSG_ONE = 0
|
|
||||||
LDAP_MSG_ALL = 1
|
|
||||||
LDAP_MSG_RECEIVED = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// LDAP_OPTions
|
|
||||||
// 0x0000 - 0x0fff reserved for api options
|
|
||||||
// 0x1000 - 0x3fff reserved for api extended options
|
|
||||||
// 0x4000 - 0x7fff reserved for private and experimental options
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_OPT_API_INFO = 0x0000
|
|
||||||
LDAP_OPT_DESC = 0x0001 // historic
|
|
||||||
LDAP_OPT_DEREF = 0x0002
|
|
||||||
LDAP_OPT_SIZELIMIT = 0x0003
|
|
||||||
LDAP_OPT_TIMELIMIT = 0x0004
|
|
||||||
// 0x05 - 0x07 not defined
|
|
||||||
|
|
||||||
LDAP_OPT_REFERRALS = 0x0008
|
|
||||||
LDAP_OPT_RESTART = 0x0009
|
|
||||||
// 0x0a - 0x10 not defined
|
|
||||||
|
|
||||||
LDAP_OPT_PROTOCOL_VERSION = 0x0011
|
|
||||||
LDAP_OPT_SERVER_CONTROLS = 0x0012
|
|
||||||
LDAP_OPT_CLIENT_CONTROLS = 0x0013
|
|
||||||
// 0x14 not defined
|
|
||||||
|
|
||||||
LDAP_OPT_API_FEATURE_INFO = 0x0015
|
|
||||||
// 0x16 - 0x2f not defined
|
|
||||||
|
|
||||||
LDAP_OPT_HOST_NAME = 0x0030
|
|
||||||
LDAP_OPT_RESULT_CODE = 0x0031
|
|
||||||
LDAP_OPT_ERROR_NUMBER = LDAP_OPT_RESULT_CODE
|
|
||||||
LDAP_OPT_DIAGNOSTIC_MESSAGE = 0x0032
|
|
||||||
LDAP_OPT_ERROR_STRING = LDAP_OPT_DIAGNOSTIC_MESSAGE
|
|
||||||
LDAP_OPT_MATCHED_DN = 0x0033
|
|
||||||
// 0x0034 - 0x3fff not defined
|
|
||||||
|
|
||||||
// 0x0091 used by Microsoft for LDAP_OPT_AUTO_RECONNECT
|
|
||||||
|
|
||||||
LDAP_OPT_SSPI_FLAGS = 0x0092
|
|
||||||
// 0x0093 used by Microsoft for LDAP_OPT_SSL_INFO
|
|
||||||
|
|
||||||
// 0x0094 used by Microsoft for LDAP_OPT_REF_DEREF_CONN_PER_MSG
|
|
||||||
|
|
||||||
LDAP_OPT_SIGN = 0x0095
|
|
||||||
LDAP_OPT_ENCRYPT = 0x0096
|
|
||||||
LDAP_OPT_SASL_METHOD = 0x0097
|
|
||||||
// 0x0098 used by Microsoft for LDAP_OPT_AREC_EXCLUSIVE
|
|
||||||
|
|
||||||
LDAP_OPT_SECURITY_CONTEXT = 0x0099
|
|
||||||
|
|
||||||
// 0x009A used by Microsoft for LDAP_OPT_ROOTDSE_CACHE
|
|
||||||
|
|
||||||
// 0x009B - 0x3fff not defined
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
// API Extensions
|
|
||||||
|
|
||||||
const LDAP_OPT_API_EXTENSION_BASE = 0x4000 // API extensions
|
|
||||||
|
|
||||||
// private and experimental options
|
|
||||||
|
|
||||||
// OpenLDAP specific options
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_OPT_DEBUG_LEVEL = 0x5001 // debug level
|
|
||||||
LDAP_OPT_TIMEOUT = 0x5002 // default timeout
|
|
||||||
LDAP_OPT_REFHOPLIMIT = 0x5003 // ref hop limit
|
|
||||||
LDAP_OPT_NETWORK_TIMEOUT = 0x5005 // socket level timeout
|
|
||||||
LDAP_OPT_URI = 0x5006
|
|
||||||
LDAP_OPT_REFERRAL_URLS = 0x5007 // Referral URLs
|
|
||||||
LDAP_OPT_SOCKBUF = 0x5008 // sockbuf
|
|
||||||
LDAP_OPT_DEFBASE = 0x5009 // searchbase
|
|
||||||
LDAP_OPT_CONNECT_ASYNC = 0x5010 // create connections asynchronously
|
|
||||||
LDAP_OPT_CONNECT_CB = 0x5011 // connection callbacks
|
|
||||||
LDAP_OPT_SESSION_REFCNT = 0x5012 // session reference count
|
|
||||||
)
|
|
||||||
|
|
||||||
// OpenLDAP TLS options
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_OPT_X_TLS = 0x6000
|
|
||||||
LDAP_OPT_X_TLS_CTX = 0x6001 // OpenSSL CTX*
|
|
||||||
LDAP_OPT_X_TLS_CACERTFILE = 0x6002
|
|
||||||
LDAP_OPT_X_TLS_CACERTDIR = 0x6003
|
|
||||||
LDAP_OPT_X_TLS_CERTFILE = 0x6004
|
|
||||||
LDAP_OPT_X_TLS_KEYFILE = 0x6005
|
|
||||||
LDAP_OPT_X_TLS_REQUIRE_CERT = 0x6006
|
|
||||||
LDAP_OPT_X_TLS_PROTOCOL_MIN = 0x6007
|
|
||||||
LDAP_OPT_X_TLS_CIPHER_SUITE = 0x6008
|
|
||||||
LDAP_OPT_X_TLS_RANDOM_FILE = 0x6009
|
|
||||||
LDAP_OPT_X_TLS_SSL_CTX = 0x600a // OpenSSL SSL*
|
|
||||||
LDAP_OPT_X_TLS_CRLCHECK = 0x600b
|
|
||||||
LDAP_OPT_X_TLS_CONNECT_CB = 0x600c
|
|
||||||
LDAP_OPT_X_TLS_CONNECT_ARG = 0x600d
|
|
||||||
LDAP_OPT_X_TLS_DHFILE = 0x600e
|
|
||||||
LDAP_OPT_X_TLS_NEWCTX = 0x600f
|
|
||||||
LDAP_OPT_X_TLS_CRLFILE = 0x6010 // GNUtls only
|
|
||||||
LDAP_OPT_X_TLS_PACKAGE = 0x6011
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_OPT_X_TLS_NEVER = 0
|
|
||||||
LDAP_OPT_X_TLS_HARD = 1
|
|
||||||
LDAP_OPT_X_TLS_DEMAND = 2
|
|
||||||
LDAP_OPT_X_TLS_ALLOW = 3
|
|
||||||
LDAP_OPT_X_TLS_TRY = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_OPT_X_TLS_CRL_NONE = 0
|
|
||||||
LDAP_OPT_X_TLS_CRL_PEER = 1
|
|
||||||
LDAP_OPT_X_TLS_CRL_ALL = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// for LDAP_OPT_X_TLS_PROTOCOL_MIN
|
|
||||||
|
|
||||||
//!!! const (
|
|
||||||
//!!! LDAP_OPT_X_TLS_PROTOCOL(maj,min) = (((maj) << 8) + (min))
|
|
||||||
//!!! LDAP_OPT_X_TLS_PROTOCOL_SSL2 = (2 << 8)
|
|
||||||
//!!! LDAP_OPT_X_TLS_PROTOCOL_SSL3 = (3 << 8)
|
|
||||||
//!!! LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 = ((3 << 8) + 1)
|
|
||||||
//!!! LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 = ((3 << 8) + 2)
|
|
||||||
//!!! LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 = ((3 << 8) + 3)
|
|
||||||
//!!! )
|
|
||||||
|
|
||||||
// OpenLDAP SASL options
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_OPT_X_SASL_MECH = 0x6100
|
|
||||||
LDAP_OPT_X_SASL_REALM = 0x6101
|
|
||||||
LDAP_OPT_X_SASL_AUTHCID = 0x6102
|
|
||||||
LDAP_OPT_X_SASL_AUTHZID = 0x6103
|
|
||||||
LDAP_OPT_X_SASL_SSF = 0x6104 // read-only
|
|
||||||
LDAP_OPT_X_SASL_SSF_EXTERNAL = 0x6105 // write-only
|
|
||||||
LDAP_OPT_X_SASL_SECPROPS = 0x6106 // write-only
|
|
||||||
LDAP_OPT_X_SASL_SSF_MIN = 0x6107
|
|
||||||
LDAP_OPT_X_SASL_SSF_MAX = 0x6108
|
|
||||||
LDAP_OPT_X_SASL_MAXBUFSIZE = 0x6109
|
|
||||||
LDAP_OPT_X_SASL_MECHLIST = 0x610a // read-only
|
|
||||||
LDAP_OPT_X_SASL_NOCANON = 0x610b
|
|
||||||
LDAP_OPT_X_SASL_USERNAME = 0x610c // read-only
|
|
||||||
LDAP_OPT_X_SASL_GSS_CREDS = 0x610d
|
|
||||||
)
|
|
||||||
|
|
||||||
// OpenLDAP GSSAPI options
|
|
||||||
|
|
||||||
const (
|
|
||||||
LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT = 0x6200
|
|
||||||
LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL = 0x6201
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// OpenLDAP per connection tcp-keepalive settings
|
|
||||||
// (Linux only, ignored where unsupported)
|
|
||||||
const (
|
|
||||||
LDAP_OPT_X_KEEPALIVE_IDLE = 0x6300
|
|
||||||
LDAP_OPT_X_KEEPALIVE_PROBES = 0x6301
|
|
||||||
LDAP_OPT_X_KEEPALIVE_INTERVAL = 0x6302
|
|
||||||
)
|
|
||||||
|
|
||||||
/* authentication methods available */
|
|
||||||
const (
|
|
||||||
LDAP_AUTH_NONE = 0x00 // no authentication
|
|
||||||
LDAP_AUTH_SIMPLE = 0x80 // context specific + primitive
|
|
||||||
LDAP_AUTH_SASL = 0xa3 // context specific + constructed
|
|
||||||
LDAP_AUTH_KRBV4 = 0xff // means do both of the following
|
|
||||||
LDAP_AUTH_KRBV41 = 0x81 // context specific + primitive
|
|
||||||
LDAP_AUTH_KRBV42 = 0x82 // context specific + primitive
|
|
||||||
)
|
|
454
src/vendor/github.com/mqu/openldap/openldap.go
generated
vendored
454
src/vendor/github.com/mqu/openldap/openldap.go
generated
vendored
@ -1,454 +0,0 @@
|
|||||||
/*
|
|
||||||
* Openldap (2.4.30) binding in GO
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* link to ldap or ldap_r (for thread-safe binding)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 - Marc Quinton.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by the MIT Licence :
|
|
||||||
* http://opensource.org/licenses/mit-license.php
|
|
||||||
*
|
|
||||||
* 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 AUTHOR 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package openldap
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
#define LDAP_DEPRECATED 1
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ldap.h>
|
|
||||||
|
|
||||||
static inline char* to_charptr(const void* s) { return (char*)s; }
|
|
||||||
static inline LDAPControl** to_ldapctrlptr(const void* s) {
|
|
||||||
return (LDAPControl**) s;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// #cgo CFLAGS: -DLDAP_DEPRECATED=1
|
|
||||||
// #cgo linux CFLAGS: -DLINUX=1
|
|
||||||
// #cgo LDFLAGS: -lldap -llber
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"unsafe"
|
|
||||||
"strings"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
/* Intialize() open an LDAP connexion ; supported url formats :
|
|
||||||
*
|
|
||||||
* ldap://host:389/
|
|
||||||
* ldaps://secure-host:636/
|
|
||||||
*
|
|
||||||
* return values :
|
|
||||||
* - on success : LDAP object, nil
|
|
||||||
* - on error : nil and error with error description.
|
|
||||||
*/
|
|
||||||
func Initialize(url string) (*Ldap, error) {
|
|
||||||
_url := C.CString(url)
|
|
||||||
defer C.free(unsafe.Pointer(_url))
|
|
||||||
|
|
||||||
var ldap *C.LDAP
|
|
||||||
|
|
||||||
// API: int ldap_initialize (LDAP **ldp, LDAP_CONST char *url )
|
|
||||||
rv := C.ldap_initialize(&ldap, _url)
|
|
||||||
|
|
||||||
if rv != 0 {
|
|
||||||
err := errors.New(fmt.Sprintf("LDAP::Initialize() error (%d) : %s", rv, ErrorToString(int(rv))))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Ldap{ldap}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* StartTLS() is used for regular LDAP (not
|
|
||||||
* LDAPS) connections to establish encryption
|
|
||||||
* after the session is running.
|
|
||||||
*
|
|
||||||
* return value :
|
|
||||||
* - nil on success,
|
|
||||||
* - error with error description on error.
|
|
||||||
*/
|
|
||||||
func (self *Ldap) StartTLS() error {
|
|
||||||
var rv int
|
|
||||||
|
|
||||||
// API: int ldap_start_tls_s(LDAP *ld, LDAPControl **serverctrls, LDAPControl **clientctrls);
|
|
||||||
rv = int(C.ldap_start_tls_s(self.conn,
|
|
||||||
C.to_ldapctrlptr(unsafe.Pointer(nil)),
|
|
||||||
C.to_ldapctrlptr(unsafe.Pointer(nil))))
|
|
||||||
|
|
||||||
if rv == LDAP_OPT_SUCCESS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::StartTLS() error (%d) : %s", rv,
|
|
||||||
ErrorToString(rv)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Bind() is used for LDAP authentifications
|
|
||||||
*
|
|
||||||
* if who is empty this is an anonymous bind
|
|
||||||
* else this is an authentificated bind
|
|
||||||
*
|
|
||||||
* return value :
|
|
||||||
* - nil on succes,
|
|
||||||
* - error with error description on error.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
func (self *Ldap) Bind(who, cred string) error {
|
|
||||||
var rv int
|
|
||||||
|
|
||||||
authmethod := C.int(LDAP_AUTH_SIMPLE)
|
|
||||||
|
|
||||||
// DEPRECATED
|
|
||||||
// API: int ldap_bind_s (LDAP *ld, LDAP_CONST char *who, LDAP_CONST char *cred, int authmethod );
|
|
||||||
if who == "" {
|
|
||||||
_who := C.to_charptr(unsafe.Pointer(nil))
|
|
||||||
_cred := C.to_charptr(unsafe.Pointer(nil))
|
|
||||||
|
|
||||||
rv = int(C.ldap_bind_s(self.conn, _who, _cred, authmethod))
|
|
||||||
} else {
|
|
||||||
_who := C.CString(who)
|
|
||||||
_cred := C.CString(cred)
|
|
||||||
defer C.free(unsafe.Pointer(_who))
|
|
||||||
rv = int(C.ldap_bind_s(self.conn, _who, _cred, authmethod))
|
|
||||||
}
|
|
||||||
|
|
||||||
if rv == LDAP_OPT_SUCCESS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
self.conn = nil
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::Bind() error (%d) : %s", rv, ErrorToString(rv)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* close LDAP connexion
|
|
||||||
*
|
|
||||||
* return value :
|
|
||||||
* - nil on succes,
|
|
||||||
* - error with error description on error.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
func (self *Ldap) Close() error {
|
|
||||||
|
|
||||||
// DEPRECATED
|
|
||||||
// API: int ldap_unbind(LDAP *ld)
|
|
||||||
rv := C.ldap_unbind(self.conn)
|
|
||||||
|
|
||||||
if rv == LDAP_OPT_SUCCESS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
self.conn = nil
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::Close() error (%d) : %s", int(rv), ErrorToString(int(rv))))
|
|
||||||
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Unbind() close LDAP connexion
|
|
||||||
*
|
|
||||||
* an alias to Ldap::Close()
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
func (self *Ldap) Unbind() error {
|
|
||||||
return self.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search() is used to search LDAP server
|
|
||||||
- base is where search is starting
|
|
||||||
- scope allows local or deep search. Supported values :
|
|
||||||
- LDAP_SCOPE_BASE
|
|
||||||
- LDAP_SCOPE_ONELEVEL
|
|
||||||
- LDAP_SCOPE_SUBTREE
|
|
||||||
- filter is an LDAP search expression,
|
|
||||||
- attributes is an array of string telling with LDAP attribute to get from this request
|
|
||||||
*/
|
|
||||||
func (self *Ldap) Search(base string, scope int, filter string, attributes []string) (*LdapMessage, error) {
|
|
||||||
|
|
||||||
var attrsonly int = 0 // false: returns all, true, returns only attributes without values
|
|
||||||
|
|
||||||
_base := C.CString(base)
|
|
||||||
defer C.free(unsafe.Pointer(_base))
|
|
||||||
|
|
||||||
_filter := C.CString(filter)
|
|
||||||
defer C.free(unsafe.Pointer(_filter))
|
|
||||||
|
|
||||||
// transform []string to C.char** null terminated array (attributes argument)
|
|
||||||
_attributes := make([]*C.char, len(attributes)+1) // default set to nil (NULL in C)
|
|
||||||
|
|
||||||
for i, arg := range attributes {
|
|
||||||
_attributes[i] = C.CString(arg)
|
|
||||||
defer C.free(unsafe.Pointer(_attributes[i]))
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg *C.LDAPMessage
|
|
||||||
|
|
||||||
// DEPRECATED
|
|
||||||
// API: int ldap_search_s (LDAP *ld, char *base, int scope, char *filter, char **attrs, int attrsonly, LdapMessage * ldap_res)
|
|
||||||
rv := int(C.ldap_search_s(self.conn, _base, C.int(scope), _filter, &_attributes[0], C.int(attrsonly), &msg))
|
|
||||||
|
|
||||||
if rv == LDAP_OPT_SUCCESS {
|
|
||||||
_msg := new(LdapMessage)
|
|
||||||
_msg.ldap = self
|
|
||||||
_msg.errno = rv
|
|
||||||
_msg.msg = msg
|
|
||||||
return _msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New(fmt.Sprintf("LDAP::Search() error : %d (%s)", rv, ErrorToString(rv)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------- Ldap* method (object oriented) -------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Create a new LdapAttribute entry with name and values.
|
|
||||||
func LdapAttributeNew(name string, values []string)(*LdapAttribute){
|
|
||||||
a := new(LdapAttribute)
|
|
||||||
a.values = values
|
|
||||||
a.name = name
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append() adds an LdapAttribute to self LdapEntry
|
|
||||||
func (self *LdapEntry) Append(a LdapAttribute){
|
|
||||||
self.values = append(self.values, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String() is used for fmt.Println(self)
|
|
||||||
//
|
|
||||||
func (self *LdapAttribute) String() string{
|
|
||||||
return self.ToText()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToText() returns a text string representation of LdapAttribute
|
|
||||||
// avoiding displaying binary data.
|
|
||||||
//
|
|
||||||
func (self *LdapAttribute) ToText() string{
|
|
||||||
|
|
||||||
var list []string
|
|
||||||
|
|
||||||
for _, a := range self.Values() {
|
|
||||||
if (!_isPrint(a)) {
|
|
||||||
list = append(list, fmt.Sprintf("binary-data[%d]", len(a)))
|
|
||||||
} else {
|
|
||||||
list = append(list, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(list) > 1 {
|
|
||||||
return fmt.Sprintf("%s: (%d)[%s]", self.name, len(list), strings.Join(list, ", "))
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s: [%s]", self.name, strings.Join(list, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name() return attribute name
|
|
||||||
func (self *LdapAttribute) Name() string{
|
|
||||||
return self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Values() returns array values for self LdapAttribute
|
|
||||||
//
|
|
||||||
func (self *LdapAttribute) Values() []string{
|
|
||||||
return self.values
|
|
||||||
}
|
|
||||||
|
|
||||||
// _isPrint() returns true if str is printable
|
|
||||||
//
|
|
||||||
// @private method
|
|
||||||
func _isPrint(str string) bool{
|
|
||||||
for _, c := range str{
|
|
||||||
|
|
||||||
if !strconv.IsPrint(rune(c)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPrint() returns true is self LdapAttribute is printable.
|
|
||||||
func (self *LdapAttribute) IsPrint() bool{
|
|
||||||
for _, a := range self.Values() {
|
|
||||||
if (!_isPrint(a)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dn() returns DN (Distinguish Name) for self LdapEntry
|
|
||||||
func (self *LdapEntry) Dn() string{
|
|
||||||
return self.dn
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attributes() returns an array of LdapAttribute
|
|
||||||
func (self *LdapEntry) Attributes() []LdapAttribute{
|
|
||||||
return self.values
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print() allow printing self LdapEntry with fmt.Println()
|
|
||||||
func (self *LdapEntry) String() string {
|
|
||||||
return self.ToText()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetValuesByName() get a list of values for self LdapEntry, using "name" attribute
|
|
||||||
func (self *LdapEntry) GetValuesByName(attrib string) []string{
|
|
||||||
|
|
||||||
for _, a := range self.values{
|
|
||||||
if a.Name() == attrib {
|
|
||||||
return a.values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
// GetOneValueByName() ; a quick way to get a single attribute value
|
|
||||||
func (self *LdapEntry) GetOneValueByName(attrib string) (string, error){
|
|
||||||
|
|
||||||
for _, a := range self.values{
|
|
||||||
if a.Name() == attrib {
|
|
||||||
return a.values[0], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", errors.New(fmt.Sprintf("LdapEntry::GetOneValueByName() error : attribute %s not found", attrib))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToText() return a string representating self LdapEntry
|
|
||||||
func (self *LdapEntry) ToText() string{
|
|
||||||
|
|
||||||
txt := fmt.Sprintf("dn: %s\n", self.dn)
|
|
||||||
|
|
||||||
for _, a := range self.values{
|
|
||||||
txt = txt + fmt.Sprintf("%s\n", a.ToText())
|
|
||||||
}
|
|
||||||
|
|
||||||
return txt
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append() add e to LdapSearchResult array
|
|
||||||
func (self *LdapSearchResult) Append(e LdapEntry){
|
|
||||||
self.entries = append(self.entries, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToText() : a quick way to print an LdapSearchResult
|
|
||||||
func (self *LdapSearchResult) ToText() string{
|
|
||||||
|
|
||||||
txt := fmt.Sprintf("# query : %s\n", self.filter)
|
|
||||||
txt = txt + fmt.Sprintf("# num results : %d\n", self.Count())
|
|
||||||
txt = txt + fmt.Sprintf("# search : %s\n", self.Filter())
|
|
||||||
txt = txt + fmt.Sprintf("# base : %s\n", self.Base())
|
|
||||||
txt = txt + fmt.Sprintf("# attributes : [%s]\n", strings.Join(self.Attributes(), ", "))
|
|
||||||
|
|
||||||
for _, e := range self.entries{
|
|
||||||
txt = txt + fmt.Sprintf("%s\n", e.ToText())
|
|
||||||
}
|
|
||||||
|
|
||||||
return txt
|
|
||||||
}
|
|
||||||
|
|
||||||
// String() : used for fmt.Println(self)
|
|
||||||
func (self *LdapSearchResult) String() string{
|
|
||||||
return self.ToText()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entries() : returns an array of LdapEntry for self
|
|
||||||
func (self *LdapSearchResult) Entries() []LdapEntry{
|
|
||||||
return self.entries
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count() : returns number of results for self search.
|
|
||||||
func (self *LdapSearchResult) Count() int{
|
|
||||||
return len(self.entries)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter() : returns filter for self search
|
|
||||||
func (self *LdapSearchResult) Filter() string{
|
|
||||||
return self.filter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter() : returns base DN for self search
|
|
||||||
func (self *LdapSearchResult) Base() string{
|
|
||||||
return self.base
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter() : returns scope for self search
|
|
||||||
func (self *LdapSearchResult) Scope() int{
|
|
||||||
return self.scope
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter() : returns an array of attributes used for this actual search
|
|
||||||
func (self *LdapSearchResult) Attributes() []string{
|
|
||||||
return self.attributes
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchAll() : a quick way to make search. This method returns an LdapSearchResult with all necessary methods to
|
|
||||||
// access data. Result is a collection (tree) of []LdapEntry / []LdapAttribute.
|
|
||||||
//
|
|
||||||
func (self *Ldap) SearchAll(base string, scope int, filter string, attributes []string) (*LdapSearchResult, error) {
|
|
||||||
|
|
||||||
sr := new(LdapSearchResult)
|
|
||||||
|
|
||||||
sr.ldap = self
|
|
||||||
sr.base = base
|
|
||||||
sr.scope = scope
|
|
||||||
sr.filter = filter
|
|
||||||
sr.attributes = attributes
|
|
||||||
|
|
||||||
// Search(base string, scope int, filter string, attributes []string) (*LDAPMessage, error)
|
|
||||||
result, err := self.Search(base, scope, filter, attributes)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return sr, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free LDAP::Result() allocated data
|
|
||||||
defer result.MsgFree()
|
|
||||||
|
|
||||||
e := result.FirstEntry()
|
|
||||||
|
|
||||||
for e != nil {
|
|
||||||
_e := new(LdapEntry)
|
|
||||||
|
|
||||||
_e.dn = e.GetDn()
|
|
||||||
|
|
||||||
attr, _ := e.FirstAttribute()
|
|
||||||
for attr != "" {
|
|
||||||
|
|
||||||
_attr := LdapAttributeNew(attr, e.GetValues(attr))
|
|
||||||
_e.Append(*_attr)
|
|
||||||
|
|
||||||
attr, _ = e.NextAttribute()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sr.Append(*_e)
|
|
||||||
|
|
||||||
e = e.NextEntry()
|
|
||||||
}
|
|
||||||
|
|
||||||
return sr, nil
|
|
||||||
}
|
|
118
src/vendor/github.com/mqu/openldap/options-errors.go
generated
vendored
118
src/vendor/github.com/mqu/openldap/options-errors.go
generated
vendored
@ -1,118 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 - Marc Quinton.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by the MIT Licence :
|
|
||||||
* http://opensource.org/licenses/mit-license.php
|
|
||||||
*
|
|
||||||
* 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 AUTHOR 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package openldap
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <ldap.h>
|
|
||||||
|
|
||||||
static inline char* to_charptr(const void* s) { return (char*)s; }
|
|
||||||
|
|
||||||
*/
|
|
||||||
// #cgo CFLAGS: -DLDAP_DEPRECATED=1
|
|
||||||
// #cgo linux CFLAGS: -DLINUX=1
|
|
||||||
// #cgo LDFLAGS: -lldap -llber
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FIXME : support all kind of option (int, int*, ...)
|
|
||||||
func (self *Ldap) SetOption(opt int, val int) error {
|
|
||||||
|
|
||||||
// API: ldap_set_option (LDAP *ld,int option, LDAP_CONST void *invalue));
|
|
||||||
rv := C.ldap_set_option(self.conn, C.int(opt), unsafe.Pointer(&val))
|
|
||||||
|
|
||||||
if rv == LDAP_OPT_SUCCESS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New(fmt.Sprintf("LDAP::SetOption() error (%d) : %s", int(rv), ErrorToString(int(rv))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME : support all kind of option (int, int*, ...) should take care of all return type for ldap_get_option
|
|
||||||
func (self *Ldap) GetOption(opt int) (val int, err error) {
|
|
||||||
|
|
||||||
// API: int ldap_get_option (LDAP *ld,int option, LDAP_CONST void *invalue));
|
|
||||||
rv := C.ldap_get_option(self.conn, C.int(opt), unsafe.Pointer(&val))
|
|
||||||
|
|
||||||
if rv == LDAP_OPT_SUCCESS {
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, errors.New(fmt.Sprintf("LDAP::GetOption() error (%d) : %s", rv, ErrorToString(int(rv))))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** WORK IN PROGRESS!
|
|
||||||
**
|
|
||||||
** OpenLDAP reentrancy/thread-safeness should be dynamically
|
|
||||||
** checked using ldap_get_option().
|
|
||||||
**
|
|
||||||
** The -lldap implementation is not thread-safe.
|
|
||||||
**
|
|
||||||
** The -lldap_r implementation is:
|
|
||||||
** LDAP_API_FEATURE_THREAD_SAFE (basic thread safety)
|
|
||||||
** but also be:
|
|
||||||
** LDAP_API_FEATURE_SESSION_THREAD_SAFE
|
|
||||||
** LDAP_API_FEATURE_OPERATION_THREAD_SAFE
|
|
||||||
**
|
|
||||||
** The preprocessor flag LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
|
|
||||||
** can be used to determine if -lldap_r is available at compile
|
|
||||||
** time. You must define LDAP_THREAD_SAFE if and only if you
|
|
||||||
** link with -lldap_r.
|
|
||||||
**
|
|
||||||
** If you fail to define LDAP_THREAD_SAFE when linking with
|
|
||||||
** -lldap_r or define LDAP_THREAD_SAFE when linking with -lldap,
|
|
||||||
** provided header definations and declarations may be incorrect.
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (self *Ldap) IsThreadSafe() bool {
|
|
||||||
// fmt.Println("IsThreadSafe()")
|
|
||||||
// opt, err := self.GetOption(LDAP_API_FEATURE_THREAD_SAFE) ; fmt.Println(opt, err)
|
|
||||||
// opt, err = self.GetOption(LDAP_THREAD_SAFE) ; fmt.Println(opt, err)
|
|
||||||
// opt, err = self.GetOption(LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE) ; fmt.Println(opt, err)
|
|
||||||
|
|
||||||
//FIXME: need to implement LDAP::GetOption(LDAP_OPT_API_FEATURE_INFO)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrorToString(err int) string {
|
|
||||||
|
|
||||||
// API: char * ldap_err2string (int err )
|
|
||||||
result := C.GoString(C.to_charptr(unsafe.Pointer(C.ldap_err2string(C.int(err)))))
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Ldap) Errno() int {
|
|
||||||
opt, _ := self.GetOption(LDAP_OPT_ERROR_NUMBER)
|
|
||||||
return opt
|
|
||||||
}
|
|
306
src/vendor/github.com/mqu/openldap/results.go
generated
vendored
306
src/vendor/github.com/mqu/openldap/results.go
generated
vendored
@ -1,306 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 - Marc Quinton.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by the MIT Licence :
|
|
||||||
* http://opensource.org/licenses/mit-license.php
|
|
||||||
*
|
|
||||||
* 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 AUTHOR 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package openldap
|
|
||||||
|
|
||||||
/*#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <ldap.h>
|
|
||||||
|
|
||||||
int _berval_get_len(struct berval **ber, int i){
|
|
||||||
return ber[i]->bv_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* _berval_get_value(struct berval **ber, int i){
|
|
||||||
return ber[i]->bv_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
// #cgo CFLAGS: -DLDAP_DEPRECATED=1
|
|
||||||
// #cgo linux CFLAGS: -DLINUX=1
|
|
||||||
// #cgo LDFLAGS: -lldap -llber
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ------------------------------------------ RESULTS methods ---------------------------------------------
|
|
||||||
/*
|
|
||||||
|
|
||||||
openldap C API :
|
|
||||||
|
|
||||||
int ldap_count_messages( LDAP *ld, LdapMessage *result )
|
|
||||||
LdapMessage *ldap_first_message( LDAP *ld, LdapMessage *result )
|
|
||||||
LdapMessage *ldap_next_message ( LDAP *ld, LdapMessage *message )
|
|
||||||
|
|
||||||
int ldap_count_entries( LDAP *ld, LdapMessage *result )
|
|
||||||
LdapMessage *ldap_first_entry( LDAP *ld, LdapMessage *result )
|
|
||||||
LdapMessage *ldap_next_entry ( LDAP *ld, LdapMessage *entry )
|
|
||||||
|
|
||||||
char *ldap_first_attribute(LDAP *ld, LdapMessage *entry, BerElement **berptr )
|
|
||||||
char *ldap_next_attribute (LDAP *ld, LdapMessage *entry, BerElement *ber )
|
|
||||||
|
|
||||||
char **ldap_get_values(LDAP *ld, LdapMessage *entry, char *attr)
|
|
||||||
struct berval **ldap_get_values_len(LDAP *ld, LdapMessage *entry,char *attr)
|
|
||||||
|
|
||||||
int ldap_count_values(char **vals)
|
|
||||||
int ldap_count_values_len(struct berval **vals)
|
|
||||||
void ldap_value_free(char **vals)
|
|
||||||
void ldap_value_free_len(struct berval **vals)
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (self *LdapMessage) Count() int {
|
|
||||||
// API : int ldap_count_messages(LDAP *ld, LDAPMessage *chain )
|
|
||||||
// err : (count = -1)
|
|
||||||
count := int(C.ldap_count_messages(self.ldap.conn, self.msg))
|
|
||||||
if count == -1 {
|
|
||||||
panic("LDAP::Count() (ldap_count_messages) error (-1)")
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *LdapMessage) FirstMessage() *LdapMessage {
|
|
||||||
|
|
||||||
var msg *C.LDAPMessage
|
|
||||||
msg = C.ldap_first_message(self.ldap.conn, self.msg)
|
|
||||||
if msg == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
_msg := new(LdapMessage)
|
|
||||||
_msg.ldap = self.ldap
|
|
||||||
_msg.errno = 0
|
|
||||||
_msg.msg = msg
|
|
||||||
return _msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *LdapMessage) NextMessage() *LdapMessage {
|
|
||||||
var msg *C.LDAPMessage
|
|
||||||
msg = C.ldap_next_message(self.ldap.conn, self.msg)
|
|
||||||
|
|
||||||
if msg == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
_msg := new(LdapMessage)
|
|
||||||
_msg.ldap = self.ldap
|
|
||||||
_msg.errno = 0
|
|
||||||
_msg.msg = msg
|
|
||||||
return _msg
|
|
||||||
}
|
|
||||||
|
|
||||||
/* an alias to ldap_count_message() ? */
|
|
||||||
func (self *LdapEntry) CountEntries() int {
|
|
||||||
// API : int ldap_count_messages(LDAP *ld, LDAPMessage *chain )
|
|
||||||
// err : (count = -1)
|
|
||||||
return int(C.ldap_count_entries(self.ldap.conn, self.entry))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *LdapMessage) FirstEntry() *LdapEntry {
|
|
||||||
|
|
||||||
var msg *C.LDAPMessage
|
|
||||||
// API: LdapMessage *ldap_first_entry( LDAP *ld, LdapMessage *result )
|
|
||||||
msg = C.ldap_first_entry(self.ldap.conn, self.msg)
|
|
||||||
if msg == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
_msg := new(LdapEntry)
|
|
||||||
_msg.ldap = self.ldap
|
|
||||||
_msg.errno = 0
|
|
||||||
_msg.entry = msg
|
|
||||||
return _msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *LdapEntry) NextEntry() *LdapEntry {
|
|
||||||
var msg *C.LDAPMessage
|
|
||||||
// API: LdapMessage *ldap_next_entry ( LDAP *ld, LdapMessage *entry )
|
|
||||||
msg = C.ldap_next_entry(self.ldap.conn, self.entry)
|
|
||||||
|
|
||||||
if msg == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
_msg := new(LdapEntry)
|
|
||||||
_msg.ldap = self.ldap
|
|
||||||
_msg.errno = 0
|
|
||||||
_msg.entry = msg
|
|
||||||
return _msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *LdapEntry) FirstAttribute() (string, error) {
|
|
||||||
|
|
||||||
var ber *C.BerElement
|
|
||||||
|
|
||||||
// API: char *ldap_first_attribute(LDAP *ld, LdapMessage *entry, BerElement **berptr )
|
|
||||||
rv := C.ldap_first_attribute(self.ldap.conn, self.entry, &ber)
|
|
||||||
|
|
||||||
if rv == nil {
|
|
||||||
// error
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
self.ber = ber
|
|
||||||
return C.GoString(rv), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *LdapEntry) NextAttribute() (string, error) {
|
|
||||||
|
|
||||||
// API: char *ldap_next_attribute (LDAP *ld, LdapMessage *entry, BerElement *ber )
|
|
||||||
rv := C.ldap_next_attribute(self.ldap.conn, self.entry, self.ber)
|
|
||||||
|
|
||||||
if rv == nil {
|
|
||||||
// error
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
return C.GoString(rv), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// private func
|
|
||||||
func sptr(p uintptr) *C.char {
|
|
||||||
return *(**C.char)(unsafe.Pointer(p))
|
|
||||||
}
|
|
||||||
|
|
||||||
// private func used to convert null terminated char*[] to go []string
|
|
||||||
func cstrings_array(x **C.char) []string {
|
|
||||||
var s []string
|
|
||||||
for p := uintptr(unsafe.Pointer(x)); sptr(p) != nil; p += unsafe.Sizeof(uintptr(0)) {
|
|
||||||
s = append(s, C.GoString(sptr(p)))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetValues() return an array of string containing values for LDAP attribute "attr".
|
|
||||||
// Binary data are supported.
|
|
||||||
func (self *LdapEntry) GetValues(attr string) []string {
|
|
||||||
var s []string
|
|
||||||
|
|
||||||
_attr := C.CString(attr)
|
|
||||||
defer C.free(unsafe.Pointer(_attr))
|
|
||||||
|
|
||||||
var bv **C.struct_berval
|
|
||||||
|
|
||||||
//API: struct berval **ldap_get_values_len(LDAP *ld, LDAPMessage *entry, char *attr)
|
|
||||||
bv = C.ldap_get_values_len(self.ldap.conn, self.entry, _attr)
|
|
||||||
|
|
||||||
var i int
|
|
||||||
count := int(C.ldap_count_values_len(bv))
|
|
||||||
|
|
||||||
for i = 0 ; i < count; i++ {
|
|
||||||
s = append(s, C.GoStringN(C._berval_get_value(bv, C.int(i)), C._berval_get_len(bv, C.int(i))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// free allocated array (bv)
|
|
||||||
C.ldap_value_free_len(bv)
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------ RESULTS -----------------------------------------------
|
|
||||||
/*
|
|
||||||
int ldap_result ( LDAP *ld, int msgid, int all, struct timeval *timeout, LdapMessage **result );
|
|
||||||
int ldap_msgfree( LdapMessage *msg );
|
|
||||||
int ldap_msgtype( LdapMessage *msg );
|
|
||||||
int ldap_msgid ( LdapMessage *msg );
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Result()
|
|
||||||
// take care to free LdapMessage result with MsgFree()
|
|
||||||
//
|
|
||||||
func (self *Ldap) Result() (*LdapMessage, error) {
|
|
||||||
|
|
||||||
var msgid int = 1
|
|
||||||
var all int = 1
|
|
||||||
|
|
||||||
var tv C.struct_timeval
|
|
||||||
tv.tv_sec = 30
|
|
||||||
|
|
||||||
var msg *C.LDAPMessage
|
|
||||||
|
|
||||||
// API: int ldap_result( LDAP *ld, int msgid, int all, struct timeval *timeout, LDAPMessage **result );
|
|
||||||
rv := C.ldap_result(self.conn, C.int(msgid), C.int(all), &tv, &msg)
|
|
||||||
|
|
||||||
if rv != LDAP_OPT_SUCCESS {
|
|
||||||
return nil, errors.New(fmt.Sprintf("LDAP::Result() error : %d (%s)", rv, ErrorToString(int(rv))))
|
|
||||||
}
|
|
||||||
|
|
||||||
_msg := new(LdapMessage)
|
|
||||||
_msg.ldap = self
|
|
||||||
_msg.errno = int(rv)
|
|
||||||
_msg.msg = msg
|
|
||||||
|
|
||||||
return _msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgFree() is used to free LDAP::Result() allocated data
|
|
||||||
//
|
|
||||||
// returns -1 on error.
|
|
||||||
//
|
|
||||||
func (self *LdapMessage) MsgFree() int{
|
|
||||||
if self.msg != nil {
|
|
||||||
rv := C.ldap_msgfree(self.msg)
|
|
||||||
self.msg = nil
|
|
||||||
return int(rv)
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------- DN Methods ---------------------------------------------------
|
|
||||||
/*
|
|
||||||
|
|
||||||
char *ldap_get_dn( LDAP *ld, LdapMessage *entry)
|
|
||||||
int ldap_str2dn( const char *str, LDAPDN *dn, unsigned flags)
|
|
||||||
void ldap_dnfree( LDAPDN dn)
|
|
||||||
int ldap_dn2str( LDAPDN dn, char **str, unsigned flags)
|
|
||||||
|
|
||||||
char **ldap_explode_dn( const char *dn, int notypes)
|
|
||||||
char **ldap_explode_rdn( const char *rdn, int notypes)
|
|
||||||
|
|
||||||
char *ldap_dn2ufn ( const char * dn )
|
|
||||||
char *ldap_dn2dcedn( const char * dn )
|
|
||||||
char *ldap_dcedn2dn( const char * dn )
|
|
||||||
char *ldap_dn2ad_canonical( const char * dn )
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// GetDn() return the DN (Distinguish Name) for self LdapEntry
|
|
||||||
func (self *LdapEntry) GetDn() string {
|
|
||||||
// API: char *ldap_get_dn( LDAP *ld, LDAPMessage *entry )
|
|
||||||
rv := C.ldap_get_dn(self.ldap.conn, self.entry)
|
|
||||||
defer C.free(unsafe.Pointer(rv))
|
|
||||||
|
|
||||||
if rv == nil {
|
|
||||||
err := self.ldap.Errno()
|
|
||||||
panic(fmt.Sprintf("LDAP::GetDn() error %d (%s)", err, ErrorToString(err)))
|
|
||||||
}
|
|
||||||
|
|
||||||
val := C.GoString(rv)
|
|
||||||
return val
|
|
||||||
}
|
|
76
src/vendor/github.com/mqu/openldap/types.go
generated
vendored
76
src/vendor/github.com/mqu/openldap/types.go
generated
vendored
@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 - Marc Quinton.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by the MIT Licence :
|
|
||||||
* http://opensource.org/licenses/mit-license.php
|
|
||||||
*
|
|
||||||
* 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 AUTHOR 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package openldap
|
|
||||||
|
|
||||||
/*
|
|
||||||
# include <ldap.h>
|
|
||||||
|
|
||||||
*/
|
|
||||||
// #cgo CFLAGS: -DLDAP_DEPRECATED=1
|
|
||||||
// #cgo linux CFLAGS: -DLINUX=1
|
|
||||||
// #cgo LDFLAGS: -lldap -llber
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
type Ldap struct {
|
|
||||||
conn *C.LDAP
|
|
||||||
}
|
|
||||||
|
|
||||||
type LdapMessage struct {
|
|
||||||
ldap *Ldap
|
|
||||||
// conn *C.LDAP
|
|
||||||
msg *C.LDAPMessage
|
|
||||||
errno int
|
|
||||||
}
|
|
||||||
|
|
||||||
type LdapAttribute struct{
|
|
||||||
name string
|
|
||||||
values []string
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type LdapEntry struct {
|
|
||||||
ldap *Ldap
|
|
||||||
// conn *C.LDAP
|
|
||||||
entry *C.LDAPMessage
|
|
||||||
errno int
|
|
||||||
ber *C.BerElement
|
|
||||||
|
|
||||||
dn string
|
|
||||||
values []LdapAttribute
|
|
||||||
}
|
|
||||||
|
|
||||||
type LdapSearchResult struct{
|
|
||||||
ldap *Ldap
|
|
||||||
|
|
||||||
scope int
|
|
||||||
filter string
|
|
||||||
base string
|
|
||||||
attributes []string
|
|
||||||
|
|
||||||
entries []LdapEntry
|
|
||||||
}
|
|
27
src/vendor/gopkg.in/asn1-ber.v1/LICENSE
generated
vendored
Normal file
27
src/vendor/gopkg.in/asn1-ber.v1/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
src/vendor/gopkg.in/asn1-ber.v1/README.md
generated
vendored
Normal file
24
src/vendor/gopkg.in/asn1-ber.v1/README.md
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[![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
|
504
src/vendor/gopkg.in/asn1-ber.v1/ber.go
generated
vendored
Normal file
504
src/vendor/gopkg.in/asn1-ber.v1/ber.go
generated
vendored
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
package ber
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
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
src/vendor/gopkg.in/asn1-ber.v1/content_int.go
generated
vendored
Normal file
25
src/vendor/gopkg.in/asn1-ber.v1/content_int.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
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
|
||||||
|
}
|
29
src/vendor/gopkg.in/asn1-ber.v1/header.go
generated
vendored
Normal file
29
src/vendor/gopkg.in/asn1-ber.v1/header.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ber
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"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")
|
||||||
|
}
|
||||||
|
|
||||||
|
return identifier, length, read, nil
|
||||||
|
}
|
103
src/vendor/gopkg.in/asn1-ber.v1/identifier.go
generated
vendored
Normal file
103
src/vendor/gopkg.in/asn1-ber.v1/identifier.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package ber
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
highBit := uint(63)
|
||||||
|
for {
|
||||||
|
if tag&(1<<highBit) != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
highBit--
|
||||||
|
}
|
||||||
|
|
||||||
|
tagBytes := int(math.Ceil(float64(highBit) / 7.0))
|
||||||
|
for i := tagBytes - 1; i >= 0; i-- {
|
||||||
|
offset := uint(i) * 7
|
||||||
|
mask := Tag(0x7f) << offset
|
||||||
|
tagByte := (tag & mask) >> offset
|
||||||
|
if i != 0 {
|
||||||
|
tagByte |= 0x80
|
||||||
|
}
|
||||||
|
b = append(b, byte(tagByte))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
71
src/vendor/gopkg.in/asn1-ber.v1/length.go
generated
vendored
Normal file
71
src/vendor/gopkg.in/asn1-ber.v1/length.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
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
|
||||||
|
length <<= 8
|
||||||
|
length |= int(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
src/vendor/gopkg.in/asn1-ber.v1/util.go
generated
vendored
Normal file
24
src/vendor/gopkg.in/asn1-ber.v1/util.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
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
src/vendor/gopkg.in/ldap.v2/LICENSE
generated
vendored
Normal file
22
src/vendor/gopkg.in/ldap.v2/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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.
|
42
src/vendor/gopkg.in/ldap.v2/Makefile
generated
vendored
Normal file
42
src/vendor/gopkg.in/ldap.v2/Makefile
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.PHONY: default install build test quicktest fmt vet lint
|
||||||
|
|
||||||
|
default: fmt vet lint build quicktest
|
||||||
|
|
||||||
|
install:
|
||||||
|
go get -t -v ./...
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build -v ./...
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -v -cover ./...
|
||||||
|
|
||||||
|
quicktest:
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# Capture output and force failure when there is non-empty output
|
||||||
|
fmt:
|
||||||
|
@echo gofmt -l .
|
||||||
|
@OUTPUT=`gofmt -l . 2>&1`; \
|
||||||
|
if [ "$$OUTPUT" ]; then \
|
||||||
|
echo "gofmt must be run on the following files:"; \
|
||||||
|
echo "$$OUTPUT"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only run on go1.5+
|
||||||
|
vet:
|
||||||
|
go tool vet -atomic -bool -copylocks -nilfunc -printf -shadow -rangeloops -unreachable -unsafeptr -unusedresult .
|
||||||
|
|
||||||
|
# https://github.com/golang/lint
|
||||||
|
# go get github.com/golang/lint/golint
|
||||||
|
# Capture output and force failure when there is non-empty output
|
||||||
|
# Only run on go1.5+
|
||||||
|
lint:
|
||||||
|
@echo golint ./...
|
||||||
|
@OUTPUT=`golint ./... 2>&1`; \
|
||||||
|
if [ "$$OUTPUT" ]; then \
|
||||||
|
echo "golint errors:"; \
|
||||||
|
echo "$$OUTPUT"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
53
src/vendor/gopkg.in/ldap.v2/README.md
generated
vendored
Normal file
53
src/vendor/gopkg.in/ldap.v2/README.md
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
[![GoDoc](https://godoc.org/gopkg.in/ldap.v2?status.svg)](https://godoc.org/gopkg.in/ldap.v2)
|
||||||
|
[![Build Status](https://travis-ci.org/go-ldap/ldap.svg)](https://travis-ci.org/go-ldap/ldap)
|
||||||
|
|
||||||
|
# Basic LDAP v3 functionality for the GO programming language.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
For the latest version use:
|
||||||
|
|
||||||
|
go get gopkg.in/ldap.v2
|
||||||
|
|
||||||
|
Import the latest version with:
|
||||||
|
|
||||||
|
import "gopkg.in/ldap.v2"
|
||||||
|
|
||||||
|
## Required Libraries:
|
||||||
|
|
||||||
|
- gopkg.in/asn1-ber.v1
|
||||||
|
|
||||||
|
## Features:
|
||||||
|
|
||||||
|
- Connecting to LDAP server (non-TLS, TLS, STARTTLS)
|
||||||
|
- Binding to LDAP server
|
||||||
|
- Searching for entries
|
||||||
|
- Filter Compile / Decompile
|
||||||
|
- Paging Search Results
|
||||||
|
- Modify Requests / Responses
|
||||||
|
- Add Requests / Responses
|
||||||
|
- Delete Requests / Responses
|
||||||
|
|
||||||
|
## Examples:
|
||||||
|
|
||||||
|
- search
|
||||||
|
- modify
|
||||||
|
|
||||||
|
## Contributing:
|
||||||
|
|
||||||
|
Bug reports and pull requests are welcome!
|
||||||
|
|
||||||
|
Before submitting a pull request, please make sure tests and verification scripts pass:
|
||||||
|
```
|
||||||
|
make all
|
||||||
|
```
|
||||||
|
|
||||||
|
To set up a pre-push hook to run the tests and verify scripts before pushing:
|
||||||
|
```
|
||||||
|
ln -s ../../.githooks/pre-push .git/hooks/pre-push
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
|
||||||
|
The design is licensed under the Creative Commons 3.0 Attributions license.
|
||||||
|
Read this article for more details: http://blog.golang.org/gopher
|
113
src/vendor/gopkg.in/ldap.v2/add.go
generated
vendored
Normal file
113
src/vendor/gopkg.in/ldap.v2/add.go
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4511
|
||||||
|
//
|
||||||
|
// AddRequest ::= [APPLICATION 8] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// attributes AttributeList }
|
||||||
|
//
|
||||||
|
// AttributeList ::= SEQUENCE OF attribute Attribute
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Attribute represents an LDAP attribute
|
||||||
|
type Attribute struct {
|
||||||
|
// Type is the name of the LDAP attribute
|
||||||
|
Type string
|
||||||
|
// Vals are the LDAP attribute values
|
||||||
|
Vals []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Attribute) encode() *ber.Packet {
|
||||||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
|
||||||
|
seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type"))
|
||||||
|
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
|
||||||
|
for _, value := range a.Vals {
|
||||||
|
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
|
||||||
|
}
|
||||||
|
seq.AppendChild(set)
|
||||||
|
return seq
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRequest represents an LDAP AddRequest operation
|
||||||
|
type AddRequest struct {
|
||||||
|
// DN identifies the entry being added
|
||||||
|
DN string
|
||||||
|
// Attributes list the attributes of the new entry
|
||||||
|
Attributes []Attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AddRequest) encode() *ber.Packet {
|
||||||
|
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
|
||||||
|
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN"))
|
||||||
|
attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
|
||||||
|
for _, attribute := range a.Attributes {
|
||||||
|
attributes.AppendChild(attribute.encode())
|
||||||
|
}
|
||||||
|
request.AppendChild(attributes)
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute adds an attribute with the given type and values
|
||||||
|
func (a *AddRequest) Attribute(attrType string, attrVals []string) {
|
||||||
|
a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAddRequest returns an AddRequest for the given DN, with no attributes
|
||||||
|
func NewAddRequest(dn string) *AddRequest {
|
||||||
|
return &AddRequest{
|
||||||
|
DN: dn,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add performs the given AddRequest
|
||||||
|
func (l *Conn) Add(addRequest *AddRequest) error {
|
||||||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||||
|
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
|
||||||
|
packet.AppendChild(addRequest.encode())
|
||||||
|
|
||||||
|
l.Debug.PrintPacket(packet)
|
||||||
|
|
||||||
|
msgCtx, err := l.sendMessage(packet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer l.finishMessage(msgCtx)
|
||||||
|
|
||||||
|
l.Debug.Printf("%d: waiting for response", msgCtx.id)
|
||||||
|
packetResponse, ok := <-msgCtx.responses
|
||||||
|
if !ok {
|
||||||
|
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
|
||||||
|
}
|
||||||
|
packet, err = packetResponse.ReadPacket()
|
||||||
|
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
if err := addLDAPDescriptions(packet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.Children[1].Tag == ApplicationAddResponse {
|
||||||
|
resultCode, resultDescription := getLDAPResultCode(packet)
|
||||||
|
if resultCode != 0 {
|
||||||
|
return NewError(resultCode, errors.New(resultDescription))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Debug.Printf("%d: returning", msgCtx.id)
|
||||||
|
return nil
|
||||||
|
}
|
143
src/vendor/gopkg.in/ldap.v2/bind.go
generated
vendored
Normal file
143
src/vendor/gopkg.in/ldap.v2/bind.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SimpleBindRequest represents a username/password bind operation
|
||||||
|
type SimpleBindRequest struct {
|
||||||
|
// Username is the name of the Directory object that the client wishes to bind as
|
||||||
|
Username string
|
||||||
|
// Password is the credentials to bind with
|
||||||
|
Password string
|
||||||
|
// Controls are optional controls to send with the bind request
|
||||||
|
Controls []Control
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimpleBindResult contains the response from the server
|
||||||
|
type SimpleBindResult struct {
|
||||||
|
Controls []Control
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSimpleBindRequest returns a bind request
|
||||||
|
func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
|
||||||
|
return &SimpleBindRequest{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
Controls: controls,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bindRequest *SimpleBindRequest) encode() *ber.Packet {
|
||||||
|
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
|
||||||
|
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
|
||||||
|
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, bindRequest.Username, "User Name"))
|
||||||
|
request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, bindRequest.Password, "Password"))
|
||||||
|
|
||||||
|
request.AppendChild(encodeControls(bindRequest.Controls))
|
||||||
|
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimpleBind performs the simple bind operation defined in the given request
|
||||||
|
func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) {
|
||||||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||||
|
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
|
||||||
|
encodedBindRequest := simpleBindRequest.encode()
|
||||||
|
packet.AppendChild(encodedBindRequest)
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgCtx, err := l.sendMessage(packet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer l.finishMessage(msgCtx)
|
||||||
|
|
||||||
|
packetResponse, ok := <-msgCtx.responses
|
||||||
|
if !ok {
|
||||||
|
return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
|
||||||
|
}
|
||||||
|
packet, err = packetResponse.ReadPacket()
|
||||||
|
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
if err := addLDAPDescriptions(packet); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &SimpleBindResult{
|
||||||
|
Controls: make([]Control, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(packet.Children) == 3 {
|
||||||
|
for _, child := range packet.Children[2].Children {
|
||||||
|
result.Controls = append(result.Controls, DecodeControl(child))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resultCode, resultDescription := getLDAPResultCode(packet)
|
||||||
|
if resultCode != 0 {
|
||||||
|
return result, NewError(resultCode, errors.New(resultDescription))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind performs a bind with the given username and password
|
||||||
|
func (l *Conn) Bind(username, password string) error {
|
||||||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||||
|
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
|
||||||
|
bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
|
||||||
|
bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
|
||||||
|
bindRequest.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, username, "User Name"))
|
||||||
|
bindRequest.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, password, "Password"))
|
||||||
|
packet.AppendChild(bindRequest)
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgCtx, err := l.sendMessage(packet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer l.finishMessage(msgCtx)
|
||||||
|
|
||||||
|
packetResponse, ok := <-msgCtx.responses
|
||||||
|
if !ok {
|
||||||
|
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
|
||||||
|
}
|
||||||
|
packet, err = packetResponse.ReadPacket()
|
||||||
|
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
if err := addLDAPDescriptions(packet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
resultCode, resultDescription := getLDAPResultCode(packet)
|
||||||
|
if resultCode != 0 {
|
||||||
|
return NewError(resultCode, errors.New(resultDescription))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
27
src/vendor/gopkg.in/ldap.v2/client.go
generated
vendored
Normal file
27
src/vendor/gopkg.in/ldap.v2/client.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client knows how to interact with an LDAP server
|
||||||
|
type Client interface {
|
||||||
|
Start()
|
||||||
|
StartTLS(config *tls.Config) error
|
||||||
|
Close()
|
||||||
|
SetTimeout(time.Duration)
|
||||||
|
|
||||||
|
Bind(username, password string) error
|
||||||
|
SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error)
|
||||||
|
|
||||||
|
Add(addRequest *AddRequest) error
|
||||||
|
Del(delRequest *DelRequest) error
|
||||||
|
Modify(modifyRequest *ModifyRequest) error
|
||||||
|
|
||||||
|
Compare(dn, attribute, value string) (bool, error)
|
||||||
|
PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error)
|
||||||
|
|
||||||
|
Search(searchRequest *SearchRequest) (*SearchResult, error)
|
||||||
|
SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
|
||||||
|
}
|
85
src/vendor/gopkg.in/ldap.v2/compare.go
generated
vendored
Normal file
85
src/vendor/gopkg.in/ldap.v2/compare.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// File contains Compare functionality
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4511
|
||||||
|
//
|
||||||
|
// CompareRequest ::= [APPLICATION 14] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// ava AttributeValueAssertion }
|
||||||
|
//
|
||||||
|
// AttributeValueAssertion ::= SEQUENCE {
|
||||||
|
// attributeDesc AttributeDescription,
|
||||||
|
// assertionValue AssertionValue }
|
||||||
|
//
|
||||||
|
// AttributeDescription ::= LDAPString
|
||||||
|
// -- Constrained to <attributedescription>
|
||||||
|
// -- [RFC4512]
|
||||||
|
//
|
||||||
|
// AttributeValue ::= OCTET STRING
|
||||||
|
//
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise
|
||||||
|
// false with any error that occurs if any.
|
||||||
|
func (l *Conn) Compare(dn, attribute, value string) (bool, error) {
|
||||||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||||
|
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
|
||||||
|
|
||||||
|
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request")
|
||||||
|
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN"))
|
||||||
|
|
||||||
|
ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion")
|
||||||
|
ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "AttributeDesc"))
|
||||||
|
ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagOctetString, value, "AssertionValue"))
|
||||||
|
request.AppendChild(ava)
|
||||||
|
packet.AppendChild(request)
|
||||||
|
|
||||||
|
l.Debug.PrintPacket(packet)
|
||||||
|
|
||||||
|
msgCtx, err := l.sendMessage(packet)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer l.finishMessage(msgCtx)
|
||||||
|
|
||||||
|
l.Debug.Printf("%d: waiting for response", msgCtx.id)
|
||||||
|
packetResponse, ok := <-msgCtx.responses
|
||||||
|
if !ok {
|
||||||
|
return false, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
|
||||||
|
}
|
||||||
|
packet, err = packetResponse.ReadPacket()
|
||||||
|
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
if err := addLDAPDescriptions(packet); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.Children[1].Tag == ApplicationCompareResponse {
|
||||||
|
resultCode, resultDescription := getLDAPResultCode(packet)
|
||||||
|
if resultCode == LDAPResultCompareTrue {
|
||||||
|
return true, nil
|
||||||
|
} else if resultCode == LDAPResultCompareFalse {
|
||||||
|
return false, nil
|
||||||
|
} else {
|
||||||
|
return false, NewError(resultCode, errors.New(resultDescription))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag)
|
||||||
|
}
|
467
src/vendor/gopkg.in/ldap.v2/conn.go
generated
vendored
Normal file
467
src/vendor/gopkg.in/ldap.v2/conn.go
generated
vendored
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
conn net.Conn
|
||||||
|
isTLS bool
|
||||||
|
isClosing bool
|
||||||
|
closeErr error
|
||||||
|
isStartingTLS bool
|
||||||
|
Debug debugging
|
||||||
|
chanConfirm chan bool
|
||||||
|
messageContexts map[int64]*messageContext
|
||||||
|
chanMessage chan *messagePacket
|
||||||
|
chanMessageID chan int64
|
||||||
|
wgSender sync.WaitGroup
|
||||||
|
wgClose sync.WaitGroup
|
||||||
|
once sync.Once
|
||||||
|
outstandingRequests uint
|
||||||
|
messageMutex sync.Mutex
|
||||||
|
requestTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Dial connects to the given address on the given network using net.Dial
|
||||||
|
// and then returns a new Conn for the connection.
|
||||||
|
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.
|
||||||
|
func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
|
||||||
|
dc, err := net.DialTimeout(network, addr, DefaultTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorNetwork, err)
|
||||||
|
}
|
||||||
|
c := tls.Client(dc, config)
|
||||||
|
err = c.Handshake()
|
||||||
|
if err != nil {
|
||||||
|
// Handshake error, close the established connection before we return an error
|
||||||
|
dc.Close()
|
||||||
|
return nil, NewError(ErrorNetwork, err)
|
||||||
|
}
|
||||||
|
conn := NewConn(c, true)
|
||||||
|
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 bool),
|
||||||
|
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() {
|
||||||
|
go l.reader()
|
||||||
|
go l.processMessages()
|
||||||
|
l.wgClose.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the connection.
|
||||||
|
func (l *Conn) Close() {
|
||||||
|
l.once.Do(func() {
|
||||||
|
l.isClosing = true
|
||||||
|
l.wgSender.Wait()
|
||||||
|
|
||||||
|
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.Print(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 {
|
||||||
|
l.requestTimeout = timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the next available messageID
|
||||||
|
func (l *Conn) nextMessageID() int64 {
|
||||||
|
if l.chanMessageID != nil {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resultCode, message := getLDAPResultCode(packet); resultCode == LDAPResultSuccess {
|
||||||
|
conn := tls.Client(l.conn, config)
|
||||||
|
|
||||||
|
if err := conn.Handshake(); err != nil {
|
||||||
|
l.Close()
|
||||||
|
return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.isTLS = true
|
||||||
|
l.conn = conn
|
||||||
|
} else {
|
||||||
|
return NewError(resultCode, fmt.Errorf("ldap: cannot StartTLS (%s)", message))
|
||||||
|
}
|
||||||
|
go l.reader()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
l.sendProcessMessage(message)
|
||||||
|
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 {
|
||||||
|
if l.isClosing {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
l.wgSender.Add(1)
|
||||||
|
l.chanMessage <- message
|
||||||
|
l.wgSender.Done()
|
||||||
|
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 != nil {
|
||||||
|
msgCtx.sendResponse(&PacketResponse{Error: l.closeErr})
|
||||||
|
}
|
||||||
|
l.Debug.Printf("Closing channel for MessageID %d", messageID)
|
||||||
|
close(msgCtx.responses)
|
||||||
|
delete(l.messageContexts, messageID)
|
||||||
|
}
|
||||||
|
close(l.chanMessageID)
|
||||||
|
l.chanConfirm <- true
|
||||||
|
close(l.chanConfirm)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var messageID int64 = 1
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case l.chanMessageID <- messageID:
|
||||||
|
messageID++
|
||||||
|
case message, ok := <-l.chanMessage:
|
||||||
|
if !ok {
|
||||||
|
l.Debug.Printf("Shutting down - message channel is closed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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
|
||||||
|
if l.requestTimeout > 0 {
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
log.Printf("ldap: recovered panic in RequestTimeout: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(l.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)
|
||||||
|
ber.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 = fmt.Errorf("unable to read LDAP response packet: %s", err)
|
||||||
|
l.Debug.Printf("reader error: %s", err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addLDAPDescriptions(packet)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
420
src/vendor/gopkg.in/ldap.v2/control.go
generated
vendored
Normal file
420
src/vendor/gopkg.in/ldap.v2/control.go
generated
vendored
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ControlTypeMap maps controls to text descriptions
|
||||||
|
var ControlTypeMap = map[string]string{
|
||||||
|
ControlTypePaging: "Paging",
|
||||||
|
ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
|
||||||
|
ControlTypeManageDsaIT: "Manage DSA IT",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"))
|
||||||
|
}
|
||||||
|
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}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
var (
|
||||||
|
ControlType = ""
|
||||||
|
Criticality = false
|
||||||
|
value *ber.Packet
|
||||||
|
)
|
||||||
|
|
||||||
|
switch len(packet.Children) {
|
||||||
|
case 0:
|
||||||
|
// at least one child is required for control type
|
||||||
|
return nil
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ControlType {
|
||||||
|
case ControlTypeManageDsaIT:
|
||||||
|
return NewControlManageDsaIT(Criticality)
|
||||||
|
case ControlTypePaging:
|
||||||
|
value.Description += " (Paging)"
|
||||||
|
c := new(ControlPaging)
|
||||||
|
if value.Value != nil {
|
||||||
|
valueChildren := ber.DecodePacket(value.Data.Bytes())
|
||||||
|
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
|
||||||
|
case ControlTypeBeheraPasswordPolicy:
|
||||||
|
value.Description += " (Password Policy - Behera)"
|
||||||
|
c := NewControlBeheraPasswordPolicy()
|
||||||
|
if value.Value != nil {
|
||||||
|
valueChildren := ber.DecodePacket(value.Data.Bytes())
|
||||||
|
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 := ber.DecodePacket(warningPacket.Data.Bytes())
|
||||||
|
val, ok := packet.Value.(int64)
|
||||||
|
if ok {
|
||||||
|
if warningPacket.Tag == 0 {
|
||||||
|
//timeBeforeExpiration
|
||||||
|
c.Expire = val
|
||||||
|
warningPacket.Value = c.Expire
|
||||||
|
} else if warningPacket.Tag == 1 {
|
||||||
|
//graceAuthNsRemaining
|
||||||
|
c.Grace = val
|
||||||
|
warningPacket.Value = c.Grace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if child.Tag == 1 {
|
||||||
|
// Error
|
||||||
|
packet := ber.DecodePacket(child.Data.Bytes())
|
||||||
|
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
|
||||||
|
case ControlTypeVChuPasswordMustChange:
|
||||||
|
c := &ControlVChuPasswordMustChange{MustChange: true}
|
||||||
|
return c
|
||||||
|
case ControlTypeVChuPasswordWarning:
|
||||||
|
c := &ControlVChuPasswordWarning{Expire: -1}
|
||||||
|
expireStr := ber.DecodeString(value.Data.Bytes())
|
||||||
|
|
||||||
|
expire, err := strconv.ParseInt(expireStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c.Expire = expire
|
||||||
|
value.Value = c.Expire
|
||||||
|
|
||||||
|
return c
|
||||||
|
default:
|
||||||
|
c := new(ControlString)
|
||||||
|
c.ControlType = ControlType
|
||||||
|
c.Criticality = Criticality
|
||||||
|
if value != nil {
|
||||||
|
c.ControlValue = value.Value.(string)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
24
src/vendor/gopkg.in/ldap.v2/debug.go
generated
vendored
Normal file
24
src/vendor/gopkg.in/ldap.v2/debug.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// debbuging type
|
||||||
|
// - has a Printf method to write the debug output
|
||||||
|
type debugging bool
|
||||||
|
|
||||||
|
// write debug output
|
||||||
|
func (debug debugging) Printf(format string, args ...interface{}) {
|
||||||
|
if debug {
|
||||||
|
log.Printf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (debug debugging) PrintPacket(packet *ber.Packet) {
|
||||||
|
if debug {
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
}
|
84
src/vendor/gopkg.in/ldap.v2/del.go
generated
vendored
Normal file
84
src/vendor/gopkg.in/ldap.v2/del.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4511
|
||||||
|
//
|
||||||
|
// DelRequest ::= [APPLICATION 10] LDAPDN
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DelRequest implements an LDAP deletion request
|
||||||
|
type DelRequest struct {
|
||||||
|
// DN is the name of the directory entry to delete
|
||||||
|
DN string
|
||||||
|
// Controls hold optional controls to send with the request
|
||||||
|
Controls []Control
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DelRequest) encode() *ber.Packet {
|
||||||
|
request := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, d.DN, "Del Request")
|
||||||
|
request.Data.Write([]byte(d.DN))
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDelRequest creates a delete request for the given DN and controls
|
||||||
|
func NewDelRequest(DN string,
|
||||||
|
Controls []Control) *DelRequest {
|
||||||
|
return &DelRequest{
|
||||||
|
DN: DN,
|
||||||
|
Controls: Controls,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del executes the given delete request
|
||||||
|
func (l *Conn) Del(delRequest *DelRequest) error {
|
||||||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||||
|
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
|
||||||
|
packet.AppendChild(delRequest.encode())
|
||||||
|
if delRequest.Controls != nil {
|
||||||
|
packet.AppendChild(encodeControls(delRequest.Controls))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Debug.PrintPacket(packet)
|
||||||
|
|
||||||
|
msgCtx, err := l.sendMessage(packet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer l.finishMessage(msgCtx)
|
||||||
|
|
||||||
|
l.Debug.Printf("%d: waiting for response", msgCtx.id)
|
||||||
|
packetResponse, ok := <-msgCtx.responses
|
||||||
|
if !ok {
|
||||||
|
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
|
||||||
|
}
|
||||||
|
packet, err = packetResponse.ReadPacket()
|
||||||
|
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
if err := addLDAPDescriptions(packet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.Children[1].Tag == ApplicationDelResponse {
|
||||||
|
resultCode, resultDescription := getLDAPResultCode(packet)
|
||||||
|
if resultCode != 0 {
|
||||||
|
return NewError(resultCode, errors.New(resultDescription))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Debug.Printf("%d: returning", msgCtx.id)
|
||||||
|
return nil
|
||||||
|
}
|
244
src/vendor/gopkg.in/ldap.v2/dn.go
generated
vendored
Normal file
244
src/vendor/gopkg.in/ldap.v2/dn.go
generated
vendored
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// File contains DN parsing functionallity
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4514
|
||||||
|
//
|
||||||
|
// distinguishedName = [ relativeDistinguishedName
|
||||||
|
// *( COMMA relativeDistinguishedName ) ]
|
||||||
|
// relativeDistinguishedName = attributeTypeAndValue
|
||||||
|
// *( PLUS attributeTypeAndValue )
|
||||||
|
// attributeTypeAndValue = attributeType EQUALS attributeValue
|
||||||
|
// attributeType = descr / numericoid
|
||||||
|
// attributeValue = string / hexstring
|
||||||
|
//
|
||||||
|
// ; The following characters are to be escaped when they appear
|
||||||
|
// ; in the value to be encoded: ESC, one of <escaped>, leading
|
||||||
|
// ; SHARP or SPACE, trailing SPACE, and NULL.
|
||||||
|
// string = [ ( leadchar / pair ) [ *( stringchar / pair )
|
||||||
|
// ( trailchar / pair ) ] ]
|
||||||
|
//
|
||||||
|
// leadchar = LUTF1 / UTFMB
|
||||||
|
// LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A /
|
||||||
|
// %x3D / %x3F-5B / %x5D-7F
|
||||||
|
//
|
||||||
|
// trailchar = TUTF1 / UTFMB
|
||||||
|
// TUTF1 = %x01-1F / %x21 / %x23-2A / %x2D-3A /
|
||||||
|
// %x3D / %x3F-5B / %x5D-7F
|
||||||
|
//
|
||||||
|
// stringchar = SUTF1 / UTFMB
|
||||||
|
// SUTF1 = %x01-21 / %x23-2A / %x2D-3A /
|
||||||
|
// %x3D / %x3F-5B / %x5D-7F
|
||||||
|
//
|
||||||
|
// pair = ESC ( ESC / special / hexpair )
|
||||||
|
// special = escaped / SPACE / SHARP / EQUALS
|
||||||
|
// escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
|
||||||
|
// hexstring = SHARP 1*hexpair
|
||||||
|
// hexpair = HEX HEX
|
||||||
|
//
|
||||||
|
// where the productions <descr>, <numericoid>, <COMMA>, <DQUOTE>,
|
||||||
|
// <EQUALS>, <ESC>, <HEX>, <LANGLE>, <NULL>, <PLUS>, <RANGLE>, <SEMI>,
|
||||||
|
// <SPACE>, <SHARP>, and <UTFMB> are defined in [RFC4512].
|
||||||
|
//
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
enchex "encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
ber "gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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]
|
||||||
|
if 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++
|
||||||
|
} else if char == '\\' {
|
||||||
|
unescapedTrailingSpaces = 0
|
||||||
|
escaping = true
|
||||||
|
} else if 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 := ber.DecodePacket(rawBER)
|
||||||
|
buffer.WriteString(packet.Data.String())
|
||||||
|
i += len(data) - 1
|
||||||
|
}
|
||||||
|
} else if char == ',' || char == '+' {
|
||||||
|
// We're done with this RDN or value, push it
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
} else if char == ' ' && buffer.Len() == 0 {
|
||||||
|
// ignore unescaped leading spaces
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
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
src/vendor/gopkg.in/ldap.v2/doc.go
generated
vendored
Normal file
4
src/vendor/gopkg.in/ldap.v2/doc.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
Package ldap provides basic LDAP v3 functionality.
|
||||||
|
*/
|
||||||
|
package ldap
|
148
src/vendor/gopkg.in/ldap.v2/error.go
generated
vendored
Normal file
148
src/vendor/gopkg.in/ldap.v2/error.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LDAP Result Codes
|
||||||
|
const (
|
||||||
|
LDAPResultSuccess = 0
|
||||||
|
LDAPResultOperationsError = 1
|
||||||
|
LDAPResultProtocolError = 2
|
||||||
|
LDAPResultTimeLimitExceeded = 3
|
||||||
|
LDAPResultSizeLimitExceeded = 4
|
||||||
|
LDAPResultCompareFalse = 5
|
||||||
|
LDAPResultCompareTrue = 6
|
||||||
|
LDAPResultAuthMethodNotSupported = 7
|
||||||
|
LDAPResultStrongAuthRequired = 8
|
||||||
|
LDAPResultReferral = 10
|
||||||
|
LDAPResultAdminLimitExceeded = 11
|
||||||
|
LDAPResultUnavailableCriticalExtension = 12
|
||||||
|
LDAPResultConfidentialityRequired = 13
|
||||||
|
LDAPResultSaslBindInProgress = 14
|
||||||
|
LDAPResultNoSuchAttribute = 16
|
||||||
|
LDAPResultUndefinedAttributeType = 17
|
||||||
|
LDAPResultInappropriateMatching = 18
|
||||||
|
LDAPResultConstraintViolation = 19
|
||||||
|
LDAPResultAttributeOrValueExists = 20
|
||||||
|
LDAPResultInvalidAttributeSyntax = 21
|
||||||
|
LDAPResultNoSuchObject = 32
|
||||||
|
LDAPResultAliasProblem = 33
|
||||||
|
LDAPResultInvalidDNSyntax = 34
|
||||||
|
LDAPResultAliasDereferencingProblem = 36
|
||||||
|
LDAPResultInappropriateAuthentication = 48
|
||||||
|
LDAPResultInvalidCredentials = 49
|
||||||
|
LDAPResultInsufficientAccessRights = 50
|
||||||
|
LDAPResultBusy = 51
|
||||||
|
LDAPResultUnavailable = 52
|
||||||
|
LDAPResultUnwillingToPerform = 53
|
||||||
|
LDAPResultLoopDetect = 54
|
||||||
|
LDAPResultNamingViolation = 64
|
||||||
|
LDAPResultObjectClassViolation = 65
|
||||||
|
LDAPResultNotAllowedOnNonLeaf = 66
|
||||||
|
LDAPResultNotAllowedOnRDN = 67
|
||||||
|
LDAPResultEntryAlreadyExists = 68
|
||||||
|
LDAPResultObjectClassModsProhibited = 69
|
||||||
|
LDAPResultAffectsMultipleDSAs = 71
|
||||||
|
LDAPResultOther = 80
|
||||||
|
|
||||||
|
ErrorNetwork = 200
|
||||||
|
ErrorFilterCompile = 201
|
||||||
|
ErrorFilterDecompile = 202
|
||||||
|
ErrorDebugging = 203
|
||||||
|
ErrorUnexpectedMessage = 204
|
||||||
|
ErrorUnexpectedResponse = 205
|
||||||
|
)
|
||||||
|
|
||||||
|
// LDAPResultCodeMap contains string descriptions for LDAP error codes
|
||||||
|
var LDAPResultCodeMap = map[uint8]string{
|
||||||
|
LDAPResultSuccess: "Success",
|
||||||
|
LDAPResultOperationsError: "Operations Error",
|
||||||
|
LDAPResultProtocolError: "Protocol Error",
|
||||||
|
LDAPResultTimeLimitExceeded: "Time Limit Exceeded",
|
||||||
|
LDAPResultSizeLimitExceeded: "Size Limit Exceeded",
|
||||||
|
LDAPResultCompareFalse: "Compare False",
|
||||||
|
LDAPResultCompareTrue: "Compare True",
|
||||||
|
LDAPResultAuthMethodNotSupported: "Auth Method Not Supported",
|
||||||
|
LDAPResultStrongAuthRequired: "Strong Auth Required",
|
||||||
|
LDAPResultReferral: "Referral",
|
||||||
|
LDAPResultAdminLimitExceeded: "Admin Limit Exceeded",
|
||||||
|
LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension",
|
||||||
|
LDAPResultConfidentialityRequired: "Confidentiality Required",
|
||||||
|
LDAPResultSaslBindInProgress: "Sasl Bind In Progress",
|
||||||
|
LDAPResultNoSuchAttribute: "No Such Attribute",
|
||||||
|
LDAPResultUndefinedAttributeType: "Undefined Attribute Type",
|
||||||
|
LDAPResultInappropriateMatching: "Inappropriate Matching",
|
||||||
|
LDAPResultConstraintViolation: "Constraint Violation",
|
||||||
|
LDAPResultAttributeOrValueExists: "Attribute Or Value Exists",
|
||||||
|
LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax",
|
||||||
|
LDAPResultNoSuchObject: "No Such Object",
|
||||||
|
LDAPResultAliasProblem: "Alias Problem",
|
||||||
|
LDAPResultInvalidDNSyntax: "Invalid DN Syntax",
|
||||||
|
LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem",
|
||||||
|
LDAPResultInappropriateAuthentication: "Inappropriate Authentication",
|
||||||
|
LDAPResultInvalidCredentials: "Invalid Credentials",
|
||||||
|
LDAPResultInsufficientAccessRights: "Insufficient Access Rights",
|
||||||
|
LDAPResultBusy: "Busy",
|
||||||
|
LDAPResultUnavailable: "Unavailable",
|
||||||
|
LDAPResultUnwillingToPerform: "Unwilling To Perform",
|
||||||
|
LDAPResultLoopDetect: "Loop Detect",
|
||||||
|
LDAPResultNamingViolation: "Naming Violation",
|
||||||
|
LDAPResultObjectClassViolation: "Object Class Violation",
|
||||||
|
LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf",
|
||||||
|
LDAPResultNotAllowedOnRDN: "Not Allowed On RDN",
|
||||||
|
LDAPResultEntryAlreadyExists: "Entry Already Exists",
|
||||||
|
LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited",
|
||||||
|
LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs",
|
||||||
|
LDAPResultOther: "Other",
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) {
|
||||||
|
if packet == nil {
|
||||||
|
return ErrorUnexpectedResponse, "Empty packet"
|
||||||
|
} else if len(packet.Children) >= 2 {
|
||||||
|
response := packet.Children[1]
|
||||||
|
if response == nil {
|
||||||
|
return ErrorUnexpectedResponse, "Empty response in packet"
|
||||||
|
}
|
||||||
|
if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
|
||||||
|
// Children[1].Children[2] is the diagnosticMessage which is guaranteed to exist as seen here: https://tools.ietf.org/html/rfc4511#section-4.1.9
|
||||||
|
return uint8(response.Children[0].Value.(int64)), response.Children[2].Value.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorNetwork, "Invalid packet format"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error holds LDAP error information
|
||||||
|
type Error struct {
|
||||||
|
// Err is the underlying error
|
||||||
|
Err error
|
||||||
|
// ResultCode is the LDAP error code
|
||||||
|
ResultCode uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewError creates an LDAP error with the given code and underlying error
|
||||||
|
func NewError(resultCode uint8, err error) error {
|
||||||
|
return &Error{ResultCode: resultCode, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrorWithCode returns true if the given error is an LDAP error with the given result code
|
||||||
|
func IsErrorWithCode(err error, desiredResultCode uint8) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
serverError, ok := err.(*Error)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return serverError.ResultCode == desiredResultCode
|
||||||
|
}
|
466
src/vendor/gopkg.in/ldap.v2/filter.go
generated
vendored
Normal file
466
src/vendor/gopkg.in/ldap.v2/filter.go
generated
vendored
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
hexpac "encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
if 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) (ret string, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ret = "("
|
||||||
|
err = nil
|
||||||
|
childStr := ""
|
||||||
|
|
||||||
|
switch packet.Tag {
|
||||||
|
case FilterAnd:
|
||||||
|
ret += "&"
|
||||||
|
for _, child := range packet.Children {
|
||||||
|
childStr, err = DecompileFilter(child)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret += childStr
|
||||||
|
}
|
||||||
|
case FilterOr:
|
||||||
|
ret += "|"
|
||||||
|
for _, child := range packet.Children {
|
||||||
|
childStr, err = DecompileFilter(child)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret += childStr
|
||||||
|
}
|
||||||
|
case FilterNot:
|
||||||
|
ret += "!"
|
||||||
|
childStr, err = DecompileFilter(packet.Children[0])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret += childStr
|
||||||
|
|
||||||
|
case FilterSubstrings:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += "="
|
||||||
|
for i, child := range packet.Children[1].Children {
|
||||||
|
if i == 0 && child.Tag != FilterSubstringsInitial {
|
||||||
|
ret += "*"
|
||||||
|
}
|
||||||
|
ret += EscapeFilter(ber.DecodeString(child.Data.Bytes()))
|
||||||
|
if child.Tag != FilterSubstringsFinal {
|
||||||
|
ret += "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case FilterEqualityMatch:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += "="
|
||||||
|
ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
|
||||||
|
case FilterGreaterOrEqual:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += ">="
|
||||||
|
ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
|
||||||
|
case FilterLessOrEqual:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += "<="
|
||||||
|
ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
|
||||||
|
case FilterPresent:
|
||||||
|
ret += ber.DecodeString(packet.Data.Bytes())
|
||||||
|
ret += "=*"
|
||||||
|
case FilterApproxMatch:
|
||||||
|
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
ret += "~="
|
||||||
|
ret += 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 {
|
||||||
|
ret += attr
|
||||||
|
}
|
||||||
|
if dnAttributes {
|
||||||
|
ret += ":dn"
|
||||||
|
}
|
||||||
|
if len(matchingRule) > 0 {
|
||||||
|
ret += ":"
|
||||||
|
ret += matchingRule
|
||||||
|
}
|
||||||
|
ret += ":="
|
||||||
|
ret += EscapeFilter(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += ")"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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 := ""
|
||||||
|
extensibleDNAttributes := false
|
||||||
|
extensibleMatchingRule := ""
|
||||||
|
condition := ""
|
||||||
|
|
||||||
|
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 += fmt.Sprintf("%c", 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 += fmt.Sprintf("%c", currentRune)
|
||||||
|
newPos += currentWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
case stateReadingCondition:
|
||||||
|
// append to the condition
|
||||||
|
condition += fmt.Sprintf("%c", 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 len(extensibleMatchingRule) > 0 {
|
||||||
|
packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule, MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include the attribute, if specified
|
||||||
|
if len(attribute) > 0 {
|
||||||
|
packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute, MatchingRuleAssertionMap[MatchingRuleAssertionType]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the value (only required child)
|
||||||
|
encodedString, encodeErr := escapedStringToEncodedBytes(condition)
|
||||||
|
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 && condition == "*":
|
||||||
|
packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute, FilterMap[FilterPresent])
|
||||||
|
case packet.Tag == FilterEqualityMatch && strings.Contains(condition, "*"):
|
||||||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
|
||||||
|
packet.Tag = FilterSubstrings
|
||||||
|
packet.Description = FilterMap[uint64(packet.Tag)]
|
||||||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
|
||||||
|
parts := strings.Split(condition, "*")
|
||||||
|
for i, part := range parts {
|
||||||
|
if part == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var tag ber.Tag
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
tag = FilterSubstringsInitial
|
||||||
|
case len(parts) - 1:
|
||||||
|
tag = FilterSubstringsFinal
|
||||||
|
default:
|
||||||
|
tag = FilterSubstringsAny
|
||||||
|
}
|
||||||
|
encodedString, encodeErr := escapedStringToEncodedBytes(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 := escapedStringToEncodedBytes(condition)
|
||||||
|
if encodeErr != nil {
|
||||||
|
return packet, newPos, encodeErr
|
||||||
|
}
|
||||||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
|
||||||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, encodedString, "Condition"))
|
||||||
|
}
|
||||||
|
|
||||||
|
newPos += currentWidth
|
||||||
|
return packet, newPos, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from "ABC\xx\xx\xx" form to literal bytes for transport
|
||||||
|
func escapedStringToEncodedBytes(escapedString string) (string, error) {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
i := 0
|
||||||
|
for i < len(escapedString) {
|
||||||
|
currentRune, currentWidth := utf8.DecodeRuneInString(escapedString[i:])
|
||||||
|
if currentRune == utf8.RuneError {
|
||||||
|
return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for escaped hex characters and convert them to their literal value for transport.
|
||||||
|
if currentRune == '\\' {
|
||||||
|
// http://tools.ietf.org/search/rfc4515
|
||||||
|
// \ (%x5C) is not a valid character unless it is followed by two HEX characters due to not
|
||||||
|
// being a member of UTF1SUBSET.
|
||||||
|
if i+2 > len(escapedString) {
|
||||||
|
return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter"))
|
||||||
|
}
|
||||||
|
escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3])
|
||||||
|
if decodeErr != nil {
|
||||||
|
return "", NewError(ErrorFilterCompile, errors.New("ldap: invalid characters for escape in filter"))
|
||||||
|
}
|
||||||
|
buffer.WriteByte(escByte[0])
|
||||||
|
i += 2 // +1 from end of loop, so 3 total for \xx.
|
||||||
|
} else {
|
||||||
|
buffer.WriteRune(currentRune)
|
||||||
|
}
|
||||||
|
|
||||||
|
i += currentWidth
|
||||||
|
}
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
320
src/vendor/gopkg.in/ldap.v2/ldap.go
generated
vendored
Normal file
320
src/vendor/gopkg.in/ldap.v2/ldap.go
generated
vendored
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
ber "gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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, errors.New("ldap: cannot process packet to add descriptions"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
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:
|
||||||
|
addRequestDescriptions(packet)
|
||||||
|
case ApplicationBindResponse:
|
||||||
|
addDefaultLDAPResponseDescriptions(packet)
|
||||||
|
case ApplicationUnbindRequest:
|
||||||
|
addRequestDescriptions(packet)
|
||||||
|
case ApplicationSearchRequest:
|
||||||
|
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 {
|
||||||
|
addControlDescriptions(packet.Children[2])
|
||||||
|
}
|
||||||
|
case ApplicationSearchResultDone:
|
||||||
|
addDefaultLDAPResponseDescriptions(packet)
|
||||||
|
case ApplicationModifyRequest:
|
||||||
|
addRequestDescriptions(packet)
|
||||||
|
case ApplicationModifyResponse:
|
||||||
|
case ApplicationAddRequest:
|
||||||
|
addRequestDescriptions(packet)
|
||||||
|
case ApplicationAddResponse:
|
||||||
|
case ApplicationDelRequest:
|
||||||
|
addRequestDescriptions(packet)
|
||||||
|
case ApplicationDelResponse:
|
||||||
|
case ApplicationModifyDNRequest:
|
||||||
|
addRequestDescriptions(packet)
|
||||||
|
case ApplicationModifyDNResponse:
|
||||||
|
case ApplicationCompareRequest:
|
||||||
|
addRequestDescriptions(packet)
|
||||||
|
case ApplicationCompareResponse:
|
||||||
|
case ApplicationAbandonRequest:
|
||||||
|
addRequestDescriptions(packet)
|
||||||
|
case ApplicationSearchResultReference:
|
||||||
|
case ApplicationExtendedRequest:
|
||||||
|
addRequestDescriptions(packet)
|
||||||
|
case ApplicationExtendedResponse:
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addControlDescriptions(packet *ber.Packet) {
|
||||||
|
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
|
||||||
|
continue
|
||||||
|
|
||||||
|
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
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if value == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch controlType {
|
||||||
|
case ControlTypePaging:
|
||||||
|
value.Description += " (Paging)"
|
||||||
|
if value.Value != nil {
|
||||||
|
valueChildren := ber.DecodePacket(value.Data.Bytes())
|
||||||
|
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 := ber.DecodePacket(value.Data.Bytes())
|
||||||
|
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 := ber.DecodePacket(warningPacket.Data.Bytes())
|
||||||
|
val, ok := packet.Value.(int64)
|
||||||
|
if ok {
|
||||||
|
if warningPacket.Tag == 0 {
|
||||||
|
//timeBeforeExpiration
|
||||||
|
value.Description += " (TimeBeforeExpiration)"
|
||||||
|
warningPacket.Value = val
|
||||||
|
} else if warningPacket.Tag == 1 {
|
||||||
|
//graceAuthNsRemaining
|
||||||
|
value.Description += " (GraceAuthNsRemaining)"
|
||||||
|
warningPacket.Value = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if child.Tag == 1 {
|
||||||
|
// Error
|
||||||
|
packet := ber.DecodePacket(child.Data.Bytes())
|
||||||
|
val, ok := packet.Value.(int8)
|
||||||
|
if !ok {
|
||||||
|
val = -1
|
||||||
|
}
|
||||||
|
child.Description = "Error"
|
||||||
|
child.Value = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addRequestDescriptions(packet *ber.Packet) {
|
||||||
|
packet.Description = "LDAP Request"
|
||||||
|
packet.Children[0].Description = "Message ID"
|
||||||
|
packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)]
|
||||||
|
if len(packet.Children) == 3 {
|
||||||
|
addControlDescriptions(packet.Children[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDefaultLDAPResponseDescriptions(packet *ber.Packet) {
|
||||||
|
resultCode, _ := getLDAPResultCode(packet)
|
||||||
|
packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")"
|
||||||
|
packet.Children[1].Children[1].Description = "Matched DN"
|
||||||
|
packet.Children[1].Children[2].Description = "Error Message"
|
||||||
|
if len(packet.Children[1].Children) > 3 {
|
||||||
|
packet.Children[1].Children[3].Description = "Referral"
|
||||||
|
}
|
||||||
|
if len(packet.Children) == 3 {
|
||||||
|
addControlDescriptions(packet.Children[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 := ber.DecodePacket(file)
|
||||||
|
addLDAPDescriptions(packet)
|
||||||
|
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)
|
||||||
|
}
|
170
src/vendor/gopkg.in/ldap.v2/modify.go
generated
vendored
Normal file
170
src/vendor/gopkg.in/ldap.v2/modify.go
generated
vendored
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// File contains Modify functionality
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4511
|
||||||
|
//
|
||||||
|
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||||
|
// object LDAPDN,
|
||||||
|
// changes SEQUENCE OF change SEQUENCE {
|
||||||
|
// operation ENUMERATED {
|
||||||
|
// add (0),
|
||||||
|
// delete (1),
|
||||||
|
// replace (2),
|
||||||
|
// ... },
|
||||||
|
// modification PartialAttribute } }
|
||||||
|
//
|
||||||
|
// PartialAttribute ::= SEQUENCE {
|
||||||
|
// type AttributeDescription,
|
||||||
|
// vals SET OF value AttributeValue }
|
||||||
|
//
|
||||||
|
// AttributeDescription ::= LDAPString
|
||||||
|
// -- Constrained to <attributedescription>
|
||||||
|
// -- [RFC4512]
|
||||||
|
//
|
||||||
|
// AttributeValue ::= OCTET STRING
|
||||||
|
//
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Change operation choices
|
||||||
|
const (
|
||||||
|
AddAttribute = 0
|
||||||
|
DeleteAttribute = 1
|
||||||
|
ReplaceAttribute = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
|
||||||
|
type PartialAttribute struct {
|
||||||
|
// Type is the type of the partial attribute
|
||||||
|
Type string
|
||||||
|
// Vals are the values of the partial attribute
|
||||||
|
Vals []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PartialAttribute) encode() *ber.Packet {
|
||||||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute")
|
||||||
|
seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type"))
|
||||||
|
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
|
||||||
|
for _, value := range p.Vals {
|
||||||
|
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
|
||||||
|
}
|
||||||
|
seq.AppendChild(set)
|
||||||
|
return seq
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
|
||||||
|
type ModifyRequest struct {
|
||||||
|
// DN is the distinguishedName of the directory entry to modify
|
||||||
|
DN string
|
||||||
|
// AddAttributes contain the attributes to add
|
||||||
|
AddAttributes []PartialAttribute
|
||||||
|
// DeleteAttributes contain the attributes to delete
|
||||||
|
DeleteAttributes []PartialAttribute
|
||||||
|
// ReplaceAttributes contain the attributes to replace
|
||||||
|
ReplaceAttributes []PartialAttribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add inserts the given attribute to the list of attributes to add
|
||||||
|
func (m *ModifyRequest) Add(attrType string, attrVals []string) {
|
||||||
|
m.AddAttributes = append(m.AddAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete inserts the given attribute to the list of attributes to delete
|
||||||
|
func (m *ModifyRequest) Delete(attrType string, attrVals []string) {
|
||||||
|
m.DeleteAttributes = append(m.DeleteAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace inserts the given attribute to the list of attributes to replace
|
||||||
|
func (m *ModifyRequest) Replace(attrType string, attrVals []string) {
|
||||||
|
m.ReplaceAttributes = append(m.ReplaceAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ModifyRequest) encode() *ber.Packet {
|
||||||
|
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request")
|
||||||
|
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN"))
|
||||||
|
changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes")
|
||||||
|
for _, attribute := range m.AddAttributes {
|
||||||
|
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
|
||||||
|
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(AddAttribute), "Operation"))
|
||||||
|
change.AppendChild(attribute.encode())
|
||||||
|
changes.AppendChild(change)
|
||||||
|
}
|
||||||
|
for _, attribute := range m.DeleteAttributes {
|
||||||
|
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
|
||||||
|
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(DeleteAttribute), "Operation"))
|
||||||
|
change.AppendChild(attribute.encode())
|
||||||
|
changes.AppendChild(change)
|
||||||
|
}
|
||||||
|
for _, attribute := range m.ReplaceAttributes {
|
||||||
|
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
|
||||||
|
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ReplaceAttribute), "Operation"))
|
||||||
|
change.AppendChild(attribute.encode())
|
||||||
|
changes.AppendChild(change)
|
||||||
|
}
|
||||||
|
request.AppendChild(changes)
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewModifyRequest creates a modify request for the given DN
|
||||||
|
func NewModifyRequest(
|
||||||
|
dn string,
|
||||||
|
) *ModifyRequest {
|
||||||
|
return &ModifyRequest{
|
||||||
|
DN: dn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify performs the ModifyRequest
|
||||||
|
func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
|
||||||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||||
|
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
|
||||||
|
packet.AppendChild(modifyRequest.encode())
|
||||||
|
|
||||||
|
l.Debug.PrintPacket(packet)
|
||||||
|
|
||||||
|
msgCtx, err := l.sendMessage(packet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer l.finishMessage(msgCtx)
|
||||||
|
|
||||||
|
l.Debug.Printf("%d: waiting for response", msgCtx.id)
|
||||||
|
packetResponse, ok := <-msgCtx.responses
|
||||||
|
if !ok {
|
||||||
|
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
|
||||||
|
}
|
||||||
|
packet, err = packetResponse.ReadPacket()
|
||||||
|
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
if err := addLDAPDescriptions(packet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.Children[1].Tag == ApplicationModifyResponse {
|
||||||
|
resultCode, resultDescription := getLDAPResultCode(packet)
|
||||||
|
if resultCode != 0 {
|
||||||
|
return NewError(resultCode, errors.New(resultDescription))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Debug.Printf("%d: returning", msgCtx.id)
|
||||||
|
return nil
|
||||||
|
}
|
148
src/vendor/gopkg.in/ldap.v2/passwdmodify.go
generated
vendored
Normal file
148
src/vendor/gopkg.in/ldap.v2/passwdmodify.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// This file contains the password modify extended operation as specified in rfc 3062
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc3062
|
||||||
|
//
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PasswordModifyRequest) encode() (*ber.Packet, error) {
|
||||||
|
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation")
|
||||||
|
request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID"))
|
||||||
|
extendedRequestValue := ber.Encode(ber.ClassContext, ber.TypePrimitive, 1, nil, "Extended Request Value: Password Modify Request")
|
||||||
|
passwordModifyRequestValue := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Password Modify Request")
|
||||||
|
if r.UserIdentity != "" {
|
||||||
|
passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, r.UserIdentity, "User Identity"))
|
||||||
|
}
|
||||||
|
if r.OldPassword != "" {
|
||||||
|
passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, r.OldPassword, "Old Password"))
|
||||||
|
}
|
||||||
|
if r.NewPassword != "" {
|
||||||
|
passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, r.NewPassword, "New Password"))
|
||||||
|
}
|
||||||
|
|
||||||
|
extendedRequestValue.AppendChild(passwordModifyRequestValue)
|
||||||
|
request.AppendChild(extendedRequestValue)
|
||||||
|
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPasswordModifyRequest creates a new PasswordModifyRequest
|
||||||
|
//
|
||||||
|
// According to the RFC 3602:
|
||||||
|
// 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) {
|
||||||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||||
|
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
|
||||||
|
|
||||||
|
encodedPasswordModifyRequest, err := passwordModifyRequest.encode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
packet.AppendChild(encodedPasswordModifyRequest)
|
||||||
|
|
||||||
|
l.Debug.PrintPacket(packet)
|
||||||
|
|
||||||
|
msgCtx, err := l.sendMessage(packet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer l.finishMessage(msgCtx)
|
||||||
|
|
||||||
|
result := &PasswordModifyResult{}
|
||||||
|
|
||||||
|
l.Debug.Printf("%d: waiting for response", msgCtx.id)
|
||||||
|
packetResponse, ok := <-msgCtx.responses
|
||||||
|
if !ok {
|
||||||
|
return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
|
||||||
|
}
|
||||||
|
packet, err = packetResponse.ReadPacket()
|
||||||
|
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet == nil {
|
||||||
|
return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
if err := addLDAPDescriptions(packet); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.Children[1].Tag == ApplicationExtendedResponse {
|
||||||
|
resultCode, resultDescription := getLDAPResultCode(packet)
|
||||||
|
if resultCode != 0 {
|
||||||
|
return nil, NewError(resultCode, errors.New(resultDescription))
|
||||||
|
}
|
||||||
|
} 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 {
|
||||||
|
passwordModifyReponseValue := ber.DecodePacket(child.Data.Bytes())
|
||||||
|
if len(passwordModifyReponseValue.Children) == 1 {
|
||||||
|
if passwordModifyReponseValue.Children[0].Tag == 0 {
|
||||||
|
result.GeneratedPassword = ber.DecodeString(passwordModifyReponseValue.Children[0].Data.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
450
src/vendor/gopkg.in/ldap.v2/search.go
generated
vendored
Normal file
450
src/vendor/gopkg.in/ldap.v2/search.go
generated
vendored
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
//
|
||||||
|
// File contains Search functionality
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4511
|
||||||
|
//
|
||||||
|
// SearchRequest ::= [APPLICATION 3] SEQUENCE {
|
||||||
|
// baseObject LDAPDN,
|
||||||
|
// scope ENUMERATED {
|
||||||
|
// baseObject (0),
|
||||||
|
// singleLevel (1),
|
||||||
|
// wholeSubtree (2),
|
||||||
|
// ... },
|
||||||
|
// derefAliases ENUMERATED {
|
||||||
|
// neverDerefAliases (0),
|
||||||
|
// derefInSearching (1),
|
||||||
|
// derefFindingBaseObj (2),
|
||||||
|
// derefAlways (3) },
|
||||||
|
// sizeLimit INTEGER (0 .. maxInt),
|
||||||
|
// timeLimit INTEGER (0 .. maxInt),
|
||||||
|
// typesOnly BOOLEAN,
|
||||||
|
// filter Filter,
|
||||||
|
// attributes AttributeSelection }
|
||||||
|
//
|
||||||
|
// AttributeSelection ::= SEQUENCE OF selector LDAPString
|
||||||
|
// -- The LDAPString is constrained to
|
||||||
|
// -- <attributeSelector> in Section 4.5.1.8
|
||||||
|
//
|
||||||
|
// Filter ::= CHOICE {
|
||||||
|
// and [0] SET SIZE (1..MAX) OF filter Filter,
|
||||||
|
// or [1] SET SIZE (1..MAX) OF filter Filter,
|
||||||
|
// not [2] Filter,
|
||||||
|
// equalityMatch [3] AttributeValueAssertion,
|
||||||
|
// substrings [4] SubstringFilter,
|
||||||
|
// greaterOrEqual [5] AttributeValueAssertion,
|
||||||
|
// lessOrEqual [6] AttributeValueAssertion,
|
||||||
|
// present [7] AttributeDescription,
|
||||||
|
// approxMatch [8] AttributeValueAssertion,
|
||||||
|
// extensibleMatch [9] MatchingRuleAssertion,
|
||||||
|
// ... }
|
||||||
|
//
|
||||||
|
// SubstringFilter ::= SEQUENCE {
|
||||||
|
// type AttributeDescription,
|
||||||
|
// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
|
||||||
|
// initial [0] AssertionValue, -- can occur at most once
|
||||||
|
// any [1] AssertionValue,
|
||||||
|
// final [2] AssertionValue } -- can occur at most once
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// MatchingRuleAssertion ::= SEQUENCE {
|
||||||
|
// matchingRule [1] MatchingRuleId OPTIONAL,
|
||||||
|
// type [2] AttributeDescription OPTIONAL,
|
||||||
|
// matchValue [3] AssertionValue,
|
||||||
|
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/asn1-ber.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 (s *SearchRequest) encode() (*ber.Packet, error) {
|
||||||
|
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request")
|
||||||
|
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, s.BaseDN, "Base DN"))
|
||||||
|
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.Scope), "Scope"))
|
||||||
|
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.DerefAliases), "Deref Aliases"))
|
||||||
|
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.SizeLimit), "Size Limit"))
|
||||||
|
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.TimeLimit), "Time Limit"))
|
||||||
|
request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, s.TypesOnly, "Types Only"))
|
||||||
|
// compile and encode filter
|
||||||
|
filterPacket, err := CompileFilter(s.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
request.AppendChild(filterPacket)
|
||||||
|
// encode attributes
|
||||||
|
attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
|
||||||
|
for _, attribute := range s.Attributes {
|
||||||
|
attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
|
||||||
|
}
|
||||||
|
request.AppendChild(attributesPacket)
|
||||||
|
return request, 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) {
|
||||||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||||
|
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
|
||||||
|
// encode search request
|
||||||
|
encodedSearchRequest, err := searchRequest.encode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
packet.AppendChild(encodedSearchRequest)
|
||||||
|
// encode search controls
|
||||||
|
if searchRequest.Controls != nil {
|
||||||
|
packet.AppendChild(encodeControls(searchRequest.Controls))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Debug.PrintPacket(packet)
|
||||||
|
|
||||||
|
msgCtx, err := l.sendMessage(packet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer l.finishMessage(msgCtx)
|
||||||
|
|
||||||
|
result := &SearchResult{
|
||||||
|
Entries: make([]*Entry, 0),
|
||||||
|
Referrals: make([]string, 0),
|
||||||
|
Controls: make([]Control, 0)}
|
||||||
|
|
||||||
|
foundSearchResultDone := false
|
||||||
|
for !foundSearchResultDone {
|
||||||
|
l.Debug.Printf("%d: waiting for response", msgCtx.id)
|
||||||
|
packetResponse, ok := <-msgCtx.responses
|
||||||
|
if !ok {
|
||||||
|
return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
|
||||||
|
}
|
||||||
|
packet, err = packetResponse.ReadPacket()
|
||||||
|
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Debug {
|
||||||
|
if err := addLDAPDescriptions(packet); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ber.PrintPacket(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
resultCode, resultDescription := getLDAPResultCode(packet)
|
||||||
|
if resultCode != 0 {
|
||||||
|
return result, NewError(resultCode, errors.New(resultDescription))
|
||||||
|
}
|
||||||
|
if len(packet.Children) == 3 {
|
||||||
|
for _, child := range packet.Children[2].Children {
|
||||||
|
result.Controls = append(result.Controls, DecodeControl(child))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foundSearchResultDone = true
|
||||||
|
case 19:
|
||||||
|
result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.Debug.Printf("%d: returning", msgCtx.id)
|
||||||
|
return result, nil
|
||||||
|
}
|
18
src/vendor/vendor.json
vendored
18
src/vendor/vendor.json
vendored
@ -300,12 +300,6 @@
|
|||||||
"revision": "3fb7a0e792edd47bf0cf1e919dfc14e2be412e15",
|
"revision": "3fb7a0e792edd47bf0cf1e919dfc14e2be412e15",
|
||||||
"revisionTime": "2016-09-07T16:20:43Z"
|
"revisionTime": "2016-09-07T16:20:43Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"checksumSHA1": "JPm8dTnTbiYhi1kdn4JjHc4AZd0=",
|
|
||||||
"path": "github.com/mqu/openldap",
|
|
||||||
"revision": "91bfc2150f6c4991335de476c800caf26a990324",
|
|
||||||
"revisionTime": "2013-12-25T09:52:30Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
|
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
|
||||||
"path": "golang.org/x/crypto/pbkdf2",
|
"path": "golang.org/x/crypto/pbkdf2",
|
||||||
@ -321,6 +315,18 @@
|
|||||||
{
|
{
|
||||||
"path": "golang.org/x/sys/unix",
|
"path": "golang.org/x/sys/unix",
|
||||||
"revision": ""
|
"revision": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "wSu8owMAP7GixsYoSZ4CmKUVhnU=",
|
||||||
|
"path": "gopkg.in/asn1-ber.v1",
|
||||||
|
"revision": "4e86f4367175e39f69d9358a5f17b4dda270378d",
|
||||||
|
"revisionTime": "2015-09-24T05:17:56Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "itYnRitfdzJjy2mZlvJ+hCJZvtY=",
|
||||||
|
"path": "gopkg.in/ldap.v2",
|
||||||
|
"revision": "8168ee085ee43257585e50c6441aadf54ecb2c9f",
|
||||||
|
"revisionTime": "2016-12-01T20:47:33Z"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rootPath": "github.com/vmware/harbor/src"
|
"rootPath": "github.com/vmware/harbor/src"
|
||||||
|
Loading…
Reference in New Issue
Block a user