Inclusive + Fix checks + Fix SSHA

This commit is contained in:
Quentin 2022-07-19 10:35:14 +02:00
parent 216e175eaf
commit 3c846b6a59
Signed by untrusted user: quentin
GPG key ID: E9602264D639FF68
10 changed files with 70 additions and 69 deletions

View file

@ -4,9 +4,9 @@
"base_dn": "dc=example,dc=com",
"user_base_dn": "ou=users,dc=example,dc=com",
"user_name_attr": "uid",
"user_name_attr": "cn",
"group_base_dn": "ou=groups,dc=example,dc=com",
"group_name_attr": "gid",
"group_name_attr": "cn",
"invitation_base_dn": "ou=invitations,dc=example,dc=com",
"invitation_name_attr": "cn",
@ -21,7 +21,7 @@
"smtp_username": "guichet",
"smtp_password": "",
"admin_account": "uid=admin,dc=example,dc=com",
"admin_account": "cn=admin,dc=example,dc=com",
"group_can_admin": "gid=admin,ou=groups,dc=example,dc=com",
"group_can_invite": "",

6
go.mod
View file

@ -5,14 +5,12 @@ go 1.13
require (
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b
github.com/emersion/go-smtp v0.12.1
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-ldap/ldap/v3 v3.1.6
github.com/google/uuid v1.1.1
github.com/gorilla/mux v1.7.3
github.com/gorilla/sessions v1.2.0
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73
github.com/minio/minio-go/v7 v7.0.0
github.com/sirupsen/logrus v1.6.0
github.com/stretchr/objx v0.1.1 // indirect
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6
)

19
go.sum
View file

@ -1,4 +1,5 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
@ -8,13 +9,12 @@ github.com/emersion/go-smtp v0.12.1 h1:1R8BDqrR2HhlGwgFYcOi+BVTvK1bMjAB65QcVpJ5s
github.com/emersion/go-smtp v0.12.1/go.mod h1:SD9V/xa4ndMw77lR3Mf7htkp8RBNYuPh9UeuBs9tpUQ=
github.com/go-asn1-ber/asn1-ber v1.3.1 h1:gvPdv/Hr++TRFCl0UbPFHC54P9N9jgsRPnmnr419Uck=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk=
github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-ldap/ldap/v3 v3.1.6 h1:VTihvB7egSAvU6KOagaiA/EvgJMR2jsjRAVIho2ydBo=
github.com/go-ldap/ldap/v3 v3.1.6/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@ -22,14 +22,14 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
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/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
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.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
@ -45,16 +45,16 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -66,7 +66,6 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgP
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View file

@ -133,9 +133,12 @@ func handleNewAccount(w http.ResponseWriter, r *http.Request, l *ldap.Conn, invi
}
func tryCreateAccount(l *ldap.Conn, data *NewAccountData, pass1 string, pass2 string, invitedBy string) {
checkFailed := false
// Check if username is correct
if match, err := regexp.MatchString("^[a-zA-Z0-9._-]+$", data.Username); !(err == nil && match) {
if match, err := regexp.MatchString("^[a-z0-9._-]+$", data.Username); !(err == nil && match) {
data.ErrorInvalidUsername = true
checkFailed = true
}
// Check if user exists
@ -150,30 +153,39 @@ func tryCreateAccount(l *ldap.Conn, data *NewAccountData, pass1 string, pass2 st
sr, err := l.Search(searchRq)
if err != nil {
data.ErrorMessage = err.Error()
return
checkFailed = true
}
if len(sr.Entries) > 0 {
data.ErrorUsernameTaken = true
return
checkFailed = true
}
// Check that password is long enough
if len(pass1) < 8 {
data.ErrorPasswordTooShort = true
return
checkFailed = true
}
if pass1 != pass2 {
data.ErrorPasswordMismatch = true
return
checkFailed = true
}
if checkFailed {
return
}
// Actually create user
req := ldap.NewAddRequest(userDn, nil)
req.Attribute("objectclass", []string{"inetOrgPerson", "organizationalPerson", "person", "top"})
req.Attribute("structuralobjectclass", []string{"inetOrgPerson"})
req.Attribute("userpassword", []string{SSHAEncode([]byte(pass1))})
pw, err := SSHAEncode(pass1)
if err != nil {
data.ErrorMessage = err.Error()
return
}
req.Attribute("userpassword", []string{pw})
req.Attribute("invitedby", []string{invitedBy})
if len(data.DisplayName) > 0 {
req.Attribute("displayname", []string{data.DisplayName})
@ -259,10 +271,15 @@ func trySendCode(login *LoginStatus, choice string, sendto string, data *SendCod
// Create invitation object in database
inviteDn := config.InvitationNameAttr + "=" + code_id + "," + config.InvitationBaseDN
req := ldap.NewAddRequest(inviteDn, nil)
req.Attribute("userpassword", []string{SSHAEncode([]byte(code_pw))})
pw, err := SSHAEncode(code_pw)
if err != nil {
data.ErrorMessage = err.Error()
return
}
req.Attribute("userpassword", []string{pw})
req.Attribute("objectclass", []string{"top", "invitationCode"})
err := login.conn.Add(req)
err = login.conn.Add(req)
if err != nil {
data.ErrorMessage = err.Error()
return

View file

@ -122,12 +122,17 @@ func handlePasswd(w http.ResponseWriter, r *http.Request) {
data.NoMatchError = true
} else {
modify_request := ldap.NewModifyRequest(login.Info.DN, nil)
modify_request.Replace("userpassword", []string{SSHAEncode([]byte(password))})
err := login.conn.Modify(modify_request)
if err != nil {
data.ErrorMessage = err.Error()
pw, err := SSHAEncode(password);
if err == nil {
modify_request.Replace("userpassword", []string{pw})
err := login.conn.Modify(modify_request)
if err != nil {
data.ErrorMessage = err.Error()
} else {
data.Success = true
}
} else {
data.Success = true
data.ErrorMessage = err.Error()
}
}
}

33
ssha.go
View file

@ -1,37 +1,10 @@
package main
import (
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/jsimonetti/pwscheme/ssha512"
)
// Encode encodes the []byte of raw password
func SSHAEncode(rawPassPhrase []byte) string {
hash := makeSSHAHash(rawPassPhrase, makeSalt())
b64 := base64.StdEncoding.EncodeToString(hash)
return fmt.Sprintf("{ssha}%s", b64)
}
// makeSalt make a 32 byte array containing random bytes.
func makeSalt() []byte {
sbytes := make([]byte, 32)
_, err := rand.Read(sbytes)
if err != nil {
log.Panicf("Could not read random bytes: %s", err)
}
return sbytes
}
// 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 {
sha := sha1.New()
sha.Write(passphrase)
sha.Write(salt)
h := sha.Sum(nil)
return append(h, salt...)
func SSHAEncode(rawPassPhrase string) (string, error) {
return ssha512.Generate(rawPassPhrase, 16)
}

View file

@ -38,7 +38,7 @@
Administration
</div>
<div class="list-group list-group-flush">
<a class="list-group-item list-group-item-action" href="/admin/users">Utilisateurs</a>
<a class="list-group-item list-group-item-action" href="/admin/users">Utilisateur·ices</a>
<a class="list-group-item list-group-item-action" href="/admin/groups">Groupes</a>
<a class="list-group-item list-group-item-action" href="/admin/ldap/{{.BaseDN}}">Explorateur LDAP</a>
</div>

View file

@ -12,7 +12,7 @@
</div>
{{end}}
{{if .WarningMessage}}
<div class="alert alert-danger mt-4">Des erreurs se sont produtes, le compte pourrait ne pas être totalement fonctionnel.
<div class="alert alert-danger mt-4">Des erreurs se sont produites, le compte pourrait ne pas être totalement fonctionnel.
<div style="font-size: 0.8em">{{ .WarningMessage }}</div>
</div>
{{end}}
@ -25,12 +25,15 @@
<form method="POST" class="mt-4">
<h5>Renseignements obligatoires</h5>
<div class="form-group">
<label for="username">Nom d'utilisateur souhaité :</label>
<label for="username">Identifiant souhaité :</label>
<input type="text" id="username" name="username" class="form-control" value="{{ .Username }}" />
<small class="form-text text-muted">
Votre identifiant doit être en minuscule.
</small>
</div>
{{if .ErrorInvalidUsername}}
<div class="alert alert-warning">
Nom d'utilisateur invalide. Ne peut contenir que les caractères suivants : chiffres, lettres, point, tiret bas (_) et tiret du milieu (-).
Nom d'utilisateur invalide. Ne peut contenir que les caractères suivants : chiffres, lettres minuscules, point, tiret bas (_) et tiret du milieu (-).
</div>
{{end}}
{{if .ErrorUsernameTaken}}
@ -41,6 +44,9 @@
<div class="form-group">
<label for="password">Mot de passe :</label>
<input type="password" id="password" name="password" class="form-control" />
<small class="form-text text-muted">
La seule contrainte est que votre mot de passe doit faire au moins 8 caractères. Utilisez chiffres, majuscules, et caractères spéciaux sans modération !
</small>
</div>
{{if .ErrorPasswordTooShort}}
<div class="alert alert-warning">
@ -58,6 +64,9 @@
{{end}}
<h5>Renseignements optionnels</h5>
<small class="form-text text-muted">
Ces informations sont utilisées exclusivement pour "pré-configurer" les services que vous utiliserez, elles n'ont pas besoin de correspondre à votre état civil.
</small>
<div class="form-group">
<label for="displayname">Nom complet :</label>
<input type="text" id="displayname" name="displayname" class="form-control" value="{{ .DisplayName }}" />

View file

@ -5,7 +5,7 @@
<form method="POST">
{{if .WrongUser}}
<div class="alert alert-danger">Nom d'utilisateur invalide.</div>
<div class="alert alert-danger">Identifiant invalide.</div>
{{end}}
{{if .WrongPass}}
<div class="alert alert-danger">Mot de passe invalide.</div>
@ -16,11 +16,11 @@
</div>
{{end}}
<div class="form-group">
<label for="username">Nom d'utilisateur:</label>
<label for="username">Identifiant :</label>
<input type="text" name="username" id="username" class="form-control" value="{{ .Username }}" />
</div>
<div class="form-group">
<label for="password">Mot de passe:</label>
<label for="password">Mot de passe :</label>
<input type="password" name="password" id="password" class="form-control" />
</div>
<button type="submit" class="btn btn-primary">Se connecter</button>
@ -30,6 +30,6 @@
<p><strong>Mot de passe oublié ?</strong>
Écrivez à <samp>coucou</samp><img src="static/image/at_sign.svg" style="height: 1em" alt="arobase" /><samp>deuxfleurs.fr</samp>
ou contactez directement votre administrateur favori.</p>
ou contactez directement votre opérateur·ice préféré·e.</p>
{{end}}

View file

@ -19,7 +19,7 @@
<form method="POST" class="mt-4" enctype="multipart/form-data">
<div class="form-row">
<div class="form-group col-md-6">
<label>Nom d'utilisateur:</label>
<label>Identifiant:</label>
<input type="text" disabled="true" class="form-control" value="{{ .Status.Info.Username }}" />
</div>
<div class="form-group col-md-6">