Compare commits

..

1 commit

Author SHA1 Message Date
Simon Beck
d28e669676 Fix wrong handling of multi value attributes
All checks were successful
continuous-integration/drone/pr Build is passing
While ldapsearch doesn't seem to mind, apps like keycloak seem to have
issues with adding multiple attributes with different values. While
the resulting ldif in ldapsearch is indistinguishable there seems to
be a slight different on the protocol level.

If adding multiple attributes with the same name and different values,
keycloak will only see the last entry. But adding a single attribute
a slice of values is seems to handle it correctly.
2022-02-10 20:39:21 +01:00
13 changed files with 42 additions and 462 deletions

View file

@ -1,3 +1,7 @@
---
kind: pipeline
name: bottin
steps:
- name: build
image: golang:stretch
@ -8,8 +12,14 @@ steps:
- go test -i -c -o test
- name: test_bottin
image: consul:1.15.4
image: consul:latest
environment:
BOTTIN_DEFAULT_ADMIN_PW: priZ4Cg0x5NkSyiIN/MpvWw4ZEy8f8s1
commands:
- ash test/runner.sh
---
kind: signature
hmac: ff246a04c3df8a2f39c8b446dea920622d61950e6caaac886931bdb05d0706ed
...

2
.gitignore vendored
View file

@ -2,5 +2,3 @@ bottin
bottin.static
config.json
test/test
result
ldap.json

5
Dockerfile Normal file
View file

@ -0,0 +1,5 @@
FROM scratch
ADD bottin.static /bottin
ENTRYPOINT ["/bottin"]

View file

@ -1,6 +1,6 @@
# Bottin
[![status-badge](https://woodpecker.deuxfleurs.fr/api/badges/38/status.svg)](https://woodpecker.deuxfleurs.fr/repos/38)
[![Build Status](https://drone.deuxfleurs.fr/api/badges/Deuxfleurs/bottin/status.svg?ref=refs/heads/main)](https://drone.deuxfleurs.fr/Deuxfleurs/bottin)
<img src="https://git.deuxfleurs.fr/Deuxfleurs/bottin/raw/branch/main/bottin.png" style="height: 150px; display: block; margin-left: auto; margin-right: auto" />
@ -24,7 +24,7 @@ Features:
- Access control through an ACL (hardcoded in the configuration file)
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/dxflrs/bottin) (built in `default.nix`).
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/lxpz/bottin_amd64).
An example for running Bottin on a Nomad cluster can be found in `bottin.hcl.example`.
Bottin takes a single command line argument, `-config <filename>`, which is the
@ -44,17 +44,6 @@ Bottin requires go 1.13 or later.
To build Bottin, clone this repository outside of your `$GOPATH`.
Then, run `make` in the root of the repo.
## Releasing
```bash
nix-build -A bin
nix-build -A docker
```
```bash
docker load < $(nix-build -A docker)
docker push dxflrs/bottin:???
```
## Server initialization

View file

@ -1,55 +0,0 @@
let
pkgsSrc = fetchTarball {
# As of 2022-07-19
url = "https://github.com/NixOS/nixpkgs/archive/d2db10786f27619d5519b12b03fb10dc8ca95e59.tar.gz";
sha256 = "0s9gigs3ylnq5b94rfcmxvrmmr3kzhs497gksajf638d5bv7zcl5";
};
gomod2nix = fetchGit {
url = "https://github.com/tweag/gomod2nix.git";
ref = "master";
rev = "40d32f82fc60d66402eb0972e6e368aeab3faf58";
};
pkgs = import pkgsSrc {
overlays = [
(self: super: {
gomod = super.callPackage "${gomod2nix}/builder/" { };
})
];
};
in rec {
bin = pkgs.gomod.buildGoApplication {
pname = "bottin-bin";
version = "0.1.0";
src = builtins.filterSource
(path: type: (builtins.match ".*/test/.*\\.(go|sum|mod)" path) == null)
./.;
modules = ./gomod2nix.toml;
CGO_ENABLED=0;
meta = with pkgs.lib; {
description = "A cloud-native LDAP server backed by a Consul datastore";
homepage = "https://git.deuxfleurs.fr/Deuxfleurs/bottin";
license = licenses.gpl3Plus;
platforms = platforms.linux;
};
};
pkg = pkgs.stdenv.mkDerivation {
pname = "bottin";
version = "0.1.0";
unpackPhase = "true";
installPhase = ''
mkdir -p $out/
cp ${bin}/bin/bottin $out/bottin
'';
};
docker = pkgs.dockerTools.buildImage {
name = "dxflrs/bottin";
config = {
Entrypoint = [ "${pkg}/bottin" ];
WorkingDir = "/";
};
};
}

View file

@ -1,79 +0,0 @@
{
"nodes": {
"gomod2nix": {
"inputs": {
"nixpkgs": "nixpkgs",
"utils": "utils"
},
"locked": {
"lastModified": 1655245309,
"narHash": "sha256-d/YPoQ/vFn1+GTmSdvbSBSTOai61FONxB4+Lt6w/IVI=",
"owner": "tweag",
"repo": "gomod2nix",
"rev": "40d32f82fc60d66402eb0972e6e368aeab3faf58",
"type": "github"
},
"original": {
"owner": "tweag",
"repo": "gomod2nix",
"rev": "40d32f82fc60d66402eb0972e6e368aeab3faf58",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1653581809,
"narHash": "sha256-Uvka0V5MTGbeOfWte25+tfRL3moECDh1VwokWSZUdoY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "83658b28fe638a170a19b8933aa008b30640fbd1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1669764884,
"narHash": "sha256-1qWR/5+WtqxSedrFbUbM3zPMO7Ec2CGWaxtK4z4DdvY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0244e143dc943bcf661fdaf581f01eb0f5000fcf",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0244e143dc943bcf661fdaf581f01eb0f5000fcf",
"type": "github"
}
},
"root": {
"inputs": {
"gomod2nix": "gomod2nix",
"nixpkgs": "nixpkgs_2"
}
},
"utils": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,39 +0,0 @@
{
description = "A cloud-native LDAP server backed by a Consul datastore";
inputs.nixpkgs.url = "github:nixos/nixpkgs/0244e143dc943bcf661fdaf581f01eb0f5000fcf";
inputs.gomod2nix.url = "github:tweag/gomod2nix/40d32f82fc60d66402eb0972e6e368aeab3faf58";
outputs = { self, nixpkgs, gomod2nix }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [
(self: super: {
gomod = super.callPackage "${gomod2nix}/builder/" { };
})
];
};
bottin = pkgs.gomod.buildGoApplication {
pname = "bottin";
version = "0.1.0";
src = builtins.filterSource
(path: type: (builtins.match ".*/test/.*\\.(go|sum|mod)" path) == null)
./.;
modules = ./gomod2nix.toml;
CGO_ENABLED=0;
meta = with pkgs.lib; {
description = "A cloud-native LDAP server backed by a Consul datastore";
homepage = "https://git.deuxfleurs.fr/Deuxfleurs/bottin";
license = licenses.gpl3Plus;
platforms = platforms.linux;
};
};
in
{
packages.x86_64-linux.bottin = bottin;
packages.x86_64-linux.default = self.packages.x86_64-linux.bottin;
};
}

2
go.mod
View file

@ -3,9 +3,9 @@ module bottin
go 1.13
require (
github.com/go-ldap/ldap/v3 v3.3.0
github.com/google/uuid v1.1.1
github.com/hashicorp/consul/api v1.3.0
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73
github.com/sirupsen/logrus v1.4.2
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect
)

10
go.sum
View file

@ -1,3 +1,5 @@
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@ -7,6 +9,11 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap v2.5.1+incompatible h1:Opaoft5zMW8IU/VRULB0eGMBQ9P5buRvCW6sFTRmMn8=
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
@ -42,8 +49,6 @@ github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG67
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73 h1:ZhC4QngptYaGx53+ph1RjxcH8fkCozBaY+935TNX4i8=
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73/go.mod h1:t0Q9JvoMTfTYdAWIk2MF69iz+Qpdk9D+PgVu6fVmaDI=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@ -83,7 +88,6 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View file

@ -1,153 +0,0 @@
schema = 3
[mod]
[mod."github.com/armon/circbuf"]
version = "v0.0.0-20150827004946-bbbad097214e"
hash = "sha256-klQjllsJZqZ2KPNx1mZT9XP+UAJkuBhmTnZdNlAflEM="
[mod."github.com/armon/go-metrics"]
version = "v0.0.0-20180917152333-f0300d1749da"
hash = "sha256-+zqX1hlJgc+IrXRzBQDMhR8GYQdc0Oj6PiIDfctgh44="
[mod."github.com/armon/go-radix"]
version = "v0.0.0-20180808171621-7fddfc383310"
hash = "sha256-ZHU4pyBqHHRuQJuYr2K+LqeAnLX9peX07cmSYK+GDHk="
[mod."github.com/bgentry/speakeasy"]
version = "v0.1.0"
hash = "sha256-Gt1vj6CFovLnO6wX5u2O4UfecY9V2J9WGw1ez4HMrgk="
[mod."github.com/davecgh/go-spew"]
version = "v1.1.1"
hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI="
[mod."github.com/fatih/color"]
version = "v1.7.0"
hash = "sha256-4In7ef7it7d+6oGUJ3pkD0V+lsL40hVtYdy2KD2ovn0="
[mod."github.com/google/btree"]
version = "v0.0.0-20180813153112-4030bb1f1f0c"
hash = "sha256-5gr0RMnlvrzCke3kwpkf92WvW3x5nnKZesoulyoYRC0="
[mod."github.com/google/uuid"]
version = "v1.1.1"
hash = "sha256-66PXC/RCPUyhS9PhkIPQFR3tbM2zZYDNPGXN7JJj3UE="
[mod."github.com/hashicorp/consul/api"]
version = "v1.3.0"
hash = "sha256-fMORNFAWK2GkRbBl/KzNHrvtCfwz/7P66HzDaE4GnuE="
[mod."github.com/hashicorp/consul/sdk"]
version = "v0.3.0"
hash = "sha256-lF47JPGfmeGjpuQw9VSNs2Mi+G7FQLKrrHteX3iXfpU="
[mod."github.com/hashicorp/errwrap"]
version = "v1.0.0"
hash = "sha256-LGSLrefkABG1kH1i+GUWiD2/ggJxiZEJ+D2YNbhZjmo="
[mod."github.com/hashicorp/go-cleanhttp"]
version = "v0.5.1"
hash = "sha256-c54zcHr9THj3MQk7hrDQcpjOcQi1MvXZ4Kpin6EbfR4="
[mod."github.com/hashicorp/go-immutable-radix"]
version = "v1.0.0"
hash = "sha256-JmNxdGaJG63Ty/sVnPjqvTyA4/k5wkzZ/QvpMK2uduw="
[mod."github.com/hashicorp/go-msgpack"]
version = "v0.5.3"
hash = "sha256-2OUYjD/Jt12TFBrtH0wRqg+lzRljDxSIhk2CqBLUczo="
[mod."github.com/hashicorp/go-multierror"]
version = "v1.0.0"
hash = "sha256-iXzjerl96o7QDiSwQjbak8R/t+YzZeoUqm59TCmy3gI="
[mod."github.com/hashicorp/go-rootcerts"]
version = "v1.0.0"
hash = "sha256-4NZJAT5/vocyto+dv6FmW4kFiYldmNvewowsYK/LiTI="
[mod."github.com/hashicorp/go-sockaddr"]
version = "v1.0.0"
hash = "sha256-orG+SHVsp5lgNRCErmhMLABVFQ3ZWfYIJ/4LTFzlvao="
[mod."github.com/hashicorp/go-syslog"]
version = "v1.0.0"
hash = "sha256-YRuq6oPMwAFVY7mvwpMDvZqGwNnb5CjBYyKI/x5mbCc="
[mod."github.com/hashicorp/go-uuid"]
version = "v1.0.1"
hash = "sha256-s1wIvBu37z4U3qK9sdUR1CtbD39N6RwfX4HgDCpCa0s="
[mod."github.com/hashicorp/go.net"]
version = "v0.0.1"
hash = "sha256-JKal3E+wPO+Hk838opKV4HHKB4O72Xy+77ncXlLkWRk="
[mod."github.com/hashicorp/golang-lru"]
version = "v0.5.0"
hash = "sha256-Lo0UyOZQ89iK0Ui3Hfk5VkWqxEzLw6Lclq4EM8VlYoo="
[mod."github.com/hashicorp/logutils"]
version = "v1.0.0"
hash = "sha256-e8t8Dm8sp/PzKClN1TOmFcrTAWNh4mZHSW7cAjVx3Bw="
[mod."github.com/hashicorp/mdns"]
version = "v1.0.0"
hash = "sha256-ravx4tklQG43OEjPiJn68iJM9ODZ6hgU0idFCEOiJGM="
[mod."github.com/hashicorp/memberlist"]
version = "v0.1.3"
hash = "sha256-IsxqevYulPt+2VGtlq068Jyq1YfIk4Ohh9TgakIGxnc="
[mod."github.com/hashicorp/serf"]
version = "v0.8.2"
hash = "sha256-diRxWOouFLTO75f2E9NlrKgie/qsT+gOOrrbf4tACHw="
[mod."github.com/jsimonetti/pwscheme"]
version = "v0.0.0-20220125093853-4d9895f5db73"
hash = "sha256-YF3RKU/4CWvLPgGzUd7Hk/2+41OUFuRWZgzQuqcsKg0="
[mod."github.com/konsorten/go-windows-terminal-sequences"]
version = "v1.0.1"
hash = "sha256-Nwp+Cza9dIu3ogVGip6wyOjWwwaq+2hU3eYIe4R7kNE="
[mod."github.com/mattn/go-colorable"]
version = "v0.0.9"
hash = "sha256-fVPF8VxbdggLAZnaexMl6Id1WjXKImzVySxKfa+ukts="
[mod."github.com/mattn/go-isatty"]
version = "v0.0.3"
hash = "sha256-9ogEEd8/kl/dImldzdBmTFdepvB0dVXEzN4o8bEqhBs="
[mod."github.com/miekg/dns"]
version = "v1.0.14"
hash = "sha256-OeijUgBaEmDapclTxfvjIqrjh4qZu3+DQpHelGGI4aA="
[mod."github.com/mitchellh/cli"]
version = "v1.0.0"
hash = "sha256-4nG7AhRcjTRCwUCdnaNaFrAKDxEEoiihaCA4lk+uM8U="
[mod."github.com/mitchellh/go-homedir"]
version = "v1.0.0"
hash = "sha256-eGmBNNTuDSMwicjTNgB54IEJuK1tgOLDJ3NHTpQCHzg="
[mod."github.com/mitchellh/go-testing-interface"]
version = "v1.0.0"
hash = "sha256-/Dpv/4i5xuK8hDH+q8YTdF6Jg6NNtfO4Wqig2JCWgrY="
[mod."github.com/mitchellh/gox"]
version = "v0.4.0"
hash = "sha256-GV3LYxzJt8YVbnSac2orlj2QR3MX/YIDrLkSkPhsjuA="
[mod."github.com/mitchellh/iochan"]
version = "v1.0.0"
hash = "sha256-b5Tp7cw/e8mL++IjsebbmKWXtb9Hrzu4Fc6M4tZKFhU="
[mod."github.com/mitchellh/mapstructure"]
version = "v1.1.2"
hash = "sha256-OU9HZYHtl0qaqMFd84w7snkkRuRY6UMSsfCnL5HYdw0="
[mod."github.com/pascaldekloe/goe"]
version = "v0.0.0-20180627143212-57f6aae5913c"
hash = "sha256-2KUjqrEC/BwkTZRxImazcI/C3H7QmXfNrlt8slwdDbc="
[mod."github.com/pkg/errors"]
version = "v0.8.1"
hash = "sha256-oe3iddfoLRwpC3ki5fifHf2ZFprtg99iNak50shiuDw="
[mod."github.com/pmezard/go-difflib"]
version = "v1.0.0"
hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA="
[mod."github.com/posener/complete"]
version = "v1.1.1"
hash = "sha256-heyPMSBzVlx7ZKgTyzl/xmUfZw3EZCcvGFGrRMIbIr8="
[mod."github.com/ryanuber/columnize"]
version = "v0.0.0-20160712163229-9b3edd62028f"
hash = "sha256-RLUQcU6Z03upKe08v6rjn9/tkyrQsgmpdEmBtWaLQfk="
[mod."github.com/sean-/seed"]
version = "v0.0.0-20170313163322-e2103e2c3529"
hash = "sha256-RQQTjvf8Y91jP5FGOyEnGMFw7zCrcSnUU4eH2CXKkT4="
[mod."github.com/sirupsen/logrus"]
version = "v1.4.2"
hash = "sha256-3QzWUsapCmg3F7JqUuINT3/UG097uzLff6iCcCgQ43o="
[mod."github.com/stretchr/objx"]
version = "v0.1.1"
hash = "sha256-HdGVZCuy7VprC5W9UxGbDmXqsKADMjpEDht7ilGVLco="
[mod."github.com/stretchr/testify"]
version = "v1.3.0"
hash = "sha256-+mSebBNccNcxbY462iKTNTWmd5ZuUkUqFebccn3EtIA="
[mod."golang.org/x/crypto"]
version = "v0.0.0-20200604202706-70a84ac30bf9"
hash = "sha256-I5ov2lV6ubB3H3pn6DFN1VPLyxeLfPUaOx2PJ9K1KH8="
[mod."golang.org/x/net"]
version = "v0.0.0-20190404232315-eb5bcb51f2a3"
hash = "sha256-kwP+BDfPsZEeeceyg4H5LgdTDT8O8YWbkpCkpyuPJJo="
[mod."golang.org/x/sync"]
version = "v0.0.0-20181221193216-37e7f081c4d4"
hash = "sha256-FUpv9bmGLDEjCxXdvR0CRDqOKRY0xrHRPzOsyQyvYK0="
[mod."golang.org/x/sys"]
version = "v0.0.0-20190422165155-953cdadca894"
hash = "sha256-YkQjSZaiVFxrmXYSKmqrGeIO3RZ95fc3dWyCaR+SosY="
[mod."golang.org/x/text"]
version = "v0.3.0"
hash = "sha256-0FFbaxF1ZuAQF3sCcA85e8MO6prFeHint36inija4NY="

39
main.go
View file

@ -12,8 +12,8 @@ import (
"os/signal"
"syscall"
message "bottin/goldap"
ldap "bottin/ldapserver"
message "bottin/goldap"
consul "github.com/hashicorp/consul/api"
log "github.com/sirupsen/logrus"
@ -320,6 +320,7 @@ func (server *Server) init() error {
return err
}
admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
if !environnement_variable_exist {
admin_pass := make([]byte, 8)
@ -328,15 +329,11 @@ func (server *Server) init() error {
return err
}
admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass)
} else {
} else {
server.logger.Debug("BOTTIN_DEFAULT_ADMIN_PW environment variable is set, using it for admin's password")
}
admin_pass_hash, err := SSHAEncode(admin_pass_str)
if err != nil {
server.logger.Error("can't create admin password")
panic(err)
}
admin_pass_hash := SSHAEncode([]byte(admin_pass_str))
admin_dn := "cn=admin," + server.config.Suffix
admin_attributes := Entry{
@ -437,8 +434,8 @@ func (server *Server) handleBindInternal(state *State, r *message.BindRequest) (
}
for _, hash := range passwd {
valid, err := SSHAMatches(hash, string(r.AuthenticationSimple()))
if valid && err == nil {
valid := SSHAMatches(hash, []byte(r.AuthenticationSimple()))
if valid {
groups, err := server.getAttribute(string(r.Name()), ATTR_MEMBEROF)
if err != nil {
return ldap.LDAPResultOperationsError, err
@ -447,32 +444,8 @@ func (server *Server) handleBindInternal(state *State, r *message.BindRequest) (
user: string(r.Name()),
groups: groups,
}
updatePasswordHash(string(r.AuthenticationSimple()), hash, server, string(r.Name()))
return ldap.LDAPResultSuccess, nil
} else {
return ldap.LDAPResultInvalidCredentials, fmt.Errorf("can't authenticate: %w", err)
}
}
return ldap.LDAPResultInvalidCredentials, fmt.Errorf("No password match")
}
// Update the hash if it's not already SSHA512
func updatePasswordHash(password string, currentHash string, server *Server, dn string) {
hashType, err := determineHashType(currentHash)
if err != nil {
server.logger.Errorf("can't determine hash type of password")
return
}
if hashType != SSHA512 {
reencodedPassword, err := SSHAEncode(password)
if err != nil {
server.logger.Errorf("can't encode password")
return
}
server.putAttributes(dn, Entry{
ATTR_USERPASSWORD: []string{reencodedPassword},
})
}
}

70
ssha.go
View file

@ -1,79 +1,25 @@
package main
import (
"errors"
"strings"
"bytes"
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"fmt"
"strings"
log "github.com/sirupsen/logrus"
//"github.com/jsimonetti/pwscheme/ssha"
"github.com/jsimonetti/pwscheme/ssha256"
"github.com/jsimonetti/pwscheme/ssha512"
)
const (
SSHA = "{SSHA}"
SSHA256 = "{SSHA256}"
SSHA512 = "{SSHA512}"
)
// Encode encodes the string to ssha512
func SSHAEncode(rawPassPhrase string) (string, error) {
return ssha512.Generate(rawPassPhrase, 16)
}
// Matches matches the encoded password and the raw password
func SSHAMatches(encodedPassPhrase string, rawPassPhrase string) (bool, error) {
hashType, err := determineHashType(encodedPassPhrase)
if err != nil {
return false, errors.New("invalid password hash stored")
}
var is_ok bool
switch hashType {
case SSHA:
is_ok = LegacySSHAMatches(encodedPassPhrase, []byte(rawPassPhrase))
return is_ok, nil
//return ssha.Validate(rawPassPhrase, encodedPassPhrase)
case SSHA256:
return ssha256.Validate(rawPassPhrase, encodedPassPhrase)
case SSHA512:
return ssha512.Validate(rawPassPhrase, encodedPassPhrase)
}
return false, errors.New("no matching hash type found")
}
func determineHashType(hash string) (string, error) {
if len(hash) >= 7 && strings.ToUpper(string(hash[0:6])) == SSHA {
return SSHA, nil
}
if len(hash) >= 10 && strings.ToUpper(string(hash[0:9])) == SSHA256 {
return SSHA256, nil
}
if len(hash) >= 10 && strings.ToUpper(string(hash[0:9])) == SSHA512 {
return SSHA512, nil
}
return "", errors.New("no valid hash found")
}
// --- legacy
// Encode encodes the []byte of raw password
func LegacySSHAEncode(rawPassPhrase []byte) string {
hash := legacyMakeSSHAHash(rawPassPhrase, legacyMakeSalt())
func SSHAEncode(rawPassPhrase []byte) string {
hash := makeSSHAHash(rawPassPhrase, makeSalt())
b64 := base64.StdEncoding.EncodeToString(hash)
return fmt.Sprintf("{ssha}%s", b64)
}
// Matches matches the encoded password and the raw password
func LegacySSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
func SSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
if !strings.EqualFold(encodedPassPhrase[:6], "{ssha}") {
return false
}
@ -84,7 +30,7 @@ func LegacySSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
}
salt := bhash[20:]
newssha := legacyMakeSSHAHash(rawPassPhrase, salt)
newssha := makeSSHAHash(rawPassPhrase, salt)
if bytes.Compare(newssha, bhash) != 0 {
return false
@ -93,7 +39,7 @@ func LegacySSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
}
// makeSalt make a 32 byte array containing random bytes.
func legacyMakeSalt() []byte {
func makeSalt() []byte {
sbytes := make([]byte, 32)
_, err := rand.Read(sbytes)
if err != nil {
@ -103,7 +49,7 @@ func legacyMakeSalt() []byte {
}
// makeSSHAHash make hasing using SHA-1 with salt. This is not the final output though. You need to append {SSHA} string with base64 of this hash.
func legacyMakeSSHAHash(passphrase, salt []byte) []byte {
func makeSSHAHash(passphrase, salt []byte) []byte {
sha := sha1.New()
sha.Write(passphrase)
sha.Write(salt)

View file

@ -7,9 +7,8 @@ import (
ldap "bottin/ldapserver"
message "bottin/goldap"
consul "github.com/hashicorp/consul/api"
message "bottin/goldap"
)
// Generic item modification function --------
@ -39,7 +38,7 @@ func (server *Server) putAttributes(dn string, attrs Entry) error {
// Retreieve previously existing attributes, which we will use to delete
// entries with the wrong case
previous_pairs, _, err := server.kv.List(prefix+"/attribute=", &server.readOpts)
previous_pairs, _, err := server.kv.List(prefix + "/attribute=", &server.readOpts)
if err != nil {
return err
}
@ -66,24 +65,6 @@ func (server *Server) putAttributes(dn string, attrs Entry) error {
}
}
// if the password is not yet hashed we hash it
if k == ATTR_USERPASSWORD {
tmpValues := []string{}
for _, pw := range values {
_, err := determineHashType(pw)
if err != nil {
encodedPassword, err := SSHAEncode(pw)
if err != nil {
return err
}
tmpValues = append(tmpValues, encodedPassword)
} else {
tmpValues = append(tmpValues, pw)
}
}
values = tmpValues
}
// If we have zero values, delete associated k/v pair
// Otherwise, write new values
if len(values) == 0 {