Add -passwordHash support (#42)

* Update main.go

* Update main.go

* Set

* Set password to db

* Main

- Password hash, after trimming, cannot be empty

* Backend

- Assume incoming data is already a hash

* Main

- CLeaned up

* README

- Updated command-line argument documentation

* Menton this

* lower case

* SQLITE Driver

- Removed comments

* Main

- `passwordhash` only valiud when non-""

* Main

- Try running `Cost([]byte)` on the UTF-8 bytes to verify the hash
This commit is contained in:
Tristan B. Velloza Kildaire
2025-12-01 22:43:35 +02:00
committed by GitHub
parent 59426fabf1
commit 12c153a364
3 changed files with 34 additions and 7 deletions

View File

@@ -68,6 +68,7 @@ The following command line switches are supported by the `yggmail` binary:
* `-smtp=listenaddr:port` — listen for SMTP on a specific address/port
* `-imap=listenaddr:port` — listen for IMAP on a specific address/port;
* `-password` — set your IMAP/SMTP password (doesn't matter if Yggmail is running or not, just make sure that Yggmail is pointing at the right database file or that you are in the right working directory).
* `-passwordHash` — Like `-password` however this sets what must be directly in the database. This assumes you passed a bcrypt hash
## Notes

View File

@@ -32,6 +32,8 @@ import (
"github.com/neilalexander/yggmail/internal/storage/sqlite3"
"github.com/neilalexander/yggmail/internal/transport"
"github.com/neilalexander/yggmail/internal/utils"
"golang.org/x/crypto/bcrypt"
)
type peerAddrList []string
@@ -57,6 +59,7 @@ func main() {
multicast := flag.Bool("multicast", false, "Connect to Yggdrasil peers on your LAN")
mcastregexp := flag.String("mcastregexp", ".*", "Regexp for multicast")
password := flag.Bool("password", false, "Set a new IMAP/SMTP password")
passwordhash := flag.String("passwordhash", "", "Set a new IMAP/SMTP password (hash)")
flag.Var(&peerAddrs, "peer", "Connect to a specific Yggdrasil static peer (this option can be given more than once)")
flag.Parse()
@@ -125,7 +128,16 @@ func main() {
log.Println("The supplied passwords do not match")
os.Exit(1)
}
if err := storage.ConfigSetPassword(strings.TrimSpace(string(password1))); err != nil {
// trim away whitespace of UTF-8 bytes now as string
finalPassword := strings.TrimSpace(string(password1))
// perform hash
hash, err := bcrypt.GenerateFromPassword([]byte(finalPassword), bcrypt.DefaultCost)
if err != nil {
log.Printf("bcrypt.GenerateFromPassword: %v\n", err)
os.Exit(1)
} else if err := storage.ConfigSetPassword(string(hash)); err != nil {
log.Println("Failed to set password:", err)
os.Exit(1)
}
@@ -133,6 +145,24 @@ func main() {
log.Println("Password for IMAP and SMTP has been updated!")
os.Exit(0)
case passwordhash != nil && *passwordhash != "":
var hash string = strings.TrimSpace(*passwordhash);
if len(hash) == 0 {
log.Println("Password hash cannot be blank");
os.Exit(1);
}
log.Printf("Using password hash: '%v'\n", hash);
if _, err := bcrypt.Cost(([]byte)(hash)); err != nil {
log.Printf("The provided hash is invalid %v\n", err);
os.Exit(1);
} else if err := storage.ConfigSetPassword(hash); err != nil {
log.Println("Failed to set password: ", err);
os.Exit(1)
}
log.Println("Password for IMAP and SMTP has been updated!")
case (multicast == nil || !*multicast) && len(peerAddrs) == 0:
log.Printf("You must specify either -peer, -multicast or both!")
os.Exit(0)

View File

@@ -74,12 +74,8 @@ func (t *TableConfig) ConfigSet(key, value string) error {
})
}
func (t *TableConfig) ConfigSetPassword(password string) error {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return fmt.Errorf("bcrypt.GenerateFromPassword: %w", err)
}
return t.ConfigSet("password", string(hash))
func (t *TableConfig) ConfigSetPassword(passwordHash string) error {
return t.ConfigSet("password", passwordHash)
}
func (t *TableConfig) ConfigTryPassword(password string) (bool, error) {