@ -17,17 +17,7 @@ HERE = os.path.dirname(os.path.realpath(__file__))
HARDENED_GITHUB_REPO = ' anthraxx/linux-hardened '
HARDENED_TRUSTED_KEY = os . path . join ( HERE , ' anthraxx.asc ' )
HARDENED_PATCHES_PATH = os . path . join ( HERE , ' hardened-patches.json ' )
MIN_KERNEL = ( 4 , 14 )
HARDENED_VERSION_RE = re . compile ( r '''
( ? P < kernel_version > [ \d . ] + ) \.
( ? P < version_suffix > [ a - z ] + )
''' , re.VERBOSE)
def parse_version ( version ) :
match = HARDENED_VERSION_RE . fullmatch ( version )
if match :
return match . groups ( )
MIN_KERNEL_VERSION = [ 4 , 14 ]
def run ( * args , * * kwargs ) :
try :
@ -78,11 +68,12 @@ def fetch_patch(*, name, release):
except StopIteration :
raise KeyError ( filename )
patch_filename = f ' { name } .patch '
try :
patch_url = find_asset ( f ' { name } .patch ' )
sig_url = find_asset ( f ' { name } .patch .sig' )
patch_url = find_asset ( patch_filename )
sig_url = find_asset ( patch_filename + ' .sig' )
except KeyError :
print ( f ' error: { name } .patch {{ ,sig }} not present ' , file = sys . stderr )
print ( f ' error: { patch_file name} {{ ,. sig }} not present ' , file = sys . stderr )
return None
sha256 , patch_path = nix_prefetch_url ( patch_url )
@ -97,16 +88,32 @@ def fetch_patch(*, name, release):
return None
return {
' name ' : patch_filename ,
' url ' : patch_url ,
' sha256 ' : sha256 ,
}
def commit_patches ( * , kernel_version , message ) :
def parse_version ( version_str ) :
version = [ ]
for component in version_str . split ( ' . ' ) :
try :
version . append ( int ( component ) )
except ValueError :
version . append ( component )
return version
def version_string ( version ) :
return ' . ' . join ( str ( component ) for component in version )
def major_kernel_version_key ( kernel_version ) :
return version_string ( kernel_version [ : - 1 ] )
def commit_patches ( * , kernel_key , message ) :
with open ( HARDENED_PATCHES_PATH + ' .new ' , ' w ' ) as new_patches_file :
json . dump ( patches , new_patches_file , indent = 4 , sort_keys = True )
new_patches_file . write ( ' \n ' )
os . rename ( HARDENED_PATCHES_PATH + ' .new ' , HARDENED_PATCHES_PATH )
message = f ' linux/hardened-patches/ { kernel_version } : { message } '
message = f ' linux/hardened-patches/ { kernel_key } : { message } '
print ( message )
if os . environ . get ( ' COMMIT ' ) :
run (
@ -125,74 +132,96 @@ NIX_VERSION_RE = re.compile(r'''
''' , re.VERBOSE)
# Get the set of currently packaged kernel versions.
kernel_versions = set ( )
kernel_versions = { }
for filename in os . listdir ( HERE ) :
filename_match = re . fullmatch ( r ' linux-( \ d+) \ .( \ d+) \ .nix ' , filename )
if filename_match :
if tuple ( int ( v ) for v in filename_match . groups ( ) ) < MIN_KERNEL :
continue
with open ( os . path . join ( HERE , filename ) ) as nix_file :
for nix_line in nix_file :
match = NIX_VERSION_RE . fullmatch ( nix_line )
if match :
kernel_versions . add ( match . group ( ' version ' ) )
kernel_version = parse_version ( match . group ( ' version ' ) )
if kernel_version < MIN_KERNEL_VERSION :
continue
kernel_key = major_kernel_version_key ( kernel_version )
kernel_versions [ kernel_key ] = kernel_version
# Remove patches for old kernel versions.
for kernel_version in patches . keys ( ) - kernel_versions :
del patches [ kernel_version ]
commit_patches ( kernel_version = kernel_version , message = ' remove ' )
# Remove patches for unpackaged kernel versions.
for kernel_key in sorted ( patches . keys ( ) - kernel_versions . keys ( ) ) :
commit_patches ( kernel_key = kernel_key , message = ' remove ' )
g = Github ( os . environ . get ( ' GITHUB_TOKEN ' ) )
repo = g . get_repo ( HARDENED_GITHUB_REPO )
releases = repo . get_releases ( )
found_kernel_versions = set ( )
failures = False
for release in releases :
remaining_kernel_versions = kernel_versions - found_kernel_versions
if not remaining_kernel_versions :
break
# Match each kernel version with the best patch version.
releases = { }
for release in repo . get_releases ( ) :
version = parse_version ( release . tag_name )
# needs to look like e.g. 5.6.3.a
if len ( version ) < 4 :
continue
version = release . tag_name
name = f ' linux-hardened- { version } '
version_info = parse_version ( version )
if not version_info :
kernel_version = version [ : - 1 ]
kernel_key = major_kernel_version_key ( kernel_version )
try :
packaged_kernel_version = kernel_versions [ kernel_key ]
except KeyError :
continue
kernel_version , version_suffix = version_info
if kernel_version in remaining_kernel_versions :
found_kernel_versions . add ( kernel_version )
try :
old_version_suffix = patches [ kernel_version ] [ ' version_suffix ' ]
old_version = f ' { kernel_version } . { old_version_suffix } '
update = old_version_suffix < version_suffix
except KeyError :
update = True
old_version = None
if update :
patch = fetch_patch ( name = name , release = release )
if patch is None :
failures = True
release_info = {
' version ' : version ,
' release ' : release ,
}
if kernel_version == packaged_kernel_version :
releases [ kernel_key ] = release_info
else :
# Fall back to the latest patch for this major kernel version,
# skipping patches for kernels newer than the packaged one.
if kernel_version > packaged_kernel_version :
continue
elif ( kernel_key not in releases or
releases [ kernel_key ] [ ' version ' ] < version ) :
releases [ kernel_key ] = release_info
# Update hardened-patches.json for each release.
for kernel_key , release_info in releases . items ( ) :
release = release_info [ ' release ' ]
version = release_info [ ' version ' ]
version_str = release . tag_name
name = f ' linux-hardened- { version_str } '
try :
old_filename = patches [ kernel_key ] [ ' name ' ]
old_version_str = ( old_filename
. replace ( ' linux-hardened- ' , ' ' )
. replace ( ' .patch ' , ' ' ) )
old_version = parse_version ( old_version_str )
update = old_version < version
except KeyError :
update = True
old_version = None
if update :
patch = fetch_patch ( name = name , release = release )
if patch is None :
failures = True
else :
patches [ kernel_key ] = patch
if old_version :
message = f ' { old_version_str } -> { version_str } '
else :
patch [ ' version_suffix ' ] = version_suffix
patches [ kernel_version ] = patch
if old_version :
message = f ' { old_version } -> { version } '
else :
message = f ' init at { version } '
commit_patches ( kernel_version = kernel_version , message = message )
message = f ' init at { version_str } '
commit_patches ( kernel_key = kernel_key , message = message )
missing_kernel_versions = kernel_versions - patches . keys ( )
missing_kernel_versions = kernel_versions . keys ( ) - patches . keys ( )
if missing_kernel_versions :
print (
f ' warning: no patches for kernel versions ' +
' , ' . join ( missing_kernel_versions ) +
' \n warning: consider manually backporting older patches (bump '
' JSON key, set version_suffix to " NixOS-a " ) ' ,
' , ' . join ( missing_kernel_versions ) ,
file = sys . stderr ,
)