• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Authors: Joshua Brindle <jbrindle@tresys.com>
2  *
3  * Assertion checker for avtab entries, taken from
4  * checkpolicy.c by Stephen Smalley <sds@tycho.nsa.gov>
5  *
6  * Copyright (C) 2005 Tresys Technology, LLC
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include <sepol/policydb/avtab.h>
24 #include <sepol/policydb/policydb.h>
25 #include <sepol/policydb/expand.h>
26 #include <sepol/policydb/util.h>
27 
28 #include "private.h"
29 #include "debug.h"
30 
31 struct avtab_match_args {
32 	sepol_handle_t *handle;
33 	policydb_t *p;
34 	avrule_t *avrule;
35 	avtab_t *avtab;
36 	unsigned long errors;
37 };
38 
report_failure(sepol_handle_t * handle,policydb_t * p,const avrule_t * avrule,unsigned int stype,unsigned int ttype,const class_perm_node_t * curperm,uint32_t perms)39 static void report_failure(sepol_handle_t *handle, policydb_t *p, const avrule_t *avrule,
40 			   unsigned int stype, unsigned int ttype,
41 			   const class_perm_node_t *curperm, uint32_t perms)
42 {
43 	if (avrule->source_filename) {
44 		ERR(handle, "neverallow on line %lu of %s (or line %lu of policy.conf) violated by allow %s %s:%s {%s };",
45 		    avrule->source_line, avrule->source_filename, avrule->line,
46 		    p->p_type_val_to_name[stype],
47 		    p->p_type_val_to_name[ttype],
48 		    p->p_class_val_to_name[curperm->tclass - 1],
49 		    sepol_av_to_string(p, curperm->tclass, perms));
50 	} else if (avrule->line) {
51 		ERR(handle, "neverallow on line %lu violated by allow %s %s:%s {%s };",
52 		    avrule->line, p->p_type_val_to_name[stype],
53 		    p->p_type_val_to_name[ttype],
54 		    p->p_class_val_to_name[curperm->tclass - 1],
55 		    sepol_av_to_string(p, curperm->tclass, perms));
56 	} else {
57 		ERR(handle, "neverallow violated by allow %s %s:%s {%s };",
58 		    p->p_type_val_to_name[stype],
59 		    p->p_type_val_to_name[ttype],
60 		    p->p_class_val_to_name[curperm->tclass - 1],
61 		    sepol_av_to_string(p, curperm->tclass, perms));
62 	}
63 }
64 
match_any_class_permissions(class_perm_node_t * cp,uint32_t class,uint32_t data)65 static int match_any_class_permissions(class_perm_node_t *cp, uint32_t class, uint32_t data)
66 {
67 	for (; cp; cp = cp->next) {
68 		if ((cp->tclass == class) && (cp->data & data)) {
69 			break;
70 		}
71 	}
72 	if (!cp)
73 		return 0;
74 
75 	return 1;
76 }
77 
extended_permissions_and(uint32_t * perms1,uint32_t * perms2)78 static int extended_permissions_and(uint32_t *perms1, uint32_t *perms2) {
79 	size_t i;
80 	for (i = 0; i < EXTENDED_PERMS_LEN; i++) {
81 		if (perms1[i] & perms2[i])
82 			return 1;
83 	}
84 
85 	return 0;
86 }
87 
check_extended_permissions(av_extended_perms_t * neverallow,avtab_extended_perms_t * allow)88 static int check_extended_permissions(av_extended_perms_t *neverallow, avtab_extended_perms_t *allow)
89 {
90 	int rc = 0;
91 	if ((neverallow->specified == AVRULE_XPERMS_IOCTLFUNCTION)
92 			&& (allow->specified == AVTAB_XPERMS_IOCTLFUNCTION)) {
93 		if (neverallow->driver == allow->driver)
94 			rc = extended_permissions_and(neverallow->perms, allow->perms);
95 	} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLFUNCTION)
96 			&& (allow->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
97 		rc = xperm_test(neverallow->driver, allow->perms);
98 	} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLDRIVER)
99 			&& (allow->specified == AVTAB_XPERMS_IOCTLFUNCTION)) {
100 		rc = xperm_test(allow->driver, neverallow->perms);
101 	} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLDRIVER)
102 			&& (allow->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
103 		rc = extended_permissions_and(neverallow->perms, allow->perms);
104 	}
105 
106 	return rc;
107 }
108 
109 /* Compute which allowed extended permissions violate the neverallow rule */
extended_permissions_violated(avtab_extended_perms_t * result,av_extended_perms_t * neverallow,avtab_extended_perms_t * allow)110 static void extended_permissions_violated(avtab_extended_perms_t *result,
111 					av_extended_perms_t *neverallow,
112 					avtab_extended_perms_t *allow)
113 {
114 	size_t i;
115 	if ((neverallow->specified == AVRULE_XPERMS_IOCTLFUNCTION)
116 			&& (allow->specified == AVTAB_XPERMS_IOCTLFUNCTION)) {
117 		result->specified = AVTAB_XPERMS_IOCTLFUNCTION;
118 		result->driver = allow->driver;
119 		for (i = 0; i < EXTENDED_PERMS_LEN; i++)
120 			result->perms[i] = neverallow->perms[i] & allow->perms[i];
121 	} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLFUNCTION)
122 			&& (allow->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
123 		result->specified = AVTAB_XPERMS_IOCTLFUNCTION;
124 		result->driver = neverallow->driver;
125 		memcpy(result->perms, neverallow->perms, sizeof(result->perms));
126 	} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLDRIVER)
127 			&& (allow->specified == AVTAB_XPERMS_IOCTLFUNCTION)) {
128 		result->specified = AVTAB_XPERMS_IOCTLFUNCTION;
129 		result->driver = allow->driver;
130 		memcpy(result->perms, allow->perms, sizeof(result->perms));
131 	} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLDRIVER)
132 			&& (allow->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
133 		result->specified = AVTAB_XPERMS_IOCTLDRIVER;
134 		for (i = 0; i < EXTENDED_PERMS_LEN; i++)
135 			result->perms[i] = neverallow->perms[i] & allow->perms[i];
136 	}
137 }
138 
139 /* Same scenarios of interest as check_assertion_extended_permissions */
report_assertion_extended_permissions(sepol_handle_t * handle,policydb_t * p,const avrule_t * avrule,unsigned int stype,unsigned int ttype,const class_perm_node_t * curperm,uint32_t perms,avtab_key_t * k,avtab_t * avtab)140 static int report_assertion_extended_permissions(sepol_handle_t *handle,
141 				policydb_t *p, const avrule_t *avrule,
142 				unsigned int stype, unsigned int ttype,
143 				const class_perm_node_t *curperm, uint32_t perms,
144 				avtab_key_t *k, avtab_t *avtab)
145 {
146 	avtab_ptr_t node;
147 	avtab_key_t tmp_key;
148 	avtab_extended_perms_t *xperms;
149 	avtab_extended_perms_t error;
150 	ebitmap_t *sattr = &p->type_attr_map[stype];
151 	ebitmap_t *tattr = &p->type_attr_map[ttype];
152 	ebitmap_node_t *snode, *tnode;
153 	unsigned int i, j;
154 	int rc = 1;
155 	int ret = 0;
156 
157 	memcpy(&tmp_key, k, sizeof(avtab_key_t));
158 	tmp_key.specified = AVTAB_XPERMS_ALLOWED;
159 
160 	ebitmap_for_each_bit(sattr, snode, i) {
161 		if (!ebitmap_node_get_bit(snode, i))
162 			continue;
163 		ebitmap_for_each_bit(tattr, tnode, j) {
164 			if (!ebitmap_node_get_bit(tnode, j))
165 				continue;
166 			tmp_key.source_type = i + 1;
167 			tmp_key.target_type = j + 1;
168 			for (node = avtab_search_node(avtab, &tmp_key);
169 			     node;
170 			     node = avtab_search_node_next(node, tmp_key.specified)) {
171 				xperms = node->datum.xperms;
172 				if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
173 						&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER))
174 					continue;
175 
176 				rc = check_extended_permissions(avrule->xperms, xperms);
177 				/* failure on the extended permission check_extended_permissions */
178 				if (rc) {
179 					extended_permissions_violated(&error, avrule->xperms, xperms);
180 					ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of policy.conf) violated by\n"
181 							"allowxperm %s %s:%s %s;",
182 							avrule->source_line, avrule->source_filename, avrule->line,
183 							p->p_type_val_to_name[i],
184 							p->p_type_val_to_name[j],
185 							p->p_class_val_to_name[curperm->tclass - 1],
186 							sepol_extended_perms_to_string(&error));
187 
188 					rc = 0;
189 					ret++;
190 				}
191 			}
192 		}
193 	}
194 
195 	/* failure on the regular permissions */
196 	if (rc) {
197 		ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of policy.conf) violated by\n"
198 				"allow %s %s:%s {%s };",
199 				avrule->source_line, avrule->source_filename, avrule->line,
200 				p->p_type_val_to_name[stype],
201 				p->p_type_val_to_name[ttype],
202 				p->p_class_val_to_name[curperm->tclass - 1],
203 				sepol_av_to_string(p, curperm->tclass, perms));
204 		ret++;
205 
206 	}
207 
208 	return ret;
209 }
210 
report_assertion_avtab_matches(avtab_key_t * k,avtab_datum_t * d,void * args)211 static int report_assertion_avtab_matches(avtab_key_t *k, avtab_datum_t *d, void *args)
212 {
213 	int rc = 0;
214 	struct avtab_match_args *a = (struct avtab_match_args *)args;
215 	sepol_handle_t *handle = a->handle;
216 	policydb_t *p = a->p;
217 	avtab_t *avtab = a->avtab;
218 	avrule_t *avrule = a->avrule;
219 	class_perm_node_t *cp;
220 	uint32_t perms;
221 	ebitmap_t src_matches, tgt_matches, self_matches, matches;
222 	ebitmap_node_t *snode, *tnode;
223 	unsigned int i, j;
224 
225 	if ((k->specified & AVTAB_ALLOWED) == 0)
226 		return 0;
227 
228 	if (!match_any_class_permissions(avrule->perms, k->target_class, d->data))
229 		return 0;
230 
231 	ebitmap_init(&src_matches);
232 	ebitmap_init(&tgt_matches);
233 	ebitmap_init(&self_matches);
234 	ebitmap_init(&matches);
235 
236 	rc = ebitmap_and(&src_matches, &avrule->stypes.types,
237 			 &p->attr_type_map[k->source_type - 1]);
238 	if (rc)
239 		goto oom;
240 
241 	if (ebitmap_length(&src_matches) == 0)
242 		goto exit;
243 
244 	rc = ebitmap_and(&tgt_matches, &avrule->ttypes.types, &p->attr_type_map[k->target_type -1]);
245 	if (rc)
246 		goto oom;
247 
248 	if (avrule->flags == RULE_SELF) {
249 		rc = ebitmap_and(&matches, &p->attr_type_map[k->source_type - 1], &p->attr_type_map[k->target_type - 1]);
250 		if (rc)
251 			goto oom;
252 		rc = ebitmap_and(&self_matches, &avrule->stypes.types, &matches);
253 		if (rc)
254 			goto oom;
255 
256 		if (ebitmap_length(&self_matches) > 0) {
257 			rc = ebitmap_union(&tgt_matches, &self_matches);
258 			if (rc)
259 				goto oom;
260 		}
261 	}
262 
263 	if (ebitmap_length(&tgt_matches) == 0)
264 		goto exit;
265 
266 	for (cp = avrule->perms; cp; cp = cp->next) {
267 
268 		perms = cp->data & d->data;
269 		if ((cp->tclass != k->target_class) || !perms) {
270 			continue;
271 		}
272 
273 		ebitmap_for_each_bit(&src_matches, snode, i) {
274 			if (!ebitmap_node_get_bit(snode, i))
275 				continue;
276 			ebitmap_for_each_bit(&tgt_matches, tnode, j) {
277 				if (!ebitmap_node_get_bit(tnode, j))
278 					continue;
279 
280 				if (avrule->specified == AVRULE_XPERMS_NEVERALLOW) {
281 					a->errors += report_assertion_extended_permissions(handle,p, avrule,
282 											i, j, cp, perms, k, avtab);
283 				} else {
284 					a->errors++;
285 					report_failure(handle, p, avrule, i, j, cp, perms);
286 				}
287 			}
288 		}
289 	}
290 	goto exit;
291 
292 oom:
293 	ERR(NULL, "Out of memory - unable to check neverallows");
294 
295 exit:
296 	ebitmap_destroy(&src_matches);
297 	ebitmap_destroy(&tgt_matches);
298 	ebitmap_destroy(&self_matches);
299 	ebitmap_destroy(&matches);
300 	return rc;
301 }
302 
report_assertion_failures(sepol_handle_t * handle,policydb_t * p,avrule_t * avrule)303 int report_assertion_failures(sepol_handle_t *handle, policydb_t *p, avrule_t *avrule)
304 {
305 	int rc;
306 	struct avtab_match_args args;
307 
308 	args.handle = handle;
309 	args.p = p;
310 	args.avrule = avrule;
311 	args.errors = 0;
312 
313 	rc = avtab_map(&p->te_avtab, report_assertion_avtab_matches, &args);
314 	if (rc)
315 		goto oom;
316 
317 	rc = avtab_map(&p->te_cond_avtab, report_assertion_avtab_matches, &args);
318 	if (rc)
319 		goto oom;
320 
321 	return args.errors;
322 
323 oom:
324 	return rc;
325 }
326 
327 /*
328  * Look up the extended permissions in avtab and verify that neverallowed
329  * permissions are not granted.
330  */
check_assertion_extended_permissions_avtab(avrule_t * avrule,avtab_t * avtab,unsigned int stype,unsigned int ttype,avtab_key_t * k,policydb_t * p)331 static int check_assertion_extended_permissions_avtab(avrule_t *avrule, avtab_t *avtab,
332 						unsigned int stype, unsigned int ttype,
333 						avtab_key_t *k, policydb_t *p)
334 {
335 	avtab_ptr_t node;
336 	avtab_key_t tmp_key;
337 	avtab_extended_perms_t *xperms;
338 	av_extended_perms_t *neverallow_xperms = avrule->xperms;
339 	ebitmap_t *sattr = &p->type_attr_map[stype];
340 	ebitmap_t *tattr = &p->type_attr_map[ttype];
341 	ebitmap_node_t *snode, *tnode;
342 	unsigned int i, j;
343 	int rc = 1;
344 
345 	memcpy(&tmp_key, k, sizeof(avtab_key_t));
346 	tmp_key.specified = AVTAB_XPERMS_ALLOWED;
347 
348 	ebitmap_for_each_bit(sattr, snode, i) {
349 		if (!ebitmap_node_get_bit(snode, i))
350 			continue;
351 		ebitmap_for_each_bit(tattr, tnode, j) {
352 			if (!ebitmap_node_get_bit(tnode, j))
353 				continue;
354 			tmp_key.source_type = i + 1;
355 			tmp_key.target_type = j + 1;
356 			for (node = avtab_search_node(avtab, &tmp_key);
357 			     node;
358 			     node = avtab_search_node_next(node, tmp_key.specified)) {
359 				xperms = node->datum.xperms;
360 
361 				if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
362 						&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER))
363 					continue;
364 				rc = check_extended_permissions(neverallow_xperms, xperms);
365 				if (rc)
366 					break;
367 			}
368 		}
369 	}
370 
371 	return rc;
372 }
373 
374 /*
375  * When the ioctl permission is granted on an avtab entry that matches an
376  * avrule neverallowxperm entry, enumerate over the matching
377  * source/target/class sets to determine if the extended permissions exist
378  * and if the neverallowed ioctls are granted.
379  *
380  * Four scenarios of interest:
381  * 1. PASS - the ioctl permission is not granted for this source/target/class
382  *    This case is handled in check_assertion_avtab_match
383  * 2. PASS - The ioctl permission is granted AND the extended permission
384  *    is NOT granted
385  * 3. FAIL - The ioctl permission is granted AND no extended permissions
386  *    exist
387  * 4. FAIL - The ioctl permission is granted AND the extended permission is
388  *    granted
389  */
check_assertion_extended_permissions(avrule_t * avrule,avtab_t * avtab,avtab_key_t * k,policydb_t * p)390 static int check_assertion_extended_permissions(avrule_t *avrule, avtab_t *avtab,
391 						avtab_key_t *k, policydb_t *p)
392 {
393 	ebitmap_t src_matches, tgt_matches, self_matches, matches;
394 	unsigned int i, j;
395 	ebitmap_node_t *snode, *tnode;
396 	class_perm_node_t *cp;
397 	int rc;
398 	int ret = 1;
399 
400 	ebitmap_init(&src_matches);
401 	ebitmap_init(&tgt_matches);
402 	ebitmap_init(&self_matches);
403 	ebitmap_init(&matches);
404 
405 	rc = ebitmap_and(&src_matches, &avrule->stypes.types,
406 			 &p->attr_type_map[k->source_type - 1]);
407 	if (rc)
408 		goto oom;
409 
410 	if (ebitmap_length(&src_matches) == 0)
411 		goto exit;
412 
413 	rc = ebitmap_and(&tgt_matches, &avrule->ttypes.types,
414 			 &p->attr_type_map[k->target_type -1]);
415 	if (rc)
416 		goto oom;
417 
418 	if (avrule->flags == RULE_SELF) {
419 		rc = ebitmap_and(&matches, &p->attr_type_map[k->source_type - 1],
420 				&p->attr_type_map[k->target_type - 1]);
421 		if (rc)
422 			goto oom;
423 		rc = ebitmap_and(&self_matches, &avrule->stypes.types, &matches);
424 		if (rc)
425 			goto oom;
426 
427 		if (ebitmap_length(&self_matches) > 0) {
428 			rc = ebitmap_union(&tgt_matches, &self_matches);
429 			if (rc)
430 				goto oom;
431 		}
432 	}
433 
434 	if (ebitmap_length(&tgt_matches) == 0)
435 		goto exit;
436 
437 	for (cp = avrule->perms; cp; cp = cp->next) {
438 		if (cp->tclass != k->target_class)
439 			continue;
440 		ebitmap_for_each_bit(&src_matches, snode, i) {
441 			if (!ebitmap_node_get_bit(snode, i))
442 				continue;
443 			ebitmap_for_each_bit(&tgt_matches, tnode, j) {
444 				if (!ebitmap_node_get_bit(tnode, j))
445 					continue;
446 
447 				ret = check_assertion_extended_permissions_avtab(
448 						avrule, avtab, i, j, k, p);
449 				if (ret)
450 					goto exit;
451 			}
452 		}
453 	}
454 	goto exit;
455 
456 oom:
457 	ERR(NULL, "Out of memory - unable to check neverallows");
458 
459 exit:
460 	ebitmap_destroy(&src_matches);
461 	ebitmap_destroy(&tgt_matches);
462 	ebitmap_destroy(&matches);
463 	return ret;
464 }
465 
check_assertion_avtab_match(avtab_key_t * k,avtab_datum_t * d,void * args)466 static int check_assertion_avtab_match(avtab_key_t *k, avtab_datum_t *d, void *args)
467 {
468 	int rc, rc2 = 0;
469 	struct avtab_match_args *a = (struct avtab_match_args *)args;
470 	policydb_t *p = a->p;
471 	avrule_t *avrule = a->avrule;
472 	avtab_t *avtab = a->avtab;
473 
474 	if ((k->specified & AVTAB_ALLOWED) == 0)
475 		goto exit;
476 
477 	if (!match_any_class_permissions(avrule->perms, k->target_class, d->data))
478 		goto exit;
479 
480 	rc = ebitmap_match_any(&avrule->stypes.types, &p->attr_type_map[k->source_type - 1]);
481 	if (rc == 0)
482 		goto exit;
483 
484 	if (avrule->flags == RULE_SELF) {
485 		/* If the neverallow uses SELF, then it is not enough that the
486 		 * neverallow's source matches the src and tgt of the rule being checked.
487 		 * It must match the same thing in the src and tgt, so AND the source
488 		 * and target together and check for a match on the result.
489 		 */
490 		ebitmap_t match;
491 		rc = ebitmap_and(&match, &p->attr_type_map[k->source_type - 1], &p->attr_type_map[k->target_type - 1] );
492 		if (rc) {
493 			ebitmap_destroy(&match);
494 			goto oom;
495 		}
496 		rc2 = ebitmap_match_any(&avrule->stypes.types, &match);
497 		ebitmap_destroy(&match);
498 	}
499 
500 	/* neverallow may have tgts even if it uses SELF */
501 	rc = ebitmap_match_any(&avrule->ttypes.types, &p->attr_type_map[k->target_type -1]);
502 	if (rc == 0 && rc2 == 0)
503 		goto exit;
504 
505 	if (avrule->specified == AVRULE_XPERMS_NEVERALLOW) {
506 		rc = check_assertion_extended_permissions(avrule, avtab, k, p);
507 		if (rc == 0)
508 			goto exit;
509 	}
510 	return 1;
511 
512 exit:
513 	return 0;
514 
515 oom:
516 	ERR(NULL, "Out of memory - unable to check neverallows");
517 	return rc;
518 }
519 
check_assertion(policydb_t * p,avrule_t * avrule)520 int check_assertion(policydb_t *p, avrule_t *avrule)
521 {
522 	int rc;
523 	struct avtab_match_args args;
524 
525 	args.handle = NULL;
526 	args.p = p;
527 	args.avrule = avrule;
528 	args.errors = 0;
529 	args.avtab = &p->te_avtab;
530 
531 	rc = avtab_map(&p->te_avtab, check_assertion_avtab_match, &args);
532 
533 	if (rc == 0) {
534 		args.avtab = &p->te_cond_avtab;
535 		rc = avtab_map(&p->te_cond_avtab, check_assertion_avtab_match, &args);
536 	}
537 
538 	return rc;
539 }
540 
check_assertions(sepol_handle_t * handle,policydb_t * p,avrule_t * avrules)541 int check_assertions(sepol_handle_t * handle, policydb_t * p,
542 		     avrule_t * avrules)
543 {
544 	int rc;
545 	avrule_t *a;
546 	unsigned long errors = 0;
547 
548 	if (!avrules) {
549 		/* Since assertions are stored in avrules, if it is NULL
550 		   there won't be any to check. This also prevents an invalid
551 		   free if the avtabs are never initialized */
552 		return 0;
553 	}
554 
555 	for (a = avrules; a != NULL; a = a->next) {
556 		if (!(a->specified & (AVRULE_NEVERALLOW | AVRULE_XPERMS_NEVERALLOW)))
557 			continue;
558 		rc = check_assertion(p, a);
559 		if (rc) {
560 			rc = report_assertion_failures(handle, p, a);
561 			if (rc < 0) {
562 				ERR(handle, "Error occurred while checking neverallows");
563 				return -1;
564 			}
565 			errors += rc;
566 		}
567 	}
568 
569 	if (errors)
570 		ERR(handle, "%lu neverallow failures occurred", errors);
571 
572 	return errors ? -1 : 0;
573 }
574