Skip to content

Commit 31bea46

Browse files
Merge pull request #27 from thuvh/fix/no-rosetta-helper
Build the BootUnlock helper natively instead of thinning /usr/bin/sec…
2 parents e5f2dc7 + 0690fdf commit 31bea46

6 files changed

Lines changed: 128 additions & 9 deletions

File tree

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/out/
2+
/files/BootUnlock

‎CHANGELOG.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
1.3.3
2+
-----
3+
4+
- Replaced the lipo-thinned copy of /usr/bin/security with a small native
5+
helper (build/BootUnlock.c) built for arm64 and x86_64, so the helper no
6+
longer needs Rosetta on Apple Silicon Macs.
7+
18
1.3.2
29
-----
310

‎build/BootUnlock.c‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Minimal replacement for `security find-generic-password -D desc -a account
3+
* -s service -w`, built natively for arm64/x86_64.
4+
*
5+
* Historically this helper was produced by lipo-thinning Apple's own
6+
* /usr/bin/security down to its x86_64 slice and ad-hoc re-signing it with a
7+
* custom identifier, purely so the System keychain ACL could trust this one
8+
* specific binary (see files/update.sh) without granting blanket access to
9+
* every process that happens to be /usr/bin/security. Apple's Mach-O for
10+
* /usr/bin/security only ships x86_64 and arm64e slices, and non-Apple
11+
* ad-hoc signatures of the arm64e slice fail the kernel's pointer
12+
* authentication ABI check, so the x86_64 slice needed Rosetta. Compiling
13+
* this ourselves for plain arm64 sidesteps that restriction entirely while
14+
* keeping the same custom-identity trust model.
15+
*/
16+
17+
#include <CoreFoundation/CoreFoundation.h>
18+
#include <Security/Security.h>
19+
#include <stdio.h>
20+
#include <string.h>
21+
22+
static void usage(const char *prog) {
23+
fprintf(stderr, "usage: %s find-generic-password -s service -a account [-D description] -w\n", prog);
24+
}
25+
26+
int main(int argc, char *argv[]) {
27+
if (argc < 2 || strcmp(argv[1], "find-generic-password") != 0) {
28+
usage(argv[0]);
29+
return 2;
30+
}
31+
32+
const char *service = NULL, *account = NULL, *description = NULL;
33+
int want_password = 0;
34+
35+
for (int i = 2; i < argc; i++) {
36+
if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
37+
service = argv[++i];
38+
} else if (strcmp(argv[i], "-a") == 0 && i + 1 < argc) {
39+
account = argv[++i];
40+
} else if (strcmp(argv[i], "-D") == 0 && i + 1 < argc) {
41+
description = argv[++i];
42+
} else if (strcmp(argv[i], "-w") == 0) {
43+
want_password = 1;
44+
}
45+
}
46+
47+
if (!service || !account || !want_password) {
48+
usage(argv[0]);
49+
return 2;
50+
}
51+
52+
SecKeychainRef systemKeychain = NULL;
53+
if (SecKeychainOpen("/Library/Keychains/System.keychain", &systemKeychain) != errSecSuccess || !systemKeychain) {
54+
return 1;
55+
}
56+
57+
CFStringRef serviceStr = CFStringCreateWithCString(kCFAllocatorDefault, service, kCFStringEncodingUTF8);
58+
CFStringRef accountStr = CFStringCreateWithCString(kCFAllocatorDefault, account, kCFStringEncodingUTF8);
59+
CFStringRef descriptionStr = description
60+
? CFStringCreateWithCString(kCFAllocatorDefault, description, kCFStringEncodingUTF8)
61+
: NULL;
62+
CFArrayRef searchList = CFArrayCreate(kCFAllocatorDefault, (const void **)&systemKeychain, 1, &kCFTypeArrayCallBacks);
63+
64+
CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
65+
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
66+
CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
67+
CFDictionarySetValue(query, kSecAttrService, serviceStr);
68+
CFDictionarySetValue(query, kSecAttrAccount, accountStr);
69+
if (descriptionStr) {
70+
CFDictionarySetValue(query, kSecAttrDescription, descriptionStr);
71+
}
72+
CFDictionarySetValue(query, kSecMatchSearchList, searchList);
73+
CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitOne);
74+
CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
75+
76+
CFTypeRef result = NULL;
77+
OSStatus status = SecItemCopyMatching(query, &result);
78+
79+
CFRelease(query);
80+
CFRelease(searchList);
81+
CFRelease(serviceStr);
82+
CFRelease(accountStr);
83+
if (descriptionStr) {
84+
CFRelease(descriptionStr);
85+
}
86+
CFRelease(systemKeychain);
87+
88+
if (status != errSecSuccess || !result) {
89+
return 1;
90+
}
91+
92+
CFDataRef data = (CFDataRef)result;
93+
fwrite(CFDataGetBytePtr(data), 1, (size_t)CFDataGetLength(data), stdout);
94+
CFRelease(result);
95+
return 0;
96+
}

‎build/Distribution.xml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
22
<installer-gui-script minSpecVersion="2">
33
<title>BootUnlock</title>
4-
<product id="au.com.openwall.BootUnlock" version="1.3.2" />
4+
<product id="au.com.openwall.BootUnlock" version="1.3.3" />
55
<background file="background.png" scaling="proportional" alignment="bottomleft"/>
66
<welcome file="welcome.rtf"/>
77
<readme file="readme.rtf"/>
@@ -20,5 +20,5 @@
2020
<choice id="au.com.openwall.BootUnlock" visible="false">
2121
<pkg-ref id="au.com.openwall.BootUnlock"/>
2222
</choice>
23-
<pkg-ref id="au.com.openwall.BootUnlock">BootUnlock-1.3.2-dist.pkg</pkg-ref>
23+
<pkg-ref id="au.com.openwall.BootUnlock">BootUnlock-1.3.3-dist.pkg</pkg-ref>
2424
</installer-gui-script>

‎build/build.sh‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ NAME="BootUnlock"
77
IDENTIFIER="au.com.openwall.$NAME"
88

99
# Package version number.
10-
VERSION="1.3.2"
10+
VERSION="1.3.3"
1111

1212
# The location to copy the contents of files.
1313
INSTALL_LOCATION="/Library/PrivilegedHelperTools/$IDENTIFIER"
@@ -17,12 +17,27 @@ set -eu -o pipefail
1717
WORK_DIR="${0%/*}"
1818
[ -z "$WORK_DIR" -o "$WORK_DIR" == "$0" ] && WORK_DIR="$(pwd)" ||:
1919

20+
echo "WORK_DIR=$WORK_DIR"
2021
mkdir -p "$WORK_DIR/../out" ||:
2122

2223
# pkgbuild need proper permissions on the source files
2324
chmod 0755 "$WORK_DIR/../files/"*.sh
2425
chmod 0644 "$WORK_DIR/../files/"*.xsl
2526

27+
# Build the "BootUnlock" keychain helper natively for both Apple Silicon and
28+
# Intel Macs. It replaces lipo-thinning Apple's own /usr/bin/security (which
29+
# only ships x86_64 and arm64e slices -- the arm64e one can't be re-signed by
30+
# a third party and run without Rosetta) with a small program compiled for
31+
# plain arm64/x86_64 that talks to the keychain itself, signed with the same
32+
# custom identifier the System keychain ACL trusts (see files/update.sh).
33+
clang -O2 -Wall \
34+
-arch x86_64 -arch arm64 \
35+
-framework Security -framework CoreFoundation \
36+
-o "$WORK_DIR/../files/$NAME" \
37+
"$WORK_DIR/BootUnlock.c"
38+
codesign -f -s - "--prefix=${IDENTIFIER%$NAME}" -r="designated => identifier $IDENTIFIER" "$WORK_DIR/../files/$NAME"
39+
chmod 0755 "$WORK_DIR/../files/$NAME"
40+
2641
# Build package.
2742
/usr/bin/pkgbuild \
2843
--identifier "$IDENTIFIER" \

‎scripts/postinstall‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,11 @@ INSTALL_LOCATION="/Library/PrivilegedHelperTools/$IDENTIFIER"
1313

1414
LAUNCH_DAEMON_PLIST="/Library/LaunchDaemons/$IDENTIFIER.plist"
1515

16-
# Here is the trick: we are abusing the security(1) tool to access items on
17-
# keychain in a secure way :). Unfortunately, on ARM-based Macs only Apple
18-
# signed binaries can run arm64e (since Apple considers that ABI to be unstable),
19-
# therefore, to make it work we are only using the x86_64 binary part.
20-
xcrun lipo /usr/bin/security -thin x86_64 -output "$INSTALL_LOCATION/$NAME"
21-
codesign -f -s - "--prefix=${IDENTIFIER%$NAME}" -r="designated => identifier $IDENTIFIER" "$INSTALL_LOCATION/$NAME"
16+
# The "$NAME" helper is a small native (arm64/x86_64) program built and
17+
# ad-hoc signed at package build time (see build/build.sh and
18+
# build/BootUnlock.c) that talks to the keychain directly under the
19+
# "$IDENTIFIER" identity trusted by the System keychain ACL (files/update.sh).
20+
# All that's left to do here is lock down its ownership and permissions.
2221
chown -h root:wheel "$INSTALL_LOCATION/$NAME"
2322
chmod 0100 "$INSTALL_LOCATION/$NAME"
2423

0 commit comments

Comments
 (0)