/* Copyright (c) 2012 Apple Inc. All rights reserved. */ #include "mechanism.h" #include "authdb.h" #include "authutilities.h" #include "crc.h" #include "debugging.h" #include "server.h" #include "authitems.h" #define MECHANISM_ID "id" #define MECHANISM_PLUGIN "plugin" #define MECHANISM_PARAM "param" #define MECHANISM_PRIVILEGED "privileged" static const char SystemPlugins[] = "/System/Library/CoreServices/SecurityAgentPlugins"; static const char LibraryPlugins[] = "/Library/Security/SecurityAgentPlugins"; static const char BuiltinMechanismPrefix[] = "builtin"; typedef struct _mechTypeItem { const char * name; uint64_t type; } mechTypeItem; static mechTypeItem mechTypeMap[] = { { "entitled", kMechanismTypeEntitled } }; struct _mechanism_s { __AUTH_BASE_STRUCT_HEADER__; auth_items_t data; bool valid; char * string; uint64_t type; }; static void _mechanism_finalize(CFTypeRef value) { mechanism_t mech = (mechanism_t)value; CFReleaseSafe(mech->data); free_safe(mech->string); } static Boolean _mechanism_equal(CFTypeRef value1, CFTypeRef value2) { mechanism_t mech1 = (mechanism_t)value1; mechanism_t mech2 = (mechanism_t)value2; if (mech1 == mech2) { return true; } if (!_compare_string(mechanism_get_plugin(mech1), mechanism_get_plugin(mech2))) { return false; } if (!_compare_string(mechanism_get_param(mech1), mechanism_get_param(mech2))) { return false; } return mechanism_is_privileged(mech1) == mechanism_is_privileged(mech2); } static CFStringRef _mechanism_copy_description(CFTypeRef value) { mechanism_t mech = (mechanism_t)value; return CFCopyDescription(mech->data); } static CFHashCode _mechanism_hash(CFTypeRef value) { uint64_t crc = crc64_init(); mechanism_t mech = (mechanism_t)value; const char * str = mechanism_get_plugin(mech); crc = crc64_update(crc, str, strlen(str)); str = mechanism_get_plugin(mech); crc = crc64_update(crc, str, strlen(str)); bool priv = mechanism_is_privileged(mech); crc = crc64_update(crc, &priv, sizeof(priv)); crc = crc64_final(crc); return crc; } AUTH_TYPE_INSTANCE(mechanism, .init = NULL, .copy = NULL, .finalize = _mechanism_finalize, .equal = _mechanism_equal, .hash = _mechanism_hash, .copyFormattingDesc = NULL, .copyDebugDesc = _mechanism_copy_description ); static CFTypeID mechanism_get_type_id() { static CFTypeID type_id = _kCFRuntimeNotATypeID; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ type_id = _CFRuntimeRegisterClass(&_auth_type_mechanism); }); return type_id; } static mechanism_t _mechanism_create() { mechanism_t mech = (mechanism_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, mechanism_get_type_id(), AUTH_CLASS_SIZE(mechanism), NULL); require(mech != NULL, done); mech->data = auth_items_create(); done: return mech; } static void _mechanism_set_type(mechanism_t mech) { const char * plugin = mechanism_get_plugin(mech); const char * param = mechanism_get_param(mech); if (strncasecmp(plugin, BuiltinMechanismPrefix, sizeof(BuiltinMechanismPrefix)) == 0) { size_t n = sizeof(mechTypeMap)/sizeof(mechTypeItem); for (size_t i = 0; i < n; i++) { if (strcasecmp(mechTypeMap[i].name, param) == 0) { mech->type = mechTypeMap[i].type; break; } } } } mechanism_t mechanism_create_with_sql(auth_items_t sql) { mechanism_t mech = NULL; require(sql != NULL, done); require(auth_items_get_int64(sql, MECHANISM_ID) != 0, done); mech = _mechanism_create(); require(mech != NULL, done); auth_items_copy(mech->data, sql); _mechanism_set_type(mech); done: return mech; } mechanism_t mechanism_create_with_string(const char * str, authdb_connection_t dbconn) { mechanism_t mech = NULL; require(str != NULL, done); require(strchr(str,':') != NULL, done); mech = _mechanism_create(); require(mech != NULL, done); const char delimiters[] = ":,"; size_t buf_len = strlen(str)+1; char * buf = (char*)calloc(1u, buf_len); strlcpy(buf, str, buf_len); char * tok = strtok(buf, delimiters); if (tok) { auth_items_set_string(mech->data, MECHANISM_PLUGIN, tok); } tok = strtok(NULL, delimiters); if (tok) { auth_items_set_string(mech->data, MECHANISM_PARAM, tok); } tok = strtok(NULL, delimiters); if (tok) { auth_items_set_int64(mech->data, MECHANISM_PRIVILEGED, strcasecmp("privileged", tok) == 0); } free(buf); if (dbconn) { mechanism_sql_fetch(mech, dbconn); } _mechanism_set_type(mech); done: return mech; } static bool _pluginExists(const char * plugin, const char * base) { bool result = false; require(plugin != NULL, done); require(base != NULL, done); char filePath[PATH_MAX]; char realPath[PATH_MAX+1]; snprintf(filePath, sizeof(filePath), "%s/%s.bundle", base, plugin); require(realpath(filePath, realPath) != NULL, done); require(strncmp(realPath, base, strlen(base)) == 0, done); if (access(filePath, F_OK) == 0) { result = true; } done: return result; } bool mechanism_exists(mechanism_t mech) { if (mech->valid) { return true; } const char * plugin = mechanism_get_plugin(mech); if (plugin == NULL) { return false; } if (strncasecmp(plugin, BuiltinMechanismPrefix, sizeof(BuiltinMechanismPrefix)) == 0) { mech->valid = true; return true; } if (_pluginExists(plugin, SystemPlugins)) { mech->valid = true; return true; } if (_pluginExists(plugin,LibraryPlugins)) { mech->valid = true; return true; } return false; } bool mechanism_sql_fetch(mechanism_t mech, authdb_connection_t dbconn) { __block bool result = false; authdb_step(dbconn, "SELECT id FROM mechanisms WHERE plugin = ? AND param = ? AND privileged = ? LIMIT 1", ^(sqlite3_stmt * stmt) { sqlite3_bind_text(stmt, 1, mechanism_get_plugin(mech), -1, NULL); sqlite3_bind_text(stmt, 2, mechanism_get_param(mech), -1, NULL); sqlite3_bind_int(stmt, 3, mechanism_is_privileged(mech)); }, ^bool(auth_items_t data) { result = true; auth_items_copy(mech->data, data); return true; }); return result; } bool mechanism_sql_commit(mechanism_t mech, authdb_connection_t dbconn) { bool result = false; result = authdb_step(dbconn, "INSERT INTO mechanisms VALUES (NULL,?,?,?)", ^(sqlite3_stmt *stmt) { sqlite3_bind_text(stmt, 1, mechanism_get_plugin(mech), -1, NULL); sqlite3_bind_text(stmt, 2, mechanism_get_param(mech), -1, NULL); sqlite3_bind_int(stmt, 3, mechanism_is_privileged(mech)); }, NULL); return result; } const char * mechanism_get_string(mechanism_t mech) { if (!mech->string) { asprintf(&mech->string, "%s:%s%s", mechanism_get_plugin(mech), mechanism_get_param(mech), mechanism_is_privileged(mech) ? ",privileged" : ""); } return mech->string; } int64_t mechanism_get_id(mechanism_t mech) { return auth_items_get_int64(mech->data, MECHANISM_ID); } const char * mechanism_get_plugin(mechanism_t mech) { return auth_items_get_string(mech->data, MECHANISM_PLUGIN); } const char * mechanism_get_param(mechanism_t mech) { return auth_items_get_string(mech->data, MECHANISM_PARAM); } bool mechanism_is_privileged(mechanism_t mech) { return auth_items_get_int64(mech->data, MECHANISM_PRIVILEGED); } uint64_t mechanism_get_type(mechanism_t mech) { return mech->type; }