2 * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * @file test_cases_db.cpp
19 * @author Pawel Wieczorek <p.wieczorek2@samsung.com>
21 * @brief Tests for Cynara's mechanism assuring integrity of database
24 #include <cynara_test_admin.h>
25 #include <cynara_test_client.h>
26 #include <cynara_test_commons.h>
27 #include <cynara_test_env.h>
28 #include <dpl/test/test_runner.h>
29 #include <sys/smack.h>
31 #include <service_manager.h>
43 using namespace CynaraTestAdmin;
44 using namespace CynaraTestClient;
49 const std::string defDb("default");
50 const std::string defDbAllow("defaultAllowed");
51 const std::string nonEmptyDb("nonEmptyDatabase");
52 const std::string cynaraTestPatternsPath("/etc/security-tests/db_patterns/");
53 const std::string directoryWildcard("/*");
55 void createDbFile(const std::string &filename)
57 int fileFd = TEMP_FAILURE_RETRY(creat(filename.c_str(), 0000));
58 RUNNER_ASSERT_ERRNO_MSG(fileFd > 0, "Creating " << filename << " file failed");
59 FdUniquePtr fileFdPtr(&fileFd);
61 int ret = smack_fsetlabel(fileFd, CynaraTestConsts::LABEL.c_str(), SMACK_LABEL_ACCESS);
62 RUNNER_ASSERT_MSG(ret == 0, "Setting smack label failed");
65 void deleteDbFile(const std::string &filename)
67 RUNNER_ASSERT_ERRNO_MSG(!unlink(filename.c_str()), "Unable to unlink " << filename << " file");
70 bool unordered_files_match(const std::string &patternFilePath, const std::string &resultFilePath) {
71 std::ifstream patternFile(patternFilePath, std::ifstream::in | std::ifstream::binary);
72 std::ifstream resultFile(resultFilePath, std::ifstream::in | std::ifstream::binary);
74 RUNNER_ASSERT_MSG(patternFile.is_open(), "Failed to open " << patternFile << ".");
75 RUNNER_ASSERT_MSG(resultFile.is_open(), "Failed to open " << resultFile << ".");
77 auto patternRecords = std::multiset<std::string>(std::istream_iterator<std::string>(patternFile),
78 std::istream_iterator<std::string>());
80 auto resultRecords = std::multiset<std::string>(std::istream_iterator<std::string>(resultFile),
81 std::istream_iterator<std::string>());
83 return patternRecords == resultRecords;
86 size_t glob_count(const std::string &source, const std::string &wildcard) {
87 //for counting files in directory
89 std::string pattern = source + wildcard;
91 //for freeing allocated memory
92 GlobPtr globbufPtr(&globbuf);
94 //actually count files in directory - including dotfiles
95 RUNNER_ASSERT_MSG(0 == glob(pattern.c_str(), GLOB_NOSORT | GLOB_PERIOD, NULL, &globbuf),
96 "Failed to search for requested pathnames in " << source << ".");
98 return globbuf.gl_pathc;
101 size_t db_files_count(const std::string &source) {
102 size_t dbFilesCount = 0;
104 //database directory must not be empty
105 RUNNER_ASSERT_MSG(0 != (dbFilesCount = glob_count(source, directoryWildcard)),
106 "Unexpected condition: " << source << " was empty.");
111 void compareDbs(const std::string &source)
113 //for accessing files in directory
114 std::string patternDir = cynaraTestPatternsPath + source;
115 DIR *patternDirPtr = nullptr;
116 struct dirent *direntPtr;
118 size_t patternFileCount = db_files_count(patternDir);
119 size_t resultFileCount = db_files_count(CynaraTestConsts::DB_DIR);
121 //directories do not match if there is different number of files
122 RUNNER_ASSERT_MSG(patternFileCount == resultFileCount,
123 "No match in database and pattern directory file count");
125 //compare files in database directory with pattern directory
126 RUNNER_ASSERT_ERRNO_MSG(patternDirPtr = opendir(patternDir.c_str()),
127 "Opening " << patternDir << " directory failed");
128 DirPtr patternDirScopedPtr(patternDirPtr);
130 while ((direntPtr = readdir(patternDirPtr)) != nullptr) {
131 if (!strcmp(direntPtr->d_name, ".")
132 || !strcmp(direntPtr->d_name, ".."))
134 std::string patternName = patternDir + "/" + direntPtr->d_name;
135 std::string resultName = CynaraTestConsts::DB_DIR + "/" + direntPtr->d_name;
137 //comparing file saved db dir with reference file from patterns dir
138 RUNNER_ASSERT_MSG(true == unordered_files_match(patternName, resultName),
139 "No match in stored file and pattern file");
143 } // anonymous namespace
146 * @brief Lockdown initialization failure caused by fake guard existence
147 * @test Expected result: refuse to write data to storage as long as guard file creation fails
148 * 1. Create fake guard file with 0000 attributes in policy database
149 * 2. Try to make a change (ALLOW) in default bucket (data dump should fail)
150 * 3. Delete fake guard file from policy database
151 * 4. Retry to make a change (ALLOW) in default bucket (data dump should proceed)
152 * 5. Check if database is saved correctly
154 void tcdb01_lockdown_init_failure_func()
158 ServiceManager serviceManager(CynaraTestConsts::SERVICE);
160 const char *bucket = CYNARA_ADMIN_DEFAULT_BUCKET;
161 const char *extra = nullptr;
163 const auto fakeBackupGuard = CynaraTestConsts::DB_DIR + "/guard";
165 createDbFile(fakeBackupGuard);
166 admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra, CYNARA_API_OPERATION_FAILED);
168 deleteDbFile(fakeBackupGuard);
169 admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra);
171 serviceManager.restartService();
172 compareDbs(defDbAllow);
176 * @brief Failure during writing to backup (before lockdown)
177 * @test Expected result: read from primary policy database
178 * 1. Write ALLOW to default bucket
179 * 2. Check if data is saved correctly
180 * 3. Create fake backup file with 0000 attributes in policy database
181 * 4. Try to make a change (DENY) in default bucket (data dump should fail)
182 * 5. Reload Cynara - policies loaded from default bucket should still be ALLOW
184 void tcdb02_write_to_backup_failure_func()
188 ServiceManager serviceManager(CynaraTestConsts::SERVICE);
190 const char *bucket = CYNARA_ADMIN_DEFAULT_BUCKET;
191 const char *extra = nullptr;
193 const auto fakeBucketDumpFile = CynaraTestConsts::DB_DIR + "/_~";
195 admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra);
196 compareDbs(defDbAllow);
198 createDbFile(fakeBucketDumpFile);
199 admin.setBucket(bucket, CYNARA_ADMIN_DENY, extra, CYNARA_API_OPERATION_FAILED);
201 serviceManager.restartService();
202 compareDbs(defDbAllow);
206 * @brief Check whether both invalid and valid backup databases are removed
207 * @test Expected result: no unnecessary backup files in policy database directory
208 * 1. Fail writing to backup database
209 * 2. Reload Cynara - policies should be loaded from primary (valid) database
210 * 3. Check if all backup files were removed
211 * 4. Successfully write changes to database
212 * 5. Reload Cynara - policies should be loaded from primary (revalidated) database
213 * 6. Check if all backup files were removed
215 void tcdb03_invalid_and_valid_backup_removal_func()
219 ServiceManager serviceManager(CynaraTestConsts::SERVICE);
221 const char *bucket = CYNARA_ADMIN_DEFAULT_BUCKET;
222 const char *extra = nullptr;
224 const auto defaultBucketDumpFile = CynaraTestConsts::DB_DIR + "/_~";
226 createDbFile(defaultBucketDumpFile);
227 admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra, CYNARA_API_OPERATION_FAILED);
229 serviceManager.restartService();
232 admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra);
234 serviceManager.restartService();
235 compareDbs(defDbAllow);
239 * @brief Comparison between database modified by Cynara with expected one
240 * @test Expected result: no differences between those files
241 * 1. Write sample policy to database (and let it save to storage)
242 * 2. Compare freshly saved files with samples from test patterns directory
244 void tcdb04_dumped_file_binary_comparison_func()
248 ServiceManager serviceManager(CynaraTestConsts::SERVICE);
250 const char *bucket = CYNARA_ADMIN_DEFAULT_BUCKET;
251 const char *client = "client";
252 const char *user = "user";
253 const char *privilege = "privilege";
254 const char *extra = nullptr;
257 CynaraPoliciesContainer cp;
258 cp.add(bucket, client, user, privilege, CYNARA_ADMIN_DENY, extra);
259 admin.setPolicies(cp, CYNARA_API_SUCCESS);
262 compareDbs(nonEmptyDb);
266 * @brief Invalid database files removal
267 * @test Expected result: no unnecessary files in policy database directory
268 * 1. Fill Cynara's policy database directory with garbage:
269 * - Sample backup file which should be removed earlier
270 * - Sample bucket file which is not mentioned in index (shouldn't exist at all)
271 * - Sample files which don't belong to database
273 * 3. Check if any of mentioned above files still remained
275 void tcdb05_non_indexed_files_removal_func()
277 ServiceManager serviceManager(CynaraTestConsts::SERVICE);
279 std::vector<std::string> filenames = { "_broken-backup~", "_non-indexed-bucket",
280 "some-file-that-doesnt-belong-here" };
282 for (const auto &filename : filenames) {
283 auto garbageFilename = CynaraTestConsts::DB_DIR + "/" + filename;
284 createDbFile(garbageFilename);
287 serviceManager.restartService();
291 RUNNER_TEST_GROUP_INIT(cynara_db_tests)
293 RUN_CYNARA_TEST(tcdb01_lockdown_init_failure)
294 RUN_CYNARA_TEST(tcdb02_write_to_backup_failure)
295 RUN_CYNARA_TEST(tcdb03_invalid_and_valid_backup_removal)
296 RUN_CYNARA_TEST(tcdb04_dumped_file_binary_comparison)
297 RUN_CYNARA_TEST(tcdb05_non_indexed_files_removal)