1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /* Copyright 2015, Richard Lowe. */
13 
14 #include <sys/secflags.h>
15 #include <sys/types.h>
16 
17 #if defined(_KERNEL)
18 #include <sys/kmem.h>
19 #include <sys/sunddi.h>
20 #else
21 #include "lint.h"		/* libc header */
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <strings.h>
25 #endif
26 
27 secflagset_t
28 secflag_to_bit(secflag_t secflag)
29 {
30 	return (1 << secflag);
31 }
32 
33 boolean_t
34 secflag_isset(secflagset_t flags, secflag_t sf)
35 {
36 	return ((flags & secflag_to_bit(sf)) != 0);
37 }
38 
39 void
40 secflag_clear(secflagset_t *flags, secflag_t sf)
41 {
42 	*flags &= ~secflag_to_bit(sf);
43 }
44 
45 void
46 secflag_set(secflagset_t *flags, secflag_t sf)
47 {
48 	*flags |= secflag_to_bit(sf);
49 }
50 
51 boolean_t
52 secflags_isempty(secflagset_t flags)
53 {
54 	return (flags == 0);
55 }
56 
57 void
58 secflags_zero(secflagset_t *flags)
59 {
60 	*flags = 0;
61 }
62 
63 void
64 secflags_fullset(secflagset_t *flags)
65 {
66 	*flags = PROC_SEC_MASK;
67 }
68 
69 void
70 secflags_copy(secflagset_t *dst, const secflagset_t *src)
71 {
72 	*dst = *src;
73 }
74 
75 boolean_t
76 secflags_issubset(secflagset_t a, secflagset_t b)
77 {
78 	return (!(a & ~b));
79 }
80 
81 boolean_t
82 secflags_issuperset(secflagset_t a, secflagset_t b)
83 {
84 	return (secflags_issubset(b, a));
85 }
86 
87 boolean_t
88 secflags_intersection(secflagset_t a, secflagset_t b)
89 {
90 	return (a & b);
91 }
92 
93 void
94 secflags_union(secflagset_t *a, const secflagset_t *b)
95 {
96 	*a |= *b;
97 }
98 
99 void
100 secflags_difference(secflagset_t *a, const secflagset_t *b)
101 {
102 	*a &= ~*b;
103 }
104 
105 boolean_t
106 psecflags_validate_delta(const psecflags_t *sf, const secflagdelta_t *delta)
107 {
108 	if (delta->psd_ass_active) {
109 		/*
110 		 * If there's a bit in lower not in args, or a bit args not in
111 		 * upper
112 		 */
113 		if (!secflags_issubset(delta->psd_assign, sf->psf_upper) ||
114 		    !secflags_issuperset(delta->psd_assign, sf->psf_lower)) {
115 			return (B_FALSE);
116 		}
117 
118 		if (!secflags_issubset(delta->psd_assign, PROC_SEC_MASK))
119 			return (B_FALSE);
120 	} else {
121 		/* If we're adding a bit not in upper */
122 		if (!secflags_isempty(delta->psd_add)) {
123 			if (!secflags_issubset(delta->psd_add, sf->psf_upper)) {
124 				return (B_FALSE);
125 			}
126 		}
127 
128 		/* If we're removing a bit that's in lower */
129 		if (!secflags_isempty(delta->psd_rem)) {
130 			if (secflags_intersection(delta->psd_rem,
131 			    sf->psf_lower)) {
132 				return (B_FALSE);
133 			}
134 		}
135 
136 		if (!secflags_issubset(delta->psd_add, PROC_SEC_MASK) ||
137 		    !secflags_issubset(delta->psd_rem, PROC_SEC_MASK))
138 			return (B_FALSE);
139 	}
140 
141 	return (B_TRUE);
142 }
143 
144 boolean_t
145 psecflags_validate(const psecflags_t *sf)
146 {
147 	if (!secflags_issubset(sf->psf_lower, PROC_SEC_MASK) ||
148 	    !secflags_issubset(sf->psf_inherit, PROC_SEC_MASK) ||
149 	    !secflags_issubset(sf->psf_effective, PROC_SEC_MASK) ||
150 	    !secflags_issubset(sf->psf_upper, PROC_SEC_MASK))
151 		return (B_FALSE);
152 
153 	if (!secflags_issubset(sf->psf_lower, sf->psf_inherit))
154 		return (B_FALSE);
155 	if (!secflags_issubset(sf->psf_lower, sf->psf_upper))
156 		return (B_FALSE);
157 	if (!secflags_issubset(sf->psf_inherit, sf->psf_upper))
158 		return (B_FALSE);
159 
160 	return (B_TRUE);
161 }
162 
163 void
164 psecflags_default(psecflags_t *sf)
165 {
166 	secflags_zero(&sf->psf_effective);
167 	secflags_zero(&sf->psf_inherit);
168 	secflags_zero(&sf->psf_lower);
169 	secflags_fullset(&sf->psf_upper);
170 }
171 
172 static struct flagdesc {
173 	secflag_t value;
174 	const char *name;
175 } flagdescs[] = {
176 	{ PROC_SEC_ASLR,		"aslr" },
177 	{ PROC_SEC_FORBIDNULLMAP,	"forbidnullmap" },
178 	{ PROC_SEC_NOEXECSTACK,		"noexecstack" },
179 	{ 0x0, NULL }
180 };
181 
182 boolean_t
183 secflag_by_name(const char *str, secflag_t *ret)
184 {
185 	struct flagdesc *fd;
186 
187 	for (fd = flagdescs; fd->name != NULL; fd++) {
188 		if (strcasecmp(str, fd->name) == 0) {
189 			*ret = fd->value;
190 			return (B_TRUE);
191 		}
192 	}
193 
194 	return (B_FALSE);
195 }
196 
197 const char *
198 secflag_to_str(secflag_t sf)
199 {
200 	struct flagdesc *fd;
201 
202 	for (fd = flagdescs; fd->name != NULL; fd++) {
203 		if (sf == fd->value)
204 			return (fd->name);
205 	}
206 
207 	return (NULL);
208 }
209 
210 void
211 secflags_to_str(secflagset_t flags, char *buf, size_t buflen)
212 {
213 	struct flagdesc *fd;
214 
215 	if (buflen >= 1)
216 		buf[0] = '\0';
217 
218 	if (flags == 0) {
219 		(void) strlcpy(buf, "none", buflen);
220 		return;
221 	}
222 
223 	for (fd = flagdescs; fd->name != NULL; fd++) {
224 		if (secflag_isset(flags, fd->value)) {
225 			if (buf[0] != '\0')
226 				(void) strlcat(buf, ",", buflen);
227 			(void) strlcat(buf, fd->name, buflen);
228 		}
229 
230 		secflag_clear(&flags, fd->value);
231 	}
232 
233 	if (flags != 0) { 	/* unknown flags */
234 		char hexbuf[19]; /* 0x%16 PRIx64 */
235 
236 		(void) snprintf(hexbuf, sizeof (hexbuf), "0x%16" PRIx64, flags);
237 		if (buf[0] != '\0')
238 			(void) strlcat(buf, ",", buflen);
239 		(void) strlcat(buf, hexbuf, buflen);
240 	}
241 }
242