Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for signons.txt format #36

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 49 additions & 10 deletions firefox_decrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,37 @@ def __iter__(self):
i["encryptedPassword"], i["encType"])


class TextCredentials(Credentials):
def __init__(self, profile, version):
self._version = version
db = os.path.join(profile, "signons{}.txt".format(str(version) if version != 1 else '' ))

super(TextCredentials, self).__init__(db)

def __iter__(self):
with open(self.db) as fh:
LOG.debug("Reading password database in TXT format")

lines = list(map(lambda x: x.strip('\n').strip('\r'), fh.readlines()))

file_id = '#2' + chr(ord('b') + self._version)
assert lines[0] == file_id

record_size = 3 + self._version
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment here detailing that with a new version of signons.txt one additional line with data was added to every record.
Also please add comments with links to http://kb.mozillazine.org/Signons.txt http://kb.mozillazine.org/Signons2.txt and http://kb.mozillazine.org/Signons3.txt for future reference.


i = lines.index('.') + 1
while i < len(lines):
hostname = lines[i]
i += 1
while lines[i] != '.':
encryptedUsername = lines[i+1]
encryptedPassword = lines[i+3]
encType = True
yield hostname, encryptedUsername, encryptedPassword, encType
i += record_size
i += 1


class NSSDecoder(object):
class SECItem(ct.Structure):
"""struct needed to interact with libnss
Expand Down Expand Up @@ -274,7 +305,7 @@ def __init__(self):
def _set_ctypes(self, restype, name, *argtypes):
"""Set input/output types on libnss C functions for automatic type casting
"""
res = getattr(self.NSS, name)
res = getattr(self.NSS if hasattr(self.NSS, name) else self.NSPR, name)
res.restype = restype
res.argtypes = argtypes
setattr(self, "_" + name, res)
Expand Down Expand Up @@ -382,6 +413,9 @@ def load_libnss(self):
# If this succeeds libnss was loaded
self.NSS = self.find_nss(locations, nssname)

if os.name == 'nt' and not (hasattr(self.NSS, "PR_ErrorToName") and hasattr(self.NSS, "PR_ErrorToString")):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is NSPR only needed on Windows. Do we need workarounds on other platforms too?

self.NSPR = self.find_nss(locations, 'nspr4.dll')

def handle_error(self):
"""If an error happens in libnss, handle it and print some debug information
"""
Expand Down Expand Up @@ -612,18 +646,23 @@ def test_password_store(export, pass_cmd):


def obtain_credentials(profile):
"""Figure out which of the 2 possible backend credential engines is available
"""Figure out which of the 3 possible backend credential engines is available
"""
try:
credentials = JsonCredentials(profile)
except NotFoundError:
engines = [
(JsonCredentials, {}),
(SqliteCredentials, {}),
(TextCredentials, {'version': 1}),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order goes from newer to older (JSON is Firefox 32+), minor thing but should we try version 3 before 1 and 2?

(TextCredentials, {'version': 2}),
(TextCredentials, {'version': 3})
]

for credential_type, extra_kwargs in engines:
try:
credentials = SqliteCredentials(profile)
return credential_type(profile, **extra_kwargs)
except NotFoundError:
LOG.error("Couldn't find credentials file (logins.json or signons.sqlite).")
raise Exit(Exit.MISSING_SECRETS)

return credentials
pass
LOG.error("Couldn't find credentials file (logins.json, signons.sqlite or signons[2|3].txt).")
raise Exit(Exit.MISSING_SECRETS)


def export_pass(to_export, pass_cmd, prefix, username_prefix):
Expand Down
14 changes: 14 additions & 0 deletions tests/direct_profile_3.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
. bash_tap_fd.sh

PASSWD=$(get_password)
CMD=$(get_script)
TEST="$(get_test_data)/test_profile_firefox_3/"


diff -u <(echo ${PASSWD} | ${CMD} ${TEST} | grep -C1 doesntexist || kill $$) <(get_user_data "doesntexist")
diff -u <(echo ${PASSWD} | ${CMD} ${TEST} | grep -C1 onemore || kill $$) <(get_user_data "onemore")
diff -u <(echo ${PASSWD} | ${CMD} ${TEST} | grep -C1 cömplex || kill $$) <(get_user_data "complex")
diff -u <(echo ${PASSWD} | ${CMD} ${TEST} | grep -C1 jãmïe || kill $$) <(get_user_data "jamie")

# vim: ai sts=4 et sw=4
15 changes: 15 additions & 0 deletions tests/profile_ini_3.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
. bash_tap_fd.sh

PASSWD=$(get_password)
CMD=$(get_script)
TEST="$(get_test_data)"
PAYLOAD="6\n${PASSWD}"


diff -u <(echo -e ${PAYLOAD} | ${CMD} ${TEST} | grep -C1 doesntexist || kill $$) <(get_user_data "doesntexist")
diff -u <(echo -e ${PAYLOAD} | ${CMD} ${TEST} | grep -C1 onemore || kill $$) <(get_user_data "onemore")
diff -u <(echo -e ${PAYLOAD} | ${CMD} ${TEST} | grep -C1 cömplex || kill $$) <(get_user_data "complex")
diff -u <(echo -e ${PAYLOAD} | ${CMD} ${TEST} | grep -C1 jãmïe || kill $$) <(get_user_data "jamie")

# vim: ai sts=4 et sw=4
1 change: 1 addition & 0 deletions tests/test_data/outputs/list.output
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
3 -> test_profile_firefox_nopassword
4 -> test_profile_firefox_59
5 -> test_profile_firefox_LЮшр
6 -> test_profile_firefox_3
5 changes: 5 additions & 0 deletions tests/test_data/profiles.ini
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ Name=firefox_non-ascii
IsRelative=1
Path=test_profile_firefox_LЮшр

[Profile6]
Name=firefox_3
IsRelative=1
Path=test_profile_firefox_3

Binary file not shown.
Binary file added tests/test_data/test_profile_firefox_3/key3.db
Binary file not shown.
28 changes: 28 additions & 0 deletions tests/test_data/test_profile_firefox_3/signons3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#2e
.
https://github.com
username
MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECFbhr69LxyH6BBBHQfGSDplu5NwpPw4kZP76
*password
MFIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECEwE0utrlss+BCjQ1hxN7W+YKiuMGi0TrIz4+T1G1QxtN/yrG6wjzQwVdGjvHeO0su3F
https://github.com
---
username
MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECF+eN9l5iGY3BBAgWfoOf8FTgM4nHSsuOSv3
*password
MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECH9ZybIXL/kgBBAc5Qo7G0Eyudb6ppFU1TU8
https://github.com
---
username
MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECAymTAzAa9WIBAhwxctjtJCC0g==
*password
MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECCoUA3tP7lpWBDDS6vJm44+kXZC2RpPVF2T6ELuLms94CTP8FaBbAhAQVyV/MOU3J28G9Phc0Dc7Uus=
https://github.com
---
username
MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECOs5BCchP2L/BAgKD9J07QcG1w==
*password
MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECCDhDUOYvMJvBBCqohmc0hayd6BvOLt83FJu
https://github.com
---
.