1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2015 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include <cJSON.h>
19 #include <algorithm>
20 #include <cerrno>
21 #include <cstdio>
22 #include <cstring>
23 #include <iostream>
24 #include <sstream>
25 
26 #include <platform/dirutils.h>
27 
28 #include "auditd.h"
29 #include "audit.h"
30 #include "auditconfig.h"
31 
32 /**
33  * Get a named object from located under "root". It should be of the
34  * given type (or -1 for a boolean value)
35  *
36  * @param root the root of the json object
37  * @param name the name of the tag to search for
38  * @param type the type for the object (or -1 for boolean)
39  */
getObject(const cJSON* root, const char* name, int type)40 cJSON* AuditConfig::getObject(const cJSON* root, const char* name, int type)
41 {
42     cJSON *ret = cJSON_GetObjectItem(const_cast<cJSON*>(root), name);
43     if (ret) {
44         if (ret->type != type) {
45             if (type == -1) {
46                 if (ret->type == cJSON_True || ret->type == cJSON_False) {
47                     return ret;
48                 }
49             }
50 
51             std::stringstream ss;
52             ss << "Incorrect type for \"" << name << "\". Should be ";
53             switch (type) {
54             case cJSON_String:
55                 ss << "string";
56                 break;
57             case cJSON_Number:
58                 ss << "number";
59                 break;
60             default:
61                 ss << type;
62             }
63 
64             throw ss.str();
65         }
66     } else {
67         std::stringstream ss;
68         ss << "Element \"" << name << "\" is not present.";
69         throw ss.str();
70     }
71 
72     return ret;
73 }
74 
AuditConfig(const cJSON *json)75 AuditConfig::AuditConfig(const cJSON *json) : AuditConfig() {
76     set_version(getObject(json, "version", cJSON_Number));
77     set_rotate_size(getObject(json, "rotate_size", cJSON_Number));
78     set_rotate_interval(getObject(json, "rotate_interval", cJSON_Number));
79     set_auditd_enabled(getObject(json, "auditd_enabled", -1));
80     set_buffered(cJSON_GetObjectItem(const_cast<cJSON*>(json), "buffered"));
81     set_log_directory(getObject(json, "log_path", cJSON_String));
82     set_descriptors_path(getObject(json, "descriptors_path", cJSON_String));
83     set_sync(getObject(json, "sync", cJSON_Array));
84     // The disabled list is depreciated in version 2
85     if (get_version() == 1) {
86         set_disabled(getObject(json, "disabled", cJSON_Array));
87     }
88     if (get_version() == 2) {
89         set_filtering_enabled(getObject(json, "filtering_enabled", -1));
90         set_uuid(getObject(json, "uuid", cJSON_String));
91         set_disabled_userids(getObject(json, "disabled_userids", cJSON_Array));
92         // event_states is optional so if not defined will not throw an
93         // exception.
94         if (cJSON_GetObjectItem(const_cast<cJSON*>(json), "event_states")) {
95             set_event_states(getObject(json, "event_states", cJSON_Object));
96         }
97     }
98 
99     std::map<std::string, int> tags;
100     tags["version"] = 1;
101     tags["rotate_size"] = 1;
102     tags["rotate_interval"] = 1;
103     tags["auditd_enabled"] = 1;
104     tags["buffered"] = 1;
105     tags["log_path"] = 1;
106     tags["descriptors_path"] = 1;
107     tags["sync"] = 1;
108     // The disabled list is depreciated in version 2 - if defined will
109     // just be ignored.
110     tags["disabled"] = 1;
111     if (get_version() == 2) {
112         tags["filtering_enabled"] = 1;
113         tags["uuid"] = 1;
114         tags["disabled_userids"] = 1;
115         tags["event_states"] = 1;
116     }
117 
118     for (cJSON *items = json->child; items != NULL; items = items->next) {
119         if (tags.find(items->string) == tags.end()) {
120             std::stringstream ss;
121             ss << "Error: Unknown token \"" << items->string << "\""
122                << std::endl;
123             throw ss.str();
124         }
125     }
126 }
127 
is_auditd_enabled(void) const128 bool AuditConfig::is_auditd_enabled(void) const {
129     return auditd_enabled;
130 }
131 
set_auditd_enabled(bool value)132 void AuditConfig::set_auditd_enabled(bool value) {
133     auditd_enabled = value;
134 }
135 
set_rotate_size(size_t size)136 void AuditConfig::set_rotate_size(size_t size) {
137     if (size > max_rotate_file_size) {
138         std::stringstream ss;
139         ss << "error: rotation size " << size
140            << " is too big. Legal range is <0, "
141            << max_rotate_file_size << "]";
142         throw ss.str();
143     }
144     rotate_size = size;
145 }
146 
get_rotate_size(void) const147 size_t AuditConfig::get_rotate_size(void) const {
148     return rotate_size;
149 }
150 
set_rotate_interval(uint32_t interval)151 void AuditConfig::set_rotate_interval(uint32_t interval) {
152     if (interval > max_file_rotation_time ||
153         interval < min_file_rotation_time) {
154         std::stringstream ss;
155         ss << "error: rotation interval "
156            << interval << " is outside the legal range ["
157            << min_file_rotation_time << ", "
158            << max_file_rotation_time << "]";
159         throw ss.str();
160     }
161 
162     rotate_interval = interval;
163 }
164 
get_rotate_interval(void) const165 uint32_t AuditConfig::get_rotate_interval(void) const {
166     return rotate_interval;
167 }
168 
set_buffered(bool enable)169 void AuditConfig::set_buffered(bool enable) {
170     buffered = enable;
171 }
172 
is_buffered(void) const173 bool AuditConfig::is_buffered(void) const {
174     return buffered;
175 }
176 
set_log_directory(const std::string &directory)177 void AuditConfig::set_log_directory(const std::string &directory) {
178     std::lock_guard<std::mutex> guard(log_path_mutex);
179     /* Sanitize path */
180     log_path = directory;
181     sanitize_path(log_path);
182     try {
183         cb::io::mkdirp(log_path);
184     } catch (const std::runtime_error& error) {
185         std::stringstream ss;
186         ss << "error: failed to create log directory \""
187            << log_path << "\": " << error.what();
188         throw ss.str();
189     }
190 }
191 
get_log_directory(void) const192 std::string AuditConfig::get_log_directory(void) const {
193     std::lock_guard<std::mutex> guard(log_path_mutex);
194     return log_path;
195 }
196 
set_descriptors_path(const std::string &directory)197 void AuditConfig::set_descriptors_path(const std::string &directory) {
198     std::lock_guard<std::mutex> guard(descriptor_path_mutex);
199     /* Sanitize path */
200     descriptors_path = directory;
201     sanitize_path(descriptors_path);
202 
203     std::stringstream tmp;
204     tmp << descriptors_path << DIRECTORY_SEPARATOR_CHARACTER;
205     tmp << "audit_events.json";
206     FILE *fp = fopen(tmp.str().c_str(), "r");
207     if (fp == NULL) {
208         std::stringstream ss;
209         ss << "Failed to open \"" << tmp.str().c_str() << "\": "
210            << strerror(errno);
211         throw ss.str();
212     }
213     fclose(fp);
214 }
215 
get_descriptors_path(void) const216 std::string AuditConfig::get_descriptors_path(void) const {
217     std::lock_guard<std::mutex> guard(descriptor_path_mutex);
218     return descriptors_path;
219 }
220 
set_version(uint32_t ver)221 void AuditConfig::set_version(uint32_t ver) {
222     if ((ver != 1) && (ver != 2))  {
223            std::stringstream ss;
224            ss << "error: version " << ver << " is not supported";
225            throw ss.str();
226        }
227     version = ver;
228 }
229 
get_version() const230 uint32_t AuditConfig::get_version() const {
231     return version;
232 }
233 
is_event_sync(uint32_t id)234 bool AuditConfig::is_event_sync(uint32_t id) {
235     std::lock_guard<std::mutex> guard(sync_mutex);
236     return std::find(sync.begin(), sync.end(), id) != sync.end();
237 }
238 
is_event_disabled(uint32_t id)239 bool AuditConfig::is_event_disabled(uint32_t id) {
240     std::lock_guard<std::mutex> guard(disabled_mutex);
241     return std::find(disabled.begin(), disabled.end(), id) != disabled.end();
242 }
243 
get_event_state(uint32_t id) const244 AuditConfig::EventState AuditConfig::get_event_state(uint32_t id) const {
245     std::lock_guard<std::mutex> guard(event_states_mutex);
246     const auto it = event_states.find(id);
247     if (it == event_states.end()) {
248         // If event state is not defined (as either enabled or disabled) then
249         // return undefined.
250         return EventState::undefined;
251     }
252     return it->second;
253 }
254 
is_event_filtered( const std::pair<std::string, std::string>& userid) const255 bool AuditConfig::is_event_filtered(
256         const std::pair<std::string, std::string>& userid) const {
257     std::lock_guard<std::mutex> guard(disabled_userids_mutex);
258     return std::find(disabled_userids.begin(),
259                      disabled_userids.end(),
260                      userid) != disabled_userids.end();
261 }
262 
set_filtering_enabled(bool value)263 void AuditConfig::set_filtering_enabled(bool value) {
264     filtering_enabled = value;
265 }
266 
is_filtering_enabled() const267 bool AuditConfig::is_filtering_enabled() const {
268     return filtering_enabled;
269 }
270 
sanitize_path(std::string &path)271 void AuditConfig::sanitize_path(std::string &path) {
272 #ifdef WIN32
273     // Make sure that the path is in windows format
274     std::replace(path.begin(), path.end(), '/', '\\');
275 #endif
276 
277     if (path.length() > 1 && path.data()[path.length() - 1] == DIRECTORY_SEPARATOR_CHARACTER) {
278         path.resize(path.length() - 1);
279     }
280 }
281 
set_uuid(const std::string &_uuid)282 void AuditConfig::set_uuid(const std::string &_uuid) {
283     std::lock_guard<std::mutex> guard(uuid_mutex);
284     uuid = _uuid;
285 }
286 
get_uuid() const287 std::string AuditConfig::get_uuid() const {
288     std::lock_guard<std::mutex> guard(uuid_mutex);
289     return uuid;
290 }
291 
set_rotate_size(cJSON *obj)292 void AuditConfig::set_rotate_size(cJSON *obj) {
293     set_rotate_size(static_cast<size_t>(obj->valueint));
294 }
295 
set_rotate_interval(cJSON *obj)296 void AuditConfig::set_rotate_interval(cJSON *obj) {
297     set_rotate_interval(static_cast<uint32_t>(obj->valueint));
298 }
299 
set_auditd_enabled(cJSON *obj)300 void AuditConfig::set_auditd_enabled(cJSON *obj) {
301     set_auditd_enabled(obj->type == cJSON_True);
302 }
303 
set_filtering_enabled(cJSON *obj)304 void AuditConfig::set_filtering_enabled(cJSON *obj) {
305     set_filtering_enabled(obj->type == cJSON_True);
306 }
307 
set_buffered(cJSON *obj)308 void AuditConfig::set_buffered(cJSON *obj) {
309     if (obj) {
310         if (obj->type == cJSON_True) {
311             set_buffered(true);
312         } else if (obj->type == cJSON_False) {
313             set_buffered(false);
314         } else {
315             std::stringstream ss;
316             ss << "Incorrect type (" << obj->type
317                << ") for \"buffered\". Should be boolean";
318             throw ss.str();
319         }
320     }
321 }
322 
set_log_directory(cJSON *obj)323 void AuditConfig::set_log_directory(cJSON *obj) {
324     set_log_directory(obj->valuestring);
325 }
326 
set_descriptors_path(cJSON *obj)327 void AuditConfig::set_descriptors_path(cJSON *obj) {
328     set_descriptors_path(obj->valuestring);
329 }
330 
set_version(cJSON *obj)331 void AuditConfig::set_version(cJSON *obj) {
332     set_version(static_cast<uint32_t>(obj->valueint));
333 }
334 
add_array(std::vector<uint32_t> &vec, cJSON *array, const char *name)335 void AuditConfig::add_array(std::vector<uint32_t> &vec, cJSON *array, const char *name) {
336     vec.clear();
337     for (auto *ii = array->child; ii != NULL; ii = ii->next) {
338         if (ii->type != cJSON_Number) {
339             std::stringstream ss;
340             ss << "Incorrect type (" << ii->type
341                << ") for element in " << name
342                <<" array. Expected numbers";
343             throw ss.str();
344         }
345         vec.push_back(gsl::narrow<uint32_t>(ii->valueint));
346     }
347 }
348 
add_event_states_object( std::unordered_map<uint32_t, EventState>& eventStates, cJSON* object, const char* name)349 void AuditConfig::add_event_states_object(
350         std::unordered_map<uint32_t, EventState>& eventStates,
351         cJSON* object,
352         const char* name) {
353     eventStates.clear();
354     cJSON* obj = object->child;
355     while (obj != nullptr) {
356         std::string event(obj->string);
357         if (obj->type != cJSON_String) {
358             throw std::invalid_argument(
359                     "Incorrect type for state. Should be string.");
360         }
361         std::string state{obj->valuestring};
362         EventState estate{EventState::undefined};
363         if (state == "enabled") {
364             estate = EventState::enabled;
365         } else if (state == "disabled") {
366             estate = EventState::disabled;
367         }
368         // add to the eventStates map
369         eventStates[std::stoi(event)] = estate;
370         obj = obj->next;
371     }
372 }
373 
add_pair_string_array( std::vector<std::pair<std::string, std::string>>& vec, cJSON* array, const char* name)374 void AuditConfig::add_pair_string_array(
375         std::vector<std::pair<std::string, std::string>>& vec,
376         cJSON* array,
377         const char* name) {
378     vec.clear();
379     for (int ii = 0; ii < cJSON_GetArraySize(array); ii++) {
380         auto element = cJSON_GetArrayItem(array, ii);
381         if (element->type != cJSON_Object) {
382             std::stringstream ss;
383             ss << "Incorrect type (" << element->type << ") for element in "
384                << name << " array. Expected objects";
385             throw std::invalid_argument(ss.str());
386         }
387         auto* source = cJSON_GetObjectItem(element, "source");
388         auto* domain = cJSON_GetObjectItem(element, "domain");
389         if (source != nullptr && source->type != cJSON_String) {
390             throw std::invalid_argument(
391                     "Incorrect type for source. Should be string.");
392         }
393         if (domain != nullptr && domain->type != cJSON_String) {
394             throw std::invalid_argument(
395                     "Incorrect type for domain. Should be string.");
396         }
397         if (source != nullptr || domain != nullptr) {
398             auto* user = cJSON_GetObjectItem(element, "user");
399             if (user != nullptr) {
400                 if (user->type != cJSON_String) {
401                     throw std::invalid_argument(
402                             "Incorrect type for user. Should be string.");
403                 }
404                 // Have a source/domain and user so build the pair and add to
405                 // the vector
406                 auto* sourceValueString = (source != nullptr) ?
407                         source->valuestring : domain->valuestring;
408                 const auto& userid =
409                         std::make_pair(sourceValueString, user->valuestring);
410                 vec.push_back(userid);
411             }
412         }
413     }
414 }
415 
set_sync(cJSON *array)416 void AuditConfig::set_sync(cJSON *array) {
417     std::lock_guard<std::mutex> guard(sync_mutex);
418     add_array(sync, array, "sync");
419 }
420 
set_disabled(cJSON *array)421 void AuditConfig::set_disabled(cJSON *array) {
422     std::lock_guard<std::mutex> guard(disabled_mutex);
423     add_array(disabled, array, "disabled");
424 }
425 
set_disabled_userids(cJSON* array)426 void AuditConfig::set_disabled_userids(cJSON* array) {
427     std::lock_guard<std::mutex> guard(disabled_userids_mutex);
428     add_pair_string_array(disabled_userids, array, "disabled_userids");
429 }
430 
set_event_states(cJSON* object)431 void AuditConfig::set_event_states(cJSON* object) {
432     std::lock_guard<std::mutex> guard(event_states_mutex);
433     add_event_states_object(event_states, object, "event_states");
434 }
435 
set_uuid(cJSON *obj)436 void AuditConfig::set_uuid(cJSON *obj) {
437     set_uuid(obj->valuestring);
438 }
439 
to_json() const440 unique_cJSON_ptr AuditConfig::to_json() const {
441     unique_cJSON_ptr ret(cJSON_CreateObject());
442     cJSON* root = ret.get();
443     if (root == nullptr) {
444         throw std::bad_alloc();
445     }
446 
447     cJSON_AddNumberToObject(root, "version", get_version());
448     cJSON_AddBoolToObject(root, "auditd_enabled", is_auditd_enabled());
449     cJSON_AddNumberToObject(root, "rotate_size", get_rotate_size());
450     cJSON_AddNumberToObject(root, "rotate_interval", get_rotate_interval());
451     cJSON_AddBoolToObject(root, "buffered", is_buffered());
452     cJSON_AddStringToObject(root, "log_path", get_log_directory().c_str());
453     cJSON_AddStringToObject(root, "descriptors_path", get_descriptors_path().c_str());
454     cJSON_AddBoolToObject(root, "filtering_enabled", is_filtering_enabled());
455     cJSON_AddStringToObject(root, "uuid", get_uuid().c_str());
456 
457     cJSON* array = cJSON_CreateArray();
458     for (const auto& v : sync) {
459         cJSON_AddItemToArray(array, cJSON_CreateNumber(v));
460     }
461     cJSON_AddItemToObject(root, "sync", array);
462 
463     array = cJSON_CreateArray();
464     for (const auto& v : disabled) {
465         cJSON_AddItemToArray(array, cJSON_CreateNumber(v));
466     }
467     cJSON_AddItemToObject(root, "disabled", array);
468 
469     array = cJSON_CreateArray();
470     for (const auto& v : disabled_userids) {
471         cJSON* userIdRoot = cJSON_CreateObject();
472         if (userIdRoot == nullptr) {
473             throw std::runtime_error(
474                     "AuditConfig::to_json - Error creating "
475                     "cJSON object");
476         }
477         cJSON_AddStringToObject(userIdRoot, "domain", v.first.c_str());
478         cJSON_AddStringToObject(userIdRoot, "user", v.second.c_str());
479         cJSON_AddItemToArray(array, userIdRoot);
480     }
481     cJSON_AddItemToObject(root, "disabled_userids", array);
482 
483     cJSON* object = cJSON_CreateObject();
484     for (const auto& v : event_states) {
485         std::string event = std::to_string(v.first);
486         EventState estate = v.second;
487         std::string state;
488         switch (estate) {
489         case EventState::enabled: {
490             state = "enabled";
491             break;
492         }
493         case EventState::disabled: {
494             state = "disabled";
495             break;
496         }
497         case EventState::undefined: {
498             throw std::logic_error(
499                     "AuditConfig::to_json - EventState:undefined should not be "
500                     "found in the event_states list");
501         }
502         }
503         cJSON_AddStringToObject(object, event.c_str(), state.c_str());
504     }
505     cJSON_AddItemToObject(root, "event_states", object);
506 
507     return ret;
508 }
509 
initialize_config(const cJSON* json)510 void AuditConfig::initialize_config(const cJSON* json) {
511     AuditConfig other(json);
512 
513     auditd_enabled = other.auditd_enabled;
514     rotate_interval = other.rotate_interval;
515     rotate_size = other.rotate_size;
516     buffered = other.buffered;
517     filtering_enabled = other.filtering_enabled;
518     {
519         std::lock_guard<std::mutex> guard(log_path_mutex);
520         log_path = other.log_path;
521     }
522     {
523         std::lock_guard<std::mutex> guard(descriptor_path_mutex);
524         descriptors_path = other.descriptors_path;
525     }
526     {
527         std::lock_guard<std::mutex> guard(sync_mutex);
528         sync = other.sync;
529     }
530 
531     {
532         std::lock_guard<std::mutex> guard(disabled_mutex);
533         disabled = other.disabled;
534     }
535 
536     {
537         std::lock_guard<std::mutex> guard(disabled_userids_mutex);
538         disabled_userids = other.disabled_userids;
539     }
540 
541     {
542         std::lock_guard<std::mutex> guard(event_states_mutex);
543         event_states = other.event_states;
544     }
545 
546     {
547         std::lock_guard<std::mutex> guard(uuid_mutex);
548         uuid = other.uuid;
549     }
550 
551     min_file_rotation_time = other.min_file_rotation_time;
552     max_file_rotation_time = other.max_file_rotation_time;
553     max_rotate_file_size = other.max_rotate_file_size;
554     version = other.version;
555 }
556