Print this page
7029 want per-process exploit mitigation features (secflags)
7030 want basic address space layout randomization (aslr)
7031 noexec_user_stack should be a secflag
7032 want a means to forbid mappings around NULL.
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libc/port/gen/priv_str_xlate.c
+++ new/usr/src/lib/libc/port/gen/priv_str_xlate.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 */
26 26
27 27 /*
28 28 * priv_str_xlate.c - Privilege translation routines.
29 29 */
30 30
31 31 #pragma weak _priv_str_to_set = priv_str_to_set
32 32 #pragma weak _priv_set_to_str = priv_set_to_str
33 33 #pragma weak _priv_gettext = priv_gettext
34 34
35 35 #include "lint.h"
36 36 #include <stdio.h>
37 37 #include <stdlib.h>
38 38 #include <ctype.h>
39 39 #include <strings.h>
40 40 #include <errno.h>
41 41 #include <string.h>
42 42 #include <locale.h>
43 43 #include <sys/param.h>
44 44 #include <priv.h>
45 45 #include <alloca.h>
46 46 #include <locale.h>
47 47 #include "libc.h"
48 48 #include "../i18n/_loc_path.h"
49 49 #include "priv_private.h"
50 50
51 51 priv_set_t *
52 52 priv_basic(void)
53 53 {
54 54 priv_data_t *d;
55 55
56 56 LOADPRIVDATA(d);
57 57
58 58 return (d->pd_basicset);
59 59 }
60 60
61 61 /*
62 62 * Name: priv_str_to_set()
63 63 *
64 64 * Description: Given a buffer with privilege strings, the
↓ open down ↓ |
64 lines elided |
↑ open up ↑ |
65 65 * equivalent privilege set is returned.
66 66 *
67 67 * Special tokens recognized: all, none, basic and "".
68 68 *
69 69 * On failure, this function returns NULL.
70 70 * *endptr == NULL and errno set: resource error.
71 71 * *endptr != NULL: parse error.
72 72 */
73 73 priv_set_t *
74 74 priv_str_to_set(const char *priv_names,
75 - const char *separators,
76 - const char **endptr)
75 + const char *separators,
76 + const char **endptr)
77 77 {
78 78
79 79 char *base;
80 80 char *offset;
81 81 char *last;
82 82 priv_set_t *pset = NULL;
83 83 priv_set_t *zone;
84 84 priv_set_t *basic;
85 85
86 86 if (endptr != NULL)
87 87 *endptr = NULL;
88 88
89 89 if ((base = libc_strdup(priv_names)) == NULL ||
90 90 (pset = priv_allocset()) == NULL) {
91 91 /* Whether base is NULL or allocated, this works */
92 92 libc_free(base);
93 93 return (NULL);
94 94 }
95 95
96 96 priv_emptyset(pset);
97 97 basic = priv_basic();
98 98 zone = privdata->pd_zoneset;
99 99
100 100 /* This is how to use strtok_r nicely in a while loop ... */
101 101 last = base;
102 102
103 103 while ((offset = strtok_r(NULL, separators, &last)) != NULL) {
104 104 /*
105 105 * Search for these special case strings.
106 106 */
107 107 if (basic != NULL && strcasecmp(offset, "basic") == 0) {
108 108 priv_union(basic, pset);
109 109 } else if (strcasecmp(offset, "none") == 0) {
110 110 priv_emptyset(pset);
111 111 } else if (strcasecmp(offset, "all") == 0) {
112 112 priv_fillset(pset);
113 113 } else if (strcasecmp(offset, "zone") == 0) {
114 114 priv_union(zone, pset);
115 115 } else {
116 116 boolean_t neg = (*offset == '-' || *offset == '!');
117 117 int privid;
118 118 int slen;
119 119
120 120 privid = priv_getbyname(offset +
121 121 ((neg || *offset == '+') ? 1 : 0));
122 122 if (privid < 0) {
123 123 slen = offset - base;
124 124 libc_free(base);
125 125 priv_freeset(pset);
126 126 if (endptr != NULL)
127 127 *endptr = priv_names + slen;
128 128 errno = EINVAL;
129 129 return (NULL);
130 130 } else {
131 131 if (neg)
132 132 PRIV_DELSET(pset, privid);
133 133 else
134 134 PRIV_ADDSET(pset, privid);
135 135 }
136 136 }
137 137 }
138 138
139 139 libc_free(base);
140 140 return (pset);
141 141 }
142 142
143 143 /*
144 144 * Name: priv_set_to_str()
145 145 *
146 146 * Description: Given a set of privileges, list of privileges are
147 147 * returned in privilege numeric order (which can be an ASCII sorted
148 148 * list as our implementation allows renumbering.
149 149 *
150 150 * String "none" identifies an empty privilege set, and string "all"
151 151 * identifies a full set.
152 152 *
153 153 * A pointer to a buffer is returned which needs to be freed by
154 154 * the caller.
155 155 *
156 156 * Several types of output are supported:
157 157 * PRIV_STR_PORT - portable output: basic,!basic
158 158 * PRIV_STR_LIT - literal output
159 159 * PRIV_STR_SHORT - shortest output
160 160 *
161 161 * NOTE: this function is called both from inside the library for the
162 162 * current environment and from outside the library using an externally
163 163 * generated priv_data_t * in order to analyze core files. It should
164 164 * return strings which can be free()ed by applications and it should
165 165 * not use any data from the current environment except in the special
166 166 * case that it is called from within libc, with a NULL priv_data_t *
167 167 * argument.
168 168 */
169 169
170 170 char *
171 171 __priv_set_to_str(
172 172 priv_data_t *d,
173 173 const priv_set_t *pset,
174 174 char separator,
175 175 int flag)
176 176 {
177 177 const char *pstr;
178 178 char *res, *resp;
179 179 int i;
180 180 char neg = separator == '!' ? '-' : '!';
181 181 priv_set_t *zone;
182 182 boolean_t all;
183 183 boolean_t use_libc_data = (d == NULL);
184 184
185 185 if (use_libc_data)
186 186 LOADPRIVDATA(d);
187 187
188 188 if (flag != PRIV_STR_PORT && __priv_isemptyset(d, pset))
189 189 return (strdup("none"));
190 190 if (flag != PRIV_STR_LIT && __priv_isfullset(d, pset))
191 191 return (strdup("all"));
192 192
193 193 /* Safe upper bound: global info contains all NULL separated privs */
194 194 res = resp = alloca(d->pd_pinfo->priv_globalinfosize);
195 195
196 196 /*
197 197 * Compute the shortest form; i.e., the form with the fewest privilege
198 198 * tokens.
199 199 * The following forms are possible:
200 200 * literal: priv1,priv2,priv3
201 201 * tokcount = present
202 202 * port: basic,!missing_basic,other
203 203 * tokcount = 1 + present - presentbasic + missingbasic
204 204 * zone: zone,!missing_zone
205 205 * tokcount = 1 + missingzone
206 206 * all: all,!missing1,!missing2
207 207 * tokcount = 1 + d->pd_nprivs - present;
208 208 *
209 209 * Note that zone and all forms are identical in the global zone;
210 210 * in that case (or any other where the token count is the same),
211 211 * all is preferred. Also, the zone form is only used when the
212 212 * indicated privileges are a subset of the zone set.
213 213 */
214 214
215 215 if (use_libc_data)
216 216 LOCKPRIVDATA();
217 217
218 218 if (flag == PRIV_STR_SHORT) {
219 219 int presentbasic, missingbasic, present, missing;
220 220 int presentzone, missingzone;
221 221 int count;
222 222
223 223 presentbasic = missingbasic = present = 0;
224 224 presentzone = missingzone = 0;
225 225 zone = d->pd_zoneset;
226 226
227 227 for (i = 0; i < d->pd_nprivs; i++) {
228 228 int mem = PRIV_ISMEMBER(pset, i);
229 229 if (d->pd_basicset != NULL &&
230 230 PRIV_ISMEMBER(d->pd_basicset, i)) {
231 231 if (mem)
232 232 presentbasic++;
233 233 else
234 234 missingbasic++;
235 235 }
236 236 if (zone != NULL && PRIV_ISMEMBER(zone, i)) {
237 237 if (mem)
238 238 presentzone++;
239 239 else
240 240 missingzone++;
241 241 }
242 242 if (mem)
243 243 present++;
244 244 }
245 245 missing = d->pd_nprivs - present;
246 246
247 247 if (1 - presentbasic + missingbasic < 0) {
248 248 flag = PRIV_STR_PORT;
249 249 count = present + 1 - presentbasic + missingbasic;
250 250 } else {
251 251 flag = PRIV_STR_LIT;
252 252 count = present;
253 253 }
254 254 if (count >= 1 + missing) {
255 255 flag = PRIV_STR_SHORT;
256 256 count = 1 + missing;
257 257 all = B_TRUE;
258 258 }
259 259 if (present == presentzone && 1 + missingzone < count) {
260 260 flag = PRIV_STR_SHORT;
261 261 all = B_FALSE;
262 262 }
263 263 }
264 264
265 265 switch (flag) {
266 266 case PRIV_STR_LIT:
267 267 *res = '\0';
268 268 break;
269 269 case PRIV_STR_PORT:
270 270 (void) strcpy(res, "basic");
271 271 if (d->pd_basicset == NULL)
272 272 flag = PRIV_STR_LIT;
273 273 break;
274 274 case PRIV_STR_SHORT:
275 275 if (all)
276 276 (void) strcpy(res, "all");
277 277 else
278 278 (void) strcpy(res, "zone");
279 279 break;
280 280 default:
281 281 if (use_libc_data)
282 282 UNLOCKPRIVDATA();
283 283 return (NULL);
284 284 }
285 285 res += strlen(res);
286 286
287 287 for (i = 0; i < d->pd_nprivs; i++) {
288 288 /* Map the privilege to the next one sorted by name */
289 289 int priv = d->pd_setsort[i];
290 290
291 291 if (PRIV_ISMEMBER(pset, priv)) {
292 292 switch (flag) {
293 293 case PRIV_STR_SHORT:
294 294 if (all || PRIV_ISMEMBER(zone, priv))
295 295 continue;
296 296 break;
297 297 case PRIV_STR_PORT:
298 298 if (PRIV_ISMEMBER(d->pd_basicset, priv))
299 299 continue;
300 300 break;
301 301 case PRIV_STR_LIT:
302 302 break;
303 303 }
304 304 if (res != resp)
305 305 *res++ = separator;
306 306 } else {
307 307 switch (flag) {
308 308 case PRIV_STR_LIT:
309 309 continue;
310 310 case PRIV_STR_PORT:
311 311 if (!PRIV_ISMEMBER(d->pd_basicset, priv))
312 312 continue;
313 313 break;
314 314 case PRIV_STR_SHORT:
315 315 if (!all && !PRIV_ISMEMBER(zone, priv))
316 316 continue;
317 317 break;
318 318 }
319 319 if (res != resp)
320 320 *res++ = separator;
321 321 *res++ = neg;
322 322 }
323 323 pstr = __priv_getbynum(d, priv);
324 324 (void) strcpy(res, pstr);
325 325 res += strlen(pstr);
326 326 }
327 327 if (use_libc_data)
328 328 UNLOCKPRIVDATA();
329 329 /* Special case the set with some high bits set */
330 330 return (strdup(*resp == '\0' ? "none" : resp));
331 331 }
332 332
333 333 /*
334 334 * priv_set_to_str() is defined to return a string that
335 335 * the caller must deallocate with free(3C). Grr...
336 336 */
337 337 char *
338 338 priv_set_to_str(const priv_set_t *pset, char separator, int flag)
339 339 {
340 340 return (__priv_set_to_str(NULL, pset, separator, flag));
341 341 }
342 342
343 343 static char *
344 344 do_priv_gettext(const char *priv, const char *file)
345 345 {
346 346 char buf[8*1024];
347 347 boolean_t inentry = B_FALSE;
348 348 FILE *namefp;
349 349
350 350 namefp = fopen(file, "rF");
351 351 if (namefp == NULL)
352 352 return (NULL);
353 353
354 354 /*
355 355 * parse the file; it must have the following format
356 356 * Lines starting with comments "#"
357 357 * Lines starting with non white space with one single token:
358 358 * the privileges; white space indented lines which are the
359 359 * description; no empty lines are allowed in the description.
360 360 */
361 361 while (fgets(buf, sizeof (buf), namefp) != NULL) {
362 362 char *lp; /* pointer to the current line */
363 363
364 364 if (buf[0] == '#')
365 365 continue;
366 366
367 367 if (buf[0] == '\n') {
368 368 inentry = B_FALSE;
369 369 continue;
370 370 }
371 371
372 372 if (inentry)
373 373 continue;
374 374
375 375 /* error; not skipping; yet line starts with white space */
376 376 if (isspace((unsigned char)buf[0]))
377 377 goto out;
378 378
379 379 /* Trim trailing newline */
380 380 buf[strlen(buf) - 1] = '\0';
381 381
382 382 if (strcasecmp(buf, priv) != 0) {
383 383 inentry = B_TRUE;
384 384 continue;
385 385 }
386 386
387 387 lp = buf;
388 388 while (fgets(lp, sizeof (buf) - (lp - buf), namefp) != NULL) {
389 389 char *tstart; /* start of text */
390 390 int len;
391 391
392 392 /* Empty line or start of next entry terminates */
393 393 if (*lp == '\n' || !isspace((unsigned char)*lp)) {
394 394 *lp = '\0';
395 395 (void) fclose(namefp);
396 396 return (strdup(buf));
397 397 }
398 398
399 399 /* Remove leading white space */
400 400 tstart = lp;
401 401 while (*tstart != '\0' &&
402 402 isspace((unsigned char)*tstart)) {
403 403 tstart++;
404 404 }
405 405
406 406 len = strlen(tstart);
407 407 (void) memmove(lp, tstart, len + 1);
408 408 lp += len;
409 409
410 410 /* Entry to big; prevent fgets() loop */
411 411 if (lp == &buf[sizeof (buf) - 1])
412 412 goto out;
413 413 }
414 414 if (lp != buf) {
415 415 *lp = '\0';
416 416 (void) fclose(namefp);
417 417 return (strdup(buf));
418 418 }
419 419 }
420 420 out:
421 421 (void) fclose(namefp);
422 422 return (NULL);
423 423 }
424 424
425 425 /*
426 426 * priv_gettext() is defined to return a string that
427 427 * the caller must deallocate with free(3C). Grr...
428 428 */
429 429 char *
430 430 priv_gettext(const char *priv)
431 431 {
432 432 char file[MAXPATHLEN];
433 433 locale_t curloc;
434 434 const char *loc;
435 435 char *ret;
436 436
437 437 /* Not a valid privilege */
438 438 if (priv_getbyname(priv) < 0)
439 439 return (NULL);
440 440
441 441 curloc = uselocale(NULL);
442 442 loc = current_locale(curloc, LC_MESSAGES);
443 443
444 444 if (snprintf(file, sizeof (file),
445 445 _DFLT_LOC_PATH "%s/LC_MESSAGES/priv_names", loc) < sizeof (file)) {
446 446 ret = do_priv_gettext(priv, (const char *)file);
447 447 if (ret != NULL)
448 448 return (ret);
449 449 }
450 450
451 451 /* If the path is too long or can't be opened, punt to default */
452 452 ret = do_priv_gettext(priv, "/etc/security/priv_names");
453 453 return (ret);
454 454 }
↓ open down ↓ |
368 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX