blank state

This commit is contained in:
Xavier Henner 2019-07-21 19:23:13 +02:00
parent 2c289f8125
commit 0e72c3a242
48 changed files with 14 additions and 5874 deletions

3
.gitignore vendored
View File

@ -1,4 +1,3 @@
test.sh test.sh
openvpn-dm-mgt-server openvpn-mgt
openvpn-dm-mgt-server.conf openvpn-dm-mgt-server.conf
iproute/iproute

34
dhcp.go
View File

@ -1,34 +0,0 @@
package main
import (
"errors"
)
func (s *OpenVpnMgt) isFree(ip string) bool {
for _, remote := range s.clients {
for _, c := range remote {
if c.PrivIP == ip {
return false
}
}
}
return true
}
// internal DHCP
func (s *OpenVpnMgt) getIP(c *vpnSession) (string, error) {
s.m.Lock()
defer s.m.Unlock()
ipmax := nextIP(s.ldap[c.Profile].ipMax).String()
sip := s.ldap[c.Profile].ipMin.String()
for ip := s.ldap[c.Profile].ipMin; sip != ipmax; ip = nextIP(ip) {
sip = ip.String()
if s.isFree(sip) {
return sip, nil
}
}
return "", errors.New("no more IP")
}

View File

@ -119,11 +119,9 @@ func (h *HttpServer) ajaxHandler(w http.ResponseWriter, r *http.Request) {
} }
webuser := strings.Replace(r.TLS.PeerCertificates[0].Subject.CommonName, " ", "", -1) webuser := strings.Replace(r.TLS.PeerCertificates[0].Subject.CommonName, " ", "", -1)
_, _, _, profilePath := h.ovpn.AuthLoop(h.minProfile, webuser, "", false)
if !inArray(h.neededProfiles, profilePath) { //TODO security
http.Error(w, fmt.Sprintf("You need on of %s profile and you have %s", h.neededProfiles, profilePath), 403)
return
}
log.Printf("%s is connected via the web interfaces\n", webuser) log.Printf("%s is connected via the web interfaces\n", webuser)
req, err := parseJsonQuery(r) req, err := parseJsonQuery(r)
@ -135,17 +133,7 @@ func (h *HttpServer) ajaxHandler(w http.ResponseWriter, r *http.Request) {
switch req.Action { switch req.Action {
case "stats": case "stats":
jsonStr, err := json.Marshal(h.ovpn.Stats())
if err != nil {
http.Error(w, fmt.Sprintf("%s", err), 500)
}
fmt.Fprintf(w, "%s", jsonStr)
case "kill": case "kill":
if err := h.ovpn.Kill(req.Params.Session, req.Params.Id, webuser); err != nil {
http.Error(w, fmt.Sprintf("%s", err), 500)
}
fmt.Fprintf(w, "{}")
default: default:
http.Error(w, "Invalid request", 500) http.Error(w, "Invalid request", 500)
} }

250
ldap.go
View File

@ -1,250 +0,0 @@
package main
import (
"crypto/tls"
"errors"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/pyke369/golang-support/rcache"
"gopkg.in/ldap.v2"
)
type ldapConfig struct {
servers []string
baseDN string
bindCn string
bindPw string
searchFilter string
attributes []string
validGroups []string
mfaType string
certAuth string
ipMin net.IP
ipMax net.IP
upgradeFrom []string
routes []string
}
func (l *ldapConfig) addIPRange(s string) error {
ips := strings.Split(s, "-")
if len(ips) != 2 {
return errors.New("invalid IPs")
}
for k, v := range []*net.IP{&(l.ipMin), &(l.ipMax)} {
if ip := net.ParseIP(strings.Trim(ips[k], " ")); ip == nil {
return errors.New(fmt.Sprintf("invalid IP '%s'", ips[k]))
} else {
*v = ip
}
}
return nil
}
// auth loop. Try all auth profiles from startProfile
// return the last possible profile and the mail if we found a mail like login
func (s *OpenVpnMgt) AuthLoop(startProfile, user, pass string, overridePwdCheck bool) (string, string, string, []string) {
login := []string{user}
profile := startProfile
mail := ""
otpSalt := ""
profilePath := []string{}
re := rcache.Get("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$")
for {
// the first login that match the mail regexp is the mail address
if mail == "" && re.MatchString(login[0]) {
mail = login[0]
}
n := profile
for k, ldap := range s.ldap {
// check if the current profile is an upgrade of the previous one
// check the startup profile first
ok := (profile == "") && (len(ldap.upgradeFrom) == 0)
for _, possible := range ldap.upgradeFrom {
if possible == profile {
ok = true
}
}
if !ok {
continue
}
err, userOk, passOk, attributes := ldap.Auth(login, pass)
if otpSalt == "" && len(attributes) > 1 && len(attributes[1]) > 0 {
otpSalt = attributes[1][0]
}
// if there is an error, try the other configurations
if err != nil {
log.Printf("user %s not validated as %s\n", user, k)
continue
}
// we did find a valid User
if userOk {
// the login for the new auth level is given by the current one
login = attributes[0]
if passOk && profile != "" {
// it's at least the second auth level, and we have a valid
// password on 2 different auth system. It's a dupplicate
// password, let's log it
log.Printf("User %s has a dupplicate password\n", user)
}
// we have either a positive auth ok a previous valid one
if passOk || profile != "" || overridePwdCheck {
profile = k
profilePath = append(profilePath, profile)
break
}
}
}
// no profile update this turn, no need to continue
if n == profile {
break
}
}
return profile, mail, otpSalt, profilePath
}
// override the real DialTLS function
func myDialTLS(network, addr string, config *tls.Config) (*ldap.Conn, error) {
dc, err := net.DialTimeout(network, addr, 3*time.Second)
if err != nil {
return nil, ldap.NewError(ldap.ErrorNetwork, err)
}
c := tls.Client(dc, config)
if err = c.Handshake(); err != nil {
// Handshake error, close the established connection before we return an error
dc.Close()
return nil, ldap.NewError(ldap.ErrorNetwork, err)
}
conn := ldap.NewConn(c, true)
conn.Start()
return conn, nil
}
func (conf *ldapConfig) Auth(logins []string, pass string) (e error, userOk, passOk bool, attributes [][]string) {
// special case. This configuration is a filter on the previous one
if len(conf.servers) == 0 && len(conf.validGroups) > 0 {
if inArray(logins, conf.validGroups) {
return nil, true, false, [][]string{logins}
}
}
// no server ldap or multiple login should not happen here
if len(logins) != 1 || len(conf.servers) == 0 {
return nil, false, false, nil
}
attributes = [][]string{logins}
for _, s := range conf.servers {
// we force ldaps because we can
l, err := myDialTLS("tcp", s+":636", &tls.Config{ServerName: s})
if err != nil {
log.Println(err)
continue
}
defer l.Close()
// First bind with a read only user
if err = l.Bind(conf.bindCn, conf.bindPw); err != nil {
log.Println(err)
return err, false, false, nil
}
search := append(conf.attributes, "dn")
// search the user
searchRequest := ldap.NewSearchRequest(
conf.baseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf(conf.searchFilter, logins[0]),
search,
nil,
)
sr, err := l.Search(searchRequest)
if err != nil {
log.Println(err)
return err, false, false, nil
}
if len(sr.Entries) != 1 {
return errors.New("User does not exist or too many entries returned"), false, false, nil
}
// check the attributes requested in the search
// a valid account must be part of the correct group (per instance)
ret := [][]string{}
for _, needed := range conf.attributes {
ok := false
for _, attribute := range sr.Entries[0].Attributes {
if (*attribute).Name == needed {
ret = append(ret, attribute.Values)
ok = true
}
}
if !ok {
ret = append(ret, []string{})
}
}
// user must have both the first and second attributes
if len(ret[0]) == 0 {
log.Printf("User %s has no %s attribute", logins[0], conf.attributes[0])
return nil, false, false, nil
}
if len(ret[1]) == 0 {
log.Printf("User %s has no %s attribute", logins[0], conf.attributes[1])
return nil, false, false, nil
}
// check if the first attribute valus are in the validGroups list
if len(conf.validGroups) > 0 && !inArray(conf.validGroups, ret[0]) {
return nil, false, false, nil
}
// if there is no validGroups check, pass the first attribute values to the
// next level
if len(conf.validGroups) == 0 {
attributes = [][]string{ret[0]}
} else {
attributes = [][]string{ret[1]}
}
if len(ret) > 2 {
attributes = append(attributes, ret[2:]...)
}
log.Printf("User %s has a valid account on %s", logins[0], s)
userdn := sr.Entries[0].DN
// if the password is empty, stop here
if pass == "" {
return nil, true, false, attributes
}
// if there is an error, it's because the password is invalid
if err = l.Bind(userdn, pass); err != nil {
return nil, true, false, attributes
}
// everything is fine,
log.Printf("User %s has a valid password on %s", logins[0], s)
return nil, true, true, attributes
}
// if we are here, no server is responding, rejectif auth
log.Println("can't join any ldap server")
return
}

152
logs.go
View File

@ -1,152 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"net/smtp"
"text/template"
"time"
)
func (c *vpnSession) LogPrintln(v ...interface{}) {
log.Println(c.Login, c.IP, v)
}
func (s *OpenVpnMgt) Log(c *vpnSession) error {
if s.vpnlogUrl != "" {
if err := c.getASInfos(s.vpnlogUrl); err != nil {
log.Println(err)
}
}
c.Time = time.Now().Round(time.Second)
jsonStr, err := json.Marshal(c)
if err != nil {
return err
}
log.Println(string(jsonStr))
if err := s.SendMail(c); err != nil {
log.Println(err)
}
return nil
}
func (c *vpnSession) getASInfos(vpnlogUrl string) error {
jsonStr, err := json.Marshal(c)
if err != nil {
return err
}
req, err := http.NewRequest("POST", vpnlogUrl, bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", "application/json")
timeout := time.Duration(3 * time.Second)
client := http.Client{
Timeout: timeout,
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
err = json.Unmarshal(body, c)
if err != nil {
return err
}
return nil
}
func (s *OpenVpnMgt) MailTemplate(c *vpnSession) error {
var buf1 bytes.Buffer
var buf2 bytes.Buffer
tmpl, err := template.New("pwnTemplate").Parse(s.pwnTemplate)
if err != nil {
return err
}
if err := tmpl.Execute(&buf1, c); err != nil {
return err
}
c.pwnMail = buf1.String()
tmpl, err = template.New("newAsTemplate").Parse(s.newAsTemplate)
if err != nil {
return err
}
if err := tmpl.Execute(&buf2, c); err != nil {
return err
}
c.newAsMail = buf2.String()
return nil
}
func (s *OpenVpnMgt) SendMail(c *vpnSession) error {
if c.Mail == "" {
return nil
}
if (s.newAsTemplate == "" || !c.NewAS) &&
(s.pwnTemplate == "" || !c.PwnedPasswd) {
// can not send mail without template or cause
return nil
}
// needed for the templating
c.MailFrom = s.MailFrom
c.CcPwnPassword = s.CcPwnPassword
// complete the templates
if err := s.MailTemplate(c); err != nil {
return err
}
mail, err := smtp.Dial(s.mailRelay)
if err != nil {
return err
}
defer mail.Close()
if c.PwnedPasswd {
mail.Mail(s.MailFrom)
mail.Rcpt(c.Mail)
if c.TooMuchPwn && s.CcPwnPassword != "" {
mail.Rcpt(s.CcPwnPassword)
}
wc, err := mail.Data()
if err != nil {
return nil
}
defer wc.Close()
buf := bytes.NewBufferString(c.pwnMail)
if _, err = buf.WriteTo(wc); err != nil {
return err
}
wc.Close()
}
if c.NewAS {
mail.Mail(s.MailFrom)
mail.Rcpt(c.Mail)
wc, err := mail.Data()
if err != nil {
return nil
}
defer wc.Close()
buf := bytes.NewBufferString(c.newAsMail)
if _, err = buf.WriteTo(wc); err != nil {
return err
}
wc.Close()
}
return nil
}

42
main.go
View File

@ -6,7 +6,6 @@ import (
"log/syslog" "log/syslog"
"math/rand" "math/rand"
"os" "os"
"strings"
"time" "time"
"github.com/pyke369/golang-support/uconfig" "github.com/pyke369/golang-support/uconfig"
@ -31,19 +30,6 @@ func main() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
server := NewVPNServer(config.GetString("config.openvpnPort", "127.0.0.01:5000")) server := NewVPNServer(config.GetString("config.openvpnPort", "127.0.0.01:5000"))
server.vpnlogUrl = config.GetString("config.vpnLogUrl", "")
server.mailRelay = config.GetString("config.mailRelay", "")
server.MailFrom = config.GetString("config.mailFrom", "")
server.CcPwnPassword = config.GetString("config.ccPwnPassword", "")
server.pwnTemplate = config.GetString("config.pwnTemplate", "")
server.newAsTemplate = config.GetString("config.newAsTemplate", "")
server.cacheDir = config.GetString("config.cacheDir", "")
server.authCa = config.GetString("config.authCa", "")
server.otpMasterSecrets = parseConfigArray(config, "config.masterSecrets")
if len(server.otpMasterSecrets) == 0 {
server.otpMasterSecrets = append(server.otpMasterSecrets, "*******************")
}
server.syslog = false server.syslog = false
if *logToSyslog { if *logToSyslog {
@ -61,34 +47,6 @@ func main() {
server.debug = true server.debug = true
} }
for _, profile := range config.GetPaths("config.profiles") {
profileName := strings.Split(profile, ".")[2]
ldapConf := ldapConfig{
servers: parseConfigArray(config, profile+".servers"),
baseDN: config.GetString(profile+".baseDN", ""),
bindCn: config.GetString(profile+".bindCn", ""),
bindPw: config.GetString(profile+".bindPw", ""),
searchFilter: config.GetString(profile+".searchFilter", ""),
attributes: parseConfigArray(config, profile+".attributes"),
validGroups: parseConfigArray(config, profile+".validGroups"),
routes: parseConfigArray(config, profile+".routes"),
mfaType: config.GetString(profile+".mfa", ""),
certAuth: config.GetString(profile+".cert", "optionnal"),
upgradeFrom: parseConfigArray(config, profile+".upgradeFrom"),
}
if err := ldapConf.addIPRange(config.GetString(profile+".IPRange", "")); err != nil {
log.Println(err)
os.Exit(1)
}
if len(ldapConf.servers) > 0 && len(ldapConf.attributes) < 2 {
log.Println("valud ldap configuration must have 2 attributes")
os.Exit(1)
}
server.ldap[profileName] = ldapConf
}
// time to start the listeners // time to start the listeners
go server.Run() go server.Run()
NewHTTPServer( NewHTTPServer(

57
otp.go
View File

@ -1,57 +0,0 @@
package main
import (
"time"
)
func (s *OpenVpnMgt) GenerateOTP(user string) ([]string, error) {
return s.GenerateOTPGeneric(user, 30, "sha1", 10, 6)
}
// alternative OTP generator, not used at the moment
// func (s *OpenVpnMgt) GenerateSlackOTP(user string) ([]string, error) {
// return s.GenerateOTPGeneric(user, 60, "sha256", 30, 8)
// }
func (s *OpenVpnMgt) TokenPassword(c *vpnSession) (bool, string) {
now := time.Now().Unix()
if len(c.password) > 40 {
salt := c.password[:4]
for i := 0; i < 3; i++ {
test := encode64(ComputeHmac256(c.baseHash(salt, now/30-int64(i)), s.otpMasterSecrets[0]))
if salt+test == c.password {
return true, c.password
}
}
}
salt := NewSalt()
return false, salt + encode64(ComputeHmac256(c.baseHash(salt, now/30), s.otpMasterSecrets[0]))
}
func (s *OpenVpnMgt) GenerateOTPGeneric(user string, period int, algo string, secretLen int, digits int) ([]string, error) {
codes := []string{}
now := time.Now()
secret := encodeSecret(ComputeHmac256(user, s.otpMasterSecrets[0])[:secretLen])
code, err := GenericTotpCode(secret, now, algo, digits, period)
if err != nil {
return codes, err
}
// the first code is the generic one
codes = append(codes, code)
for i := 1; i < 4; i++ {
code, _ = GenericTotpCode(secret, now.Add(-1*time.Second*time.Duration(period*i)), algo, digits, period)
codes = append(codes, code)
}
for j := 1; j < len(s.otpMasterSecrets); j++ {
secret = encodeSecret(ComputeHmac256(user, s.otpMasterSecrets[j])[:secretLen])
for i := 0; i < 3; i++ {
code, _ = GenericTotpCode(secret, now.Add(-1*time.Second*time.Duration(period*i)), algo, digits, period)
codes = append(codes, code)
}
}
return codes, nil
}

View File

@ -1,43 +0,0 @@
# external files
tls-auth /etc/openvpn/tlsauth.key
dh /etc/openvpn/dh2048.pem
ca /usr/local/share/ca-certificates/Dailymotion.crt
cert /etc/ssl/certs/vpn.dailymotion.com-cert.pem
key /etc/ssl/private/vpn.dailymotion.com-key.pem
# local parameters
port 41690
tls-server
mode server
ifconfig 192.168.200.1 255.255.248.0
topology subnet
dev vpnadmin
dev-type tun
#local 188.65.121.190
# security
user openvpn
group openvpn
reneg-sec 43200
management 127.0.0.1 4000
management-client
management-client-auth
auth-user-pass-optional
client-cert-not-required
username-as-common-name
# push
push "dhcp-option DNS 10.190.32.2"
push "dhcp-option DNS 10.190.32.20"
push "route-gateway 192.168.200.1"
push "topology subnet"
# crypto
cipher aes-128-cbc
keepalive 10 120
persist-key
ifconfig-nowarn
persist-remote-ip
persist-tun
verb 0

View File

@ -1,5 +0,0 @@
sudo: false
language: go
go:
- 1.11.x
- tip

View File

@ -1,19 +0,0 @@
Copyright (C) 2018 by Matt Evans
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.

View File

@ -1,62 +0,0 @@
# pwned-passwords
[![GoDoc](https://godoc.org/github.com/mattevans/pwned-passwords?status.svg)](https://godoc.org/github.com/mattevans/pwned-passwords)
[![Build Status](https://travis-ci.org/mattevans/pwned-passwords.svg?branch=master)](https://travis-ci.org/mattevans/pwned-passwords)
[![Go Report Card](https://goreportcard.com/badge/github.com/mattevans/pwned-passwords)](https://goreportcard.com/report/github.com/mattevans/pwned-passwords)
[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/mattevans/pwned-passwords/blob/master/LICENSE)
A simple [Go](http://golang.org) client library for checking compromised passwords against [HIBP Pwned Passwords](https://haveibeenpwned.com/Passwords).
Upon request, results will be cached (in-memory), keyed by hash. With a two hour expiry window, subsequent requests will use cached data or fetch fresh data accordingly.
Installation
-----------------
`go get -u github.com/mattevans/pwned-passwords`
Usage
-----------------
```go
package main
import (
"fmt"
hibp "github.com/mattevans/pwned-passwords"
"os"
)
func main() {
// Init a client.
client := hibp.NewClient()
// Check to see if your given string is compromised.
pwned, err := client.Pwned.Compromised("string to check")
if err != nil {
fmt.Println("Pwned failed")
os.Exit(1)
}
if pwned {
// Oh dear!
// You should avoid using that password
} else {
// Woo!
// All clear!
}
}
```
**Expire in-memory cache**
```go
client.Cache.Expire(HASHED_VALUE)
```
```go
client.Cache.ExpireAll()
```
Contributing
-----------------
If you've found a bug or would like to contribute, please create an issue here on GitHub, or better yet fork the project and submit a pull request!

View File

@ -1,65 +0,0 @@
package hibp
import "time"
// cache holds our our cached hash/compromised pairs results.
var cache map[string]*PwnedStore
// cacheTTL stores the time to live of our cache (2 hours).
var cacheTTL = 2 * time.Hour
// CacheService handles in-memory caching of our hash/compromised pairs.
type CacheService service
// Get will return our stored in-memory hash/compromised pairs, if we have them.
func (s *CacheService) Get(hash string) *PwnedStore {
// Is our cache expired?
if s.IsExpired(hash) {
return nil
}
// Use stored results.
return cache[hash]
}
// Store will save our hash/compromised pairs to a PwnedStore.
func (s *CacheService) Store(hash string, compromised bool) {
// No cache? Initialize it.
if cache == nil {
cache = map[string]*PwnedStore{}
}
// Store
tn := time.Now()
cache[hash] = &PwnedStore{
Hash: hash,
Compromised: compromised,
UpdatedAt: &tn,
}
}
// IsExpired checks if we have cached hash and that it isn't expired.
func (s *CacheService) IsExpired(hash string) bool {
// No cache? bail.
if cache[hash] == nil {
return true
}
// Expired cache? bail.
lastUpdated := cache[hash].UpdatedAt
if lastUpdated != nil && lastUpdated.Add(cacheTTL).Before(time.Now()) {
return true
}
return false
}
// Expire will expire the cache for a given hash.
func (s *CacheService) Expire(hash string) {
cache[hash] = nil
}
// ExpireAll will expire all cache.
func (s *CacheService) ExpireAll() {
cache = nil
}

View File

@ -1,115 +0,0 @@
package hibp
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
const (
packageVersion = "0.0.1"
backendURL = "https://api.pwnedpasswords.com"
userAgent = "pwned-passwords-golang/" + packageVersion
)
// Client holds a connection to the HIBP API.
type Client struct {
client *http.Client
AppID string
UserAgent string
BackendURL *url.URL
// Services used for communicating with the API.
Pwned *PwnedService
Cache *CacheService
}
type service struct {
client *Client
}
// NewClient creates a new Client with the appropriate connection details and
// services used for communicating with the API.
func NewClient() *Client {
// Init new http.Client.
httpClient := http.DefaultClient
// Parse BE URL.
baseURL, _ := url.Parse(backendURL)
c := &Client{
client: httpClient,
BackendURL: baseURL,
UserAgent: userAgent,
}
c.Pwned = &PwnedService{client: c}
c.Cache = &CacheService{client: c}
return c
}
// NewRequest creates an API request. A relative URL can be provided in urlPath,
// which will be resolved to the BackendURL of the Client.
func (c *Client) NewRequest(method, urlPath string, body interface{}) (*http.Request, error) {
// Parse our URL.
rel, err := url.Parse(urlPath)
if err != nil {
return nil, err
}
// Resolve to absolute URI.
u := c.BackendURL.ResolveReference(rel)
buf := new(bytes.Buffer)
if body != nil {
err = json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
// Create the request.
req, err := http.NewRequest(method, u.String(), buf)
if err != nil {
return nil, err
}
// Add our packages UA.
req.Header.Add("User-Agent", c.UserAgent)
return req, nil
}
// Do sends an API request and returns the API response.
func (c *Client) Do(req *http.Request) ([]string, error) {
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer func() {
if rerr := resp.Body.Close(); err == nil {
err = rerr
}
}()
// Error if anything else but 200.
// The API should always return a 200 (unless something is wrong) as per
// https://haveibeenpwned.com/API/v2#SearchingPwnedPasswordsByRange
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("Unexpected API response status: %v", resp.StatusCode)
}
// Parse our resp.Body.
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// Response is returned as new-line'd string, split and return.
return strings.Split(string(body), "\r\n"), err
}

View File

@ -1,89 +0,0 @@
package hibp
import (
"crypto/sha1"
"encoding/hex"
"errors"
"fmt"
"strconv"
"strings"
"time"
)
// PwnedService handles retrieving pwned hashes from in-memory cache or
// by fetching fresh results.
type PwnedService service
// PwnedStore holds our pwned password hashes and compromised status.
type PwnedStore struct {
Hash string `json:"hash"`
Compromised bool `json:"compromised"`
UpdatedAt *time.Time `json:"updated_at"`
}
// Compromised will build and execute a request to HIBP to check to see
// if the passed value is compromised or not.
func (s *PwnedService) Compromised(value string) (bool, error) {
var err error
// Our value being checked is empty, we don't want that.
if value == "" {
return false, errors.New("Value for compromised check cannot be empty")
}
// SHA-1 hash our input value.
hashedStr := _hashString(value)
// If we have cached results, use them.
cache := s.client.Cache.Get(hashedStr)
if cache != nil {
hashedStr = cache.Hash
return cache.Compromised, err
}
// Pop our prefix and suffix.
prefix := strings.ToUpper(hashedStr[:5])
suffix := strings.ToUpper(hashedStr[5:])
// Build request.
request, err := s.client.NewRequest("GET", fmt.Sprintf("range/%s", prefix), nil)
if err != nil {
return false, err
}
// Make request.
response, err := s.client.Do(request)
if err != nil {
return false, err
}
// Range our response ([]string).
for _, target := range response {
// If our target, minus the compromised count matches our suffix.
if string(target[:35]) == suffix {
_, err = strconv.ParseInt(target[36:], 10, 64)
if err != nil {
return false, err
}
// Store in cache as compromised.
s.client.Cache.Store(hashedStr, true)
// Return.
return true, err
}
}
// Store in cache as non-compromised.
s.client.Cache.Store(hashedStr, false)
// Return.
return false, err
}
// _hashString will return a sha1 hash of the given value.
func _hashString(value string) string {
alg := sha1.New()
alg.Write([]byte(value))
return strings.ToUpper(hex.EncodeToString(alg.Sum(nil)))
}

View File

@ -1,36 +0,0 @@
language: go
matrix:
include:
- go: 1.2.x
env: GOOS=linux GOARCH=amd64
- go: 1.2.x
env: GOOS=linux GOARCH=386
- go: 1.2.x
env: GOOS=windows GOARCH=amd64
- go: 1.2.x
env: GOOS=windows GOARCH=386
- go: 1.3.x
- go: 1.4.x
- go: 1.5.x
- go: 1.6.x
- go: 1.7.x
- go: 1.8.x
- go: 1.9.x
- go: 1.10.x
- go: 1.11.x
env: GOOS=linux GOARCH=amd64
- go: 1.11.x
env: GOOS=linux GOARCH=386
- go: 1.11.x
env: GOOS=windows GOARCH=amd64
- go: 1.11.x
env: GOOS=windows GOARCH=386
- go: tip
go_import_path: gopkg.in/asn-ber.v1
install:
- go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v
- go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v
- go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover
- go build -v ./...
script:
- go test -v -cover ./... || go test -v ./...

22
vendor/gopkg.in/asn1-ber.v1/LICENSE generated vendored
View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
Portions copyright (c) 2015-2016 go-asn1-ber Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,24 +0,0 @@
[![GoDoc](https://godoc.org/gopkg.in/asn1-ber.v1?status.svg)](https://godoc.org/gopkg.in/asn1-ber.v1) [![Build Status](https://travis-ci.org/go-asn1-ber/asn1-ber.svg)](https://travis-ci.org/go-asn1-ber/asn1-ber)
ASN1 BER Encoding / Decoding Library for the GO programming language.
---------------------------------------------------------------------
Required libraries:
None
Working:
Very basic encoding / decoding needed for LDAP protocol
Tests Implemented:
A few
TODO:
Fix all encoding / decoding to conform to ASN1 BER spec
Implement Tests / Benchmarks
---
The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
The design is licensed under the Creative Commons 3.0 Attributions license.
Read this article for more details: http://blog.golang.org/gopher

512
vendor/gopkg.in/asn1-ber.v1/ber.go generated vendored
View File

@ -1,512 +0,0 @@
package ber
import (
"bytes"
"errors"
"fmt"
"io"
"math"
"os"
"reflect"
)
// MaxPacketLengthBytes specifies the maximum allowed packet size when calling ReadPacket or DecodePacket. Set to 0 for
// no limit.
var MaxPacketLengthBytes int64 = math.MaxInt32
type Packet struct {
Identifier
Value interface{}
ByteValue []byte
Data *bytes.Buffer
Children []*Packet
Description string
}
type Identifier struct {
ClassType Class
TagType Type
Tag Tag
}
type Tag uint64
const (
TagEOC Tag = 0x00
TagBoolean Tag = 0x01
TagInteger Tag = 0x02
TagBitString Tag = 0x03
TagOctetString Tag = 0x04
TagNULL Tag = 0x05
TagObjectIdentifier Tag = 0x06
TagObjectDescriptor Tag = 0x07
TagExternal Tag = 0x08
TagRealFloat Tag = 0x09
TagEnumerated Tag = 0x0a
TagEmbeddedPDV Tag = 0x0b
TagUTF8String Tag = 0x0c
TagRelativeOID Tag = 0x0d
TagSequence Tag = 0x10
TagSet Tag = 0x11
TagNumericString Tag = 0x12
TagPrintableString Tag = 0x13
TagT61String Tag = 0x14
TagVideotexString Tag = 0x15
TagIA5String Tag = 0x16
TagUTCTime Tag = 0x17
TagGeneralizedTime Tag = 0x18
TagGraphicString Tag = 0x19
TagVisibleString Tag = 0x1a
TagGeneralString Tag = 0x1b
TagUniversalString Tag = 0x1c
TagCharacterString Tag = 0x1d
TagBMPString Tag = 0x1e
TagBitmask Tag = 0x1f // xxx11111b
// HighTag indicates the start of a high-tag byte sequence
HighTag Tag = 0x1f // xxx11111b
// HighTagContinueBitmask indicates the high-tag byte sequence should continue
HighTagContinueBitmask Tag = 0x80 // 10000000b
// HighTagValueBitmask obtains the tag value from a high-tag byte sequence byte
HighTagValueBitmask Tag = 0x7f // 01111111b
)
const (
// LengthLongFormBitmask is the mask to apply to the length byte to see if a long-form byte sequence is used
LengthLongFormBitmask = 0x80
// LengthValueBitmask is the mask to apply to the length byte to get the number of bytes in the long-form byte sequence
LengthValueBitmask = 0x7f
// LengthIndefinite is returned from readLength to indicate an indefinite length
LengthIndefinite = -1
)
var tagMap = map[Tag]string{
TagEOC: "EOC (End-of-Content)",
TagBoolean: "Boolean",
TagInteger: "Integer",
TagBitString: "Bit String",
TagOctetString: "Octet String",
TagNULL: "NULL",
TagObjectIdentifier: "Object Identifier",
TagObjectDescriptor: "Object Descriptor",
TagExternal: "External",
TagRealFloat: "Real (float)",
TagEnumerated: "Enumerated",
TagEmbeddedPDV: "Embedded PDV",
TagUTF8String: "UTF8 String",
TagRelativeOID: "Relative-OID",
TagSequence: "Sequence and Sequence of",
TagSet: "Set and Set OF",
TagNumericString: "Numeric String",
TagPrintableString: "Printable String",
TagT61String: "T61 String",
TagVideotexString: "Videotex String",
TagIA5String: "IA5 String",
TagUTCTime: "UTC Time",
TagGeneralizedTime: "Generalized Time",
TagGraphicString: "Graphic String",
TagVisibleString: "Visible String",
TagGeneralString: "General String",
TagUniversalString: "Universal String",
TagCharacterString: "Character String",
TagBMPString: "BMP String",
}
type Class uint8
const (
ClassUniversal Class = 0 // 00xxxxxxb
ClassApplication Class = 64 // 01xxxxxxb
ClassContext Class = 128 // 10xxxxxxb
ClassPrivate Class = 192 // 11xxxxxxb
ClassBitmask Class = 192 // 11xxxxxxb
)
var ClassMap = map[Class]string{
ClassUniversal: "Universal",
ClassApplication: "Application",
ClassContext: "Context",
ClassPrivate: "Private",
}
type Type uint8
const (
TypePrimitive Type = 0 // xx0xxxxxb
TypeConstructed Type = 32 // xx1xxxxxb
TypeBitmask Type = 32 // xx1xxxxxb
)
var TypeMap = map[Type]string{
TypePrimitive: "Primitive",
TypeConstructed: "Constructed",
}
var Debug bool = false
func PrintBytes(out io.Writer, buf []byte, indent string) {
data_lines := make([]string, (len(buf)/30)+1)
num_lines := make([]string, (len(buf)/30)+1)
for i, b := range buf {
data_lines[i/30] += fmt.Sprintf("%02x ", b)
num_lines[i/30] += fmt.Sprintf("%02d ", (i+1)%100)
}
for i := 0; i < len(data_lines); i++ {
out.Write([]byte(indent + data_lines[i] + "\n"))
out.Write([]byte(indent + num_lines[i] + "\n\n"))
}
}
func PrintPacket(p *Packet) {
printPacket(os.Stdout, p, 0, false)
}
func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
indent_str := ""
for len(indent_str) != indent {
indent_str += " "
}
class_str := ClassMap[p.ClassType]
tagtype_str := TypeMap[p.TagType]
tag_str := fmt.Sprintf("0x%02X", p.Tag)
if p.ClassType == ClassUniversal {
tag_str = tagMap[p.Tag]
}
value := fmt.Sprint(p.Value)
description := ""
if p.Description != "" {
description = p.Description + ": "
}
fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indent_str, description, class_str, tagtype_str, tag_str, p.Data.Len(), value)
if printBytes {
PrintBytes(out, p.Bytes(), indent_str)
}
for _, child := range p.Children {
printPacket(out, child, indent+1, printBytes)
}
}
// ReadPacket reads a single Packet from the reader
func ReadPacket(reader io.Reader) (*Packet, error) {
p, _, err := readPacket(reader)
if err != nil {
return nil, err
}
return p, nil
}
func DecodeString(data []byte) string {
return string(data)
}
func ParseInt64(bytes []byte) (ret int64, err error) {
if len(bytes) > 8 {
// We'll overflow an int64 in this case.
err = fmt.Errorf("integer too large")
return
}
for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
ret <<= 8
ret |= int64(bytes[bytesRead])
}
// Shift up and down in order to sign extend the result.
ret <<= 64 - uint8(len(bytes))*8
ret >>= 64 - uint8(len(bytes))*8
return
}
func encodeInteger(i int64) []byte {
n := int64Length(i)
out := make([]byte, n)
var j int
for ; n > 0; n-- {
out[j] = (byte(i >> uint((n-1)*8)))
j++
}
return out
}
func int64Length(i int64) (numBytes int) {
numBytes = 1
for i > 127 {
numBytes++
i >>= 8
}
for i < -128 {
numBytes++
i >>= 8
}
return
}
// DecodePacket decodes the given bytes into a single Packet
// If a decode error is encountered, nil is returned.
func DecodePacket(data []byte) *Packet {
p, _, _ := readPacket(bytes.NewBuffer(data))
return p
}
// DecodePacketErr decodes the given bytes into a single Packet
// If a decode error is encountered, nil is returned
func DecodePacketErr(data []byte) (*Packet, error) {
p, _, err := readPacket(bytes.NewBuffer(data))
if err != nil {
return nil, err
}
return p, nil
}
// readPacket reads a single Packet from the reader, returning the number of bytes read
func readPacket(reader io.Reader) (*Packet, int, error) {
identifier, length, read, err := readHeader(reader)
if err != nil {
return nil, read, err
}
p := &Packet{
Identifier: identifier,
}
p.Data = new(bytes.Buffer)
p.Children = make([]*Packet, 0, 2)
p.Value = nil
if p.TagType == TypeConstructed {
// TODO: if universal, ensure tag type is allowed to be constructed
// Track how much content we've read
contentRead := 0
for {
if length != LengthIndefinite {
// End if we've read what we've been told to
if contentRead == length {
break
}
// Detect if a packet boundary didn't fall on the expected length
if contentRead > length {
return nil, read, fmt.Errorf("expected to read %d bytes, read %d", length, contentRead)
}
}
// Read the next packet
child, r, err := readPacket(reader)
if err != nil {
return nil, read, err
}
contentRead += r
read += r
// Test is this is the EOC marker for our packet
if isEOCPacket(child) {
if length == LengthIndefinite {
break
}
return nil, read, errors.New("eoc child not allowed with definite length")
}
// Append and continue
p.AppendChild(child)
}
return p, read, nil
}
if length == LengthIndefinite {
return nil, read, errors.New("indefinite length used with primitive type")
}
// Read definite-length content
if MaxPacketLengthBytes > 0 && int64(length) > MaxPacketLengthBytes {
return nil, read, fmt.Errorf("length %d greater than maximum %d", length, MaxPacketLengthBytes)
}
content := make([]byte, length, length)
if length > 0 {
_, err := io.ReadFull(reader, content)
if err != nil {
if err == io.EOF {
return nil, read, io.ErrUnexpectedEOF
}
return nil, read, err
}
read += length
}
if p.ClassType == ClassUniversal {
p.Data.Write(content)
p.ByteValue = content
switch p.Tag {
case TagEOC:
case TagBoolean:
val, _ := ParseInt64(content)
p.Value = val != 0
case TagInteger:
p.Value, _ = ParseInt64(content)
case TagBitString:
case TagOctetString:
// the actual string encoding is not known here
// (e.g. for LDAP content is already an UTF8-encoded
// string). Return the data without further processing
p.Value = DecodeString(content)
case TagNULL:
case TagObjectIdentifier:
case TagObjectDescriptor:
case TagExternal:
case TagRealFloat:
case TagEnumerated:
p.Value, _ = ParseInt64(content)
case TagEmbeddedPDV:
case TagUTF8String:
p.Value = DecodeString(content)
case TagRelativeOID:
case TagSequence:
case TagSet:
case TagNumericString:
case TagPrintableString:
p.Value = DecodeString(content)
case TagT61String:
case TagVideotexString:
case TagIA5String:
case TagUTCTime:
case TagGeneralizedTime:
case TagGraphicString:
case TagVisibleString:
case TagGeneralString:
case TagUniversalString:
case TagCharacterString:
case TagBMPString:
}
} else {
p.Data.Write(content)
}
return p, read, nil
}
func (p *Packet) Bytes() []byte {
var out bytes.Buffer
out.Write(encodeIdentifier(p.Identifier))
out.Write(encodeLength(p.Data.Len()))
out.Write(p.Data.Bytes())
return out.Bytes()
}
func (p *Packet) AppendChild(child *Packet) {
p.Data.Write(child.Bytes())
p.Children = append(p.Children, child)
}
func Encode(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet {
p := new(Packet)
p.ClassType = ClassType
p.TagType = TagType
p.Tag = Tag
p.Data = new(bytes.Buffer)
p.Children = make([]*Packet, 0, 2)
p.Value = Value
p.Description = Description
if Value != nil {
v := reflect.ValueOf(Value)
if ClassType == ClassUniversal {
switch Tag {
case TagOctetString:
sv, ok := v.Interface().(string)
if ok {
p.Data.Write([]byte(sv))
}
}
}
}
return p
}
func NewSequence(Description string) *Packet {
return Encode(ClassUniversal, TypeConstructed, TagSequence, nil, Description)
}
func NewBoolean(ClassType Class, TagType Type, Tag Tag, Value bool, Description string) *Packet {
intValue := int64(0)
if Value {
intValue = 1
}
p := Encode(ClassType, TagType, Tag, nil, Description)
p.Value = Value
p.Data.Write(encodeInteger(intValue))
return p
}
func NewInteger(ClassType Class, TagType Type, Tag Tag, Value interface{}, Description string) *Packet {
p := Encode(ClassType, TagType, Tag, nil, Description)
p.Value = Value
switch v := Value.(type) {
case int:
p.Data.Write(encodeInteger(int64(v)))
case uint:
p.Data.Write(encodeInteger(int64(v)))
case int64:
p.Data.Write(encodeInteger(v))
case uint64:
// TODO : check range or add encodeUInt...
p.Data.Write(encodeInteger(int64(v)))
case int32:
p.Data.Write(encodeInteger(int64(v)))
case uint32:
p.Data.Write(encodeInteger(int64(v)))
case int16:
p.Data.Write(encodeInteger(int64(v)))
case uint16:
p.Data.Write(encodeInteger(int64(v)))
case int8:
p.Data.Write(encodeInteger(int64(v)))
case uint8:
p.Data.Write(encodeInteger(int64(v)))
default:
// TODO : add support for big.Int ?
panic(fmt.Sprintf("Invalid type %T, expected {u|}int{64|32|16|8}", v))
}
return p
}
func NewString(ClassType Class, TagType Type, Tag Tag, Value, Description string) *Packet {
p := Encode(ClassType, TagType, Tag, nil, Description)
p.Value = Value
p.Data.Write([]byte(Value))
return p
}

View File

@ -1,25 +0,0 @@
package ber
func encodeUnsignedInteger(i uint64) []byte {
n := uint64Length(i)
out := make([]byte, n)
var j int
for ; n > 0; n-- {
out[j] = (byte(i >> uint((n-1)*8)))
j++
}
return out
}
func uint64Length(i uint64) (numBytes int) {
numBytes = 1
for i > 255 {
numBytes++
i >>= 8
}
return
}

View File

@ -1,35 +0,0 @@
package ber
import (
"errors"
"fmt"
"io"
)
func readHeader(reader io.Reader) (identifier Identifier, length int, read int, err error) {
if i, c, err := readIdentifier(reader); err != nil {
return Identifier{}, 0, read, err
} else {
identifier = i
read += c
}
if l, c, err := readLength(reader); err != nil {
return Identifier{}, 0, read, err
} else {
length = l
read += c
}
// Validate length type with identifier (x.600, 8.1.3.2.a)
if length == LengthIndefinite && identifier.TagType == TypePrimitive {
return Identifier{}, 0, read, errors.New("indefinite length used with primitive type")
}
if length < LengthIndefinite {
err = fmt.Errorf("length cannot be less than %d", LengthIndefinite)
return
}
return identifier, length, read, nil
}

View File

@ -1,112 +0,0 @@
package ber
import (
"errors"
"fmt"
"io"
)
func readIdentifier(reader io.Reader) (Identifier, int, error) {
identifier := Identifier{}
read := 0
// identifier byte
b, err := readByte(reader)
if err != nil {
if Debug {
fmt.Printf("error reading identifier byte: %v\n", err)
}
return Identifier{}, read, err
}
read++
identifier.ClassType = Class(b) & ClassBitmask
identifier.TagType = Type(b) & TypeBitmask
if tag := Tag(b) & TagBitmask; tag != HighTag {
// short-form tag
identifier.Tag = tag
return identifier, read, nil
}
// high-tag-number tag
tagBytes := 0
for {
b, err := readByte(reader)
if err != nil {
if Debug {
fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err)
}
return Identifier{}, read, err
}
tagBytes++
read++
// Lowest 7 bits get appended to the tag value (x.690, 8.1.2.4.2.b)
identifier.Tag <<= 7
identifier.Tag |= Tag(b) & HighTagValueBitmask
// First byte may not be all zeros (x.690, 8.1.2.4.2.c)
if tagBytes == 1 && identifier.Tag == 0 {
return Identifier{}, read, errors.New("invalid first high-tag-number tag byte")
}
// Overflow of int64
// TODO: support big int tags?
if tagBytes > 9 {
return Identifier{}, read, errors.New("high-tag-number tag overflow")
}
// Top bit of 0 means this is the last byte in the high-tag-number tag (x.690, 8.1.2.4.2.a)
if Tag(b)&HighTagContinueBitmask == 0 {
break
}
}
return identifier, read, nil
}
func encodeIdentifier(identifier Identifier) []byte {
b := []byte{0x0}
b[0] |= byte(identifier.ClassType)
b[0] |= byte(identifier.TagType)
if identifier.Tag < HighTag {
// Short-form
b[0] |= byte(identifier.Tag)
} else {
// high-tag-number
b[0] |= byte(HighTag)
tag := identifier.Tag
b = append(b, encodeHighTag(tag)...)
}
return b
}
func encodeHighTag(tag Tag) []byte {
// set cap=4 to hopefully avoid additional allocations
b := make([]byte, 0, 4)
for tag != 0 {
// t := last 7 bits of tag (HighTagValueBitmask = 0x7F)
t := tag & HighTagValueBitmask
// right shift tag 7 to remove what was just pulled off
tag >>= 7
// if b already has entries this entry needs a continuation bit (0x80)
if len(b) != 0 {
t |= HighTagContinueBitmask
}
b = append(b, byte(t))
}
// reverse
// since bits were pulled off 'tag' small to high the byte slice is in reverse order.
// example: tag = 0xFF results in {0x7F, 0x01 + 0x80 (continuation bit)}
// this needs to be reversed into 0x81 0x7F
for i, j := 0, len(b)-1; i < len(b)/2; i++ {
b[i], b[j-i] = b[j-i], b[i]
}
return b
}

View File

@ -1,81 +0,0 @@
package ber
import (
"errors"
"fmt"
"io"
)
func readLength(reader io.Reader) (length int, read int, err error) {
// length byte
b, err := readByte(reader)
if err != nil {
if Debug {
fmt.Printf("error reading length byte: %v\n", err)
}
return 0, 0, err
}
read++
switch {
case b == 0xFF:
// Invalid 0xFF (x.600, 8.1.3.5.c)
return 0, read, errors.New("invalid length byte 0xff")
case b == LengthLongFormBitmask:
// Indefinite form, we have to decode packets until we encounter an EOC packet (x.600, 8.1.3.6)
length = LengthIndefinite
case b&LengthLongFormBitmask == 0:
// Short definite form, extract the length from the bottom 7 bits (x.600, 8.1.3.4)
length = int(b) & LengthValueBitmask
case b&LengthLongFormBitmask != 0:
// Long definite form, extract the number of length bytes to follow from the bottom 7 bits (x.600, 8.1.3.5.b)
lengthBytes := int(b) & LengthValueBitmask
// Protect against overflow
// TODO: support big int length?
if lengthBytes > 8 {
return 0, read, errors.New("long-form length overflow")
}
// Accumulate into a 64-bit variable
var length64 int64
for i := 0; i < lengthBytes; i++ {
b, err = readByte(reader)
if err != nil {
if Debug {
fmt.Printf("error reading long-form length byte %d: %v\n", i, err)
}
return 0, read, err
}
read++
// x.600, 8.1.3.5
length64 <<= 8
length64 |= int64(b)
}
// Cast to a platform-specific integer
length = int(length64)
// Ensure we didn't overflow
if int64(length) != length64 {
return 0, read, errors.New("long-form length overflow")
}
default:
return 0, read, errors.New("invalid length byte")
}
return length, read, nil
}
func encodeLength(length int) []byte {
length_bytes := encodeUnsignedInteger(uint64(length))
if length > 127 || len(length_bytes) > 1 {
longFormBytes := []byte{(LengthLongFormBitmask | byte(len(length_bytes)))}
longFormBytes = append(longFormBytes, length_bytes...)
length_bytes = longFormBytes
}
return length_bytes
}

24
vendor/gopkg.in/asn1-ber.v1/util.go generated vendored
View File

@ -1,24 +0,0 @@
package ber
import "io"
func readByte(reader io.Reader) (byte, error) {
bytes := make([]byte, 1, 1)
_, err := io.ReadFull(reader, bytes)
if err != nil {
if err == io.EOF {
return 0, io.ErrUnexpectedEOF
}
return 0, err
}
return bytes[0], nil
}
func isEOCPacket(p *Packet) bool {
return p != nil &&
p.Tag == TagEOC &&
p.ClassType == ClassUniversal &&
p.TagType == TypePrimitive &&
len(p.ByteValue) == 0 &&
len(p.Children) == 0
}

0
vendor/gopkg.in/ldap.v2/.gitignore generated vendored
View File

31
vendor/gopkg.in/ldap.v2/.travis.yml generated vendored
View File

@ -1,31 +0,0 @@
language: go
env:
global:
- VET_VERSIONS="1.6 1.7 1.8 1.9 tip"
- LINT_VERSIONS="1.6 1.7 1.8 1.9 tip"
go:
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- 1.7
- 1.8
- 1.9
- tip
matrix:
fast_finish: true
allow_failures:
- go: tip
go_import_path: gopkg.in/ldap.v2
install:
- go get gopkg.in/asn1-ber.v1
- go get gopkg.in/ldap.v2
- go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover
- go get github.com/golang/lint/golint || true
- go build -v ./...
script:
- make test
- make fmt
- if [[ "$VET_VERSIONS" == *"$TRAVIS_GO_VERSION"* ]]; then make vet; fi
- if [[ "$LINT_VERSIONS" == *"$TRAVIS_GO_VERSION"* ]]; then make lint; fi

22
vendor/gopkg.in/ldap.v2/LICENSE generated vendored
View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
Portions copyright (c) 2015-2016 go-ldap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

52
vendor/gopkg.in/ldap.v2/Makefile generated vendored
View File

@ -1,52 +0,0 @@
.PHONY: default install build test quicktest fmt vet lint
GO_VERSION := $(shell go version | cut -d' ' -f3 | cut -d. -f2)
# Only use the `-race` flag on newer versions of Go
IS_OLD_GO := $(shell test $(GO_VERSION) -le 2 && echo true)
ifeq ($(IS_OLD_GO),true)
RACE_FLAG :=
else
RACE_FLAG := -race -cpu 1,2,4
endif
default: fmt vet lint build quicktest
install:
go get -t -v ./...
build:
go build -v ./...
test:
go test -v $(RACE_FLAG) -cover ./...
quicktest:
go test ./...
# Capture output and force failure when there is non-empty output
fmt:
@echo gofmt -l .
@OUTPUT=`gofmt -l . 2>&1`; \
if [ "$$OUTPUT" ]; then \
echo "gofmt must be run on the following files:"; \
echo "$$OUTPUT"; \
exit 1; \
fi
# Only run on go1.5+
vet:
go tool vet -atomic -bool -copylocks -nilfunc -printf -shadow -rangeloops -unreachable -unsafeptr -unusedresult .
# https://github.com/golang/lint
# go get github.com/golang/lint/golint
# Capture output and force failure when there is non-empty output
# Only run on go1.5+
lint:
@echo golint ./...
@OUTPUT=`golint ./... 2>&1`; \
if [ "$$OUTPUT" ]; then \
echo "golint errors:"; \
echo "$$OUTPUT"; \
exit 1; \
fi

53
vendor/gopkg.in/ldap.v2/README.md generated vendored
View File

@ -1,53 +0,0 @@
[![GoDoc](https://godoc.org/gopkg.in/ldap.v2?status.svg)](https://godoc.org/gopkg.in/ldap.v2)
[![Build Status](https://travis-ci.org/go-ldap/ldap.svg)](https://travis-ci.org/go-ldap/ldap)
# Basic LDAP v3 functionality for the GO programming language.
## Install
For the latest version use:
go get gopkg.in/ldap.v2
Import the latest version with:
import "gopkg.in/ldap.v2"
## Required Libraries:
- gopkg.in/asn1-ber.v1
## Features:
- Connecting to LDAP server (non-TLS, TLS, STARTTLS)
- Binding to LDAP server
- Searching for entries
- Filter Compile / Decompile
- Paging Search Results
- Modify Requests / Responses
- Add Requests / Responses
- Delete Requests / Responses
## Examples:
- search
- modify
## Contributing:
Bug reports and pull requests are welcome!
Before submitting a pull request, please make sure tests and verification scripts pass:
```
make all
```
To set up a pre-push hook to run the tests and verify scripts before pushing:
```
ln -s ../../.githooks/pre-push .git/hooks/pre-push
```
---
The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
The design is licensed under the Creative Commons 3.0 Attributions license.
Read this article for more details: http://blog.golang.org/gopher

113
vendor/gopkg.in/ldap.v2/add.go generated vendored
View File

@ -1,113 +0,0 @@
//
// https://tools.ietf.org/html/rfc4511
//
// AddRequest ::= [APPLICATION 8] SEQUENCE {
// entry LDAPDN,
// attributes AttributeList }
//
// AttributeList ::= SEQUENCE OF attribute Attribute
package ldap
import (
"errors"
"log"
"gopkg.in/asn1-ber.v1"
)
// Attribute represents an LDAP attribute
type Attribute struct {
// Type is the name of the LDAP attribute
Type string
// Vals are the LDAP attribute values
Vals []string
}
func (a *Attribute) encode() *ber.Packet {
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type"))
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
for _, value := range a.Vals {
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
}
seq.AppendChild(set)
return seq
}
// AddRequest represents an LDAP AddRequest operation
type AddRequest struct {
// DN identifies the entry being added
DN string
// Attributes list the attributes of the new entry
Attributes []Attribute
}
func (a AddRequest) encode() *ber.Packet {
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN"))
attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
for _, attribute := range a.Attributes {
attributes.AppendChild(attribute.encode())
}
request.AppendChild(attributes)
return request
}
// Attribute adds an attribute with the given type and values
func (a *AddRequest) Attribute(attrType string, attrVals []string) {
a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals})
}
// NewAddRequest returns an AddRequest for the given DN, with no attributes
func NewAddRequest(dn string) *AddRequest {
return &AddRequest{
DN: dn,
}
}
// Add performs the given AddRequest
func (l *Conn) Add(addRequest *AddRequest) error {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
packet.AppendChild(addRequest.encode())
l.Debug.PrintPacket(packet)
msgCtx, err := l.sendMessage(packet)
if err != nil {
return err
}
defer l.finishMessage(msgCtx)
l.Debug.Printf("%d: waiting for response", msgCtx.id)
packetResponse, ok := <-msgCtx.responses
if !ok {
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
if l.Debug {
if err := addLDAPDescriptions(packet); err != nil {
return err
}
ber.PrintPacket(packet)
}
if packet.Children[1].Tag == ApplicationAddResponse {
resultCode, resultDescription := getLDAPResultCode(packet)
if resultCode != 0 {
return NewError(resultCode, errors.New(resultDescription))
}
} else {
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
}
l.Debug.Printf("%d: returning", msgCtx.id)
return nil
}

View File

@ -1,13 +0,0 @@
// +build go1.4
package ldap
import (
"sync/atomic"
)
// For compilers that support it, we just use the underlying sync/atomic.Value
// type.
type atomicValue struct {
atomic.Value
}

View File

@ -1,28 +0,0 @@
// +build !go1.4
package ldap
import (
"sync"
)
// This is a helper type that emulates the use of the "sync/atomic.Value"
// struct that's available in Go 1.4 and up.
type atomicValue struct {
value interface{}
lock sync.RWMutex
}
func (av *atomicValue) Store(val interface{}) {
av.lock.Lock()
av.value = val
av.lock.Unlock()
}
func (av *atomicValue) Load() interface{} {
av.lock.RLock()
ret := av.value
av.lock.RUnlock()
return ret
}

143
vendor/gopkg.in/ldap.v2/bind.go generated vendored
View File

@ -1,143 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ldap
import (
"errors"
"gopkg.in/asn1-ber.v1"
)
// SimpleBindRequest represents a username/password bind operation
type SimpleBindRequest struct {
// Username is the name of the Directory object that the client wishes to bind as
Username string
// Password is the credentials to bind with
Password string
// Controls are optional controls to send with the bind request
Controls []Control
}
// SimpleBindResult contains the response from the server
type SimpleBindResult struct {
Controls []Control
}
// NewSimpleBindRequest returns a bind request
func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
return &SimpleBindRequest{
Username: username,
Password: password,
Controls: controls,
}
}
func (bindRequest *SimpleBindRequest) encode() *ber.Packet {
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, bindRequest.Username, "User Name"))
request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, bindRequest.Password, "Password"))
request.AppendChild(encodeControls(bindRequest.Controls))
return request
}
// SimpleBind performs the simple bind operation defined in the given request
func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
encodedBindRequest := simpleBindRequest.encode()
packet.AppendChild(encodedBindRequest)
if l.Debug {
ber.PrintPacket(packet)
}
msgCtx, err := l.sendMessage(packet)
if err != nil {
return nil, err
}
defer l.finishMessage(msgCtx)
packetResponse, ok := <-msgCtx.responses
if !ok {
return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return nil, err
}
if l.Debug {
if err := addLDAPDescriptions(packet); err != nil {
return nil, err
}
ber.PrintPacket(packet)
}
result := &SimpleBindResult{
Controls: make([]Control, 0),
}
if len(packet.Children) == 3 {
for _, child := range packet.Children[2].Children {
result.Controls = append(result.Controls, DecodeControl(child))
}
}
resultCode, resultDescription := getLDAPResultCode(packet)
if resultCode != 0 {
return result, NewError(resultCode, errors.New(resultDescription))
}
return result, nil
}
// Bind performs a bind with the given username and password
func (l *Conn) Bind(username, password string) error {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
bindRequest.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, username, "User Name"))
bindRequest.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, password, "Password"))
packet.AppendChild(bindRequest)
if l.Debug {
ber.PrintPacket(packet)
}
msgCtx, err := l.sendMessage(packet)
if err != nil {
return err
}
defer l.finishMessage(msgCtx)
packetResponse, ok := <-msgCtx.responses
if !ok {
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
if l.Debug {
if err := addLDAPDescriptions(packet); err != nil {
return err
}
ber.PrintPacket(packet)
}
resultCode, resultDescription := getLDAPResultCode(packet)
if resultCode != 0 {
return NewError(resultCode, errors.New(resultDescription))
}
return nil
}

27
vendor/gopkg.in/ldap.v2/client.go generated vendored
View File

@ -1,27 +0,0 @@
package ldap
import (
"crypto/tls"
"time"
)
// Client knows how to interact with an LDAP server
type Client interface {
Start()
StartTLS(config *tls.Config) error
Close()
SetTimeout(time.Duration)
Bind(username, password string) error
SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error)
Add(addRequest *AddRequest) error
Del(delRequest *DelRequest) error
Modify(modifyRequest *ModifyRequest) error
Compare(dn, attribute, value string) (bool, error)
PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error)
Search(searchRequest *SearchRequest) (*SearchResult, error)
SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
}

85
vendor/gopkg.in/ldap.v2/compare.go generated vendored
View File

@ -1,85 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// File contains Compare functionality
//
// https://tools.ietf.org/html/rfc4511
//
// CompareRequest ::= [APPLICATION 14] SEQUENCE {
// entry LDAPDN,
// ava AttributeValueAssertion }
//
// AttributeValueAssertion ::= SEQUENCE {
// attributeDesc AttributeDescription,
// assertionValue AssertionValue }
//
// AttributeDescription ::= LDAPString
// -- Constrained to <attributedescription>
// -- [RFC4512]
//
// AttributeValue ::= OCTET STRING
//
package ldap
import (
"errors"
"fmt"
"gopkg.in/asn1-ber.v1"
)
// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise
// false with any error that occurs if any.
func (l *Conn) Compare(dn, attribute, value string) (bool, error) {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request")
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN"))
ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion")
ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "AttributeDesc"))
ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagOctetString, value, "AssertionValue"))
request.AppendChild(ava)
packet.AppendChild(request)
l.Debug.PrintPacket(packet)
msgCtx, err := l.sendMessage(packet)
if err != nil {
return false, err
}
defer l.finishMessage(msgCtx)
l.Debug.Printf("%d: waiting for response", msgCtx.id)
packetResponse, ok := <-msgCtx.responses
if !ok {
return false, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return false, err
}
if l.Debug {
if err := addLDAPDescriptions(packet); err != nil {
return false, err
}
ber.PrintPacket(packet)
}
if packet.Children[1].Tag == ApplicationCompareResponse {
resultCode, resultDescription := getLDAPResultCode(packet)
if resultCode == LDAPResultCompareTrue {
return true, nil
} else if resultCode == LDAPResultCompareFalse {
return false, nil
} else {
return false, NewError(resultCode, errors.New(resultDescription))
}
}
return false, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag)
}

470
vendor/gopkg.in/ldap.v2/conn.go generated vendored
View File

@ -1,470 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ldap
import (
"crypto/tls"
"errors"
"fmt"
"log"
"net"
"sync"
"sync/atomic"
"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
closing uint32
closeErr atomicValue
isStartingTLS bool
Debug debugging
chanConfirm chan struct{}
messageContexts map[int64]*messageContext
chanMessage chan *messagePacket
chanMessageID chan int64
wgClose sync.WaitGroup
outstandingRequests uint
messageMutex sync.Mutex
requestTimeout int64
}
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 struct{}),
chanMessageID: make(chan int64),
chanMessage: make(chan *messagePacket, 10),
messageContexts: map[int64]*messageContext{},
requestTimeout: 0,
isTLS: isTLS,
}
}
// Start initializes goroutines to read responses and process messages
func (l *Conn) Start() {
go l.reader()
go l.processMessages()
l.wgClose.Add(1)
}
// isClosing returns whether or not we're currently closing.
func (l *Conn) isClosing() bool {
return atomic.LoadUint32(&l.closing) == 1
}
// setClosing sets the closing value to true
func (l *Conn) setClosing() bool {
return atomic.CompareAndSwapUint32(&l.closing, 0, 1)
}
// Close closes the connection.
func (l *Conn) Close() {
l.messageMutex.Lock()
defer l.messageMutex.Unlock()
if l.setClosing() {
l.Debug.Printf("Sending quit message and waiting for confirmation")
l.chanMessage <- &messagePacket{Op: MessageQuit}
<-l.chanConfirm
close(l.chanMessage)
l.Debug.Printf("Closing network connection")
if err := l.conn.Close(); err != nil {
log.Println(err)
}
l.wgClose.Done()
}
l.wgClose.Wait()
}
// SetTimeout sets the time after a request is sent that a MessageTimeout triggers
func (l *Conn) SetTimeout(timeout time.Duration) {
if timeout > 0 {
atomic.StoreInt64(&l.requestTimeout, int64(timeout))
}
}
// Returns the next available messageID
func (l *Conn) nextMessageID() int64 {
if messageID, ok := <-l.chanMessageID; ok {
return messageID
}
return 0
}
// StartTLS sends the command to start a TLS session and then creates a new TLS Client
func (l *Conn) StartTLS(config *tls.Config) error {
if l.isTLS {
return NewError(ErrorNetwork, errors.New("ldap: already encrypted"))
}
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS")
request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
packet.AppendChild(request)
l.Debug.PrintPacket(packet)
msgCtx, err := l.sendMessageWithFlags(packet, startTLS)
if err != nil {
return err
}
defer l.finishMessage(msgCtx)
l.Debug.Printf("%d: waiting for response", msgCtx.id)
packetResponse, ok := <-msgCtx.responses
if !ok {
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
if l.Debug {
if err := addLDAPDescriptions(packet); err != nil {
l.Close()
return err
}
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 {
l.messageMutex.Lock()
defer l.messageMutex.Unlock()
if l.isClosing() {
return false
}
l.chanMessage <- message
return true
}
func (l *Conn) processMessages() {
defer func() {
if err := recover(); err != nil {
log.Printf("ldap: recovered panic in processMessages: %v", err)
}
for messageID, msgCtx := range l.messageContexts {
// If we are closing due to an error, inform anyone who
// is waiting about the error.
if l.isClosing() && l.closeErr.Load() != nil {
msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)})
}
l.Debug.Printf("Closing channel for MessageID %d", messageID)
close(msgCtx.responses)
delete(l.messageContexts, messageID)
}
close(l.chanMessageID)
close(l.chanConfirm)
}()
var messageID int64 = 1
for {
select {
case l.chanMessageID <- messageID:
messageID++
case message := <-l.chanMessage:
switch message.Op {
case MessageQuit:
l.Debug.Printf("Shutting down - quit message received")
return
case MessageRequest:
// Add to message list and write to network
l.Debug.Printf("Sending message %d", message.MessageID)
buf := message.Packet.Bytes()
_, err := l.conn.Write(buf)
if err != nil {
l.Debug.Printf("Error Sending Message: %s", err.Error())
message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)})
close(message.Context.responses)
break
}
// Only add to messageContexts if we were able to
// successfully write the message.
l.messageContexts[message.MessageID] = message.Context
// Add timeout if defined
requestTimeout := time.Duration(atomic.LoadInt64(&l.requestTimeout))
if requestTimeout > 0 {
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("ldap: recovered panic in RequestTimeout: %v", err)
}
}()
time.Sleep(requestTimeout)
timeoutMessage := &messagePacket{
Op: MessageTimeout,
MessageID: message.MessageID,
}
l.sendProcessMessage(timeoutMessage)
}()
}
case MessageResponse:
l.Debug.Printf("Receiving message %d", message.MessageID)
if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
msgCtx.sendResponse(&PacketResponse{message.Packet, nil})
} else {
log.Printf("Received unexpected message %d, %v", message.MessageID, l.isClosing())
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.Store(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
vendor/gopkg.in/ldap.v2/control.go generated vendored
View File

@ -1,420 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ldap
import (
"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
vendor/gopkg.in/ldap.v2/debug.go generated vendored
View File

@ -1,24 +0,0 @@
package ldap
import (
"log"
"gopkg.in/asn1-ber.v1"
)
// debugging 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
vendor/gopkg.in/ldap.v2/del.go generated vendored
View File

@ -1,84 +0,0 @@
//
// https://tools.ietf.org/html/rfc4511
//
// DelRequest ::= [APPLICATION 10] LDAPDN
package ldap
import (
"errors"
"log"
"gopkg.in/asn1-ber.v1"
)
// DelRequest implements an LDAP deletion request
type DelRequest struct {
// DN is the name of the directory entry to delete
DN string
// Controls hold optional controls to send with the request
Controls []Control
}
func (d DelRequest) encode() *ber.Packet {
request := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, d.DN, "Del Request")
request.Data.Write([]byte(d.DN))
return request
}
// NewDelRequest creates a delete request for the given DN and controls
func NewDelRequest(DN string,
Controls []Control) *DelRequest {
return &DelRequest{
DN: DN,
Controls: Controls,
}
}
// Del executes the given delete request
func (l *Conn) Del(delRequest *DelRequest) error {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
packet.AppendChild(delRequest.encode())
if delRequest.Controls != nil {
packet.AppendChild(encodeControls(delRequest.Controls))
}
l.Debug.PrintPacket(packet)
msgCtx, err := l.sendMessage(packet)
if err != nil {
return err
}
defer l.finishMessage(msgCtx)
l.Debug.Printf("%d: waiting for response", msgCtx.id)
packetResponse, ok := <-msgCtx.responses
if !ok {
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
if l.Debug {
if err := addLDAPDescriptions(packet); err != nil {
return err
}
ber.PrintPacket(packet)
}
if packet.Children[1].Tag == ApplicationDelResponse {
resultCode, resultDescription := getLDAPResultCode(packet)
if resultCode != 0 {
return NewError(resultCode, errors.New(resultDescription))
}
} else {
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
}
l.Debug.Printf("%d: returning", msgCtx.id)
return nil
}

247
vendor/gopkg.in/ldap.v2/dn.go generated vendored
View File

@ -1,247 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// File contains DN parsing functionality
//
// https://tools.ietf.org/html/rfc4514
//
// distinguishedName = [ relativeDistinguishedName
// *( COMMA relativeDistinguishedName ) ]
// relativeDistinguishedName = attributeTypeAndValue
// *( PLUS attributeTypeAndValue )
// attributeTypeAndValue = attributeType EQUALS attributeValue
// attributeType = descr / numericoid
// attributeValue = string / hexstring
//
// ; The following characters are to be escaped when they appear
// ; in the value to be encoded: ESC, one of <escaped>, leading
// ; SHARP or SPACE, trailing SPACE, and NULL.
// string = [ ( leadchar / pair ) [ *( stringchar / pair )
// ( trailchar / pair ) ] ]
//
// leadchar = LUTF1 / UTFMB
// LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A /
// %x3D / %x3F-5B / %x5D-7F
//
// trailchar = TUTF1 / UTFMB
// TUTF1 = %x01-1F / %x21 / %x23-2A / %x2D-3A /
// %x3D / %x3F-5B / %x5D-7F
//
// stringchar = SUTF1 / UTFMB
// SUTF1 = %x01-21 / %x23-2A / %x2D-3A /
// %x3D / %x3F-5B / %x5D-7F
//
// pair = ESC ( ESC / special / hexpair )
// special = escaped / SPACE / SHARP / EQUALS
// escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
// hexstring = SHARP 1*hexpair
// hexpair = HEX HEX
//
// where the productions <descr>, <numericoid>, <COMMA>, <DQUOTE>,
// <EQUALS>, <ESC>, <HEX>, <LANGLE>, <NULL>, <PLUS>, <RANGLE>, <SEMI>,
// <SPACE>, <SHARP>, and <UTFMB> are defined in [RFC4512].
//
package ldap
import (
"bytes"
enchex "encoding/hex"
"errors"
"fmt"
"strings"
"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
if len(attribute.Type) == 0 {
return nil, errors.New("incomplete type, value pair")
}
attribute.Value = stringFromBuffer()
rdn.Attributes = append(rdn.Attributes, attribute)
attribute = new(AttributeTypeAndValue)
if char == ',' {
dn.RDNs = append(dn.RDNs, rdn)
rdn = new(RelativeDN)
rdn.Attributes = make([]*AttributeTypeAndValue, 0)
}
} 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
vendor/gopkg.in/ldap.v2/doc.go generated vendored
View File

@ -1,4 +0,0 @@
/*
Package ldap provides basic LDAP v3 functionality.
*/
package ldap

155
vendor/gopkg.in/ldap.v2/error.go generated vendored
View File

@ -1,155 +0,0 @@
package ldap
import (
"fmt"
"gopkg.in/asn1-ber.v1"
)
// LDAP Result Codes
const (
LDAPResultSuccess = 0
LDAPResultOperationsError = 1
LDAPResultProtocolError = 2
LDAPResultTimeLimitExceeded = 3
LDAPResultSizeLimitExceeded = 4
LDAPResultCompareFalse = 5
LDAPResultCompareTrue = 6
LDAPResultAuthMethodNotSupported = 7
LDAPResultStrongAuthRequired = 8
LDAPResultReferral = 10
LDAPResultAdminLimitExceeded = 11
LDAPResultUnavailableCriticalExtension = 12
LDAPResultConfidentialityRequired = 13
LDAPResultSaslBindInProgress = 14
LDAPResultNoSuchAttribute = 16
LDAPResultUndefinedAttributeType = 17
LDAPResultInappropriateMatching = 18
LDAPResultConstraintViolation = 19
LDAPResultAttributeOrValueExists = 20
LDAPResultInvalidAttributeSyntax = 21
LDAPResultNoSuchObject = 32
LDAPResultAliasProblem = 33
LDAPResultInvalidDNSyntax = 34
LDAPResultAliasDereferencingProblem = 36
LDAPResultInappropriateAuthentication = 48
LDAPResultInvalidCredentials = 49
LDAPResultInsufficientAccessRights = 50
LDAPResultBusy = 51
LDAPResultUnavailable = 52
LDAPResultUnwillingToPerform = 53
LDAPResultLoopDetect = 54
LDAPResultNamingViolation = 64
LDAPResultObjectClassViolation = 65
LDAPResultNotAllowedOnNonLeaf = 66
LDAPResultNotAllowedOnRDN = 67
LDAPResultEntryAlreadyExists = 68
LDAPResultObjectClassModsProhibited = 69
LDAPResultAffectsMultipleDSAs = 71
LDAPResultOther = 80
ErrorNetwork = 200
ErrorFilterCompile = 201
ErrorFilterDecompile = 202
ErrorDebugging = 203
ErrorUnexpectedMessage = 204
ErrorUnexpectedResponse = 205
)
// LDAPResultCodeMap contains string descriptions for LDAP error codes
var LDAPResultCodeMap = map[uint8]string{
LDAPResultSuccess: "Success",
LDAPResultOperationsError: "Operations Error",
LDAPResultProtocolError: "Protocol Error",
LDAPResultTimeLimitExceeded: "Time Limit Exceeded",
LDAPResultSizeLimitExceeded: "Size Limit Exceeded",
LDAPResultCompareFalse: "Compare False",
LDAPResultCompareTrue: "Compare True",
LDAPResultAuthMethodNotSupported: "Auth Method Not Supported",
LDAPResultStrongAuthRequired: "Strong Auth Required",
LDAPResultReferral: "Referral",
LDAPResultAdminLimitExceeded: "Admin Limit Exceeded",
LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension",
LDAPResultConfidentialityRequired: "Confidentiality Required",
LDAPResultSaslBindInProgress: "Sasl Bind In Progress",
LDAPResultNoSuchAttribute: "No Such Attribute",
LDAPResultUndefinedAttributeType: "Undefined Attribute Type",
LDAPResultInappropriateMatching: "Inappropriate Matching",
LDAPResultConstraintViolation: "Constraint Violation",
LDAPResultAttributeOrValueExists: "Attribute Or Value Exists",
LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax",
LDAPResultNoSuchObject: "No Such Object",
LDAPResultAliasProblem: "Alias Problem",
LDAPResultInvalidDNSyntax: "Invalid DN Syntax",
LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem",
LDAPResultInappropriateAuthentication: "Inappropriate Authentication",
LDAPResultInvalidCredentials: "Invalid Credentials",
LDAPResultInsufficientAccessRights: "Insufficient Access Rights",
LDAPResultBusy: "Busy",
LDAPResultUnavailable: "Unavailable",
LDAPResultUnwillingToPerform: "Unwilling To Perform",
LDAPResultLoopDetect: "Loop Detect",
LDAPResultNamingViolation: "Naming Violation",
LDAPResultObjectClassViolation: "Object Class Violation",
LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf",
LDAPResultNotAllowedOnRDN: "Not Allowed On RDN",
LDAPResultEntryAlreadyExists: "Entry Already Exists",
LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited",
LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs",
LDAPResultOther: "Other",
ErrorNetwork: "Network Error",
ErrorFilterCompile: "Filter Compile Error",
ErrorFilterDecompile: "Filter Decompile Error",
ErrorDebugging: "Debugging Error",
ErrorUnexpectedMessage: "Unexpected Message",
ErrorUnexpectedResponse: "Unexpected Response",
}
func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) {
if packet == nil {
return ErrorUnexpectedResponse, "Empty packet"
} else if len(packet.Children) >= 2 {
response := packet.Children[1]
if response == nil {
return ErrorUnexpectedResponse, "Empty response in packet"
}
if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
// Children[1].Children[2] is the diagnosticMessage which is guaranteed to exist as seen here: https://tools.ietf.org/html/rfc4511#section-4.1.9
return uint8(response.Children[0].Value.(int64)), response.Children[2].Value.(string)
}
}
return ErrorNetwork, "Invalid packet format"
}
// Error holds LDAP error information
type Error struct {
// Err is the underlying error
Err error
// ResultCode is the LDAP error code
ResultCode uint8
}
func (e *Error) Error() string {
return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
}
// NewError creates an LDAP error with the given code and underlying error
func NewError(resultCode uint8, err error) error {
return &Error{ResultCode: resultCode, Err: err}
}
// IsErrorWithCode returns true if the given error is an LDAP error with the given result code
func IsErrorWithCode(err error, desiredResultCode uint8) bool {
if err == nil {
return false
}
serverError, ok := err.(*Error)
if !ok {
return false
}
return serverError.ResultCode == desiredResultCode
}

469
vendor/gopkg.in/ldap.v2/filter.go generated vendored
View File

@ -1,469 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ldap
import (
"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
}
switch {
case pos > len(filter):
return nil, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
case pos < len(filter):
return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:])))
}
return packet, nil
}
// DecompileFilter converts a packet representation of a filter into a string representation
func DecompileFilter(packet *ber.Packet) (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
vendor/gopkg.in/ldap.v2/ldap.go generated vendored
View File

@ -1,320 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ldap
import (
"errors"
"io/ioutil"
"os"
"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
vendor/gopkg.in/ldap.v2/modify.go generated vendored
View File

@ -1,170 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// File contains Modify functionality
//
// https://tools.ietf.org/html/rfc4511
//
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
// object LDAPDN,
// changes SEQUENCE OF change SEQUENCE {
// operation ENUMERATED {
// add (0),
// delete (1),
// replace (2),
// ... },
// modification PartialAttribute } }
//
// PartialAttribute ::= SEQUENCE {
// type AttributeDescription,
// vals SET OF value AttributeValue }
//
// AttributeDescription ::= LDAPString
// -- Constrained to <attributedescription>
// -- [RFC4512]
//
// AttributeValue ::= OCTET STRING
//
package ldap
import (
"errors"
"log"
"gopkg.in/asn1-ber.v1"
)
// Change operation choices
const (
AddAttribute = 0
DeleteAttribute = 1
ReplaceAttribute = 2
)
// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
type PartialAttribute struct {
// Type is the type of the partial attribute
Type string
// Vals are the values of the partial attribute
Vals []string
}
func (p *PartialAttribute) encode() *ber.Packet {
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute")
seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type"))
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
for _, value := range p.Vals {
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
}
seq.AppendChild(set)
return seq
}
// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
type ModifyRequest struct {
// DN is the distinguishedName of the directory entry to modify
DN string
// AddAttributes contain the attributes to add
AddAttributes []PartialAttribute
// DeleteAttributes contain the attributes to delete
DeleteAttributes []PartialAttribute
// ReplaceAttributes contain the attributes to replace
ReplaceAttributes []PartialAttribute
}
// Add inserts the given attribute to the list of attributes to add
func (m *ModifyRequest) Add(attrType string, attrVals []string) {
m.AddAttributes = append(m.AddAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
}
// Delete inserts the given attribute to the list of attributes to delete
func (m *ModifyRequest) Delete(attrType string, attrVals []string) {
m.DeleteAttributes = append(m.DeleteAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
}
// Replace inserts the given attribute to the list of attributes to replace
func (m *ModifyRequest) Replace(attrType string, attrVals []string) {
m.ReplaceAttributes = append(m.ReplaceAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
}
func (m ModifyRequest) encode() *ber.Packet {
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request")
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN"))
changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes")
for _, attribute := range m.AddAttributes {
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(AddAttribute), "Operation"))
change.AppendChild(attribute.encode())
changes.AppendChild(change)
}
for _, attribute := range m.DeleteAttributes {
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(DeleteAttribute), "Operation"))
change.AppendChild(attribute.encode())
changes.AppendChild(change)
}
for _, attribute := range m.ReplaceAttributes {
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ReplaceAttribute), "Operation"))
change.AppendChild(attribute.encode())
changes.AppendChild(change)
}
request.AppendChild(changes)
return request
}
// NewModifyRequest creates a modify request for the given DN
func NewModifyRequest(
dn string,
) *ModifyRequest {
return &ModifyRequest{
DN: dn,
}
}
// Modify performs the ModifyRequest
func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
packet.AppendChild(modifyRequest.encode())
l.Debug.PrintPacket(packet)
msgCtx, err := l.sendMessage(packet)
if err != nil {
return err
}
defer l.finishMessage(msgCtx)
l.Debug.Printf("%d: waiting for response", msgCtx.id)
packetResponse, ok := <-msgCtx.responses
if !ok {
return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
}
packet, err = packetResponse.ReadPacket()
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
return err
}
if l.Debug {
if err := addLDAPDescriptions(packet); err != nil {
return err
}
ber.PrintPacket(packet)
}
if packet.Children[1].Tag == ApplicationModifyResponse {
resultCode, resultDescription := getLDAPResultCode(packet)
if resultCode != 0 {
return NewError(resultCode, errors.New(resultDescription))
}
} else {
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
}
l.Debug.Printf("%d: returning", msgCtx.id)
return nil
}

View File

@ -1,148 +0,0 @@
// 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 {
passwordModifyResponseValue := ber.DecodePacket(child.Data.Bytes())
if len(passwordModifyResponseValue.Children) == 1 {
if passwordModifyResponseValue.Children[0].Tag == 0 {
result.GeneratedPassword = ber.DecodeString(passwordModifyResponseValue.Children[0].Data.Bytes())
}
}
}
}
return result, nil
}

450
vendor/gopkg.in/ldap.v2/search.go generated vendored
View File

@ -1,450 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// 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

@ -3,16 +3,13 @@ package main
import ( import (
"bufio" "bufio"
"errors" "errors"
"fmt"
"io" "io"
"log" "log"
"net" "net"
"os" "os"
"strconv"
"strings" "strings"
"sync" "sync"
hibp "github.com/mattevans/pwned-passwords"
"github.com/pyke369/golang-support/rcache" "github.com/pyke369/golang-support/rcache"
) )
@ -22,20 +19,9 @@ type OpenVpnMgt struct {
buf map[string]*bufio.ReadWriter buf map[string]*bufio.ReadWriter
m sync.RWMutex m sync.RWMutex
ret chan []string ret chan []string
ldap map[string]ldapConfig
clients map[string]map[int]*vpnSession
authCa string
vpnlogUrl string
mailRelay string
MailFrom string
CcPwnPassword string
pwnTemplate string
newAsTemplate string
cacheDir string
syslog bool syslog bool
otpMasterSecrets []string
hibpClient *hibp.Client
debug bool debug bool
hold bool
} }
// NewServer returns a pointer to a new server // NewServer returns a pointer to a new server
@ -43,10 +29,7 @@ func NewVPNServer(port string) *OpenVpnMgt {
return &OpenVpnMgt{ return &OpenVpnMgt{
port: port, port: port,
ret: make(chan []string), ret: make(chan []string),
ldap: make(map[string]ldapConfig),
buf: make(map[string]*bufio.ReadWriter), buf: make(map[string]*bufio.ReadWriter),
clients: make(map[string]map[int]*vpnSession),
hibpClient: hibp.NewClient(),
} }
} }
@ -77,16 +60,6 @@ func (s *OpenVpnMgt) Run() {
} }
} }
func (s *OpenVpnMgt) CheckPwn(c *vpnSession) error {
c.LogPrintln("checking pwn password")
pwned, err := s.hibpClient.Pwned.Compromised(c.password)
if err != nil {
return err
}
c.PwnedPasswd = pwned
return nil
}
// send a command to the server. Set the channel to receive the response // send a command to the server. Set the channel to receive the response
func (s *OpenVpnMgt) sendCommand(msg []string, remote string) (error, []string) { func (s *OpenVpnMgt) sendCommand(msg []string, remote string) (error, []string) {
if len(s.buf) == 0 { if len(s.buf) == 0 {
@ -117,23 +90,6 @@ func (s *OpenVpnMgt) sendCommand(msg []string, remote string) (error, []string)
return nil, ret return nil, ret
} }
// send the list of all connected clients
func (s *OpenVpnMgt) Stats() map[string]map[int]*vpnSession {
return s.clients
}
func (s *OpenVpnMgt) Kill(session string, id int, killer string) error {
if _, ok := s.clients[session]; !ok {
return errors.New("unknown session")
}
if _, ok := s.clients[session][id]; !ok {
return errors.New("unknown session id")
}
log.Printf("user %s's session killed from the web by %s\n", s.clients[session][id].Login, killer)
err, _ := s.sendCommand([]string{fmt.Sprintf("client-kill %d", id)}, session)
return err
}
// send the help command on all vpn servers. Kind of useless // send the help command on all vpn servers. Kind of useless
func (s *OpenVpnMgt) Help() (error, map[string]map[string]string) { func (s *OpenVpnMgt) Help() (error, map[string]map[string]string) {
ret := make(map[string]map[string]string) ret := make(map[string]map[string]string)
@ -169,133 +125,15 @@ func (s *OpenVpnMgt) Version() (error, map[string][]string) {
return nil, ret return nil, ret
} }
// called after a client is confirmed connected and authenticated
func (s *OpenVpnMgt) ClientValidated(line, remote string) {
err, c := s.getClient(line, remote)
if err != nil {
log.Println(err, line)
return
}
c.Status = "success"
infos := <-s.ret
if err := c.ParseEnv(s, &infos); err != nil {
log.Println(err)
}
s.Log(c)
}
// called after a client is disconnected, including for auth issues
func (s *OpenVpnMgt) ClientDisconnect(line, remote string) {
err, c := s.getClient(line, remote)
if err != nil {
log.Println(err)
return
}
<-s.ret
// if the disconnect is due to an auth failure, don't change the status
if c.Status == "success" {
c.Operation = "log out"
}
// Don't log the initial auth failure due to absence of OTP code
// And don't log the auth failure during re auth
if c.Operation != "re auth" && c.Status != "Need OTP Code" {
s.Log(c)
}
defer delete(s.clients[remote], c.cID)
}
// called at the initial connexion
func (s *OpenVpnMgt) ClientConnect(line, remote string) {
c := NewVPNSession()
c.vpnserver = remote
c.ParseSessionId(line)
s.clients[remote][c.cID] = c
infos := <-s.ret
if err := c.ParseEnv(s, &infos); err != nil {
log.Println(err)
return
}
c.Auth(s)
}
func (s *OpenVpnMgt) ClientReAuth(line, remote string) {
err, c := s.getClient(line, remote)
if err != nil {
log.Println(err, line)
return
}
c.ParseSessionId(line)
infos := <-s.ret
if err := c.ParseEnv(s, &infos); err != nil {
log.Println(err)
return
}
// reset some values
c.Profile = ""
c.Status = "system failure"
c.Operation = "re auth"
c.Auth(s)
}
// find a client among all registered sessions
func (s *OpenVpnMgt) getClient(line, remote string) (error, *vpnSession) {
re := rcache.Get("^[^0-9]*,([0-9]+)[^0-9]*")
match := re.FindStringSubmatch(line)
if len(match) == 0 {
return errors.New("invalid message"), nil
}
id, err := strconv.Atoi(match[1])
if err != nil {
return err, nil
}
if _, ok := s.clients[remote]; !ok {
return errors.New("unknown vpn server"), nil
}
if c, ok := s.clients[remote][id]; ok {
return nil, c
}
return errors.New("unknown vpn client"), nil
}
// update counters
func (s *OpenVpnMgt) updateCounters(line, remote string) {
p := strings.Split(strings.Replace(line, ":", ",", 1), ",")
err, c := s.getClient(p[0]+","+p[1], remote)
if err != nil {
log.Println(err, line)
return
}
if c.BwWrite, err = strconv.Atoi(p[2]); err != nil {
c.LogPrintln(err)
return
}
if c.BwRead, err = strconv.Atoi(p[3]); err != nil {
c.LogPrintln(err)
return
}
return
}
// main loop for a given openvpn server // main loop for a given openvpn server
func (s *OpenVpnMgt) handleConn(conn net.Conn) { func (s *OpenVpnMgt) handleConn(conn net.Conn) {
remote := conn.RemoteAddr().String() remote := conn.RemoteAddr().String()
defer conn.Close() defer conn.Close()
defer delete(s.buf, remote) defer delete(s.buf, remote)
defer delete(s.clients, remote)
// we store the buffer pointer in the struct, to be accessed from other methods // we store the buffer pointer in the struct, to be accessed from other methods
s.buf[remote] = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) s.buf[remote] = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
s.clients[remote] = make(map[int]*vpnSession)
// most response are multilined, use response to concatenate them // most response are multilined, use response to concatenate them
response := []string{} response := []string{}
@ -311,21 +149,6 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
return return
} }
// ask for statistics
if _, err := s.buf[remote].WriteString("bytecount 30\r\n"); err != nil {
log.Println(err)
return
}
if err := s.buf[remote].Flush(); err != nil {
log.Println(err)
return
}
if line, err := s.buf[remote].ReadString('\n'); err != nil ||
line != "SUCCESS: bytecount interval changed\r\n" {
log.Println("Bogus Client")
return
}
log.Printf("Valid openvpn connected from %s\n", remote) log.Printf("Valid openvpn connected from %s\n", remote)
for { for {
@ -364,27 +187,6 @@ func (s *OpenVpnMgt) handleConn(conn net.Conn) {
// command successfull, we can ignore // command successfull, we can ignore
case strings.HasPrefix(line, ">SUCCESS: client-deny command succeeded"): case strings.HasPrefix(line, ">SUCCESS: client-deny command succeeded"):
// trafic stats
case strings.HasPrefix(line, ">BYTECOUNT_CLI"):
go s.updateCounters(line, remote)
// new bloc for a disconnect event.
// We start the receiving handler, which will wait for the Channel message
case strings.HasPrefix(line, ">CLIENT:DISCONNECT"):
go s.ClientDisconnect(line, remote)
// new bloc for a connect event.
// We start the receiving handler, which will wait for the Channel message
case strings.HasPrefix(line, ">CLIENT:ADDRESS"):
case strings.HasPrefix(line, ">CLIENT:ESTABLISHED"):
go s.ClientValidated(line, remote)
case strings.HasPrefix(line, ">CLIENT:CONNECT"):
go s.ClientConnect(line, remote)
case strings.HasPrefix(line, ">CLIENT:REAUTH"):
go s.ClientReAuth(line, remote)
default: default:
response = append(response, line) response = append(response, line)
} }

View File

@ -1,317 +0,0 @@
package main
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/pyke369/golang-support/rcache"
"os"
"os/exec"
"strconv"
"strings"
"time"
)
type vpnSession struct {
Time time.Time `json:"time"`
Login string `json:"username"`
Operation string `json:"operation"`
Status string `json:"status"`
Profile string `json:"profile"`
TwoFA bool `json:"2fa_auth"`
IP string `json:"client_ip"`
PrivIP string `json:"private_ip"`
AsNumber string `json:"as_number"`
AsName string `json:"as_name"`
NewAS bool `json:"as_new"`
PwnedPasswd bool `json:"pwned_passwd"`
Hostname string `json:"hostname"`
TooMuchPwn bool `json:"too_much_pwn"`
BwRead int `json:"in_bytes"`
BwWrite int `json:"out_bytes"`
Mail string `json:"-"`
cID int `json:"-"`
kID int `json:"-"`
port int `json:"-"`
dev string `json:"-"`
netmask string `json:"-"`
password string `json:"-"`
otpCode string `json:"-"`
localIP string `json:"-"`
vpnserver string `json:"-"`
pwnMail string `json:"-"`
newAsMail string `json:"-"`
MailFrom string `json:"-"`
CcPwnPassword string `json:"-"`
}
func NewVPNSession() *vpnSession {
v := vpnSession{
Time: time.Now().Round(time.Second),
Status: "system failure",
Operation: "log in",
}
v.Hostname, _ = os.Hostname()
return &v
}
func (c *vpnSession) String() string {
if res, err := json.MarshalIndent(c, " ", " "); err == nil {
return string(res)
}
return ""
}
func (c *vpnSession) b64Login() string {
return base64.StdEncoding.EncodeToString([]byte(c.Login))
}
func (c *vpnSession) baseHash(salt string, i int64) string {
return fmt.Sprintf("%s%s%s%s", salt, c.Login, c.IP, i)
}
func (c *vpnSession) AddRoute(ip string) error {
var cmd *exec.Cmd
if os.Geteuid() == 0 {
cmd = exec.Command("/bin/ip", "route", "replace", ip+"/32", "dev", c.dev)
} else {
cmd = exec.Command("/usr/bin/sudo", "/bin/ip", "route", "replace", ip+"/32", "dev", c.dev)
}
return cmd.Run()
}
func (c *vpnSession) ParseSessionId(line string) error {
var err error
re := rcache.Get("^>CLIENT:[^,]*,([0-9]+),([0-9]+)$")
match := re.FindStringSubmatch(line)
if len(match) == 0 {
return errors.New("invalid message")
}
if c.cID, err = strconv.Atoi(match[1]); err != nil {
return err
}
if c.kID, err = strconv.Atoi(match[2]); err != nil {
return err
}
return nil
}
func (c *vpnSession) ParseEnv(s *OpenVpnMgt, infos *[]string) error {
var err error
r := rcache.Get("[^a-zA-Z0-9./_@-]")
renv := rcache.Get("^>CLIENT:ENV,([^=]*)=(.*)$")
for _, line := range *infos {
p := renv.FindStringSubmatch(line)
if len(p) != 3 {
continue
}
switch p[1] {
case "trusted_port":
if c.port, err = strconv.Atoi(r.ReplaceAllString(p[2], "")); err != nil {
return err
}
case "untrusted_port":
if c.port, err = strconv.Atoi(r.ReplaceAllString(p[2], "")); err != nil {
return err
}
case "trusted_ip":
c.IP = r.ReplaceAllString(p[2], "")
case "untrusted_ip":
c.IP = r.ReplaceAllString(p[2], "")
case "ifconfig_pool_remote_ip":
c.PrivIP = r.ReplaceAllString(p[2], "")
case "ifconfig_local":
c.localIP = r.ReplaceAllString(p[2], "")
case "bytes_received":
if c.BwWrite, err = strconv.Atoi(p[2]); err != nil {
break
}
case "bytes_sent":
if c.BwRead, err = strconv.Atoi(p[2]); err != nil {
break
}
case "password":
switch {
case strings.HasPrefix(p[2], "CRV1"):
split := strings.Split(p[2], ":")
if len(split) != 5 {
break
}
c.password = split[2]
c.otpCode = split[4]
if c.otpCode == "" {
c.otpCode = "***"
}
// don't check that password against the ibp database
case strings.HasPrefix(p[2], "SCRV1"):
split := strings.Split(p[2], ":")
if len(split) != 3 {
break
}
data, err := base64.StdEncoding.DecodeString(split[1])
if err != nil {
break
}
c.password = string(data)
data, err = base64.StdEncoding.DecodeString(split[2])
if err != nil {
c.password = p[2]
break
}
c.otpCode = string(data)
if c.otpCode == "" {
c.otpCode = "***"
}
// only check if the password is pwned on the first connection
if c.Operation == "log in" {
go s.CheckPwn(c)
}
default:
c.password = p[2]
c.otpCode = ""
// only check if the password is pwned on the first connection
if c.Operation == "log in" {
go s.CheckPwn(c)
}
}
case "username":
c.Login = strings.ToLower(r.ReplaceAllString(p[2], ""))
case "dev":
c.dev = r.ReplaceAllString(p[2], "")
case "ifconfig_netmask":
c.netmask = r.ReplaceAllString(p[2], "")
}
}
return nil
}
func (c *vpnSession) Auth(s *OpenVpnMgt) {
var cmd []string
var ip string
var errIP error
err, ok := c.auth(s)
// if auth is ok, time to get an IP address
if ok == 0 && c.PrivIP == "" {
ip, errIP = s.getIP(c)
if errIP != nil {
ok = -10
err = errIP
} else {
if err := c.AddRoute(ip); err != nil {
c.LogPrintln(err)
}
}
}
switch {
case ok == 0:
cmd = []string{
fmt.Sprintf("client-auth %d %d", c.cID, c.kID),
}
if c.netmask == "255.255.255.255" {
cmd = append(cmd, fmt.Sprintf("ifconfig-push %s %s", ip, c.localIP))
} else {
cmd = append(cmd, fmt.Sprintf("ifconfig-push %s %s", ip, c.netmask))
}
for _, r := range s.ldap[c.Profile].routes {
cmd = append(cmd, fmt.Sprintf("push \"route %s vpn_gateway\"", r))
}
cmd = append(cmd, "END")
c.Status = "success"
case ok < 0:
cmd = []string{fmt.Sprintf("client-deny %d %d \"%s\" \"%s\"",
c.cID, c.kID, err, err)}
case ok == 1:
cmd = []string{fmt.Sprintf(
"client-deny %d %d \"Need OTP\" \"CRV1:R,E:%s:%s:OTP Code \"",
c.cID, c.kID, c.password, c.b64Login())}
}
if err, _ := s.sendCommand(cmd, c.vpnserver); err != nil {
c.LogPrintln(err)
}
return
}
// main authentication function.
// returns 0 if auth is valid
// returns 1 if an TOTP code is necessary
// returns a negative if auth is not valid
func (c *vpnSession) auth(s *OpenVpnMgt) (error, int) {
// an empty password is not good
if c.password == "" {
c.Status = "Empty Password"
return errors.New("Empty Password"), -1
}
// check if the password is a valid token (see TOTP request)
tokenPasswordOk, tokenPassword := s.TokenPassword(c)
// password is a token. We remove it from the session object to
// avoid checking it against the ldap
if tokenPasswordOk {
c.password = ""
}
otpSalt := ""
c.Profile, c.Mail, otpSalt, _ = s.AuthLoop("", c.Login, c.password, tokenPasswordOk)
// no profile validated, we stop here
if c.Profile == "" {
c.Status = "fail (password)"
return errors.New("Authentication Failed"), -3
}
// if the otp is not empty, we check it against the valid codes as soon as
// possible
otpvalidated := false
if c.otpCode != "" {
codes, err := s.GenerateOTP(c.Login + otpSalt)
if err != nil {
return err, -2
}
for _, possible := range codes {
if possible == c.otpCode {
otpvalidated = true
}
}
}
// check the MFA requested by the secured profile
c.TwoFA = true
switch s.ldap[c.Profile].mfaType {
case "internal":
if otpvalidated {
return nil, 0
}
// log that the failure is due to the OTP
if c.otpCode == "" {
c.Status = "Need OTP Code"
} else {
c.Status = "fail (OTP) : "
}
c.password = tokenPassword
return errors.New("Need OTP Code"), 1
case "okta":
//TODO implement okta MFA
c.Status = "fail (Okta)"
return nil, -4
default:
c.TwoFA = false
}
// no MFA requested, the login is valid
return nil, 0
}