Compare commits
23 commits
main
...
import_gol
Author | SHA1 | Date | |
---|---|---|---|
|
5f0f6a730a | ||
|
7c8bd455e6 | ||
|
2f09e10933 | ||
|
49fcbcae7c | ||
|
3f8e633045 | ||
|
e4778a9a89 | ||
|
eb7f00c526 | ||
|
a9b04a255f | ||
|
3e658ac201 | ||
|
a9d9484f23 | ||
|
974d8806d8 | ||
|
986ef6d59d | ||
|
09512a271d | ||
|
139a40bdf3 | ||
|
bfd12a48c4 | ||
|
aeac8faf15 | ||
|
2f350965ce | ||
|
5fbe39c38a | ||
|
f93f9e6958 | ||
|
f294b66874 | ||
|
32cce4c4d3 | ||
|
9e4b4a0162 | ||
|
75f4a916c3 |
33 changed files with 560 additions and 1464 deletions
26
.drone.yml
Normal file
26
.drone.yml
Normal file
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
kind: pipeline
|
||||
name: bottin
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: golang:stretch
|
||||
commands:
|
||||
- go get -d -v
|
||||
- go build -v
|
||||
- cd test_automatic
|
||||
- go get -d -v
|
||||
- go build -v
|
||||
|
||||
- name: test_bottin
|
||||
image: consul:latest
|
||||
environment:
|
||||
BOTTIN_DEFAULT_ADMIN_PW: priZ4Cg0x5NkSyiIN/MpvWw4ZEy8f8s1
|
||||
commands:
|
||||
- ash test_automatic/start_test.sh
|
||||
|
||||
---
|
||||
kind: signature
|
||||
hmac: a4455c124ee87ca8b0ef1779560703573f3a3f24d406e4cb281b9e0dab4ceeda
|
||||
|
||||
...
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,6 +1,4 @@
|
|||
bottin
|
||||
bottin.static
|
||||
config.json
|
||||
test/test
|
||||
result
|
||||
ldap.json
|
||||
test_automatic/integration
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
steps:
|
||||
- name: build
|
||||
image: golang:stretch
|
||||
commands:
|
||||
- go get -d -v
|
||||
- go build -v
|
||||
- cd test
|
||||
- go test -i -c -o test
|
||||
|
||||
- name: test_bottin
|
||||
image: consul:1.15.4
|
||||
environment:
|
||||
BOTTIN_DEFAULT_ADMIN_PW: priZ4Cg0x5NkSyiIN/MpvWw4ZEy8f8s1
|
||||
commands:
|
||||
- ash test/runner.sh
|
5
Dockerfile
Normal file
5
Dockerfile
Normal file
|
@ -0,0 +1,5 @@
|
|||
FROM scratch
|
||||
|
||||
ADD bottin.static /bottin
|
||||
|
||||
ENTRYPOINT ["/bottin"]
|
15
README.md
15
README.md
|
@ -1,6 +1,6 @@
|
|||
# Bottin
|
||||
|
||||
[![status-badge](https://woodpecker.deuxfleurs.fr/api/badges/38/status.svg)](https://woodpecker.deuxfleurs.fr/repos/38)
|
||||
[![Build Status](https://drone.deuxfleurs.fr/api/badges/Deuxfleurs/bottin/status.svg?ref=refs/heads/main)](https://drone.deuxfleurs.fr/Deuxfleurs/bottin)
|
||||
|
||||
<img src="https://git.deuxfleurs.fr/Deuxfleurs/bottin/raw/branch/main/bottin.png" style="height: 150px; display: block; margin-left: auto; margin-right: auto" />
|
||||
|
||||
|
@ -24,7 +24,7 @@ Features:
|
|||
- Access control through an ACL (hardcoded in the configuration file)
|
||||
|
||||
|
||||
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/dxflrs/bottin) (built in `default.nix`).
|
||||
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/lxpz/bottin_amd64).
|
||||
An example for running Bottin on a Nomad cluster can be found in `bottin.hcl.example`.
|
||||
|
||||
Bottin takes a single command line argument, `-config <filename>`, which is the
|
||||
|
@ -44,17 +44,6 @@ Bottin requires go 1.13 or later.
|
|||
To build Bottin, clone this repository outside of your `$GOPATH`.
|
||||
Then, run `make` in the root of the repo.
|
||||
|
||||
## Releasing
|
||||
|
||||
```bash
|
||||
nix-build -A bin
|
||||
nix-build -A docker
|
||||
```
|
||||
|
||||
```bash
|
||||
docker load < $(nix-build -A docker)
|
||||
docker push dxflrs/bottin:???
|
||||
```
|
||||
|
||||
## Server initialization
|
||||
|
||||
|
|
55
default.nix
55
default.nix
|
@ -1,55 +0,0 @@
|
|||
let
|
||||
pkgsSrc = fetchTarball {
|
||||
# As of 2022-07-19
|
||||
url = "https://github.com/NixOS/nixpkgs/archive/d2db10786f27619d5519b12b03fb10dc8ca95e59.tar.gz";
|
||||
sha256 = "0s9gigs3ylnq5b94rfcmxvrmmr3kzhs497gksajf638d5bv7zcl5";
|
||||
};
|
||||
gomod2nix = fetchGit {
|
||||
url = "https://github.com/tweag/gomod2nix.git";
|
||||
ref = "master";
|
||||
rev = "40d32f82fc60d66402eb0972e6e368aeab3faf58";
|
||||
};
|
||||
|
||||
pkgs = import pkgsSrc {
|
||||
overlays = [
|
||||
(self: super: {
|
||||
gomod = super.callPackage "${gomod2nix}/builder/" { };
|
||||
})
|
||||
];
|
||||
};
|
||||
in rec {
|
||||
bin = pkgs.gomod.buildGoApplication {
|
||||
pname = "bottin-bin";
|
||||
version = "0.1.0";
|
||||
src = builtins.filterSource
|
||||
(path: type: (builtins.match ".*/test/.*\\.(go|sum|mod)" path) == null)
|
||||
./.;
|
||||
modules = ./gomod2nix.toml;
|
||||
|
||||
CGO_ENABLED=0;
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
description = "A cloud-native LDAP server backed by a Consul datastore";
|
||||
homepage = "https://git.deuxfleurs.fr/Deuxfleurs/bottin";
|
||||
license = licenses.gpl3Plus;
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
};
|
||||
pkg = pkgs.stdenv.mkDerivation {
|
||||
pname = "bottin";
|
||||
version = "0.1.0";
|
||||
unpackPhase = "true";
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/
|
||||
cp ${bin}/bin/bottin $out/bottin
|
||||
'';
|
||||
};
|
||||
docker = pkgs.dockerTools.buildImage {
|
||||
name = "dxflrs/bottin";
|
||||
config = {
|
||||
Entrypoint = [ "${pkg}/bottin" ];
|
||||
WorkingDir = "/";
|
||||
};
|
||||
};
|
||||
}
|
79
flake.lock
79
flake.lock
|
@ -1,79 +0,0 @@
|
|||
{
|
||||
"nodes": {
|
||||
"gomod2nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"utils": "utils"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1655245309,
|
||||
"narHash": "sha256-d/YPoQ/vFn1+GTmSdvbSBSTOai61FONxB4+Lt6w/IVI=",
|
||||
"owner": "tweag",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "40d32f82fc60d66402eb0972e6e368aeab3faf58",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "tweag",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "40d32f82fc60d66402eb0972e6e368aeab3faf58",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1653581809,
|
||||
"narHash": "sha256-Uvka0V5MTGbeOfWte25+tfRL3moECDh1VwokWSZUdoY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "83658b28fe638a170a19b8933aa008b30640fbd1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1669764884,
|
||||
"narHash": "sha256-1qWR/5+WtqxSedrFbUbM3zPMO7Ec2CGWaxtK4z4DdvY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0244e143dc943bcf661fdaf581f01eb0f5000fcf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0244e143dc943bcf661fdaf581f01eb0f5000fcf",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"gomod2nix": "gomod2nix",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
"locked": {
|
||||
"lastModified": 1653893745,
|
||||
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
39
flake.nix
39
flake.nix
|
@ -1,39 +0,0 @@
|
|||
{
|
||||
description = "A cloud-native LDAP server backed by a Consul datastore";
|
||||
|
||||
inputs.nixpkgs.url = "github:nixos/nixpkgs/0244e143dc943bcf661fdaf581f01eb0f5000fcf";
|
||||
inputs.gomod2nix.url = "github:tweag/gomod2nix/40d32f82fc60d66402eb0972e6e368aeab3faf58";
|
||||
|
||||
outputs = { self, nixpkgs, gomod2nix }:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
system = "x86_64-linux";
|
||||
overlays = [
|
||||
(self: super: {
|
||||
gomod = super.callPackage "${gomod2nix}/builder/" { };
|
||||
})
|
||||
];
|
||||
};
|
||||
bottin = pkgs.gomod.buildGoApplication {
|
||||
pname = "bottin";
|
||||
version = "0.1.0";
|
||||
src = builtins.filterSource
|
||||
(path: type: (builtins.match ".*/test/.*\\.(go|sum|mod)" path) == null)
|
||||
./.;
|
||||
modules = ./gomod2nix.toml;
|
||||
|
||||
CGO_ENABLED=0;
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
description = "A cloud-native LDAP server backed by a Consul datastore";
|
||||
homepage = "https://git.deuxfleurs.fr/Deuxfleurs/bottin";
|
||||
license = licenses.gpl3Plus;
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
packages.x86_64-linux.bottin = bottin;
|
||||
packages.x86_64-linux.default = self.packages.x86_64-linux.bottin;
|
||||
};
|
||||
}
|
4
go.mod
4
go.mod
|
@ -3,9 +3,9 @@ module bottin
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/go-ldap/ldap/v3 v3.3.0
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/hashicorp/consul/api v1.3.0
|
||||
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect
|
||||
)
|
||||
|
|
14
go.sum
14
go.sum
|
@ -1,3 +1,5 @@
|
|||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
|
@ -7,6 +9,11 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-ldap/ldap v2.5.1+incompatible h1:Opaoft5zMW8IU/VRULB0eGMBQ9P5buRvCW6sFTRmMn8=
|
||||
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
|
||||
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
|
@ -42,10 +49,10 @@ github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG67
|
|||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73 h1:ZhC4QngptYaGx53+ph1RjxcH8fkCozBaY+935TNX4i8=
|
||||
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73/go.mod h1:t0Q9JvoMTfTYdAWIk2MF69iz+Qpdk9D+PgVu6fVmaDI=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 h1:wIONC+HMNRqmWBjuMxhatuSzHaljStc4gjDeKycxy0A=
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3/go.mod h1:37YR9jabpiIxsb8X9VCIx8qFOjTDIIrIHHODa8C4gz0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
||||
|
@ -77,13 +84,14 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
|
@ -223,33 +223,19 @@ func parseInt64(bytes []byte) (ret int64, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func sizeInt64(i int64) int {
|
||||
n := 1
|
||||
|
||||
for i > 127 {
|
||||
n++
|
||||
i >>= 8
|
||||
func sizeInt64(i int64) (size int) {
|
||||
for ; i != 0 || size == 0; i >>= 8 {
|
||||
size++
|
||||
}
|
||||
|
||||
for i < -128 {
|
||||
n++
|
||||
i >>= 8
|
||||
}
|
||||
|
||||
return n
|
||||
return
|
||||
}
|
||||
|
||||
func writeInt64(bytes *Bytes, i int64) int {
|
||||
n := sizeInt64(i)
|
||||
buf := [8]byte{}
|
||||
|
||||
for j := 0; j < n; j++ {
|
||||
b := i >> uint((n-1-j)*8)
|
||||
buf[j] = byte(b)
|
||||
func writeInt64(bytes *Bytes, i int64) (size int) {
|
||||
for ; i != 0 || size == 0; i >>= 8 { // Write at least one byte even if the value is 0
|
||||
bytes.writeBytes([]byte{byte(i)})
|
||||
size++
|
||||
}
|
||||
bytes.writeBytes(buf[:n])
|
||||
|
||||
return n
|
||||
return
|
||||
}
|
||||
|
||||
// parseInt treats the given bytes as a big-endian, signed integer and returns
|
||||
|
@ -727,13 +713,7 @@ func writeTagAndLength(bytes *Bytes, t TagAndLength) (size int) {
|
|||
panic("Can't have a negative length")
|
||||
|
||||
} else if t.Length >= 128 {
|
||||
lengthBytes := 0
|
||||
val := t.Length
|
||||
for val > 0 {
|
||||
lengthBytes++
|
||||
bytes.writeBytes([]byte{byte(val & 0xff)})
|
||||
val >>= 8
|
||||
}
|
||||
lengthBytes := writeInt64(bytes, int64(t.Length))
|
||||
bytes.writeBytes([]byte{byte(0x80 | byte(lengthBytes))})
|
||||
size += lengthBytes + 1
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
package message
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
func TestSizeInt64(t *testing.T) {
|
||||
s := sizeInt64(0)
|
||||
if s != 1 {
|
||||
t.Errorf("computed size is %d, expected 1", s)
|
||||
}
|
||||
|
||||
s = sizeInt64(127)
|
||||
if s != 1 {
|
||||
t.Errorf("computed size is %d, expected 1", s)
|
||||
}
|
||||
|
||||
s = sizeInt64(128)
|
||||
if s != 2 {
|
||||
t.Errorf("computed size is %d, expected 2", s)
|
||||
}
|
||||
|
||||
s = sizeInt64(50000)
|
||||
if s != 3 {
|
||||
t.Errorf("computed size is %d, expected 3", s)
|
||||
}
|
||||
|
||||
s = sizeInt64(-12345)
|
||||
if s != 2 {
|
||||
t.Errorf("computed size is %d, expected 2", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteInt64(t *testing.T) {
|
||||
vtests := []int64{0, 127, 128, 50000, -12345}
|
||||
expsize := []int{1, 1, 2, 3, 2}
|
||||
expresult := [][]byte{{0x00}, {0x7F}, {0x00, 0x80}, {0x00, 0xc3, 0x50}, {0xcf, 0xc7}}
|
||||
|
||||
for idx, v := range vtests {
|
||||
fs := sizeInt64(v)
|
||||
b := NewBytes(fs, make([]byte, fs))
|
||||
t.Log("computing", v)
|
||||
s := writeInt64(b, v)
|
||||
if s != expsize[idx] {
|
||||
t.Errorf("computed size is %d, expected %d", s, expsize[idx])
|
||||
}
|
||||
if !bytes.Equal(b.Bytes(), expresult[idx]) {
|
||||
t.Errorf("wrong computed bytes, got %v, expected %v", b.Bytes(), expresult[idx])
|
||||
}
|
||||
a, e := parseInt64(b.Bytes())
|
||||
t.Log("parse", a, e)
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package message
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func toHex(b []byte) (r string) {
|
||||
r = "[ "
|
||||
for _, e := range b {
|
||||
r += fmt.Sprintf("0x%x ", e)
|
||||
}
|
||||
return r + "]"
|
||||
}
|
||||
|
||||
func TestMessageID(t *testing.T) {
|
||||
m := NewLDAPMessageWithProtocolOp(UnbindRequest{})
|
||||
m.SetMessageID(128)
|
||||
buf, err := m.Write()
|
||||
if err != nil {
|
||||
t.Errorf("marshalling failed with %v", err)
|
||||
}
|
||||
t.Logf("%v", toHex(buf.Bytes()))
|
||||
|
||||
ret, err := ReadLDAPMessage(NewBytes(0, buf.Bytes()))
|
||||
if err != nil {
|
||||
t.Errorf("unmarshalling failed with %v", err)
|
||||
}
|
||||
if _, ok := ret.ProtocolOp().(UnbindRequest); !ok {
|
||||
t.Errorf("should be an unbind request")
|
||||
}
|
||||
if ret.MessageID() != 128 {
|
||||
t.Errorf("Expect message id 128, got %d", ret.MessageID())
|
||||
}
|
||||
t.Log("Done, marshal/unmarshall worked")
|
||||
}
|
||||
|
||||
func TestSearchEntry(t *testing.T) {
|
||||
m := NewLDAPMessageWithProtocolOp(SearchResultEntry{
|
||||
objectName:"cn=êige€nbgtz,ou=users,dc=deuxfleurs,dc=fr",
|
||||
attributes: PartialAttributeList{
|
||||
PartialAttribute{
|
||||
type_:"displayname",
|
||||
vals:[]AttributeValue{"êiGe€NBgTZ"},
|
||||
},
|
||||
PartialAttribute{
|
||||
type_:"objectclass",
|
||||
vals:[]AttributeValue{"inetOrgPerson"},
|
||||
},
|
||||
PartialAttribute{
|
||||
type_:"objectclass",
|
||||
vals:[]AttributeValue{"organizationalPerson"},
|
||||
},
|
||||
PartialAttribute{
|
||||
type_:"objectclass",
|
||||
vals:[]AttributeValue{"person"},
|
||||
},
|
||||
PartialAttribute{
|
||||
type_:"objectclass",
|
||||
vals:[]AttributeValue{"top"},
|
||||
},
|
||||
PartialAttribute{
|
||||
type_:"structuralobjectclass",
|
||||
vals:[]AttributeValue{"inetOrgPerson"},
|
||||
},
|
||||
},
|
||||
})
|
||||
m.SetMessageID(24)
|
||||
buf, err := m.Write()
|
||||
if err != nil {
|
||||
t.Errorf("marshalling failed with %v", err)
|
||||
}
|
||||
if buf.Bytes()[0] != 0x30 {
|
||||
t.Logf("Malformed message: %v", toHex(buf.Bytes()))
|
||||
}
|
||||
}
|
153
gomod2nix.toml
153
gomod2nix.toml
|
@ -1,153 +0,0 @@
|
|||
schema = 3
|
||||
|
||||
[mod]
|
||||
[mod."github.com/armon/circbuf"]
|
||||
version = "v0.0.0-20150827004946-bbbad097214e"
|
||||
hash = "sha256-klQjllsJZqZ2KPNx1mZT9XP+UAJkuBhmTnZdNlAflEM="
|
||||
[mod."github.com/armon/go-metrics"]
|
||||
version = "v0.0.0-20180917152333-f0300d1749da"
|
||||
hash = "sha256-+zqX1hlJgc+IrXRzBQDMhR8GYQdc0Oj6PiIDfctgh44="
|
||||
[mod."github.com/armon/go-radix"]
|
||||
version = "v0.0.0-20180808171621-7fddfc383310"
|
||||
hash = "sha256-ZHU4pyBqHHRuQJuYr2K+LqeAnLX9peX07cmSYK+GDHk="
|
||||
[mod."github.com/bgentry/speakeasy"]
|
||||
version = "v0.1.0"
|
||||
hash = "sha256-Gt1vj6CFovLnO6wX5u2O4UfecY9V2J9WGw1ez4HMrgk="
|
||||
[mod."github.com/davecgh/go-spew"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI="
|
||||
[mod."github.com/fatih/color"]
|
||||
version = "v1.7.0"
|
||||
hash = "sha256-4In7ef7it7d+6oGUJ3pkD0V+lsL40hVtYdy2KD2ovn0="
|
||||
[mod."github.com/google/btree"]
|
||||
version = "v0.0.0-20180813153112-4030bb1f1f0c"
|
||||
hash = "sha256-5gr0RMnlvrzCke3kwpkf92WvW3x5nnKZesoulyoYRC0="
|
||||
[mod."github.com/google/uuid"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-66PXC/RCPUyhS9PhkIPQFR3tbM2zZYDNPGXN7JJj3UE="
|
||||
[mod."github.com/hashicorp/consul/api"]
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-fMORNFAWK2GkRbBl/KzNHrvtCfwz/7P66HzDaE4GnuE="
|
||||
[mod."github.com/hashicorp/consul/sdk"]
|
||||
version = "v0.3.0"
|
||||
hash = "sha256-lF47JPGfmeGjpuQw9VSNs2Mi+G7FQLKrrHteX3iXfpU="
|
||||
[mod."github.com/hashicorp/errwrap"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-LGSLrefkABG1kH1i+GUWiD2/ggJxiZEJ+D2YNbhZjmo="
|
||||
[mod."github.com/hashicorp/go-cleanhttp"]
|
||||
version = "v0.5.1"
|
||||
hash = "sha256-c54zcHr9THj3MQk7hrDQcpjOcQi1MvXZ4Kpin6EbfR4="
|
||||
[mod."github.com/hashicorp/go-immutable-radix"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-JmNxdGaJG63Ty/sVnPjqvTyA4/k5wkzZ/QvpMK2uduw="
|
||||
[mod."github.com/hashicorp/go-msgpack"]
|
||||
version = "v0.5.3"
|
||||
hash = "sha256-2OUYjD/Jt12TFBrtH0wRqg+lzRljDxSIhk2CqBLUczo="
|
||||
[mod."github.com/hashicorp/go-multierror"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-iXzjerl96o7QDiSwQjbak8R/t+YzZeoUqm59TCmy3gI="
|
||||
[mod."github.com/hashicorp/go-rootcerts"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-4NZJAT5/vocyto+dv6FmW4kFiYldmNvewowsYK/LiTI="
|
||||
[mod."github.com/hashicorp/go-sockaddr"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-orG+SHVsp5lgNRCErmhMLABVFQ3ZWfYIJ/4LTFzlvao="
|
||||
[mod."github.com/hashicorp/go-syslog"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-YRuq6oPMwAFVY7mvwpMDvZqGwNnb5CjBYyKI/x5mbCc="
|
||||
[mod."github.com/hashicorp/go-uuid"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-s1wIvBu37z4U3qK9sdUR1CtbD39N6RwfX4HgDCpCa0s="
|
||||
[mod."github.com/hashicorp/go.net"]
|
||||
version = "v0.0.1"
|
||||
hash = "sha256-JKal3E+wPO+Hk838opKV4HHKB4O72Xy+77ncXlLkWRk="
|
||||
[mod."github.com/hashicorp/golang-lru"]
|
||||
version = "v0.5.0"
|
||||
hash = "sha256-Lo0UyOZQ89iK0Ui3Hfk5VkWqxEzLw6Lclq4EM8VlYoo="
|
||||
[mod."github.com/hashicorp/logutils"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-e8t8Dm8sp/PzKClN1TOmFcrTAWNh4mZHSW7cAjVx3Bw="
|
||||
[mod."github.com/hashicorp/mdns"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-ravx4tklQG43OEjPiJn68iJM9ODZ6hgU0idFCEOiJGM="
|
||||
[mod."github.com/hashicorp/memberlist"]
|
||||
version = "v0.1.3"
|
||||
hash = "sha256-IsxqevYulPt+2VGtlq068Jyq1YfIk4Ohh9TgakIGxnc="
|
||||
[mod."github.com/hashicorp/serf"]
|
||||
version = "v0.8.2"
|
||||
hash = "sha256-diRxWOouFLTO75f2E9NlrKgie/qsT+gOOrrbf4tACHw="
|
||||
[mod."github.com/jsimonetti/pwscheme"]
|
||||
version = "v0.0.0-20220125093853-4d9895f5db73"
|
||||
hash = "sha256-YF3RKU/4CWvLPgGzUd7Hk/2+41OUFuRWZgzQuqcsKg0="
|
||||
[mod."github.com/konsorten/go-windows-terminal-sequences"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-Nwp+Cza9dIu3ogVGip6wyOjWwwaq+2hU3eYIe4R7kNE="
|
||||
[mod."github.com/mattn/go-colorable"]
|
||||
version = "v0.0.9"
|
||||
hash = "sha256-fVPF8VxbdggLAZnaexMl6Id1WjXKImzVySxKfa+ukts="
|
||||
[mod."github.com/mattn/go-isatty"]
|
||||
version = "v0.0.3"
|
||||
hash = "sha256-9ogEEd8/kl/dImldzdBmTFdepvB0dVXEzN4o8bEqhBs="
|
||||
[mod."github.com/miekg/dns"]
|
||||
version = "v1.0.14"
|
||||
hash = "sha256-OeijUgBaEmDapclTxfvjIqrjh4qZu3+DQpHelGGI4aA="
|
||||
[mod."github.com/mitchellh/cli"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-4nG7AhRcjTRCwUCdnaNaFrAKDxEEoiihaCA4lk+uM8U="
|
||||
[mod."github.com/mitchellh/go-homedir"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-eGmBNNTuDSMwicjTNgB54IEJuK1tgOLDJ3NHTpQCHzg="
|
||||
[mod."github.com/mitchellh/go-testing-interface"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-/Dpv/4i5xuK8hDH+q8YTdF6Jg6NNtfO4Wqig2JCWgrY="
|
||||
[mod."github.com/mitchellh/gox"]
|
||||
version = "v0.4.0"
|
||||
hash = "sha256-GV3LYxzJt8YVbnSac2orlj2QR3MX/YIDrLkSkPhsjuA="
|
||||
[mod."github.com/mitchellh/iochan"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-b5Tp7cw/e8mL++IjsebbmKWXtb9Hrzu4Fc6M4tZKFhU="
|
||||
[mod."github.com/mitchellh/mapstructure"]
|
||||
version = "v1.1.2"
|
||||
hash = "sha256-OU9HZYHtl0qaqMFd84w7snkkRuRY6UMSsfCnL5HYdw0="
|
||||
[mod."github.com/pascaldekloe/goe"]
|
||||
version = "v0.0.0-20180627143212-57f6aae5913c"
|
||||
hash = "sha256-2KUjqrEC/BwkTZRxImazcI/C3H7QmXfNrlt8slwdDbc="
|
||||
[mod."github.com/pkg/errors"]
|
||||
version = "v0.8.1"
|
||||
hash = "sha256-oe3iddfoLRwpC3ki5fifHf2ZFprtg99iNak50shiuDw="
|
||||
[mod."github.com/pmezard/go-difflib"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA="
|
||||
[mod."github.com/posener/complete"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-heyPMSBzVlx7ZKgTyzl/xmUfZw3EZCcvGFGrRMIbIr8="
|
||||
[mod."github.com/ryanuber/columnize"]
|
||||
version = "v0.0.0-20160712163229-9b3edd62028f"
|
||||
hash = "sha256-RLUQcU6Z03upKe08v6rjn9/tkyrQsgmpdEmBtWaLQfk="
|
||||
[mod."github.com/sean-/seed"]
|
||||
version = "v0.0.0-20170313163322-e2103e2c3529"
|
||||
hash = "sha256-RQQTjvf8Y91jP5FGOyEnGMFw7zCrcSnUU4eH2CXKkT4="
|
||||
[mod."github.com/sirupsen/logrus"]
|
||||
version = "v1.4.2"
|
||||
hash = "sha256-3QzWUsapCmg3F7JqUuINT3/UG097uzLff6iCcCgQ43o="
|
||||
[mod."github.com/stretchr/objx"]
|
||||
version = "v0.1.1"
|
||||
hash = "sha256-HdGVZCuy7VprC5W9UxGbDmXqsKADMjpEDht7ilGVLco="
|
||||
[mod."github.com/stretchr/testify"]
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-+mSebBNccNcxbY462iKTNTWmd5ZuUkUqFebccn3EtIA="
|
||||
[mod."golang.org/x/crypto"]
|
||||
version = "v0.0.0-20200604202706-70a84ac30bf9"
|
||||
hash = "sha256-I5ov2lV6ubB3H3pn6DFN1VPLyxeLfPUaOx2PJ9K1KH8="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.0.0-20190404232315-eb5bcb51f2a3"
|
||||
hash = "sha256-kwP+BDfPsZEeeceyg4H5LgdTDT8O8YWbkpCkpyuPJJo="
|
||||
[mod."golang.org/x/sync"]
|
||||
version = "v0.0.0-20181221193216-37e7f081c4d4"
|
||||
hash = "sha256-FUpv9bmGLDEjCxXdvR0CRDqOKRY0xrHRPzOsyQyvYK0="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.0.0-20190422165155-953cdadca894"
|
||||
hash = "sha256-YkQjSZaiVFxrmXYSKmqrGeIO3RZ95fc3dWyCaR+SosY="
|
||||
[mod."golang.org/x/text"]
|
||||
version = "v0.3.0"
|
||||
hash = "sha256-0FFbaxF1ZuAQF3sCcA85e8MO6prFeHint36inija4NY="
|
|
@ -206,10 +206,7 @@ func (c *client) close() {
|
|||
}
|
||||
|
||||
func (c *client) writeMessage(m *ldap.LDAPMessage) {
|
||||
data, err := m.Write()
|
||||
if err != nil {
|
||||
Logger.Errorf("bottin: unable to marshal response message: %v", err)
|
||||
}
|
||||
data, _ := m.Write()
|
||||
|
||||
//Logger.Printf(">>> %d - %s - hex=%x", c.Numero, m.ProtocolOpName(), data.Bytes())
|
||||
Logger.Tracef(">>> [%d] %#v", c.Numero, m)
|
||||
|
|
41
main.go
41
main.go
|
@ -12,10 +12,10 @@ import (
|
|||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
message "bottin/goldap"
|
||||
ldap "bottin/ldapserver"
|
||||
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
message "bottin/goldap"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -320,6 +320,7 @@ func (server *Server) init() error {
|
|||
return err
|
||||
}
|
||||
|
||||
|
||||
admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
|
||||
if !environnement_variable_exist {
|
||||
admin_pass := make([]byte, 8)
|
||||
|
@ -328,15 +329,11 @@ func (server *Server) init() error {
|
|||
return err
|
||||
}
|
||||
admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass)
|
||||
} else {
|
||||
server.logger.Debug("BOTTIN_DEFAULT_ADMIN_PW environment variable is set, using it for admin's password")
|
||||
} else {
|
||||
server.logger.Printf("It seems that exists a password in environnement variable")
|
||||
}
|
||||
|
||||
admin_pass_hash, err := SSHAEncode(admin_pass_str)
|
||||
if err != nil {
|
||||
server.logger.Error("can't create admin password")
|
||||
panic(err)
|
||||
}
|
||||
admin_pass_hash := SSHAEncode([]byte(admin_pass_str))
|
||||
|
||||
admin_dn := "cn=admin," + server.config.Suffix
|
||||
admin_attributes := Entry{
|
||||
|
@ -437,8 +434,8 @@ func (server *Server) handleBindInternal(state *State, r *message.BindRequest) (
|
|||
}
|
||||
|
||||
for _, hash := range passwd {
|
||||
valid, err := SSHAMatches(hash, string(r.AuthenticationSimple()))
|
||||
if valid && err == nil {
|
||||
valid := SSHAMatches(hash, []byte(r.AuthenticationSimple()))
|
||||
if valid {
|
||||
groups, err := server.getAttribute(string(r.Name()), ATTR_MEMBEROF)
|
||||
if err != nil {
|
||||
return ldap.LDAPResultOperationsError, err
|
||||
|
@ -447,32 +444,8 @@ func (server *Server) handleBindInternal(state *State, r *message.BindRequest) (
|
|||
user: string(r.Name()),
|
||||
groups: groups,
|
||||
}
|
||||
|
||||
updatePasswordHash(string(r.AuthenticationSimple()), hash, server, string(r.Name()))
|
||||
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
} else {
|
||||
return ldap.LDAPResultInvalidCredentials, fmt.Errorf("can't authenticate: %w", err)
|
||||
}
|
||||
}
|
||||
return ldap.LDAPResultInvalidCredentials, fmt.Errorf("No password match")
|
||||
}
|
||||
|
||||
// Update the hash if it's not already SSHA512
|
||||
func updatePasswordHash(password string, currentHash string, server *Server, dn string) {
|
||||
hashType, err := determineHashType(currentHash)
|
||||
if err != nil {
|
||||
server.logger.Errorf("can't determine hash type of password")
|
||||
return
|
||||
}
|
||||
if hashType != SSHA512 {
|
||||
reencodedPassword, err := SSHAEncode(password)
|
||||
if err != nil {
|
||||
server.logger.Errorf("can't encode password")
|
||||
return
|
||||
}
|
||||
server.putAttributes(dn, Entry{
|
||||
ATTR_USERPASSWORD: []string{reencodedPassword},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
5
read.go
5
read.go
|
@ -213,11 +213,10 @@ func (server *Server) handleSearchInternal(state *State, w ldap.ResponseWriter,
|
|||
continue
|
||||
}
|
||||
// Send result
|
||||
resultVals := []message.AttributeValue{}
|
||||
for _, v := range val {
|
||||
resultVals = append(resultVals, message.AttributeValue(v))
|
||||
e.AddAttribute(message.AttributeDescription(attr),
|
||||
message.AttributeValue(v))
|
||||
}
|
||||
e.AddAttribute(message.AttributeDescription(attr), resultVals...)
|
||||
}
|
||||
w.Write(e)
|
||||
}
|
||||
|
|
70
ssha.go
70
ssha.go
|
@ -1,79 +1,25 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
//"github.com/jsimonetti/pwscheme/ssha"
|
||||
"github.com/jsimonetti/pwscheme/ssha256"
|
||||
"github.com/jsimonetti/pwscheme/ssha512"
|
||||
)
|
||||
|
||||
const (
|
||||
SSHA = "{SSHA}"
|
||||
SSHA256 = "{SSHA256}"
|
||||
SSHA512 = "{SSHA512}"
|
||||
)
|
||||
|
||||
// Encode encodes the string to ssha512
|
||||
func SSHAEncode(rawPassPhrase string) (string, error) {
|
||||
return ssha512.Generate(rawPassPhrase, 16)
|
||||
}
|
||||
|
||||
// Matches matches the encoded password and the raw password
|
||||
func SSHAMatches(encodedPassPhrase string, rawPassPhrase string) (bool, error) {
|
||||
hashType, err := determineHashType(encodedPassPhrase)
|
||||
if err != nil {
|
||||
return false, errors.New("invalid password hash stored")
|
||||
}
|
||||
|
||||
var is_ok bool
|
||||
switch hashType {
|
||||
case SSHA:
|
||||
is_ok = LegacySSHAMatches(encodedPassPhrase, []byte(rawPassPhrase))
|
||||
return is_ok, nil
|
||||
//return ssha.Validate(rawPassPhrase, encodedPassPhrase)
|
||||
case SSHA256:
|
||||
return ssha256.Validate(rawPassPhrase, encodedPassPhrase)
|
||||
case SSHA512:
|
||||
return ssha512.Validate(rawPassPhrase, encodedPassPhrase)
|
||||
}
|
||||
|
||||
return false, errors.New("no matching hash type found")
|
||||
}
|
||||
|
||||
func determineHashType(hash string) (string, error) {
|
||||
if len(hash) >= 7 && strings.ToUpper(string(hash[0:6])) == SSHA {
|
||||
return SSHA, nil
|
||||
}
|
||||
if len(hash) >= 10 && strings.ToUpper(string(hash[0:9])) == SSHA256 {
|
||||
return SSHA256, nil
|
||||
}
|
||||
if len(hash) >= 10 && strings.ToUpper(string(hash[0:9])) == SSHA512 {
|
||||
return SSHA512, nil
|
||||
}
|
||||
|
||||
return "", errors.New("no valid hash found")
|
||||
}
|
||||
|
||||
// --- legacy
|
||||
|
||||
// Encode encodes the []byte of raw password
|
||||
func LegacySSHAEncode(rawPassPhrase []byte) string {
|
||||
hash := legacyMakeSSHAHash(rawPassPhrase, legacyMakeSalt())
|
||||
func SSHAEncode(rawPassPhrase []byte) string {
|
||||
hash := makeSSHAHash(rawPassPhrase, makeSalt())
|
||||
b64 := base64.StdEncoding.EncodeToString(hash)
|
||||
return fmt.Sprintf("{ssha}%s", b64)
|
||||
}
|
||||
|
||||
// Matches matches the encoded password and the raw password
|
||||
func LegacySSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
|
||||
func SSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
|
||||
if !strings.EqualFold(encodedPassPhrase[:6], "{ssha}") {
|
||||
return false
|
||||
}
|
||||
|
@ -84,7 +30,7 @@ func LegacySSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
|
|||
}
|
||||
salt := bhash[20:]
|
||||
|
||||
newssha := legacyMakeSSHAHash(rawPassPhrase, salt)
|
||||
newssha := makeSSHAHash(rawPassPhrase, salt)
|
||||
|
||||
if bytes.Compare(newssha, bhash) != 0 {
|
||||
return false
|
||||
|
@ -93,7 +39,7 @@ func LegacySSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
|
|||
}
|
||||
|
||||
// makeSalt make a 32 byte array containing random bytes.
|
||||
func legacyMakeSalt() []byte {
|
||||
func makeSalt() []byte {
|
||||
sbytes := make([]byte, 32)
|
||||
_, err := rand.Read(sbytes)
|
||||
if err != nil {
|
||||
|
@ -103,7 +49,7 @@ func legacyMakeSalt() []byte {
|
|||
}
|
||||
|
||||
// makeSSHAHash make hasing using SHA-1 with salt. This is not the final output though. You need to append {SSHA} string with base64 of this hash.
|
||||
func legacyMakeSSHAHash(passphrase, salt []byte) []byte {
|
||||
func makeSSHAHash(passphrase, salt []byte) []byte {
|
||||
sha := sha1.New()
|
||||
sha.Write(passphrase)
|
||||
sha.Write(salt)
|
||||
|
|
|
@ -1,158 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddThenDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
//SetUp - Create Users and Groups
|
||||
inst, err := Init()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//TearDown - Delete all the users and groups created
|
||||
err = inst.Clean()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirmAddAttributes(t *testing.T) {
|
||||
t.Parallel()
|
||||
//SetUp - Create Users and Groups
|
||||
inst, err := Init()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//Test search_attribute to confirm the Add
|
||||
if ok, err := inst.CompareOurDataWithConsul(); !ok {
|
||||
t.Error(err)
|
||||
}
|
||||
//TearDown - Delete all the users and groups created
|
||||
err = inst.Clean()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
//Modifyrequest Test
|
||||
func TestModifyRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
//SetUp - Create Users and Groups
|
||||
inst, err := Init()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//Test modify all data (groups and users)
|
||||
err = inst.ModifyRandomAllData()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//TearDown - Delete all the users and groups created
|
||||
err = inst.Clean()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModifyRequestAndCheck(t *testing.T) {
|
||||
t.Parallel()
|
||||
//SetUp - Create Users and Groups
|
||||
inst, err := Init()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//Test modify all data (groups and users)
|
||||
err = inst.ModifyRandomAllData()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//Check if the data was modify on Consul
|
||||
if ok, err := inst.CompareOurDataWithConsul(); !ok {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//TearDown - Delete all the users and groups created
|
||||
err = inst.Clean()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddUserInGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
//SetUp - Create Users and Groups
|
||||
inst, err := Init()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//Add users in group
|
||||
err = inst.AddAllUsersInGroup()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//TearDown - Delete all the users and groups created
|
||||
err = inst.Clean()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteGroupsAfterAddedUsers(t *testing.T) {
|
||||
t.Parallel()
|
||||
//SetUp - Create Users and Groups
|
||||
inst, err := Init()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//Add users in group
|
||||
err = inst.AddAllUsersInGroup()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
//Delete the half groups
|
||||
number := len(inst.dataGroups) / 2
|
||||
err = inst.clean(inst.dataGroups[0:number])
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
inst.dataGroups = inst.dataGroups[number:len(inst.dataGroups)]
|
||||
|
||||
//Check all the groups in memberOf exist
|
||||
ok, err := inst.CheckMemberOf()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !ok {
|
||||
t.Errorf("Found group in memberOf that isn't in Consul.")
|
||||
}
|
||||
|
||||
//TearDown - Delete all the users and groups created
|
||||
err = inst.Clean()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
//Example of paralellism Test
|
||||
func TestPrincipal(t *testing.T) {
|
||||
t.Run("A=Add and delete", TestAddThenDelete)
|
||||
t.Run("A=Modify", TestModifyRequest)
|
||||
if !testing.Short() {
|
||||
t.Run("B=Add attributes", TestConfirmAddAttributes)
|
||||
t.Run("B=Modify and check", TestModifyRequestAndCheck)
|
||||
t.Run("C=Add user in group", TestAddUserInGroup)
|
||||
t.Run("C=Delete group", TestDeleteGroupsAfterAddedUsers)
|
||||
}
|
||||
}
|
281
test/create.go
281
test/create.go
|
@ -1,281 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
//Mux value, this value permits do not have two identicals values in the parallel instances
|
||||
type StoreAllCN struct {
|
||||
mu sync.Mutex
|
||||
cn map[string]struct{}
|
||||
}
|
||||
|
||||
var allNames = StoreAllCN{cn: make(map[string]struct{})}
|
||||
|
||||
//Type used for the tests
|
||||
type attributes struct {
|
||||
Name string
|
||||
Data []string
|
||||
}
|
||||
|
||||
type data_DN struct {
|
||||
DN string
|
||||
Attributes []attributes
|
||||
}
|
||||
|
||||
type instance struct {
|
||||
numberUsers, numberGroups int
|
||||
dataGroups, dataUsers []data_DN
|
||||
logging *ldap.Conn
|
||||
}
|
||||
|
||||
//Create a new object instance
|
||||
//With this instance, we can obtain an isolated container where
|
||||
//we have our users and groups. It allows to run tests in parallel.
|
||||
func NewInstance(numberUsers, numberGroups int) (*instance, error) {
|
||||
l, err := Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logging.Level = logrus.InfoLevel
|
||||
|
||||
inst := instance{
|
||||
numberUsers: numberUsers,
|
||||
numberGroups: numberGroups,
|
||||
dataGroups: []data_DN{},
|
||||
dataUsers: []data_DN{},
|
||||
logging: l,
|
||||
}
|
||||
|
||||
err = inst.createOrganizationnalUnit()
|
||||
if ldap.IsErrorWithCode(err, uint16(68)) {
|
||||
logging.Warn("OrganizationnalUnit already created")
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = inst.CreateGroups()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = inst.CreateUsers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &inst, nil
|
||||
}
|
||||
|
||||
//Part: Created users or groups or OU
|
||||
|
||||
func (inst *instance) createOrganizationnalUnit() error {
|
||||
dn := []string{"ou=groups,dc=deuxfleurs,dc=fr", "ou=users,dc=deuxfleurs,dc=fr"}
|
||||
attributes := []map[string][]string{{
|
||||
"description": []string{"OrganizationalUnit qui regroupe tous les groupes"},
|
||||
"objectclass": []string{"organizationalUnit", "top"},
|
||||
"ou": []string{"groups"},
|
||||
"structuralobjectclass": []string{"organizationalUnit"},
|
||||
},
|
||||
{
|
||||
"description": []string{"OrganizationalUnit qui regroupe tous les users"},
|
||||
"objectclass": []string{"organizationalUnit", "top"},
|
||||
"ou": []string{"users"},
|
||||
"structuralobjectclass": []string{"organizationalUnit"},
|
||||
},
|
||||
}
|
||||
|
||||
for index := range dn {
|
||||
err := inst.Add_Request(dn[index], attributes[index])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
//Part: Create User or group
|
||||
|
||||
func (inst *instance) CreateUsers() (err error) {
|
||||
|
||||
dn := "cn=%s,ou=users,dc=deuxfleurs,dc=fr"
|
||||
attributes := map[string][]string{
|
||||
"displayname": {},
|
||||
"objectclass": {"inetOrgPerson", "organizationalPerson", "person", "top"},
|
||||
"structuralobjectclass": {"inetOrgPerson"},
|
||||
}
|
||||
|
||||
du, err := inst.create(dn, []string{"displayname"}, inst.numberUsers, attributes, inst.dataUsers)
|
||||
if err == nil {
|
||||
inst.dataUsers = du
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (inst *instance) CreateGroups() error {
|
||||
dn := "cn=%s,ou=groups,dc=deuxfleurs,dc=fr"
|
||||
attributes := map[string][]string{
|
||||
"description": {},
|
||||
"objectclass": {"groupOfNames", "top"},
|
||||
"structuralobjectclass": {"groupOfNames"},
|
||||
}
|
||||
|
||||
dg, err := inst.create(dn, []string{"description"}, inst.numberGroups, attributes, inst.dataGroups)
|
||||
if err == nil {
|
||||
inst.dataGroups = dg
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Hard Function: She does:
|
||||
//- generate an unique name
|
||||
//- store the Data of each AddRequest in instance struct
|
||||
//- send AddRequest to Bottin
|
||||
func (inst *instance) create(dn string, unique_attr []string, number int, attributes map[string][]string, data []data_DN) ([]data_DN, error) {
|
||||
for i := 0; i < number; i++ {
|
||||
name := inst.GenerateName()
|
||||
|
||||
datDn := data_DN{DN: fmt.Sprintf(dn, name)}
|
||||
|
||||
for _, value := range unique_attr {
|
||||
attributes[value] = []string{name}
|
||||
}
|
||||
|
||||
datDn.Attributes = MapAttToStruct(attributes)
|
||||
data = append(data, datDn)
|
||||
|
||||
err := inst.Add_Request(fmt.Sprintf(dn, name), attributes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
//Part: clean
|
||||
|
||||
func (inst *instance) Clean() error {
|
||||
err := inst.CleanGroups()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = inst.CleanUsers()
|
||||
return err
|
||||
}
|
||||
|
||||
func (inst *instance) CleanUsers() error {
|
||||
err := inst.clean(inst.dataUsers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inst.dataUsers = []data_DN{}
|
||||
return err
|
||||
}
|
||||
|
||||
func (inst *instance) CleanGroups() error {
|
||||
err := inst.clean(inst.dataGroups)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inst.dataGroups = []data_DN{}
|
||||
return err
|
||||
}
|
||||
|
||||
func (inst *instance) clean(stock []data_DN) error {
|
||||
logging.Debugf("Delete %d elements.", len(stock))
|
||||
for _, value := range stock {
|
||||
err := inst.Delete_Request(value.DN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Part: Verify if a data_Dn is a group or an user
|
||||
func (inst *instance) VerifyUser(user data_DN) (bool, error) {
|
||||
dn := "ou=users,dc=deuxfleurs,dc=fr"
|
||||
cn := strings.Split(user.DN, ",")[0]
|
||||
filter := fmt.Sprintf("(%s)", cn)
|
||||
|
||||
res, err := inst.Search_Request(dn, filter, []string{"cn"})
|
||||
|
||||
return len(res.Entries) == 1, err
|
||||
}
|
||||
|
||||
func (inst *instance) VerifyGroup(group data_DN) (bool, error) {
|
||||
dn := "ou=groups,dc=deuxfleurs,dc=fr"
|
||||
cn := strings.Split(group.DN, ",")[0]
|
||||
filter := fmt.Sprintf("(%s)", cn)
|
||||
|
||||
res, err := inst.Search_Request(dn, filter, []string{"cn"})
|
||||
|
||||
return len(res.Entries) == 1, err
|
||||
}
|
||||
|
||||
//Part: Add user in a group
|
||||
func (inst *instance) AddUserInGroup(user, group data_DN) error {
|
||||
|
||||
err := inst.Modify_Request(group.DN, nil, nil, map[string][]string{
|
||||
"member": {user.DN},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (inst *instance) AddUserSliceInGroup(users_cn []string, group_dn string) error {
|
||||
|
||||
err := inst.Modify_Request(group_dn, nil, nil, map[string][]string{
|
||||
"member": users_cn,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
//Part: modify, add, delete data_DN struct
|
||||
|
||||
func AddAtt(name string, data []string, dat data_DN) data_DN {
|
||||
dat.Attributes = append(dat.Attributes, attributes{
|
||||
Name: name,
|
||||
Data: data,
|
||||
})
|
||||
|
||||
logging.Debug(fmt.Sprintf("Attributes %s add from %s.", name, dat.DN))
|
||||
return dat
|
||||
}
|
||||
|
||||
func DelAtt(name string, dat data_DN) data_DN {
|
||||
for index, value := range dat.Attributes {
|
||||
if value.Name == name {
|
||||
dat.Attributes[index] = dat.Attributes[len(dat.Attributes)-1]
|
||||
//tmp := dat.Attributes[:len(dat.Attributes)-1]
|
||||
dat.Attributes = []attributes{}
|
||||
logging.Debugf("Attributes %s delete from %s.", name, dat.DN)
|
||||
return dat
|
||||
}
|
||||
}
|
||||
logging.Debugf("Can't delete attribute %s from %s.", name, dat.DN)
|
||||
return dat
|
||||
}
|
||||
|
||||
func ReplaceAtt(name string, data []string, dat data_DN) data_DN {
|
||||
for index, value := range dat.Attributes {
|
||||
if value.Name == name {
|
||||
dat.Attributes[index] = attributes{
|
||||
Name: name,
|
||||
Data: data,
|
||||
}
|
||||
logging.Debugf("Replace attributes %s from %s succesful..", name, dat.DN)
|
||||
return dat
|
||||
}
|
||||
}
|
||||
logging.Debugf("Can't replace attributes %s from %s.", name, dat.DN)
|
||||
return dat
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
|
||||
const default_users, default_groups = 1000, 1000
|
||||
|
||||
func Init() (*instance, error) {
|
||||
inst, err := NewInstance(default_users, default_groups)
|
||||
return inst, err
|
||||
}
|
||||
|
||||
//Part to compare our datas
|
||||
func (inst *instance) CompareOurDataWithConsul() (bool, error) {
|
||||
if ok, err := inst.VerifyOurData(inst.dataUsers); !ok {
|
||||
return false, err
|
||||
}
|
||||
if ok, err := inst.VerifyOurData(inst.dataGroups); !ok {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (inst *instance) VerifyOurData(tabData []data_DN) (bool, error) {
|
||||
for _, value := range tabData {
|
||||
names := getNamesAtt(value)
|
||||
cn := strings.Split(value.DN, ",")[0]
|
||||
res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), names)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(res.Entries) != 1 {
|
||||
return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries))
|
||||
}
|
||||
if !Compare(value, res.Entries[0]) {
|
||||
return false, fmt.Errorf("no match with the DN: %s", value.DN)
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func Compare(dat data_DN, ent *ldap.Entry) bool {
|
||||
for _, value := range dat.Attributes {
|
||||
logging.Debugf("Attributes from %s is now: %s.", dat.DN, dat.Attributes)
|
||||
entVal := GetAttributeValuesBottin(ent, value.Name)
|
||||
logging.Debugf("Values of the Entry: attributName: %s, Values: %s.", value.Name, entVal)
|
||||
if !CompareSliceString(entVal, value.Data) {
|
||||
logging.Debugf("Values expected: %s, values found: %s.", value.Data, entVal)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//Part modify datas
|
||||
func (inst *instance) ModifyRandomAllData() error {
|
||||
dg, err := inst.ModifyRandom(inst.dataGroups, []string{"description"})
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
inst.dataGroups = dg
|
||||
}
|
||||
|
||||
dg, err = inst.ModifyRandom(inst.dataUsers, []string{"displayname"})
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
inst.dataUsers = dg
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Function which modify random way the attributes in attName of a data_DN's slice, it can delete, replace and delete
|
||||
//The function modify also in the dat object
|
||||
func (inst *instance) ModifyRandom(dat []data_DN, attName []string) ([]data_DN, error) {
|
||||
for index, value := range dat {
|
||||
del := make(map[string][]string)
|
||||
add := make(map[string][]string)
|
||||
replace := make(map[string][]string)
|
||||
|
||||
for _, att := range attName {
|
||||
|
||||
switch selNumber := R.Intn(3); selNumber {
|
||||
case 0:
|
||||
del[att] = []string{}
|
||||
value = DelAtt(att, value)
|
||||
logging.Debug(fmt.Sprintf("Delete the attribute %s of the DN %s.", att, value.DN))
|
||||
case 1:
|
||||
name := inst.GenerateName()
|
||||
value = AddAtt(name, []string{name}, value)
|
||||
add[name] = []string{name}
|
||||
logging.Debug(fmt.Sprintf("Add the attribute %s with value %s of the DN %s.", name, name, value.DN))
|
||||
case 2:
|
||||
name := inst.GenerateName()
|
||||
value = ReplaceAtt(att, []string{name}, value)
|
||||
replace[att] = []string{name}
|
||||
logging.Debug(fmt.Sprintf("Replace the attribute %s with value %s of the DN %s.", att, name, value.DN))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
err := inst.Modify_Request(value.DN, add, del, replace)
|
||||
if err != nil {
|
||||
return dat, err
|
||||
}
|
||||
dat[index] = value
|
||||
}
|
||||
return dat, nil
|
||||
}
|
||||
|
||||
//Add all users in a random group
|
||||
func (inst *instance) AddAllUsersInGroup() error {
|
||||
for _, value := range inst.dataGroups {
|
||||
valueRand := (len(inst.dataUsers) + 1) / 30
|
||||
if valueRand == 0 {
|
||||
valueRand = 1
|
||||
}
|
||||
numberOfMembers := R.Intn(valueRand) + 1
|
||||
logging.Debugf("%s will be have %d members.", value.DN, numberOfMembers)
|
||||
|
||||
groupMemory := make(map[int]struct{})
|
||||
users_cn := []string{}
|
||||
|
||||
for i := 0; i < numberOfMembers; i++ {
|
||||
selectGroup := R.Intn(len(inst.dataUsers))
|
||||
for _, ok := groupMemory[selectGroup]; ok; _, ok = groupMemory[selectGroup] {
|
||||
selectGroup = R.Intn(len(inst.dataUsers))
|
||||
|
||||
logging.Debugf("Search an other member. The value is %d , and we have %d members available.", selectGroup, len(inst.dataUsers))
|
||||
}
|
||||
groupMemory[selectGroup] = struct{}{}
|
||||
|
||||
users_cn = append(users_cn, inst.dataGroups[selectGroup].DN)
|
||||
|
||||
}
|
||||
err := inst.AddUserSliceInGroup(users_cn, value.DN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Check if the groups in memberOf exist in Consul
|
||||
func (inst *instance) CheckMemberOf() (bool, error) {
|
||||
for _, value := range inst.dataUsers {
|
||||
cn := strings.Split(value.DN, ",")[0]
|
||||
res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), []string{"memberOf"})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(res.Entries) != 1 {
|
||||
return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries))
|
||||
}
|
||||
attValues := GetAttributeValuesBottin(res.Entries[0], "memberOf")
|
||||
for _, dnGroup := range attValues {
|
||||
logging.Debugf("Verify if the group %s exist...", dnGroup)
|
||||
ok, err := inst.VerifyGroup(data_DN{DN: dnGroup})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !ok {
|
||||
return false, fmt.Errorf("don't found the group: %s", dnGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
128
test/handler.go
128
test/handler.go
|
@ -1,128 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
||||
ldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const maxlength_generateName, minlength_generateName = 25, 3
|
||||
|
||||
const bindusername = "cn=admin,dc=deuxfleurs,dc=fr"
|
||||
const adresse = "127.0.0.1"
|
||||
const port = 1389
|
||||
|
||||
var logging = logrus.New()
|
||||
|
||||
const seed = 654258
|
||||
|
||||
var R = rand.New(rand.NewSource(seed))
|
||||
|
||||
var bindpassword = "sf7yO52NCuE"
|
||||
|
||||
//Handler just to facilite the print error
|
||||
func PrintError(LDAPError error) {
|
||||
if LDAPError != nil {
|
||||
logging.Fatal(LDAPError)
|
||||
}
|
||||
}
|
||||
|
||||
//Generate an unique name, which store in all_names
|
||||
func (inst *instance) GenerateName() (name string) {
|
||||
alphabet := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
|
||||
"w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
|
||||
"Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "é", "è", "ê", "ë", "à", "@", "â", "ä", "û", "ü", "ù", "$", "£", "%", "ø", "€"}
|
||||
length := R.Intn(maxlength_generateName) + minlength_generateName
|
||||
|
||||
//Check if this name not exist already
|
||||
//Lock thhis variable because she is hared with other goroutine
|
||||
allNames.mu.Lock()
|
||||
for only_one := true; only_one; _, only_one = allNames.cn[name] {
|
||||
//Create the name
|
||||
for i := 0; i < length; i++ {
|
||||
name += alphabet[R.Intn(len(alphabet))]
|
||||
}
|
||||
|
||||
}
|
||||
//Add the new name in the map to store this one
|
||||
allNames.cn[name] = struct{}{}
|
||||
allNames.mu.Unlock()
|
||||
logging.Debug(fmt.Sprintf("Name generated: %s.", name))
|
||||
return
|
||||
}
|
||||
|
||||
//Transform attributes in map format to the struct attributes
|
||||
func MapAttToStruct(att map[string][]string) []attributes {
|
||||
resultat := []attributes{}
|
||||
for key, value := range att {
|
||||
logging.Debug(fmt.Sprintf("Transform: key: %s, values: %s to attributes struct.\n", key, value))
|
||||
resultat = append(resultat, attributes{
|
||||
Name: key,
|
||||
Data: value,
|
||||
})
|
||||
}
|
||||
return resultat
|
||||
}
|
||||
|
||||
func Connect() (*ldap.Conn, error) {
|
||||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if key, ok := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW"); ok {
|
||||
bindpassword = key
|
||||
}
|
||||
//l.Debug.Enable(true)
|
||||
err = l.Bind(bindusername, bindpassword)
|
||||
logging.Debug("Connection succesful")
|
||||
return l, err
|
||||
}
|
||||
|
||||
//Handler to get only attributes names
|
||||
func getNamesAtt(dat data_DN) []string {
|
||||
resultat := []string{}
|
||||
for _, values := range dat.Attributes {
|
||||
resultat = append(resultat, values.Name)
|
||||
}
|
||||
return resultat
|
||||
}
|
||||
|
||||
//Handler to compare slice string
|
||||
func CompareSliceString(string1, string2 []string) bool {
|
||||
if len(string1) != len(string2) {
|
||||
return false
|
||||
} else {
|
||||
for index := range string1 {
|
||||
if string1[index] != string2[index] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//Handler to remove an element in slice string
|
||||
func DeleteElementSliceString(s string, sSlice []string) []string {
|
||||
|
||||
for index, value := range sSlice {
|
||||
if value == s {
|
||||
sSlice[index] = sSlice[len(sSlice)-1]
|
||||
return sSlice[:len(sSlice)-1]
|
||||
}
|
||||
}
|
||||
return sSlice
|
||||
}
|
||||
|
||||
//Get attributes entry values bottin bug
|
||||
func GetAttributeValuesBottin(ent *ldap.Entry, name string) (res []string) {
|
||||
for _, val := range ent.Attributes {
|
||||
if val.Name == name {
|
||||
res = append(res, val.Values...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
ldap "github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
|
||||
func (inst *instance) Add_Request(dn string, attributes map[string][]string) error {
|
||||
//Create the AddRequest
|
||||
req := ldap.NewAddRequest(dn, nil)
|
||||
for key, value := range attributes {
|
||||
req.Attribute(key, value)
|
||||
}
|
||||
|
||||
//Send the request
|
||||
err := inst.logging.Add(req)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
//Use enum to select Replace,Delete,Modify
|
||||
func (inst *instance) Modify_Request(dn string, add_attributes, delete_attributes, replace_attributes map[string][]string) error {
|
||||
modifyReq := ldap.NewModifyRequest(dn, nil)
|
||||
|
||||
for key, value := range add_attributes {
|
||||
modifyReq.Add(key, value)
|
||||
}
|
||||
|
||||
for key, value := range delete_attributes {
|
||||
modifyReq.Delete(key, value)
|
||||
}
|
||||
|
||||
for key, value := range replace_attributes {
|
||||
modifyReq.Replace(key, value)
|
||||
}
|
||||
|
||||
err := inst.logging.Modify(modifyReq)
|
||||
return err
|
||||
}
|
||||
|
||||
func (inst *instance) Delete_Request(dn string) error {
|
||||
del := ldap.NewDelRequest(dn, nil)
|
||||
|
||||
err := inst.logging.Del(del)
|
||||
return err
|
||||
}
|
||||
|
||||
func (inst *instance) Search_Request(dn, filter string, name_attributes []string) (*ldap.SearchResult, error) {
|
||||
searchReq := ldap.NewSearchRequest(
|
||||
dn,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
filter,
|
||||
name_attributes,
|
||||
nil,
|
||||
)
|
||||
|
||||
res, err := inst.logging.Search(searchReq)
|
||||
logging.Debugf("Search Request made with: dn: %s, filter: %s, attributes: %s. \n", dn, filter, name_attributes)
|
||||
return res, err
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
echo $BOTTIN_DEFAULT_ADMIN_PW
|
||||
consul agent -dev > /dev/null 2>&1 &
|
||||
sleep 2
|
||||
cp test/config.json.test config.json
|
||||
./bottin > /tmp/bottin.log 2>&1 &
|
||||
sleep 1
|
||||
./test/test -test.v -test.failfast -test.short -test.run TestPrincipal
|
||||
./test/test -test.v -test.failfast -test.run TestPrincipal/B=
|
||||
|
||||
jobs
|
||||
kill %2
|
||||
kill %1
|
BIN
test_automatic/Scan_Bad_Packets.pcapng
Normal file
BIN
test_automatic/Scan_Bad_Packets.pcapng
Normal file
Binary file not shown.
BIN
test_automatic/Scan_Good_Packets.pcapng
Normal file
BIN
test_automatic/Scan_Good_Packets.pcapng
Normal file
Binary file not shown.
|
@ -1,6 +1,5 @@
|
|||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
|
@ -8,12 +7,10 @@ github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ
|
|||
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
431
test_automatic/integration.go
Normal file
431
test_automatic/integration.go
Normal file
|
@ -0,0 +1,431 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
|
||||
const bindusername = "cn=admin,dc=deuxfleurs,dc=fr"
|
||||
const adresse = "127.0.0.1"
|
||||
const port = 1389
|
||||
var bindpassword string
|
||||
|
||||
var all_names = make(map[string]struct{})
|
||||
|
||||
|
||||
|
||||
func printError(LDAPError error) {
|
||||
if LDAPError != nil {
|
||||
log.Fatal(LDAPError)
|
||||
}
|
||||
}
|
||||
|
||||
func createOU(l *ldap.Conn) error {
|
||||
|
||||
req := ldap.NewAddRequest("ou=groups,dc=deuxfleurs,dc=fr",nil)
|
||||
req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les groupes"})
|
||||
req.Attribute("objectclass",[]string{"organizationalUnit", "top"})
|
||||
req.Attribute("ou",[]string{"groups"})
|
||||
req.Attribute("structuralobjectclass", []string{"organizationalUnit"})
|
||||
|
||||
err := l.Add(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req = ldap.NewAddRequest("ou=users,dc=deuxfleurs,dc=fr",nil)
|
||||
req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les utilisateurs"})
|
||||
req.Attribute("objectclass",[]string{"organizationalUnit", "top"})
|
||||
req.Attribute("ou",[]string{"users"})
|
||||
req.Attribute("structuralobjectclass", []string{"organizationalUnit"})
|
||||
|
||||
err = l.Add(req)
|
||||
return err
|
||||
}
|
||||
|
||||
func generateName(r *rand.Rand) (name string) {
|
||||
for only_one := true; only_one; _, only_one = all_names[name]{
|
||||
name = fmt.Sprintf("%d",r.Int())
|
||||
}
|
||||
all_names[name] = struct{}{}
|
||||
log.Debug(fmt.Sprintf("Name generated: %s.\n", name))
|
||||
return
|
||||
}
|
||||
|
||||
func createGroup(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) {
|
||||
StructuralObjectClass := []string{"groupOfNames"}
|
||||
ObjectClass := []string{"groupOfNames","top"}
|
||||
|
||||
|
||||
|
||||
for i := 0; i<20; i++ {
|
||||
//Generate name and check if he is unique
|
||||
name := generateName(r)
|
||||
|
||||
req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=groups,dc=deuxfleurs,dc=fr",name),nil)
|
||||
req.Attribute("description",[]string{generateName(r)})
|
||||
req.Attribute("objectclass",ObjectClass)
|
||||
req.Attribute("structuralobjectclass",StructuralObjectClass)
|
||||
|
||||
err = l.Add(req)
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] groupe.\n",i))
|
||||
return nil, err
|
||||
}
|
||||
tab_AddRequest = append(tab_AddRequest, *req)
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createUser(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) {
|
||||
StructuralObjectClass := []string{"inetOrgPerson"}
|
||||
ObjectClass := []string{"inetOrgPerson","organizationalPerson","person","top"}
|
||||
|
||||
for i := 0; i<20; i++ {
|
||||
name := generateName(r)
|
||||
|
||||
req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=users,dc=deuxfleurs,dc=fr",name),nil)
|
||||
req.Attribute("displayname",[]string{generateName(r)})
|
||||
req.Attribute("objectclass",ObjectClass)
|
||||
req.Attribute("structuralobjectclass",StructuralObjectClass)
|
||||
|
||||
err = l.Add(req)
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] user.\n",i))
|
||||
return nil, err
|
||||
}
|
||||
tab_AddRequest = append(tab_AddRequest, *req)
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func search_attributes(tab_Attributes []ldap.Attribute, tipe string) (*ldap.Attribute) {
|
||||
for _,att := range tab_Attributes {
|
||||
if att.Type == tipe {
|
||||
return &att
|
||||
}
|
||||
}
|
||||
return nil
|
||||
// return ldap.Attribute{}
|
||||
}
|
||||
|
||||
func test_attributes(l *ldap.Conn, tab_AddRequest []ldap.AddRequest, filter_objectclass, user_or_group string) (err error) {
|
||||
for _, addRequest := range tab_AddRequest {
|
||||
|
||||
//On prend le cn en supposant qu'il est unique
|
||||
cn := strings.Split(addRequest.DN,",")[0]
|
||||
|
||||
//On crée la requête pour la recherche
|
||||
search_req := ldap.NewSearchRequest(
|
||||
fmt.Sprintf("ou=%s,dc=deuxfleurs,dc=fr",user_or_group),
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf("(&(objectclass=%s)(%s))", filter_objectclass,cn),
|
||||
[]string{"displayname","objectclass","structuralobjectclass"},
|
||||
nil,
|
||||
)
|
||||
|
||||
//On lance la recherche
|
||||
result, err := l.Search(search_req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(result.Entries) != 1 {
|
||||
return errors.New("Test a trouvé plusieurs displaynames en commun ou en a trouvé aucun")
|
||||
}
|
||||
|
||||
//On compare les attributs qu'on a reçu avec les attributs qu'on a envoyé
|
||||
result_attributes := result.Entries[0].Attributes
|
||||
log.Debug(fmt.Sprintf("La longueur est de %d, contient : \n %s.\n",len(result_attributes), result_attributes))
|
||||
|
||||
//Notre recherche crée un attribut par valeur, même si les valeurs viennent du même nom d'attribut
|
||||
//Par exemple: objectclass possède 4 valeurs. Alors on aura 4 EntryAttribute qui contient chacune une des 4 valeurs de l'attribut objectclass
|
||||
|
||||
//j est l'indice qui représente la j-ème valeur de notre attribut
|
||||
var j int
|
||||
var att *ldap.Attribute
|
||||
for i,attributes := range result_attributes {
|
||||
//On cherche l'attribut de l'user i qui a le même nom que celui qu'on a reçu et qu'on traite dans cette boucle
|
||||
if j == 0 {
|
||||
att = search_attributes(addRequest.Attributes, attributes.Name)
|
||||
if att == nil {
|
||||
return errors.New(fmt.Sprintf("Error: test_attributes - Don't find match name attributes. We search %s.\n", attributes.Name))
|
||||
}
|
||||
}
|
||||
log.Debug(fmt.Sprintf("Le nom de l'attribut est %s, sa valeur est: \n %s.", att.Type, att))
|
||||
|
||||
if j >= len(att.Vals) || att.Vals[j] != attributes.Values[0] {
|
||||
return errors.New(fmt.Sprintf("Error: test_attributes - Theses values aren't the same: %d, %d",att.Vals, attributes.Values))
|
||||
}
|
||||
|
||||
if i+1 < len(result_attributes) && result_attributes[i+1].Name == attributes.Name {
|
||||
j += 1
|
||||
} else { j = 0}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func clean(l *ldap.Conn, AddReq_users, AddReq_groups []ldap.AddRequest,user, group bool) (err error){
|
||||
log.Debug("Debut clean")
|
||||
if(user) {
|
||||
for _,req := range AddReq_users {
|
||||
delReq := ldap.NewDelRequest(req.DN,nil)
|
||||
err = l.Del(delReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if group {
|
||||
for _,req := range AddReq_groups {
|
||||
delReq := ldap.NewDelRequest(req.DN, nil)
|
||||
err = l.Del(delReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
defer log.Debug("Fin clean")
|
||||
return
|
||||
}
|
||||
|
||||
func test_modify_attributes(l *ldap.Conn, r *rand.Rand, tab_AddReq []ldap.AddRequest, tab_type_name []string) (err error) {
|
||||
for _, AddReq := range tab_AddReq {
|
||||
modReq := ldap.NewModifyRequest(AddReq.DN,nil)
|
||||
for _, type_name := range tab_type_name {
|
||||
newName := generateName(r)
|
||||
modReq.Replace(type_name, []string{newName})
|
||||
att := search_attributes(AddReq.Attributes, type_name)
|
||||
att.Vals[0] = newName
|
||||
}
|
||||
err = l.Modify(modReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func add_user_in_groups(l *ldap.Conn, r *rand.Rand, users, groups []ldap.AddRequest) (err error) {
|
||||
for _,group := range groups {
|
||||
numberUsers := r.Intn(19) + 1 //Always a minimum of 1 user
|
||||
list_users := []string{}
|
||||
for i:=0; i < numberUsers; i++ {
|
||||
list_users = append(list_users, users[i].DN)
|
||||
}
|
||||
modifyReq := ldap.NewModifyRequest( group.DN, nil)
|
||||
modifyReq.Add("member", list_users)
|
||||
|
||||
err = l.Modify(modifyReq)
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("Error: ModifyReq failed, func:add_users_in_groups from group:\n %d",group))
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func delete_groups(l *ldap.Conn, groups []ldap.AddRequest) (list map[string][]string ,err error) {
|
||||
list = make(map[string][]string)
|
||||
for _, group := range groups {
|
||||
//Get lists_users
|
||||
cn := strings.Split(group.DN,",")[0]
|
||||
search_req := ldap.NewSearchRequest(
|
||||
"ou=groups,dc=deuxfleurs,dc=fr",
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf("(&(objectclass=groupOfNames)(%s))",cn),
|
||||
[]string{"member"},
|
||||
nil,
|
||||
)
|
||||
res , err := l.Search(search_req)
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("Error Search: func: delete_groups_and_check_memberOf, from group: \n %d", group))
|
||||
return list, err
|
||||
}
|
||||
if len(res.Entries) != 1 {
|
||||
err = errors.New(fmt.Sprintf("SearchResult get: %s, SearchResult wanted: 1", len(res.Entries)))
|
||||
return list, err
|
||||
}
|
||||
EntryAtt := res.Entries[0].Attributes
|
||||
list_users := []string{}
|
||||
for _, att := range EntryAtt {
|
||||
list_users = append(list_users ,att.Values[0])
|
||||
}
|
||||
|
||||
//Del group
|
||||
del := ldap.NewDelRequest( group.DN, nil)
|
||||
err = l.Del(del)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
list[group.DN] = list_users
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func check_memberOf(l *ldap.Conn, list map[string][]string) (err error) {
|
||||
//Check the memberOf of all users
|
||||
for groupeDN,_ := range list{
|
||||
search_req := ldap.NewSearchRequest(
|
||||
"ou=users,dc=deuxfleurs,dc=fr",
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,0 ,0, false,
|
||||
fmt.Sprintf("(&(objectclass=inetOrgPerson)(memberOf=%s))",groupeDN),
|
||||
[]string{"cn"},
|
||||
nil,
|
||||
)
|
||||
res, err := l.Search(search_req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(res.Entries) != 0 {
|
||||
err = errors.New(fmt.Sprintf("L'user '%s' a encore le DN d'un groupe supprimé: %s",res.Entries[0].Attributes[0].Values[0],groupeDN))
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func reconnect(l *ldap.Conn) (l_nouv *ldap.Conn, err error){
|
||||
l.Close()
|
||||
l_nouv, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse,port))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = l_nouv.Bind(bindusername, bindpassword)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
var ok bool
|
||||
bindpassword, ok = os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
|
||||
if !ok {
|
||||
if len(os.Args) == 2 {
|
||||
bindpassword = os.Args[1]
|
||||
} else {
|
||||
bindpassword = ""
|
||||
}
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprintf("Password selected: %s",bindpassword))
|
||||
//log.SetLevel(log.TraceLevel)
|
||||
|
||||
//Create a connection with Bottin server
|
||||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port))
|
||||
//l.Debug = true
|
||||
printError(err)
|
||||
|
||||
//Bind with the admin account generated
|
||||
err = l.Bind(bindusername, bindpassword)
|
||||
printError(err)
|
||||
|
||||
//Create our object Rand, it's important to always have the same values
|
||||
source := rand.NewSource(666475745)
|
||||
r := rand.New(source)
|
||||
log.Info(fmt.Sprintf("The seed of the rand object is %d.\n",r.Seed))
|
||||
|
||||
//Create user and groups OrgaUnit
|
||||
err = createOU(l)
|
||||
if ldap.IsErrorWithCode(err, uint16(68)) {
|
||||
log.Warn("Les OrganizationalUnit users et groups sont déjà présents.")
|
||||
}else {
|
||||
printError(err)
|
||||
log.Info("Création des OU de groups et users")
|
||||
}
|
||||
|
||||
//Create random groups
|
||||
tab_AddRequest_groups, err := createGroup(r, l)
|
||||
printError(err)
|
||||
log.Info(fmt.Sprintf("Création des groupes aléatoirement réussi: %d\n", len(tab_AddRequest_groups)))
|
||||
|
||||
//Create random users
|
||||
tab_AddRequest_users, err := createUser(r, l)
|
||||
printError(err)
|
||||
log.Info(fmt.Sprintf("Création des users aléatoirement réussi: %d\n", len(tab_AddRequest_users)))
|
||||
|
||||
//Search and compare attribute Users. (We keep Attribute object from 'Create random users' and compare with the result of our search)
|
||||
err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson","users")
|
||||
printError(err)
|
||||
log.Info("Tous les attributs users insérés dans Consul ont été vérifiés..\n")
|
||||
|
||||
//Search and compare attributes Groups
|
||||
err = test_attributes(l,tab_AddRequest_groups, "groupOfNames","groups")
|
||||
printError(err)
|
||||
log.Info("Tous les attributs groups insérés dans Consul ont été vérifiés.\n")
|
||||
|
||||
|
||||
//Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this
|
||||
l,err = reconnect(l)
|
||||
printError(err)
|
||||
//Modify attributes users and groups.
|
||||
|
||||
//Modify users' attributes and check them
|
||||
|
||||
log.Debug(fmt.Sprintf("Les valeurs sont:\n %s", tab_AddRequest_users))
|
||||
err = test_modify_attributes(l, r, tab_AddRequest_users, []string{"displayname"})
|
||||
printError(err)
|
||||
log.Debug("Modifications users faites")
|
||||
|
||||
//Check if the attributes are correct:
|
||||
err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson", "users")
|
||||
printError(err)
|
||||
log.Info("Les modifications ont bien été prises en compte")
|
||||
log.Debug(fmt.Sprintf("Les nouvelles valeurs sont:\n %s", tab_AddRequest_users))
|
||||
|
||||
|
||||
|
||||
|
||||
//Modify users' attributes and check them
|
||||
err = test_modify_attributes(l, r, tab_AddRequest_groups, []string{"description"})
|
||||
printError(err)
|
||||
log.Info("Modifications groups faites")
|
||||
|
||||
//Check if the attributes are correct:
|
||||
err = test_attributes(l,tab_AddRequest_groups, "groupOfNames", "groups")
|
||||
printError(err)
|
||||
log.Info("Les modifications ont bien été prises en compte")
|
||||
|
||||
//Close the connection
|
||||
l, err = reconnect(l)
|
||||
printError(err)
|
||||
|
||||
//Add users in group, search them, delete several samples and search again to be sur it's good
|
||||
err = add_user_in_groups(l, r, tab_AddRequest_users, tab_AddRequest_groups)
|
||||
printError(err)
|
||||
log.Info("Ajout d'users dans les groupes fait")
|
||||
|
||||
//Close the connection
|
||||
l, err = reconnect(l)
|
||||
printError(err)
|
||||
|
||||
list, err := delete_groups(l, tab_AddRequest_groups)
|
||||
printError(err)
|
||||
log.Info("groupe supprimé")
|
||||
|
||||
|
||||
l,err = reconnect(l)
|
||||
printError(err)
|
||||
|
||||
err = check_memberOf(l, list)
|
||||
printError(err)
|
||||
log.Info("Le memberOf a été correctement vidé")
|
||||
|
||||
//Clean: Delete all users and groups (not OU users and groups)
|
||||
err = clean(l, tab_AddRequest_users, tab_AddRequest_groups, true, false)
|
||||
printError(err)
|
||||
log.Info("Clean succes")
|
||||
|
||||
defer os.Exit(0)
|
||||
return
|
||||
|
||||
}
|
40
test_automatic/rapport_beug_bottin.txt
Normal file
40
test_automatic/rapport_beug_bottin.txt
Normal file
|
@ -0,0 +1,40 @@
|
|||
Introduction et Observation premières:
|
||||
|
||||
Lors de la réalisation de mon code Go, j'ai trouvé un beug qui provoquait l'arrêt de mon programme car Bottin envoyé une réponse erronée à mon programme.
|
||||
Dans logs de Bottin je ne voyais aucune erreur, Bottin n'avait pas cessé de fonctionner. Il s'arrêtait à chaque fois sur mes requêtes Del (mes dernières).
|
||||
|
||||
|
||||
Reproduction du beug:
|
||||
|
||||
Pour reproduire le beug, il suffit de lancer le programme interrogation.go et de commenter les lignes suivantes :
|
||||
|
||||
260 //Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this
|
||||
261 l.Close()
|
||||
262 l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse, port))
|
||||
263 printError(err)
|
||||
264 err = l.Bind(bindusername, bindpassword)
|
||||
265 printError(err)
|
||||
|
||||
Ainsi on obtient l'erreur suivante:
|
||||
|
||||
2021/07/07 01:25:51 Received unexpected message -128, false
|
||||
|
||||
|
||||
Test réalisé pour comprendre la source du problème:
|
||||
|
||||
Ma première hypothèses fut que j'envoyais trop de requêtes dans un court laps de temps et ainsi Bottin ou Consul ne pouvait pas suivre.
|
||||
J'ai placé un sleep de 50 puis de 100 Millisecondes entre chaque requête, j'obtenais toujours la même erreur.
|
||||
J'ai essayé de mettre un sleep de 10 secondes avant mes requêtes de suppression mais j'obtenais toujours la même chose.
|
||||
|
||||
Hack pour résoudre:
|
||||
|
||||
La première solution qui a fonctionné était de réduire le nombre de requêtes en n'exécutant pas certains tests random.
|
||||
La dernière solutions qui est utilisée:
|
||||
Fermée la connexion puis la réouvrir permet de palier à ce problème
|
||||
|
||||
Hypothèses du problème ?:
|
||||
|
||||
Existerait-il un Buffer par Bind qui se remplirait ? Et lorsque celui-ci est plein, ne renvoie pas d'erreur juste n'arrive plus à répondre.
|
||||
|
||||
Erwan DUFOUR
|
||||
Deuxfleurs
|
12
test_automatic/start_test.sh
Executable file
12
test_automatic/start_test.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
|
||||
#export BOTTIN_DEFAULT_ADMIN_PW=$(openssl rand -base64 24)
|
||||
echo $BOTTIN_DEFAULT_ADMIN_PW
|
||||
consul agent -dev > /dev/null 2>&1 &
|
||||
sleep 2
|
||||
cp test_automatic/config.json.test config.json
|
||||
./bottin > /dev/null 2>&1 &
|
||||
sleep 1
|
||||
./test_automatic/integration
|
||||
rm config.json
|
||||
exit 0
|
23
write.go
23
write.go
|
@ -7,9 +7,8 @@ import (
|
|||
|
||||
ldap "bottin/ldapserver"
|
||||
|
||||
message "bottin/goldap"
|
||||
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
message "bottin/goldap"
|
||||
)
|
||||
|
||||
// Generic item modification function --------
|
||||
|
@ -39,7 +38,7 @@ func (server *Server) putAttributes(dn string, attrs Entry) error {
|
|||
|
||||
// Retreieve previously existing attributes, which we will use to delete
|
||||
// entries with the wrong case
|
||||
previous_pairs, _, err := server.kv.List(prefix+"/attribute=", &server.readOpts)
|
||||
previous_pairs, _, err := server.kv.List(prefix + "/attribute=", &server.readOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -66,24 +65,6 @@ func (server *Server) putAttributes(dn string, attrs Entry) error {
|
|||
}
|
||||
}
|
||||
|
||||
// if the password is not yet hashed we hash it
|
||||
if k == ATTR_USERPASSWORD {
|
||||
tmpValues := []string{}
|
||||
for _, pw := range values {
|
||||
_, err := determineHashType(pw)
|
||||
if err != nil {
|
||||
encodedPassword, err := SSHAEncode(pw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpValues = append(tmpValues, encodedPassword)
|
||||
} else {
|
||||
tmpValues = append(tmpValues, pw)
|
||||
}
|
||||
}
|
||||
values = tmpValues
|
||||
}
|
||||
|
||||
// If we have zero values, delete associated k/v pair
|
||||
// Otherwise, write new values
|
||||
if len(values) == 0 {
|
||||
|
|
Loading…
Reference in a new issue