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:
Ricardo Katz 2016-12-30 06:03:30 -02:00 committed by yhua123
parent 226ab5a5f7
commit 160e22f0fe
44 changed files with 4365 additions and 1683 deletions

View File

@ -16,6 +16,7 @@ LDAP_BASE_DN=$ldap_basedn
LDAP_FILTER=$ldap_filter
LDAP_UID=$ldap_uid
LDAP_SCOPE=$ldap_scope
LDAP_CONNECT_TIMEOUT=$ldap_connect_timeout
UI_SECRET=$ui_secret
SECRET_KEY=$secret_key
SELF_REGISTRATION=$self_registration

View File

@ -21,7 +21,7 @@ COPY src/favicon.ico /go/bin/favicon.ico
COPY make/jsminify.sh /tmp/jsminify.sh
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 \
&& 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/ \

View File

@ -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
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.
db_password = root123

View File

@ -101,6 +101,7 @@ else:
ldap_filter = ""
ldap_uid = rcp.get("configuration", "ldap_uid")
ldap_scope = rcp.get("configuration", "ldap_scope")
ldap_connect_timeout = rcp.get("configuration", "ldap_connect_timeout")
db_password = rcp.get("configuration", "db_password")
self_registration = rcp.get("configuration", "self_registration")
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_uid=ldap_uid,
ldap_scope=ldap_scope,
ldap_connect_timeout=ldap_connect_timeout,
self_registration=self_registration,
use_compressed_js=use_compressed_js,
ui_secret=ui_secret,

View File

@ -18,7 +18,7 @@ COPY ./src/favicon.ico /harbor/favicon.ico
COPY ./make/jsminify.sh /tmp/jsminify.sh
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 \
&& timestamp=`date '+%s'` \
&& /tmp/jsminify.sh /harbor/views/sections/script-include.htm /harbor/static/resources/js/harbor.app.min.$timestamp.js /harbor/ \

View File

@ -18,7 +18,11 @@ package ldap
import (
"errors"
"fmt"
"strconv"
"strings"
"time"
"crypto/tls"
"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/config"
"github.com/mqu/openldap"
goldap "gopkg.in/ldap.v2"
)
// Auth implements Authenticator interface to authenticate against LDAP
@ -35,6 +39,59 @@ type Auth struct{}
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,
// 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.
@ -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)
}
}
ldapURL := config.LDAP().URL
if ldapURL == "" {
return nil, errors.New("can not get any available LDAP_URL")
}
log.Debug("ldapURL:", ldapURL)
ldap, err := openldap.Initialize(ldapURL)
ldap, err := Connect()
if err != nil {
return nil, err
}
ldap.SetOption(openldap.LDAP_OPT_PROTOCOL_VERSION, openldap.LDAP_VERSION3)
ldapBaseDn := config.LDAP().BaseDn
if ldapBaseDn == "" {
@ -86,27 +138,42 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
ldapScope := config.LDAP().Scope
var scope int
if ldapScope == "1" {
scope = openldap.LDAP_SCOPE_BASE
scope = goldap.ScopeBaseObject
} else if ldapScope == "2" {
scope = openldap.LDAP_SCOPE_ONELEVEL
scope = goldap.ScopeSingleLevel
} else {
scope = openldap.LDAP_SCOPE_SUBTREE
scope = goldap.ScopeWholeSubtree
}
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 {
return nil, err
}
if len(result.Entries()) == 0 {
if len(result.Entries) == 0 {
log.Warningf("Not found an entry.")
return nil, nil
} else if len(result.Entries()) != 1 {
} else if len(result.Entries) != 1 {
log.Warningf("Found more than one entry.")
return nil, nil
}
en := result.Entries()[0]
bindDN := en.Dn()
log.Debug("found entry:", en)
entry := result.Entries[0]
bindDN := entry.DN
log.Debug("found entry:", bindDN)
err = ldap.Bind(bindDN, m.Password)
if err != nil {
log.Debug("Bind user error", err)
@ -115,9 +182,10 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
defer ldap.Close()
u := models.User{}
for _, attr := range en.Attributes() {
val := attr.Values()[0]
switch attr.Name() {
for _, attr := range entry.Attributes {
val := attr.Values[0]
switch attr.Name {
case "uid":
u.Realname = val
case "cn":

View File

@ -26,13 +26,14 @@ import (
// LDAPSetting wraps the setting of an LDAP server
type LDAPSetting struct {
URL string
BaseDn string
SearchDn string
SearchPwd string
UID string
Filter string
Scope string
URL string
BaseDn string
SearchDn string
SearchPwd string
UID string
Filter string
Scope string
ConnectTimeout string
}
type uiParser struct{}
@ -42,13 +43,14 @@ func (up *uiParser) Parse(raw map[string]string, config map[string]interface{})
mode := raw["AUTH_MODE"]
if mode == "ldap_auth" {
setting := LDAPSetting{
URL: raw["LDAP_URL"],
BaseDn: raw["LDAP_BASE_DN"],
SearchDn: raw["LDAP_SEARCH_DN"],
SearchPwd: raw["LDAP_SEARCH_PWD"],
UID: raw["LDAP_UID"],
Filter: raw["LDAP_FILTER"],
Scope: raw["LDAP_SCOPE"],
URL: raw["LDAP_URL"],
BaseDn: raw["LDAP_BASE_DN"],
SearchDn: raw["LDAP_SEARCH_DN"],
SearchPwd: raw["LDAP_SEARCH_PWD"],
UID: raw["LDAP_UID"],
Filter: raw["LDAP_FILTER"],
Scope: raw["LDAP_SCOPE"],
ConnectTimeout: raw["LDAP_CONNECT_TIMEOUT"],
}
config["ldap"] = setting
}
@ -83,7 +85,7 @@ func (up *uiParser) Parse(raw map[string]string, config map[string]interface{})
var uiConfig *commonConfig.Config
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{
Config: make(map[string]interface{}),
Loader: &commonConfig.EnvConfigLoader{Keys: uiKeys},

View File

@ -29,6 +29,7 @@ var (
"cn",
"uid",
"2",
"5",
}
tokenExp = "3"
tokenExpRes = 3
@ -52,6 +53,7 @@ func TestMain(m *testing.M) {
os.Setenv("LDAP_UID", ldap.UID)
os.Setenv("LDAP_SCOPE", ldap.Scope)
os.Setenv("LDAP_FILTER", ldap.Filter)
os.Setenv("LDAP_CONNECT_TIMEOUT", ldap.ConnectTimeout)
os.Setenv("TOKEN_EXPIRATION", tokenExp)
os.Setenv("HARBOR_ADMIN_PASSWORD", adminPassword)
os.Setenv("EXT_REG_URL", externalRegURL)
@ -76,6 +78,7 @@ func TestMain(m *testing.M) {
os.Unsetenv("LDAP_UID")
os.Unsetenv("LDAP_SCOPE")
os.Unsetenv("LDAP_FILTER")
os.Unsetenv("LDAP_CONNECT_TIMEOUT")
os.Unsetenv("TOKEN_EXPIRATION")
os.Unsetenv("HARBOR_ADMIN_PASSWORD")
os.Unsetenv("EXT_REG_URL")

View File

@ -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.

View File

@ -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.

View File

@ -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
}

View File

@ -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
)

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

View File

@ -300,12 +300,6 @@
"revision": "3fb7a0e792edd47bf0cf1e919dfc14e2be412e15",
"revisionTime": "2016-09-07T16:20:43Z"
},
{
"checksumSHA1": "JPm8dTnTbiYhi1kdn4JjHc4AZd0=",
"path": "github.com/mqu/openldap",
"revision": "91bfc2150f6c4991335de476c800caf26a990324",
"revisionTime": "2013-12-25T09:52:30Z"
},
{
"checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=",
"path": "golang.org/x/crypto/pbkdf2",
@ -321,6 +315,18 @@
{
"path": "golang.org/x/sys/unix",
"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"