Proof passphrase.h. Fix helper for empty output, fortify against ENOMEM and mmap(2) error

This commit is contained in:
наб 2021-11-19 00:37:08 +01:00
parent 8df40d5506
commit ea0a5bd52e
No known key found for this signature in database
GPG Key ID: BCFD0B018D2658F1
3 changed files with 38 additions and 23 deletions

View File

@ -11,7 +11,7 @@
[ -s /run/tzpfms-err ] && plymouth display-message --text="$(cat /run/tzpfms-err)"
elif [ -e /run/systemd/system ] && command -v systemd-ask-password > /dev/null; then # --no-tty matches zfs and actually works
# shellcheck disable=SC2016
TZPFMS_PASSPHRASE_HELPER='exec systemd-ask-password --no-tty --id="tzpfms:$2" "$1: "' "$@" 2>/run/tzpfms-err; ret="$?"
TZPFMS_PASSPHRASE_HELPER='exec systemd-ask-password --no-tty --id="tzpfms:$2" "$1:"' "$@" 2>/run/tzpfms-err; ret="$?"
else
# Mimic /scripts/zfs#decrypt_fs(): setting "printk" temporarily to "7" will allow prompt even if kernel option "quiet"
read -r printk _ < /proc/sys/kernel/printk

View File

@ -3,24 +3,33 @@
.Sh ENVIRONMENT VARIABLES
.Bl -tag -compact -width "TZPFMS"
.It Ev TZPFMS_PASSPHRASE_HELPER
If set and nonempty, will be run as
.Dl Pa /bin/ Ns Nm sh Fl c Li \&"$TZPFMS_PASSPHRASE_HELPER" \&"$TZPFMS_PASSPHRASE_HELPER" Qo Ar prepared prompt Qc Qo Ar target Qc Qo Oo Li new Oc Qc Qo Oo Li again Oc Qc
to provide a passphrase, instead of reading from the standard input.
If set and nonempty, will be run via
.Pa /bin/ Ns Nm sh Fl c
.\"Li \&"$TZPFMS_PASSPHRASE_HELPER" \&"$TZPFMS_PASSPHRASE_HELPER" Qo Ar prepared prompt Qc Qo Ar target Qc Qo Oo Li new Oc Qc Qo Oo Li again Oc Qc
to provide a passphrase, instead of reading from the standard input stream.
.Pp
The standard output stream of the helper is tied to an anonymous file and used in its entirety as the passphrase, except for a trailing new-line, if any.
The second argument contains either the dataset name or the element of the TPM hierarchy.
The third argument is
.Li new
if this is for a new passphrase, and the fourth is
.Li again
if it's the second prompt for that passphrase.
The first argument already contains all of this information, as a pre-formatted noun phrase.
The arguments are:
.Bl -enum -compact -offset "@@" -width "@"
.It
Pre-formatted noun phrase with all the information below
.It
Either the dataset name or the element of the TPM hierarchy.
.It
.Qq new
if this is for a new passphrase
.It
.Qq again
if it's the second prompt for that passphrase
.El
.Pp
If the helper doesn't exist
.Pq the shell exits with Sy 127 ,
a diagnostic is issued and the normal prompt is used as fall-back.
If it fails for any other reason, the prompting is aborted.
.Pp
An example value would be:
.No ' Ns Nm systemd-ask-password Fl -id Ns Li = Ns Qo Li tzpfms:\& Ns Ar $2 Qc Qo Ar $1 Ns Li ": " Qc Ns ' .
An example value facilitating
.Xr systemd 1
integration would be:
.No ' Ns Ic exec Nm systemd-ask-password Fl -id Ns Li = Ns Qo Li tzpfms:\& Ns Ar $2 Qc Qo Ar $1 Ns Li ": " Qc Ns ' .
.El

View File

@ -107,19 +107,25 @@ static int get_key_material_helper(const char * helper, const char * whom, bool
case 0:
struct stat sb;
fstat(outfd, &sb);
if(auto out = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, outfd, 0); out != MAP_FAILED) {
if(!sb.st_size) // unmmappable
return buf = nullptr, len_out = 0, 0;
else if(auto out = static_cast<uint8_t *>(mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, outfd, 0)); out != MAP_FAILED) {
quickscope_wrapper out_deleter{[=] { munmap(out, sb.st_size); }};
buf = static_cast<uint8_t *>(malloc(sb.st_size)); // TODO:if failed
len_out = sb.st_size;
memcpy(buf, out, sb.st_size);
// Trim ending newline, if any
if(buf[len_out - 1] == '\n')
buf[--len_out] = '\0';
if(out[len_out - 1] == '\n') // Trim ending newline, if any
--len_out;
if(!len_out)
buf = nullptr;
else {
if(!(buf = static_cast<uint8_t *>(malloc(len_out))))
len_out = 0, (void)TRY("allocate passphrase", -1);
memcpy(buf, out, len_out);
}
return 0;
} else
; // error
TRY("read back passphrase", -1);
case 127: // enoent, error already written by shell or child
case 127: // ENOENT, error already written by shell or child
return -1;
default:
@ -215,8 +221,8 @@ static int get_key_material_raw(const char * whom, bool again, bool newkey, uint
static int get_key_material_dispatch(const char * whom, bool again, bool newkey, uint8_t *& buf, size_t & len_out) {
static const char * helper{};
if(!helper)
helper = getenv("TZPFMS_PASSPHRASE_HELPER");
if(helper && *helper) {
helper = getenv("TZPFMS_PASSPHRASE_HELPER") ?: "";
if(*helper) {
if(auto err = get_key_material_helper(helper, whom, again, newkey, buf, len_out); err != -1)
return err;
else