From 9c197cc35ef4739fe182a9af5ea787bf79c795c1 Mon Sep 17 00:00:00 2001 From: Chris F Ravenscroft Date: Sun, 25 Jul 2021 17:00:20 -0700 Subject: [PATCH] Stronger, salted paswords using bcrypt. (#195) --- README.md | 12 +++++ go.mod | 1 + go.sum | 9 ++++ pkg/config/config.go | 2 + pkg/handler/config.go | 66 +++++++++++++++++++-------- pkg/plugins/basesqlhandler.go | 86 +++++++++++++++++++++++------------ pkg/plugins/mysql.go | 1 + pkg/plugins/postgres.go | 1 + pkg/plugins/sqlite.go | 1 + sample-simple.cfg | 13 ++++++ 10 files changed, 142 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index fdf051f..944df5f 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,11 @@ Here's a sample config wth hardcoded users and groups: primarygroup = 5501 passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood sshkeys = [ "ssh-dss AAAAB3..." ] +[[users]] + name = "uberhackers" + unixid = 5006 + primarygroup = 5501 + passbcrypt = "243261243130244B62463462656F7265504F762E794F324957746D656541326B4B46596275674A79336A476845764B616D65446169784E41384F4432" # dogood [[groups]] name = "superheros" unixid = 5501 @@ -141,6 +146,10 @@ More configuration options are documented here: https://github.com/glauth/glauth * Specify OTP secret used to validate OTP passcode * Example: 3hnvnk4ycv44glzigd6s25j4dougs3rk * default = blank + * passappbcrypt + * Specify an array of app passwords which can also succesfully bind - these bypass the OTP check. Hash the same way as password. + * Example: ["c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa","4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88"] + * default = blank * passappsha256 * Specify an array of app passwords which can also succesfully bind - these bypass the OTP check. Hash the same way as password. * Example: ["c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa","4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88"] @@ -153,6 +162,9 @@ More configuration options are documented here: https://github.com/glauth/glauth ### OpenSSH keys: GLAuth can store a user's SSH authorized keys. Add one or more keys per user as shown above, then setup the goklp helper: https://github.com/appliedtrust/goklp +### Strong Passwords +If you are currently using sha256 passwords (`passsha256` or `passappsha256`) moving to strong, salted paswords is recommended. Simply switch to `passbcrypt` and/or `passappbcrypt` password types. Currently (2021) 212 is a reasonably good value, depending our your server's CPU. + ### Two Factor Authentication GLAuth can be configured to accept OTP tokens as appended to a users password. Support is added for both **TOTP tokens** (often known by it's most prominent implementation, "Google Authenticator") and **Yubikey OTP tokens**. diff --git a/go.mod b/go.mod index 28eaf5b..ee726f7 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/pquerna/otp v1.3.0 github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect gopkg.in/amz.v3 v3.0.0-20201001071545-24fc1eceb27b gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c ) diff --git a/go.sum b/go.sum index eda966b..f8c72c2 100644 --- a/go.sum +++ b/go.sum @@ -51,14 +51,23 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc h1:ejaC8rvIvCWmsaFrvmGOxhBuMxxhBB1xRshuM98XQ7M= github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc/go.mod h1:tso14hwzqX4VbnWTNsxiL0DvMb2OwbGISFA7jDibdWc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= gopkg.in/amz.v3 v3.0.0-20201001071545-24fc1eceb27b h1:tdSyAzD5FHJmZAZ13d5WexAo7YJkiyGphhzFfInMnDw= gopkg.in/amz.v3 v3.0.0-20201001071545-24fc1eceb27b/go.mod h1:cE0dCGx2UfBTjLFlzEx4EXJUmoX6BXBoX9GjKOvqha4= diff --git a/pkg/config/config.go b/pkg/config/config.go index 951906f..77f5370 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -51,7 +51,9 @@ type User struct { Name string OtherGroups []int PassSHA256 string + PassBcrypt string PassAppSHA256 []string + PassAppBcrypt []string PrimaryGroup int SSHKeys []string OTPSecret string diff --git a/pkg/handler/config.go b/pkg/handler/config.go index f48ae6f..bcf7fb3 100644 --- a/pkg/handler/config.go +++ b/pkg/handler/config.go @@ -19,6 +19,7 @@ import ( "github.com/go-logr/logr" "github.com/nmcclain/ldap" "github.com/pquerna/otp/totp" + "golang.org/x/crypto/bcrypt" ) var configattributematcher = regexp.MustCompile(`(?i)\((?P[a-zA-Z0-9]+)\s*=\s*(?P.*)\)`) @@ -129,9 +130,7 @@ func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultC // Store the full bind password provided before possibly modifying // in the otp check - // Generate a hash of the provided password - hashFull := sha256.New() - hashFull.Write([]byte(bindSimplePw)) + untouchedBindSimplePw := bindSimplePw // Test OTP if exists if len(user.OTPSecret) > 0 && !validotp { @@ -146,21 +145,33 @@ func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultC // finally, validate user's pw // check app passwords first - for index, appPw := range user.PassAppSHA256 { - - if appPw != hex.EncodeToString(hashFull.Sum(nil)) { - h.log.V(6).Info("Attempt to bind app pw failed", "index", index, "binddn", bindDN, "src", conn.RemoteAddr()) - } else { - stats.Frontend.Add("bind_successes", 1) - h.log.V(6).Info("Bind success using app pw", "index", index, "binddn", bindDN, "src", conn.RemoteAddr()) - return ldap.LDAPResultSuccess, nil + if user.PassAppBcrypt != nil { + for index, appPw := range user.PassAppBcrypt { + decoded, err := hex.DecodeString(appPw) + if err != nil { + h.log.V(6).Info("invalid app credentials", "incorrect stored hash", "(omitted)") + } else { + if bcrypt.CompareHashAndPassword(decoded, []byte(untouchedBindSimplePw)) == nil { + stats.Frontend.Add("bind_successes", 1) + h.log.V(6).Info("Bind success using app pw", "index", index, "binddn", bindDN, "src", conn.RemoteAddr()) + return ldap.LDAPResultSuccess, nil + } + } + } + } + if user.PassAppSHA256 != nil { + hashFull := sha256.New() + hashFull.Write([]byte(untouchedBindSimplePw)) + for index, appPw := range user.PassAppSHA256 { + if appPw != hex.EncodeToString(hashFull.Sum(nil)) { + h.log.V(6).Info("Attempt to bind app pw failed", "index", index, "binddn", bindDN, "src", conn.RemoteAddr()) + } else { + stats.Frontend.Add("bind_successes", 1) + h.log.V(6).Info("Bind success using app pw", "index", index, "binddn", bindDN, "src", conn.RemoteAddr()) + return ldap.LDAPResultSuccess, nil + } } - } - - // then check main password with the hash - hash := sha256.New() - hash.Write([]byte(bindSimplePw)) // Then ensure the OTP is valid before checking if !validotp { @@ -168,10 +179,25 @@ func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultC return ldap.LDAPResultInvalidCredentials, nil } - // Now, check the hash - if user.PassSHA256 != hex.EncodeToString(hash.Sum(nil)) { - h.log.V(2).Info("invalid credentials", "binddn", bindDN, "src", conn.RemoteAddr()) - return ldap.LDAPResultInvalidCredentials, nil + // Now, check the pasword hash + if user.PassBcrypt != "" { + decoded, err := hex.DecodeString(user.PassBcrypt) + if err != nil { + h.log.V(2).Info("invalid credentials", "incorrect stored hash", "(omitted)") + return ldap.LDAPResultInvalidCredentials, nil + } + if bcrypt.CompareHashAndPassword(decoded, []byte(bindSimplePw)) != nil { + h.log.V(2).Info("invalid credentials", "binddn", bindDN, "src", conn.RemoteAddr()) + return ldap.LDAPResultInvalidCredentials, nil + } + } + if user.PassSHA256 != "" { + hash := sha256.New() + hash.Write([]byte(bindSimplePw)) + if user.PassSHA256 != hex.EncodeToString(hash.Sum(nil)) { + h.log.V(2).Info("invalid credentials", "binddn", bindDN, "src", conn.RemoteAddr()) + return ldap.LDAPResultInvalidCredentials, nil + } } stats.Frontend.Add("bind_successes", 1) diff --git a/pkg/plugins/basesqlhandler.go b/pkg/plugins/basesqlhandler.go index 20bb1da..a81324d 100644 --- a/pkg/plugins/basesqlhandler.go +++ b/pkg/plugins/basesqlhandler.go @@ -19,6 +19,7 @@ import ( "github.com/go-logr/logr" "github.com/nmcclain/ldap" "github.com/pquerna/otp/totp" + "golang.org/x/crypto/bcrypt" ) type SqlBackend interface { @@ -111,9 +112,9 @@ func (h databaseHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resul // find the user user := config.User{} err = h.database.cnx.QueryRow(fmt.Sprintf(` - SELECT u.unixid,u.primarygroup,u.passsha256,u.otpsecret,u.yubikey + SELECT u.unixid,u.primarygroup,u.passbcrypt,u.passsha256,u.otpsecret,u.yubikey FROM users u WHERE lower(u.name)=%s`, h.sqlBackend.GetPrepareSymbol()), userName).Scan( - &user.UnixID, &user.PrimaryGroup, &user.PassSHA256, &user.OTPSecret, &user.Yubikey) + &user.UnixID, &user.PrimaryGroup, &user.PassBcrypt, &user.PassSHA256, &user.OTPSecret, &user.Yubikey) if err != nil { h.log.V(2).Info(fmt.Sprintf("Bind Error: User %s not found.", userName)) return ldap.LDAPResultInvalidCredentials, nil @@ -157,9 +158,7 @@ func (h databaseHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resul // Store the full bind password provided before possibly modifying // in the otp check - // Generate a hash of the provided password - hashFull := sha256.New() - hashFull.Write([]byte(bindSimplePw)) + untouchedBindSimplePw := bindSimplePw // Test OTP if exists if len(user.OTPSecret) > 0 && !validotp { @@ -174,21 +173,33 @@ func (h databaseHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resul // finally, validate user's pw // check app passwords first - for index, appPw := range user.PassAppSHA256 { - - if appPw != hex.EncodeToString(hashFull.Sum(nil)) { - h.log.V(2).Info(fmt.Sprintf("Attempted to bind app pw #%d - failure as %s from %s", index, bindDN, conn.RemoteAddr().String())) - } else { - stats.Frontend.Add("bind_successes", 1) - h.log.V(3).Info("Bind success using app pw #%d as %s from %s", index, bindDN, conn.RemoteAddr().String()) - return ldap.LDAPResultSuccess, nil + if user.PassAppBcrypt != nil { + for index, appPw := range user.PassAppBcrypt { + decoded, err := hex.DecodeString(appPw) + if err != nil { + h.log.V(6).Info("invalid app credentials", "incorrect stored hash", "(omitted)") + } else { + if bcrypt.CompareHashAndPassword(decoded, []byte(untouchedBindSimplePw)) == nil { + stats.Frontend.Add("bind_successes", 1) + h.log.V(6).Info("Bind success using app pw", "index", index, "binddn", bindDN, "src", conn.RemoteAddr()) + return ldap.LDAPResultSuccess, nil + } + } + } + } + if user.PassAppSHA256 != nil { + hashFull := sha256.New() + hashFull.Write([]byte(untouchedBindSimplePw)) + for index, appPw := range user.PassAppSHA256 { + if appPw != hex.EncodeToString(hashFull.Sum(nil)) { + h.log.V(2).Info(fmt.Sprintf("Attempted to bind app pw #%d - failure as %s from %s", index, bindDN, conn.RemoteAddr().String())) + } else { + stats.Frontend.Add("bind_successes", 1) + h.log.V(3).Info("Bind success using app pw #%d as %s from %s", index, bindDN, conn.RemoteAddr().String()) + return ldap.LDAPResultSuccess, nil + } } - } - - // then check main password with the hash - hash := sha256.New() - hash.Write([]byte(bindSimplePw)) // Then ensure the OTP is valid before checking if !validotp { @@ -196,10 +207,25 @@ func (h databaseHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resul return ldap.LDAPResultInvalidCredentials, nil } - // Now, check the hash - if user.PassSHA256 != hex.EncodeToString(hash.Sum(nil)) { - h.log.V(2).Info(fmt.Sprintf("Bind Error: invalid credentials as %s from %s", bindDN, conn.RemoteAddr().String())) - return ldap.LDAPResultInvalidCredentials, nil + // Now, check the password hash + if user.PassBcrypt != "" { + decoded, err := hex.DecodeString(user.PassBcrypt) + if err != nil { + h.log.V(2).Info("invalid credentials", "incorrect stored hash", "(omitted)") + return ldap.LDAPResultInvalidCredentials, nil + } + if bcrypt.CompareHashAndPassword(decoded, []byte(bindSimplePw)) != nil { + h.log.V(2).Info("invalid credentials", "binddn", bindDN, "src", conn.RemoteAddr()) + return ldap.LDAPResultInvalidCredentials, nil + } + } + if user.PassSHA256 != "" { + hash := sha256.New() + hash.Write([]byte(bindSimplePw)) + if user.PassSHA256 != hex.EncodeToString(hash.Sum(nil)) { + h.log.V(2).Info(fmt.Sprintf("Bind Error: invalid credentials as %s from %s", bindDN, conn.RemoteAddr().String())) + return ldap.LDAPResultInvalidCredentials, nil + } } stats.Frontend.Add("bind_successes", 1) @@ -249,7 +275,7 @@ func (h databaseHandler) Search(bindDN string, searchReq ldap.SearchRequest, con } rows, err := h.database.cnx.Query(` - SELECT u.name,u.unixid,u.primarygroup,u.passsha256,u.otpsecret,u.yubikey,u.othergroups,u.givenname,u.sn,u.mail,u.loginshell,u.homedirectory,u.disabled + SELECT u.name,u.unixid,u.primarygroup,u.passbcrypt,u.passsha256,u.otpsecret,u.yubikey,u.othergroups,u.givenname,u.sn,u.mail,u.loginshell,u.homedirectory,u.disabled FROM users u`) if err != nil { return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: Unable to retrieve data [%s]", err.Error()) @@ -260,7 +286,7 @@ func (h databaseHandler) Search(bindDN string, searchReq ldap.SearchRequest, con var disabled int u := config.User{} for rows.Next() { - err := rows.Scan(&u.Name, &u.UnixID, &u.PrimaryGroup, &u.PassSHA256, &u.OTPSecret, &u.Yubikey, &otherGroups, &u.GivenName, &u.SN, &u.Mail, &u.LoginShell, &u.Homedir, &disabled) + err := rows.Scan(&u.Name, &u.UnixID, &u.PrimaryGroup, &u.PassBcrypt, &u.PassSHA256, &u.OTPSecret, &u.Yubikey, &otherGroups, &u.GivenName, &u.SN, &u.Mail, &u.LoginShell, &u.Homedir, &disabled) if err != nil { return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: Unable to retrieve data [%s]", err.Error()) } @@ -295,9 +321,9 @@ func (h databaseHandler) FindUser(userName string) (f bool, u config.User, err e found := false err = h.database.cnx.QueryRow(fmt.Sprintf(` - SELECT u.unixid,u.primarygroup,u.passsha256,u.otpsecret,u.yubikey + SELECT u.unixid,u.primarygroup,u.passbcrypt,u.passsha256,u.otpsecret,u.yubikey FROM users u WHERE lower(u.name)=%s`, h.sqlBackend.GetPrepareSymbol()), userName).Scan( - &user.UnixID, &user.PrimaryGroup, &user.PassSHA256, &user.OTPSecret, &user.Yubikey) + &user.UnixID, &user.PrimaryGroup, &user.PassBcrypt, &user.PassSHA256, &user.OTPSecret, &user.Yubikey) if err == nil { found = true } @@ -377,7 +403,7 @@ func (h databaseHandler) getGroupMembers(gid int) []string { members := make(map[string]bool) rows, err := h.database.cnx.Query(` - SELECT u.name,u.unixid,u.primarygroup,u.passsha256,u.otpsecret,u.yubikey,u.othergroups + SELECT u.name,u.unixid,u.primarygroup,u.passbcrypt,u.passsha256,u.otpsecret,u.yubikey,u.othergroups FROM users u WHERE lower(u.name)=?`, ) if err != nil { @@ -389,7 +415,7 @@ func (h databaseHandler) getGroupMembers(gid int) []string { var otherGroups string u := config.User{} for rows.Next() { - err := rows.Scan(&u.Name, &u.UnixID, &u.PrimaryGroup, &u.PassSHA256, &u.OTPSecret, &u.Yubikey, &otherGroups) + err := rows.Scan(&u.Name, &u.UnixID, &u.PrimaryGroup, &u.PassBcrypt, &u.PassSHA256, &u.OTPSecret, &u.Yubikey, &otherGroups) if err != nil { return []string{} } @@ -435,7 +461,7 @@ func (h databaseHandler) getGroupMembers(gid int) []string { func (h databaseHandler) getGroupMemberIDs(gid int) []string { members := make(map[string]bool) rows, err := h.database.cnx.Query(` - SELECT u.name,u.unixid,u.primarygroup,u.passsha256,u.otpsecret,u.yubikey,u.othergroups + SELECT u.name,u.unixid,u.primarygroup,u.passbcrypt,u.passsha256,u.otpsecret,u.yubikey,u.othergroups FROM users u`) if err != nil { // Silent fail... for now @@ -446,7 +472,7 @@ func (h databaseHandler) getGroupMemberIDs(gid int) []string { var otherGroups string u := config.User{} for rows.Next() { - err := rows.Scan(&u.Name, &u.UnixID, &u.PrimaryGroup, &u.PassSHA256, &u.OTPSecret, &u.Yubikey, &otherGroups) + err := rows.Scan(&u.Name, &u.UnixID, &u.PrimaryGroup, &u.PassBcrypt, &u.PassSHA256, &u.OTPSecret, &u.Yubikey, &otherGroups) if err != nil { return []string{} } diff --git a/pkg/plugins/mysql.go b/pkg/plugins/mysql.go index bfe4a4c..c7bdf65 100644 --- a/pkg/plugins/mysql.go +++ b/pkg/plugins/mysql.go @@ -40,6 +40,7 @@ CREATE TABLE IF NOT EXISTS users ( homedirectory VARCHAR(64) DEFAULT '', disabled SMALLINT DEFAULT 0, passsha256 VARCHAR(64) DEFAULT '', + passbcrypt VARCHAR(64) DEFAULT '', otpsecret VARCHAR(64) DEFAULT '', yubikey VARCHAR(128) DEFAULT '') `) diff --git a/pkg/plugins/postgres.go b/pkg/plugins/postgres.go index 5ab0b27..45b5954 100644 --- a/pkg/plugins/postgres.go +++ b/pkg/plugins/postgres.go @@ -40,6 +40,7 @@ CREATE TABLE IF NOT EXISTS users ( homedirectory TEXT DEFAULT '', disabled SMALLINT DEFAULT 0, passsha256 TEXT DEFAULT '', + passbcrypt TEXT DEFAULT '', otpsecret TEXT DEFAULT '', yubikey TEXT DEFAULT '') `) diff --git a/pkg/plugins/sqlite.go b/pkg/plugins/sqlite.go index 216970e..fb210f5 100644 --- a/pkg/plugins/sqlite.go +++ b/pkg/plugins/sqlite.go @@ -40,6 +40,7 @@ CREATE TABLE IF NOT EXISTS users ( homedirectory TEXT DEFAULT '', disabled SMALLINT DEFAULT 0, passsha256 TEXT DEFAULT '', + passbcrypt TEXT DEFAULT '', otpsecret TEXT DEFAULT '', yubikey TEXT DEFAULT '') `) diff --git a/sample-simple.cfg b/sample-simple.cfg index 3e7a00f..b7d8306 100644 --- a/sample-simple.cfg +++ b/sample-simple.cfg @@ -86,6 +86,19 @@ debug = true otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk" yubikey = "vvjrcfalhlaa" +[[users]] + name = "uberhackers" + unixid = 5005 + primarygroup = 5501 + # bcrypt format: hex($2y$2^$$) + passappbcrypt = [ + "243261243130244B62463462656F7265504F762E794F324957746D656541326B4B46596275674A79336A476845764B616D65446169784E41384F4432" # dogood + ] + # uncomment and comment out above array to test password with otp code + # passappbcrypt = "243261243130244B62463462656F7265504F762E794F324957746D656541326B4B46596275674A79336A476845764B616D65446169784E41384F4432" # dogood + otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk" + + ################# # The groups section contains a hardcoded list of valid users. [[groups]]