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