*/
#include "SecTask.h"
+#include "SecTaskPriv.h"
#include <utilities/debugging.h>
#include <syslog.h>
#include <utilities/SecCFWrappers.h>
-#define USE_LIBPROC 0
-#if USE_LIBPROC
-#include <libproc.h>
-#else
#include <sys/sysctl.h>
-#endif
+
+#if TARGET_OS_OSX
+/* These won't exist until we unify codesigning */
+#include "SecCode.h"
+#include "SecCodePriv.h"
+#include "SecRequirement.h"
+#endif /* TARGET_OS_OSX */
struct __SecTask {
CFRuntimeBase base;
- pid_t pid_self;
audit_token_t token;
/* Track whether we've loaded entitlements independently since after the
* load, entitlements may legitimately be NULL */
Boolean entitlementsLoaded;
CFDictionaryRef entitlements;
+
+ /* for debugging only, shown by debugDescription */
+ int lastFailure;
};
static bool check_task(SecTaskRef task) {
static void SecTaskFinalize(CFTypeRef cfTask)
{
SecTaskRef task = (SecTaskRef) cfTask;
-
- if (task->entitlements != NULL) {
- CFRelease(task->entitlements);
- task->entitlements = NULL;
- }
+ CFReleaseNull(task->entitlements);
}
{
SecTaskRef task = (SecTaskRef) cfTask;
pid_t pid;
- if (task->pid_self==-1) {
- audit_token_to_au32(task->token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
- } else {
- pid = task->pid_self;
- }
+ audit_token_to_au32(task->token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
-#if USE_LIBPROC
-#define MAX_PROCNAME 32
- char task_name[MAX_PROCNAME + 1] = {};
- proc_name(pid, task_name, MAX_PROCNAME);
-#else
const char *task_name;
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
struct kinfo_proc kp;
task_name = strerror(errno);
else
task_name = kp.kp_proc.p_comm;
-#endif
- return CFStringCreateWithFormat(CFGetAllocator(task), NULL, CFSTR("%s[%" PRIdPID "]"), task_name, pid);
+ return CFStringCreateWithFormat(CFGetAllocator(task), NULL, CFSTR("%s[%" PRIdPID "]/%d#%d LF=%d"), task_name, pid,
+ task->entitlementsLoaded, task->entitlements ? (int)CFDictionaryGetCount(task->entitlements) : -1, task->lastFailure);
}
CFGiblisWithFunctions(SecTask, NULL, NULL, SecTaskFinalize, NULL, NULL, NULL, SecTaskCopyDebugDescription, NULL, NULL, NULL)
SecTaskRef task = init_task_ref(allocator);
if (task != NULL) {
- memset(&task->token, 0, sizeof(task->token));
- task->entitlementsLoaded = false;
- task->entitlements = NULL;
- task->pid_self = getpid();
+ kern_return_t kr = KERN_FAILURE;
+ mach_msg_type_number_t autoken_cnt = TASK_AUDIT_TOKEN_COUNT;
+ kr = task_info(mach_task_self(), TASK_AUDIT_TOKEN, (task_info_t)&task->token, &autoken_cnt);
+ if (kr == KERN_SUCCESS) {
+ task->entitlementsLoaded = false;
+ task->entitlements = NULL;
+ } else {
+ CFReleaseNull(task);
+ }
}
return task;
memcpy(&task->token, &token, sizeof(token));
task->entitlementsLoaded = false;
task->entitlements = NULL;
- task->pid_self = -1;
}
return task;
static int
csops_task(SecTaskRef task, int ops, void *blob, size_t size)
{
- if (task->pid_self==-1) {
- pid_t pid;
- audit_token_to_au32(task->token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
- return csops_audittoken(pid, ops, blob, size, &task->token);
- }
- else
- return csops(task->pid_self, ops, blob, size);
+ int rc;
+
+ pid_t pid;
+ audit_token_to_au32(task->token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
+ rc = csops_audittoken(pid, ops, blob, size, &task->token);
+
+ task->lastFailure = (rc == -1) ? errno : 0;
+ return rc;
}
-/* This may need to be exported at some point */
-CFStringRef
-SecTaskCopySigningIdentifier(SecTaskRef task, CFErrorRef *error)
+static CFStringRef
+SecTaskCopyIdentifier(SecTaskRef task, int op, CFErrorRef *error)
{
CFStringRef signingId = NULL;
char *data = NULL;
uint32_t bufferlen;
int ret;
- ret = csops_task(task, CS_OPS_IDENTITY, &header, sizeof(header));
+ ret = csops_task(task, op, &header, sizeof(header));
if (ret != -1 || errno != ERANGE)
return NULL;
ret = ENOMEM;
goto out;
}
- ret = csops_task(task, CS_OPS_IDENTITY, data, bufferlen);
+ ret = csops_task(task, op, data, bufferlen);
if (ret) {
ret = errno;
goto out;
return signingId;
}
+CFStringRef
+SecTaskCopySigningIdentifier(SecTaskRef task, CFErrorRef *error)
+{
+ return SecTaskCopyIdentifier(task, CS_OPS_IDENTITY, error);
+}
+
+CFStringRef
+SecTaskCopyTeamIdentifier(SecTaskRef task, CFErrorRef *error)
+{
+ return SecTaskCopyIdentifier(task, CS_OPS_TEAMID, error);
+}
+
+uint32_t
+SecTaskGetCodeSignStatus(SecTaskRef task)
+{
+ uint32_t flags = 0;
+ if (csops_task(task, CS_OPS_STATUS, &flags, sizeof(flags)) != 0)
+ return 0;
+ return flags;
+}
static bool SecTaskLoadEntitlements(SecTaskRef task, CFErrorRef *error)
{
syslog(LOG_NOTICE, "Failed to get cs_flags, error=%d", errno);
}
- syslog(LOG_NOTICE, "SecTaskLoadEntitlements failed error=%d cs_flags=%x, task->pid_self=%d", entitlementErrno, cs_flags, task->pid_self); // to ease diagnostics
+ if (cs_flags != 0) { // was signed
- CFStringRef description = SecTaskCopyDebugDescription(task);
- char *descriptionBuf = NULL;
- CFIndex descriptionSize = CFStringGetLength(description) * 4;
- descriptionBuf = (char *)malloc(descriptionSize);
- if (!CFStringGetCString(description, descriptionBuf, descriptionSize, kCFStringEncodingUTF8)) {
- descriptionBuf[0] = 0;
- }
+ pid_t pid;
+ audit_token_to_au32(task->token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
+ syslog(LOG_NOTICE, "SecTaskLoadEntitlements failed error=%d cs_flags=%x, pid=%d", entitlementErrno, cs_flags, pid); // to ease diagnostics
- syslog(LOG_NOTICE, "SecTaskCopyDebugDescription: %s", descriptionBuf);
- CFRelease(description);
- free(descriptionBuf);
+ CFStringRef description = SecTaskCopyDebugDescription(task);
+ char *descriptionBuf = NULL;
+ CFIndex descriptionSize = CFStringGetLength(description) * 4;
+ descriptionBuf = (char *)malloc(descriptionSize);
+ if (!CFStringGetCString(description, descriptionBuf, descriptionSize, kCFStringEncodingUTF8)) {
+ descriptionBuf[0] = 0;
+ }
+
+ syslog(LOG_NOTICE, "SecTaskCopyDebugDescription: %s", descriptionBuf);
+ CFReleaseNull(description);
+ free(descriptionBuf);
+ }
+ task->lastFailure = entitlementErrno; // was overwritten by csops_task(CS_OPS_STATUS) above
// EINVAL is what the kernel says for unsigned code, so we'll have to let that pass
if (entitlementErrno == EINVAL) {
bufferlen = ntohl(header.length);
/* check for insane values */
if (bufferlen > 1024 * 1024 || bufferlen < 8) {
- ret = EINVAL;
+ ret = E2BIG;
goto out;
}
buffer = malloc(bufferlen);
CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer+8, bufferlen-8, kCFAllocatorNull);
entitlements = (CFMutableDictionaryRef) CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListMutableContainers, NULL, error);
- CFRelease(data);
+ CFReleaseNull(data);
if((entitlements==NULL) || (CFGetTypeID(entitlements)!=CFDictionaryGetTypeID())){
- ret = EINVAL;
+ ret = EDOM; // don't use EINVAL here; it conflates problems with syscall error returns
goto out;
}
}
task->entitlementsLoaded = true;
out:
- if(entitlements)
- CFRelease(entitlements);
+ CFReleaseNull(entitlements);
if(buffer)
free(buffer);
if (ret && error && *error==NULL)
out:
return values;
}
+
+#if TARGET_OS_OSX
+/*
+ * Determine if the given task meets a specified requirement.
+ */
+OSStatus
+SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement)
+{
+ OSStatus status;
+ SecCodeRef code = NULL;
+ SecRequirementRef req = NULL;
+
+ CFMutableDictionaryRef codeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFDataRef auditData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)&task->token, sizeof(audit_token_t));
+ CFDictionarySetValue(codeDict, kSecGuestAttributeAudit, auditData);
+ status = SecCodeCopyGuestWithAttributes(NULL, codeDict, kSecCSDefaultFlags, &code);
+ CFReleaseNull(codeDict);
+ CFReleaseNull(auditData);
+
+ if (!status) {
+ status = SecRequirementCreateWithString(requirement,
+ kSecCSDefaultFlags, &req);
+ }
+ if (!status) {
+ status = SecCodeCheckValidity(code, kSecCSDefaultFlags, req);
+ }
+
+ CFReleaseNull(req);
+ CFReleaseNull(code);
+
+ return status;
+}
+#endif /* TARGET_OS_OSX */
+
+Boolean SecTaskEntitlementsValidated(SecTaskRef task) {
+ // TODO: Cache the result
+ uint32_t csflags = 0;
+ const uint32_t mask = CS_VALID | CS_KILL | CS_ENTITLEMENTS_VALIDATED;
+ const uint32_t debug_mask = CS_DEBUGGED | CS_ENTITLEMENTS_VALIDATED;
+ int rc = csops_task(task, CS_OPS_STATUS, &csflags, sizeof(csflags));
+ // Allow debugged processes that were valid to continue being treated as valid
+ // We need this all the time (not just on internal) because third parties may need to debug their entitled process in xcode
+ return (rc != -1) && ((mask & csflags) == mask || (debug_mask & csflags) == debug_mask);
+}
+