2023-07-05 22:07:10 +02:00
|
|
|
package prosody
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/hmac"
|
|
|
|
"crypto/sha1"
|
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2023-07-07 23:55:20 +02:00
|
|
|
"io"
|
2023-07-05 22:07:10 +02:00
|
|
|
"os/exec"
|
2023-07-07 23:40:57 +02:00
|
|
|
"strconv"
|
2023-07-05 22:07:10 +02:00
|
|
|
|
|
|
|
"github.com/xdg-go/pbkdf2"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (p *Prosody) ChangePassword(user string, currentPwd string, newPwd string) error {
|
|
|
|
acc, err := p.loadAccount(user)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("p.loadAccount %w", err)
|
|
|
|
}
|
|
|
|
|
2023-07-07 23:40:57 +02:00
|
|
|
iterationCount, err := strconv.Atoi(acc.IterationCount)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("strconv.Atoi %w", err)
|
|
|
|
}
|
|
|
|
storedKey, err := hashPassword(currentPwd, acc.Salt, iterationCount)
|
2023-07-05 22:07:10 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("hashPassword: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compare the hashes
|
2023-07-07 23:36:05 +02:00
|
|
|
if storedKey != acc.StoredKey {
|
2023-07-05 22:07:10 +02:00
|
|
|
return errors.New("password is incorrect")
|
|
|
|
}
|
|
|
|
|
2023-08-04 23:04:44 +02:00
|
|
|
cmd := exec.Command("/usr/bin/prosodyctl", "passwd", user)
|
2023-07-07 23:55:20 +02:00
|
|
|
// Create a pipe to write to the process's standard input.
|
|
|
|
stdin, err := cmd.StdinPipe()
|
2023-07-05 22:07:10 +02:00
|
|
|
if err != nil {
|
2023-07-07 23:55:20 +02:00
|
|
|
return fmt.Errorf("creating stdin pipe: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the process.
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
return fmt.Errorf("starting command: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the password to the process's standard input.
|
|
|
|
_, err = io.WriteString(stdin, newPwd+"\n"+newPwd+"\n")
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("writing to stdin pipe: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close the pipe to indicate that we're done writing.
|
|
|
|
if err := stdin.Close(); err != nil {
|
|
|
|
return fmt.Errorf("closing stdin pipe: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for the command to finish.
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
|
|
return fmt.Errorf("waiting for command: %w", err)
|
2023-07-05 22:07:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func hashPassword(password, salt string, iterationCount int) (string, error) {
|
|
|
|
// Hash the password using the SCRAM mechanism
|
|
|
|
saltedPassword := pbkdf2.Key([]byte(password), []byte(salt), iterationCount, 20, sha1.New)
|
|
|
|
clientKey := hmacSha1(saltedPassword, []byte("Client Key"))
|
|
|
|
storedKey := sha1.Sum(clientKey)
|
|
|
|
|
|
|
|
return hex.EncodeToString(storedKey[:]), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func hmacSha1(key, data []byte) []byte {
|
|
|
|
mac := hmac.New(sha1.New, key)
|
|
|
|
mac.Write(data)
|
|
|
|
return mac.Sum(nil)
|
|
|
|
}
|