1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <getopt.h>
5 #include <errno.h>
6 #include <stdbool.h>
7 #include <fts.h>
8 #include <selinux/selinux.h>
9 #include <selinux/label.h>
10 
11 #include "../src/label_file.h"
12 
usage(const char *progname)13 static __attribute__ ((__noreturn__)) void usage(const char *progname)
14 {
15 	fprintf(stderr,
16 		"usage:  %s [-vr] [-f file] path\n\n"
17 		"Where:\n\t"
18 		"-v  Validate file_contxts entries against loaded policy.\n\t"
19 		"-r  Recursively descend directories.\n\t"
20 		"-f  Optional file_contexts file (defaults to current policy).\n\t"
21 		"path  Path to check current SHA1 digest against file_contexts entries.\n\n"
22 		"This will check the directory selinux.sehash SHA1 digest for "
23 		"<path> against\na newly generated digest based on the "
24 		"file_context entries for that node\n(using the regx, mode "
25 		"and path entries).\n", progname);
26 	exit(1);
27 }
28 
main(int argc, char **argv)29 int main(int argc, char **argv)
30 {
31 	int opt, fts_flags;
32 	size_t i, digest_len;
33 	bool status, recurse = false;
34 	FTS *fts;
35 	FTSENT *ftsent;
36 	char *validate = NULL, *file = NULL;
37 	char *paths[2] = { NULL, NULL };
38 	uint8_t *xattr_digest = NULL;
39 	uint8_t *calculated_digest = NULL;
40 	char *sha1_buf = NULL;
41 
42 	struct selabel_handle *hnd;
43 	struct selinux_opt selabel_option[] = {
44 		{ SELABEL_OPT_PATH, file },
45 		{ SELABEL_OPT_VALIDATE, validate }
46 	};
47 
48 	if (argc < 2)
49 		usage(argv[0]);
50 
51 	while ((opt = getopt(argc, argv, "f:rv")) > 0) {
52 		switch (opt) {
53 		case 'f':
54 			file = optarg;
55 			break;
56 		case 'r':
57 			recurse = true;
58 			break;
59 		case 'v':
60 			validate = (char *)1;
61 			break;
62 		default:
63 			usage(argv[0]);
64 		}
65 	}
66 
67 	if (optind >= argc) {
68 		fprintf(stderr, "No pathname specified\n");
69 		exit(-1);
70 	}
71 
72 	paths[0] = argv[optind];
73 
74 	selabel_option[0].value = file;
75 	selabel_option[1].value = validate;
76 
77 	hnd = selabel_open(SELABEL_CTX_FILE, selabel_option, 2);
78 	if (!hnd) {
79 		fprintf(stderr, "ERROR: selabel_open - Could not obtain "
80 							     "handle:  %s\n",
81 							     strerror(errno));
82 		return -1;
83 	}
84 
85 	fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
86 	fts = fts_open(paths, fts_flags, NULL);
87 	if (!fts) {
88 		printf("fts error on %s: %s\n",
89 		       paths[0], strerror(errno));
90 		return -1;
91 	}
92 
93 	while ((ftsent = fts_read(fts)) != NULL) {
94 		switch (ftsent->fts_info) {
95 		case FTS_DP:
96 			continue;
97 		case FTS_D: {
98 
99 			xattr_digest = NULL;
100 			calculated_digest = NULL;
101 			digest_len = 0;
102 
103 			status = selabel_get_digests_all_partial_matches(hnd,
104 							 ftsent->fts_path,
105 							 &calculated_digest,
106 							 &xattr_digest,
107 							 &digest_len);
108 
109 			sha1_buf = calloc(1, digest_len * 2 + 1);
110 			if (!sha1_buf) {
111 				fprintf(stderr, "Could not calloc buffer ERROR: %s\n",
112 					    strerror(errno));
113 				return -1;
114 			}
115 
116 			if (status) { /* They match */
117 				printf("xattr and file_contexts SHA1 digests match for: %s\n",
118 				       ftsent->fts_path);
119 
120 				if (calculated_digest) {
121 					for (i = 0; i < digest_len; i++)
122 						sprintf((&sha1_buf[i * 2]),
123 							"%02x",
124 							calculated_digest[i]);
125 					printf("SHA1 digest: %s\n", sha1_buf);
126 				}
127 			} else {
128 				if (!calculated_digest) {
129 					printf("No SHA1 digest available for: %s\n",
130 					       ftsent->fts_path);
131 					printf("as file_context entry is \"<<none>>\"\n");
132 					goto cleanup;
133 				}
134 
135 				printf("The file_context entries for: %s\n",
136 				       ftsent->fts_path);
137 
138 				for (i = 0; i < digest_len; i++)
139 					sprintf((&sha1_buf[i * 2]), "%02x",
140 						calculated_digest[i]);
141 				printf("generated SHA1 digest: %s\n", sha1_buf);
142 
143 				if (!xattr_digest) {
144 					printf("however there is no selinux.sehash xattr entry.\n");
145 				} else {
146 					printf("however it does NOT match the current entry of:\n");
147 					for (i = 0; i < digest_len; i++)
148 						sprintf((&sha1_buf[i * 2]),
149 							"%02x",
150 							xattr_digest[i]);
151 					printf("%s\n", sha1_buf);
152 				}
153 			}
154 			cleanup:
155 			free(xattr_digest);
156 			free(calculated_digest);
157 			free(sha1_buf);
158 			break;
159 		}
160 		default:
161 			break;
162 		}
163 
164 		if (!recurse)
165 			break;
166 	}
167 
168 	(void) fts_close(fts);
169 	(void) selabel_close(hnd);
170 	return 0;
171 }
172