Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Factors out XPC server message acceptance logic #39

Merged
merged 2 commits into from
Jan 9, 2022
Merged

Conversation

jakaplan
Copy link
Contributor

@jakaplan jakaplan commented Jan 6, 2022

Factors out the security logic into a SecureMessageAcceptor struct.

Resolves #36

Factors out the security logic into a SecureMessageAcceptor struct.
@jakaplan
Copy link
Contributor Author

jakaplan commented Jan 6, 2022

@amomchilov when you have the time, would appreciate you taking a look. I went with a compositional approach over inheritance as it seemed simpler to me.

if #available(macOS 11, *) { // publicly documented, but only available since macOS 11
SecCodeCreateWithXPCMessage(message, SecCSFlags(), &code)
} else { // private undocumented function: xpc_connection_get_audit_token, available on prior versions of macOS
if var auditToken = xpc_connection_get_audit_token(connection) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you add an else case, then code will be initialized in all code paths, removing the need for it to be optional.

Better yet, you should just flip this with a guard as well: guard var auditToken = ... else { return false }

Copy link
Contributor Author

@jakaplan jakaplan Jan 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is code needs to be optional because both SecCodeCreateWithXPCMessage and SecCodeCopyGuestWithAttributes expect a parameter of type UnsafeMutablePointer<SecCode?> which can't be satisfied with a non-optional type. Is there a way to make this work?

Incorporated feedback on second point about guard statement.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AH I didn't know that was the expected param type. Usually optional promotion happens, but not in the case of using & to generate a temporary pointer. You'd have to check, maybe it does do optional promotion in that case too, IDR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately it's not allowed. Xcode outputs:

Cannot convert value of type 'UnsafeMutablePointer' to expected argument type 'UnsafeMutablePointer<SecCode?>'

Comment on lines 30 to 40
// Accept message if code is valid and meets any of the client requirements
var accept = false
if let code = code {
for requirement in self.requirements {
if SecCodeCheckValidity(code, SecCSFlags(), requirement) == errSecSuccess {
accept = true
}
}
}

return accept
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This for loop is approximating contains(where:) (but without the short-circuiting behaviour)

Suggested change
// Accept message if code is valid and meets any of the client requirements
var accept = false
if let code = code {
for requirement in self.requirements {
if SecCodeCheckValidity(code, SecCSFlags(), requirement) == errSecSuccess {
accept = true
}
}
}
return accept
guard let code = code else { return false }
return self.requirements.contains(where: { SecCodeCheckValidity(code, SecCSFlags(), $0) == errSecSuccess }

Comment on lines +52 to +61
private func xpc_connection_get_audit_token(_ connection: xpc_connection_t) -> audit_token_t? {
// Attempt to dynamically load the function
guard let handle = dlopen(nil, RTLD_LAZY) else {
return nil
}
defer { dlclose(handle) }
guard let sym = dlsym(handle, "xpc_connection_get_audit_token") else {
return nil
}
typealias functionSignature = @convention(c) (xpc_connection_t, UnsafeMutablePointer<audit_token_t>) -> Void
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets called on every message. It's probably worth caching the resulting function, thought that would be best done in a follow up PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had the same thought while refactoring this, but didn't want to make such a change as part of this PR.

/// Accepts messages which meet the provided code signing requirements.
///
/// Uses undocumented functionality prior to macOS 11.
internal struct SecureMessageAcceptor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be behind a protocol, so you can make an "always allow" implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally had it written that way, but it ended up with quite a bit more code across multiple files so it didn't seem worth the complexity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. If we need it in the future, we can just do an "extract interface" refactor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, can absolutely refactor in the future if warranted

@amomchilov
Copy link
Contributor

amomchilov commented Jan 9, 2022

Meta point: I would suggest:

  1. You make the PR title more useful, to make it more clear when it shows up. E.g., this is part of my GitHub inbox:

    Screen Shot 2022-01-09 at 2 06 45 PM

    Perhaps "Factor out SecureMessageAcceptor"?

  2. Use one of the recognized keywords (listed here) in the PR description, to make GitHub automatically link this PR to the issue. E.g. Resolves #36.

@jakaplan jakaplan changed the title Addresses issue #36 Factors out XPC server message acceptance logic Jan 9, 2022
@jakaplan jakaplan merged commit e1a68f2 into main Jan 9, 2022
@jakaplan jakaplan deleted the message-acceptor branch January 9, 2022 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for client security requirements in anonymous services
2 participants