Compare commits
15 commits
test-go-Bo
...
main
Author | SHA1 | Date | |
---|---|---|---|
1d338a9cc1 | |||
e9bf550e02 | |||
55ef7530ae | |||
a2be316d91 | |||
9cab98d2ce | |||
de71260bbf | |||
0ef8313a67 | |||
|
9ce0d22c99 | ||
|
f05e41c9aa | ||
dbd9003714 | |||
a08be6b395 | |||
2a844bd559 | |||
2707dd77c5 | |||
477d7014ed | |||
|
a53641e773 |
100 changed files with 12461 additions and 89 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,3 +2,5 @@ bottin
|
||||||
bottin.static
|
bottin.static
|
||||||
config.json
|
config.json
|
||||||
test/test
|
test/test
|
||||||
|
result
|
||||||
|
ldap.json
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: bottin
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
image: golang:stretch
|
image: golang:stretch
|
||||||
|
@ -12,14 +8,8 @@ steps:
|
||||||
- go test -i -c -o test
|
- go test -i -c -o test
|
||||||
|
|
||||||
- name: test_bottin
|
- name: test_bottin
|
||||||
image: consul:latest
|
image: consul:1.15.4
|
||||||
environment:
|
environment:
|
||||||
BOTTIN_DEFAULT_ADMIN_PW: priZ4Cg0x5NkSyiIN/MpvWw4ZEy8f8s1
|
BOTTIN_DEFAULT_ADMIN_PW: priZ4Cg0x5NkSyiIN/MpvWw4ZEy8f8s1
|
||||||
commands:
|
commands:
|
||||||
- ash test/runner.sh
|
- ash test/runner.sh
|
||||||
|
|
||||||
---
|
|
||||||
kind: signature
|
|
||||||
hmac: ff246a04c3df8a2f39c8b446dea920622d61950e6caaac886931bdb05d0706ed
|
|
||||||
|
|
||||||
...
|
|
|
@ -1,5 +0,0 @@
|
||||||
FROM scratch
|
|
||||||
|
|
||||||
ADD bottin.static /bottin
|
|
||||||
|
|
||||||
ENTRYPOINT ["/bottin"]
|
|
15
README.md
15
README.md
|
@ -1,6 +1,6 @@
|
||||||
# Bottin
|
# Bottin
|
||||||
|
|
||||||
[![Build Status](https://drone.deuxfleurs.fr/api/badges/Deuxfleurs/bottin/status.svg?ref=refs/heads/main)](https://drone.deuxfleurs.fr/Deuxfleurs/bottin)
|
[![status-badge](https://woodpecker.deuxfleurs.fr/api/badges/38/status.svg)](https://woodpecker.deuxfleurs.fr/repos/38)
|
||||||
|
|
||||||
<img src="https://git.deuxfleurs.fr/Deuxfleurs/bottin/raw/branch/main/bottin.png" style="height: 150px; display: block; margin-left: auto; margin-right: auto" />
|
<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)
|
- Access control through an ACL (hardcoded in the configuration file)
|
||||||
|
|
||||||
|
|
||||||
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/lxpz/bottin_amd64).
|
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/dxflrs/bottin) (built in `default.nix`).
|
||||||
An example for running Bottin on a Nomad cluster can be found in `bottin.hcl.example`.
|
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
|
Bottin takes a single command line argument, `-config <filename>`, which is the
|
||||||
|
@ -44,6 +44,17 @@ Bottin requires go 1.13 or later.
|
||||||
To build Bottin, clone this repository outside of your `$GOPATH`.
|
To build Bottin, clone this repository outside of your `$GOPATH`.
|
||||||
Then, run `make` in the root of the repo.
|
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
|
## Server initialization
|
||||||
|
|
||||||
|
|
55
default.nix
Normal file
55
default.nix
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
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
Normal file
79
flake.lock
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
{
|
||||||
|
"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
Normal file
39
flake.nix
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
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
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-ldap/ldap/v3 v3.3.0
|
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/hashicorp/consul/api v1.3.0
|
github.com/hashicorp/consul/api v1.3.0
|
||||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.4.2
|
||||||
|
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect
|
||||||
)
|
)
|
||||||
|
|
14
go.sum
14
go.sum
|
@ -1,5 +1,3 @@
|
||||||
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/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 h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
@ -9,11 +7,6 @@ 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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/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 h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
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=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
|
@ -49,10 +42,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/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 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
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 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
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-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/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
||||||
|
@ -84,14 +77,13 @@ 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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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-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-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 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
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-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-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/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 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
|
25
goldap/abandon_request.go
Normal file
25
goldap/abandon_request.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AbandonRequest ::= [APPLICATION 16] MessageID
|
||||||
|
|
||||||
|
func readAbandonRequest(bytes *Bytes) (ret AbandonRequest, err error) {
|
||||||
|
var mes MessageID
|
||||||
|
mes, err = readTaggedMessageID(bytes, classApplication, TagAbandonRequest)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAbandonRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = AbandonRequest(mes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (abandon AbandonRequest) size() int {
|
||||||
|
return MessageID(abandon).sizeTagged(TagAbandonRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (abandon AbandonRequest) write(bytes *Bytes) int {
|
||||||
|
return MessageID(abandon).writeTagged(bytes, classApplication, TagAbandonRequest)
|
||||||
|
}
|
53
goldap/add_request.go
Normal file
53
goldap/add_request.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AddRequest ::= [APPLICATION 8] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// attributes AttributeList }
|
||||||
|
|
||||||
|
func (add *AddRequest) Entry() LDAPDN {
|
||||||
|
return add.entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (add *AddRequest) Attributes() AttributeList {
|
||||||
|
return add.attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAddRequest(bytes *Bytes) (ret AddRequest, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagAddRequest, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAddRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (add *AddRequest) readComponents(bytes *Bytes) (err error) {
|
||||||
|
add.entry, err = readLDAPDN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
add.attributes, err = readAttributeList(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (add AddRequest) size() (size int) {
|
||||||
|
size += add.entry.size()
|
||||||
|
size += add.attributes.size()
|
||||||
|
size += sizeTagAndLength(TagAddRequest, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (add AddRequest) write(bytes *Bytes) (size int) {
|
||||||
|
size += add.attributes.write(bytes)
|
||||||
|
size += add.entry.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagAddRequest, size)
|
||||||
|
return
|
||||||
|
}
|
28
goldap/add_response.go
Normal file
28
goldap/add_response.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AddResponse ::= [APPLICATION 9] LDAPResult
|
||||||
|
|
||||||
|
func (l *AddResponse) SetResultCode(code int) {
|
||||||
|
l.resultCode = ENUMERATED(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAddResponse(bytes *Bytes) (ret AddResponse, err error) {
|
||||||
|
var res LDAPResult
|
||||||
|
res, err = readTaggedLDAPResult(bytes, classApplication, TagAddResponse)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAddResponse:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = AddResponse(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AddResponse) size() int {
|
||||||
|
return LDAPResult(a).sizeTagged(TagAddResponse)
|
||||||
|
}
|
||||||
|
func (a AddResponse) write(bytes *Bytes) int {
|
||||||
|
return LDAPResult(a).writeTagged(bytes, classApplication, TagAddResponse)
|
||||||
|
}
|
760
goldap/asn1.go
Normal file
760
goldap/asn1.go
Normal file
|
@ -0,0 +1,760 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
// Below code is largely inspired from the standard golang library encoding/asn
|
||||||
|
// If put BEGIN / END tags in the comments to give the original library name
|
||||||
|
import (
|
||||||
|
// "errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
// "strconv"
|
||||||
|
// "time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// BEGIN: encoding/asn1/common.go
|
||||||
|
//
|
||||||
|
|
||||||
|
// Copyright 2009 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.
|
||||||
|
const (
|
||||||
|
tagBoolean = 1
|
||||||
|
tagInteger = 2
|
||||||
|
// tagBitString = 3
|
||||||
|
tagOctetString = 4
|
||||||
|
// tagOID = 6
|
||||||
|
tagEnum = 10
|
||||||
|
// tagUTF8String = 12
|
||||||
|
tagSequence = 16
|
||||||
|
tagSet = 17
|
||||||
|
// tagPrintableString = 19
|
||||||
|
// tagT61String = 20
|
||||||
|
// tagIA5String = 22
|
||||||
|
// tagUTCTime = 23
|
||||||
|
// tagGeneralizedTime = 24
|
||||||
|
tagGeneralString = 27
|
||||||
|
)
|
||||||
|
|
||||||
|
var tagNames = map[int]string{
|
||||||
|
tagBoolean: "BOOLEAN",
|
||||||
|
tagInteger: "INTEGER",
|
||||||
|
tagOctetString: "OCTET STRING",
|
||||||
|
tagEnum: "ENUM",
|
||||||
|
tagSequence: "SEQUENCE",
|
||||||
|
tagSet: "SET",
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
classUniversal = 0
|
||||||
|
classApplication = 1
|
||||||
|
classContextSpecific = 2
|
||||||
|
// classPrivate = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
var classNames = map[int]string{
|
||||||
|
classUniversal: "UNIVERSAL",
|
||||||
|
classApplication: "APPLICATION",
|
||||||
|
classContextSpecific: "CONTEXT SPECIFIC",
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
isCompound = true
|
||||||
|
isNotCompound = false
|
||||||
|
)
|
||||||
|
|
||||||
|
var compoundNames = map[bool]string{
|
||||||
|
isCompound: "COMPOUND",
|
||||||
|
isNotCompound: "NOT COMPOUND",
|
||||||
|
}
|
||||||
|
|
||||||
|
type TagAndLength struct {
|
||||||
|
Class, Tag, Length int
|
||||||
|
IsCompound bool
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// END: encoding/asn1/common.go
|
||||||
|
//
|
||||||
|
|
||||||
|
func (t *TagAndLength) Expect(class int, tag int, isCompound bool) (err error) {
|
||||||
|
err = t.ExpectClass(class)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("Expect: %s.", err)}
|
||||||
|
}
|
||||||
|
err = t.ExpectTag(tag)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("Expect: %s.", err)}
|
||||||
|
}
|
||||||
|
err = t.ExpectCompound(isCompound)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("Expect: %s.", err)}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (t *TagAndLength) ExpectClass(class int) (err error) {
|
||||||
|
if class != t.Class {
|
||||||
|
err = SyntaxError{fmt.Sprintf("ExpectClass: wrong tag class: got %d (%s), expected %d (%s)", t.Class, classNames[t.Class], class, classNames[class])}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (t *TagAndLength) ExpectTag(tag int) (err error) {
|
||||||
|
if tag != t.Tag {
|
||||||
|
err = SyntaxError{fmt.Sprintf("ExpectTag: wrong tag value: got %d (%s), expected %d (%s)", t.Tag, tagNames[t.Tag], tag, tagNames[tag])}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (t *TagAndLength) ExpectCompound(isCompound bool) (err error) {
|
||||||
|
if isCompound != t.IsCompound {
|
||||||
|
err = SyntaxError{fmt.Sprintf("ExpectCompound: wrong tag compound: got %t (%s), expected %t (%s)", t.IsCompound, compoundNames[t.IsCompound], isCompound, compoundNames[isCompound])}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) {
|
||||||
|
ret, offset, err = parseTagAndLength(bytes, initOffset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// BEGIN encoding/asn1/asn1.go
|
||||||
|
//
|
||||||
|
|
||||||
|
// Copyright 2009 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 asn1 implements parsing of DER-encoded ASN.1 data structures,
|
||||||
|
// as defined in ITU-T Rec X.690.
|
||||||
|
//
|
||||||
|
// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,''
|
||||||
|
// http://luca.ntop.org/Teaching/Appunti/asn1.html.
|
||||||
|
// package asn1
|
||||||
|
|
||||||
|
// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc
|
||||||
|
// are different encoding formats for those objects. Here, we'll be dealing
|
||||||
|
// with DER, the Distinguished Encoding Rules. DER is used in X.509 because
|
||||||
|
// it's fast to parse and, unlike BER, has a unique encoding for every object.
|
||||||
|
// When calculating hashes over objects, it's important that the resulting
|
||||||
|
// bytes be the same at both ends and DER removes this margin of error.
|
||||||
|
//
|
||||||
|
// ASN.1 is very complex and this package doesn't attempt to implement
|
||||||
|
// everything by any means.
|
||||||
|
|
||||||
|
//import (
|
||||||
|
// "fmt"
|
||||||
|
// "math/big"
|
||||||
|
// "reflect"
|
||||||
|
// "strconv"
|
||||||
|
// "time"
|
||||||
|
//)
|
||||||
|
|
||||||
|
// A StructuralError suggests that the ASN.1 data is valid, but the Go type
|
||||||
|
// which is receiving it doesn't match.
|
||||||
|
type StructuralError struct {
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }
|
||||||
|
|
||||||
|
// A SyntaxError suggests that the ASN.1 data is invalid.
|
||||||
|
type SyntaxError struct {
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }
|
||||||
|
|
||||||
|
// We start by dealing with each of the primitive types in turn.
|
||||||
|
|
||||||
|
// BOOLEAN
|
||||||
|
|
||||||
|
func parseBool(bytes []byte) (ret bool, err error) {
|
||||||
|
if len(bytes) > 1 {
|
||||||
|
err = SyntaxError{"invalid boolean: should be encoded on one byte only"}
|
||||||
|
return
|
||||||
|
} else if len(bytes) == 0 {
|
||||||
|
err = SyntaxError{"invalid boolean: no data to read"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DER demands that "If the encoding represents the boolean value TRUE,
|
||||||
|
// its single contents octet shall have all eight bits set to one."
|
||||||
|
// Thus only 0 and 255 are valid encoded values.
|
||||||
|
switch bytes[0] {
|
||||||
|
case 0:
|
||||||
|
ret = false
|
||||||
|
case 0xff:
|
||||||
|
ret = true
|
||||||
|
default:
|
||||||
|
err = SyntaxError{"invalid boolean: should be 0x00 of 0xFF"}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func sizeBool(b bool) int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeBool(bytes *Bytes, b bool) int {
|
||||||
|
if b == false {
|
||||||
|
return bytes.writeBytes([]byte{0x00})
|
||||||
|
} else {
|
||||||
|
return bytes.writeBytes([]byte{0xff})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// INTEGER
|
||||||
|
|
||||||
|
// parseInt64 treats the given bytes as a big-endian, signed integer and
|
||||||
|
// returns the result.
|
||||||
|
func parseInt64(bytes []byte) (ret int64, err error) {
|
||||||
|
if len(bytes) > 8 {
|
||||||
|
// We'll overflow an int64 in this case.
|
||||||
|
err = StructuralError{"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 sizeInt64(i int64) int {
|
||||||
|
n := 1
|
||||||
|
|
||||||
|
for i > 127 {
|
||||||
|
n++
|
||||||
|
i >>= 8
|
||||||
|
}
|
||||||
|
|
||||||
|
for i < -128 {
|
||||||
|
n++
|
||||||
|
i >>= 8
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
bytes.writeBytes(buf[:n])
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseInt treats the given bytes as a big-endian, signed integer and returns
|
||||||
|
// the result.
|
||||||
|
func parseInt32(bytes []byte) (int32, error) {
|
||||||
|
ret64, err := parseInt64(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if ret64 != int64(int32(ret64)) {
|
||||||
|
return 0, StructuralError{"integer too large"}
|
||||||
|
}
|
||||||
|
return int32(ret64), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sizeInt32(i int32) int {
|
||||||
|
return sizeInt64(int64(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeInt32(bytes *Bytes, i int32) int {
|
||||||
|
return writeInt64(bytes, int64(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
var bigOne = big.NewInt(1)
|
||||||
|
|
||||||
|
// // parseBigInt treats the given bytes as a big-endian, signed integer and returns
|
||||||
|
// // the result.
|
||||||
|
// func parseBigInt(bytes []byte) *big.Int {
|
||||||
|
// ret := new(big.Int)
|
||||||
|
// if len(bytes) > 0 && bytes[0]&0x80 == 0x80 {
|
||||||
|
// // This is a negative number.
|
||||||
|
// notBytes := make([]byte, len(bytes))
|
||||||
|
// for i := range notBytes {
|
||||||
|
// notBytes[i] = ^bytes[i]
|
||||||
|
// }
|
||||||
|
// ret.SetBytes(notBytes)
|
||||||
|
// ret.Add(ret, bigOne)
|
||||||
|
// ret.Neg(ret)
|
||||||
|
// return ret
|
||||||
|
// }
|
||||||
|
// ret.SetBytes(bytes)
|
||||||
|
// return ret
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // BIT STRING
|
||||||
|
|
||||||
|
// // BitString is the structure to use when you want an ASN.1 BIT STRING type. A
|
||||||
|
// // bit string is padded up to the nearest byte in memory and the number of
|
||||||
|
// // valid bits is recorded. Padding bits will be zero.
|
||||||
|
// type BitString struct {
|
||||||
|
// Bytes []byte // bits packed into bytes.
|
||||||
|
// BitLength int // length in bits.
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // At returns the bit at the given index. If the index is out of range it
|
||||||
|
// // returns false.
|
||||||
|
// func (b BitString) At(i int) int {
|
||||||
|
// if i < 0 || i >= b.BitLength {
|
||||||
|
// return 0
|
||||||
|
// }
|
||||||
|
// x := i / 8
|
||||||
|
// y := 7 - uint(i%8)
|
||||||
|
// return int(b.Bytes[x]>>y) & 1
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // RightAlign returns a slice where the padding bits are at the beginning. The
|
||||||
|
// // slice may share memory with the BitString.
|
||||||
|
// func (b BitString) RightAlign() []byte {
|
||||||
|
// shift := uint(8 - (b.BitLength % 8))
|
||||||
|
// if shift == 8 || len(b.Bytes) == 0 {
|
||||||
|
// return b.Bytes
|
||||||
|
// }
|
||||||
|
|
||||||
|
// a := make([]byte, len(b.Bytes))
|
||||||
|
// a[0] = b.Bytes[0] >> shift
|
||||||
|
// for i := 1; i < len(b.Bytes); i++ {
|
||||||
|
// a[i] = b.Bytes[i-1] << (8 - shift)
|
||||||
|
// a[i] |= b.Bytes[i] >> shift
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return a
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // parseBitString parses an ASN.1 bit string from the given byte slice and returns it.
|
||||||
|
// func parseBitString(bytes []byte) (ret BitString, err error) {
|
||||||
|
// if len(bytes) == 0 {
|
||||||
|
// err = SyntaxError{"zero length BIT STRING"}
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// paddingBits := int(bytes[0])
|
||||||
|
// if paddingBits > 7 ||
|
||||||
|
// len(bytes) == 1 && paddingBits > 0 ||
|
||||||
|
// bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {
|
||||||
|
// err = SyntaxError{"invalid padding bits in BIT STRING"}
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// ret.BitLength = (len(bytes)-1)*8 - paddingBits
|
||||||
|
// ret.Bytes = bytes[1:]
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// OBJECT IDENTIFIER
|
||||||
|
|
||||||
|
// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
|
||||||
|
// type ObjectIdentifier []int
|
||||||
|
|
||||||
|
// // Equal reports whether oi and other represent the same identifier.
|
||||||
|
// func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
|
||||||
|
// if len(oi) != len(other) {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// for i := 0; i < len(oi); i++ {
|
||||||
|
// if oi[i] != other[i] {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (oi ObjectIdentifier) String() string {
|
||||||
|
// var s string
|
||||||
|
|
||||||
|
// for i, v := range oi {
|
||||||
|
// if i > 0 {
|
||||||
|
// s += "."
|
||||||
|
// }
|
||||||
|
// s += strconv.Itoa(v)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return s
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and
|
||||||
|
// // returns it. An object identifier is a sequence of variable length integers
|
||||||
|
// // that are assigned in a hierarchy.
|
||||||
|
// func parseObjectIdentifier(bytes []byte) (s []int, err error) {
|
||||||
|
// if len(bytes) == 0 {
|
||||||
|
// err = SyntaxError{"zero length OBJECT IDENTIFIER"}
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // In the worst case, we get two elements from the first byte (which is
|
||||||
|
// // encoded differently) and then every varint is a single byte long.
|
||||||
|
// s = make([]int, len(bytes)+1)
|
||||||
|
|
||||||
|
// // The first varint is 40*value1 + value2:
|
||||||
|
// // According to this packing, value1 can take the values 0, 1 and 2 only.
|
||||||
|
// // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
|
||||||
|
// // then there are no restrictions on value2.
|
||||||
|
// v, offset, err := parseBase128Int(bytes, 0)
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if v < 80 {
|
||||||
|
// s[0] = v / 40
|
||||||
|
// s[1] = v % 40
|
||||||
|
// } else {
|
||||||
|
// s[0] = 2
|
||||||
|
// s[1] = v - 80
|
||||||
|
// }
|
||||||
|
|
||||||
|
// i := 2
|
||||||
|
// for ; offset < len(bytes); i++ {
|
||||||
|
// v, offset, err = parseBase128Int(bytes, offset)
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// s[i] = v
|
||||||
|
// }
|
||||||
|
// s = s[0:i]
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ENUMERATED
|
||||||
|
|
||||||
|
// An Enumerated is represented as a plain int.
|
||||||
|
type Enumerated int
|
||||||
|
|
||||||
|
// FLAG
|
||||||
|
|
||||||
|
// A Flag accepts any data and is set to true if present.
|
||||||
|
type Flag bool
|
||||||
|
|
||||||
|
// parseBase128Int parses a base-128 encoded int from the given offset in the
|
||||||
|
// given byte slice. It returns the value and the new offset.
|
||||||
|
func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) {
|
||||||
|
offset = initOffset
|
||||||
|
for shifted := 0; offset < len(bytes); shifted++ {
|
||||||
|
if shifted > 4 {
|
||||||
|
err = StructuralError{"base 128 integer too large"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret <<= 7
|
||||||
|
b := bytes[offset]
|
||||||
|
ret |= int(b & 0x7f)
|
||||||
|
offset++
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = SyntaxError{"truncated base 128 integer"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func sizeBase128Int(value int) (size int) {
|
||||||
|
for i := value; i > 0; i >>= 7 {
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write start as the end of the slice and goes back
|
||||||
|
// We assume we have enough size
|
||||||
|
func writeBase128Int(bytes *Bytes, value int) (size int) {
|
||||||
|
for ; value > 0 || size == 0; value >>= 7 { // Write at least one byte even if the value is 0
|
||||||
|
// Get the 7 lowest bits
|
||||||
|
b := byte(value) & 0x7f
|
||||||
|
if value < 128 {
|
||||||
|
b |= 0x80
|
||||||
|
}
|
||||||
|
bytes.writeBytes([]byte{b})
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// // UTCTime
|
||||||
|
|
||||||
|
// func parseUTCTime(bytes []byte) (ret time.Time, err error) {
|
||||||
|
// s := string(bytes)
|
||||||
|
// ret, err = time.Parse("0601021504Z0700", s)
|
||||||
|
// if err != nil {
|
||||||
|
// ret, err = time.Parse("060102150405Z0700", s)
|
||||||
|
// }
|
||||||
|
// if err == nil && ret.Year() >= 2050 {
|
||||||
|
// // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
|
||||||
|
// ret = ret.AddDate(-100, 0, 0)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // parseGeneralizedTime parses the GeneralizedTime from the given byte slice
|
||||||
|
// // and returns the resulting time.
|
||||||
|
// func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
|
||||||
|
// return time.Parse("20060102150405Z0700", string(bytes))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // PrintableString
|
||||||
|
|
||||||
|
// // parsePrintableString parses a ASN.1 PrintableString from the given byte
|
||||||
|
// // array and returns it.
|
||||||
|
// func parsePrintableString(bytes []byte) (ret string, err error) {
|
||||||
|
// for _, b := range bytes {
|
||||||
|
// if !isPrintable(b) {
|
||||||
|
// err = SyntaxError{"PrintableString contains invalid character"}
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ret = string(bytes)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // isPrintable returns true iff the given b is in the ASN.1 PrintableString set.
|
||||||
|
// func isPrintable(b byte) bool {
|
||||||
|
// return 'a' <= b && b <= 'z' ||
|
||||||
|
// 'A' <= b && b <= 'Z' ||
|
||||||
|
// '0' <= b && b <= '9' ||
|
||||||
|
// '\'' <= b && b <= ')' ||
|
||||||
|
// '+' <= b && b <= '/' ||
|
||||||
|
// b == ' ' ||
|
||||||
|
// b == ':' ||
|
||||||
|
// b == '=' ||
|
||||||
|
// b == '?' ||
|
||||||
|
// // This is technically not allowed in a PrintableString.
|
||||||
|
// // However, x509 certificates with wildcard strings don't
|
||||||
|
// // always use the correct string type so we permit it.
|
||||||
|
// b == '*'
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // IA5String
|
||||||
|
|
||||||
|
// // parseIA5String parses a ASN.1 IA5String (ASCII string) from the given
|
||||||
|
// // byte slice and returns it.
|
||||||
|
// func parseIA5String(bytes []byte) (ret string, err error) {
|
||||||
|
// for _, b := range bytes {
|
||||||
|
// if b >= 0x80 {
|
||||||
|
// err = SyntaxError{"IA5String contains invalid character"}
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ret = string(bytes)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // T61String
|
||||||
|
|
||||||
|
// // parseT61String parses a ASN.1 T61String (8-bit clean string) from the given
|
||||||
|
// // byte slice and returns it.
|
||||||
|
// func parseT61String(bytes []byte) (ret string, err error) {
|
||||||
|
// return string(bytes), nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// UTF8String
|
||||||
|
|
||||||
|
// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte
|
||||||
|
// array and returns it.
|
||||||
|
// func parseUTF8String(bytes []byte) (ret string, err error) {
|
||||||
|
// return string(bytes), nil
|
||||||
|
// }
|
||||||
|
// func sizeUTF8String(s string) int {
|
||||||
|
// return len(s)
|
||||||
|
// }
|
||||||
|
// func writeUTF8String(bytes *Bytes, s string) int {
|
||||||
|
// return bytes.writeString(s)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Octet string
|
||||||
|
func parseOctetString(bytes []byte) (ret []byte, err error) {
|
||||||
|
return bytes, nil
|
||||||
|
}
|
||||||
|
func sizeOctetString(s []byte) int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
func writeOctetString(bytes *Bytes, s []byte) int {
|
||||||
|
return bytes.writeBytes(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A RawValue represents an undecoded ASN.1 object.
|
||||||
|
type RawValue struct {
|
||||||
|
Class, Tag int
|
||||||
|
IsCompound bool
|
||||||
|
Bytes []byte
|
||||||
|
FullBytes []byte // includes the tag and length
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawContent is used to signal that the undecoded, DER data needs to be
|
||||||
|
// preserved for a struct. To use it, the first field of the struct must have
|
||||||
|
// this type. It's an error for any of the other fields to have this type.
|
||||||
|
type RawContent []byte
|
||||||
|
|
||||||
|
// Tagging
|
||||||
|
|
||||||
|
// parseTagAndLength parses an ASN.1 tag and length pair from the given offset
|
||||||
|
// into a byte slice. It returns the parsed data and the new offset. SET and
|
||||||
|
// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we
|
||||||
|
// don't distinguish between ordered and unordered objects in this code.
|
||||||
|
func parseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) {
|
||||||
|
offset = initOffset
|
||||||
|
b := bytes[offset]
|
||||||
|
offset++
|
||||||
|
ret.Class = int(b >> 6)
|
||||||
|
ret.IsCompound = b&0x20 == 0x20
|
||||||
|
ret.Tag = int(b & 0x1f)
|
||||||
|
|
||||||
|
// If the bottom five bits are set, then the tag number is actually base 128
|
||||||
|
// encoded afterwards
|
||||||
|
if ret.Tag == 0x1f {
|
||||||
|
ret.Tag, offset, err = parseBase128Int(bytes, offset)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if offset >= len(bytes) {
|
||||||
|
err = SyntaxError{"truncated tag or length"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b = bytes[offset]
|
||||||
|
offset++
|
||||||
|
if b&0x80 == 0 {
|
||||||
|
// The length is encoded in the bottom 7 bits.
|
||||||
|
ret.Length = int(b & 0x7f)
|
||||||
|
} else {
|
||||||
|
// Bottom 7 bits give the number of length bytes to follow.
|
||||||
|
numBytes := int(b & 0x7f)
|
||||||
|
if numBytes == 0 {
|
||||||
|
err = SyntaxError{"indefinite length found (not DER)"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret.Length = 0
|
||||||
|
for i := 0; i < numBytes; i++ {
|
||||||
|
if offset >= len(bytes) {
|
||||||
|
err = SyntaxError{"truncated tag or length"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b = bytes[offset]
|
||||||
|
offset++
|
||||||
|
if ret.Length >= 1<<23 {
|
||||||
|
// We can't shift ret.length up without
|
||||||
|
// overflowing.
|
||||||
|
err = StructuralError{"length too large"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret.Length <<= 8
|
||||||
|
ret.Length |= int(b)
|
||||||
|
if ret.Length == 0 {
|
||||||
|
// DER requires that lengths be minimal.
|
||||||
|
err = StructuralError{"superfluous leading zeros in length"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// func writeTagAndLength(out *forkableWriter, t tagAndLength) (err error) {
|
||||||
|
// b := uint8(t.class) << 6
|
||||||
|
// if t.isCompound {
|
||||||
|
// b |= 0x20
|
||||||
|
// }
|
||||||
|
// if t.tag >= 31 {
|
||||||
|
// b |= 0x1f
|
||||||
|
// err = out.WriteByte(b)
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// err = marshalBase128Int(out, int64(t.tag))
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// b |= uint8(t.tag)
|
||||||
|
// err = out.WriteByte(b)
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if t.length >= 128 {
|
||||||
|
// l := lengthLength(t.length)
|
||||||
|
// err = out.WriteByte(0x80 | byte(l))
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// err = marshalLength(out, t.length)
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// err = out.WriteByte(byte(t.length))
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
func sizeTagAndLength(tag int, length int) (size int) {
|
||||||
|
// Compute the size of the tag
|
||||||
|
size = 1
|
||||||
|
if tag >= 31 {
|
||||||
|
// Long-form identifier if the tag is greater than 30
|
||||||
|
// http://en.wikipedia.org/wiki/X.690#Identifier_tags_greater_than_30
|
||||||
|
size += sizeBase128Int(tag)
|
||||||
|
}
|
||||||
|
// Compute the size of the length using the definite form
|
||||||
|
// http://en.wikipedia.org/wiki/X.690#The_definite_form
|
||||||
|
size += 1
|
||||||
|
if length >= 128 {
|
||||||
|
size += 1
|
||||||
|
for length > 255 {
|
||||||
|
size++
|
||||||
|
length >>= 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTagAndLength(bytes *Bytes, t TagAndLength) (size int) {
|
||||||
|
// We are writing backward, so write the length bytes first
|
||||||
|
if t.Length < 0 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
bytes.writeBytes([]byte{byte(0x80 | byte(lengthBytes))})
|
||||||
|
size += lengthBytes + 1
|
||||||
|
|
||||||
|
} else if t.Length < 128 {
|
||||||
|
size += bytes.writeBytes([]byte{byte(t.Length)})
|
||||||
|
}
|
||||||
|
// Then write the tag
|
||||||
|
b := uint8(t.Class) << 6
|
||||||
|
if t.IsCompound {
|
||||||
|
b |= 0x20
|
||||||
|
}
|
||||||
|
if t.Tag >= 31 {
|
||||||
|
b |= 0x1f
|
||||||
|
size += writeBase128Int(bytes, t.Tag)
|
||||||
|
} else {
|
||||||
|
b |= uint8(t.Tag)
|
||||||
|
}
|
||||||
|
size += bytes.writeBytes([]byte{byte(b)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// END encoding/asn1/asn1.go
|
||||||
|
//
|
54
goldap/asn1_test.go
Normal file
54
goldap/asn1_test.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
44
goldap/assertion_value.go
Normal file
44
goldap/assertion_value.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AssertionValue ::= OCTET STRING
|
||||||
|
|
||||||
|
func readAssertionValue(bytes *Bytes) (assertionvalue AssertionValue, err error) {
|
||||||
|
var octetstring OCTETSTRING
|
||||||
|
octetstring, err = readOCTETSTRING(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAssertionValue:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assertionvalue = AssertionValue(octetstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTaggedAssertionValue(bytes *Bytes, class int, tag int) (assertionvalue AssertionValue, err error) {
|
||||||
|
var octetstring OCTETSTRING
|
||||||
|
octetstring, err = readTaggedOCTETSTRING(bytes, class, tag)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedAssertionValue:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assertionvalue = AssertionValue(octetstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion AssertionValue) size() int {
|
||||||
|
return OCTETSTRING(assertion).size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion AssertionValue) sizeTagged(tag int) int {
|
||||||
|
return OCTETSTRING(assertion).sizeTagged(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion AssertionValue) write(bytes *Bytes) int {
|
||||||
|
return OCTETSTRING(assertion).write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion AssertionValue) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return OCTETSTRING(assertion).writeTagged(bytes, class, tag)
|
||||||
|
}
|
40
goldap/attribute.go
Normal file
40
goldap/attribute.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Attribute ::= PartialAttribute(WITH COMPONENTS {
|
||||||
|
// ...,
|
||||||
|
// vals (SIZE(1..MAX))})
|
||||||
|
|
||||||
|
func (attribute *Attribute) Type_() AttributeDescription {
|
||||||
|
return attribute.type_
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attribute *Attribute) Vals() []AttributeValue {
|
||||||
|
return attribute.vals
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAttribute(bytes *Bytes) (ret Attribute, err error) {
|
||||||
|
var par PartialAttribute
|
||||||
|
par, err = readPartialAttribute(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAttribute:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(par.vals) == 0 {
|
||||||
|
err = LdapError{"readAttribute: expecting at least one value"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = Attribute(par)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attribute Attribute) size() (size int) {
|
||||||
|
return PartialAttribute(attribute).size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attribute Attribute) write(bytes *Bytes) (size int) {
|
||||||
|
return PartialAttribute(attribute).write(bytes)
|
||||||
|
}
|
50
goldap/attribute_description.go
Normal file
50
goldap/attribute_description.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeDescription ::= LDAPString
|
||||||
|
// -- Constrained to <attributedescription>
|
||||||
|
// -- [RFC4512]
|
||||||
|
|
||||||
|
func (description AttributeDescription) Pointer() *AttributeDescription { return &description }
|
||||||
|
|
||||||
|
func readAttributeDescription(bytes *Bytes) (ret AttributeDescription, err error) {
|
||||||
|
var ldapstring LDAPString
|
||||||
|
ldapstring, err = readLDAPString(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAttributeDescription:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// @TODO: check RFC4512
|
||||||
|
ret = AttributeDescription(ldapstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTaggedAttributeDescription(bytes *Bytes, class int, tag int) (ret AttributeDescription, err error) {
|
||||||
|
var ldapstring LDAPString
|
||||||
|
ldapstring, err = readTaggedLDAPString(bytes, class, tag)
|
||||||
|
// @TODO: check RFC4512
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedAttributeDescription:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = AttributeDescription(ldapstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (description AttributeDescription) size() int {
|
||||||
|
return LDAPString(description).size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (description AttributeDescription) sizeTagged(tag int) int {
|
||||||
|
return LDAPString(description).sizeTagged(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (description AttributeDescription) write(bytes *Bytes) int {
|
||||||
|
return LDAPString(description).write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (description AttributeDescription) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return LDAPString(description).writeTagged(bytes, class, tag)
|
||||||
|
}
|
43
goldap/attribute_list.go
Normal file
43
goldap/attribute_list.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeList ::= SEQUENCE OF attribute Attribute
|
||||||
|
|
||||||
|
func readAttributeList(bytes *Bytes) (ret AttributeList, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAttributeList:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (list *AttributeList) readComponents(bytes *Bytes) (err error) {
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
var attr Attribute
|
||||||
|
attr, err = readAttribute(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*list = append(*list, attr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (list AttributeList) size() (size int) {
|
||||||
|
for _, att := range list {
|
||||||
|
size += att.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (list AttributeList) write(bytes *Bytes) (size int) {
|
||||||
|
for i := len(list) - 1; i >= 0; i-- {
|
||||||
|
size += list[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
46
goldap/attribute_selection.go
Normal file
46
goldap/attribute_selection.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeSelection ::= SEQUENCE OF selector LDAPString
|
||||||
|
// -- The LDAPString is constrained to
|
||||||
|
// -- <attributeSelector> in Section 4.5.1.8
|
||||||
|
|
||||||
|
func readAttributeSelection(bytes *Bytes) (attributeSelection AttributeSelection, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, attributeSelection.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAttributeSelection:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (selection *AttributeSelection) readComponents(bytes *Bytes) (err error) {
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
var ldapstring LDAPString
|
||||||
|
ldapstring, err = readLDAPString(bytes)
|
||||||
|
// @TOTO: check <attributeSelector> in Section 4.5.1.8
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*selection = append(*selection, ldapstring)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (selection AttributeSelection) write(bytes *Bytes) (size int) {
|
||||||
|
for i := len(selection) - 1; i >= 0; i-- {
|
||||||
|
size += selection[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (selection AttributeSelection) size() (size int) {
|
||||||
|
for _, selector := range selection {
|
||||||
|
size += selector.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
24
goldap/attribute_value.go
Normal file
24
goldap/attribute_value.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeValue ::= OCTET STRING
|
||||||
|
|
||||||
|
func readAttributeValue(bytes *Bytes) (ret AttributeValue, err error) {
|
||||||
|
octetstring, err := readOCTETSTRING(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAttributeValue:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = AttributeValue(octetstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (value AttributeValue) write(bytes *Bytes) int {
|
||||||
|
return OCTETSTRING(value).write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (value AttributeValue) size() int {
|
||||||
|
return OCTETSTRING(value).size()
|
||||||
|
}
|
77
goldap/attribute_value_assertion.go
Normal file
77
goldap/attribute_value_assertion.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeValueAssertion ::= SEQUENCE {
|
||||||
|
// attributeDesc AttributeDescription,
|
||||||
|
// assertionValue AssertionValue }
|
||||||
|
|
||||||
|
func (assertion *AttributeValueAssertion) AttributeDesc() AttributeDescription {
|
||||||
|
return assertion.attributeDesc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion *AttributeValueAssertion) AssertionValue() AssertionValue {
|
||||||
|
return assertion.assertionValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAttributeValueAssertion(bytes *Bytes) (ret AttributeValueAssertion, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAttributeValueAssertion:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTaggedAttributeValueAssertion(bytes *Bytes, class int, tag int) (ret AttributeValueAssertion, err error) {
|
||||||
|
err = bytes.ReadSubBytes(class, tag, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedAttributeValueAssertion:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion *AttributeValueAssertion) readComponents(bytes *Bytes) (err error) {
|
||||||
|
assertion.attributeDesc, err = readAttributeDescription(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assertion.assertionValue, err = readAssertionValue(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion AttributeValueAssertion) write(bytes *Bytes) (size int) {
|
||||||
|
size += assertion.assertionValue.write(bytes)
|
||||||
|
size += assertion.attributeDesc.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion AttributeValueAssertion) writeTagged(bytes *Bytes, class int, tag int) (size int) {
|
||||||
|
size += assertion.assertionValue.write(bytes)
|
||||||
|
size += assertion.attributeDesc.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion AttributeValueAssertion) size() (size int) {
|
||||||
|
size += assertion.attributeDesc.size()
|
||||||
|
size += assertion.assertionValue.size()
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertion AttributeValueAssertion) sizeTagged(tag int) (size int) {
|
||||||
|
size += assertion.attributeDesc.size()
|
||||||
|
size += assertion.assertionValue.size()
|
||||||
|
size += sizeTagAndLength(tag, size)
|
||||||
|
return
|
||||||
|
}
|
37
goldap/authentication_choice.go
Normal file
37
goldap/authentication_choice.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// AuthenticationChoice ::= CHOICE {
|
||||||
|
// simple [0] OCTET STRING,
|
||||||
|
// -- 1 and 2 reserved
|
||||||
|
// sasl [3] SaslCredentials,
|
||||||
|
// ... }
|
||||||
|
|
||||||
|
func readAuthenticationChoice(bytes *Bytes) (ret AuthenticationChoice, err error) {
|
||||||
|
tagAndLength, err := bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tagAndLength.ExpectClass(classContextSpecific)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch tagAndLength.Tag {
|
||||||
|
case TagAuthenticationChoiceSimple:
|
||||||
|
ret, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagAuthenticationChoiceSimple)
|
||||||
|
case TagAuthenticationChoiceSaslCredentials:
|
||||||
|
ret, err = readSaslCredentials(bytes)
|
||||||
|
default:
|
||||||
|
err = LdapError{fmt.Sprintf("readAuthenticationChoice: invalid tag value %d for AuthenticationChoice", tagAndLength.Tag)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
93
goldap/bind_request.go
Normal file
93
goldap/bind_request.go
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// BindRequest ::= [APPLICATION 0] SEQUENCE {
|
||||||
|
// version INTEGER (1 .. 127),
|
||||||
|
// name LDAPDN,
|
||||||
|
// authentication AuthenticationChoice }
|
||||||
|
|
||||||
|
func (request *BindRequest) Name() LDAPDN {
|
||||||
|
return request.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request *BindRequest) Authentication() AuthenticationChoice {
|
||||||
|
return request.authentication
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request *BindRequest) AuthenticationSimple() OCTETSTRING {
|
||||||
|
return request.Authentication().(OCTETSTRING)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request *BindRequest) AuthenticationChoice() string {
|
||||||
|
switch request.Authentication().(type) {
|
||||||
|
case OCTETSTRING:
|
||||||
|
return "simple"
|
||||||
|
case SaslCredentials:
|
||||||
|
return "sasl"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func readBindRequest(bytes *Bytes) (bindrequest BindRequest, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagBindRequest, bindrequest.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readBindRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request *BindRequest) readComponents(bytes *Bytes) (err error) {
|
||||||
|
request.version, err = readINTEGER(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !(request.version >= BindRequestVersionMin && request.version <= BindRequestVersionMax) {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents: invalid version %d, must be between %d and %d", request.version, BindRequestVersionMin, BindRequestVersionMax)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
request.name, err = readLDAPDN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
request.authentication, err = readAuthenticationChoice(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request BindRequest) write(bytes *Bytes) (size int) {
|
||||||
|
switch request.authentication.(type) {
|
||||||
|
case OCTETSTRING:
|
||||||
|
size += request.authentication.(OCTETSTRING).writeTagged(bytes, classContextSpecific, TagAuthenticationChoiceSimple)
|
||||||
|
case SaslCredentials:
|
||||||
|
size += request.authentication.(SaslCredentials).writeTagged(bytes, classContextSpecific, TagAuthenticationChoiceSaslCredentials)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unknown authentication choice: %#v", request.authentication))
|
||||||
|
}
|
||||||
|
size += request.name.write(bytes)
|
||||||
|
size += request.version.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagBindRequest, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request BindRequest) size() (size int) {
|
||||||
|
size += request.version.size()
|
||||||
|
size += request.name.size()
|
||||||
|
switch request.authentication.(type) {
|
||||||
|
case OCTETSTRING:
|
||||||
|
size += request.authentication.(OCTETSTRING).sizeTagged(TagAuthenticationChoiceSimple)
|
||||||
|
case SaslCredentials:
|
||||||
|
size += request.authentication.(SaslCredentials).sizeTagged(TagAuthenticationChoiceSaslCredentials)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unknown authentication choice: %#v", request.authentication))
|
||||||
|
}
|
||||||
|
|
||||||
|
size += sizeTagAndLength(TagBindRequest, size)
|
||||||
|
return
|
||||||
|
}
|
56
goldap/bind_response.go
Normal file
56
goldap/bind_response.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// BindResponse ::= [APPLICATION 1] SEQUENCE {
|
||||||
|
// COMPONENTS OF LDAPResult,
|
||||||
|
// serverSaslCreds [7] OCTET STRING OPTIONAL }
|
||||||
|
|
||||||
|
func readBindResponse(bytes *Bytes) (bindresponse BindResponse, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagBindResponse, bindresponse.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readBindResponse:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (response *BindResponse) readComponents(bytes *Bytes) (err error) {
|
||||||
|
response.LDAPResult.readComponents(bytes)
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == TagBindResponseServerSaslCreds {
|
||||||
|
var serverSaslCreds OCTETSTRING
|
||||||
|
serverSaslCreds, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagBindResponseServerSaslCreds)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.serverSaslCreds = serverSaslCreds.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (response BindResponse) write(bytes *Bytes) (size int) {
|
||||||
|
if response.serverSaslCreds != nil {
|
||||||
|
size += response.serverSaslCreds.writeTagged(bytes, classContextSpecific, TagBindResponseServerSaslCreds)
|
||||||
|
}
|
||||||
|
size += response.LDAPResult.writeComponents(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagBindResponse, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (response BindResponse) size() (size int) {
|
||||||
|
if response.serverSaslCreds != nil {
|
||||||
|
size += response.serverSaslCreds.sizeTagged(TagBindResponseServerSaslCreds)
|
||||||
|
}
|
||||||
|
size += response.LDAPResult.sizeComponents()
|
||||||
|
size += sizeTagAndLength(TagBindResponse, size)
|
||||||
|
return
|
||||||
|
}
|
62
goldap/boolean.go
Normal file
62
goldap/boolean.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func readBOOLEAN(bytes *Bytes) (ret BOOLEAN, err error) {
|
||||||
|
var value interface{}
|
||||||
|
value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagBoolean, tagBoolean)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readBOOLEAN:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = BOOLEAN(value.(bool))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (boolean BOOLEAN) write(bytes *Bytes) int {
|
||||||
|
return bytes.WritePrimitiveSubBytes(classUniversal, tagBoolean, boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (boolean BOOLEAN) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return bytes.WritePrimitiveSubBytes(class, tag, boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTaggedBOOLEAN(bytes *Bytes, class int, tag int) (ret BOOLEAN, err error) {
|
||||||
|
var value interface{}
|
||||||
|
value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagBoolean)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedBOOLEAN:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = BOOLEAN(value.(bool))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func SizePrimitiveSubBytes(tag int, value interface{}) (size int) {
|
||||||
|
switch value.(type) {
|
||||||
|
case BOOLEAN:
|
||||||
|
size = sizeBool(bool(value.(BOOLEAN)))
|
||||||
|
case INTEGER:
|
||||||
|
size = sizeInt32(int32(value.(INTEGER)))
|
||||||
|
case ENUMERATED:
|
||||||
|
size = sizeInt32(int32(value.(ENUMERATED)))
|
||||||
|
case OCTETSTRING:
|
||||||
|
size = sizeOctetString([]byte(string(value.(OCTETSTRING))))
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("SizePrimitiveSubBytes: invalid value type %v", value))
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (boolean BOOLEAN) size() int {
|
||||||
|
return SizePrimitiveSubBytes(tagBoolean, boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (boolean BOOLEAN) sizeTagged(tag int) int {
|
||||||
|
return SizePrimitiveSubBytes(tag, boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (boolean BOOLEAN) Bool() bool {
|
||||||
|
return bool(boolean)
|
||||||
|
}
|
199
goldap/bytes.go
Normal file
199
goldap/bytes.go
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bytes struct {
|
||||||
|
offset int
|
||||||
|
bytes []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes *Bytes) getBytes() []byte {
|
||||||
|
return bytes.bytes
|
||||||
|
}
|
||||||
|
func NewBytes(offset int, bytes []byte) (ret *Bytes) {
|
||||||
|
return &Bytes{offset: offset, bytes: bytes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes Bytes) Debug() {
|
||||||
|
fmt.Printf("Offset: %d, Bytes: %+v\n", bytes.offset, bytes.bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a string with the hex dump of the bytes around the current offset
|
||||||
|
// The current offset byte is put in brackets
|
||||||
|
// Example: 0x01, [0x02], 0x03
|
||||||
|
func (bytes *Bytes) DumpCurrentBytes() (ret string) {
|
||||||
|
var strings [3]string
|
||||||
|
for i := -1; i <= 1; i++ {
|
||||||
|
if bytes.offset+i >= 0 && bytes.offset+i < len(bytes.bytes) {
|
||||||
|
strings[i+1] = fmt.Sprintf("%#x", bytes.bytes[bytes.offset+i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = fmt.Sprintf("%s, [%s], %s", strings[0], strings[1], strings[2])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes *Bytes) HasMoreData() bool {
|
||||||
|
return bytes.offset < len(bytes.bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes *Bytes) ParseTagAndLength() (ret TagAndLength, err error) {
|
||||||
|
var offset int
|
||||||
|
ret, offset, err = ParseTagAndLength(bytes.bytes, bytes.offset)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("ParseTagAndLength: %s", err.Error())}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
bytes.offset = offset
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes *Bytes) ReadSubBytes(class int, tag int, callback func(bytes *Bytes) error) (err error) {
|
||||||
|
// Check tag
|
||||||
|
tagAndLength, err := bytes.ParseTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())}
|
||||||
|
}
|
||||||
|
err = tagAndLength.Expect(class, tag, isCompound)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())}
|
||||||
|
}
|
||||||
|
|
||||||
|
start := bytes.offset
|
||||||
|
end := bytes.offset + tagAndLength.Length
|
||||||
|
|
||||||
|
// Check we got enough bytes to process
|
||||||
|
if end > len(bytes.bytes) {
|
||||||
|
return LdapError{fmt.Sprintf("ReadSubBytes: data truncated: expecting %d bytes at offset %d", tagAndLength.Length, bytes.offset)}
|
||||||
|
}
|
||||||
|
// Process sub-bytes
|
||||||
|
subBytes := Bytes{offset: 0, bytes: bytes.bytes[start:end]}
|
||||||
|
err = callback(&subBytes)
|
||||||
|
if err != nil {
|
||||||
|
bytes.offset += subBytes.offset
|
||||||
|
err = LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check we got no more bytes to process
|
||||||
|
if subBytes.HasMoreData() {
|
||||||
|
return LdapError{fmt.Sprintf("ReadSubBytes: data too long: %d more bytes to read at offset %d", end-bytes.offset, bytes.offset)}
|
||||||
|
}
|
||||||
|
// Move offset
|
||||||
|
bytes.offset = end
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func SizeSubBytes(tag int, callback func() int) (size int) {
|
||||||
|
size = callback()
|
||||||
|
size += sizeTagAndLength(tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes *Bytes) WritePrimitiveSubBytes(class int, tag int, value interface{}) (size int) {
|
||||||
|
switch value.(type) {
|
||||||
|
case BOOLEAN:
|
||||||
|
size = writeBool(bytes, bool(value.(BOOLEAN)))
|
||||||
|
case INTEGER:
|
||||||
|
size = writeInt32(bytes, int32(value.(INTEGER)))
|
||||||
|
case ENUMERATED:
|
||||||
|
size = writeInt32(bytes, int32(value.(ENUMERATED)))
|
||||||
|
case OCTETSTRING:
|
||||||
|
size = writeOctetString(bytes, []byte(string(value.(OCTETSTRING))))
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("WritePrimitiveSubBytes: invalid value type %v", value))
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(class, isNotCompound, tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes *Bytes) WriteTagAndLength(class int, compound bool, tag int, length int) int {
|
||||||
|
return writeTagAndLength(bytes, TagAndLength{Class: class, IsCompound: compound, Tag: tag, Length: length})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes *Bytes) writeString(s string) (size int) {
|
||||||
|
size = len(s)
|
||||||
|
start := bytes.offset - size
|
||||||
|
if start < 0 {
|
||||||
|
panic("Not enough space for string")
|
||||||
|
}
|
||||||
|
copy(bytes.bytes[start:], s)
|
||||||
|
bytes.offset = start
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes *Bytes) writeBytes(b []byte) (size int) {
|
||||||
|
size = len(b)
|
||||||
|
start := bytes.offset - size
|
||||||
|
if start < 0 {
|
||||||
|
panic("Not enough space for bytes")
|
||||||
|
}
|
||||||
|
copy(bytes.bytes[start:], b)
|
||||||
|
bytes.offset = start
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Parse tag, length and read the a primitive value
|
||||||
|
// Supported types are:
|
||||||
|
// - boolean
|
||||||
|
// - integer (parsed as int32)
|
||||||
|
// - enumerated (parsed as int32)
|
||||||
|
// - UTF8 string
|
||||||
|
// - Octet string
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - class: the expected class value(classUniversal, classApplication, classContextSpecific)
|
||||||
|
// - tag: the expected tag value
|
||||||
|
// - typeTag: the real primitive type to parse (tagBoolean, tagInteger, tagEnym, tagUTF8String, tagOctetString)
|
||||||
|
//
|
||||||
|
func (bytes *Bytes) ReadPrimitiveSubBytes(class int, tag int, typeTag int) (value interface{}, err error) {
|
||||||
|
// Check tag
|
||||||
|
tagAndLength, err := bytes.ParseTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tagAndLength.Expect(class, tag, isNotCompound)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
start := bytes.offset
|
||||||
|
end := bytes.offset + tagAndLength.Length
|
||||||
|
|
||||||
|
// Check we got enough bytes to process
|
||||||
|
if end > len(bytes.bytes) {
|
||||||
|
// err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: data truncated: expecting %d bytes at offset %d but only %d bytes are remaining (start: %d, length: %d, end: %d, len(b): %d, bytes: %#+v)", tagAndLength.Length, *b.offset, len(b.bytes)-start, start, tagAndLength.Length, end, len(b.bytes), b.bytes)}
|
||||||
|
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: data truncated: expecting %d bytes at offset %d but only %d bytes are remaining", tagAndLength.Length, bytes.offset, len(bytes.bytes)-start)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Process sub-bytes
|
||||||
|
subBytes := bytes.bytes[start:end]
|
||||||
|
switch typeTag {
|
||||||
|
case tagBoolean:
|
||||||
|
value, err = parseBool(subBytes)
|
||||||
|
case tagInteger:
|
||||||
|
value, err = parseInt32(subBytes)
|
||||||
|
case tagEnum:
|
||||||
|
value, err = parseInt32(subBytes)
|
||||||
|
case tagOctetString:
|
||||||
|
value, err = parseOctetString(subBytes)
|
||||||
|
default:
|
||||||
|
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: invalid type tag value %d", typeTag)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Move offset
|
||||||
|
bytes.offset = end
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bytes *Bytes) Bytes() []byte {
|
||||||
|
return bytes.bytes
|
||||||
|
}
|
172
goldap/bytes_test.go
Normal file
172
goldap/bytes_test.go
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadPrimitiveSubBytesTestData(t *testing.T) {
|
||||||
|
for i, test := range PrimitiveSubBytesTestData() {
|
||||||
|
value, err := test.bytes.ReadPrimitiveSubBytes(test.class, test.tag, test.typeTag)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d failed: %s", i+1, err)
|
||||||
|
} else if !reflect.DeepEqual(test.value, value) {
|
||||||
|
t.Errorf("#%d: Wrong value %#v, got %#v", i+1, test.value, value)
|
||||||
|
} else if test.offset != test.bytes.offset {
|
||||||
|
t.Errorf("#%d: Wrong Offset, value %#v, got %#v", i+1, test.offset, test.bytes.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSizePrimitiveSubBytesTestData(t *testing.T) {
|
||||||
|
for i, test := range PrimitiveSubBytesTestData() {
|
||||||
|
value, err := test.bytes.ReadPrimitiveSubBytes(test.class, test.tag, test.typeTag)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d failed: %s", i+1, err)
|
||||||
|
} else if !reflect.DeepEqual(test.value, value) {
|
||||||
|
t.Errorf("#%d: Wrong value %#v, got %#v", i+1, test.value, value)
|
||||||
|
} else if test.offset != test.bytes.offset {
|
||||||
|
t.Errorf("#%d: Wrong Offset, value %#v, got %#v", i+1, test.offset, test.bytes.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInt(value int) (ret *int) {
|
||||||
|
ret = &value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrimitiveSubBytesTestSingleData struct {
|
||||||
|
bytes Bytes // Input
|
||||||
|
class int // Expected class
|
||||||
|
tag int // Expected tag
|
||||||
|
typeTag int // Expected type
|
||||||
|
value interface{} // Expected output
|
||||||
|
offset int // Expected offset after processing
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrimitiveSubBytesTestData() []PrimitiveSubBytesTestSingleData {
|
||||||
|
|
||||||
|
return []PrimitiveSubBytesTestSingleData{
|
||||||
|
// Test 1
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 0,
|
||||||
|
bytes: []byte{0x02, 0x01, 0x09},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagInteger,
|
||||||
|
typeTag: tagInteger,
|
||||||
|
value: int32(0x09),
|
||||||
|
offset: 3,
|
||||||
|
},
|
||||||
|
// Test 2
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 0,
|
||||||
|
bytes: []byte{0x02, 0x02, 0x09, 0x87},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagInteger,
|
||||||
|
typeTag: tagInteger,
|
||||||
|
value: int32(0x0987),
|
||||||
|
offset: 4,
|
||||||
|
},
|
||||||
|
// Test 3
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 0,
|
||||||
|
bytes: []byte{0x02, 0x03, 0x09, 0x87, 0x65},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagInteger,
|
||||||
|
typeTag: tagInteger,
|
||||||
|
value: int32(0x098765),
|
||||||
|
offset: 5,
|
||||||
|
},
|
||||||
|
// Test 4
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 0,
|
||||||
|
bytes: []byte{0x02, 0x04, 0x09, 0x87, 0x65, 0x43},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagInteger,
|
||||||
|
typeTag: tagInteger,
|
||||||
|
value: int32(0x09876543),
|
||||||
|
offset: 6,
|
||||||
|
},
|
||||||
|
// Test 5
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 2,
|
||||||
|
bytes: []byte{0x30, 0x03, 0x02, 0x01, 0x0f},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagInteger,
|
||||||
|
typeTag: tagInteger,
|
||||||
|
value: int32(0x0f),
|
||||||
|
offset: 5,
|
||||||
|
},
|
||||||
|
// Test 6
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 2,
|
||||||
|
bytes: []byte{0x30, 0x16, 0x02, 0x01, 0x0f, 0x60, 0x11, 0x02, 0x01, 0x03, 0x04, 0x00, 0xa3, 0x0a, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagInteger,
|
||||||
|
typeTag: tagInteger,
|
||||||
|
value: int32(0x0f),
|
||||||
|
offset: 5,
|
||||||
|
},
|
||||||
|
// Test 7
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 2,
|
||||||
|
bytes: []byte{0x30, 0x19, 0x02, 0x04, 0x7f, 0xff, 0xff, 0xff, 0x60, 0x11, 0x02, 0x01, 0x03, 0x04, 0x00, 0xa3, 0x0a, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagInteger,
|
||||||
|
typeTag: tagInteger,
|
||||||
|
value: int32(0x07fffffff),
|
||||||
|
offset: 8,
|
||||||
|
},
|
||||||
|
// Test 8
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 0,
|
||||||
|
bytes: []byte{0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagOctetString,
|
||||||
|
typeTag: tagOctetString,
|
||||||
|
value: []byte("CRAM-MD5"),
|
||||||
|
offset: 10,
|
||||||
|
},
|
||||||
|
// Test 9
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 0,
|
||||||
|
bytes: []byte{0x04, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagOctetString,
|
||||||
|
typeTag: tagOctetString,
|
||||||
|
value: []byte("Hello, 世界"),
|
||||||
|
offset: 15,
|
||||||
|
},
|
||||||
|
// Test 10
|
||||||
|
{
|
||||||
|
bytes: Bytes{
|
||||||
|
offset: 10,
|
||||||
|
bytes: []byte{0x30, 0x1d, 0x02, 0x01, 0x05, 0x60, 0x18, 0x02, 0x01, 0x03, 0x04, 0x07, 0x6d, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x80, 0x0a, 0x6d, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64},
|
||||||
|
},
|
||||||
|
class: classUniversal,
|
||||||
|
tag: tagOctetString,
|
||||||
|
typeTag: tagOctetString,
|
||||||
|
value: []byte("myLogin"),
|
||||||
|
offset: 19,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
53
goldap/compare_request.go
Normal file
53
goldap/compare_request.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// CompareRequest ::= [APPLICATION 14] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// ava AttributeValueAssertion }
|
||||||
|
|
||||||
|
func (request *CompareRequest) Entry() LDAPDN {
|
||||||
|
return request.entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request *CompareRequest) Ava() *AttributeValueAssertion {
|
||||||
|
return &request.ava
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCompareRequest(bytes *Bytes) (ret CompareRequest, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagCompareRequest, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readCompareRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request *CompareRequest) readComponents(bytes *Bytes) (err error) {
|
||||||
|
request.entry, err = readLDAPDN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
request.ava, err = readAttributeValueAssertion(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request CompareRequest) write(bytes *Bytes) (size int) {
|
||||||
|
size += request.ava.write(bytes)
|
||||||
|
size += request.entry.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagCompareRequest, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (request CompareRequest) size() (size int) {
|
||||||
|
size += request.entry.size()
|
||||||
|
size += request.ava.size()
|
||||||
|
size += sizeTagAndLength(TagCompareRequest, size)
|
||||||
|
return
|
||||||
|
}
|
29
goldap/compare_response.go
Normal file
29
goldap/compare_response.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// CompareResponse ::= [APPLICATION 15] LDAPResult
|
||||||
|
|
||||||
|
func (response *CompareResponse) SetResultCode(code int) {
|
||||||
|
response.resultCode = ENUMERATED(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCompareResponse(bytes *Bytes) (ret CompareResponse, err error) {
|
||||||
|
var res LDAPResult
|
||||||
|
res, err = readTaggedLDAPResult(bytes, classApplication, TagCompareResponse)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readCompareResponse:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = CompareResponse(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (response CompareResponse) write(bytes *Bytes) int {
|
||||||
|
return LDAPResult(response).writeTagged(bytes, classApplication, TagCompareResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (response CompareResponse) size() int {
|
||||||
|
return LDAPResult(response).sizeTagged(TagCompareResponse)
|
||||||
|
}
|
94
goldap/control.go
Normal file
94
goldap/control.go
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control ::= SEQUENCE {
|
||||||
|
// controlType LDAPOID,
|
||||||
|
// criticality BOOLEAN DEFAULT FALSE,
|
||||||
|
// controlValue OCTET STRING OPTIONAL }
|
||||||
|
|
||||||
|
func (control *Control) ControlType() LDAPOID {
|
||||||
|
return control.controlType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (control *Control) Criticality() BOOLEAN {
|
||||||
|
return control.criticality
|
||||||
|
}
|
||||||
|
|
||||||
|
func (control *Control) ControlValue() *OCTETSTRING {
|
||||||
|
return control.controlValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func readControl(bytes *Bytes) (control Control, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, control.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readControl:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (control *Control) readComponents(bytes *Bytes) (err error) {
|
||||||
|
control.controlType, err = readLDAPOID(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == tagBoolean {
|
||||||
|
control.criticality, err = readBOOLEAN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if control.criticality == false {
|
||||||
|
err = errors.New(fmt.Sprintf("readComponents: criticality default value FALSE should not be specified"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var octetstring OCTETSTRING
|
||||||
|
octetstring, err = readOCTETSTRING(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
control.controlValue = octetstring.Pointer()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (control Control) write(bytes *Bytes) (size int) {
|
||||||
|
if control.controlValue != nil {
|
||||||
|
size += control.controlValue.write(bytes)
|
||||||
|
}
|
||||||
|
if control.criticality != BOOLEAN(false) {
|
||||||
|
size += control.criticality.write(bytes)
|
||||||
|
}
|
||||||
|
size += control.controlType.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (control Control) size() (size int) {
|
||||||
|
if control.controlValue != nil {
|
||||||
|
size += control.controlValue.size()
|
||||||
|
}
|
||||||
|
if control.criticality != BOOLEAN(false) {
|
||||||
|
size += control.criticality.size()
|
||||||
|
}
|
||||||
|
size += control.controlType.size()
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
44
goldap/controls.go
Normal file
44
goldap/controls.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Controls ::= SEQUENCE OF control Control
|
||||||
|
|
||||||
|
func readTaggedControls(bytes *Bytes, class int, tag int) (controls Controls, err error) {
|
||||||
|
err = bytes.ReadSubBytes(class, tag, controls.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedControls:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (controls *Controls) readComponents(bytes *Bytes) (err error) {
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
var control Control
|
||||||
|
control, err = readControl(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*controls = append(*controls, control)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (controls Controls) Pointer() *Controls { return &controls }
|
||||||
|
|
||||||
|
func (controls Controls) writeTagged(bytes *Bytes, class int, tag int) (size int) {
|
||||||
|
for i := len(controls) - 1; i >= 0; i-- {
|
||||||
|
size += controls[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (controls Controls) sizeTagged(tag int) (size int) {
|
||||||
|
for _, control := range controls {
|
||||||
|
size += control.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tag, size)
|
||||||
|
return
|
||||||
|
}
|
24
goldap/del_request.go
Normal file
24
goldap/del_request.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// DelRequest ::= [APPLICATION 10] LDAPDN
|
||||||
|
func readDelRequest(bytes *Bytes) (ret DelRequest, err error) {
|
||||||
|
var res LDAPDN
|
||||||
|
res, err = readTaggedLDAPDN(bytes, classApplication, TagDelRequest)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readDelRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = DelRequest(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (del DelRequest) write(bytes *Bytes) int {
|
||||||
|
return LDAPDN(del).writeTagged(bytes, classApplication, TagDelRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (del DelRequest) size() int {
|
||||||
|
return LDAPDN(del).sizeTagged(TagDelRequest)
|
||||||
|
}
|
29
goldap/del_response.go
Normal file
29
goldap/del_response.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// DelResponse ::= [APPLICATION 11] LDAPResult
|
||||||
|
|
||||||
|
func (del *DelResponse) SetResultCode(code int) {
|
||||||
|
del.resultCode = ENUMERATED(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readDelResponse(bytes *Bytes) (ret DelResponse, err error) {
|
||||||
|
var res LDAPResult
|
||||||
|
res, err = readTaggedLDAPResult(bytes, classApplication, TagDelResponse)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readDelResponse:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = DelResponse(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (del DelResponse) write(bytes *Bytes) int {
|
||||||
|
return LDAPResult(del).writeTagged(bytes, classApplication, TagDelResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (del DelResponse) size() int {
|
||||||
|
return LDAPResult(del).sizeTagged(TagDelResponse)
|
||||||
|
}
|
60
goldap/dn.go
Normal file
60
goldap/dn.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAPDN ::= LDAPString -- Constrained to <distinguishedName>
|
||||||
|
// -- [RFC4514]
|
||||||
|
|
||||||
|
func readLDAPDN(bytes *Bytes) (ret LDAPDN, err error) {
|
||||||
|
var str LDAPString
|
||||||
|
str, err = readLDAPString(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readLDAPDN:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = LDAPDN(str)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTaggedLDAPDN(bytes *Bytes, class int, tag int) (ret LDAPDN, err error) {
|
||||||
|
var ldapstring LDAPString
|
||||||
|
ldapstring, err = readTaggedLDAPString(bytes, class, tag)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedLDAPDN:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// @TODO: check RFC4514
|
||||||
|
ret = LDAPDN(ldapstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPDN) Pointer() *LDAPDN { return &l }
|
||||||
|
|
||||||
|
func readRelativeLDAPDN(bytes *Bytes) (ret RelativeLDAPDN, err error) {
|
||||||
|
var ldapstring LDAPString
|
||||||
|
ldapstring, err = readLDAPString(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readRelativeLDAPDN:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// @TODO: check RFC4514
|
||||||
|
ret = RelativeLDAPDN(ldapstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPDN) write(bytes *Bytes) int {
|
||||||
|
return LDAPString(l).write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPDN) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return LDAPString(l).writeTagged(bytes, class, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPDN) size() int {
|
||||||
|
return LDAPString(l).size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPDN) sizeTagged(tag int) int {
|
||||||
|
return LDAPString(l).sizeTagged(tag)
|
||||||
|
}
|
34
goldap/enumerated.go
Normal file
34
goldap/enumerated.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func (enum ENUMERATED) Int() int {
|
||||||
|
return int(enum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readENUMERATED(bytes *Bytes, allowedValues map[ENUMERATED]string) (ret ENUMERATED, err error) {
|
||||||
|
var value interface{}
|
||||||
|
value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagEnum, tagEnum)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readENUMERATED:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = ENUMERATED(value.(int32))
|
||||||
|
if _, ok := allowedValues[ret]; !ok {
|
||||||
|
err = LdapError{fmt.Sprintf("readENUMERATED: Invalid ENUMERATED VALUE %d", ret)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum ENUMERATED) write(bytes *Bytes) int {
|
||||||
|
return bytes.WritePrimitiveSubBytes(classUniversal, tagEnum, enum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum ENUMERATED) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return bytes.WritePrimitiveSubBytes(class, tag, enum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum ENUMERATED) size() int {
|
||||||
|
return SizePrimitiveSubBytes(tagEnum, enum)
|
||||||
|
}
|
9
goldap/error.go
Normal file
9
goldap/error.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
type LdapError struct {
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err LdapError) Error() string {
|
||||||
|
return err.Msg
|
||||||
|
}
|
69
goldap/extended_request.go
Normal file
69
goldap/extended_request.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
|
||||||
|
// requestName [0] LDAPOID,
|
||||||
|
// requestValue [1] OCTET STRING OPTIONAL }
|
||||||
|
|
||||||
|
func (extended *ExtendedRequest) RequestName() LDAPOID {
|
||||||
|
return extended.requestName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (extended *ExtendedRequest) RequestValue() *OCTETSTRING {
|
||||||
|
return extended.requestValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func readExtendedRequest(bytes *Bytes) (ret ExtendedRequest, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagExtendedRequest, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readExtendedRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (extended *ExtendedRequest) readComponents(bytes *Bytes) (err error) {
|
||||||
|
extended.requestName, err = readTaggedLDAPOID(bytes, classContextSpecific, TagExtendedRequestName)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == TagExtendedRequestValue {
|
||||||
|
var requestValue OCTETSTRING
|
||||||
|
requestValue, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagExtendedRequestValue)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
extended.requestValue = requestValue.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (extended ExtendedRequest) write(bytes *Bytes) (size int) {
|
||||||
|
if extended.requestValue != nil {
|
||||||
|
size += extended.requestValue.writeTagged(bytes, classContextSpecific, TagExtendedRequestValue)
|
||||||
|
}
|
||||||
|
size += extended.requestName.writeTagged(bytes, classContextSpecific, TagExtendedRequestName)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagExtendedRequest, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (extended ExtendedRequest) size() (size int) {
|
||||||
|
size += extended.requestName.sizeTagged(TagExtendedRequestName)
|
||||||
|
if extended.requestValue != nil {
|
||||||
|
size += extended.requestValue.sizeTagged(TagExtendedRequestValue)
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(TagExtendedRequest, size)
|
||||||
|
return
|
||||||
|
}
|
85
goldap/extended_response.go
Normal file
85
goldap/extended_response.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
|
||||||
|
// COMPONENTS OF LDAPResult,
|
||||||
|
// responseName [10] LDAPOID OPTIONAL,
|
||||||
|
// responseValue [11] OCTET STRING OPTIONAL }
|
||||||
|
|
||||||
|
func (extended *ExtendedResponse) SetResponseName(name LDAPOID) {
|
||||||
|
extended.responseName = &name
|
||||||
|
}
|
||||||
|
|
||||||
|
func readExtendedResponse(bytes *Bytes) (ret ExtendedResponse, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagExtendedResponse, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readExtendedResponse:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (extended *ExtendedResponse) readComponents(bytes *Bytes) (err error) {
|
||||||
|
extended.LDAPResult.readComponents(bytes)
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == TagExtendedResponseName {
|
||||||
|
var oid LDAPOID
|
||||||
|
oid, err = readTaggedLDAPOID(bytes, classContextSpecific, TagExtendedResponseName)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
extended.responseName = oid.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == TagExtendedResponseValue {
|
||||||
|
var responseValue OCTETSTRING
|
||||||
|
responseValue, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagExtendedResponseValue)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
extended.responseValue = responseValue.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (extended ExtendedResponse) write(bytes *Bytes) (size int) {
|
||||||
|
if extended.responseValue != nil {
|
||||||
|
size += extended.responseValue.writeTagged(bytes, classContextSpecific, TagExtendedResponseValue)
|
||||||
|
}
|
||||||
|
if extended.responseName != nil {
|
||||||
|
size += extended.responseName.writeTagged(bytes, classContextSpecific, TagExtendedResponseName)
|
||||||
|
}
|
||||||
|
size += extended.LDAPResult.writeComponents(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagExtendedResponse, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (extended ExtendedResponse) size() (size int) {
|
||||||
|
size += extended.LDAPResult.sizeComponents()
|
||||||
|
if extended.responseName != nil {
|
||||||
|
size += extended.responseName.sizeTagged(TagExtendedResponseName)
|
||||||
|
}
|
||||||
|
if extended.responseValue != nil {
|
||||||
|
size += extended.responseValue.sizeTagged(TagExtendedResponseValue)
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(TagExtendedResponse, size)
|
||||||
|
return
|
||||||
|
}
|
70
goldap/filter.go
Normal file
70
goldap/filter.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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,
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//Sermersheim Standards Track [Page 57]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//RFC 4511 LDAPv3 June 2006
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// substrings [4] SubstringFilter,
|
||||||
|
// greaterOrEqual [5] AttributeValueAssertion,
|
||||||
|
// lessOrEqual [6] AttributeValueAssertion,
|
||||||
|
// present [7] AttributeDescription,
|
||||||
|
// approxMatch [8] AttributeValueAssertion,
|
||||||
|
// extensibleMatch [9] MatchingRuleAssertion,
|
||||||
|
// ... }
|
||||||
|
|
||||||
|
func readFilter(bytes *Bytes) (filter Filter, err error) {
|
||||||
|
var tagAndLength TagAndLength
|
||||||
|
tagAndLength, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tagAndLength.ExpectClass(classContextSpecific)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch tagAndLength.Tag {
|
||||||
|
case TagFilterAnd:
|
||||||
|
filter, err = readFilterAnd(bytes)
|
||||||
|
case TagFilterOr:
|
||||||
|
filter, err = readFilterOr(bytes)
|
||||||
|
case TagFilterNot:
|
||||||
|
filter, err = readFilterNot(bytes)
|
||||||
|
case TagFilterEqualityMatch:
|
||||||
|
filter, err = readFilterEqualityMatch(bytes)
|
||||||
|
case TagFilterSubstrings:
|
||||||
|
filter, err = readFilterSubstrings(bytes)
|
||||||
|
case TagFilterGreaterOrEqual:
|
||||||
|
filter, err = readFilterGreaterOrEqual(bytes)
|
||||||
|
case TagFilterLessOrEqual:
|
||||||
|
filter, err = readFilterLessOrEqual(bytes)
|
||||||
|
case TagFilterPresent:
|
||||||
|
filter, err = readFilterPresent(bytes)
|
||||||
|
case TagFilterApproxMatch:
|
||||||
|
filter, err = readFilterApproxMatch(bytes)
|
||||||
|
case TagFilterExtensibleMatch:
|
||||||
|
filter, err = readFilterExtensibleMatch(bytes)
|
||||||
|
default:
|
||||||
|
err = LdapError{fmt.Sprintf("readFilter: invalid tag value %d for filter", tagAndLength.Tag)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
54
goldap/filter_and.go
Normal file
54
goldap/filter_and.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// and [0] SET SIZE (1..MAX) OF filter Filter,
|
||||||
|
|
||||||
|
func (filterAnd FilterAnd) getFilterTag() int {
|
||||||
|
return TagFilterAnd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAnd FilterAnd) size() (size int) {
|
||||||
|
for _, filter := range filterAnd {
|
||||||
|
size += filter.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(TagFilterAnd, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAnd *FilterAnd) readComponents(bytes *Bytes) (err error) {
|
||||||
|
count := 0
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
count++
|
||||||
|
var filter Filter
|
||||||
|
filter, err = readFilter(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents (filter %d):\n%s", count, err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*filterAnd = append(*filterAnd, filter)
|
||||||
|
}
|
||||||
|
if len(*filterAnd) == 0 {
|
||||||
|
err = LdapError{"readComponents: expecting at least one Filter"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterAnd FilterAnd) write(bytes *Bytes) (size int) {
|
||||||
|
|
||||||
|
for i := len(filterAnd) - 1; i >= 0; i-- {
|
||||||
|
size += filterAnd[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterAnd, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFilterAnd(bytes *Bytes) (filterand FilterAnd, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classContextSpecific, TagFilterAnd, filterand.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterAnd:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
34
goldap/filter_approx_match.go
Normal file
34
goldap/filter_approx_match.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// approxMatch [8] AttributeValueAssertion,
|
||||||
|
func readFilterApproxMatch(bytes *Bytes) (ret FilterApproxMatch, err error) {
|
||||||
|
var attributevalueassertion AttributeValueAssertion
|
||||||
|
attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterApproxMatch)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterApproxMatch:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = FilterApproxMatch(attributevalueassertion)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// approxMatch [8] AttributeValueAssertion,
|
||||||
|
func (f FilterApproxMatch) write(bytes *Bytes) int {
|
||||||
|
return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterApproxMatch)
|
||||||
|
}
|
||||||
|
func (filterAnd FilterApproxMatch) getFilterTag() int {
|
||||||
|
return TagFilterApproxMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// approxMatch [8] AttributeValueAssertion,
|
||||||
|
func (f FilterApproxMatch) size() int {
|
||||||
|
return AttributeValueAssertion(f).sizeTagged(TagFilterApproxMatch)
|
||||||
|
}
|
||||||
|
func (a *FilterApproxMatch) AttributeDesc() AttributeDescription {
|
||||||
|
return a.attributeDesc
|
||||||
|
}
|
||||||
|
func (a *FilterApproxMatch) AssertionValue() AssertionValue {
|
||||||
|
return a.assertionValue
|
||||||
|
}
|
34
goldap/filter_equality_match.go
Normal file
34
goldap/filter_equality_match.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// equalityMatch [3] AttributeValueAssertion,
|
||||||
|
func readFilterEqualityMatch(bytes *Bytes) (ret FilterEqualityMatch, err error) {
|
||||||
|
var attributevalueassertion AttributeValueAssertion
|
||||||
|
attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterEqualityMatch)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterEqualityMatch:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = FilterEqualityMatch(attributevalueassertion)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// equalityMatch [3] AttributeValueAssertion,
|
||||||
|
func (f FilterEqualityMatch) write(bytes *Bytes) int {
|
||||||
|
return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterEqualityMatch)
|
||||||
|
}
|
||||||
|
func (filter FilterEqualityMatch) getFilterTag() int {
|
||||||
|
return TagFilterEqualityMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// equalityMatch [3] AttributeValueAssertion,
|
||||||
|
func (f FilterEqualityMatch) size() int {
|
||||||
|
return AttributeValueAssertion(f).sizeTagged(TagFilterEqualityMatch)
|
||||||
|
}
|
||||||
|
func (a *FilterEqualityMatch) AttributeDesc() AttributeDescription {
|
||||||
|
return a.attributeDesc
|
||||||
|
}
|
||||||
|
func (a *FilterEqualityMatch) AssertionValue() AssertionValue {
|
||||||
|
return a.assertionValue
|
||||||
|
}
|
28
goldap/filter_extensible_match.go
Normal file
28
goldap/filter_extensible_match.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// extensibleMatch [9] MatchingRuleAssertion,
|
||||||
|
func readFilterExtensibleMatch(bytes *Bytes) (filterextensiblematch FilterExtensibleMatch, err error) {
|
||||||
|
var matchingruleassertion MatchingRuleAssertion
|
||||||
|
matchingruleassertion, err = readTaggedMatchingRuleAssertion(bytes, classContextSpecific, TagFilterExtensibleMatch)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterExtensibleMatch:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filterextensiblematch = FilterExtensibleMatch(matchingruleassertion)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// extensibleMatch [9] MatchingRuleAssertion,
|
||||||
|
func (f FilterExtensibleMatch) write(bytes *Bytes) int {
|
||||||
|
return MatchingRuleAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterExtensibleMatch)
|
||||||
|
}
|
||||||
|
func (filterAnd FilterExtensibleMatch) getFilterTag() int {
|
||||||
|
return TagFilterExtensibleMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// extensibleMatch [9] MatchingRuleAssertion,
|
||||||
|
func (f FilterExtensibleMatch) size() int {
|
||||||
|
return MatchingRuleAssertion(f).sizeTagged(TagFilterExtensibleMatch)
|
||||||
|
}
|
34
goldap/filter_greater_or_equal.go
Normal file
34
goldap/filter_greater_or_equal.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// greaterOrEqual [5] AttributeValueAssertion,
|
||||||
|
func readFilterGreaterOrEqual(bytes *Bytes) (ret FilterGreaterOrEqual, err error) {
|
||||||
|
var attributevalueassertion AttributeValueAssertion
|
||||||
|
attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterGreaterOrEqual)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterGreaterOrEqual:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = FilterGreaterOrEqual(attributevalueassertion)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// greaterOrEqual [5] AttributeValueAssertion,
|
||||||
|
func (filter FilterGreaterOrEqual) write(bytes *Bytes) int {
|
||||||
|
return AttributeValueAssertion(filter).writeTagged(bytes, classContextSpecific, TagFilterGreaterOrEqual)
|
||||||
|
}
|
||||||
|
func (filter FilterGreaterOrEqual) getFilterTag() int {
|
||||||
|
return TagFilterGreaterOrEqual
|
||||||
|
}
|
||||||
|
|
||||||
|
// greaterOrEqual [5] AttributeValueAssertion,
|
||||||
|
func (filter FilterGreaterOrEqual) size() int {
|
||||||
|
return AttributeValueAssertion(filter).sizeTagged(TagFilterGreaterOrEqual)
|
||||||
|
}
|
||||||
|
func (filter *FilterGreaterOrEqual) AttributeDesc() AttributeDescription {
|
||||||
|
return filter.attributeDesc
|
||||||
|
}
|
||||||
|
func (filter *FilterGreaterOrEqual) AssertionValue() AssertionValue {
|
||||||
|
return filter.assertionValue
|
||||||
|
}
|
34
goldap/filter_less_or_equal.go
Normal file
34
goldap/filter_less_or_equal.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// lessOrEqual [6] AttributeValueAssertion,
|
||||||
|
func readFilterLessOrEqual(bytes *Bytes) (ret FilterLessOrEqual, err error) {
|
||||||
|
var attributevalueassertion AttributeValueAssertion
|
||||||
|
attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterLessOrEqual)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterLessOrEqual:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = FilterLessOrEqual(attributevalueassertion)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// lessOrEqual [6] AttributeValueAssertion,
|
||||||
|
func (f FilterLessOrEqual) write(bytes *Bytes) int {
|
||||||
|
return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterLessOrEqual)
|
||||||
|
}
|
||||||
|
func (filterAnd FilterLessOrEqual) getFilterTag() int {
|
||||||
|
return TagFilterLessOrEqual
|
||||||
|
}
|
||||||
|
|
||||||
|
// lessOrEqual [6] AttributeValueAssertion,
|
||||||
|
func (f FilterLessOrEqual) size() int {
|
||||||
|
return AttributeValueAssertion(f).sizeTagged(TagFilterLessOrEqual)
|
||||||
|
}
|
||||||
|
func (a *FilterLessOrEqual) AttributeDesc() AttributeDescription {
|
||||||
|
return a.attributeDesc
|
||||||
|
}
|
||||||
|
func (a *FilterLessOrEqual) AssertionValue() AssertionValue {
|
||||||
|
return a.assertionValue
|
||||||
|
}
|
40
goldap/filter_not.go
Normal file
40
goldap/filter_not.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func (filterNot FilterNot) getFilterTag() int {
|
||||||
|
return TagFilterNot
|
||||||
|
}
|
||||||
|
|
||||||
|
// not [2] Filter,
|
||||||
|
func (filterNot FilterNot) size() (size int) {
|
||||||
|
size = filterNot.Filter.size()
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (filterNot *FilterNot) readComponents(bytes *Bytes) (err error) {
|
||||||
|
filterNot.Filter, err = readFilter(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// not [2] Filter,
|
||||||
|
func (filterNot FilterNot) write(bytes *Bytes) (size int) {
|
||||||
|
size = filterNot.Filter.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterNot, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// not [2] Filter,
|
||||||
|
func readFilterNot(bytes *Bytes) (filternot FilterNot, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classContextSpecific, TagFilterNot, filternot.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterNot:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
52
goldap/filter_or.go
Normal file
52
goldap/filter_or.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// or [1] SET SIZE (1..MAX) OF filter Filter,
|
||||||
|
func readFilterOr(bytes *Bytes) (filteror FilterOr, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classContextSpecific, TagFilterOr, filteror.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterOr:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (filteror *FilterOr) readComponents(bytes *Bytes) (err error) {
|
||||||
|
count := 0
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
count++
|
||||||
|
var filter Filter
|
||||||
|
filter, err = readFilter(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents (filter %d): %s", count, err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*filteror = append(*filteror, filter)
|
||||||
|
}
|
||||||
|
if len(*filteror) == 0 {
|
||||||
|
err = LdapError{"readComponents: expecting at least one Filter"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// or [1] SET SIZE (1..MAX) OF filter Filter,
|
||||||
|
func (f FilterOr) write(bytes *Bytes) (size int) {
|
||||||
|
for i := len(f) - 1; i >= 0; i-- {
|
||||||
|
size += f[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterOr, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (filter FilterOr) getFilterTag() int {
|
||||||
|
return TagFilterOr
|
||||||
|
}
|
||||||
|
|
||||||
|
// or [1] SET SIZE (1..MAX) OF filter Filter,
|
||||||
|
func (f FilterOr) size() (size int) {
|
||||||
|
for _, filter := range f {
|
||||||
|
size += filter.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(TagFilterOr, size)
|
||||||
|
return
|
||||||
|
}
|
28
goldap/filter_present.go
Normal file
28
goldap/filter_present.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// present [7] AttributeDescription,
|
||||||
|
func readFilterPresent(bytes *Bytes) (ret FilterPresent, err error) {
|
||||||
|
var attributedescription AttributeDescription
|
||||||
|
attributedescription, err = readTaggedAttributeDescription(bytes, classContextSpecific, TagFilterPresent)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterPresent:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = FilterPresent(attributedescription)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// present [7] AttributeDescription,
|
||||||
|
func (f FilterPresent) write(bytes *Bytes) int {
|
||||||
|
return AttributeDescription(f).writeTagged(bytes, classContextSpecific, TagFilterPresent)
|
||||||
|
}
|
||||||
|
func (filterAnd FilterPresent) getFilterTag() int {
|
||||||
|
return TagFilterPresent
|
||||||
|
}
|
||||||
|
|
||||||
|
// present [7] AttributeDescription,
|
||||||
|
func (f FilterPresent) size() int {
|
||||||
|
return AttributeDescription(f).sizeTagged(TagFilterPresent)
|
||||||
|
}
|
187
goldap/filter_substring.go
Normal file
187
goldap/filter_substring.go
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// substrings [4] SubstringFilter,
|
||||||
|
func readFilterSubstrings(bytes *Bytes) (filtersubstrings FilterSubstrings, err error) {
|
||||||
|
var substringfilter SubstringFilter
|
||||||
|
substringfilter, err = readTaggedSubstringFilter(bytes, classContextSpecific, TagFilterSubstrings)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readFilterSubstrings:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filtersubstrings = FilterSubstrings(substringfilter)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
func readTaggedSubstringFilter(bytes *Bytes, class int, tag int) (substringfilter SubstringFilter, err error) {
|
||||||
|
err = bytes.ReadSubBytes(class, tag, substringfilter.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedSubstringFilter:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (substringfilter *SubstringFilter) readComponents(bytes *Bytes) (err error) {
|
||||||
|
substringfilter.type_, err = readAttributeDescription(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = substringfilter.readSubstrings(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (substringfilter *SubstringFilter) readSubstrings(bytes *Bytes) (err error) {
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, substringfilter.readSubstringsComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSubstrings:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (substringfilter *SubstringFilter) readSubstringsComponents(bytes *Bytes) (err error) {
|
||||||
|
var foundInitial = 0
|
||||||
|
var foundFinal = 0
|
||||||
|
var tagAndLength TagAndLength
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
tagAndLength, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var assertionvalue AssertionValue
|
||||||
|
switch tagAndLength.Tag {
|
||||||
|
case TagSubstringInitial:
|
||||||
|
foundInitial++
|
||||||
|
if foundInitial > 1 {
|
||||||
|
err = LdapError{"readSubstringsComponents: initial can occur at most once"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringInitial)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
substringfilter.substrings = append(substringfilter.substrings, SubstringInitial(assertionvalue))
|
||||||
|
case TagSubstringAny:
|
||||||
|
assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringAny)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
substringfilter.substrings = append(substringfilter.substrings, SubstringAny(assertionvalue))
|
||||||
|
case TagSubstringFinal:
|
||||||
|
foundFinal++
|
||||||
|
if foundFinal > 1 {
|
||||||
|
err = LdapError{"readSubstringsComponents: final can occur at most once"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringFinal)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
substringfilter.substrings = append(substringfilter.substrings, SubstringFinal(assertionvalue))
|
||||||
|
default:
|
||||||
|
err = LdapError{fmt.Sprintf("readSubstringsComponents: invalid tag %d", tagAndLength.Tag)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(substringfilter.substrings) == 0 {
|
||||||
|
err = LdapError{"readSubstringsComponents: expecting at least one substring"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// substrings [4] SubstringFilter,
|
||||||
|
func (f FilterSubstrings) write(bytes *Bytes) int {
|
||||||
|
return SubstringFilter(f).writeTagged(bytes, classContextSpecific, TagFilterSubstrings)
|
||||||
|
}
|
||||||
|
func (s SubstringFilter) writeTagged(bytes *Bytes, class int, tag int) (size int) {
|
||||||
|
for i := len(s.substrings) - 1; i >= 0; i-- {
|
||||||
|
substring := s.substrings[i]
|
||||||
|
switch substring.(type) {
|
||||||
|
case SubstringInitial:
|
||||||
|
size += AssertionValue(substring.(SubstringInitial)).writeTagged(bytes, classContextSpecific, TagSubstringInitial)
|
||||||
|
case SubstringAny:
|
||||||
|
size += AssertionValue(substring.(SubstringAny)).writeTagged(bytes, classContextSpecific, TagSubstringAny)
|
||||||
|
case SubstringFinal:
|
||||||
|
size += AssertionValue(substring.(SubstringFinal)).writeTagged(bytes, classContextSpecific, TagSubstringFinal)
|
||||||
|
default:
|
||||||
|
panic("Unknown type for SubstringFilter substring")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
size += s.type_.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
func (s SubstringFilter) write(bytes *Bytes) (size int) {
|
||||||
|
return s.writeTagged(bytes, classUniversal, tagSequence)
|
||||||
|
}
|
||||||
|
func (filter FilterSubstrings) getFilterTag() int {
|
||||||
|
return TagFilterSubstrings
|
||||||
|
}
|
||||||
|
|
||||||
|
// substrings [4] SubstringFilter,
|
||||||
|
func (f FilterSubstrings) size() int {
|
||||||
|
return SubstringFilter(f).sizeTagged(TagFilterSubstrings)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
func (s SubstringFilter) size() (size int) {
|
||||||
|
return s.sizeTagged(tagSequence)
|
||||||
|
}
|
||||||
|
func (s SubstringFilter) sizeTagged(tag int) (size int) {
|
||||||
|
for _, substring := range s.substrings {
|
||||||
|
switch substring.(type) {
|
||||||
|
case SubstringInitial:
|
||||||
|
size += AssertionValue(substring.(SubstringInitial)).sizeTagged(TagSubstringInitial)
|
||||||
|
case SubstringAny:
|
||||||
|
size += AssertionValue(substring.(SubstringAny)).sizeTagged(TagSubstringAny)
|
||||||
|
case SubstringFinal:
|
||||||
|
size += AssertionValue(substring.(SubstringFinal)).sizeTagged(TagSubstringFinal)
|
||||||
|
default:
|
||||||
|
panic("Unknown type for SubstringFilter substring")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
size += s.type_.size()
|
||||||
|
size += sizeTagAndLength(tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (s *FilterSubstrings) Type_() AttributeDescription {
|
||||||
|
return s.type_
|
||||||
|
}
|
||||||
|
func (s *FilterSubstrings) Substrings() []Substring {
|
||||||
|
return s.substrings
|
||||||
|
}
|
53
goldap/integer.go
Normal file
53
goldap/integer.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func readINTEGER(bytes *Bytes) (ret INTEGER, err error) {
|
||||||
|
var value interface{}
|
||||||
|
value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagInteger, tagInteger)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readINTEGER:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = INTEGER(value.(int32))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func readTaggedINTEGER(bytes *Bytes, class int, tag int) (ret INTEGER, err error) {
|
||||||
|
var value interface{}
|
||||||
|
value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagInteger)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedINTEGER:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = INTEGER(value.(int32))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func readTaggedPositiveINTEGER(bytes *Bytes, class int, tag int) (ret INTEGER, err error) {
|
||||||
|
ret, err = readTaggedINTEGER(bytes, class, tag)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedPositiveINTEGER:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !(ret >= 0 && ret <= maxInt) {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedPositiveINTEGER: Invalid INTEGER value %d ! Expected value between 0 and %d", ret, maxInt)}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func readPositiveINTEGER(bytes *Bytes) (ret INTEGER, err error) {
|
||||||
|
return readTaggedPositiveINTEGER(bytes, classUniversal, tagInteger)
|
||||||
|
}
|
||||||
|
func (i INTEGER) write(bytes *Bytes) int {
|
||||||
|
return bytes.WritePrimitiveSubBytes(classUniversal, tagInteger, i)
|
||||||
|
}
|
||||||
|
func (i INTEGER) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return bytes.WritePrimitiveSubBytes(class, tag, i)
|
||||||
|
}
|
||||||
|
func (i INTEGER) size() int {
|
||||||
|
return SizePrimitiveSubBytes(tagInteger, i)
|
||||||
|
}
|
||||||
|
func (i INTEGER) sizeTagged(tag int) int {
|
||||||
|
return SizePrimitiveSubBytes(tag, i)
|
||||||
|
}
|
||||||
|
func (l INTEGER) Int() int {
|
||||||
|
return int(l)
|
||||||
|
}
|
89
goldap/intermediate_response.go
Normal file
89
goldap/intermediate_response.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
|
||||||
|
// responseName [0] LDAPOID OPTIONAL,
|
||||||
|
// responseValue [1] OCTET STRING OPTIONAL }
|
||||||
|
func readIntermediateResponse(bytes *Bytes) (ret IntermediateResponse, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagIntermediateResponse, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readIntermediateResponse:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (bytes *Bytes) PreviewTagAndLength() (tagAndLength TagAndLength, err error) {
|
||||||
|
previousOffset := bytes.offset // Save offset
|
||||||
|
tagAndLength, err = bytes.ParseTagAndLength()
|
||||||
|
bytes.offset = previousOffset // Restore offset
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (res *IntermediateResponse) readComponents(bytes *Bytes) (err error) {
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == TagIntermediateResponseName {
|
||||||
|
var oid LDAPOID
|
||||||
|
oid, err = readTaggedLDAPOID(bytes, classContextSpecific, TagIntermediateResponseName)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.responseName = oid.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == TagIntermediateResponseValue {
|
||||||
|
var str OCTETSTRING
|
||||||
|
str, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagIntermediateResponseValue)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.responseValue = str.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
|
||||||
|
// responseName [0] LDAPOID OPTIONAL,
|
||||||
|
// responseValue [1] OCTET STRING OPTIONAL }
|
||||||
|
func (i IntermediateResponse) write(bytes *Bytes) (size int) {
|
||||||
|
if i.responseValue != nil {
|
||||||
|
size += i.responseValue.writeTagged(bytes, classContextSpecific, TagIntermediateResponseValue)
|
||||||
|
}
|
||||||
|
if i.responseName != nil {
|
||||||
|
size += i.responseName.writeTagged(bytes, classContextSpecific, TagIntermediateResponseName)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagIntermediateResponse, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
|
||||||
|
// responseName [0] LDAPOID OPTIONAL,
|
||||||
|
// responseValue [1] OCTET STRING OPTIONAL }
|
||||||
|
func (i IntermediateResponse) size() (size int) {
|
||||||
|
if i.responseName != nil {
|
||||||
|
size += i.responseName.sizeTagged(TagIntermediateResponseName)
|
||||||
|
}
|
||||||
|
if i.responseValue != nil {
|
||||||
|
size += i.responseValue.sizeTagged(TagIntermediateResponseValue)
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(TagIntermediateResponse, size)
|
||||||
|
return
|
||||||
|
}
|
117
goldap/matching_rule_assertion.go
Normal file
117
goldap/matching_rule_assertion.go
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// MatchingRuleAssertion ::= SEQUENCE {
|
||||||
|
// matchingRule [1] MatchingRuleId OPTIONAL,
|
||||||
|
// type [2] AttributeDescription OPTIONAL,
|
||||||
|
// matchValue [3] AssertionValue,
|
||||||
|
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
||||||
|
func readTaggedMatchingRuleAssertion(bytes *Bytes, class int, tag int) (ret MatchingRuleAssertion, err error) {
|
||||||
|
err = bytes.ReadSubBytes(class, tag, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedMatchingRuleAssertion:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (matchingruleassertion *MatchingRuleAssertion) readComponents(bytes *Bytes) (err error) {
|
||||||
|
err = matchingruleassertion.readMatchingRule(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("readComponents: %s", err.Error())}
|
||||||
|
}
|
||||||
|
err = matchingruleassertion.readType(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("readComponents: %s", err.Error())}
|
||||||
|
}
|
||||||
|
matchingruleassertion.matchValue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagMatchingRuleAssertionMatchValue)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("readComponents: %s", err.Error())}
|
||||||
|
}
|
||||||
|
matchingruleassertion.dnAttributes, err = readTaggedBOOLEAN(bytes, classContextSpecific, TagMatchingRuleAssertionDnAttributes)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("readComponents: %s", err.Error())}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (matchingruleassertion *MatchingRuleAssertion) readMatchingRule(bytes *Bytes) (err error) {
|
||||||
|
var tagAndLength TagAndLength
|
||||||
|
tagAndLength, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("readMatchingRule: %s", err.Error())}
|
||||||
|
}
|
||||||
|
if tagAndLength.Tag == TagMatchingRuleAssertionMatchingRule {
|
||||||
|
var matchingRule MatchingRuleId
|
||||||
|
matchingRule, err = readTaggedMatchingRuleId(bytes, classContextSpecific, TagMatchingRuleAssertionMatchingRule)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("readMatchingRule: %s", err.Error())}
|
||||||
|
}
|
||||||
|
matchingruleassertion.matchingRule = matchingRule.Pointer()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (matchingruleassertion *MatchingRuleAssertion) readType(bytes *Bytes) (err error) {
|
||||||
|
var tagAndLength TagAndLength
|
||||||
|
tagAndLength, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("readType: %s", err.Error())}
|
||||||
|
}
|
||||||
|
if tagAndLength.Tag == TagMatchingRuleAssertionType {
|
||||||
|
var attributedescription AttributeDescription
|
||||||
|
attributedescription, err = readTaggedAttributeDescription(bytes, classContextSpecific, TagMatchingRuleAssertionType)
|
||||||
|
if err != nil {
|
||||||
|
return LdapError{fmt.Sprintf("readType: %s", err.Error())}
|
||||||
|
}
|
||||||
|
matchingruleassertion.type_ = &attributedescription
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m MatchingRuleAssertion) writeTagged(bytes *Bytes, class int, tag int) (size int) {
|
||||||
|
if m.dnAttributes != BOOLEAN(false) {
|
||||||
|
size += m.dnAttributes.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionDnAttributes)
|
||||||
|
}
|
||||||
|
size += m.matchValue.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionMatchValue)
|
||||||
|
if m.type_ != nil {
|
||||||
|
size += m.type_.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionType)
|
||||||
|
}
|
||||||
|
if m.matchingRule != nil {
|
||||||
|
size += m.matchingRule.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionMatchingRule)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MatchingRuleAssertion ::= SEQUENCE {
|
||||||
|
// matchingRule [1] MatchingRuleId OPTIONAL,
|
||||||
|
// type [2] AttributeDescription OPTIONAL,
|
||||||
|
// matchValue [3] AssertionValue,
|
||||||
|
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
||||||
|
func (m MatchingRuleAssertion) write(bytes *Bytes) (size int) {
|
||||||
|
return m.writeTagged(bytes, classUniversal, tagSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MatchingRuleAssertion ::= SEQUENCE {
|
||||||
|
// matchingRule [1] MatchingRuleId OPTIONAL,
|
||||||
|
// type [2] AttributeDescription OPTIONAL,
|
||||||
|
// matchValue [3] AssertionValue,
|
||||||
|
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
||||||
|
func (m MatchingRuleAssertion) size() (size int) {
|
||||||
|
return m.sizeTagged(tagSequence)
|
||||||
|
}
|
||||||
|
func (m MatchingRuleAssertion) sizeTagged(tag int) (size int) {
|
||||||
|
if m.matchingRule != nil {
|
||||||
|
size += m.matchingRule.sizeTagged(TagMatchingRuleAssertionMatchingRule)
|
||||||
|
}
|
||||||
|
if m.type_ != nil {
|
||||||
|
size += m.type_.sizeTagged(TagMatchingRuleAssertionType)
|
||||||
|
}
|
||||||
|
size += m.matchValue.sizeTagged(TagMatchingRuleAssertionMatchValue)
|
||||||
|
if m.dnAttributes != BOOLEAN(false) {
|
||||||
|
size += m.dnAttributes.sizeTagged(TagMatchingRuleAssertionDnAttributes)
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tag, size)
|
||||||
|
return
|
||||||
|
}
|
29
goldap/matching_rule_id.go
Normal file
29
goldap/matching_rule_id.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// MatchingRuleId ::= LDAPString
|
||||||
|
func readTaggedMatchingRuleId(bytes *Bytes, class int, tag int) (matchingruleid MatchingRuleId, err error) {
|
||||||
|
var ldapstring LDAPString
|
||||||
|
ldapstring, err = readTaggedLDAPString(bytes, class, tag)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedMatchingRuleId:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
matchingruleid = MatchingRuleId(ldapstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m MatchingRuleId) Pointer() *MatchingRuleId { return &m }
|
||||||
|
|
||||||
|
//
|
||||||
|
// MatchingRuleId ::= LDAPString
|
||||||
|
func (m MatchingRuleId) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return LDAPString(m).writeTagged(bytes, class, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MatchingRuleId ::= LDAPString
|
||||||
|
func (m MatchingRuleId) sizeTagged(tag int) int {
|
||||||
|
return LDAPString(m).sizeTagged(tag)
|
||||||
|
}
|
139
goldap/message.go
Normal file
139
goldap/message.go
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This appendix is normative.
|
||||||
|
//
|
||||||
|
// Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18}
|
||||||
|
// -- Copyright (C) The Internet Society (2006). This version of
|
||||||
|
// -- this ASN.1 module is part of RFC 4511; see the RFC itself
|
||||||
|
// -- for full legal notices.
|
||||||
|
// DEFINITIONS
|
||||||
|
// IMPLICIT TAGS
|
||||||
|
// EXTENSIBILITY IMPLIED ::=
|
||||||
|
//
|
||||||
|
// BEGIN
|
||||||
|
//
|
||||||
|
// LDAPMessage ::= SEQUENCE {
|
||||||
|
// messageID MessageID,
|
||||||
|
// protocolOp CHOICE {
|
||||||
|
// bindRequest BindRequest,
|
||||||
|
// bindResponse BindResponse,
|
||||||
|
// unbindRequest UnbindRequest,
|
||||||
|
// searchRequest SearchRequest,
|
||||||
|
// searchResEntry SearchResultEntry,
|
||||||
|
// searchResDone SearchResultDone,
|
||||||
|
// searchResRef SearchResultReference,
|
||||||
|
// modifyRequest ModifyRequest,
|
||||||
|
// modifyResponse ModifyResponse,
|
||||||
|
// addRequest AddRequest,
|
||||||
|
// addResponse AddResponse,
|
||||||
|
// delRequest DelRequest,
|
||||||
|
// delResponse DelResponse,
|
||||||
|
// modDNRequest ModifyDNRequest,
|
||||||
|
// modDNResponse ModifyDNResponse,
|
||||||
|
// compareRequest CompareRequest,
|
||||||
|
// compareResponse CompareResponse,
|
||||||
|
// abandonRequest AbandonRequest,
|
||||||
|
// extendedReq ExtendedRequest,
|
||||||
|
// extendedResp ExtendedResponse,
|
||||||
|
// ...,
|
||||||
|
// intermediateResponse IntermediateResponse },
|
||||||
|
// controls [0] Controls OPTIONAL }
|
||||||
|
//
|
||||||
|
|
||||||
|
func NewLDAPMessage() *LDAPMessage { return &LDAPMessage{} }
|
||||||
|
|
||||||
|
func (message *LDAPMessage) readComponents(bytes *Bytes) (err error) {
|
||||||
|
message.messageID, err = readMessageID(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.protocolOp, err = readProtocolOp(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == TagLDAPMessageControls {
|
||||||
|
var controls Controls
|
||||||
|
controls, err = readTaggedControls(bytes, classContextSpecific, TagLDAPMessageControls)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.controls = controls.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LDAPMessage) Write() (bytes *Bytes, err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("Error in LDAPMessage.Write: %s", e)}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Compute the needed size
|
||||||
|
totalSize := m.size()
|
||||||
|
// Initialize the structure
|
||||||
|
bytes = &Bytes{
|
||||||
|
bytes: make([]byte, totalSize),
|
||||||
|
offset: totalSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go !
|
||||||
|
size := 0
|
||||||
|
if m.controls != nil {
|
||||||
|
size += m.controls.writeTagged(bytes, classContextSpecific, TagLDAPMessageControls)
|
||||||
|
}
|
||||||
|
size += m.protocolOp.write(bytes)
|
||||||
|
size += m.messageID.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
// Check
|
||||||
|
if size != totalSize || bytes.offset != 0 {
|
||||||
|
err = LdapError{fmt.Sprintf("Something went wrong while writing the message ! Size is %d instead of %d, final offset is %d instead of 0", size, totalSize, bytes.offset)}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m *LDAPMessage) size() (size int) {
|
||||||
|
size += m.messageID.size()
|
||||||
|
size += m.protocolOp.size()
|
||||||
|
if m.controls != nil {
|
||||||
|
size += m.controls.sizeTagged(TagLDAPMessageControls)
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (l *LDAPMessage) MessageID() MessageID {
|
||||||
|
return l.messageID
|
||||||
|
}
|
||||||
|
func (l *LDAPMessage) SetMessageID(ID int) {
|
||||||
|
l.messageID = MessageID(ID)
|
||||||
|
}
|
||||||
|
func (l *LDAPMessage) Controls() *Controls {
|
||||||
|
return l.controls
|
||||||
|
}
|
||||||
|
func (l *LDAPMessage) ProtocolOp() ProtocolOp {
|
||||||
|
return l.protocolOp
|
||||||
|
}
|
||||||
|
func (l *LDAPMessage) ProtocolOpName() string {
|
||||||
|
return reflect.TypeOf(l.ProtocolOp()).Name()
|
||||||
|
}
|
||||||
|
func (l *LDAPMessage) ProtocolOpType() int {
|
||||||
|
switch l.protocolOp.(type) {
|
||||||
|
case BindRequest:
|
||||||
|
return TagBindRequest
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
46
goldap/message_id.go
Normal file
46
goldap/message_id.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func readTaggedMessageID(bytes *Bytes, class int, tag int) (ret MessageID, err error) {
|
||||||
|
var integer INTEGER
|
||||||
|
integer, err = readTaggedPositiveINTEGER(bytes, class, tag)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedMessageID:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return MessageID(integer), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageID ::= INTEGER (0 .. maxInt)
|
||||||
|
//
|
||||||
|
// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
|
||||||
|
//
|
||||||
|
func readMessageID(bytes *Bytes) (ret MessageID, err error) {
|
||||||
|
return readTaggedMessageID(bytes, classUniversal, tagInteger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageID ::= INTEGER (0 .. maxInt)
|
||||||
|
//
|
||||||
|
// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
|
||||||
|
//
|
||||||
|
func (m MessageID) write(bytes *Bytes) int {
|
||||||
|
return INTEGER(m).write(bytes)
|
||||||
|
}
|
||||||
|
func (m MessageID) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return INTEGER(m).writeTagged(bytes, class, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageID ::= INTEGER (0 .. maxInt)
|
||||||
|
//
|
||||||
|
// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
|
||||||
|
//
|
||||||
|
func (m MessageID) size() int {
|
||||||
|
return INTEGER(m).size()
|
||||||
|
}
|
||||||
|
func (m MessageID) sizeTagged(tag int) int {
|
||||||
|
return INTEGER(m).sizeTagged(tag)
|
||||||
|
}
|
||||||
|
func (l MessageID) Int() int {
|
||||||
|
return int(l)
|
||||||
|
}
|
76
goldap/message_test.go
Normal file
76
goldap/message_test.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
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()))
|
||||||
|
}
|
||||||
|
}
|
87
goldap/modify_dn_request.go
Normal file
87
goldap/modify_dn_request.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// newrdn RelativeLDAPDN,
|
||||||
|
// deleteoldrdn BOOLEAN,
|
||||||
|
// newSuperior [0] LDAPDN OPTIONAL }
|
||||||
|
func readModifyDNRequest(bytes *Bytes) (ret ModifyDNRequest, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagModifyDNRequest, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readModifyDNRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (req *ModifyDNRequest) readComponents(bytes *Bytes) (err error) {
|
||||||
|
req.entry, err = readLDAPDN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.newrdn, err = readRelativeLDAPDN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.deleteoldrdn, err = readBOOLEAN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == TagModifyDNRequestNewSuperior {
|
||||||
|
var ldapdn LDAPDN
|
||||||
|
ldapdn, err = readTaggedLDAPDN(bytes, classContextSpecific, TagModifyDNRequestNewSuperior)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.newSuperior = ldapdn.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// newrdn RelativeLDAPDN,
|
||||||
|
// deleteoldrdn BOOLEAN,
|
||||||
|
// newSuperior [0] LDAPDN OPTIONAL }
|
||||||
|
func (m ModifyDNRequest) write(bytes *Bytes) (size int) {
|
||||||
|
if m.newSuperior != nil {
|
||||||
|
size += m.newSuperior.writeTagged(bytes, classContextSpecific, TagModifyDNRequestNewSuperior)
|
||||||
|
}
|
||||||
|
size += m.deleteoldrdn.write(bytes)
|
||||||
|
size += m.newrdn.write(bytes)
|
||||||
|
size += m.entry.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagModifyDNRequest, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// newrdn RelativeLDAPDN,
|
||||||
|
// deleteoldrdn BOOLEAN,
|
||||||
|
// newSuperior [0] LDAPDN OPTIONAL }
|
||||||
|
func (m ModifyDNRequest) size() (size int) {
|
||||||
|
size += m.entry.size()
|
||||||
|
size += m.newrdn.size()
|
||||||
|
size += m.deleteoldrdn.size()
|
||||||
|
if m.newSuperior != nil {
|
||||||
|
size += m.newSuperior.sizeTagged(TagModifyDNRequestNewSuperior)
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(TagModifyDNRequest, size)
|
||||||
|
return
|
||||||
|
}
|
28
goldap/modify_dn_response.go
Normal file
28
goldap/modify_dn_response.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyDNResponse ::= [APPLICATION 13] LDAPResult
|
||||||
|
func readModifyDNResponse(bytes *Bytes) (ret ModifyDNResponse, err error) {
|
||||||
|
var res LDAPResult
|
||||||
|
res, err = readTaggedLDAPResult(bytes, classApplication, TagModifyDNResponse)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readModifyDNResponse:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = ModifyDNResponse(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyDNResponse ::= [APPLICATION 13] LDAPResult
|
||||||
|
func (m ModifyDNResponse) write(bytes *Bytes) int {
|
||||||
|
return LDAPResult(m).writeTagged(bytes, classApplication, TagModifyDNResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyDNResponse ::= [APPLICATION 13] LDAPResult
|
||||||
|
func (m ModifyDNResponse) size() int {
|
||||||
|
return LDAPResult(m).sizeTagged(TagModifyDNResponse)
|
||||||
|
}
|
89
goldap/modify_request.go
Normal file
89
goldap/modify_request.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||||
|
// object LDAPDN,
|
||||||
|
// changes SEQUENCE OF change SEQUENCE {
|
||||||
|
// operation ENUMERATED {
|
||||||
|
// add (0),
|
||||||
|
// delete (1),
|
||||||
|
// replace (2),
|
||||||
|
// ... },
|
||||||
|
// modification PartialAttribute } }
|
||||||
|
func readModifyRequest(bytes *Bytes) (ret ModifyRequest, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagModifyRequest, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readModifyRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m *ModifyRequest) readComponents(bytes *Bytes) (err error) {
|
||||||
|
m.object, err = readLDAPDN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, m.readChanges)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m *ModifyRequest) readChanges(bytes *Bytes) (err error) {
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
var c ModifyRequestChange
|
||||||
|
c, err = readModifyRequestChange(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readChanges:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.changes = append(m.changes, c)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||||
|
// object LDAPDN,
|
||||||
|
// changes SEQUENCE OF change SEQUENCE {
|
||||||
|
// operation ENUMERATED {
|
||||||
|
// add (0),
|
||||||
|
// delete (1),
|
||||||
|
// replace (2),
|
||||||
|
// ... },
|
||||||
|
// modification PartialAttribute } }
|
||||||
|
func (m ModifyRequest) write(bytes *Bytes) (size int) {
|
||||||
|
for i := len(m.changes) - 1; i >= 0; i-- {
|
||||||
|
size += m.changes[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
size += m.object.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagModifyRequest, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||||
|
// object LDAPDN,
|
||||||
|
// changes SEQUENCE OF change SEQUENCE {
|
||||||
|
// operation ENUMERATED {
|
||||||
|
// add (0),
|
||||||
|
// delete (1),
|
||||||
|
// replace (2),
|
||||||
|
// ... },
|
||||||
|
// modification PartialAttribute } }
|
||||||
|
func (m ModifyRequest) size() (size int) {
|
||||||
|
for _, change := range m.changes {
|
||||||
|
size += change.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
size += m.object.size()
|
||||||
|
size += sizeTagAndLength(TagModifyRequest, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m *ModifyRequest) Object() LDAPDN {
|
||||||
|
return m.object
|
||||||
|
}
|
||||||
|
func (m *ModifyRequest) Changes() []ModifyRequestChange {
|
||||||
|
return m.changes
|
||||||
|
}
|
43
goldap/modify_request_change.go
Normal file
43
goldap/modify_request_change.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func readModifyRequestChange(bytes *Bytes) (ret ModifyRequestChange, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readModifyRequestChange:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m *ModifyRequestChange) readComponents(bytes *Bytes) (err error) {
|
||||||
|
m.operation, err = readENUMERATED(bytes, EnumeratedModifyRequestChangeOperation)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.modification, err = readPartialAttribute(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m ModifyRequestChange) write(bytes *Bytes) (size int) {
|
||||||
|
size += m.modification.write(bytes)
|
||||||
|
size += m.operation.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m ModifyRequestChange) size() (size int) {
|
||||||
|
size += m.operation.size()
|
||||||
|
size += m.modification.size()
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m *ModifyRequestChange) Operation() ENUMERATED {
|
||||||
|
return m.operation
|
||||||
|
}
|
||||||
|
func (m *ModifyRequestChange) Modification() *PartialAttribute {
|
||||||
|
return &m.modification
|
||||||
|
}
|
36
goldap/modify_response.go
Normal file
36
goldap/modify_response.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyResponse ::= [APPLICATION 7] LDAPResult
|
||||||
|
func readModifyResponse(bytes *Bytes) (ret ModifyResponse, err error) {
|
||||||
|
var res LDAPResult
|
||||||
|
res, err = readTaggedLDAPResult(bytes, classApplication, TagModifyResponse)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readModifyResponse:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = ModifyResponse(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (l LDAPResult) writeTagged(bytes *Bytes, class int, tag int) (size int) {
|
||||||
|
size += l.writeComponents(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyResponse ::= [APPLICATION 7] LDAPResult
|
||||||
|
func (m ModifyResponse) write(bytes *Bytes) int {
|
||||||
|
return LDAPResult(m).writeTagged(bytes, classApplication, TagModifyResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyResponse ::= [APPLICATION 7] LDAPResult
|
||||||
|
func (m ModifyResponse) size() int {
|
||||||
|
return LDAPResult(m).sizeTagged(TagModifyResponse)
|
||||||
|
}
|
||||||
|
func (l *ModifyResponse) SetResultCode(code int) {
|
||||||
|
l.resultCode = ENUMERATED(code)
|
||||||
|
}
|
44
goldap/octetstring.go
Normal file
44
goldap/octetstring.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func readOCTETSTRING(bytes *Bytes) (ret OCTETSTRING, err error) {
|
||||||
|
var value interface{}
|
||||||
|
value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagOctetString, tagOctetString)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readOCTETSTRING:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = OCTETSTRING(value.([]byte))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTaggedOCTETSTRING(bytes *Bytes, class int, tag int) (ret OCTETSTRING, err error) {
|
||||||
|
var value interface{}
|
||||||
|
value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagOctetString)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedOCTETSTRING:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = OCTETSTRING(value.([]byte))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (o OCTETSTRING) Pointer() *OCTETSTRING { return &o }
|
||||||
|
func (o OCTETSTRING) write(bytes *Bytes) int {
|
||||||
|
return bytes.WritePrimitiveSubBytes(classUniversal, tagOctetString, o)
|
||||||
|
}
|
||||||
|
func (o OCTETSTRING) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return bytes.WritePrimitiveSubBytes(class, tag, o)
|
||||||
|
}
|
||||||
|
func (o OCTETSTRING) size() int {
|
||||||
|
return SizePrimitiveSubBytes(tagOctetString, o)
|
||||||
|
}
|
||||||
|
func (o OCTETSTRING) sizeTagged(tag int) int {
|
||||||
|
return SizePrimitiveSubBytes(tag, o)
|
||||||
|
}
|
||||||
|
func (l OCTETSTRING) String() string {
|
||||||
|
return string(l)
|
||||||
|
}
|
||||||
|
func (l OCTETSTRING) Bytes() []byte {
|
||||||
|
return []byte(l)
|
||||||
|
}
|
50
goldap/oid.go
Normal file
50
goldap/oid.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// LDAPOID ::= OCTET STRING -- Constrained to <numericoid>
|
||||||
|
// -- [RFC4512]
|
||||||
|
|
||||||
|
func (l LDAPOID) String() string {
|
||||||
|
return string(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPOID) Bytes() []byte {
|
||||||
|
return []byte(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPOID) Pointer() *LDAPOID { return &l }
|
||||||
|
|
||||||
|
func readTaggedLDAPOID(bytes *Bytes, class int, tag int) (ret LDAPOID, err error) {
|
||||||
|
var octetstring OCTETSTRING
|
||||||
|
octetstring, err = readTaggedOCTETSTRING(bytes, class, tag)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedLDAPOID:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// @TODO: check RFC4512 for <numericoid>
|
||||||
|
ret = LDAPOID(octetstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLDAPOID(bytes *Bytes) (ret LDAPOID, err error) {
|
||||||
|
return readTaggedLDAPOID(bytes, classUniversal, tagOctetString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPOID) write(bytes *Bytes) int {
|
||||||
|
return OCTETSTRING(l).write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPOID) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return OCTETSTRING(l).writeTagged(bytes, class, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPOID) size() int {
|
||||||
|
return OCTETSTRING(l).size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LDAPOID) sizeTagged(tag int) int {
|
||||||
|
return OCTETSTRING(l).sizeTagged(tag)
|
||||||
|
}
|
76
goldap/partial_attribute.go
Normal file
76
goldap/partial_attribute.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// PartialAttribute ::= SEQUENCE {
|
||||||
|
// type AttributeDescription,
|
||||||
|
// vals SET OF value AttributeValue }
|
||||||
|
func readPartialAttribute(bytes *Bytes) (ret PartialAttribute, err error) {
|
||||||
|
ret = PartialAttribute{vals: make([]AttributeValue, 0, 10)}
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readPartialAttribute:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (partialattribute *PartialAttribute) readComponents(bytes *Bytes) (err error) {
|
||||||
|
partialattribute.type_, err = readAttributeDescription(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSet, partialattribute.readValsComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (partialattribute *PartialAttribute) readValsComponents(bytes *Bytes) (err error) {
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
var attributevalue AttributeValue
|
||||||
|
attributevalue, err = readAttributeValue(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readValsComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
partialattribute.vals = append(partialattribute.vals, attributevalue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PartialAttribute ::= SEQUENCE {
|
||||||
|
// type AttributeDescription,
|
||||||
|
// vals SET OF value AttributeValue }
|
||||||
|
func (p PartialAttribute) write(bytes *Bytes) (size int) {
|
||||||
|
for i := len(p.vals) - 1; i >= 0; i-- {
|
||||||
|
size += p.vals[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSet, size)
|
||||||
|
size += p.type_.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PartialAttribute ::= SEQUENCE {
|
||||||
|
// type AttributeDescription,
|
||||||
|
// vals SET OF value AttributeValue }
|
||||||
|
func (p PartialAttribute) size() (size int) {
|
||||||
|
for _, value := range p.vals {
|
||||||
|
size += value.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tagSet, size)
|
||||||
|
size += p.type_.size()
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (p *PartialAttribute) Type_() AttributeDescription {
|
||||||
|
return p.type_
|
||||||
|
}
|
||||||
|
func (p *PartialAttribute) Vals() []AttributeValue {
|
||||||
|
return p.vals
|
||||||
|
}
|
53
goldap/partial_attribute_list.go
Normal file
53
goldap/partial_attribute_list.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// PartialAttributeList ::= SEQUENCE OF
|
||||||
|
// partialAttribute PartialAttribute
|
||||||
|
func readPartialAttributeList(bytes *Bytes) (ret PartialAttributeList, err error) {
|
||||||
|
ret = PartialAttributeList(make([]PartialAttribute, 0, 10))
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readPartialAttributeList:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (partialattributelist *PartialAttributeList) readComponents(bytes *Bytes) (err error) {
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
var partialattribute PartialAttribute
|
||||||
|
partialattribute, err = readPartialAttribute(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*partialattributelist = append(*partialattributelist, partialattribute)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PartialAttributeList ::= SEQUENCE OF
|
||||||
|
// partialAttribute PartialAttribute
|
||||||
|
func (p PartialAttributeList) write(bytes *Bytes) (size int) {
|
||||||
|
for i := len(p) - 1; i >= 0; i-- {
|
||||||
|
size += p[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PartialAttributeList ::= SEQUENCE OF
|
||||||
|
// partialAttribute PartialAttribute
|
||||||
|
func (p PartialAttributeList) size() (size int) {
|
||||||
|
for _, att := range p {
|
||||||
|
size += att.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (p *PartialAttributeList) add(a PartialAttribute) {
|
||||||
|
*p = append(*p, a)
|
||||||
|
}
|
63
goldap/protocol_op.go
Normal file
63
goldap/protocol_op.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func readProtocolOp(bytes *Bytes) (ret ProtocolOp, err error) {
|
||||||
|
tagAndLength, err := bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readProtocolOp:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch tagAndLength.Tag {
|
||||||
|
case TagBindRequest:
|
||||||
|
ret, err = readBindRequest(bytes)
|
||||||
|
case TagBindResponse:
|
||||||
|
ret, err = readBindResponse(bytes)
|
||||||
|
case TagUnbindRequest:
|
||||||
|
ret, err = readUnbindRequest(bytes)
|
||||||
|
case TagSearchRequest:
|
||||||
|
ret, err = readSearchRequest(bytes)
|
||||||
|
case TagSearchResultEntry:
|
||||||
|
ret, err = readSearchResultEntry(bytes)
|
||||||
|
case TagSearchResultDone:
|
||||||
|
ret, err = readSearchResultDone(bytes)
|
||||||
|
case TagSearchResultReference:
|
||||||
|
ret, err = readSearchResultReference(bytes)
|
||||||
|
case TagModifyRequest:
|
||||||
|
ret, err = readModifyRequest(bytes)
|
||||||
|
case TagModifyResponse:
|
||||||
|
ret, err = readModifyResponse(bytes)
|
||||||
|
case TagAddRequest:
|
||||||
|
ret, err = readAddRequest(bytes)
|
||||||
|
case TagAddResponse:
|
||||||
|
ret, err = readAddResponse(bytes)
|
||||||
|
case TagDelRequest:
|
||||||
|
ret, err = readDelRequest(bytes)
|
||||||
|
case TagDelResponse:
|
||||||
|
ret, err = readDelResponse(bytes)
|
||||||
|
case TagModifyDNRequest:
|
||||||
|
ret, err = readModifyDNRequest(bytes)
|
||||||
|
case TagModifyDNResponse:
|
||||||
|
ret, err = readModifyDNResponse(bytes)
|
||||||
|
case TagCompareRequest:
|
||||||
|
ret, err = readCompareRequest(bytes)
|
||||||
|
case TagCompareResponse:
|
||||||
|
ret, err = readCompareResponse(bytes)
|
||||||
|
case TagAbandonRequest:
|
||||||
|
ret, err = readAbandonRequest(bytes)
|
||||||
|
case TagExtendedRequest:
|
||||||
|
ret, err = readExtendedRequest(bytes)
|
||||||
|
case TagExtendedResponse:
|
||||||
|
ret, err = readExtendedResponse(bytes)
|
||||||
|
case TagIntermediateResponse:
|
||||||
|
ret, err = readIntermediateResponse(bytes)
|
||||||
|
default:
|
||||||
|
err = LdapError{fmt.Sprintf("readProtocolOp: invalid tag value %d for protocolOp", tagAndLength.Tag)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readProtocolOp:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
18
goldap/read.go
Normal file
18
goldap/read.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReadLDAPMessage(bytes *Bytes) (message LDAPMessage, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classUniversal, tagSequence, message.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("ReadLDAPMessage:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// END
|
||||||
|
//
|
3146
goldap/read_error_test.go
Normal file
3146
goldap/read_error_test.go
Normal file
File diff suppressed because one or more lines are too long
2936
goldap/read_test.go
Normal file
2936
goldap/read_test.go
Normal file
File diff suppressed because one or more lines are too long
50
goldap/referral.go
Normal file
50
goldap/referral.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
|
||||||
|
func readTaggedReferral(bytes *Bytes, class int, tag int) (referral Referral, err error) {
|
||||||
|
err = bytes.ReadSubBytes(class, tag, referral.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedReferral:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (referral *Referral) readComponents(bytes *Bytes) (err error) {
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
var uri URI
|
||||||
|
uri, err = readURI(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*referral = append(*referral, uri)
|
||||||
|
}
|
||||||
|
if len(*referral) == 0 {
|
||||||
|
return LdapError{"readComponents: expecting at least one URI"}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (referral Referral) Pointer() *Referral { return &referral }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
|
||||||
|
func (r Referral) writeTagged(bytes *Bytes, class int, tag int) (size int) {
|
||||||
|
for i := len(r) - 1; i >= 0; i-- {
|
||||||
|
size += r[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
|
||||||
|
func (r Referral) sizeTagged(tag int) (size int) {
|
||||||
|
for _, uri := range r {
|
||||||
|
size += uri.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tag, size)
|
||||||
|
return
|
||||||
|
}
|
15
goldap/relative_ldap_dn.go
Normal file
15
goldap/relative_ldap_dn.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
//
|
||||||
|
// RelativeLDAPDN ::= LDAPString -- Constrained to <name-component>
|
||||||
|
// -- [RFC4514]
|
||||||
|
func (r RelativeLDAPDN) write(bytes *Bytes) int {
|
||||||
|
return LDAPString(r).write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// RelativeLDAPDN ::= LDAPString -- Constrained to <name-component>
|
||||||
|
// -- [RFC4514]
|
||||||
|
func (r RelativeLDAPDN) size() int {
|
||||||
|
return LDAPString(r).size()
|
||||||
|
}
|
282
goldap/result.go
Normal file
282
goldap/result.go
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAPResult ::= SEQUENCE {
|
||||||
|
// resultCode ENUMERATED {
|
||||||
|
// success (0),
|
||||||
|
// operationsError (1),
|
||||||
|
// protocolError (2),
|
||||||
|
// timeLimitExceeded (3),
|
||||||
|
// sizeLimitExceeded (4),
|
||||||
|
// compareFalse (5),
|
||||||
|
// compareTrue (6),
|
||||||
|
// authMethodNotSupported (7),
|
||||||
|
// strongerAuthRequired (8),
|
||||||
|
// -- 9 reserved --
|
||||||
|
// referral (10),
|
||||||
|
// adminLimitExceeded (11),
|
||||||
|
// unavailableCriticalExtension (12),
|
||||||
|
// confidentialityRequired (13),
|
||||||
|
// saslBindInProgress (14),
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//Sermersheim Standards Track [Page 55]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//RFC 4511 LDAPv3 June 2006
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// noSuchAttribute (16),
|
||||||
|
// undefinedAttributeType (17),
|
||||||
|
// inappropriateMatching (18),
|
||||||
|
// constraintViolation (19),
|
||||||
|
// attributeOrValueExists (20),
|
||||||
|
// invalidAttributeSyntax (21),
|
||||||
|
// -- 22-31 unused --
|
||||||
|
// noSuchObject (32),
|
||||||
|
// aliasProblem (33),
|
||||||
|
// invalidDNSyntax (34),
|
||||||
|
// -- 35 reserved for undefined isLeaf --
|
||||||
|
// aliasDereferencingProblem (36),
|
||||||
|
// -- 37-47 unused --
|
||||||
|
// inappropriateAuthentication (48),
|
||||||
|
// invalidCredentials (49),
|
||||||
|
// insufficientAccessRights (50),
|
||||||
|
// busy (51),
|
||||||
|
// unavailable (52),
|
||||||
|
// unwillingToPerform (53),
|
||||||
|
// loopDetect (54),
|
||||||
|
// -- 55-63 unused --
|
||||||
|
// namingViolation (64),
|
||||||
|
// objectClassViolation (65),
|
||||||
|
// notAllowedOnNonLeaf (66),
|
||||||
|
// notAllowedOnRDN (67),
|
||||||
|
// entryAlreadyExists (68),
|
||||||
|
// objectClassModsProhibited (69),
|
||||||
|
// -- 70 reserved for CLDAP --
|
||||||
|
// affectsMultipleDSAs (71),
|
||||||
|
// -- 72-79 unused --
|
||||||
|
// other (80),
|
||||||
|
// ... },
|
||||||
|
// matchedDN LDAPDN,
|
||||||
|
// diagnosticMessage LDAPString,
|
||||||
|
// referral [3] Referral OPTIONAL }
|
||||||
|
func readTaggedLDAPResult(bytes *Bytes, class int, tag int) (ret LDAPResult, err error) {
|
||||||
|
err = bytes.ReadSubBytes(class, tag, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedLDAPResult:\n%s", err.Error())}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func readLDAPResult(bytes *Bytes) (ldapresult LDAPResult, err error) {
|
||||||
|
return readTaggedLDAPResult(bytes, classUniversal, tagSequence)
|
||||||
|
}
|
||||||
|
func (ldapresult *LDAPResult) readComponents(bytes *Bytes) (err error) {
|
||||||
|
ldapresult.resultCode, err = readENUMERATED(bytes, EnumeratedLDAPResultCode)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ldapresult.matchedDN, err = readLDAPDN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ldapresult.diagnosticMessage, err = readLDAPString(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var tag TagAndLength
|
||||||
|
tag, err = bytes.PreviewTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tag.Tag == TagLDAPResultReferral {
|
||||||
|
var referral Referral
|
||||||
|
referral, err = readTaggedReferral(bytes, classContextSpecific, TagLDAPResultReferral)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ldapresult.referral = referral.Pointer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAPResult ::= SEQUENCE {
|
||||||
|
// resultCode ENUMERATED {
|
||||||
|
// success (0),
|
||||||
|
// operationsError (1),
|
||||||
|
// protocolError (2),
|
||||||
|
// timeLimitExceeded (3),
|
||||||
|
// sizeLimitExceeded (4),
|
||||||
|
// compareFalse (5),
|
||||||
|
// compareTrue (6),
|
||||||
|
// authMethodNotSupported (7),
|
||||||
|
// strongerAuthRequired (8),
|
||||||
|
// -- 9 reserved --
|
||||||
|
// referral (10),
|
||||||
|
// adminLimitExceeded (11),
|
||||||
|
// unavailableCriticalExtension (12),
|
||||||
|
// confidentialityRequired (13),
|
||||||
|
// saslBindInProgress (14),
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//Sermersheim Standards Track [Page 55]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//RFC 4511 LDAPv3 June 2006
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// noSuchAttribute (16),
|
||||||
|
// undefinedAttributeType (17),
|
||||||
|
// inappropriateMatching (18),
|
||||||
|
// constraintViolation (19),
|
||||||
|
// attributeOrValueExists (20),
|
||||||
|
// invalidAttributeSyntax (21),
|
||||||
|
// -- 22-31 unused --
|
||||||
|
// noSuchObject (32),
|
||||||
|
// aliasProblem (33),
|
||||||
|
// invalidDNSyntax (34),
|
||||||
|
// -- 35 reserved for undefined isLeaf --
|
||||||
|
// aliasDereferencingProblem (36),
|
||||||
|
// -- 37-47 unused --
|
||||||
|
// inappropriateAuthentication (48),
|
||||||
|
// invalidCredentials (49),
|
||||||
|
// insufficientAccessRights (50),
|
||||||
|
// busy (51),
|
||||||
|
// unavailable (52),
|
||||||
|
// unwillingToPerform (53),
|
||||||
|
// loopDetect (54),
|
||||||
|
// -- 55-63 unused --
|
||||||
|
// namingViolation (64),
|
||||||
|
// objectClassViolation (65),
|
||||||
|
// notAllowedOnNonLeaf (66),
|
||||||
|
// notAllowedOnRDN (67),
|
||||||
|
// entryAlreadyExists (68),
|
||||||
|
// objectClassModsProhibited (69),
|
||||||
|
// -- 70 reserved for CLDAP --
|
||||||
|
// affectsMultipleDSAs (71),
|
||||||
|
// -- 72-79 unused --
|
||||||
|
// other (80),
|
||||||
|
// ... },
|
||||||
|
// matchedDN LDAPDN,
|
||||||
|
// diagnosticMessage LDAPString,
|
||||||
|
// referral [3] Referral OPTIONAL }
|
||||||
|
func (l LDAPResult) write(bytes *Bytes) (size int) {
|
||||||
|
size += l.writeComponents(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (l LDAPResult) writeComponents(bytes *Bytes) (size int) {
|
||||||
|
if l.referral != nil {
|
||||||
|
size += l.referral.writeTagged(bytes, classContextSpecific, TagLDAPResultReferral)
|
||||||
|
}
|
||||||
|
size += l.diagnosticMessage.write(bytes)
|
||||||
|
size += l.matchedDN.write(bytes)
|
||||||
|
size += l.resultCode.write(bytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAPResult ::= SEQUENCE {
|
||||||
|
// resultCode ENUMERATED {
|
||||||
|
// success (0),
|
||||||
|
// operationsError (1),
|
||||||
|
// protocolError (2),
|
||||||
|
// timeLimitExceeded (3),
|
||||||
|
// sizeLimitExceeded (4),
|
||||||
|
// compareFalse (5),
|
||||||
|
// compareTrue (6),
|
||||||
|
// authMethodNotSupported (7),
|
||||||
|
// strongerAuthRequired (8),
|
||||||
|
// -- 9 reserved --
|
||||||
|
// referral (10),
|
||||||
|
// adminLimitExceeded (11),
|
||||||
|
// unavailableCriticalExtension (12),
|
||||||
|
// confidentialityRequired (13),
|
||||||
|
// saslBindInProgress (14),
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//Sermersheim Standards Track [Page 55]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//RFC 4511 LDAPv3 June 2006
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// noSuchAttribute (16),
|
||||||
|
// undefinedAttributeType (17),
|
||||||
|
// inappropriateMatching (18),
|
||||||
|
// constraintViolation (19),
|
||||||
|
// attributeOrValueExists (20),
|
||||||
|
// invalidAttributeSyntax (21),
|
||||||
|
// -- 22-31 unused --
|
||||||
|
// noSuchObject (32),
|
||||||
|
// aliasProblem (33),
|
||||||
|
// invalidDNSyntax (34),
|
||||||
|
// -- 35 reserved for undefined isLeaf --
|
||||||
|
// aliasDereferencingProblem (36),
|
||||||
|
// -- 37-47 unused --
|
||||||
|
// inappropriateAuthentication (48),
|
||||||
|
// invalidCredentials (49),
|
||||||
|
// insufficientAccessRights (50),
|
||||||
|
// busy (51),
|
||||||
|
// unavailable (52),
|
||||||
|
// unwillingToPerform (53),
|
||||||
|
// loopDetect (54),
|
||||||
|
// -- 55-63 unused --
|
||||||
|
// namingViolation (64),
|
||||||
|
// objectClassViolation (65),
|
||||||
|
// notAllowedOnNonLeaf (66),
|
||||||
|
// notAllowedOnRDN (67),
|
||||||
|
// entryAlreadyExists (68),
|
||||||
|
// objectClassModsProhibited (69),
|
||||||
|
// -- 70 reserved for CLDAP --
|
||||||
|
// affectsMultipleDSAs (71),
|
||||||
|
// -- 72-79 unused --
|
||||||
|
// other (80),
|
||||||
|
// ... },
|
||||||
|
// matchedDN LDAPDN,
|
||||||
|
// diagnosticMessage LDAPString,
|
||||||
|
// referral [3] Referral OPTIONAL }
|
||||||
|
func (l LDAPResult) size() (size int) {
|
||||||
|
size += l.sizeComponents()
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (l LDAPResult) sizeTagged(tag int) (size int) {
|
||||||
|
size += l.sizeComponents()
|
||||||
|
size += sizeTagAndLength(tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (l LDAPResult) sizeComponents() (size int) {
|
||||||
|
if l.referral != nil {
|
||||||
|
size += l.referral.sizeTagged(TagLDAPResultReferral)
|
||||||
|
}
|
||||||
|
size += l.diagnosticMessage.size()
|
||||||
|
size += l.matchedDN.size()
|
||||||
|
size += l.resultCode.size()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (l *LDAPResult) SetResultCode(code int) {
|
||||||
|
l.resultCode = ENUMERATED(code)
|
||||||
|
}
|
||||||
|
func (l *LDAPResult) SeMatchedDN(code string) {
|
||||||
|
l.matchedDN = LDAPDN(code)
|
||||||
|
}
|
||||||
|
func (l *LDAPResult) SetDiagnosticMessage(code string) {
|
||||||
|
l.diagnosticMessage = LDAPString(code)
|
||||||
|
}
|
||||||
|
func (l *LDAPResult) SetReferral(r *Referral) {
|
||||||
|
l.referral = r
|
||||||
|
}
|
63
goldap/sasl_credentials.go
Normal file
63
goldap/sasl_credentials.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// SaslCredentials ::= SEQUENCE {
|
||||||
|
// mechanism LDAPString,
|
||||||
|
// credentials OCTET STRING OPTIONAL }
|
||||||
|
//
|
||||||
|
func readSaslCredentials(bytes *Bytes) (authentication SaslCredentials, err error) {
|
||||||
|
authentication = SaslCredentials{}
|
||||||
|
err = bytes.ReadSubBytes(classContextSpecific, TagAuthenticationChoiceSaslCredentials, authentication.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSaslCredentials:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (authentication *SaslCredentials) readComponents(bytes *Bytes) (err error) {
|
||||||
|
authentication.mechanism, err = readLDAPString(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bytes.HasMoreData() {
|
||||||
|
var credentials OCTETSTRING
|
||||||
|
credentials, err = readOCTETSTRING(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authentication.credentials = credentials.Pointer()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SaslCredentials ::= SEQUENCE {
|
||||||
|
// mechanism LDAPString,
|
||||||
|
// credentials OCTET STRING OPTIONAL }
|
||||||
|
//
|
||||||
|
func (s SaslCredentials) writeTagged(bytes *Bytes, class int, tag int) (size int) {
|
||||||
|
if s.credentials != nil {
|
||||||
|
size += s.credentials.write(bytes)
|
||||||
|
}
|
||||||
|
size += s.mechanism.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SaslCredentials ::= SEQUENCE {
|
||||||
|
// mechanism LDAPString,
|
||||||
|
// credentials OCTET STRING OPTIONAL }
|
||||||
|
//
|
||||||
|
func (s SaslCredentials) sizeTagged(tag int) (size int) {
|
||||||
|
if s.credentials != nil {
|
||||||
|
size += s.credentials.size()
|
||||||
|
}
|
||||||
|
size += s.mechanism.size()
|
||||||
|
size += sizeTagAndLength(tag, size)
|
||||||
|
return
|
||||||
|
}
|
246
goldap/search_request.go
Normal file
246
goldap/search_request.go
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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 }
|
||||||
|
func readSearchRequest(bytes *Bytes) (searchrequest SearchRequest, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagSearchRequest, searchrequest.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSearchRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (searchrequest *SearchRequest) readComponents(bytes *Bytes) (err error) {
|
||||||
|
searchrequest.baseObject, err = readLDAPDN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
searchrequest.scope, err = readENUMERATED(bytes, EnumeratedSearchRequestScope)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
searchrequest.derefAliases, err = readENUMERATED(bytes, EnumeratedSearchRequestDerefAliases)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
searchrequest.sizeLimit, err = readPositiveINTEGER(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
searchrequest.timeLimit, err = readPositiveINTEGER(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
searchrequest.typesOnly, err = readBOOLEAN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
searchrequest.filter, err = readFilter(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
searchrequest.attributes, err = readAttributeSelection(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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 }
|
||||||
|
func (s SearchRequest) write(bytes *Bytes) (size int) {
|
||||||
|
size += s.attributes.write(bytes)
|
||||||
|
size += s.filter.write(bytes)
|
||||||
|
size += s.typesOnly.write(bytes)
|
||||||
|
size += s.timeLimit.write(bytes)
|
||||||
|
size += s.sizeLimit.write(bytes)
|
||||||
|
size += s.derefAliases.write(bytes)
|
||||||
|
size += s.scope.write(bytes)
|
||||||
|
size += s.baseObject.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchRequest, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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 }
|
||||||
|
func (s SearchRequest) size() (size int) {
|
||||||
|
size += s.baseObject.size()
|
||||||
|
size += s.scope.size()
|
||||||
|
size += s.derefAliases.size()
|
||||||
|
size += s.sizeLimit.size()
|
||||||
|
size += s.timeLimit.size()
|
||||||
|
size += s.typesOnly.size()
|
||||||
|
size += s.filter.size()
|
||||||
|
size += s.attributes.size()
|
||||||
|
size += sizeTagAndLength(TagSearchRequest, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) BaseObject() LDAPDN {
|
||||||
|
return s.baseObject
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) Scope() ENUMERATED {
|
||||||
|
return s.scope
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) DerefAliases() ENUMERATED {
|
||||||
|
return s.derefAliases
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) SizeLimit() INTEGER {
|
||||||
|
return s.sizeLimit
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) TimeLimit() INTEGER {
|
||||||
|
return s.timeLimit
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) TypesOnly() BOOLEAN {
|
||||||
|
return s.typesOnly
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) Attributes() AttributeSelection {
|
||||||
|
return s.attributes
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) Filter() Filter {
|
||||||
|
return s.filter
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) FilterString() string {
|
||||||
|
str, _ := s.decompileFilter(s.Filter())
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
func (s *SearchRequest) decompileFilter(packet Filter) (ret string, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = errors.New("error decompiling filter")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ret = "("
|
||||||
|
err = nil
|
||||||
|
childStr := ""
|
||||||
|
|
||||||
|
switch f := packet.(type) {
|
||||||
|
case FilterAnd:
|
||||||
|
ret += "&"
|
||||||
|
for _, child := range f {
|
||||||
|
childStr, err = s.decompileFilter(child)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret += childStr
|
||||||
|
}
|
||||||
|
case FilterOr:
|
||||||
|
ret += "|"
|
||||||
|
for _, child := range f {
|
||||||
|
childStr, err = s.decompileFilter(child)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret += childStr
|
||||||
|
}
|
||||||
|
case FilterNot:
|
||||||
|
ret += "!"
|
||||||
|
childStr, err = s.decompileFilter(f.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret += childStr
|
||||||
|
|
||||||
|
case FilterSubstrings:
|
||||||
|
ret += string(f.Type_())
|
||||||
|
ret += "="
|
||||||
|
for _, fs := range f.Substrings() {
|
||||||
|
switch fsv := fs.(type) {
|
||||||
|
case SubstringInitial:
|
||||||
|
ret += string(fsv) + "*"
|
||||||
|
case SubstringAny:
|
||||||
|
ret += "*" + string(fsv) + "*"
|
||||||
|
case SubstringFinal:
|
||||||
|
ret += "*" + string(fsv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case FilterEqualityMatch:
|
||||||
|
ret += string(f.AttributeDesc())
|
||||||
|
ret += "="
|
||||||
|
ret += string(f.AssertionValue())
|
||||||
|
case FilterGreaterOrEqual:
|
||||||
|
ret += string(f.AttributeDesc())
|
||||||
|
ret += ">="
|
||||||
|
ret += string(f.AssertionValue())
|
||||||
|
case FilterLessOrEqual:
|
||||||
|
ret += string(f.AttributeDesc())
|
||||||
|
ret += "<="
|
||||||
|
ret += string(f.AssertionValue())
|
||||||
|
case FilterPresent:
|
||||||
|
// if 0 == len(packet.Children) {
|
||||||
|
// ret += ber.DecodeString(packet.Data.Bytes())
|
||||||
|
// } else {
|
||||||
|
// ret += ber.DecodeString(packet.Children[0].Data.Bytes())
|
||||||
|
// }
|
||||||
|
ret += string(f)
|
||||||
|
ret += "=*"
|
||||||
|
case FilterApproxMatch:
|
||||||
|
ret += string(f.AttributeDesc())
|
||||||
|
ret += "~="
|
||||||
|
ret += string(f.AssertionValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += ")"
|
||||||
|
return
|
||||||
|
}
|
31
goldap/search_result_done.go
Normal file
31
goldap/search_result_done.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultDone ::= [APPLICATION 5] LDAPResult
|
||||||
|
func readSearchResultDone(bytes *Bytes) (ret SearchResultDone, err error) {
|
||||||
|
var ldapresult LDAPResult
|
||||||
|
ldapresult, err = readTaggedLDAPResult(bytes, classApplication, TagSearchResultDone)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSearchResultDone:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret = SearchResultDone(ldapresult)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultDone ::= [APPLICATION 5] LDAPResult
|
||||||
|
func (s SearchResultDone) write(bytes *Bytes) int {
|
||||||
|
return LDAPResult(s).writeTagged(bytes, classApplication, TagSearchResultDone)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultDone ::= [APPLICATION 5] LDAPResult
|
||||||
|
func (s SearchResultDone) size() int {
|
||||||
|
return LDAPResult(s).sizeTagged(TagSearchResultDone)
|
||||||
|
}
|
||||||
|
func (l *SearchResultDone) SetResultCode(code int) {
|
||||||
|
l.resultCode = ENUMERATED(code)
|
||||||
|
}
|
58
goldap/search_result_entry.go
Normal file
58
goldap/search_result_entry.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
|
||||||
|
// objectName LDAPDN,
|
||||||
|
// attributes PartialAttributeList }
|
||||||
|
func readSearchResultEntry(bytes *Bytes) (searchresultentry SearchResultEntry, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagSearchResultEntry, searchresultentry.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSearchResultEntry:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (searchresultentry *SearchResultEntry) readComponents(bytes *Bytes) (err error) {
|
||||||
|
searchresultentry.objectName, err = readLDAPDN(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
searchresultentry.attributes, err = readPartialAttributeList(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
|
||||||
|
// objectName LDAPDN,
|
||||||
|
// attributes PartialAttributeList }
|
||||||
|
func (s SearchResultEntry) write(bytes *Bytes) (size int) {
|
||||||
|
size += s.attributes.write(bytes)
|
||||||
|
size += s.objectName.write(bytes)
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchResultEntry, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
|
||||||
|
// objectName LDAPDN,
|
||||||
|
// attributes PartialAttributeList }
|
||||||
|
func (s SearchResultEntry) size() (size int) {
|
||||||
|
size += s.objectName.size()
|
||||||
|
size += s.attributes.size()
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (s *SearchResultEntry) SetObjectName(on string) {
|
||||||
|
s.objectName = LDAPDN(on)
|
||||||
|
}
|
||||||
|
func (s *SearchResultEntry) AddAttribute(name AttributeDescription, values ...AttributeValue) {
|
||||||
|
var ea = PartialAttribute{type_: name, vals: values}
|
||||||
|
s.attributes.add(ea)
|
||||||
|
}
|
53
goldap/search_result_reference.go
Normal file
53
goldap/search_result_reference.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultReference ::= [APPLICATION 19] SEQUENCE
|
||||||
|
// SIZE (1..MAX) OF uri URI
|
||||||
|
func readSearchResultReference(bytes *Bytes) (ret SearchResultReference, err error) {
|
||||||
|
err = bytes.ReadSubBytes(classApplication, TagSearchResultReference, ret.readComponents)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readSearchResultReference:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (s *SearchResultReference) readComponents(bytes *Bytes) (err error) {
|
||||||
|
for bytes.HasMoreData() {
|
||||||
|
var uri URI
|
||||||
|
uri, err = readURI(bytes)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*s = append(*s, uri)
|
||||||
|
}
|
||||||
|
if len(*s) == 0 {
|
||||||
|
err = LdapError{"readComponents: expecting at least one URI"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultReference ::= [APPLICATION 19] SEQUENCE
|
||||||
|
// SIZE (1..MAX) OF uri URI
|
||||||
|
func (s SearchResultReference) write(bytes *Bytes) (size int) {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
size += s[i].write(bytes)
|
||||||
|
}
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchResultReference, size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultReference ::= [APPLICATION 19] SEQUENCE
|
||||||
|
// SIZE (1..MAX) OF uri URI
|
||||||
|
func (s SearchResultReference) size() (size int) {
|
||||||
|
for _, uri := range s {
|
||||||
|
size += uri.size()
|
||||||
|
}
|
||||||
|
size += sizeTagAndLength(tagSequence, size)
|
||||||
|
return
|
||||||
|
}
|
85
goldap/size_test.go
Normal file
85
goldap/size_test.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSizeLDAPMessage(t *testing.T) {
|
||||||
|
|
||||||
|
var testData = getLDAPMessageTestData()
|
||||||
|
for i, test := range testData {
|
||||||
|
message, err := ReadLDAPMessage(&test.bytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d error at offset %d (%s): %s", i, test.bytes.offset, test.bytes.DumpCurrentBytes(), err)
|
||||||
|
}
|
||||||
|
size := message.size()
|
||||||
|
expected := len(test.bytes.bytes)
|
||||||
|
if size != expected {
|
||||||
|
t.Errorf("#%d: wrong size, GOT: %d, EXPECTED: %d", i, size, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagAndLengthTestData struct {
|
||||||
|
tag int
|
||||||
|
length int
|
||||||
|
expectedSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSizeTagAndLengthTestData() (ret []tagAndLengthTestData) {
|
||||||
|
return []tagAndLengthTestData{
|
||||||
|
// Length between 0 and 127 are encoded on one byte
|
||||||
|
{
|
||||||
|
tag: tagSequence,
|
||||||
|
length: 0,
|
||||||
|
expectedSize: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: tagSequence,
|
||||||
|
length: 127,
|
||||||
|
expectedSize: 2,
|
||||||
|
},
|
||||||
|
// Length between 128 and 255 are encoded on two bytes
|
||||||
|
{
|
||||||
|
tag: tagSequence,
|
||||||
|
length: 128,
|
||||||
|
expectedSize: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: tagSequence,
|
||||||
|
length: 255,
|
||||||
|
expectedSize: 3,
|
||||||
|
},
|
||||||
|
// Length between 256 (2^8) and 65535 (2^16-1) are encoded on three bytes
|
||||||
|
{
|
||||||
|
tag: tagSequence,
|
||||||
|
length: 256,
|
||||||
|
expectedSize: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: tagSequence,
|
||||||
|
length: 65535,
|
||||||
|
expectedSize: 4,
|
||||||
|
},
|
||||||
|
// Length between 65536 (2^16) and 16777215 (2^24-1) are encoded on four bytes
|
||||||
|
{
|
||||||
|
tag: tagSequence,
|
||||||
|
length: 65536,
|
||||||
|
expectedSize: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: tagSequence,
|
||||||
|
length: 16777215,
|
||||||
|
expectedSize: 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestSizeTagAndLength(t *testing.T) {
|
||||||
|
for i, test := range getSizeTagAndLengthTestData() {
|
||||||
|
size := sizeTagAndLength(test.tag, test.length)
|
||||||
|
if test.expectedSize != size {
|
||||||
|
t.Errorf("#%d: wrong size, GOT: %d, EXPECTED: %d", i, size, test.expectedSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
goldap/string.go
Normal file
38
goldap/string.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func readTaggedLDAPString(bytes *Bytes, class int, tag int) (ldapstring LDAPString, err error) {
|
||||||
|
var octetstring OCTETSTRING
|
||||||
|
octetstring, err = readTaggedOCTETSTRING(bytes, class, tag)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readTaggedLDAPString:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ldapstring = LDAPString(octetstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// LDAPString ::= OCTET STRING -- UTF-8 encoded,
|
||||||
|
// -- [ISO10646] characters
|
||||||
|
func readLDAPString(bytes *Bytes) (ldapstring LDAPString, err error) {
|
||||||
|
return readTaggedLDAPString(bytes, classUniversal, tagOctetString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LDAPString ::= OCTET STRING -- UTF-8 encoded,
|
||||||
|
// -- [ISO10646] characters
|
||||||
|
func (s LDAPString) write(bytes *Bytes) int {
|
||||||
|
return OCTETSTRING(s).write(bytes)
|
||||||
|
}
|
||||||
|
func (s LDAPString) writeTagged(bytes *Bytes, class int, tag int) int {
|
||||||
|
return OCTETSTRING(s).writeTagged(bytes, class, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LDAPString ::= OCTET STRING -- UTF-8 encoded,
|
||||||
|
// -- [ISO10646] characters
|
||||||
|
func (s LDAPString) size() int {
|
||||||
|
return OCTETSTRING(s).size()
|
||||||
|
}
|
||||||
|
func (s LDAPString) sizeTagged(tag int) int {
|
||||||
|
return OCTETSTRING(s).sizeTagged(tag)
|
||||||
|
}
|
739
goldap/struct.go
Normal file
739
goldap/struct.go
Normal file
|
@ -0,0 +1,739 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
type OCTETSTRING string
|
||||||
|
type INTEGER int32 // In this RFC the max INTEGER value is 2^31 - 1, so int32 is enough
|
||||||
|
type BOOLEAN bool
|
||||||
|
type ENUMERATED int32
|
||||||
|
|
||||||
|
// This appendix is normative.
|
||||||
|
//
|
||||||
|
// Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18}
|
||||||
|
// -- Copyright (C) The Internet Society (2006). This version of
|
||||||
|
// -- this ASN.1 module is part of RFC 4511; see the RFC itself
|
||||||
|
// -- for full legal notices.
|
||||||
|
// DEFINITIONS
|
||||||
|
// IMPLICIT TAGS
|
||||||
|
// EXTENSIBILITY IMPLIED ::=
|
||||||
|
//
|
||||||
|
// BEGIN
|
||||||
|
//
|
||||||
|
// LDAPMessage ::= SEQUENCE {
|
||||||
|
// messageID MessageID,
|
||||||
|
// protocolOp CHOICE {
|
||||||
|
// bindRequest BindRequest,
|
||||||
|
// bindResponse BindResponse,
|
||||||
|
// unbindRequest UnbindRequest,
|
||||||
|
// searchRequest SearchRequest,
|
||||||
|
// searchResEntry SearchResultEntry,
|
||||||
|
// searchResDone SearchResultDone,
|
||||||
|
// searchResRef SearchResultReference,
|
||||||
|
// modifyRequest ModifyRequest,
|
||||||
|
// modifyResponse ModifyResponse,
|
||||||
|
// addRequest AddRequest,
|
||||||
|
// addResponse AddResponse,
|
||||||
|
// delRequest DelRequest,
|
||||||
|
// delResponse DelResponse,
|
||||||
|
// modDNRequest ModifyDNRequest,
|
||||||
|
// modDNResponse ModifyDNResponse,
|
||||||
|
// compareRequest CompareRequest,
|
||||||
|
// compareResponse CompareResponse,
|
||||||
|
// abandonRequest AbandonRequest,
|
||||||
|
// extendedReq ExtendedRequest,
|
||||||
|
// extendedResp ExtendedResponse,
|
||||||
|
// ...,
|
||||||
|
// intermediateResponse IntermediateResponse },
|
||||||
|
// controls [0] Controls OPTIONAL }
|
||||||
|
//
|
||||||
|
type LDAPMessage struct {
|
||||||
|
messageID MessageID
|
||||||
|
protocolOp ProtocolOp
|
||||||
|
controls *Controls
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagLDAPMessageControls = 0
|
||||||
|
|
||||||
|
type ProtocolOp interface {
|
||||||
|
size() int
|
||||||
|
write(*Bytes) int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageID ::= INTEGER (0 .. maxInt)
|
||||||
|
//
|
||||||
|
type MessageID INTEGER
|
||||||
|
|
||||||
|
// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
|
||||||
|
const maxInt = INTEGER(2147483647)
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAPString ::= OCTET STRING -- UTF-8 encoded,
|
||||||
|
// -- [ISO10646] characters
|
||||||
|
type LDAPString OCTETSTRING
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//Sermersheim Standards Track [Page 54]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//RFC 4511 LDAPv3 June 2006
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// LDAPOID ::= OCTET STRING -- Constrained to <numericoid>
|
||||||
|
// -- [RFC4512]
|
||||||
|
type LDAPOID OCTETSTRING
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAPDN ::= LDAPString -- Constrained to <distinguishedName>
|
||||||
|
// -- [RFC4514]
|
||||||
|
type LDAPDN LDAPString
|
||||||
|
|
||||||
|
//
|
||||||
|
// RelativeLDAPDN ::= LDAPString -- Constrained to <name-component>
|
||||||
|
// -- [RFC4514]
|
||||||
|
type RelativeLDAPDN LDAPString
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeDescription ::= LDAPString
|
||||||
|
// -- Constrained to <attributedescription>
|
||||||
|
// -- [RFC4512]
|
||||||
|
type AttributeDescription LDAPString
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeValue ::= OCTET STRING
|
||||||
|
type AttributeValue OCTETSTRING
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeValueAssertion ::= SEQUENCE {
|
||||||
|
// attributeDesc AttributeDescription,
|
||||||
|
// assertionValue AssertionValue }
|
||||||
|
type AttributeValueAssertion struct {
|
||||||
|
attributeDesc AttributeDescription
|
||||||
|
assertionValue AssertionValue
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// AssertionValue ::= OCTET STRING
|
||||||
|
type AssertionValue OCTETSTRING
|
||||||
|
|
||||||
|
//
|
||||||
|
// PartialAttribute ::= SEQUENCE {
|
||||||
|
// type AttributeDescription,
|
||||||
|
// vals SET OF value AttributeValue }
|
||||||
|
type PartialAttribute struct {
|
||||||
|
type_ AttributeDescription
|
||||||
|
vals []AttributeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Attribute ::= PartialAttribute(WITH COMPONENTS {
|
||||||
|
// ...,
|
||||||
|
// vals (SIZE(1..MAX))})
|
||||||
|
type Attribute PartialAttribute
|
||||||
|
|
||||||
|
//
|
||||||
|
// MatchingRuleId ::= LDAPString
|
||||||
|
type MatchingRuleId LDAPString
|
||||||
|
|
||||||
|
//
|
||||||
|
// LDAPResult ::= SEQUENCE {
|
||||||
|
// resultCode ENUMERATED {
|
||||||
|
// success (0),
|
||||||
|
// operationsError (1),
|
||||||
|
// protocolError (2),
|
||||||
|
// timeLimitExceeded (3),
|
||||||
|
// sizeLimitExceeded (4),
|
||||||
|
// compareFalse (5),
|
||||||
|
// compareTrue (6),
|
||||||
|
// authMethodNotSupported (7),
|
||||||
|
// strongerAuthRequired (8),
|
||||||
|
// -- 9 reserved --
|
||||||
|
// referral (10),
|
||||||
|
// adminLimitExceeded (11),
|
||||||
|
// unavailableCriticalExtension (12),
|
||||||
|
// confidentialityRequired (13),
|
||||||
|
// saslBindInProgress (14),
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//Sermersheim Standards Track [Page 55]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//RFC 4511 LDAPv3 June 2006
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// noSuchAttribute (16),
|
||||||
|
// undefinedAttributeType (17),
|
||||||
|
// inappropriateMatching (18),
|
||||||
|
// constraintViolation (19),
|
||||||
|
// attributeOrValueExists (20),
|
||||||
|
// invalidAttributeSyntax (21),
|
||||||
|
// -- 22-31 unused --
|
||||||
|
// noSuchObject (32),
|
||||||
|
// aliasProblem (33),
|
||||||
|
// invalidDNSyntax (34),
|
||||||
|
// -- 35 reserved for undefined isLeaf --
|
||||||
|
// aliasDereferencingProblem (36),
|
||||||
|
// -- 37-47 unused --
|
||||||
|
// inappropriateAuthentication (48),
|
||||||
|
// invalidCredentials (49),
|
||||||
|
// insufficientAccessRights (50),
|
||||||
|
// busy (51),
|
||||||
|
// unavailable (52),
|
||||||
|
// unwillingToPerform (53),
|
||||||
|
// loopDetect (54),
|
||||||
|
// -- 55-63 unused --
|
||||||
|
// namingViolation (64),
|
||||||
|
// objectClassViolation (65),
|
||||||
|
// notAllowedOnNonLeaf (66),
|
||||||
|
// notAllowedOnRDN (67),
|
||||||
|
// entryAlreadyExists (68),
|
||||||
|
// objectClassModsProhibited (69),
|
||||||
|
// -- 70 reserved for CLDAP --
|
||||||
|
// affectsMultipleDSAs (71),
|
||||||
|
// -- 72-79 unused --
|
||||||
|
// other (80),
|
||||||
|
// ... },
|
||||||
|
// matchedDN LDAPDN,
|
||||||
|
// diagnosticMessage LDAPString,
|
||||||
|
// referral [3] Referral OPTIONAL }
|
||||||
|
//
|
||||||
|
type LDAPResult struct {
|
||||||
|
resultCode ENUMERATED
|
||||||
|
matchedDN LDAPDN
|
||||||
|
diagnosticMessage LDAPString
|
||||||
|
referral *Referral
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagLDAPResultReferral = 3
|
||||||
|
|
||||||
|
const ResultCodeSuccess = 0
|
||||||
|
const ResultCodeOperationsError = 1
|
||||||
|
const ResultCodeProtocolError = 2
|
||||||
|
const ResultCodeTimeLimitExceeded = 3
|
||||||
|
const ResultCodeSizeLimitExceeded = 4
|
||||||
|
const ResultCodeCompareFalse = 5
|
||||||
|
const ResultCodeCompareTrue = 6
|
||||||
|
const ResultCodeAuthMethodNotSupported = 7
|
||||||
|
const ResultCodeStrongerAuthRequired = 8
|
||||||
|
const ResultCodeReferral = 10
|
||||||
|
const ResultCodeAdminLimitExceeded = 11
|
||||||
|
const ResultCodeUnavailableCriticalExtension = 12
|
||||||
|
const ResultCodeConfidentialityRequired = 13
|
||||||
|
const ResultCodeSaslBindInProgress = 14
|
||||||
|
const ResultCodeNoSuchAttribute = 16
|
||||||
|
const ResultCodeUndefinedAttributeType = 17
|
||||||
|
const ResultCodeInappropriateMatching = 18
|
||||||
|
const ResultCodeConstraintViolation = 19
|
||||||
|
const ResultCodeAttributeOrValueExists = 20
|
||||||
|
const ResultCodeInvalidAttributeSyntax = 21
|
||||||
|
const ResultCodeNoSuchObject = 32
|
||||||
|
const ResultCodeAliasProblem = 33
|
||||||
|
const ResultCodeInvalidDNSyntax = 34
|
||||||
|
const ResultCodeAliasDereferencingProblem = 36
|
||||||
|
const ResultCodeInappropriateAuthentication = 48
|
||||||
|
const ResultCodeInvalidCredentials = 49
|
||||||
|
const ResultCodeInsufficientAccessRights = 50
|
||||||
|
const ResultCodeBusy = 51
|
||||||
|
const ResultCodeUnavailable = 52
|
||||||
|
const ResultCodeUnwillingToPerform = 53
|
||||||
|
const ResultCodeLoopDetect = 54
|
||||||
|
const ResultCodeNamingViolation = 64
|
||||||
|
const ResultCodeObjectClassViolation = 65
|
||||||
|
const ResultCodeNotAllowedOnNonLeaf = 66
|
||||||
|
const ResultCodeNotAllowedOnRDN = 67
|
||||||
|
const ResultCodeEntryAlreadyExists = 68
|
||||||
|
const ResultCodeObjectClassModsProhibited = 69
|
||||||
|
const ResultCodeAffectsMultipleDSAs = 71
|
||||||
|
const ResultCodeOther = 80
|
||||||
|
|
||||||
|
var EnumeratedLDAPResultCode = map[ENUMERATED]string{
|
||||||
|
ResultCodeSuccess: "success",
|
||||||
|
ResultCodeOperationsError: "operationsError",
|
||||||
|
ResultCodeProtocolError: "protocolError",
|
||||||
|
ResultCodeTimeLimitExceeded: "timeLimitExceeded",
|
||||||
|
ResultCodeSizeLimitExceeded: "sizeLimitExceeded",
|
||||||
|
ResultCodeCompareFalse: "compareFalse",
|
||||||
|
ResultCodeCompareTrue: "compareTrue",
|
||||||
|
ResultCodeAuthMethodNotSupported: "authMethodNotSupported",
|
||||||
|
ResultCodeStrongerAuthRequired: "strongerAuthRequired",
|
||||||
|
// -- 9 reserved --
|
||||||
|
ResultCodeReferral: "referral",
|
||||||
|
ResultCodeAdminLimitExceeded: "adminLimitExceeded",
|
||||||
|
ResultCodeUnavailableCriticalExtension: "unavailableCriticalExtension",
|
||||||
|
ResultCodeConfidentialityRequired: "confidentialityRequired",
|
||||||
|
ResultCodeSaslBindInProgress: "saslBindInProgress",
|
||||||
|
ResultCodeNoSuchAttribute: "noSuchAttribute",
|
||||||
|
ResultCodeUndefinedAttributeType: "undefinedAttributeType",
|
||||||
|
ResultCodeInappropriateMatching: "inappropriateMatching",
|
||||||
|
ResultCodeConstraintViolation: "constraintViolation",
|
||||||
|
ResultCodeAttributeOrValueExists: "attributeOrValueExists",
|
||||||
|
ResultCodeInvalidAttributeSyntax: "invalidAttributeSyntax",
|
||||||
|
// -- 22-31 unused --
|
||||||
|
ResultCodeNoSuchObject: "noSuchObject",
|
||||||
|
ResultCodeAliasProblem: "aliasProblem",
|
||||||
|
ResultCodeInvalidDNSyntax: "invalidDNSyntax",
|
||||||
|
// -- 35 reserved for undefined isLeaf --
|
||||||
|
ResultCodeAliasDereferencingProblem: "aliasDereferencingProblem",
|
||||||
|
// -- 37-47 unused --
|
||||||
|
ResultCodeInappropriateAuthentication: "inappropriateAuthentication",
|
||||||
|
ResultCodeInvalidCredentials: "invalidCredentials",
|
||||||
|
ResultCodeInsufficientAccessRights: "insufficientAccessRights",
|
||||||
|
ResultCodeBusy: "busy",
|
||||||
|
ResultCodeUnavailable: "unavailable",
|
||||||
|
ResultCodeUnwillingToPerform: "unwillingToPerform",
|
||||||
|
ResultCodeLoopDetect: "loopDetect",
|
||||||
|
// -- 55-63 unused --
|
||||||
|
ResultCodeNamingViolation: "namingViolation",
|
||||||
|
ResultCodeObjectClassViolation: "objectClassViolation",
|
||||||
|
ResultCodeNotAllowedOnNonLeaf: "notAllowedOnNonLeaf",
|
||||||
|
ResultCodeNotAllowedOnRDN: "notAllowedOnRDN",
|
||||||
|
ResultCodeEntryAlreadyExists: "entryAlreadyExists",
|
||||||
|
ResultCodeObjectClassModsProhibited: "objectClassModsProhibited",
|
||||||
|
// -- 70 reserved for CLDAP --
|
||||||
|
ResultCodeAffectsMultipleDSAs: "affectsMultipleDSAs",
|
||||||
|
// -- 72-79 unused --
|
||||||
|
ResultCodeOther: "other",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
|
||||||
|
type Referral []URI
|
||||||
|
|
||||||
|
//
|
||||||
|
// URI ::= LDAPString -- limited to characters permitted in
|
||||||
|
// -- URIs
|
||||||
|
type URI LDAPString
|
||||||
|
|
||||||
|
//
|
||||||
|
// Controls ::= SEQUENCE OF control Control
|
||||||
|
type Controls []Control
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control ::= SEQUENCE {
|
||||||
|
// controlType LDAPOID,
|
||||||
|
// criticality BOOLEAN DEFAULT FALSE,
|
||||||
|
// controlValue OCTET STRING OPTIONAL }
|
||||||
|
type Control struct {
|
||||||
|
controlType LDAPOID
|
||||||
|
criticality BOOLEAN
|
||||||
|
controlValue *OCTETSTRING
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//Sermersheim Standards Track [Page 56]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//RFC 4511 LDAPv3 June 2006
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// BindRequest ::= [APPLICATION 0] SEQUENCE {
|
||||||
|
// version INTEGER (1 .. 127),
|
||||||
|
// name LDAPDN,
|
||||||
|
// authentication AuthenticationChoice }
|
||||||
|
const TagBindRequest = 0
|
||||||
|
const BindRequestVersionMin = 1
|
||||||
|
const BindRequestVersionMax = 127
|
||||||
|
|
||||||
|
type BindRequest struct {
|
||||||
|
version INTEGER
|
||||||
|
name LDAPDN
|
||||||
|
authentication AuthenticationChoice
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// AuthenticationChoice ::= CHOICE {
|
||||||
|
// simple [0] OCTET STRING,
|
||||||
|
// -- 1 and 2 reserved
|
||||||
|
// sasl [3] SaslCredentials,
|
||||||
|
// ... }
|
||||||
|
const TagAuthenticationChoiceSimple = 0
|
||||||
|
const TagAuthenticationChoiceSaslCredentials = 3
|
||||||
|
|
||||||
|
type AuthenticationChoice interface {
|
||||||
|
sizeTagged(int) int
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SaslCredentials ::= SEQUENCE {
|
||||||
|
// mechanism LDAPString,
|
||||||
|
// credentials OCTET STRING OPTIONAL }
|
||||||
|
type SaslCredentials struct {
|
||||||
|
mechanism LDAPString
|
||||||
|
credentials *OCTETSTRING
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// BindResponse ::= [APPLICATION 1] SEQUENCE {
|
||||||
|
// COMPONENTS OF LDAPResult,
|
||||||
|
// serverSaslCreds [7] OCTET STRING OPTIONAL }
|
||||||
|
const TagBindResponse = 1
|
||||||
|
const TagBindResponseServerSaslCreds = 7
|
||||||
|
|
||||||
|
type BindResponse struct {
|
||||||
|
LDAPResult
|
||||||
|
serverSaslCreds *OCTETSTRING
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// UnbindRequest ::= [APPLICATION 2] NULL
|
||||||
|
const TagUnbindRequest = 2
|
||||||
|
|
||||||
|
type UnbindRequest struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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 }
|
||||||
|
const TagSearchRequest = 3
|
||||||
|
|
||||||
|
type SearchRequest struct {
|
||||||
|
baseObject LDAPDN
|
||||||
|
scope ENUMERATED
|
||||||
|
derefAliases ENUMERATED
|
||||||
|
sizeLimit INTEGER
|
||||||
|
timeLimit INTEGER
|
||||||
|
typesOnly BOOLEAN
|
||||||
|
filter Filter
|
||||||
|
attributes AttributeSelection
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchRequestScopeBaseObject = 0
|
||||||
|
const SearchRequestSingleLevel = 1
|
||||||
|
const SearchRequestHomeSubtree = 2
|
||||||
|
|
||||||
|
var EnumeratedSearchRequestScope = map[ENUMERATED]string{
|
||||||
|
SearchRequestScopeBaseObject: "baseObject",
|
||||||
|
SearchRequestSingleLevel: "singleLevel",
|
||||||
|
SearchRequestHomeSubtree: "homeSubtree",
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchRequetDerefAliasesNeverDerefAliases = 0
|
||||||
|
const SearchRequetDerefAliasesDerefInSearching = 1
|
||||||
|
const SearchRequetDerefAliasesDerefFindingBaseObj = 2
|
||||||
|
const SearchRequetDerefAliasesDerefAlways = 3
|
||||||
|
|
||||||
|
var EnumeratedSearchRequestDerefAliases = map[ENUMERATED]string{
|
||||||
|
SearchRequetDerefAliasesNeverDerefAliases: "neverDerefAliases",
|
||||||
|
SearchRequetDerefAliasesDerefInSearching: "derefInSearching",
|
||||||
|
SearchRequetDerefAliasesDerefFindingBaseObj: "derefFindingBaseObj",
|
||||||
|
SearchRequetDerefAliasesDerefAlways: "derefAlways",
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeSelection ::= SEQUENCE OF selector LDAPString
|
||||||
|
// -- The LDAPString is constrained to
|
||||||
|
// -- <attributeSelector> in Section 4.5.1.8
|
||||||
|
type AttributeSelection []LDAPString
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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,
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//Sermersheim Standards Track [Page 57]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//RFC 4511 LDAPv3 June 2006
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// substrings [4] SubstringFilter,
|
||||||
|
// greaterOrEqual [5] AttributeValueAssertion,
|
||||||
|
// lessOrEqual [6] AttributeValueAssertion,
|
||||||
|
// present [7] AttributeDescription,
|
||||||
|
// approxMatch [8] AttributeValueAssertion,
|
||||||
|
// extensibleMatch [9] MatchingRuleAssertion,
|
||||||
|
// ... }
|
||||||
|
const TagFilterAnd = 0
|
||||||
|
const TagFilterOr = 1
|
||||||
|
const TagFilterNot = 2
|
||||||
|
const TagFilterEqualityMatch = 3
|
||||||
|
const TagFilterSubstrings = 4
|
||||||
|
const TagFilterGreaterOrEqual = 5
|
||||||
|
const TagFilterLessOrEqual = 6
|
||||||
|
const TagFilterPresent = 7
|
||||||
|
const TagFilterApproxMatch = 8
|
||||||
|
const TagFilterExtensibleMatch = 9
|
||||||
|
|
||||||
|
type Filter interface {
|
||||||
|
size() int
|
||||||
|
write(*Bytes) int
|
||||||
|
getFilterTag() int
|
||||||
|
}
|
||||||
|
type FilterAnd []Filter
|
||||||
|
type FilterOr []Filter
|
||||||
|
type FilterNot struct {
|
||||||
|
Filter
|
||||||
|
}
|
||||||
|
type FilterEqualityMatch AttributeValueAssertion
|
||||||
|
type FilterSubstrings SubstringFilter
|
||||||
|
type FilterGreaterOrEqual AttributeValueAssertion
|
||||||
|
type FilterLessOrEqual AttributeValueAssertion
|
||||||
|
type FilterPresent AttributeDescription
|
||||||
|
type FilterApproxMatch AttributeValueAssertion
|
||||||
|
type FilterExtensibleMatch 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
|
||||||
|
// }
|
||||||
|
type SubstringFilter struct {
|
||||||
|
type_ AttributeDescription
|
||||||
|
substrings []Substring
|
||||||
|
}
|
||||||
|
|
||||||
|
type Substring interface{}
|
||||||
|
|
||||||
|
const TagSubstringInitial = 0
|
||||||
|
const TagSubstringAny = 1
|
||||||
|
const TagSubstringFinal = 2
|
||||||
|
|
||||||
|
type SubstringInitial AssertionValue
|
||||||
|
type SubstringAny AssertionValue
|
||||||
|
type SubstringFinal AssertionValue
|
||||||
|
|
||||||
|
//
|
||||||
|
// MatchingRuleAssertion ::= SEQUENCE {
|
||||||
|
// matchingRule [1] MatchingRuleId OPTIONAL,
|
||||||
|
// type [2] AttributeDescription OPTIONAL,
|
||||||
|
// matchValue [3] AssertionValue,
|
||||||
|
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
||||||
|
type MatchingRuleAssertion struct {
|
||||||
|
matchingRule *MatchingRuleId
|
||||||
|
type_ *AttributeDescription
|
||||||
|
matchValue AssertionValue
|
||||||
|
dnAttributes BOOLEAN
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagMatchingRuleAssertionMatchingRule = 1
|
||||||
|
const TagMatchingRuleAssertionType = 2
|
||||||
|
const TagMatchingRuleAssertionMatchValue = 3
|
||||||
|
const TagMatchingRuleAssertionDnAttributes = 4
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
|
||||||
|
// objectName LDAPDN,
|
||||||
|
// attributes PartialAttributeList }
|
||||||
|
const TagSearchResultEntry = 4
|
||||||
|
|
||||||
|
type SearchResultEntry struct {
|
||||||
|
objectName LDAPDN
|
||||||
|
attributes PartialAttributeList
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PartialAttributeList ::= SEQUENCE OF
|
||||||
|
// partialAttribute PartialAttribute
|
||||||
|
type PartialAttributeList []PartialAttribute
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultReference ::= [APPLICATION 19] SEQUENCE
|
||||||
|
// SIZE (1..MAX) OF uri URI
|
||||||
|
const TagSearchResultReference = 19
|
||||||
|
|
||||||
|
type SearchResultReference []URI
|
||||||
|
|
||||||
|
//
|
||||||
|
// SearchResultDone ::= [APPLICATION 5] LDAPResult
|
||||||
|
const TagSearchResultDone = 5
|
||||||
|
|
||||||
|
type SearchResultDone LDAPResult
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||||
|
// object LDAPDN,
|
||||||
|
// changes SEQUENCE OF change SEQUENCE {
|
||||||
|
// operation ENUMERATED {
|
||||||
|
// add (0),
|
||||||
|
// delete (1),
|
||||||
|
// replace (2),
|
||||||
|
// ... },
|
||||||
|
// modification PartialAttribute } }
|
||||||
|
const TagModifyRequest = 6
|
||||||
|
|
||||||
|
type ModifyRequest struct {
|
||||||
|
object LDAPDN
|
||||||
|
changes []ModifyRequestChange
|
||||||
|
}
|
||||||
|
type ModifyRequestChange struct {
|
||||||
|
operation ENUMERATED
|
||||||
|
modification PartialAttribute
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModifyRequestChangeOperationAdd = 0
|
||||||
|
const ModifyRequestChangeOperationDelete = 1
|
||||||
|
const ModifyRequestChangeOperationReplace = 2
|
||||||
|
|
||||||
|
var EnumeratedModifyRequestChangeOperation = map[ENUMERATED]string{
|
||||||
|
ModifyRequestChangeOperationAdd: "add",
|
||||||
|
ModifyRequestChangeOperationDelete: "delete",
|
||||||
|
ModifyRequestChangeOperationReplace: "replace",
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyResponse ::= [APPLICATION 7] LDAPResult
|
||||||
|
const TagModifyResponse = 7
|
||||||
|
|
||||||
|
type ModifyResponse LDAPResult
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//Sermersheim Standards Track [Page 58]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//RFC 4511 LDAPv3 June 2006
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// AddRequest ::= [APPLICATION 8] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// attributes AttributeList }
|
||||||
|
const TagAddRequest = 8
|
||||||
|
|
||||||
|
type AddRequest struct {
|
||||||
|
entry LDAPDN
|
||||||
|
attributes AttributeList
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// AttributeList ::= SEQUENCE OF attribute Attribute
|
||||||
|
type AttributeList []Attribute
|
||||||
|
|
||||||
|
//
|
||||||
|
// AddResponse ::= [APPLICATION 9] LDAPResult
|
||||||
|
const TagAddResponse = 9
|
||||||
|
|
||||||
|
type AddResponse LDAPResult
|
||||||
|
|
||||||
|
//
|
||||||
|
// DelRequest ::= [APPLICATION 10] LDAPDN
|
||||||
|
const TagDelRequest = 10
|
||||||
|
|
||||||
|
type DelRequest LDAPDN
|
||||||
|
|
||||||
|
//
|
||||||
|
// DelResponse ::= [APPLICATION 11] LDAPResult
|
||||||
|
const TagDelResponse = 11
|
||||||
|
|
||||||
|
type DelResponse LDAPResult
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// newrdn RelativeLDAPDN,
|
||||||
|
// deleteoldrdn BOOLEAN,
|
||||||
|
// newSuperior [0] LDAPDN OPTIONAL }
|
||||||
|
const TagModifyDNRequest = 12
|
||||||
|
|
||||||
|
type ModifyDNRequest struct {
|
||||||
|
entry LDAPDN
|
||||||
|
newrdn RelativeLDAPDN
|
||||||
|
deleteoldrdn BOOLEAN
|
||||||
|
newSuperior *LDAPDN
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagModifyDNRequestNewSuperior = 0
|
||||||
|
|
||||||
|
//
|
||||||
|
// ModifyDNResponse ::= [APPLICATION 13] LDAPResult
|
||||||
|
const TagModifyDNResponse = 13
|
||||||
|
|
||||||
|
type ModifyDNResponse LDAPResult
|
||||||
|
|
||||||
|
//
|
||||||
|
// CompareRequest ::= [APPLICATION 14] SEQUENCE {
|
||||||
|
// entry LDAPDN,
|
||||||
|
// ava AttributeValueAssertion }
|
||||||
|
const TagCompareRequest = 14
|
||||||
|
|
||||||
|
type CompareRequest struct {
|
||||||
|
entry LDAPDN
|
||||||
|
ava AttributeValueAssertion
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareResponse ::= [APPLICATION 15] LDAPResult
|
||||||
|
const TagCompareResponse = 15
|
||||||
|
|
||||||
|
type CompareResponse LDAPResult
|
||||||
|
|
||||||
|
//
|
||||||
|
// AbandonRequest ::= [APPLICATION 16] MessageID
|
||||||
|
const TagAbandonRequest = 16
|
||||||
|
|
||||||
|
type AbandonRequest MessageID
|
||||||
|
|
||||||
|
//
|
||||||
|
// ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
|
||||||
|
// requestName [0] LDAPOID,
|
||||||
|
// requestValue [1] OCTET STRING OPTIONAL }
|
||||||
|
const TagExtendedRequest = 23
|
||||||
|
|
||||||
|
type ExtendedRequest struct {
|
||||||
|
requestName LDAPOID
|
||||||
|
requestValue *OCTETSTRING
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagExtendedRequestName = 0
|
||||||
|
const TagExtendedRequestValue = 1
|
||||||
|
|
||||||
|
//
|
||||||
|
// ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
|
||||||
|
// COMPONENTS OF LDAPResult,
|
||||||
|
// responseName [10] LDAPOID OPTIONAL,
|
||||||
|
// responseValue [11] OCTET STRING OPTIONAL }
|
||||||
|
const TagExtendedResponse = 24
|
||||||
|
|
||||||
|
type ExtendedResponse struct {
|
||||||
|
LDAPResult
|
||||||
|
responseName *LDAPOID
|
||||||
|
responseValue *OCTETSTRING
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagExtendedResponseName = 10
|
||||||
|
const TagExtendedResponseValue = 11
|
||||||
|
|
||||||
|
//
|
||||||
|
// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
|
||||||
|
// responseName [0] LDAPOID OPTIONAL,
|
||||||
|
// responseValue [1] OCTET STRING OPTIONAL }
|
||||||
|
const TagIntermediateResponse = 25
|
||||||
|
|
||||||
|
type IntermediateResponse struct {
|
||||||
|
responseName *LDAPOID
|
||||||
|
responseValue *OCTETSTRING
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagIntermediateResponseName = 0
|
||||||
|
const TagIntermediateResponseValue = 1
|
||||||
|
|
||||||
|
//
|
||||||
|
// END
|
||||||
|
//
|
7
goldap/struct_methods.go
Normal file
7
goldap/struct_methods.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
func NewLDAPMessageWithProtocolOp(po ProtocolOp) *LDAPMessage {
|
||||||
|
m := NewLDAPMessage()
|
||||||
|
m.protocolOp = po
|
||||||
|
return m
|
||||||
|
}
|
38
goldap/unbind_request.go
Normal file
38
goldap/unbind_request.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// UnbindRequest ::= [APPLICATION 2] NULL
|
||||||
|
func readUnbindRequest(bytes *Bytes) (unbindrequest UnbindRequest, err error) {
|
||||||
|
var tagAndLength TagAndLength
|
||||||
|
tagAndLength, err = bytes.ParseTagAndLength()
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readUnbindRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tagAndLength.Expect(classApplication, TagUnbindRequest, isNotCompound)
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readUnbindRequest:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tagAndLength.Length != 0 {
|
||||||
|
err = LdapError{"readUnbindRequest: expecting NULL"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// UnbindRequest ::= [APPLICATION 2] NULL
|
||||||
|
func (u UnbindRequest) write(bytes *Bytes) (size int) {
|
||||||
|
size += bytes.WriteTagAndLength(classApplication, isNotCompound, TagUnbindRequest, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// UnbindRequest ::= [APPLICATION 2] NULL
|
||||||
|
func (u UnbindRequest) size() (size int) {
|
||||||
|
size = sizeTagAndLength(TagUnbindRequest, 0)
|
||||||
|
return
|
||||||
|
}
|
32
goldap/uri.go
Normal file
32
goldap/uri.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//
|
||||||
|
// URI ::= LDAPString -- limited to characters permitted in
|
||||||
|
// -- URIs
|
||||||
|
func readURI(bytes *Bytes) (uri URI, err error) {
|
||||||
|
var ldapstring LDAPString
|
||||||
|
ldapstring, err = readLDAPString(bytes)
|
||||||
|
// @TODO: check permitted chars in URI
|
||||||
|
if err != nil {
|
||||||
|
err = LdapError{fmt.Sprintf("readURI:\n%s", err.Error())}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uri = URI(ldapstring)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// URI ::= LDAPString -- limited to characters permitted in
|
||||||
|
// -- URIs
|
||||||
|
func (u URI) write(bytes *Bytes) int {
|
||||||
|
return LDAPString(u).write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// URI ::= LDAPString -- limited to characters permitted in
|
||||||
|
// -- URIs
|
||||||
|
func (u URI) size() int {
|
||||||
|
return LDAPString(u).size()
|
||||||
|
}
|
6
goldap/write.go
Normal file
6
goldap/write.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
type Writable interface {
|
||||||
|
write(bytes *Bytes) int
|
||||||
|
writeTagged(bytes *Bytes, class int, tag int) int
|
||||||
|
}
|
19
goldap/write_test.go
Normal file
19
goldap/write_test.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWriteLDAPMessage(t *testing.T) {
|
||||||
|
|
||||||
|
var testData = getLDAPMessageTestData()
|
||||||
|
for i, test := range testData {
|
||||||
|
bytes, err := test.out.Write()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d error at offset %d (%s): %s\nEXPECTED BYTES: %#v\nWRITTEN BYTES: %#v\n", i, test.bytes.offset, test.bytes.DumpCurrentBytes(), err, test.bytes.getBytes(), bytes.getBytes())
|
||||||
|
} else if !reflect.DeepEqual(bytes.getBytes(), test.bytes.getBytes()) {
|
||||||
|
t.Errorf("#%d:\nGOT:\n%#+v\nEXPECTED:\n%#+v", i, bytes.getBytes(), test.bytes.getBytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
153
gomod2nix.toml
Normal file
153
gomod2nix.toml
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
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="
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ldap "github.com/lor00x/goldap/message"
|
ldap "bottin/goldap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserState interface{}
|
type UserState interface{}
|
||||||
|
@ -206,7 +206,10 @@ func (c *client) close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) writeMessage(m *ldap.LDAPMessage) {
|
func (c *client) writeMessage(m *ldap.LDAPMessage) {
|
||||||
data, _ := m.Write()
|
data, err := m.Write()
|
||||||
|
if err != nil {
|
||||||
|
Logger.Errorf("bottin: unable to marshal response message: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
//Logger.Printf(">>> %d - %s - hex=%x", c.Numero, m.ProtocolOpName(), data.Bytes())
|
//Logger.Printf(">>> %d - %s - hex=%x", c.Numero, m.ProtocolOpName(), data.Bytes())
|
||||||
Logger.Tracef(">>> [%d] %#v", c.Numero, m)
|
Logger.Tracef(">>> [%d] %#v", c.Numero, m)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package ldapserver
|
package ldapserver
|
||||||
|
|
||||||
import ldap "github.com/lor00x/goldap/message"
|
import ldap "bottin/goldap"
|
||||||
|
|
||||||
// LDAP Application Codes
|
// LDAP Application Codes
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ldapserver
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
ldap "github.com/lor00x/goldap/message"
|
ldap "bottin/goldap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
ldap "github.com/lor00x/goldap/message"
|
ldap "bottin/goldap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type messagePacket struct {
|
type messagePacket struct {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package ldapserver
|
package ldapserver
|
||||||
|
|
||||||
import ldap "github.com/lor00x/goldap/message"
|
import ldap "bottin/goldap"
|
||||||
|
|
||||||
func NewBindResponse(resultCode int) ldap.BindResponse {
|
func NewBindResponse(resultCode int) ldap.BindResponse {
|
||||||
r := ldap.BindResponse{}
|
r := ldap.BindResponse{}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package ldapserver
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
ldap "github.com/lor00x/goldap/message"
|
ldap "bottin/goldap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Constant to LDAP Request protocol Type names
|
// Constant to LDAP Request protocol Type names
|
||||||
|
|
39
main.go
39
main.go
|
@ -12,10 +12,10 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
message "bottin/goldap"
|
||||||
ldap "bottin/ldapserver"
|
ldap "bottin/ldapserver"
|
||||||
|
|
||||||
consul "github.com/hashicorp/consul/api"
|
consul "github.com/hashicorp/consul/api"
|
||||||
message "github.com/lor00x/goldap/message"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -320,7 +320,6 @@ func (server *Server) init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
|
admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
|
||||||
if !environnement_variable_exist {
|
if !environnement_variable_exist {
|
||||||
admin_pass := make([]byte, 8)
|
admin_pass := make([]byte, 8)
|
||||||
|
@ -329,11 +328,15 @@ func (server *Server) init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass)
|
admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass)
|
||||||
} else {
|
} else {
|
||||||
server.logger.Debug("BOTTIN_DEFAULT_ADMIN_PW environment variable is set, using it for admin's password")
|
server.logger.Debug("BOTTIN_DEFAULT_ADMIN_PW environment variable is set, using it for admin's password")
|
||||||
}
|
}
|
||||||
|
|
||||||
admin_pass_hash := SSHAEncode([]byte(admin_pass_str))
|
admin_pass_hash, err := SSHAEncode(admin_pass_str)
|
||||||
|
if err != nil {
|
||||||
|
server.logger.Error("can't create admin password")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
admin_dn := "cn=admin," + server.config.Suffix
|
admin_dn := "cn=admin," + server.config.Suffix
|
||||||
admin_attributes := Entry{
|
admin_attributes := Entry{
|
||||||
|
@ -434,8 +437,8 @@ func (server *Server) handleBindInternal(state *State, r *message.BindRequest) (
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, hash := range passwd {
|
for _, hash := range passwd {
|
||||||
valid := SSHAMatches(hash, []byte(r.AuthenticationSimple()))
|
valid, err := SSHAMatches(hash, string(r.AuthenticationSimple()))
|
||||||
if valid {
|
if valid && err == nil {
|
||||||
groups, err := server.getAttribute(string(r.Name()), ATTR_MEMBEROF)
|
groups, err := server.getAttribute(string(r.Name()), ATTR_MEMBEROF)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ldap.LDAPResultOperationsError, err
|
return ldap.LDAPResultOperationsError, err
|
||||||
|
@ -444,8 +447,32 @@ func (server *Server) handleBindInternal(state *State, r *message.BindRequest) (
|
||||||
user: string(r.Name()),
|
user: string(r.Name()),
|
||||||
groups: groups,
|
groups: groups,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePasswordHash(string(r.AuthenticationSimple()), hash, server, string(r.Name()))
|
||||||
|
|
||||||
return ldap.LDAPResultSuccess, nil
|
return ldap.LDAPResultSuccess, nil
|
||||||
|
} else {
|
||||||
|
return ldap.LDAPResultInvalidCredentials, fmt.Errorf("can't authenticate: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ldap.LDAPResultInvalidCredentials, fmt.Errorf("No password match")
|
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},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
7
read.go
7
read.go
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
ldap "bottin/ldapserver"
|
ldap "bottin/ldapserver"
|
||||||
|
|
||||||
message "github.com/lor00x/goldap/message"
|
message "bottin/goldap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generic read utility functions ----------
|
// Generic read utility functions ----------
|
||||||
|
@ -213,10 +213,11 @@ func (server *Server) handleSearchInternal(state *State, w ldap.ResponseWriter,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Send result
|
// Send result
|
||||||
|
resultVals := []message.AttributeValue{}
|
||||||
for _, v := range val {
|
for _, v := range val {
|
||||||
e.AddAttribute(message.AttributeDescription(attr),
|
resultVals = append(resultVals, message.AttributeValue(v))
|
||||||
message.AttributeValue(v))
|
|
||||||
}
|
}
|
||||||
|
e.AddAttribute(message.AttributeDescription(attr), resultVals...)
|
||||||
}
|
}
|
||||||
w.Write(e)
|
w.Write(e)
|
||||||
}
|
}
|
||||||
|
|
70
ssha.go
70
ssha.go
|
@ -1,25 +1,79 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
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
|
// Encode encodes the []byte of raw password
|
||||||
func SSHAEncode(rawPassPhrase []byte) string {
|
func LegacySSHAEncode(rawPassPhrase []byte) string {
|
||||||
hash := makeSSHAHash(rawPassPhrase, makeSalt())
|
hash := legacyMakeSSHAHash(rawPassPhrase, legacyMakeSalt())
|
||||||
b64 := base64.StdEncoding.EncodeToString(hash)
|
b64 := base64.StdEncoding.EncodeToString(hash)
|
||||||
return fmt.Sprintf("{ssha}%s", b64)
|
return fmt.Sprintf("{ssha}%s", b64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matches matches the encoded password and the raw password
|
// Matches matches the encoded password and the raw password
|
||||||
func SSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
|
func LegacySSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
|
||||||
if !strings.EqualFold(encodedPassPhrase[:6], "{ssha}") {
|
if !strings.EqualFold(encodedPassPhrase[:6], "{ssha}") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -30,7 +84,7 @@ func SSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
|
||||||
}
|
}
|
||||||
salt := bhash[20:]
|
salt := bhash[20:]
|
||||||
|
|
||||||
newssha := makeSSHAHash(rawPassPhrase, salt)
|
newssha := legacyMakeSSHAHash(rawPassPhrase, salt)
|
||||||
|
|
||||||
if bytes.Compare(newssha, bhash) != 0 {
|
if bytes.Compare(newssha, bhash) != 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -39,7 +93,7 @@ func SSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeSalt make a 32 byte array containing random bytes.
|
// makeSalt make a 32 byte array containing random bytes.
|
||||||
func makeSalt() []byte {
|
func legacyMakeSalt() []byte {
|
||||||
sbytes := make([]byte, 32)
|
sbytes := make([]byte, 32)
|
||||||
_, err := rand.Read(sbytes)
|
_, err := rand.Read(sbytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -49,7 +103,7 @@ func makeSalt() []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.
|
// 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 makeSSHAHash(passphrase, salt []byte) []byte {
|
func legacyMakeSSHAHash(passphrase, salt []byte) []byte {
|
||||||
sha := sha1.New()
|
sha := sha1.New()
|
||||||
sha.Write(passphrase)
|
sha.Write(passphrase)
|
||||||
sha.Write(salt)
|
sha.Write(salt)
|
||||||
|
|
|
@ -147,14 +147,12 @@ func TestDeleteGroupsAfterAddedUsers(t *testing.T) {
|
||||||
|
|
||||||
//Example of paralellism Test
|
//Example of paralellism Test
|
||||||
func TestPrincipal(t *testing.T) {
|
func TestPrincipal(t *testing.T) {
|
||||||
|
t.Run("A=Add and delete", TestAddThenDelete)
|
||||||
t.Run("A=1", TestAddThenDelete)
|
t.Run("A=Modify", TestModifyRequest)
|
||||||
t.Run("A=2", TestModifyRequest)
|
|
||||||
if !testing.Short() {
|
if !testing.Short() {
|
||||||
t.Run("B=1", TestConfirmAddAttributes)
|
t.Run("B=Add attributes", TestConfirmAddAttributes)
|
||||||
t.Run("B=2", TestModifyRequestAndCheck)
|
t.Run("B=Modify and check", TestModifyRequestAndCheck)
|
||||||
t.Run("C=1", TestAddUserInGroup)
|
t.Run("C=Add user in group", TestAddUserInGroup)
|
||||||
t.Run("C=2", TestDeleteGroupsAfterAddedUsers)
|
t.Run("C=Delete group", TestDeleteGroupsAfterAddedUsers)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,9 @@ func PrintError(LDAPError error) {
|
||||||
|
|
||||||
//Generate an unique name, which store in all_names
|
//Generate an unique name, which store in all_names
|
||||||
func (inst *instance) GenerateName() (name string) {
|
func (inst *instance) GenerateName() (name string) {
|
||||||
alphabet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
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
|
length := R.Intn(maxlength_generateName) + minlength_generateName
|
||||||
|
|
||||||
//Check if this name not exist already
|
//Check if this name not exist already
|
||||||
|
@ -41,7 +43,7 @@ func (inst *instance) GenerateName() (name string) {
|
||||||
for only_one := true; only_one; _, only_one = allNames.cn[name] {
|
for only_one := true; only_one; _, only_one = allNames.cn[name] {
|
||||||
//Create the name
|
//Create the name
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
name += string(alphabet[R.Intn(len(alphabet))])
|
name += alphabet[R.Intn(len(alphabet))]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,18 +54,6 @@ func (inst *instance) GenerateName() (name string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Handler to around the bug with MessageId
|
|
||||||
func (inst *instance) Reconnect() (err error) {
|
|
||||||
inst.logging.Close()
|
|
||||||
inst.logging, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = inst.logging.Bind(bindusername, bindpassword)
|
|
||||||
//logging.Debug("Reconnect succesful")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Transform attributes in map format to the struct attributes
|
//Transform attributes in map format to the struct attributes
|
||||||
func MapAttToStruct(att map[string][]string) []attributes {
|
func MapAttToStruct(att map[string][]string) []attributes {
|
||||||
resultat := []attributes{}
|
resultat := []attributes{}
|
||||||
|
@ -86,7 +76,7 @@ func Connect() (*ldap.Conn, error) {
|
||||||
if key, ok := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW"); ok {
|
if key, ok := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW"); ok {
|
||||||
bindpassword = key
|
bindpassword = key
|
||||||
}
|
}
|
||||||
|
//l.Debug.Enable(true)
|
||||||
err = l.Bind(bindusername, bindpassword)
|
err = l.Bind(bindusername, bindpassword)
|
||||||
logging.Debug("Connection succesful")
|
logging.Debug("Connection succesful")
|
||||||
return l, err
|
return l, err
|
||||||
|
|
|
@ -13,8 +13,6 @@ func (inst *instance) Add_Request(dn string, attributes map[string][]string) err
|
||||||
|
|
||||||
//Send the request
|
//Send the request
|
||||||
err := inst.logging.Add(req)
|
err := inst.logging.Add(req)
|
||||||
//@FIXME: Remove when you try to correct the bug MessageID
|
|
||||||
inst.Reconnect()
|
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,8 +34,6 @@ func (inst *instance) Modify_Request(dn string, add_attributes, delete_attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
err := inst.logging.Modify(modifyReq)
|
err := inst.logging.Modify(modifyReq)
|
||||||
//@FIXME: Remove when you try to correct the bug MessageID
|
|
||||||
inst.Reconnect()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +41,6 @@ func (inst *instance) Delete_Request(dn string) error {
|
||||||
del := ldap.NewDelRequest(dn, nil)
|
del := ldap.NewDelRequest(dn, nil)
|
||||||
|
|
||||||
err := inst.logging.Del(del)
|
err := inst.logging.Del(del)
|
||||||
//@FIXME: Remove when you try to correct the bug MessageID
|
|
||||||
inst.Reconnect()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +55,5 @@ func (inst *instance) Search_Request(dn, filter string, name_attributes []string
|
||||||
|
|
||||||
res, err := inst.logging.Search(searchReq)
|
res, err := inst.logging.Search(searchReq)
|
||||||
logging.Debugf("Search Request made with: dn: %s, filter: %s, attributes: %s. \n", dn, filter, name_attributes)
|
logging.Debugf("Search Request made with: dn: %s, filter: %s, attributes: %s. \n", dn, filter, name_attributes)
|
||||||
//@FIXME: Remove when you try to correct the bug MessageID
|
|
||||||
inst.Reconnect()
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,11 @@ echo $BOTTIN_DEFAULT_ADMIN_PW
|
||||||
consul agent -dev > /dev/null 2>&1 &
|
consul agent -dev > /dev/null 2>&1 &
|
||||||
sleep 2
|
sleep 2
|
||||||
cp test/config.json.test config.json
|
cp test/config.json.test config.json
|
||||||
./bottin > /dev/null 2>&1 &
|
./bottin > /tmp/bottin.log 2>&1 &
|
||||||
sleep 1
|
sleep 1
|
||||||
./test/test -test.v -test.failfast -test.short -test.run TestPrincipal
|
./test/test -test.v -test.failfast -test.short -test.run TestPrincipal
|
||||||
./test/test -test.v -test.failfast -test.run TestPrincipal/B=
|
./test/test -test.v -test.failfast -test.run TestPrincipal/B=
|
||||||
|
|
||||||
|
jobs
|
||||||
|
kill %2
|
||||||
|
kill %1
|
||||||
|
|
23
write.go
23
write.go
|
@ -7,8 +7,9 @@ import (
|
||||||
|
|
||||||
ldap "bottin/ldapserver"
|
ldap "bottin/ldapserver"
|
||||||
|
|
||||||
|
message "bottin/goldap"
|
||||||
|
|
||||||
consul "github.com/hashicorp/consul/api"
|
consul "github.com/hashicorp/consul/api"
|
||||||
message "github.com/lor00x/goldap/message"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generic item modification function --------
|
// Generic item modification function --------
|
||||||
|
@ -38,7 +39,7 @@ func (server *Server) putAttributes(dn string, attrs Entry) error {
|
||||||
|
|
||||||
// Retreieve previously existing attributes, which we will use to delete
|
// Retreieve previously existing attributes, which we will use to delete
|
||||||
// entries with the wrong case
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -65,6 +66,24 @@ 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
|
// If we have zero values, delete associated k/v pair
|
||||||
// Otherwise, write new values
|
// Otherwise, write new values
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
|
|
Loading…
Reference in a new issue