2024-02-27 14:39:34 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exit codes:
|
|
|
|
* 0: authentication successful
|
|
|
|
* 1: authentication failed (email address not found or password mismatch
|
|
|
|
* 2: other error
|
|
|
|
*/
|
|
|
|
func main() {
|
2024-02-28 16:15:57 +01:00
|
|
|
// Opens the SQLite database
|
|
|
|
dbFile, dbFileSet := os.LookupEnv("HOSTUX_EMAIL_DATABASE_SQLITE")
|
|
|
|
if !dbFileSet {
|
|
|
|
logMessage("Environment variable HOSTUX_EMAIL_DATABASE_SQLITE must be set.")
|
2024-02-27 14:39:34 +01:00
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
|
|
|
|
db, err := sql.Open("sqlite3", dbFile)
|
|
|
|
if err != nil {
|
2024-02-28 16:15:57 +01:00
|
|
|
logMessage("Error opening database:", err)
|
2024-02-27 14:39:34 +01:00
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
// Reads stdin input (email address, password, each ends with \n)
|
|
|
|
var emailAddress, password string
|
2024-02-28 16:15:57 +01:00
|
|
|
logMessage("Enter email address:")
|
2024-02-27 14:39:34 +01:00
|
|
|
fmt.Scanln(&emailAddress)
|
2024-02-28 16:15:57 +01:00
|
|
|
logMessage("Enter password:")
|
2024-02-27 14:39:34 +01:00
|
|
|
fmt.Scanln(&password)
|
|
|
|
|
|
|
|
// Finds database records
|
|
|
|
rows, err := db.Query(`SELECT eap.id, eap.password FROM "email-addresses" AS ea
|
|
|
|
LEFT JOIN "email-addresses-passwords" AS eap ON ea.emailAddress = eap.emailAddress
|
|
|
|
WHERE ea.emailAddress = ?`, emailAddress)
|
|
|
|
if err != nil {
|
2024-02-28 16:15:57 +01:00
|
|
|
logMessage("Database error: ", err)
|
2024-02-27 14:39:34 +01:00
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
// Loops through records
|
|
|
|
for rows.Next() {
|
|
|
|
var hashedPassword string
|
|
|
|
var id int
|
|
|
|
|
|
|
|
err := rows.Scan(&id, &hashedPassword)
|
|
|
|
if err != nil {
|
2024-02-28 16:15:57 +01:00
|
|
|
logMessage("Database error: ", err)
|
2024-02-27 14:39:34 +01:00
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compares provided password with database
|
|
|
|
err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
|
|
|
if err == nil {
|
|
|
|
// No error = the passwords match
|
|
|
|
// Update lastAccess time
|
|
|
|
rows.Close()
|
|
|
|
_, err := db.Exec(`UPDATE "email-addresses-passwords"
|
|
|
|
SET lastAccess = ?
|
|
|
|
WHERE id = ?`, time.Now().Format(time.RFC3339), id)
|
|
|
|
|
|
|
|
if err != nil {
|
2024-02-28 16:15:57 +01:00
|
|
|
logMessage("Database error: ", err)
|
2024-02-27 14:39:34 +01:00
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
|
2024-02-28 16:15:57 +01:00
|
|
|
logMessage("Authentication successful")
|
2024-02-27 14:39:34 +01:00
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have not returned by now, it means that:
|
|
|
|
// * either there were no records
|
|
|
|
// * or no record matched the provided password
|
|
|
|
|
2024-02-28 16:15:57 +01:00
|
|
|
logMessage("Authentication failed: account not found or password incorrect")
|
2024-02-27 14:39:34 +01:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
2024-02-28 16:15:57 +01:00
|
|
|
|
|
|
|
func logMessage(args ...interface{}) error {
|
|
|
|
// Print the message to the console
|
|
|
|
fmt.Println(args...)
|
|
|
|
|
|
|
|
// Log to file if required
|
|
|
|
logFile, logFileSet := os.LookupEnv("HOSTUX_EMAIL_CHECKPW_LOGFILE")
|
|
|
|
if logFileSet {
|
|
|
|
// Open the file in append mode, create it if it doesn't exist
|
|
|
|
file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
// Write the message to the file
|
|
|
|
_, err = fmt.Fprintln(file, args...)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|