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 /* 28 * priv_str_xlate.c - Privilege translation routines. 29 */ 30 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 34 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" 50 51 priv_set_t * 52 priv_basic(void) 53 { 54 priv_data_t *d; 55 56 LOADPRIVDATA(d); 57 58 return (d->pd_basicset); 59 } 60 61 /* 62 * Name: priv_str_to_set() 63 * 64 * Description: Given a buffer with privilege strings, the 65 * equivalent privilege set is returned. 66 * 67 * Special tokens recognized: all, none, basic and "". 68 * 69 * On failure, this function returns NULL. 70 * *endptr == NULL and errno set: resource error. 71 * *endptr != NULL: parse error. 72 */ 73 priv_set_t * 74 priv_str_to_set(const char *priv_names, 75 const char *separators, 76 const char **endptr) 77 { 78 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; 85 86 if (endptr != NULL) 87 *endptr = NULL; 88 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); 94 } 95 96 priv_emptyset(pset); 97 basic = priv_basic(); 98 zone = privdata->pd_zoneset; 99 100 /* This is how to use strtok_r nicely in a while loop ... */ 101 last = base; 102 103 while ((offset = strtok_r(NULL, separators, &last)) != NULL) { 104 /* 105 * Search for these special case strings. 106 */ 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; 119 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); 135 } 136 } 137 } 138 139 libc_free(base); 140 return (pset); 141 } 142 143 /* 144 * Name: priv_set_to_str() 145 * 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. 149 * 150 * String "none" identifies an empty privilege set, and string "all" 151 * identifies a full set. 152 * 153 * A pointer to a buffer is returned which needs to be freed by 154 * the caller. 155 * 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 160 * 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. 168 */ 169 170 char * 171 __priv_set_to_str( 172 priv_data_t *d, 173 const priv_set_t *pset, 174 char separator, 175 int flag) 176 { 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); 184 185 if (use_libc_data) 186 LOADPRIVDATA(d); 187 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")); 192 193 /* Safe upper bound: global info contains all NULL separated privs */ 194 res = resp = alloca(d->pd_pinfo->priv_globalinfosize); 195 196 /* 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; 208 * 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. 213 */ 214 215 if (use_libc_data) 216 LOCKPRIVDATA(); 217 218 if (flag == PRIV_STR_SHORT) { 219 int presentbasic, missingbasic, present, missing; 220 int presentzone, missingzone; 221 int count; 222 223 presentbasic = missingbasic = present = 0; 224 presentzone = missingzone = 0; 225 zone = d->pd_zoneset; 226 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++; 235 } 236 if (zone != NULL && PRIV_ISMEMBER(zone, i)) { 237 if (mem) 238 presentzone++; 239 else 240 missingzone++; 241 } 242 if (mem) 243 present++; 244 } 245 missing = d->pd_nprivs - present; 246 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; 253 } 254 if (count >= 1 + missing) { 255 flag = PRIV_STR_SHORT; 256 count = 1 + missing; 257 all = B_TRUE; 258 } 259 if (present == presentzone && 1 + missingzone < count) { 260 flag = PRIV_STR_SHORT; 261 all = B_FALSE; 262 } 263 } 264 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); 284 } 285 res += strlen(res); 286 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]; 290 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; 303 } 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; 318 } 319 if (res != resp) 320 *res++ = separator; 321 *res++ = neg; 322 } 323 pstr = __priv_getbynum(d, priv); 324 (void) strcpy(res, pstr); 325 res += strlen(pstr); 326 } 327 if (use_libc_data) 328 UNLOCKPRIVDATA(); 329 /* Special case the set with some high bits set */ 330 return (strdup(*resp == '\0' ? "none" : resp)); 331 } 332 333 /* 334 * priv_set_to_str() is defined to return a string that 335 * the caller must deallocate with free(3C). Grr... 336 */ 337 char * 338 priv_set_to_str(const priv_set_t *pset, char separator, int flag) 339 { 340 return (__priv_set_to_str(NULL, pset, separator, flag)); 341 } 342 343 static char * 344 do_priv_gettext(const char *priv, const char *file) 345 { 346 char buf[8*1024]; 347 boolean_t inentry = B_FALSE; 348 FILE *namefp; 349 350 namefp = fopen(file, "rF"); 351 if (namefp == NULL) 352 return (NULL); 353 354 /* 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. 360 */ 361 while (fgets(buf, sizeof (buf), namefp) != NULL) { 362 char *lp; /* pointer to the current line */ 363 364 if (buf[0] == '#') 365 continue; 366 367 if (buf[0] == '\n') { 368 inentry = B_FALSE; 369 continue; 370 } 371 372 if (inentry) 373 continue; 374 375 /* error; not skipping; yet line starts with white space */ 376 if (isspace((unsigned char)buf[0])) 377 goto out; 378 379 /* Trim trailing newline */ 380 buf[strlen(buf) - 1] = '\0'; 381 382 if (strcasecmp(buf, priv) != 0) { 383 inentry = B_TRUE; 384 continue; 385 } 386 387 lp = buf; 388 while (fgets(lp, sizeof (buf) - (lp - buf), namefp) != NULL) { 389 char *tstart; /* start of text */ 390 int len; 391 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)); 397 } 398 399 /* Remove leading white space */ 400 tstart = lp; 401 while (*tstart != '\0' && 402 isspace((unsigned char)*tstart)) { 403 tstart++; 404 } 405 406 len = strlen(tstart); 407 (void) memmove(lp, tstart, len + 1); 408 lp += len; 409 410 /* Entry to big; prevent fgets() loop */ 411 if (lp == &buf[sizeof (buf) - 1]) 412 goto out; 413 } 414 if (lp != buf) { 415 *lp = '\0'; 416 (void) fclose(namefp); 417 return (strdup(buf)); 418 } 419 } 420 out: 421 (void) fclose(namefp); 422 return (NULL); 423 } 424 425 /* 426 * priv_gettext() is defined to return a string that 427 * the caller must deallocate with free(3C). Grr... 428 */ 429 char * 430 priv_gettext(const char *priv) 431 { 432 char file[MAXPATHLEN]; 433 locale_t curloc; 434 const char *loc; 435 char *ret; 436 437 /* Not a valid privilege */ 438 if (priv_getbyname(priv) < 0) 439 return (NULL); 440 441 curloc = uselocale(NULL); 442 loc = current_locale(curloc, LC_MESSAGES); 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