diff --git a/README.md b/README.md index 95f49ff..303b71f 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/cmd/yggmail/main.go b/cmd/yggmail/main.go index f361b02..777e62d 100644 --- a/cmd/yggmail/main.go +++ b/cmd/yggmail/main.go @@ -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) diff --git a/internal/storage/sqlite3/table_config.go b/internal/storage/sqlite3/table_config.go index 8008b9a..721a638 100644 --- a/internal/storage/sqlite3/table_config.go +++ b/internal/storage/sqlite3/table_config.go @@ -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) {