xref: /freebsd/stand/lua/password.lua (revision 0957b409)
1--
2-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3--
4-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
5-- Copyright (C) 2018 Kyle Evans <kevans@FreeBSD.org>
6-- All rights reserved.
7--
8-- Redistribution and use in source and binary forms, with or without
9-- modification, are permitted provided that the following conditions
10-- are met:
11-- 1. Redistributions of source code must retain the above copyright
12--    notice, this list of conditions and the following disclaimer.
13-- 2. Redistributions in binary form must reproduce the above copyright
14--    notice, this list of conditions and the following disclaimer in the
15--    documentation and/or other materials provided with the distribution.
16--
17-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27-- SUCH DAMAGE.
28--
29-- $FreeBSD$
30--
31
32local core = require("core")
33local screen = require("screen")
34
35local password = {}
36
37local INCORRECT_PASSWORD = "loader: incorrect password"
38-- Asterisks as a password mask
39local show_password_mask = false
40local twiddle_chars = {"/", "-", "\\", "|"}
41local screen_setup = false
42
43-- Module exports
44function password.read(prompt_length)
45	local str = ""
46	local twiddle_pos = 1
47
48	local function draw_twiddle()
49		printc(twiddle_chars[twiddle_pos])
50		-- Reset cursor to just after the password prompt
51		screen.setcursor(prompt_length + 2, screen.default_y)
52		twiddle_pos = (twiddle_pos % #twiddle_chars) + 1
53	end
54
55	-- Space between the prompt and any on-screen feedback
56	printc(" ")
57	while true do
58		local ch = io.getchar()
59		if ch == core.KEY_ENTER then
60			break
61		end
62		if ch == core.KEY_BACKSPACE or ch == core.KEY_DELETE then
63			if #str > 0 then
64				if show_password_mask then
65					printc("\008 \008")
66				else
67					draw_twiddle()
68				end
69				str = str:sub(1, #str - 1)
70			end
71		else
72			if show_password_mask then
73				printc("*")
74			else
75				draw_twiddle()
76			end
77			str = str .. string.char(ch)
78		end
79	end
80	return str
81end
82
83function password.check()
84	-- pwd is optionally supplied if we want to check it
85	local function doPrompt(prompt, pwd)
86		local attempts = 1
87
88		local function clear_incorrect_text_prompt()
89			printc("\r" .. string.rep(" ", #INCORRECT_PASSWORD))
90		end
91
92		if not screen_setup then
93			screen.clear()
94			screen.defcursor()
95			screen_setup = true
96		end
97
98		while true do
99			if attempts > 1 then
100				clear_incorrect_text_prompt()
101			end
102			screen.defcursor()
103			printc(prompt)
104			local read_pwd = password.read(#prompt)
105			if pwd == nil or pwd == read_pwd then
106				-- Clear the prompt + twiddle
107				printc(string.rep(" ", #prompt + 5))
108				return read_pwd
109			end
110			printc("\n" .. INCORRECT_PASSWORD)
111			attempts = attempts + 1
112			loader.delay(3*1000*1000)
113		end
114	end
115	local function compare(prompt, pwd)
116		if pwd == nil then
117			return
118		end
119		doPrompt(prompt, pwd)
120	end
121
122	local boot_pwd = loader.getenv("bootlock_password")
123	compare("Bootlock password:", boot_pwd)
124
125	local geli_prompt = loader.getenv("geom_eli_passphrase_prompt")
126	if geli_prompt ~= nil and geli_prompt:lower() == "yes" then
127		local passphrase = doPrompt("GELI Passphrase:")
128		loader.setenv("kern.geom.eli.passphrase", passphrase)
129	end
130
131	local pwd = loader.getenv("password")
132	if pwd ~= nil then
133		core.autoboot()
134	end
135	compare("Loader password:", pwd)
136end
137
138return password
139