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() { // Checks usage if len(os.Args) != 2 { fmt.Println("Usage: hostux_check_credentials [database.sqlite]") os.Exit(2) } // Opens the SQLite database dbFile := os.Args[1] db, err := sql.Open("sqlite3", dbFile) if err != nil { fmt.Println("Error opening database:", err) os.Exit(2) } defer db.Close() // Reads stdin input (email address, password, each ends with \n) var emailAddress, password string fmt.Println("Enter email address:") fmt.Scanln(&emailAddress) fmt.Println("Enter password:") 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 { fmt.Println("Database error: ", err) 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 { fmt.Println("Database error: ", err) 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 { fmt.Println("Database error: ", err) os.Exit(2) } fmt.Println("Authentication successful") 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 fmt.Println("Authentication failed: account not found or password incorrect") os.Exit(1) }