1/*
2 * Author: Mary Garvin <mgarvin@tresys.com>
3 *
4 * Copyright (C) 2007-2008 Tresys Technology, LLC
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2.1 of the License, or (at your option) any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 */
20
21#include "test-downgrade.h"
22#include "parse_util.h"
23#include "helpers.h"
24
25#include <sepol/debug.h>
26#include <sepol/handle.h>
27#include <sepol/policydb/policydb.h>
28#include <sepol/policydb/link.h>
29#include <sepol/policydb/expand.h>
30#include <sepol/policydb/conditional.h>
31#include <limits.h>
32#include <CUnit/Basic.h>
33
34#define POLICY_BIN_HI	"policies/test-downgrade/policy.hi"
35#define POLICY_BIN_LO	"policies/test-downgrade/policy.lo"
36
37static policydb_t policydb;
38
39/*
40 * Function Name:  downgrade_test_init
41 *
42 * Input: None
43 *
44 * Output: None
45 *
46 * Description: Initialize the policydb (policy data base structure)
47 */
48int downgrade_test_init(void)
49{
50	/* Initialize the policydb_t structure */
51	if (policydb_init(&policydb)) {
52		fprintf(stderr, "%s:  Out of memory!\n", __FUNCTION__);
53		return -1;
54	}
55
56	return 0;
57}
58
59/*
60 * Function Name:  downgrade_test_cleanup
61 *
62 * Input: None
63 *
64 * Output: None
65 *
66 * Description: Destroys policydb structure
67 */
68int downgrade_test_cleanup(void)
69{
70	policydb_destroy(&policydb);
71
72	return 0;
73}
74
75/*
76 * Function Name: downgrade_add_tests
77 *
78 * Input: CU_pSuite
79 *
80 * Output: Returns 0 upon success.  Returns a CUnit error value on failure.
81 *
82 * Description:  Add the given downgrade tests to the downgrade suite.
83 */
84int downgrade_add_tests(CU_pSuite suite)
85{
86	if (CU_add_test(suite, "downgrade", test_downgrade) == NULL)
87		return CU_get_error();
88
89	return 0;
90}
91
92/*
93 * Function Name:  test_downgrade_possible
94 *
95 * Input: None
96 *
97 * Output: None
98 *
99 * Description:
100 * Tests the backward compatability of MLS and Non-MLS binary policy versions.
101 */
102void test_downgrade(void)
103{
104	if (do_downgrade_test(0) < 0)
105		fprintf(stderr,
106		        "\nError during downgrade testing of Non-MLS policy\n");
107
108
109	if (do_downgrade_test(1) < 0)
110		fprintf(stderr,
111			"\nError during downgrade testing of MLS policy\n");
112}
113
114/*
115 * Function Name:  do_downgrade_test
116 *
117 * Input: 0 for Non-MLS policy and 1 for MLS policy downgrade testing
118 *
119 * Output: 0 on success, negative number upon failure
120 *
121 * Description: This function handles the downgrade testing.
122 *              A binary policy is read into the policydb structure, the
123 *              policy version is decreased by a specific amount, written
124 *              back out and then read back in again.  The process is
125 *              repeated until the minimum policy version is reached.
126 */
127int do_downgrade_test(int mls)
128{
129	policydb_t policydb_tmp;
130	int hi, lo, version;
131
132	/* Reset policydb for re-use */
133	policydb_destroy(&policydb);
134	downgrade_test_init();
135
136	/* Read in the hi policy from file */
137	if (read_binary_policy(POLICY_BIN_HI, &policydb) != 0) {
138		fprintf(stderr, "error reading %spolicy binary\n", mls ? "mls " : "");
139		CU_FAIL("Unable to read the binary policy");
140		return -1;
141	}
142
143	/* Change MLS value based on parameter */
144	policydb.mls = mls ? 1 : 0;
145
146	for (hi = policydb.policyvers; hi >= POLICYDB_VERSION_MIN; hi--) {
147		/* Stash old version number */
148		version = policydb.policyvers;
149
150		/* Try downgrading to each possible version. */
151		for (lo = hi - 1; lo >= POLICYDB_VERSION_MIN; lo--) {
152
153			/* Reduce policy version */
154			policydb.policyvers = lo;
155
156			/* Write out modified binary policy */
157			if (write_binary_policy(POLICY_BIN_LO, &policydb) != 0) {
158				/*
159				 * Error from MLS to pre-MLS is expected due
160				 * to MLS re-implementation in version 19.
161				 */
162				if (mls && lo < POLICYDB_VERSION_MLS)
163					continue;
164
165				fprintf(stderr, "error writing %spolicy binary, version %d (downgraded from %d)\n", mls ? "mls " : "", lo, hi);
166				CU_FAIL("Failed to write downgraded binary policy");
167					return -1;
168			}
169
170			/* Make sure we can read back what we wrote. */
171			if (policydb_init(&policydb_tmp)) {
172				fprintf(stderr, "%s:  Out of memory!\n",
173					__FUNCTION__);
174				return -1;
175			}
176			if (read_binary_policy(POLICY_BIN_LO, &policydb_tmp) != 0) {
177				fprintf(stderr, "error reading %spolicy binary, version %d (downgraded from %d)\n", mls ? "mls " : "", lo, hi);
178				CU_FAIL("Unable to read downgraded binary policy");
179				return -1;
180			}
181			policydb_destroy(&policydb_tmp);
182		}
183		/* Restore version number */
184		policydb.policyvers = version;
185    }
186
187    return 0;
188}
189
190/*
191 * Function Name: read_binary_policy
192 *
193 * Input: char * which is the path to the file containing the binary policy
194 *
195 * Output: Returns 0 upon success.  Upon failure, -1 is returned.
196 *	   Possible failures are, filename with given path does not exist,
197 *	   a failure to open the file, or a failure from prolicydb_read
198 *	   function call.
199 *
200 * Description:  Get a filename, open file and read binary policy into policydb
201 * 				 structure.
202 */
203int read_binary_policy(const char *path, policydb_t *p)
204{
205	FILE *in_fp = NULL;
206	struct policy_file f;
207	int rc;
208
209	/* Open the binary policy file */
210	if ((in_fp = fopen(path, "rb")) == NULL) {
211		fprintf(stderr, "Unable to open %s: %s\n", path,
212			strerror(errno));
213		sepol_handle_destroy(f.handle);
214		return -1;
215	}
216
217	/* Read in the binary policy.  */
218	memset(&f, 0, sizeof(struct policy_file));
219	f.type = PF_USE_STDIO;
220	f.fp = in_fp;
221	rc = policydb_read(p, &f, 0);
222
223	sepol_handle_destroy(f.handle);
224	fclose(in_fp);
225	return rc;
226}
227
228/*
229 * Function Name: write_binary_policy
230 *
231 * Input: char * which is the path to the file containing the binary policy
232 *
233 * Output: Returns 0 upon success.  Upon failure, -1 is returned.
234 *	   Possible failures are, filename with given path does not exist,
235 *	   a failure to open the file, or a failure from prolicydb_read
236 *	   function call.
237 *
238 * Description:  open file and write the binary policy from policydb structure.
239 */
240int write_binary_policy(const char *path, policydb_t *p)
241{
242	FILE *out_fp = NULL;
243	struct policy_file f;
244	sepol_handle_t *handle;
245	int rc;
246
247	/* We don't want libsepol to print warnings to stderr */
248	handle = sepol_handle_create();
249	if (handle == NULL) {
250		fprintf(stderr, "Out of memory!\n");
251		return -1;
252	}
253	sepol_msg_set_callback(handle, NULL, NULL);
254
255	/* Open the binary policy file for writing */
256	if ((out_fp = fopen(path, "w" )) == NULL) {
257		fprintf(stderr, "Unable to open %s: %s\n", path,
258			strerror(errno));
259		sepol_handle_destroy(f.handle);
260		return -1;
261	}
262
263	/* Write the binary policy */
264	memset(&f, 0, sizeof(struct policy_file));
265	f.type = PF_USE_STDIO;
266	f.fp = out_fp;
267	f.handle = handle;
268	rc = policydb_write(p, &f);
269
270	sepol_handle_destroy(f.handle);
271	fclose(out_fp);
272	return rc;
273}
274