Use pure implementation of crypt to generate and check password hashes.

This allows to remove the dependency to libc.
This commit is contained in:
Clement Michaud 2019-11-01 19:31:51 +01:00 committed by Clément Michaud
parent ee2b181a7f
commit 5bd9e831eb
8 changed files with 58 additions and 41 deletions

View File

@ -6,6 +6,7 @@ Release Notes - Version 4.0.0
* The local storage has been replaced by a good old sqlite3 database.
* The "secure" flag from the SMTP notifier configuration has been removed as TLS is used by default when available.
* authelia-scripts tool has been rewritten in Go.
* Use pure implementation of crypt to avoid CGO and dependency to libc.
Release Notes - Version 3.16.3
------------------------------

View File

@ -1,16 +1,40 @@
FROM alpine:3.9.4
# =======================================
# ===== Build image for the backend =====
# =======================================
FROM golang:1.13-alpine AS builder-backend
# gcc and musl-dev are required for building go-sqlite3
RUN apk --no-cache add gcc musl-dev
WORKDIR /go/src/app
COPY . .
# CGO_ENABLED=1 is mandatory for building go-sqlite3
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -o authelia
# ========================================
# ===== Build image for the frontend =====
# ========================================
FROM node:11-alpine AS builder-frontend
WORKDIR /node/src/app
COPY client .
# Install the dependencies and build
RUN npm ci && npm run build
# ===================================
# ===== Authelia official image =====
# ===================================
FROM alpine:3.10.3
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /usr/app
RUN apk --no-cache add ca-certificates tzdata wget
# Install the libc required by the password hashing compiled with CGO.
RUN wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
RUN wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.30-r0/glibc-2.30-r0.apk
RUN apk --no-cache add glibc-2.30-r0.apk
ADD dist/authelia authelia
ADD dist/public_html public_html
COPY --from=builder-backend /go/src/app/authelia authelia
COPY --from=builder-frontend /node/src/app/build public_html
EXPOSE 9091

View File

@ -96,7 +96,7 @@ func (p *FileUserProvider) UpdatePassword(username string, newPassword string) e
return fmt.Errorf("User '%s' does not exist in database", username)
}
hash := HashPassword(newPassword, nil)
hash := HashPassword(newPassword, "")
details.HashedPassword = fmt.Sprintf("{CRYPT}%s", hash)
p.lock.Lock()

View File

@ -1,29 +1,15 @@
package authentication
// #cgo LDFLAGS: -lcrypt
// #define _GNU_SOURCE
// #include <crypt.h>
// #include <stdlib.h>
import "C"
import (
"errors"
"fmt"
"log"
"math/rand"
"strconv"
"strings"
"unsafe"
)
// Crypt wraps C library crypt_r
func crypt(key string, salt string) string {
data := C.struct_crypt_data{}
ckey := C.CString(key)
csalt := C.CString(salt)
out := C.GoString(C.crypt_r(ckey, csalt, &data))
C.free(unsafe.Pointer(ckey))
C.free(unsafe.Pointer(csalt))
return out
}
"github.com/simia-tech/crypt"
)
// PasswordHash represents all characteristics of a password hash.
// Authelia only supports salted SHA512 method, i.e., $6$ mode.
@ -79,14 +65,15 @@ func RandomString(n int) string {
// HashPassword generate a salt and hash the password with the salt and a constant
// number of rounds.
func HashPassword(password string, salt *string) string {
var generatedSalt string
if salt == nil {
generatedSalt = fmt.Sprintf("$6$rounds=50000$%s$", RandomString(16))
} else {
generatedSalt = *salt
func HashPassword(password string, salt string) string {
if salt == "" {
salt = fmt.Sprintf("$6$rounds=50000$%s", RandomString(16))
}
return crypt(password, generatedSalt)
hash, err := crypt.Crypt(password, salt)
if err != nil {
log.Fatal(err)
}
return hash
}
// CheckPassword check a password against a hash.
@ -96,6 +83,6 @@ func CheckPassword(password string, hash string) (bool, error) {
return false, err
}
salt := fmt.Sprintf("$6$rounds=%d$%s$", passwordHash.Rounds, passwordHash.Salt)
pHash := HashPassword(password, &salt)
pHash := HashPassword(password, salt)
return pHash == hash, nil
}

View File

@ -7,13 +7,12 @@ import (
)
func TestShouldHashPassword(t *testing.T) {
salt := "$6$rounds=5000$aFr56HjK3DrB8t3S$"
hash := HashPassword("password", &salt)
assert.Equal(t, "$6$rounds=5000$aFr56HjK3DrB8t3S$3yTiN5991WnlmhE8qlMmayIiUiT5ppq68CIuHBrGgQHJ4RWSCb0AykB0E6Ij761ZTzLaCZKuXpurcBiqDR1hu.", hash)
hash := HashPassword("password", "$6$rounds=50000$aFr56HjK3DrB8t3S")
assert.Equal(t, "$6$rounds=50000$aFr56HjK3DrB8t3S$zhPQiS85cgBlNhUKKE6n/AHMlpqrvYSnSL3fEVkK0yHFQ.oFFAd8D4OhPAy18K5U61Z2eBhxQXExGU/eknXlY1", hash)
}
func TestShouldCheckPassword(t *testing.T) {
ok, err := CheckPassword("password", "$6$rounds=5000$aFr56HjK3DrB8t3S$3yTiN5991WnlmhE8qlMmayIiUiT5ppq68CIuHBrGgQHJ4RWSCb0AykB0E6Ij761ZTzLaCZKuXpurcBiqDR1hu.")
ok, err := CheckPassword("password", "$6$rounds=50000$aFr56HjK3DrB8t3S$zhPQiS85cgBlNhUKKE6n/AHMlpqrvYSnSL3fEVkK0yHFQ.oFFAd8D4OhPAy18K5U61Z2eBhxQXExGU/eknXlY1")
assert.NoError(t, err)
assert.True(t, ok)

View File

@ -9,5 +9,5 @@ import (
// HashPassword hash the provided password with crypt sha256 hash function
func HashPassword(cobraCmd *cobra.Command, args []string) {
fmt.Println(authentication.HashPassword(args[0], nil))
fmt.Println(authentication.HashPassword(args[0], ""))
}

1
go.mod
View File

@ -14,6 +14,7 @@ require (
github.com/golang/snappy v0.0.1 // indirect
github.com/mattn/go-sqlite3 v1.11.0
github.com/pquerna/otp v1.2.0
github.com/simia-tech/crypt v0.2.0
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.5
github.com/stretchr/testify v1.4.0

5
go.sum
View File

@ -59,6 +59,8 @@ github.com/savsgio/dictpool v0.0.0-20191028211042-a886cee3358a h1:HHo8bk/5tOTL+U
github.com/savsgio/dictpool v0.0.0-20191028211042-a886cee3358a/go.mod h1:hnGRFeigcU3gTEMTWW8OjfoSYztn2GPcriOY9iIzCrA=
github.com/savsgio/gotils v0.0.0-20190925070755-524bc4f47500 h1:9Pi10H7E8E79/x2HSe1FmMGd7BJ1WAqDKzwjpv+ojFg=
github.com/savsgio/gotils v0.0.0-20190925070755-524bc4f47500/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY=
github.com/simia-tech/crypt v0.2.0 h1:cU8qdqUYNuEFKSMq15yaB2aI1aC5vrn6dFOonT6Kg6o=
github.com/simia-tech/crypt v0.2.0/go.mod h1:DMwvjPTzsiHrjqHVW5HvIbF4vUUzMCYDKVLsPWmLdTo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
@ -71,6 +73,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
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.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
@ -92,6 +95,7 @@ github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA=
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -99,6 +103,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181116161606-93218def8b18/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=