|
|
@ -51,8 +51,10 @@ func Parse(htpasswdBytes []byte) (Passwds, error) { |
|
|
|
_, exists := passwords[parts[0]] |
|
|
|
_, exists := passwords[parts[0]] |
|
|
|
|
|
|
|
|
|
|
|
if exists { |
|
|
|
if exists { |
|
|
|
err = errors.New("invalid htpasswd file - user " + |
|
|
|
err = &UserError{ |
|
|
|
parts[0] + " defined more than once") |
|
|
|
Name: parts[0], |
|
|
|
|
|
|
|
Err: ErrExists, |
|
|
|
|
|
|
|
} |
|
|
|
return passwords, err |
|
|
|
return passwords, err |
|
|
|
} |
|
|
|
} |
|
|
|
passwords[parts[0]] = parts[1] |
|
|
|
passwords[parts[0]] = parts[1] |
|
|
@ -78,8 +80,12 @@ func CreateUser(file, user, passwd string, algo Hasher) error { |
|
|
|
// using the given name, password and hashing algorithm
|
|
|
|
// using the given name, password and hashing algorithm
|
|
|
|
func (pp Passwds) CreateUser(user, passwd string, algo Hasher) error { |
|
|
|
func (pp Passwds) CreateUser(user, passwd string, algo Hasher) error { |
|
|
|
if _, exists := pp[user]; exists { |
|
|
|
if _, exists := pp[user]; exists { |
|
|
|
return fmt.Errorf("user %s already exists", user) |
|
|
|
return &UserError{ |
|
|
|
|
|
|
|
Name: user, |
|
|
|
|
|
|
|
Err: ErrExists, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
h, err := algo.Hash(passwd) |
|
|
|
h, err := algo.Hash(passwd) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
@ -106,8 +112,12 @@ func UpdateUser(file, user, passwd string, algo Hasher) error { |
|
|
|
// using the given name, password and hashing algorithm
|
|
|
|
// using the given name, password and hashing algorithm
|
|
|
|
func (pp Passwds) UpdateUser(user, passwd string, algo Hasher) error { |
|
|
|
func (pp Passwds) UpdateUser(user, passwd string, algo Hasher) error { |
|
|
|
if _, exists := pp[user]; !exists { |
|
|
|
if _, exists := pp[user]; !exists { |
|
|
|
return fmt.Errorf("user %s does not exist", user) |
|
|
|
return &UserError{ |
|
|
|
|
|
|
|
Name: user, |
|
|
|
|
|
|
|
Err: ErrNotExists, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
h, err := algo.Hash(passwd) |
|
|
|
h, err := algo.Hash(passwd) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
@ -133,7 +143,10 @@ func DeleteUser(file, user string) error { |
|
|
|
// DeleteUser deletes the named user from the named file
|
|
|
|
// DeleteUser deletes the named user from the named file
|
|
|
|
func (pp Passwds) DeleteUser(user string) error { |
|
|
|
func (pp Passwds) DeleteUser(user string) error { |
|
|
|
if _, exists := pp[user]; !exists { |
|
|
|
if _, exists := pp[user]; !exists { |
|
|
|
return fmt.Errorf("user %s does not exist", user) |
|
|
|
return &UserError{ |
|
|
|
|
|
|
|
Name: user, |
|
|
|
|
|
|
|
Err: ErrNotExists, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
delete(pp, user) |
|
|
|
delete(pp, user) |
|
|
@ -154,12 +167,20 @@ func VerifyUser(file, user, passwd string) error { |
|
|
|
// with the given Passwd object
|
|
|
|
// with the given Passwd object
|
|
|
|
func (pp Passwds) VerifyUser(user, passwd string) error { |
|
|
|
func (pp Passwds) VerifyUser(user, passwd string) error { |
|
|
|
if _, ok := pp[user]; !ok { |
|
|
|
if _, ok := pp[user]; !ok { |
|
|
|
return fmt.Errorf("user %s does not exist", user) |
|
|
|
return &UserError{ |
|
|
|
|
|
|
|
Name: user, |
|
|
|
|
|
|
|
Err: ErrNotExists, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
alg, err := identifyHash(pp[user]) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
alg := identifyHash(pp[user]) |
|
|
|
return fmt.Errorf("cannot identify algo %v", alg) |
|
|
|
if alg == nil { |
|
|
|
|
|
|
|
return &UserError{ |
|
|
|
|
|
|
|
Name: user, |
|
|
|
|
|
|
|
Err: ErrInvalidAlgorithm, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return alg.Match(passwd, pp[user]) |
|
|
|
return alg.Match(passwd, pp[user]) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -177,24 +198,24 @@ func (pp Passwds) Bytes() []byte { |
|
|
|
return pass |
|
|
|
return pass |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func identifyHash(h string) (Hasher, error) { |
|
|
|
func identifyHash(h string) Hasher { |
|
|
|
switch { |
|
|
|
switch { |
|
|
|
case strings.HasPrefix(h, "$2a$"), strings.HasPrefix(h, "$2y$"), |
|
|
|
case strings.HasPrefix(h, "$2a$"), strings.HasPrefix(h, "$2y$"), |
|
|
|
strings.HasPrefix(h, "$2x$"), strings.HasPrefix(h, "$2b$"): |
|
|
|
strings.HasPrefix(h, "$2x$"), strings.HasPrefix(h, "$2b$"): |
|
|
|
return new(Bcrypt), nil |
|
|
|
return new(Bcrypt) |
|
|
|
case strings.HasPrefix(h, "$apr1$"): |
|
|
|
case strings.HasPrefix(h, "$apr1$"): |
|
|
|
return new(Apr1), nil |
|
|
|
return new(Apr1) |
|
|
|
case strings.HasPrefix(h, "{SHA}"): |
|
|
|
case strings.HasPrefix(h, "{SHA}"): |
|
|
|
return new(Sha), nil |
|
|
|
return new(Sha) |
|
|
|
case strings.HasPrefix(h, "{SSHA}"): |
|
|
|
case strings.HasPrefix(h, "{SSHA}"): |
|
|
|
return new(Ssha), nil |
|
|
|
return new(Ssha) |
|
|
|
case strings.HasPrefix(h, "$5$"): |
|
|
|
case strings.HasPrefix(h, "$5$"): |
|
|
|
return new(Sha256), nil |
|
|
|
return new(Sha256) |
|
|
|
case strings.HasPrefix(h, "$6$"): |
|
|
|
case strings.HasPrefix(h, "$6$"): |
|
|
|
return new(Sha512), nil |
|
|
|
return new(Sha512) |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("unsupported hash algorithm") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func validLine(parts []string, lineNumber int, line string) (bool, error) { |
|
|
|
func validLine(parts []string, lineNumber int, line string) (bool, error) { |
|
|
|