priv_str_xlate.c revision 2550:8d932f4716ae
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 52priv_set_t * 53priv_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 */ 74priv_set_t * 75priv_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 171char * 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 */ 338char * 339priv_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 344static char * 345do_priv_gettext(const char *priv, const char *file) 346{ 347 char buf[8*1024]; 348 boolean_t inentry = B_FALSE; 349 FILE *namefp; 350 351 namefp = fopen(file, "rF"); 352 if (namefp == NULL) 353 return (NULL); 354 355 /* 356 * parse the file; it must have the following format 357 * Lines starting with comments "#" 358 * Lines starting with non white space with one single token: 359 * the privileges; white space indented lines which are the 360 * description; no empty lines are allowed in the description. 361 */ 362 while (fgets(buf, sizeof (buf), namefp) != NULL) { 363 char *lp; /* pointer to the current line */ 364 365 if (buf[0] == '#') 366 continue; 367 368 if (buf[0] == '\n') { 369 inentry = B_FALSE; 370 continue; 371 } 372 373 if (inentry) 374 continue; 375 376 /* error; not skipping; yet line starts with white space */ 377 if (isspace((unsigned char)buf[0])) 378 goto out; 379 380 /* Trim trailing newline */ 381 buf[strlen(buf) - 1] = '\0'; 382 383 if (strcasecmp(buf, priv) != 0) { 384 inentry = B_TRUE; 385 continue; 386 } 387 388 lp = buf; 389 while (fgets(lp, sizeof (buf) - (lp - buf), namefp) != NULL) { 390 char *tstart; /* start of text */ 391 int len; 392 393 /* Empty line or start of next entry terminates */ 394 if (*lp == '\n' || !isspace((unsigned char)*lp)) { 395 *lp = '\0'; 396 (void) fclose(namefp); 397 return (strdup(buf)); 398 } 399 400 /* Remove leading white space */ 401 tstart = lp; 402 while (*tstart != '\0' && 403 isspace((unsigned char)*tstart)) { 404 tstart++; 405 } 406 407 len = strlen(tstart); 408 (void) memmove(lp, tstart, len + 1); 409 lp += len; 410 411 /* Entry to big; prevent fgets() loop */ 412 if (lp == &buf[sizeof (buf) - 1]) 413 goto out; 414 } 415 if (lp != buf) { 416 *lp = '\0'; 417 (void) fclose(namefp); 418 return (strdup(buf)); 419 } 420 } 421out: 422 (void) fclose(namefp); 423 return (NULL); 424} 425 426/* 427 * priv_gettext() is defined to return a string that 428 * the caller must deallocate with free(3C). Grr... 429 */ 430char * 431priv_gettext(const char *priv) 432{ 433 char file[MAXPATHLEN]; 434 const char *loc; 435 char *ret; 436 437 /* Not a valid privilege */ 438 if (priv_getbyname(priv) < 0) 439 return (NULL); 440 441 if ((loc = setlocale(LC_MESSAGES, NULL)) == NULL) 442 loc = "C"; 443 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); 449 } 450 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); 454} 455