Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
F
fdeunlock
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Insights
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Security & Compliance
Security & Compliance
Dependency List
Packages
Packages
Container Registry
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Robin Schneider
fdeunlock
Commits
34ce0c87
Verified
Commit
34ce0c87
authored
Mar 30, 2017
by
Robin Schneider
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rework how the config is handled and make `diff_command` configurable
parent
b52c5fc5
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
86 additions
and
56 deletions
+86
-56
docs/configuration.rst
docs/configuration.rst
+5
-5
docs/host_checkers.rst
docs/host_checkers.rst
+16
-1
fdeunlock/checker.py
fdeunlock/checker.py
+35
-28
fdeunlock/fdeunlock.py
fdeunlock/fdeunlock.py
+23
-21
fdeunlock/helpers.py
fdeunlock/helpers.py
+7
-1
No files found.
docs/configuration.rst
View file @
34ce0c87
...
...
@@ -28,11 +28,11 @@ Each host has it’s own section. Supported options inside sections:
The command can make use of the following tokens, which are expanded at
runtime:
* ``
{originalhost}
``, hostname as it was specified on the command-line.
* ``
{host}
``, target hostname, after any substitution by ssh.
* ``
{ssh_port}
``, SSH port.
* ``
{hostname}
``, hostname without domain.
* ``
{domain}
``, domain of the host.
* ``
%(originalhost)s
``, hostname as it was specified on the command-line.
* ``
%(host)s
``, target hostname, after any substitution by ssh.
* ``
%(ssh_port)s
``, SSH port.
* ``
%(hostname)s
``, hostname without domain.
* ``
%(domain)s
``, domain of the host.
``start_command_shell``
Boolean determining if :ref:`config_start_command <fdeunlock__ref_config_start_command>`
...
...
docs/host_checkers.rst
View file @
34ce0c87
...
...
@@ -62,7 +62,22 @@ SSH based checkers
[DEFAULT]
additional_checksum_commands =
uname -a
dd if=/dev/vda bs=512 count=1 | sha512sum -
dmesg | egrep '(DMI:|Command line:|Booting paravirtualized kernel on bare hardware)' | sed 's/^\[\s*[[:digit:].]\+\]\s*//;'
cat /sys/devices/virtual/dmi/id/board_serial /sys/devices/virtual/dmi/id/product_uuid
ls /dev/disk/by-id/
grep '^MemTotal:' /proc/meminfo
dd if=/dev/sda bs=512 count=1 | sha512sum -
The ``diff_command`` configuration option can be used to set a different text
diffing program than the default `diff`.
Comparison is run on your local machine so note that the diffing program is
exposed to untrusted input.
Example:
.. code-block:: ini
[DEFAULT]
diff_command = git diff --color-words --no-index
Proper remote attestation (Trusted Computing) should be implemented.
Feel free to add supported for this to FDEunlock :-)
...
...
fdeunlock/checker.py
View file @
34ce0c87
...
...
@@ -106,24 +106,29 @@ class LinkLayerAddressChecker(NetworkBasedChecker):
' '
.
join
(
ip_command
)))
neigh_status
=
proc
.
stdout
.
read
()
.
decode
(
'utf-8'
)
.
strip
()
.
split
()
link_layer_ind
=
neigh_status
.
index
(
'lladdr'
)
if
link_layer_ind
==
-
1
:
raise
Exception
(
"lladdr in `ip` command output not found."
)
untrusted_link_layer_address
=
neigh_status
[
link_layer_ind
+
1
]
if
not
re
.
match
(
r"^[0-9a-f]{2}([-:])[0-9a-f]{2}(\1[0-9a-f]{2}){4}$"
,
untrusted_link_layer_address
.
lower
()):
raise
Exception
(
"MAC address not valid: {}."
.
format
(
untrusted_link_layer_address
))
LOG
.
debug
(
"Link layer address: {}"
.
format
(
untrusted_link_layer_address
))
host
=
self
.
_unlocker
.
_original_host
link_layer_addresses
=
self
.
_unlocker
.
_properties
.
get
(
host
,
'link_layer_addresses'
,
fallback
=
''
)
try
:
link_layer_ind
=
neigh_status
.
index
(
'lladdr'
)
except
ValueError
:
link_layer_ind
=
-
1
untrusted_link_layer_address
=
'Unable to read link layer address'
LOG
.
debug
(
untrusted_link_layer_address
)
else
:
untrusted_link_layer_address
=
neigh_status
[
link_layer_ind
+
1
]
if
not
re
.
match
(
r"^[0-9a-f]{2}([-:])[0-9a-f]{2}(\1[0-9a-f]{2}){4}$"
,
untrusted_link_layer_address
.
lower
()):
raise
Exception
(
"MAC address not valid: {}."
.
format
(
untrusted_link_layer_address
))
LOG
.
debug
(
"Link layer address: {}"
.
format
(
untrusted_link_layer_address
))
original_host
=
self
.
_unlocker
.
_original_host
link_layer_addresses
=
self
.
_unlocker
.
_properties
.
get
(
original_host
,
'link_layer_addresses'
,
fallback
=
''
)
link_layer_addresses
=
[
a
.
strip
()
for
a
in
link_layer_addresses
.
split
(
'
\n
'
)
if
a
]
if
len
(
link_layer_addresses
)
==
0
:
LOG
.
info
(
"No link layer addresses found to compare to. Trusting the current once (TOFU)."
)
self
.
_unlocker
.
_properties
.
set
(
host
,
'link_layer_addresses'
,
untrusted_link_layer_address
)
self
.
_unlocker
.
_properties
.
set
(
original_
host
,
'link_layer_addresses'
,
untrusted_link_layer_address
)
return
True
elif
untrusted_link_layer_address
in
link_layer_addresses
:
LOG
.
info
(
"Link layer address matches the trusted once."
)
LOG
.
info
(
"Link layer address matches the trusted once: {}"
.
format
(
untrusted_link_layer_address
))
return
True
self
.
_link_layer_addresses
=
link_layer_addresses
...
...
@@ -141,9 +146,9 @@ class LinkLayerAddressChecker(NetworkBasedChecker):
))
answer
=
input
(
"Choose one of 'save', 'ignore' (for current run) or anything else to exit: "
)
if
answer
==
'save'
:
host
=
self
.
_unlocker
.
_original_host
original_
host
=
self
.
_unlocker
.
_original_host
self
.
_unlocker
.
_properties
.
set
(
host
,
original_
host
,
'link_layer_addresses'
,
self
.
_untrusted_link_layer_address
,
)
...
...
@@ -168,13 +173,13 @@ class ChecksumChecker(SshBasedChecker):
os
.
makedirs
(
self
.
_bin_dir
,
exist_ok
=
True
)
# pylint: disable=unexpected-keyword-arg
os
.
makedirs
(
self
.
_checksum_dir
,
exist_ok
=
True
)
# pylint: disable=unexpected-keyword-arg
host
=
self
.
_unlocker
.
_original_host
original_
host
=
self
.
_unlocker
.
_original_host
self
.
_checksum_file
=
os
.
path
.
join
(
self
.
_checksum_dir
,
'{}_initramfs.list'
.
format
(
host
))
'{}_initramfs.list'
.
format
(
original_
host
))
self
.
_untrusted_checksum_file
=
os
.
path
.
join
(
self
.
_checksum_dir
,
'{}_untrusted_initramfs.list'
.
format
(
host
))
'{}_untrusted_initramfs.list'
.
format
(
original_
host
))
def
check
(
self
,
shell
=
None
,
**
kwargs
):
...
...
@@ -188,8 +193,8 @@ class ChecksumChecker(SshBasedChecker):
shell
.
copy_to_remote
(
self
.
checksum_prog
,
'/root/hashdeep'
)
shell
.
run_command
(
'chmod 500 /root/hashdeep'
)
host
=
self
.
_unlocker
.
_original_host
additional_checksum_commands
=
self
.
_unlocker
.
_cfg
.
get
(
host
,
'additional_checksum_commands'
,
fallback
=
'
'
)
original_
host
=
self
.
_unlocker
.
_original_host
additional_checksum_commands
=
self
.
_unlocker
.
_cfg
.
get
(
original_host
,
'additional_checksum_commands
'
)
additional_checksum_commands
=
[
a
.
strip
()
for
a
in
additional_checksum_commands
.
split
(
'
\n
'
)
if
a
]
with
open
(
self
.
_untrusted_checksum_file
,
'w'
)
as
untrusted_checksum_fh
:
...
...
@@ -206,8 +211,8 @@ class ChecksumChecker(SshBasedChecker):
' -not -path "/run/*"'
' -print0'
# 'find /root -type f -print0'
' | sort -z'
' | xargs -0 /root/hashdeep -r -c md5,sha1,sha256 | grep -v "^[#
%
]"'
#
' | sort -z'
' | xargs -0 /root/hashdeep -r -c md5,sha1,sha256 | grep -v "^[#
%
]"
| sort -t "," -k4
'
)
shell
.
prompt
()
for
cmd
in
additional_checksum_commands
:
...
...
@@ -237,7 +242,9 @@ class ChecksumChecker(SshBasedChecker):
def
update
(
self
):
LOG
.
info
(
"Deviation to trusted checksums:"
)
subprocess
.
call
([
'diff'
,
self
.
_checksum_file
,
self
.
_untrusted_checksum_file
])
original_host
=
self
.
_unlocker
.
_original_host
diff_command
=
self
.
_unlocker
.
_cfg
.
get
(
original_host
,
'diff_command'
)
.
split
(
' '
)
subprocess
.
call
(
diff_command
+
[
self
.
_checksum_file
,
self
.
_untrusted_checksum_file
])
answer
=
input
(
"Choose one of 'save', 'ignore' (for current run) or anything else to exit: "
)
if
LOG
.
isEnabledFor
(
logging
.
DEBUG
):
hexdump
(
answer
.
encode
(
'utf-8'
))
...
...
@@ -277,12 +284,12 @@ class AuthenticatedLatencyChecker(SshBasedChecker):
untrusted_latency
=
end
-
start
LOG
.
info
(
"Latency to execute a command over SSH and get the response back: {:.4f} s"
.
format
(
untrusted_latency
))
host
=
self
.
_unlocker
.
_original_host
latency
=
self
.
_unlocker
.
_properties
.
getfloat
(
host
,
'authenticated_latency'
,
fallback
=-
1.0
)
deviation
=
self
.
_unlocker
.
_cfg
.
get
(
host
,
'authenticated_latency_deviation'
,
fallback
=
0.01
)
original_
host
=
self
.
_unlocker
.
_original_host
latency
=
self
.
_unlocker
.
_properties
.
getfloat
(
original_
host
,
'authenticated_latency'
,
fallback
=-
1.0
)
deviation
=
self
.
_unlocker
.
_cfg
.
get
float
(
original_host
,
'authenticated_latency_deviation'
)
if
latency
==
-
1.0
:
LOG
.
info
(
"No latency found to compare to. Trusting the current one (TOFU)."
)
self
.
_unlocker
.
_properties
.
set
(
host
,
'authenticated_latency'
,
str
(
untrusted_latency
))
self
.
_unlocker
.
_properties
.
set
(
original_
host
,
'authenticated_latency'
,
str
(
untrusted_latency
))
return
True
elif
latency
-
deviation
<=
untrusted_latency
and
untrusted_latency
<=
latency
+
deviation
:
LOG
.
info
(
"Latency is within the boundaries."
)
...
...
@@ -301,9 +308,9 @@ class AuthenticatedLatencyChecker(SshBasedChecker):
))
answer
=
input
(
"Choose one of 'save', 'ignore' (for current run) or anything else to exit: "
)
if
answer
==
'save'
:
host
=
self
.
_unlocker
.
_original_host
original_
host
=
self
.
_unlocker
.
_original_host
self
.
_unlocker
.
_properties
.
set
(
host
,
original_
host
,
'authenticated_latency'
,
str
(
self
.
_untrusted_latency
),
)
...
...
fdeunlock/fdeunlock.py
View file @
34ce0c87
...
...
@@ -44,22 +44,31 @@ class FdeUnlock(object):
self
.
_original_host
=
host
if
host
not
in
self
.
_properties
:
self
.
_properties
.
add_section
(
host
)
if
not
self
.
_cfg
.
has_section
(
self
.
_original_host
):
self
.
_cfg
.
add_section
(
self
.
_original_host
)
if
not
self
.
_properties
.
has_section
(
self
.
_original_host
):
self
.
_properties
.
add_section
(
self
.
_original_host
)
ssh_host_cfg
=
self
.
_ssh_cfg
.
lookup
(
host
)
ssh_host_cfg
=
self
.
_ssh_cfg
.
lookup
(
self
.
_original_
host
)
self
.
_address_family
=
self
.
_cfg
.
get
(
host
,
'address_family'
,
self
.
_original_
host
,
'address_family'
,
fallback
=
ssh_host_cfg
.
get
(
'addressfamily'
,
'any'
))
start_command
=
self
.
_cfg
.
get
(
host
,
'start_command'
,
fallback
=
None
)
start_command_shell
=
self
.
_cfg
.
getboolean
(
host
,
'start_command_shell'
,
fallback
=
False
)
port
=
ssh_host_cfg
.
get
(
'port'
,
22
)
host
=
ssh_host_cfg
.
get
(
'hostname'
,
host
)
host
=
ssh_host_cfg
.
get
(
'hostname'
,
self
.
_original_
host
)
LOG
.
debug
(
"SSH options: host: {}, port: {}"
.
format
(
host
,
port
,
self
.
_original_
host
,
port
,
))
start_command
=
self
.
_cfg
.
get
(
self
.
_original_host
,
'start_command'
,
vars
=
{
'originalhost'
:
self
.
_original_host
,
'host'
:
host
,
'ssh_port'
:
port
,
'hostname'
:
self
.
_original_host
.
split
(
'.'
)[
0
],
'domain'
:
'.'
.
join
(
self
.
_original_host
.
split
(
'.'
)[
1
:]),
},
)
while
True
:
if
self
.
_is_reachable
(
host
):
...
...
@@ -103,17 +112,10 @@ class FdeUnlock(object):
if
start_command
is
None
:
LOG
.
info
(
"Host offline. Waiting …"
)
else
:
command
=
start_command
.
format
(
originalhost
=
self
.
_original_host
,
host
=
host
,
ssh_port
=
port
,
hostname
=
self
.
_original_host
.
split
(
'.'
)[
0
],
domain
=
'.'
.
join
(
self
.
_original_host
.
split
(
'.'
)[
1
:]),
)
LOG
.
info
(
"Host offline. Attempting to start using: {}"
.
format
(
command
))
LOG
.
info
(
"Host offline. Attempting to start using: {}"
.
format
(
start_command
))
returncode
=
subprocess
.
call
(
command
.
split
(
' '
),
shell
=
s
tart_command_shell
start_
command
.
split
(
' '
),
shell
=
s
elf
.
_cfg
.
getboolean
(
self
.
_original_host
,
'start_command_shell'
),
)
LOG
.
info
(
"Start command returned with: {}"
.
format
(
returncode
))
start_command
=
None
...
...
@@ -193,7 +195,7 @@ class FdeUnlock(object):
checker
=
checker_class
(
self
)
violation_message
=
(
"
Check
{} report an unrecoverable violation causing"
"{} report an unrecoverable violation causing"
" FDEunlock to stop at this point."
.
format
(
checker_class
.
__name__
))
if
not
checker
.
check
(
shell
=
shell
):
if
not
checker
.
update
():
...
...
fdeunlock/helpers.py
View file @
34ce0c87
...
...
@@ -54,7 +54,13 @@ def read_config():
cfg_files
.
append
(
os
.
path
.
join
(
config_dir
,
'config.cfg'
))
for
cfg_file
in
cfg_files
:
ensure_permissions
(
cfg_file
,
0o0600
)
cfg
=
configparser
.
ConfigParser
()
cfg
=
configparser
.
ConfigParser
(
defaults
=
{
'start_command'
:
None
,
'start_command_shell'
:
'False'
,
'additional_checksum_commands'
:
''
,
'diff_command'
:
'diff'
,
'authenticated_latency_deviation'
:
'0.01'
,
})
cfg
.
read
(
cfg_files
)
LOG
.
debug
(
"Read configuration files: {}"
.
format
(
cfg_files
))
return
cfg
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment