Differences between revisions 1 and 19 (spanning 18 versions)
Revision 1 as of 2019-01-02 15:10:57
Size: 8168
Editor: DomQ
Comment: Excerpted from BuildingOnDarwin; added roadmap table and control steps
Revision 19 as of 2021-08-25 17:02:12
Size: 10665
Editor: PhilB
Comment: Add a link to a script to automate the generation of the code-signing certificate
Deletions are marked like this. Additions are marked like this.
Line 5: Line 5:
If you try to use your [[BuildingOnDarwin|freshly built gdb]], you will get an error message such as: If you try to use your [[BuildingOnDarwin|freshly built gdb]], you might get an error message such as:
Line 13: Line 13:
This is because the Darwin kernel will refuse to allow gdb to debug another process if you don't have special rights, since debugging a process means having full control over that process, and that isn't allowed by default since it would be exploitable by malware. (The kernel won't refuse if you are root, but of course you don't want to be root to debug.) This is because modern Darwin kernels restrict the capability to assume control over another process (here, for the purpose of debugging it), since that capability is a boon to malware. In order for said {{{taskgated}}} to grant access to gdb, the latter must be fitted with [[https://developer.apple.com/documentation/security/hardened_runtime|entitlements]], which consist of [[https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html|digitally-signed metadata]] inside the {{{gdb}}} binary.
Line 15: Line 15:
= Procedure =
Line 16: Line 17:
= Method for Mac OS X 10.5 (Leopard) and later = These instructions apply to Mac OS versions 10.14 (Mojave) thru 11.x (Big Sur). [[See below#Notes_for_older_versions]] for older versions of Mac OS X.
Line 18: Line 19:
The most up to date and secure method to allow gdb to control another process is to [[https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html|sign it]] with any system-trusted code signing authority. <<Anchor(checklist)>>
Line 20: Line 21:
|| '''Step''' || '''Success criteria''' ||
|| [[#Create_a_certificate|Create a certificate]] || {{{security find-certificate -c gdb-cert}}} ||
|| '''Step''' || '''Check''' ||
|| [[#Create a certificate in the System Keychain|Create a certificate in the System Keychain]] ||{{{security find-certificate -c gdb-cert |grep System.keychain}}}<<BR>>{{{security find-certificate -p -c gdb-cert | openssl x509 -checkend 0}}} ||
Line 23: Line 24:
|| [[#Refresh_the_system.27s_certificates_and_code-signing_data|Refresh the system's certificates and code-signing data]] || None known (besides ultimately getting your {{{gdb}}} to work) ||
|| [[#Sign_and_entitle_the_gdb_binary|Sign and entitle gdb using the certificate]] || {{{codesign -vv $(which gdb)}}}<<BR>>{{{codesign -d --entitlements - $(which gdb)}}} (Mojave and later) ||
|| [[#Sign_and_entitle_the_gdb_binary|Sign and entitle gdb using the certificate]] || {{{codesign -vv $(which gdb)}}}<<BR>>{{{codesign -d --entitlements :- $(which gdb)|grep -a com.apple.security.cs.debugger}}} ||
|| [[#Refresh_the_system.27s_certificates_and_code-signing_data|Refresh the system's certificates and code-signing data]] || None known (besides checking that your {{{gdb}}} now works) ||
|| Try {{{gdb}}} again || Still no luck? See [[#Troubleshooting / further diagnosis|Troubleshooting]] ||
Line 26: Line 28:
== Create a certificate in the System Keychain ==
Line 27: Line 30:
== Create a certificate == === Automated steps ===

Download and run [[https://github.com/conda-forge/gdb-feedstock/blob/master/recipe/macos-codesign/macos-setup-codesign.sh|this script]] from the conda-forge GDB package. '''Note''': the script creates a certificate named {{{gdb_codesign}}} instead of {{{gdb-cert}}}, so adapt the commands below accordingly.

=== Manual steps ===
Line 35: Line 42:
💡 If you cannot store the certificate in the {{{System}}} keychain, create it in the {{{login}}} keychain, then export it. You can then import it into the {{{System}}} keychain. 💡 If you cannot store the certificate in the {{{System}}} keychain: create it in the {{{login}}} keychain instead, then export it. You can then import it into the {{{System}}} keychain.
Line 37: Line 44:
'''Control:''' in the terminal type Finally, quit the Keychain Access application to refresh the certificate store.

=== Control ===

In the terminal type
Line 42: Line 53:
This should display some details about your newly minted certificate. If you want more details about it, you can install OpenSSL and type This should display some details about your newly minted certificate, e.g.

{{{
keychain: "/Library/Keychains/System.keychain"
version: 256
class: 0x80001000
attributes:
    "alis"<blob>="gdb-cert"
[...]
}}}

Make sure that {{{keychain:}}} is the System keychain, as shown.

Also, make sure that your certificate is not expired yet:

{{{
security find-certificate -p -c gdb-cert | openssl x509 -checkend 0
}}}


💡If you want to inspect the entire X509 data structure, you can type
Line 49: Line 80:
Using the contextual menu for the certificate, select {{{Get Info}}}, open the {{{Trust}}} item, and set {{{Code Signing}}} to {{{Always Trust}}}. === Automated steps ===
Line 51: Line 82:
Finally, quit the Keychain Access application to refresh the certificate store. The certificate created by the {{{macos-setup-codesign.sh}}} script mentioned in the previous section is already correctly set up.
Line 53: Line 84:
'''Control:''' in the terminal type === Manual steps ===

Start Keychain Access again. Using the contextual menu for the certificate, select {{{Get Info}}}, open the {{{Trust}}} item, and set {{{Code Signing}}} to {{{Always Trust}}}.

Finally, quit the Keychain Access application once more to refresh the certificate store.

=== Control ===
In the terminal type
Line 59: Line 97:

== Sign and entitle the gdb binary ==

 1. Create a {{{gdb-entitlement.xml}}} file containing the following: {{{
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.debugger</key>
    <true/>
</dict>
</plist>
}}}
 2. If the certificate you generated in the previous section is known as {{{gdb-cert}}}, use: {{{
codesign --entitlements gdb-entitlement.xml -fs gdb-cert $(which gdb)
}}}

💡 You may have to prepend this command with `sudo` if the `gdb` binary is located in a place that is not writable by regular users.

If you plan to build gdb frequently, this step can be automated by passing {{{--enable-codesign=gdb-cert}}} (assuming, again, that {{{gdb-cert}}} is the name of the certificate) to {{{configure}}}.

'''Control:''' in the terminal type

{{{
codesign -vv $(which gdb)
codesign -d --entitlements :- $(which gdb)
}}}
Line 73: Line 138:
== Sign and entitle the gdb binary ==

 1. Create a {{{gdb-entitlement.xml}}} file containing the following: {{{
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.debugger</key>
    <true/>
</dict>
</plist>
</pre>}}}
 2. If the certificate you generated in the previous section is known as {{{gdb-cert}}}, use: {{{
$ codesign --entitlements gdb-entitlement.xml -fs gdb-cert gdb
}}}

Make sure to specify the full path to the {{{gdb}}} binary. You may also have to prepend this command with `sudo` if the `gdb` binary is located in a place that is not writable by regular users.

If you plan to build gdb frequently, this step can be automated by passing {{{--enable-codesign=gdb-cert}}} (assuming, again, that {{{gdb-cert}}} is the name of the certificate) to {{{configure}}}.
Line 95: Line 140:
If you used the code-signing method and you still get the {{{(os/kern) failure (0x5)}}} error, one of the following items may pinpoint you to the cause.
 * If you use macOS 10.13 "High Sierra": gdb 8.1 is incompatible with macOS 10.13. Use gdb 8.0.1 instead. FIXME: is there a bugzilla bug tracking the incompatibility? Shall link it here.
 * Ensure that the certificate is in the {{{System}}} keychain
 * Ensure that the certificate is marked as {{{Always Trust}}}
 * Ensure that the gdb binary is correctly signed with the certificate. You can check this with the following command:
{{{
$ codesign -v gdb
}}}
(use the full path to the {{{gdb}}} binary)
⚠ '''There is plenty of other reasons why gdb might not work on Darwin''', besides it not being code-signed with the {{{com.apple.security.cs.debugger}}} entitlement.
 * [[https://sourceware.org/bugzilla/show_bug.cgi?id=13157|gdb does not understand “fat” binaries at the moment]]. You may have to preprocess the target binary with {{{lipo -thin x86_64}}}
 * If gdb launches the process, but then hangs, you might be running into [[https://sourceware.org/bugzilla/show_bug.cgi?id=24069|bug #24069]]. Keep trying again until the debugger starts successfully, or apply the suggested patch and rebuild gdb.
Line 105: Line 144:
If this command does not print anything, it means the binary is correctly signed. If there is a problem with the code signature, it will print an error message. In the latter case, re-sign it.
 * If you are under macOS 10.12 (Sierra) or later, ensure you issue {{{set startup-with-shell off}}} before you run the program to be debugged.
If, on the other hand you still get the {{{(os/kern) failure (0x5)}}} error, one of the following suggestions may pinpoint you to the cause.
 * If you are trying to attach to a running program using gdb's {{{attach}}} command, then that program (the ''target'' one, not gdb) must have the {{{com.apple.security.get-task-allow}}} entitlement set (in addition to gdb's own entitlement). You can achieve that with {{{codesign}}} using the same instructions and certificate you already used to sign gdb — Just change the entitlement name inside the XML plist file accordingly.
 * [[#checklist|Double-check all commands in the checklist]] and redo the corresponding step in case of trouble
Line 115: Line 155:
== Watch Logs ==

The following command will stream the log messages pertaining to {{{taskgated}}} and code-signing issues:

{{{
log stream --predicate 'process = "taskgated" OR (process = "kernel" AND eventMessage CONTAINS "macOSTaskPolicy")' --info
}}}
Line 117: Line 165:
= Old method = = Notes for older versions =
Line 119: Line 167:
'''Warning''': do '''''not''''' combine (part of) these instructions with the code signing method, because they interfere with each other. See the [[#Troubleshooting code-signing|troubleshooting]] section in case you (possibly) did mix them to fix up any potential issues. In general, following the instructions in this section is '''''strongly unrecommended''''' if you are using Mac OS X 10.6 (Snow Leopard) or later. == High Sierra (10.13) ==
Line 121: Line 169:
In Mac OS X 10.4 (Tiger), the kernel would accept processes whose primary effective group is procmod or procview. That meant that making gdb setgid procmod worked. [[https://sourceware.org/bugzilla/show_bug.cgi?id=22960|gdb 8.1 is incompatible with macOS 10.13]]. [[https://stackoverflow.com/a/49104154/435004|Downgrade to gdb 8.0.1 instead]]

== Leopard (10.5) thru High Sierra (10.13) ==

These versions do not have entitlement metadata; just signing the gdb binary was enough to make it work. In that case, the {{{codesign}}} command to use is just

{{{
codesign -fs gdb-cert $(which gdb)
}}}

Accordingly, all checks based on {{{codesign -d --entitlements}}} won't work, so just skip them.

== Tiger (10.4) and before ==

<<Anchor(old method)>>

'''Warning''': do '''''not''''' combine (part of) these instructions with the code signing method, because they interfere with each other. In general, following the instructions in this section is '''''strongly discouraged'''''.

In Mac OS X 10.4 (Tiger), the kernel would let processes whose primary effective group is procmod or procview act as debuggers. That means that making gdb setgid procmod worked.

If you try to use your freshly built gdb, you might get an error message such as:

Starting program: /x/y/foo
Unable to find Mach task port for process-id 28885: (os/kern) failure (0x5).
 (please check gdb is codesigned - see taskgated(8))

This is because modern Darwin kernels restrict the capability to assume control over another process (here, for the purpose of debugging it), since that capability is a boon to malware. In order for said taskgated to grant access to gdb, the latter must be fitted with entitlements, which consist of digitally-signed metadata inside the gdb binary.

1. Procedure

These instructions apply to Mac OS versions 10.14 (Mojave) thru 11.x (Big Sur). See below#Notes_for_older_versions for older versions of Mac OS X.

Step

Check

Create a certificate in the System Keychain

security find-certificate -c gdb-cert |grep System.keychain
security find-certificate -p -c gdb-cert | openssl x509 -checkend 0

Trust the certificate for code signing

security dump-trust-settings -d

Sign and entitle gdb using the certificate

codesign -vv $(which gdb)
codesign -d --entitlements :- $(which gdb)|grep -a com.apple.security.cs.debugger

Refresh the system's certificates and code-signing data

None known (besides checking that your gdb now works)

Try gdb again

Still no luck? See Troubleshooting

1.1. Create a certificate in the System Keychain

1.1.1. Automated steps

Download and run this script from the conda-forge GDB package. Note: the script creates a certificate named gdb_codesign instead of gdb-cert, so adapt the commands below accordingly.

1.1.2. Manual steps

Start Keychain Access application (/Applications/Utilities/Keychain Access.app)

Open the menu item /Keychain Access/Certificate Assistant/Create a Certificate...

Choose a name (gdb-cert in the example), set Identity Type to Self Signed Root, set Certificate Type to Code Signing and select the Let me override defaults. Click several times on Continue until you get to the Specify a Location For The Certificate screen, then set Keychain to System.

💡 If you cannot store the certificate in the System keychain: create it in the login keychain instead, then export it. You can then import it into the System keychain.

Finally, quit the Keychain Access application to refresh the certificate store.

1.1.3. Control

In the terminal type

security find-certificate -c gdb-cert

This should display some details about your newly minted certificate, e.g.

keychain: "/Library/Keychains/System.keychain"
version: 256
class: 0x80001000 
attributes:
    "alis"<blob>="gdb-cert"
[...]

Make sure that keychain: is the System keychain, as shown.

Also, make sure that your certificate is not expired yet:

security find-certificate -p -c gdb-cert | openssl x509 -checkend 0

💡If you want to inspect the entire X509 data structure, you can type

security find-certificate -p -c gdb-cert |openssl x509 -noout -text

1.2. Trust the certificate for code signing

1.2.1. Automated steps

The certificate created by the macos-setup-codesign.sh script mentioned in the previous section is already correctly set up.

1.2.2. Manual steps

Start Keychain Access again. Using the contextual menu for the certificate, select Get Info, open the Trust item, and set Code Signing to Always Trust.

Finally, quit the Keychain Access application once more to refresh the certificate store.

1.2.3. Control

In the terminal type

security dump-trust-settings -d

This should show the gdb-cert certificate (perhaps among others) and its trust settings, including Code Signing.

1.3. Sign and entitle the gdb binary

  1. Create a gdb-entitlement.xml file containing the following:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>com.apple.security.cs.debugger</key>
        <true/>
    </dict>
    </plist>
  2. If the certificate you generated in the previous section is known as gdb-cert, use:

    codesign --entitlements gdb-entitlement.xml -fs gdb-cert $(which gdb)

💡 You may have to prepend this command with sudo if the gdb binary is located in a place that is not writable by regular users.

If you plan to build gdb frequently, this step can be automated by passing --enable-codesign=gdb-cert (assuming, again, that gdb-cert is the name of the certificate) to configure.

Control: in the terminal type

codesign -vv $(which gdb)
codesign -d --entitlements :- $(which gdb)

1.4. Refresh the system's certificates and code-signing data

The most reliable way is to reboot your system.

A less invasive way is to and restart taskgated service by killing the current running taskgated process (at any time in the process, but no later than before trying to run gdb again):

sudo killall taskgated

However, sometimes the taskgated service will not restart successfully after killing it, so ensure that it is alive after this step by checking e.g. ps $(pgrep -f taskgated). Or just reboot your system, as mentioned above.

2. Troubleshooting / further diagnosis

There is plenty of other reasons why gdb might not work on Darwin, besides it not being code-signed with the com.apple.security.cs.debugger entitlement.

If, on the other hand you still get the (os/kern) failure (0x5) error, one of the following suggestions may pinpoint you to the cause.

  • If you are trying to attach to a running program using gdb's attach command, then that program (the target one, not gdb) must have the com.apple.security.get-task-allow entitlement set (in addition to gdb's own entitlement). You can achieve that with codesign using the same instructions and certificate you already used to sign gdb — Just change the entitlement name inside the XML plist file accordingly.

  • Double-check all commands in the checklist and redo the corresponding step in case of trouble

  • You may have also (partly) followed the instructions from the old method section. E.g., if the gdb binary is setgid procmod, the code-signing method will not work anymore. In that case, restore the gdb binary to the original settings with the following commands:

$ chmod 755 gdb
$ chgrp admin gdb

(possibly prepended with sudo if the gdb binary is located in a place that cannot be modified by regular users or if you do not belong to the admin group, and replacing gdb with the full path to the gdb binary)

  • Ensure that you code-signed the actual gdb binary and not just a script that starts this binary (you can code-sign shell scripts too). Check using e.g. the file command that the file you code-signed is a binary and not a text file or shell script.

2.1. Watch Logs

The following command will stream the log messages pertaining to taskgated and code-signing issues:

log stream --predicate 'process = "taskgated" OR (process = "kernel" AND eventMessage CONTAINS "macOSTaskPolicy")' --info


3. Notes for older versions

3.1. High Sierra (10.13)

gdb 8.1 is incompatible with macOS 10.13. Downgrade to gdb 8.0.1 instead

3.2. Leopard (10.5) thru High Sierra (10.13)

These versions do not have entitlement metadata; just signing the gdb binary was enough to make it work. In that case, the codesign command to use is just

codesign -fs gdb-cert $(which gdb)

Accordingly, all checks based on codesign -d --entitlements won't work, so just skip them.

3.3. Tiger (10.4) and before

Warning: do not combine (part of) these instructions with the code signing method, because they interfere with each other. In general, following the instructions in this section is strongly discouraged.

In Mac OS X 10.4 (Tiger), the kernel would let processes whose primary effective group is procmod or procview act as debuggers. That means that making gdb setgid procmod worked.

Later versions of Darwin still accept this convention provided that taskgated (the daemon that controls the access) is invoked with option '-p'. This daemon is configured by /System/Library/LaunchDaemons/com.apple.taskgated.plist, which is where you can add this '-p' option. On OS X 10.11 (El Capitan) and later, this requires (temporarily) disabling rootless mode, otherwise that file cannot be modified.

On OS X 10.10 (Yosemite) and later, you also need to be a member of the Unix group _developer for this method to work. In case your use name is Jack, you can do this with the following command:

$ sudo dscl . merge /Groups/_developer GroupMembership Jack

Follow the instructions above to refresh all subsystems that need to know about this group change.

None: PermissionsDarwin (last edited 2021-08-25 17:02:12 by PhilB)

All content (C) 2008 Free Software Foundation. For terms of use, redistribution, and modification, please see the WikiLicense page.