1/* Copyright (c) 2012 Apple Inc. All rights reserved. */
2
3#include "mechanism.h"
4#include "authdb.h"
5#include "authutilities.h"
6#include "crc.h"
7#include "debugging.h"
8#include "server.h"
9#include "authitems.h"
10
11#define MECHANISM_ID "id"
12#define MECHANISM_PLUGIN "plugin"
13#define MECHANISM_PARAM "param"
14#define MECHANISM_PRIVILEGED "privileged"
15
16static const char SystemPlugins[] = "/System/Library/CoreServices/SecurityAgentPlugins";
17static const char LibraryPlugins[] = "/Library/Security/SecurityAgentPlugins";
18static const char BuiltinMechanismPrefix[] = "builtin";
19
20typedef struct _mechTypeItem
21{
22    const char * name;
23    uint64_t type;
24} mechTypeItem;
25
26static mechTypeItem mechTypeMap[] =
27{
28    { "entitled", kMechanismTypeEntitled }
29};
30
31struct _mechanism_s {
32    __AUTH_BASE_STRUCT_HEADER__;
33
34    auth_items_t data;
35
36    bool valid;
37    char * string;
38
39    uint64_t type;
40};
41
42static void
43_mechanism_finalize(CFTypeRef value)
44{
45    mechanism_t mech = (mechanism_t)value;
46
47    CFReleaseSafe(mech->data);
48    free_safe(mech->string);
49}
50
51static Boolean
52_mechanism_equal(CFTypeRef value1, CFTypeRef value2)
53{
54    mechanism_t mech1 = (mechanism_t)value1;
55    mechanism_t mech2 = (mechanism_t)value2;
56
57    if (mech1 == mech2) {
58        return true;
59    }
60
61    if (!_compare_string(mechanism_get_plugin(mech1), mechanism_get_plugin(mech2))) {
62        return false;
63    }
64
65    if (!_compare_string(mechanism_get_param(mech1), mechanism_get_param(mech2))) {
66        return false;
67    }
68
69    return mechanism_is_privileged(mech1) == mechanism_is_privileged(mech2);
70}
71
72static CFStringRef
73_mechanism_copy_description(CFTypeRef value)
74{
75    mechanism_t mech = (mechanism_t)value;
76    return CFCopyDescription(mech->data);
77}
78
79static CFHashCode
80_mechanism_hash(CFTypeRef value)
81{
82    uint64_t crc = crc64_init();
83    mechanism_t mech = (mechanism_t)value;
84
85    const char * str = mechanism_get_plugin(mech);
86    crc = crc64_update(crc, str, strlen(str));
87    str = mechanism_get_plugin(mech);
88    crc = crc64_update(crc, str, strlen(str));
89    bool priv = mechanism_is_privileged(mech);
90    crc = crc64_update(crc, &priv, sizeof(priv));
91    crc = crc64_final(crc);
92
93    return crc;
94}
95
96AUTH_TYPE_INSTANCE(mechanism,
97                   .init = NULL,
98                   .copy = NULL,
99                   .finalize = _mechanism_finalize,
100                   .equal = _mechanism_equal,
101                   .hash = _mechanism_hash,
102                   .copyFormattingDesc = NULL,
103                   .copyDebugDesc = _mechanism_copy_description
104                   );
105
106static CFTypeID mechanism_get_type_id() {
107    static CFTypeID type_id = _kCFRuntimeNotATypeID;
108    static dispatch_once_t onceToken;
109
110    dispatch_once(&onceToken, ^{
111        type_id = _CFRuntimeRegisterClass(&_auth_type_mechanism);
112    });
113
114    return type_id;
115}
116
117static mechanism_t
118_mechanism_create()
119{
120    mechanism_t mech = (mechanism_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, mechanism_get_type_id(), AUTH_CLASS_SIZE(mechanism), NULL);
121    require(mech != NULL, done);
122
123    mech->data = auth_items_create();
124
125done:
126    return mech;
127}
128
129static void _mechanism_set_type(mechanism_t mech)
130{
131    const char * plugin = mechanism_get_plugin(mech);
132    const char * param = mechanism_get_param(mech);
133    if (strncasecmp(plugin, BuiltinMechanismPrefix, sizeof(BuiltinMechanismPrefix)) == 0) {
134        size_t n = sizeof(mechTypeMap)/sizeof(mechTypeItem);
135        for (size_t i = 0; i < n; i++) {
136            if (strcasecmp(mechTypeMap[i].name, param) == 0) {
137                mech->type = mechTypeMap[i].type;
138                break;
139            }
140        }
141    }
142}
143
144mechanism_t
145mechanism_create_with_sql(auth_items_t sql)
146{
147    mechanism_t mech = NULL;
148    require(sql != NULL, done);
149    require(auth_items_get_int64(sql, MECHANISM_ID) != 0, done);
150
151    mech = _mechanism_create();
152    require(mech != NULL, done);
153
154    auth_items_copy(mech->data, sql);
155
156    _mechanism_set_type(mech);
157
158done:
159    return mech;
160}
161
162mechanism_t
163mechanism_create_with_string(const char * str, authdb_connection_t dbconn)
164{
165    mechanism_t mech = NULL;
166    require(str != NULL, done);
167    require(strchr(str,':') != NULL, done);
168
169    mech = _mechanism_create();
170    require(mech != NULL, done);
171
172    const char delimiters[] = ":,";
173    size_t buf_len = strlen(str)+1;
174    char * buf = (char*)calloc(1u, buf_len);
175    strlcpy(buf, str, buf_len);
176
177    char * tok = strtok(buf, delimiters);
178    if (tok) {
179        auth_items_set_string(mech->data, MECHANISM_PLUGIN, tok);
180    }
181    tok = strtok(NULL, delimiters);
182    if (tok) {
183        auth_items_set_string(mech->data, MECHANISM_PARAM, tok);
184    }
185    tok = strtok(NULL, delimiters);
186    if (tok) {
187        auth_items_set_int64(mech->data, MECHANISM_PRIVILEGED, strcasecmp("privileged", tok) == 0);
188    }
189    free(buf);
190
191    if (dbconn) {
192        mechanism_sql_fetch(mech, dbconn);
193    }
194
195    _mechanism_set_type(mech);
196
197done:
198    return mech;
199}
200
201static
202bool _pluginExists(const char * plugin, const char * base)
203{
204    bool result = false;
205
206    require(plugin != NULL, done);
207    require(base != NULL, done);
208
209    char filePath[PATH_MAX];
210    char realPath[PATH_MAX+1];
211    snprintf(filePath, sizeof(filePath), "%s/%s.bundle", base, plugin);
212
213    require(realpath(filePath, realPath) != NULL, done);
214    require(strncmp(realPath, base, strlen(base)) == 0, done);
215
216    if (access(filePath, F_OK) == 0) {
217        result = true;
218    }
219
220done:
221    return result;
222}
223
224bool
225mechanism_exists(mechanism_t mech)
226{
227    if (mech->valid) {
228        return true;
229    }
230
231    const char * plugin = mechanism_get_plugin(mech);
232    if (plugin == NULL) {
233        return false;
234    }
235
236    if (strncasecmp(plugin, BuiltinMechanismPrefix, sizeof(BuiltinMechanismPrefix)) == 0) {
237        mech->valid = true;
238        return true;
239    }
240
241    if (_pluginExists(plugin, SystemPlugins)) {
242        mech->valid = true;
243        return true;
244    }
245
246    if (_pluginExists(plugin,LibraryPlugins)) {
247        mech->valid = true;
248        return true;
249    }
250
251    return false;
252}
253
254bool
255mechanism_sql_fetch(mechanism_t mech, authdb_connection_t dbconn)
256{
257    __block bool result = false;
258
259    authdb_step(dbconn, "SELECT id FROM mechanisms WHERE plugin = ? AND param = ? AND privileged = ? LIMIT 1", ^(sqlite3_stmt * stmt) {
260        sqlite3_bind_text(stmt, 1, mechanism_get_plugin(mech), -1, NULL);
261        sqlite3_bind_text(stmt, 2, mechanism_get_param(mech), -1, NULL);
262        sqlite3_bind_int(stmt, 3, mechanism_is_privileged(mech));
263    }, ^bool(auth_items_t data) {
264        result = true;
265        auth_items_copy(mech->data, data);
266        return true;
267    });
268
269    return result;
270}
271
272bool
273mechanism_sql_commit(mechanism_t mech, authdb_connection_t dbconn)
274{
275    bool result = false;
276
277    result = authdb_step(dbconn, "INSERT INTO mechanisms VALUES (NULL,?,?,?)", ^(sqlite3_stmt *stmt) {
278        sqlite3_bind_text(stmt, 1, mechanism_get_plugin(mech), -1, NULL);
279        sqlite3_bind_text(stmt, 2, mechanism_get_param(mech), -1, NULL);
280        sqlite3_bind_int(stmt, 3, mechanism_is_privileged(mech));
281    }, NULL);
282
283    return result;
284}
285
286const char *
287mechanism_get_string(mechanism_t mech)
288{
289    if (!mech->string) {
290        asprintf(&mech->string, "%s:%s%s", mechanism_get_plugin(mech), mechanism_get_param(mech), mechanism_is_privileged(mech) ? ",privileged" : "");
291    }
292
293    return mech->string;
294}
295
296int64_t
297mechanism_get_id(mechanism_t mech)
298{
299    return auth_items_get_int64(mech->data, MECHANISM_ID);
300}
301
302const char *
303mechanism_get_plugin(mechanism_t mech)
304{
305    return auth_items_get_string(mech->data, MECHANISM_PLUGIN);
306}
307
308const char *
309mechanism_get_param(mechanism_t mech)
310{
311    return auth_items_get_string(mech->data, MECHANISM_PARAM);
312}
313
314bool
315mechanism_is_privileged(mechanism_t mech)
316{
317    return auth_items_get_int64(mech->data, MECHANISM_PRIVILEGED);
318}
319
320uint64_t
321mechanism_get_type(mechanism_t mech)
322{
323    return mech->type;
324}
325