#include <security_ocspd/ocspd.h>
#include <Security/Authorization.h>
#include <Security/AuthorizationDB.h>
#include <security_utilities/threading.h>
#include <security_utilities/globalizer.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <bsm/libbsm.h>
#include <security_ocspd/ocspdDebug.h>
#include <Security/SecBase.h>
#include <Security/SecTrustSettings.h>
#include <Security/TrustSettingsSchema.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include "ocspdServer.h"
#include <membership.h>
static ModuleNexus<Mutex> gTrustSettingsLock;
#define TRUST_SETTINGS_RIGHT_USER "com.apple.trust-settings.user"
#define TRUST_SETTINGS_RIGHT_ADMIN "com.apple.trust-settings.admin"
#define TRUST_SETTINGS_RULE_USER CFSTR("authenticate-session-owner");
#define TRUST_SETTINGS_RULE_ADMIN CFSTR("is-root");
#define TRUST_SETTINGS_PATH_MODE 0755
#define TRUST_SETTINGS_FILES_MODE 0600
#define UUID_STR_LEN ((2 * sizeof(uuid_t)) + 5)
static void uuidString(
uuid_t uuid,
char *cp)
{
unsigned dex;
unsigned char *uuidCp = (unsigned char *)uuid;
for(dex=0; dex<sizeof(uuid_t); dex++) {
sprintf(cp, "%02X", *uuidCp++);
cp += 2;
switch(dex) {
case 3:
case 5:
case 7:
case 9:
*cp++ = '-';
break;
default:
break;
}
}
*cp = '\0';
}
static void trustSettingsPath(
audit_token_t auditToken,
SecTrustSettingsDomain domain,
char *path)
{
switch(domain) {
case kSecTrustSettingsDomainUser:
{
uid_t euid;
uuid_t uuid;
audit_token_to_au32(auditToken, NULL, &euid, NULL, NULL, NULL, NULL, NULL, NULL);
if(!mbr_uid_to_uuid(euid, uuid)) {
char uuidStr[UUID_STR_LEN];
uuidString(uuid, uuidStr);
snprintf(path, MAXPATHLEN + 1, "%s/%s.plist", TRUST_SETTINGS_PATH, uuidStr);
}
else {
snprintf(path, MAXPATHLEN + 1, "%s/%lu.plist", TRUST_SETTINGS_PATH,
(unsigned long)euid);
}
break;
}
case kSecTrustSettingsDomainAdmin:
snprintf(path, MAXPATHLEN + 1, "%s/%s", TRUST_SETTINGS_PATH, ADMIN_TRUST_SETTINGS);
break;
case kSecTrustSettingsDomainSystem:
strcpy(path, SYSTEM_TRUST_SETTINGS_PATH);
break;
default:
break;
}
}
static mach_port_t gBootstrapPort=MACH_PORT_NULL;
static mach_port_t gAuditSessionPort=MACH_PORT_NULL;
void switchToContext(mach_port_t clientBootstrapPort, audit_token_t auditToken)
{
au_asid_t asid, tmp_asid;
mach_port_t clientAuditSessionPort=MACH_PORT_NULL;
kern_return_t kr;
audit_token_to_au32(auditToken, NULL, NULL, NULL, NULL, NULL, NULL, &asid, NULL);
audit_session_port(asid, &clientAuditSessionPort);
if (gBootstrapPort == MACH_PORT_NULL) {
task_get_bootstrap_port(mach_task_self(), &gBootstrapPort);
}
kr = task_set_bootstrap_port(mach_task_self(), clientBootstrapPort);
if (kr != KERN_SUCCESS)
ocspdErrorLog("Unable to set client bootstrap port\n");
if (gAuditSessionPort == MACH_PORT_NULL) {
gAuditSessionPort = audit_session_self();
mach_port_mod_refs(mach_task_self(), gAuditSessionPort, MACH_PORT_RIGHT_SEND, +1);
}
tmp_asid = audit_session_join(clientAuditSessionPort);
if (tmp_asid == AU_DEFAUDITSID)
ocspdErrorLog("Unable to join client security session\n");
}
void restoreContext()
{
if (gBootstrapPort != MACH_PORT_NULL)
{
kern_return_t kr = task_set_bootstrap_port(mach_task_self(), gBootstrapPort);
if (kr != KERN_SUCCESS)
ocspdErrorLog("Unable to restore server bootstrap port");
}
if (gAuditSessionPort != MACH_PORT_NULL)
{
au_asid_t asid = audit_session_join(gAuditSessionPort);
if (asid == AU_DEFAUDITSID)
ocspdErrorLog("Unable to rejoin original security session");
}
}
kern_return_t ocsp_server_trustSettingsRead(
mach_port_t serverport,
audit_token_t auditToken,
uint32_t domain,
Data *trustSettings,
mach_msg_type_number_t *trustSettingsCnt,
OSStatus *rcode)
{
StLock<Mutex> _(gTrustSettingsLock());
char path[MAXPATHLEN + 1];
trustSettingsPath(auditToken, domain, path);
unsigned char *fileData = NULL;
unsigned fileDataLen;
if(readFile(path, &fileData, &fileDataLen)) {
ocspdTrustDebug("trustSettingsRead: no file at %s", path);
*rcode = errSecNoTrustSettings;
*trustSettings = NULL;
*trustSettingsCnt = 0;
return 0;
}
CSSM_DATA cdata;
Allocator &alloc = OcspdServer::active().alloc();
cdata.Data = (uint8 *)alloc.malloc(fileDataLen);
cdata.Length = fileDataLen;
memmove(cdata.Data, fileData, fileDataLen);
free(fileData);
passDataToCaller(cdata, trustSettings, trustSettingsCnt);
ocspdTrustDebug("trustSettingsRead: read %lu bytes from %s",
(unsigned long)cdata.Length, path);
*rcode = noErr;
return 0;
}
kern_return_t ocsp_server_trustSettingsWrite(
mach_port_t serverport,
audit_token_t auditToken,
mach_port_t clientport,
uint32_t domain,
Data authBlob,
mach_msg_type_number_t authBlobCnt,
Data trustSettings,
mach_msg_type_number_t trustSettingsCnt,
OSStatus *rcode)
{
StLock<Mutex> _(gTrustSettingsLock());
const char *authRight = NULL;
CFStringRef authRule = NULL;
char path[MAXPATHLEN + 1];
trustSettingsPath(auditToken, domain, path);
switch(domain) {
case kSecTrustSettingsDomainUser:
authRight = TRUST_SETTINGS_RIGHT_USER;
authRule = TRUST_SETTINGS_RULE_USER;
break;
case kSecTrustSettingsDomainAdmin:
authRight = TRUST_SETTINGS_RIGHT_ADMIN;
authRule = TRUST_SETTINGS_RULE_ADMIN;
break;
case kSecTrustSettingsDomainSystem:
*rcode = errSecDataNotModifiable;
return 0;
}
AuthorizationExternalForm extForm;
if(authBlobCnt > sizeof(extForm)) {
ocspdErrorLog("trustSettingsWrite: authBlob too big\n");
*rcode = paramErr;
return 0;
}
AuthorizationRef authRef;
OSStatus ortn;
if(AuthorizationRightGet(authRight, NULL)) {
ortn = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
0, &authRef);
if(ortn) {
ocspdErrorLog("trustSettingsWrite: AuthorizationCreate failure\n");
*rcode = internalComponentErr;
return 0;
}
ortn = AuthorizationRightSet(authRef, authRight, authRule,
NULL, NULL, NULL);
if(ortn) {
ocspdErrorLog("trustSettingsWrite: AuthorizationRightSet failure\n");
*rcode = internalComponentErr;
return 0;
}
AuthorizationFree(authRef, 0);
}
memmove(&extForm, authBlob, authBlobCnt);
ortn = AuthorizationCreateFromExternalForm(&extForm, &authRef);
if(ortn) {
ocspdErrorLog("trustSettingsWrite: AuthorizationCreateFromExternalForm failure\n");
*rcode = paramErr;
return 0;
}
AuthorizationItem authItem = {authRight, 0, NULL, 0};
AuthorizationRights authRights = { 1, &authItem };
AuthorizationFlags authFlags = kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagExtendRights;
switchToContext(clientport, auditToken);
ortn = AuthorizationCopyRights(authRef, &authRights, NULL,
authFlags, NULL);
restoreContext();
if(ortn) {
ocspdErrorLog("trustSettingsWrite: AuthorizationCopyRights failure\n");
}
AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
if(ortn) {
*rcode = ortn;
return 0;
}
if(trustSettingsCnt == 0) {
ocspdTrustDebug("trustSettingsWrite: DELETING %s", path);
if(unlink(path)) {
*rcode = errno;
ocspdErrorLog("trustSettingsWrite: unlink error %d\n", errno);
}
else {
*rcode = noErr;
}
return 0;
}
struct stat sb;
if(stat(TRUST_SETTINGS_PATH, &sb)) {
ocspdTrustDebug("trustSettingsWrite: creating %s", TRUST_SETTINGS_PATH);
int errcode = mkpath_np(TRUST_SETTINGS_PATH, TRUST_SETTINGS_PATH_MODE);
if(errcode) {
ocspdErrorLog("trustSettingsWrite: mkpath_np() returned %d\n", errcode);
*rcode = internalComponentErr;
return 0;
}
chmod(TRUST_SETTINGS_PATH, TRUST_SETTINGS_PATH_MODE);
}
if(writeFile(path, (const unsigned char *)trustSettings, trustSettingsCnt)) {
ocspdErrorLog("trustSettingsWrite: writeFile() error\n");
*rcode = internalComponentErr;
}
else {
ocspdTrustDebug("trustSettingsWrite: wrote %lu bytes to %s",
(unsigned long)trustSettingsCnt, path);
chmod(path, TRUST_SETTINGS_FILES_MODE);
*rcode = noErr;
}
return 0;
}