1/*
2 * Copyright 1999-2004 Gentoo Technologies, Inc.
3 * Distributed under the terms of the GNU General Public License v2
4 * $Header: /home/cvsroot/gentoo-projects/hardened/policycoreutils-extra/src/sestatus.c,v 1.10 2004/03/26 19:25:52 pebenito Exp $
5 * Patch provided by Steve Grubb
6 */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <errno.h>
12#include <selinux/selinux.h>
13#include <selinux/get_default_type.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <dirent.h>
17#include <unistd.h>
18#include <libgen.h>
19#include <ctype.h>
20
21#define PROC_BASE "/proc"
22#define MAX_CHECK 50
23#define CONF "/etc/sestatus.conf"
24
25/* conf file sections */
26#define PROCS "[process]"
27#define FILES "[files]"
28
29/* buffer size for cmp_cmdline */
30#define BUFSIZE 255
31
32/* column to put the output (must be a multiple of 8) */
33static unsigned int COL = 32;
34
35extern char *selinux_mnt;
36
37int cmp_cmdline(const char *command, int pid)
38{
39
40	char buf[BUFSIZE];
41	char filename[BUFSIZE];
42
43	memset(buf, '\0', BUFSIZE);
44
45	/* first read the proc entry */
46	sprintf(filename, "%s/%d/exe", PROC_BASE, pid);
47
48	if (readlink(filename, buf, BUFSIZE) < 0)
49		return 0;
50
51	if (buf[BUFSIZE - 1] != '\0')
52		buf[BUFSIZE - 1] = '\0';
53
54	/* check if this is the command we're looking for. */
55	if (strcmp(command, buf) == 0)
56		return 1;
57	else
58		return 0;
59}
60
61int pidof(const char *command)
62{
63/* inspired by killall5.c from psmisc */
64	DIR *dir;
65	struct dirent *de;
66	int pid, ret = -1, self = getpid();
67
68	if (!(dir = opendir(PROC_BASE))) {
69		perror(PROC_BASE);
70		return -1;
71	}
72
73	while ((de = readdir(dir)) != NULL) {
74		errno = 0;
75		pid = (int)strtol(de->d_name, (char **)NULL, 10);
76		if (errno || pid == 0 || pid == self)
77			continue;
78		if (cmp_cmdline(command, pid)) {
79			ret = pid;
80			break;
81		}
82	}
83
84	closedir(dir);
85	return ret;
86}
87
88void load_checks(char *pc[], int *npc, char *fc[], int *nfc)
89{
90
91	FILE *fp = fopen(CONF, "r");
92	char buf[255], *bufp;
93	int buf_len, section = -1;
94	int proclen = strlen(PROCS);
95	int filelen = strlen(FILES);
96
97	if (fp == NULL) {
98		printf("\nUnable to open %s.\n", CONF);
99		return;
100	}
101
102	while (!feof(fp)) {
103		if (!fgets(buf, sizeof buf, fp))
104			break;
105
106		buf_len = strlen(buf);
107		if (buf[buf_len - 1] == '\n')
108			buf[buf_len - 1] = 0;
109
110		bufp = buf;
111		while (*bufp && isspace(*bufp)) {
112			bufp++;
113			buf_len--;
114		}
115
116		if (*bufp == '#')
117			/* skip comments */
118			continue;
119
120		if (*bufp) {
121			if (!(*bufp))
122				goto out;
123
124			if (strncmp(bufp, PROCS, proclen) == 0)
125				section = 0;
126			else if (strncmp(bufp, FILES, filelen) == 0)
127				section = 1;
128			else {
129				switch (section) {
130				case 0:
131					if (*npc >= MAX_CHECK)
132						break;
133					pc[*npc] =
134					    (char *)malloc((buf_len) *
135							   sizeof(char));
136					memcpy(pc[*npc], bufp, buf_len);
137					(*npc)++;
138					bufp = NULL;
139					break;
140				case 1:
141					if (*nfc >= MAX_CHECK)
142						break;
143					fc[*nfc] =
144					    (char *)malloc((buf_len) *
145							   sizeof(char));
146					memcpy(fc[*nfc], bufp, buf_len);
147					(*nfc)++;
148					bufp = NULL;
149					break;
150				default:
151					/* ignore lines before a section */
152					printf("Line not in a section: %s.\n",
153					       buf);
154					break;
155				}
156			}
157		}
158	}
159      out:
160	fclose(fp);
161	return;
162}
163
164void printf_tab(const char *outp)
165{
166	char buf[20];
167	snprintf(buf, sizeof(buf), "%%-%us", COL);
168	printf(buf, outp);
169
170}
171
172int main(int argc, char **argv)
173{
174	/* these vars are reused several times */
175	int rc, opt, i, c;
176	char *context, *root_path;
177
178	/* files that need context checks */
179	char *fc[MAX_CHECK];
180	char *cterm = ttyname(0);
181	int nfc = 0;
182	struct stat m;
183
184	/* processes that need context checks */
185	char *pc[MAX_CHECK];
186	int npc = 0;
187
188	/* booleans */
189	char **bools;
190	int nbool;
191
192	int verbose = 0;
193	int show_bools = 0;
194
195	/* policy */
196	const char *pol_name, *root_dir;
197	char *pol_path;
198
199
200	while (1) {
201		opt = getopt(argc, argv, "vb");
202		if (opt == -1)
203			break;
204		switch (opt) {
205		case 'v':
206			verbose = 1;
207			break;
208		case 'b':
209			show_bools = 1;
210			break;
211		default:
212			/* invalid option */
213			printf("\nUsage: %s [OPTION]\n\n", basename(argv[0]));
214			printf("  -v  Verbose check of process and file contexts.\n");
215			printf("  -b  Display current state of booleans.\n");
216			printf("\nWithout options, show SELinux status.\n");
217			return -1;
218		}
219	}
220	printf_tab("SELinux status:");
221	rc = is_selinux_enabled();
222
223	switch (rc) {
224	case 1:
225		printf("enabled\n");
226		break;
227	case 0:
228		printf("disabled\n");
229		return 0;
230		break;
231	default:
232		printf("unknown (%s)\n", strerror(errno));
233		return 0;
234		break;
235	}
236
237	printf_tab("SELinuxfs mount:");
238	if (selinux_mnt != NULL) {
239		printf("%s\n", selinux_mnt);
240	} else {
241		printf("not mounted\n\n");
242		printf("Please mount selinuxfs for proper results.\n");
243		return -1;
244	}
245
246	printf_tab("SELinux root directory:");
247	root_dir = selinux_path();
248	if (root_dir == NULL) {
249		printf("error (%s)\n", strerror(errno));
250		return -1;
251	}
252	/* The path has a trailing '/' so duplicate to edit */
253	root_path = strdup(root_dir);
254	if (!root_path) {
255		printf("malloc error (%s)\n", strerror(errno));
256		return -1;
257	}
258	/* actually blank the '/' */
259	root_path[strlen(root_path) - 1] = '\0';
260	printf("%s\n", root_path);
261	free(root_path);
262
263	/* Dump all the path information */
264	printf_tab("Loaded policy name:");
265	pol_path = strdup(selinux_policy_root());
266	if (pol_path) {
267		pol_name = basename(pol_path);
268		puts(pol_name);
269		free(pol_path);
270	} else {
271		printf("error (%s)\n", strerror(errno));
272	}
273
274	printf_tab("Current mode:");
275	rc = security_getenforce();
276	switch (rc) {
277	case 1:
278		printf("enforcing\n");
279		break;
280	case 0:
281		printf("permissive\n");
282		break;
283	default:
284		printf("unknown (%s)\n", strerror(errno));
285		break;
286	}
287
288	printf_tab("Mode from config file:");
289	if (selinux_getenforcemode(&rc) == 0) {
290		switch (rc) {
291		case 1:
292			printf("enforcing\n");
293			break;
294		case 0:
295			printf("permissive\n");
296			break;
297		case -1:
298			printf("disabled\n");
299			break;
300		}
301	} else {
302		printf("error (%s)\n", strerror(errno));
303	}
304
305	printf_tab("Policy MLS status:");
306	rc = is_selinux_mls_enabled();
307	switch (rc) {
308		case 0:
309			printf("disabled\n");
310			break;
311		case 1:
312			printf("enabled\n");
313			break;
314		default:
315			printf("error (%s)\n", strerror(errno));
316			break;
317	}
318
319	printf_tab("Policy deny_unknown status:");
320	rc = security_deny_unknown();
321	switch (rc) {
322		case 0:
323			printf("allowed\n");
324			break;
325		case 1:
326			printf("denied\n");
327			break;
328		default:
329			printf("error (%s)\n", strerror(errno));
330			break;
331	}
332
333	rc = security_policyvers();
334	printf_tab("Max kernel policy version:");
335	if (rc < 0)
336		printf("unknown (%s)\n", strerror(errno));
337	else
338		printf("%d\n", rc);
339
340
341	if (show_bools) {
342		/* show booleans */
343		if (security_get_boolean_names(&bools, &nbool) >= 0) {
344			printf("\nPolicy booleans:\n");
345
346			for (i = 0; i < nbool; i++) {
347				if (strlen(bools[i]) + 1 > COL)
348					COL = strlen(bools[i]) + 1;
349			}
350			for (i = 0; i < nbool; i++) {
351				printf_tab(bools[i]);
352
353				rc = security_get_boolean_active(bools[i]);
354				switch (rc) {
355				case 1:
356					printf("on");
357					break;
358				case 0:
359					printf("off");
360					break;
361				default:
362					printf("unknown (%s)", strerror(errno));
363					break;
364				}
365				c = security_get_boolean_pending(bools[i]);
366				if (c != rc)
367					switch (c) {
368					case 1:
369						printf(" (activate pending)");
370						break;
371					case 0:
372						printf(" (inactivate pending)");
373						break;
374					default:
375						printf(" (pending error: %s)",
376						       strerror(errno));
377						break;
378					}
379				printf("\n");
380
381				/* free up the booleans */
382				free(bools[i]);
383			}
384			free(bools);
385		}
386	}
387	/* only show contexts if -v is given */
388	if (!verbose)
389		return 0;
390
391	load_checks(pc, &npc, fc, &nfc);
392
393	printf("\nProcess contexts:\n");
394
395	printf_tab("Current context:");
396	if (getcon(&context) >= 0) {
397		printf("%s\n", context);
398		freecon(context);
399	} else
400		printf("unknown (%s)\n", strerror(errno));
401
402	printf_tab("Init context:");
403	if (getpidcon(1, &context) >= 0) {
404		printf("%s\n", context);
405		freecon(context);
406	} else
407		printf("unknown (%s)\n", strerror(errno));
408
409	for (i = 0; i < npc; i++) {
410		rc = pidof(pc[i]);
411		if (rc > 0) {
412			if (getpidcon(rc, &context) < 0)
413				continue;
414
415			printf_tab(pc[i]);
416			printf("%s\n", context);
417			freecon(context);
418		}
419	}
420
421	printf("\nFile contexts:\n");
422
423	/* controlling term */
424	printf_tab("Controlling terminal:");
425	if (lgetfilecon(cterm, &context) >= 0) {
426		printf("%s\n", context);
427		freecon(context);
428	} else {
429		printf("unknown (%s)\n", strerror(errno));
430	}
431
432	for (i = 0; i < nfc; i++) {
433		if (lgetfilecon(fc[i], &context) >= 0) {
434			printf_tab(fc[i]);
435
436			/* check if this is a symlink */
437			if (lstat(fc[i], &m)) {
438				printf
439				    ("%s (could not check link status (%s)!)\n",
440				     context, strerror(errno));
441				freecon(context);
442				continue;
443			}
444			if (S_ISLNK(m.st_mode)) {
445				/* print link target context */
446				printf("%s -> ", context);
447				freecon(context);
448
449				if (getfilecon(fc[i], &context) >= 0) {
450					printf("%s\n", context);
451					freecon(context);
452				} else {
453					printf("unknown (%s)\n",
454					       strerror(errno));
455				}
456			} else {
457				printf("%s\n", context);
458				freecon(context);
459			}
460		}
461	}
462
463	return 0;
464}
465