[Google Pixel XL Questions & Answers] NZedPred:
user build with release keys

I mentioned before I was going to attempt to do a build that would pass safety net without needing to install Magisk and use hiding modules. I haven't quite managed that, but thought I'd post details of the progress that I've made.

Note for the below, I have a Pixel, so my codename is always sailfish - replace with marlin for the Pixel XL. Also, follow the instructions above to include MindTheGapps (you need the Google services to do the SafetyNet check).

Now, importantly I found that the Eleven app didn't compile under a user build (it's fine in a userdebug build). If you find the same issue, best to remove it from the project by adding this to a local manifest, e.g. .repo/local_manifest/excluded-projects.xml

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<!-- Does not compile in a user build -->
<remove-project name="LineageOS/android_packages_apps_Eleven" />
</manifest>

Follow instructions in this link to set up some build keys:

https://wiki.lineageos.org/signing_builds.html (the section Generating the keys). For convenience, the commands are pasted below (customize the subject):

subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
mkdir ~/.android-certs
for x in releasekey platform shared media testkey; do \
./development/tools/make_key ~/.android-certs/$x "$subject"; \
done

Now for building, do a 'repo sync' and 'source build/envsetup.sh' as you normally would, but now it's time to apply a few patches.

Kernel command line patch:

https://gist.github.com/kirelagin/211af699bd321f9448d310502074bd9c

Or get the raw version of the patch directly here:

https://gist.githubusercontent.com/...e45f468cbefc3a9c571bd3d8b2a877/safetynet.diff

This patch needs to be applied to the kernel/google/marlin folder. Its purpose is to change a few flags in the command line to remove the fact that the bootloader is unlocked, etc.

cd kernel/google/marlin
git apply safetynet.diff

The below is not required now, a recent patch fixed this issue

Now normally, as the build files replace the device fingerprint with the fingerprint from stock, we would expect that doing e.g. in a shell in the rom "getprop | grep fingerprint" would return said fingerprint in all cases. However, I always found that one, 'ro.build.fingerprint' would always be the default Lineage build fingerprint. I couldn't find how to get that one in particular to be the same as the others, so I ended up doing something that isn't portable but works :)

Patch system/code/base/properties.cpp by applying one of the following patches (you must be within the system/code folder)

Sailfish:

diff --git a/base/properties.cpp b/base/properties.cpp
index d5a5918ce..49959ffad 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -79,6 +79,10 @@ static int __system_property_set(const char* key, const char* value) {

std::string GetProperty(const std::string& key, const std::string& default_value) {
std::string property_value;
+ if (key == "ro.build.fingerprint") {
+ property_value = "google/sailfish/sailfish:10/QP1A.191005.007.A1/5908163:user/release-keys";
+ return property_value;
+ }
#if defined(__BIONIC__)
const prop_info* pi = __system_property_find(key.c_str());
if (pi == nullptr) return default_value;

If you have a Marlin, use the following:

diff --git a/base/properties.cpp b/base/properties.cpp
index d5a5918ce..49959ffad 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -79,6 +79,10 @@ static int __system_property_set(const char* key, const char* value) {

std::string GetProperty(const std::string& key, const std::string& default_value) {
std::string property_value;
+ if (key == "ro.build.fingerprint") {
+ property_value = "google/marlin/marlin:10/QP1A.191005.007.A1/5908163:user/release-keys";
+ return property_value;
+ }
#if defined(__BIONIC__)
const prop_info* pi = __system_property_find(key.c_str());
if (pi == nullptr) return default_value;

The value of the fingerprint is in the file device/google/marlin/lineage_[sailfish/marlin].mk. The patches above have a very simple job - if the ro.build.fingerprint property is requested, return a hard-coded value. Simple, works, but needs to be changed per device, and won't ever be seen in the Lineage repo :silly:

At the moment, the builds have SELinux in permissive mode. I've read that one of the CTS profile checks is to ensure that SELinux is in enforcing mode. We can override this by patching two files from within the external/selinux folder

diff --git a/libselinux/src/selinux_config.c b/libselinux/src/selinux_config.c
index b06cb63b..7877010c 100644
--- a/libselinux/src/selinux_config.c
+++ b/libselinux/src/selinux_config.c
@@ -87,44 +87,8 @@ static const uint16_t file_path_suffixes_idx[NEL] = {

int selinux_getenforcemode(int *enforce)
{
-	int ret = -1;
-	FILE *cfg = fopen(SELINUXCONFIG, "re");
-	if (cfg) {
-	char *buf;
-	int len = sizeof(SELINUXTAG) - 1;
-	buf = malloc(selinux_page_size);
-	if (!buf) {
-	fclose(cfg);
-	return -1;
-	}
-	while (fgets_unlocked(buf, selinux_page_size, cfg)) {
-	if (strncmp(buf, SELINUXTAG, len))
-	continue;
-	if (!strncasecmp
-	(buf + len, "enforcing", sizeof("enforcing") - 1)) {
-	*enforce = 1;
-	ret = 0;
-	break;
-	} else
-	if (!strncasecmp
-	(buf + len, "permissive",
-	sizeof("permissive") - 1)) {
-	*enforce = 0;
-	ret = 0;
-	break;
-	} else
-	if (!strncasecmp
-	(buf + len, "disabled",
-	sizeof("disabled") - 1)) {
-	*enforce = -1;
-	ret = 0;
-	break;
-	}
-	}
-	fclose(cfg);
-	free(buf);
-	}
-	return ret;
+ *enforce = 1;
+ return 0;
}

hidden_def(selinux_getenforcemode)
diff --git a/libselinux/src/sestatus.c b/libselinux/src/sestatus.c
index ed29dc55..5b057932 100644
--- a/libselinux/src/sestatus.c
+++ b/libselinux/src/sestatus.c
@@ -130,30 +130,7 @@ int selinux_status_updated(void)
*/
int selinux_status_getenforce(void)
{
-	uint32_t	seqno;
-	uint32_t	enforcing;
-
-	if (selinux_status == NULL) {
-	errno = EINVAL;
-	return -1;
-	}
-
-	if (selinux_status == MAP_FAILED) {
-	if (avc_netlink_check_nb() < 0)
-	return -1;
-
-	return fallback_enforcing;
-	}
-
-	/* sequence must not be changed during references */
-	do {
-	seqno = read_sequence(selinux_status);
-
-	enforcing = selinux_status->enforcing;
-
-	} while (seqno != read_sequence(selinux_status));
-
-	return enforcing ? 1 : 0;
+	return 1;
}

/*

Now, I have a script that does the steps of:

Sets up a user build

Builds the appropriate files used for a signed build

Signs the APKs

Creates an installable zip / OTA from the signed zips

#!/bin/bash

echo "This script must be sourced."

BUILD="user"
DEVICE="sailfish"
DATE=$(date +%Y-%m-%d)
VERSION=17.0

croot && lunch lineage_$DEVICE-$BUILD

SIGNED_TARGET="$OUT/lineage-$VERSION-$DEVICE-signed-target_files-$BUILD-$DATE.zip"
SIGNED_OTA="$OUT/lineage-$VERSION-$DEVICE-signed-ota-$BUILD-$DATE.zip"

mka target-files-package otatools && \
./build/tools/releasetools/sign_target_files_apks -o -d ~/.android-certs $OUT/obj/PACKAGING/target_files_intermediates/*-target_files-*.zip $SIGNED_TARGET && \
./build/tools/releasetools/ota_from_target_files -k ~/.android-certs/releasekey --block $SIGNED_TARGET $SIGNED_OTA

It must be sourced (i.e. source do-build #or whatever you call the script). You can easily change some of the parameters above, e.g. sailfish to marlin.

So for my testing, after doing the above changes, I can get a basic integrity pass in Safety Net, but not a CTS profile pass. I tried using Magisk, and it passes Safety Net without the need for using any further prop tweaks (normally I would still need one of those). So at least that is a little bit of progress.

Hope that this is helpful to someone. And if anyone has any other insights as to what can be done to try and get a CTS profile match, let us all know!