X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/e0e0d90ebff497686991a933ae2f7db24e7d8e0f..805875f8be937062d519339f1e38e2c2e81d06e4:/sectask/SecTask.c diff --git a/sectask/SecTask.c b/sectask/SecTask.c index cba86072..a73416c7 100644 --- a/sectask/SecTask.c +++ b/sectask/SecTask.c @@ -22,6 +22,7 @@ */ #include "SecTask.h" +#include "SecTaskPriv.h" #include @@ -34,24 +35,29 @@ #include #include #include +#include -#define USE_LIBPROC 0 -#if USE_LIBPROC -#include -#else #include -#endif + +#if TARGET_OS_OSX +/* These won't exist until we unify codesigning */ +#include +#include +#include +#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) { @@ -61,11 +67,7 @@ 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); } @@ -76,17 +78,8 @@ static CFStringRef SecTaskCopyDebugDescription(CFTypeRef cfTask) { 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; @@ -95,9 +88,9 @@ static CFStringRef SecTaskCopyDebugDescription(CFTypeRef cfTask) 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) @@ -113,10 +106,15 @@ SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator) 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; @@ -130,12 +128,26 @@ SecTaskRef SecTaskCreateWithAuditToken(CFAllocatorRef allocator, audit_token_t t memcpy(&task->token, &token, sizeof(token)); task->entitlementsLoaded = false; task->entitlements = NULL; - task->pid_self = -1; } return task; } +_Nullable SecTaskRef +SecTaskCreateWithXPCMessage(xpc_object_t _Nonnull message) +{ + audit_token_t token; + + if (message == NULL || xpc_get_type(message) != XPC_TYPE_DICTIONARY) { + return NULL; + } + xpc_dictionary_get_audit_token(message, &token); + + return SecTaskCreateWithAuditToken(NULL, token); +} + + + struct csheader { uint32_t magic; uint32_t length; @@ -144,18 +156,18 @@ struct csheader { 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; @@ -163,7 +175,7 @@ SecTaskCopySigningIdentifier(SecTaskRef task, CFErrorRef *error) 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; @@ -178,7 +190,7 @@ SecTaskCopySigningIdentifier(SecTaskRef task, CFErrorRef *error) 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; @@ -196,6 +208,26 @@ SecTaskCopySigningIdentifier(SecTaskRef task, CFErrorRef *error) 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) { @@ -217,19 +249,25 @@ 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) { @@ -242,7 +280,7 @@ static bool SecTaskLoadEntitlements(SecTaskRef task, CFErrorRef *error) bufferlen = ntohl(header.length); /* check for insane values */ if (bufferlen > 1024 * 1024 || bufferlen < 8) { - ret = EINVAL; + ret = E2BIG; goto out; } buffer = malloc(bufferlen); @@ -258,10 +296,10 @@ static bool SecTaskLoadEntitlements(SecTaskRef task, CFErrorRef *error) 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; } } @@ -270,8 +308,7 @@ static bool SecTaskLoadEntitlements(SecTaskRef task, CFErrorRef *error) task->entitlementsLoaded = true; out: - if(entitlements) - CFRelease(entitlements); + CFReleaseNull(entitlements); if(buffer) free(buffer); if (ret && error && *error==NULL) @@ -332,3 +369,48 @@ CFDictionaryRef SecTaskCopyValuesForEntitlements(SecTaskRef task, CFArrayRef ent out: return values; } + +#if SEC_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 /* SEC_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); +} +