xref: /freebsd/stand/lua/password.lua (revision 9636a14538f56ad9b59c9d77abd9a06d0510d7b9)
1088b4f5fSWarner Losh--
2*4d846d26SWarner Losh-- SPDX-License-Identifier: BSD-2-Clause
372e39d71SKyle Evans--
4088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
5e12ff891SKyle Evans-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
6088b4f5fSWarner Losh-- All rights reserved.
7088b4f5fSWarner Losh--
8088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
9088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
10088b4f5fSWarner Losh-- are met:
11088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
12088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
13088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
14088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
15088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
16088b4f5fSWarner Losh--
17088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27088b4f5fSWarner Losh-- SUCH DAMAGE.
28088b4f5fSWarner Losh--
29088b4f5fSWarner Losh
30aedd6be5SKyle Evanslocal core = require("core")
31aedd6be5SKyle Evanslocal screen = require("screen")
32088b4f5fSWarner Losh
33aedd6be5SKyle Evanslocal password = {}
340901ba3aSKyle Evans
356c8e9a84SEdward Tomasz Napieralalocal INCORRECT_PASSWORD = "loader: incorrect password"
36a6f1506fSKyle Evans-- Asterisks as a password mask
37a6f1506fSKyle Evanslocal show_password_mask = false
38a6f1506fSKyle Evanslocal twiddle_chars = {"/", "-", "\\", "|"}
39d61897abSKyle Evanslocal screen_setup = false
40c8518398SKyle Evans
41c206dd4dSKyle Evanslocal function setup_screen()
42c206dd4dSKyle Evans	screen.clear()
43c206dd4dSKyle Evans	screen.defcursor()
44c206dd4dSKyle Evans	screen_setup = true
45c206dd4dSKyle Evansend
46c206dd4dSKyle Evans
47b5746545SKyle Evans-- Module exports
480901ba3aSKyle Evansfunction password.read(prompt_length)
49aedd6be5SKyle Evans	local str = ""
508620ae01SKyle Evans	local twiddle_pos = 1
51088b4f5fSWarner Losh
52a6f1506fSKyle Evans	local function draw_twiddle()
536c8e9a84SEdward Tomasz Napierala		printc(twiddle_chars[twiddle_pos])
54ed1d0954SKyle Evans		-- Reset cursor to just after the password prompt
55ed1d0954SKyle Evans		screen.setcursor(prompt_length + 2, screen.default_y)
56a6f1506fSKyle Evans		twiddle_pos = (twiddle_pos % #twiddle_chars) + 1
57a6f1506fSKyle Evans	end
58a6f1506fSKyle Evans
59a6f1506fSKyle Evans	-- Space between the prompt and any on-screen feedback
60223e9874SKyle Evans	printc(" ")
61a5e2e5c7SKyle Evans	while true do
62e2df27e3SKyle Evans		local ch = io.getchar()
639f71d421SKyle Evans		if ch == core.KEY_ENTER then
64aedd6be5SKyle Evans			break
65088b4f5fSWarner Losh		end
669f71d421SKyle Evans		if ch == core.KEY_BACKSPACE or ch == core.KEY_DELETE then
67cb4fbe4eSKyle Evans			if #str > 0 then
68a6f1506fSKyle Evans				if show_password_mask then
69223e9874SKyle Evans					printc("\008 \008")
70a6f1506fSKyle Evans				else
71a6f1506fSKyle Evans					draw_twiddle()
72a6f1506fSKyle Evans				end
73cb4fbe4eSKyle Evans				str = str:sub(1, #str - 1)
74088b4f5fSWarner Losh			end
75088b4f5fSWarner Losh		else
76a6f1506fSKyle Evans			if show_password_mask then
77223e9874SKyle Evans				printc("*")
78a6f1506fSKyle Evans			else
79a6f1506fSKyle Evans				draw_twiddle()
80a6f1506fSKyle Evans			end
81aedd6be5SKyle Evans			str = str .. string.char(ch)
82088b4f5fSWarner Losh		end
83a5e2e5c7SKyle Evans	end
84aedd6be5SKyle Evans	return str
85088b4f5fSWarner Loshend
86088b4f5fSWarner Losh
87088b4f5fSWarner Loshfunction password.check()
8811cac431SKyle Evans	-- pwd is optionally supplied if we want to check it
89322a2dddSKyle Evans	local function doPrompt(prompt, pwd)
900901ba3aSKyle Evans		local attempts = 1
910901ba3aSKyle Evans
920901ba3aSKyle Evans		local function clear_incorrect_text_prompt()
936c8e9a84SEdward Tomasz Napierala			printc("\r" .. string.rep(" ", #INCORRECT_PASSWORD))
940901ba3aSKyle Evans		end
950901ba3aSKyle Evans
96d61897abSKyle Evans		if not screen_setup then
97c206dd4dSKyle Evans			setup_screen()
98d61897abSKyle Evans		end
99d61897abSKyle Evans
1009f71d421SKyle Evans		while true do
1016c8e9a84SEdward Tomasz Napierala			if attempts > 1 then
1026c8e9a84SEdward Tomasz Napierala				clear_incorrect_text_prompt()
1036c8e9a84SEdward Tomasz Napierala			end
1040901ba3aSKyle Evans			screen.defcursor()
105223e9874SKyle Evans			printc(prompt)
1060901ba3aSKyle Evans			local read_pwd = password.read(#prompt)
1079f71d421SKyle Evans			if pwd == nil or pwd == read_pwd then
1080901ba3aSKyle Evans				-- Clear the prompt + twiddle
109223e9874SKyle Evans				printc(string.rep(" ", #prompt + 5))
110aedd6be5SKyle Evans				return read_pwd
111088b4f5fSWarner Losh			end
112223e9874SKyle Evans			printc("\n" .. INCORRECT_PASSWORD)
1130901ba3aSKyle Evans			attempts = attempts + 1
114aedd6be5SKyle Evans			loader.delay(3*1000*1000)
115088b4f5fSWarner Losh		end
11611cac431SKyle Evans	end
11711cac431SKyle Evans	local function compare(prompt, pwd)
1189f71d421SKyle Evans		if pwd == nil then
119aedd6be5SKyle Evans			return
12011cac431SKyle Evans		end
121322a2dddSKyle Evans		doPrompt(prompt, pwd)
122088b4f5fSWarner Losh	end
123088b4f5fSWarner Losh
124aedd6be5SKyle Evans	local boot_pwd = loader.getenv("bootlock_password")
1256c8e9a84SEdward Tomasz Napierala	compare("Bootlock password:", boot_pwd)
126088b4f5fSWarner Losh
127aedd6be5SKyle Evans	local geli_prompt = loader.getenv("geom_eli_passphrase_prompt")
1289f71d421SKyle Evans	if geli_prompt ~= nil and geli_prompt:lower() == "yes" then
129322a2dddSKyle Evans		local passphrase = doPrompt("GELI Passphrase:")
130aedd6be5SKyle Evans		loader.setenv("kern.geom.eli.passphrase", passphrase)
13111cac431SKyle Evans	end
13211cac431SKyle Evans
133aedd6be5SKyle Evans	local pwd = loader.getenv("password")
1349f71d421SKyle Evans	if pwd ~= nil then
135aedd6be5SKyle Evans		core.autoboot()
1360ab68e92SCyrus Rahman		loader.setenv("autoboot_delay", "NO")
137c206dd4dSKyle Evans		-- The autoboot sequence was interrupted, so we'll need to
138c206dd4dSKyle Evans		-- prompt for a password.  Put the screen back into a known
139c206dd4dSKyle Evans		-- good state, otherwise we're drawing back a couple lines
140c206dd4dSKyle Evans		-- in the middle of other text.
141c206dd4dSKyle Evans		setup_screen()
142088b4f5fSWarner Losh	end
1436c8e9a84SEdward Tomasz Napierala	compare("Loader password:", pwd)
144088b4f5fSWarner Loshend
145088b4f5fSWarner Losh
146aedd6be5SKyle Evansreturn password
147