Merging tizen into ckm. Stage 1.
[platform/core/test/security-tests.git] / tests / cynara-tests / test_cases_db.cpp
1 /*
2  * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /**
18  * @file        test_cases_db.cpp
19  * @author      Pawel Wieczorek <p.wieczorek2@samsung.com>
20  * @version     0.1
21  * @brief       Tests for Cynara's mechanism assuring integrity of database
22  */
23
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>
30
31 #include <service_manager.h>
32 #include <dirent.h>
33 #include <fcntl.h>
34 #include <fstream>
35 #include <glob.h>
36 #include <iterator>
37 #include <memory.h>
38 #include <set>
39 #include <string>
40 #include <unistd.h>
41 #include <vector>
42
43 using namespace CynaraTestAdmin;
44 using namespace CynaraTestClient;
45
46 namespace
47 {
48
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("/*");
54
55 void createDbFile(const std::string &filename)
56 {
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);
60
61     int ret = smack_fsetlabel(fileFd, CynaraTestConsts::LABEL.c_str(), SMACK_LABEL_ACCESS);
62     RUNNER_ASSERT_MSG(ret == 0, "Setting smack label failed");
63 }
64
65 void deleteDbFile(const std::string &filename)
66 {
67     RUNNER_ASSERT_ERRNO_MSG(!unlink(filename.c_str()), "Unable to unlink " << filename << " file");
68 }
69
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);
73
74     RUNNER_ASSERT_MSG(patternFile.is_open(), "Failed to open " << patternFile << ".");
75     RUNNER_ASSERT_MSG(resultFile.is_open(), "Failed to open " << resultFile << ".");
76
77     auto patternRecords = std::multiset<std::string>(std::istream_iterator<std::string>(patternFile),
78                                                      std::istream_iterator<std::string>());
79
80     auto resultRecords = std::multiset<std::string>(std::istream_iterator<std::string>(resultFile),
81                                                     std::istream_iterator<std::string>());
82
83     return patternRecords == resultRecords;
84 }
85
86 size_t glob_count(const std::string &source, const std::string &wildcard) {
87     //for counting files in directory
88     glob_t globbuf;
89     std::string pattern = source + wildcard;
90
91     //for freeing allocated memory
92     GlobPtr globbufPtr(&globbuf);
93
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 << ".");
97
98     return globbuf.gl_pathc;
99 }
100
101 size_t db_files_count(const std::string &source) {
102     size_t dbFilesCount = 0;
103
104     //database directory must not be empty
105     RUNNER_ASSERT_MSG(0 != (dbFilesCount = glob_count(source, directoryWildcard)),
106                       "Unexpected condition: " << source << " was empty.");
107
108     return dbFilesCount;
109 }
110
111 void compareDbs(const std::string &source)
112 {
113     //for accessing files in directory
114     std::string patternDir = cynaraTestPatternsPath + source;
115     DIR *patternDirPtr = nullptr;
116     struct dirent *direntPtr;
117
118     size_t patternFileCount = db_files_count(patternDir);
119     size_t resultFileCount = db_files_count(CynaraTestConsts::DB_DIR);
120
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");
124
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);
129
130     while ((direntPtr = readdir(patternDirPtr)) != nullptr) {
131         if (!strcmp(direntPtr->d_name, ".")
132          || !strcmp(direntPtr->d_name, ".."))
133             continue;
134         std::string patternName = patternDir + "/" + direntPtr->d_name;
135         std::string resultName = CynaraTestConsts::DB_DIR + "/" + direntPtr->d_name;
136
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");
140     }
141 }
142
143 } // anonymous namespace
144
145 /**
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
153  */
154 void tcdb01_lockdown_init_failure_func()
155 {
156     Admin admin;
157     Client cynara;
158     ServiceManager serviceManager(CynaraTestConsts::SERVICE);
159
160     const char *bucket = CYNARA_ADMIN_DEFAULT_BUCKET;
161     const char *extra = nullptr;
162
163     const auto fakeBackupGuard = CynaraTestConsts::DB_DIR + "/guard";
164
165     createDbFile(fakeBackupGuard);
166     admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra, CYNARA_API_OPERATION_FAILED);
167
168     deleteDbFile(fakeBackupGuard);
169     admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra);
170
171     serviceManager.restartService();
172     compareDbs(defDbAllow);
173 }
174
175 /**
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
183  */
184 void tcdb02_write_to_backup_failure_func()
185 {
186     Admin admin;
187     Client cynara;
188     ServiceManager serviceManager(CynaraTestConsts::SERVICE);
189
190     const char *bucket = CYNARA_ADMIN_DEFAULT_BUCKET;
191     const char *extra = nullptr;
192
193     const auto fakeBucketDumpFile = CynaraTestConsts::DB_DIR + "/_~";
194
195     admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra);
196     compareDbs(defDbAllow);
197
198     createDbFile(fakeBucketDumpFile);
199     admin.setBucket(bucket, CYNARA_ADMIN_DENY, extra, CYNARA_API_OPERATION_FAILED);
200
201     serviceManager.restartService();
202     compareDbs(defDbAllow);
203 }
204
205 /**
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
214  */
215 void tcdb03_invalid_and_valid_backup_removal_func()
216 {
217     Admin admin;
218     Client cynara;
219     ServiceManager serviceManager(CynaraTestConsts::SERVICE);
220
221     const char *bucket = CYNARA_ADMIN_DEFAULT_BUCKET;
222     const char *extra = nullptr;
223
224     const auto defaultBucketDumpFile = CynaraTestConsts::DB_DIR + "/_~";
225
226     createDbFile(defaultBucketDumpFile);
227     admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra, CYNARA_API_OPERATION_FAILED);
228
229     serviceManager.restartService();
230     compareDbs(defDb);
231
232     admin.setBucket(bucket, CYNARA_ADMIN_ALLOW, extra);
233
234     serviceManager.restartService();
235     compareDbs(defDbAllow);
236 }
237
238 /**
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
243  */
244 void tcdb04_dumped_file_binary_comparison_func()
245 {
246     Admin admin;
247     Client cynara;
248     ServiceManager serviceManager(CynaraTestConsts::SERVICE);
249
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;
255
256     {
257         CynaraPoliciesContainer cp;
258         cp.add(bucket, client, user, privilege, CYNARA_ADMIN_DENY, extra);
259         admin.setPolicies(cp, CYNARA_API_SUCCESS);
260     }
261
262     compareDbs(nonEmptyDb);
263 }
264
265 /**
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
272  * 2. Reload Cynara
273  * 3. Check if any of mentioned above files still remained
274  */
275 void tcdb05_non_indexed_files_removal_func()
276 {
277     ServiceManager serviceManager(CynaraTestConsts::SERVICE);
278
279     std::vector<std::string> filenames = { "_broken-backup~", "_non-indexed-bucket",
280                                            "some-file-that-doesnt-belong-here" };
281
282     for (const auto &filename : filenames) {
283         auto garbageFilename = CynaraTestConsts::DB_DIR + "/" + filename;
284         createDbFile(garbageFilename);
285     }
286
287     serviceManager.restartService();
288     compareDbs(defDb);
289 }
290
291 RUNNER_TEST_GROUP_INIT(cynara_db_tests)
292
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)