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