import hashlib
import pickle
import time
-from datetime import datetime, timedelta
+from datetime import datetime
from argparse import ArgumentParser
import collections
requiresroot = 'Yes'
else:
requiresroot = 'No'
- wikidata += '{{App|id=%s|name=%s|added=%s|lastupdated=%s|source=%s|tracker=%s|web=%s|changelog=%s|donate=%s|flattr=%s|bitcoin=%s|litecoin=%s|license=%s|root=%s|author=%s|email=%s}}\n' % (
+ wikidata += '{{App|id=%s|name=%s|added=%s|lastupdated=%s|source=%s|tracker=%s|web=%s|changelog=%s|donate=%s|flattr=%s|liberapay=%s|bitcoin=%s|litecoin=%s|license=%s|root=%s|author=%s|email=%s}}\n' % (
appid,
app.Name,
app.added.strftime('%Y-%m-%d') if app.added else '',
app.Changelog,
app.Donate,
app.FlattrID,
+ app.LiberapayID,
app.Bitcoin,
app.Litecoin,
app.License,
Checks whether there are more than one classes.dex or AndroidManifest.xml
files, which is invalid and an essential part of the "Master Key" attack.
-
http://www.saurik.com/id/17
+
+ Janus is similar to Master Key but is perhaps easier to scan for.
+ https://www.guardsquare.com/en/blog/new-android-vulnerability-allows-attackers-modify-apps-without-affecting-their-signatures
"""
+ found_vuln = False
+
# statically load this pattern
if not hasattr(has_known_vulnerability, "pattern"):
has_known_vulnerability.pattern = re.compile(b'.*OpenSSL ([01][0-9a-z.-]+)')
+ with open(filename.encode(), 'rb') as fp:
+ first4 = fp.read(4)
+ if first4 != b'\x50\x4b\x03\x04':
+ raise FDroidException(_('{path} has bad file signature "{pattern}", possible Janus exploit!')
+ .format(path=filename, pattern=first4.decode().replace('\n', ' ')) + '\n'
+ + 'https://www.guardsquare.com/en/blog/new-android-vulnerability-allows-attackers-modify-apps-without-affecting-their-signatures')
+
files_in_apk = set()
with zipfile.ZipFile(filename) as zf:
for name in zf.namelist():
else:
logging.warning(_('"{path}" contains outdated {name} ({version})')
.format(path=filename, name=name, version=version))
- return True
+ found_vuln = True
break
elif name == 'AndroidManifest.xml' or name == 'classes.dex' or name.endswith('.so'):
if name in files_in_apk:
- return True
+ logging.warning(_('{apkfilename} has multiple {name} files, looks like Master Key exploit!')
+ .format(apkfilename=filename, name=name))
+ found_vuln = True
files_in_apk.add(name)
-
- return False
+ return found_vuln
def insert_obbs(repodir, apps, apks):
"""
- sourcedirs = glob.glob(os.path.join('build', '[A-Za-z]*', 'fastlane', 'metadata', 'android', '[a-z][a-z]*'))
+ sourcedirs = glob.glob(os.path.join('build', '[A-Za-z]*', 'src', '[A-Za-z]*', 'fastlane', 'metadata', 'android', '[a-z][a-z]*'))
+ sourcedirs += glob.glob(os.path.join('build', '[A-Za-z]*', 'fastlane', 'metadata', 'android', '[a-z][a-z]*'))
sourcedirs += glob.glob(os.path.join('build', '[A-Za-z]*', 'metadata', '[a-z][a-z]*'))
sourcedirs += glob.glob(os.path.join('metadata', '[A-Za-z]*', '[a-z][a-z]*'))
continue
locale = segments[-1]
destdir = os.path.join('repo', packageName, locale)
+
+ # flavours specified in build receipt
+ build_flavours = ""
+ if apps[packageName] and 'builds' in apps[packageName] and len(apps[packageName].builds) > 0\
+ and 'gradle' in apps[packageName].builds[-1]:
+ build_flavours = apps[packageName].builds[-1].gradle
+
+ if len(segments) >= 5 and segments[4] == "fastlane" and segments[3] not in build_flavours:
+ logging.debug("ignoring due to wrong flavour")
+ continue
+
for f in files:
if f in ('description.txt', 'full_description.txt'):
_set_localized_text_entry(apps[packageName], locale, 'description',
apkzip = zipfile.ZipFile(apkfile, 'r')
- # if an APK has files newer than the system time, suggest updating
- # the system clock. This is useful for offline systems, used for
- # signing, which do not have another source of clock sync info. It
- # has to be more than 24 hours newer because ZIP/APK files do not
- # store timezone info
manifest = apkzip.getinfo('AndroidManifest.xml')
if manifest.date_time[1] == 0: # month can't be zero
logging.debug(_('AndroidManifest.xml has no date'))
else:
- dt_obj = datetime(*manifest.date_time)
- checkdt = dt_obj - timedelta(1)
- if datetime.today() < checkdt:
- logging.warning('System clock is older than manifest in: '
- + apkfilename
- + '\nSet clock to that time using:\n'
- + 'sudo date -s "' + str(dt_obj) + '"')
+ common.check_system_clock(datetime(*manifest.date_time), apkfilename)
# extract icons from APK zip file
iconfilename = "%s.%s.png" % (apk['packageName'], apk['versionCode'])
with open('template.yml') as f:
metatxt = f.read()
if 'name' in apk and apk['name'] != '':
- metatxt = re.sub(r'^(((Auto)?Name|Summary):).*$',
+ metatxt = re.sub(r'''^(((Auto)?Name|Summary):)[ '"\.]*$''',
r'\1 ' + apk['name'],
metatxt,
flags=re.IGNORECASE | re.MULTILINE)
parser = ArgumentParser()
common.setup_global_opts(parser)
parser.add_argument("--create-key", action="store_true", default=False,
- help=_("Create a repo signing key in a keystore"))
+ help=_("Add a repo signing key to an unsigned repo"))
parser.add_argument("-c", "--create-metadata", action="store_true", default=False,
- help=_("Create skeleton metadata files that are missing"))
+ help=_("Add skeleton metadata files for APKs that are missing them"))
parser.add_argument("--delete-unknown", action="store_true", default=False,
help=_("Delete APKs and/or OBBs without metadata from the repo"))
parser.add_argument("-b", "--buildreport", action="store_true", default=False,
parser.add_argument("-w", "--wiki", default=False, action="store_true",
help=_("Update the wiki"))
parser.add_argument("--pretty", action="store_true", default=False,
- help=_("Produce human-readable index.xml"))
+ help=_("Produce human-readable XML/JSON for index files"))
parser.add_argument("--clean", action="store_true", default=False,
help=_("Clean update - don't uses caches, reprocess all APKs"))
parser.add_argument("--nosign", action="store_true", default=False,