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