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 * 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 * 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 * 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 * 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 * 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 * 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