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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2026 Oxide Computer Company 24 */ 25 26 #include <alloca.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <sys/stat.h> 31 #include <pwd.h> 32 #include <nss_dbdefs.h> 33 #include <deflt.h> 34 #include <auth_attr.h> 35 #include <prof_attr.h> 36 #include <user_attr.h> 37 38 #define COPYTOSTACK(dst, csrc) { \ 39 size_t len = strlen(csrc) + 1; \ 40 dst = alloca(len); \ 41 (void) memcpy(dst, csrc, len); \ 42 } 43 44 static kva_t *get_default_attrs(const char *); 45 static void free_default_attrs(kva_t *); 46 47 /* 48 * Enumeration functions for auths and profiles; the enumeration functions 49 * take a callback with four arguments: 50 * const char * profile name (or NULL unless wantattr is false) 51 * kva_t * attributes (or NULL unless wantattr is true) 52 * void * context 53 * void * pointer to the result 54 * When the call back returns non-zero, the enumeration ends. 55 * The function might be NULL but only for profiles as we are always collecting 56 * all the profiles. 57 * Both the auths and the profiles arguments may be NULL. 58 * 59 * These should be the only implementation of the algorithm of "finding me 60 * all the profiles/athorizations/keywords/etc. 61 */ 62 63 #define CONSUSER_PROFILE_KW "consprofile" 64 #define DEF_LOCK_AFTER_RETRIES "LOCK_AFTER_RETRIES=" 65 66 static struct dfltplcy { 67 char *attr; 68 const char *defkw; 69 } dfltply[] = { 70 /* CONSUSER MUST BE FIRST! */ 71 { CONSUSER_PROFILE_KW, DEF_CONSUSER}, 72 { PROFATTR_AUTHS_KW, DEF_AUTH}, 73 { PROFATTR_PROFS_KW, DEF_PROF}, 74 { USERATTR_LIMPRIV_KW, DEF_LIMITPRIV}, 75 { USERATTR_DFLTPRIV_KW, DEF_DFLTPRIV}, 76 { USERATTR_LOCK_AFTER_RETRIES_KW, DEF_LOCK_AFTER_RETRIES} 77 }; 78 79 #define NDFLTPLY (sizeof (dfltply)/sizeof (struct dfltplcy)) 80 #define GETCONSPROF(a) (kva_match((a), CONSUSER_PROFILE_KW)) 81 #define GETPROF(a) (kva_match((a), PROFATTR_PROFS_KW)) 82 83 /* 84 * Enumerate profiles from listed profiles. 85 */ 86 static int _auth_match_noun(const char *, const char *, size_t, const char *); 87 88 int 89 _enum_common_p(const char *cprofiles, 90 int (*cb)(const char *, kva_t *, void *, void *), 91 void *ctxt, void *pres, boolean_t wantattr, 92 int *pcnt, char *profs[MAXPROFS]) 93 { 94 char *prof, *last; 95 char *profiles; 96 profattr_t *pa; 97 int i; 98 int res = 0; 99 100 if (cprofiles == NULL) 101 return (0); 102 103 if (*pcnt > 0 && strcmp(profs[*pcnt - 1], PROFILE_STOP) == 0) 104 return (0); 105 106 COPYTOSTACK(profiles, cprofiles) 107 108 while (prof = strtok_r(profiles, KV_SEPSTR, &last)) { 109 110 profiles = NULL; /* For next iterations of strtok_r */ 111 112 for (i = 0; i < *pcnt; i++) 113 if (strcmp(profs[i], prof) == 0) 114 goto cont; 115 116 if (*pcnt >= MAXPROFS) /* oops: too many profs */ 117 return (-1); 118 119 /* Add it */ 120 if ((profs[*pcnt] = strdup(prof)) == NULL) 121 return (-1); 122 (*pcnt)++; 123 124 if (strcmp(profs[*pcnt - 1], PROFILE_STOP) == 0) 125 break; 126 127 /* find the profiles for this profile */ 128 pa = getprofnam(prof); 129 130 if (cb != NULL && (!wantattr || pa != NULL && pa->attr != NULL)) 131 res = cb(prof, pa ? pa->attr : NULL, ctxt, pres); 132 133 if (pa != NULL) { 134 if (res == 0 && pa->attr != NULL) { 135 res = _enum_common_p(GETPROF(pa->attr), cb, 136 ctxt, pres, wantattr, pcnt, profs); 137 } 138 free_profattr(pa); 139 } 140 if (res != 0) 141 return (res); 142 cont: 143 continue; 144 } 145 return (res); 146 } 147 148 /* 149 * Enumerate all attributes associated with a username and the profiles 150 * associated with the user. 151 */ 152 static int 153 _enum_common(const char *username, 154 int (*cb)(const char *, kva_t *, void *, void *), 155 void *ctxt, void *pres, boolean_t wantattr) 156 { 157 userattr_t *ua; 158 int res = 0; 159 int cnt = 0; 160 char *profs[MAXPROFS]; 161 kva_t *kattrs; 162 163 if (cb == NULL) 164 return (-1); 165 166 ua = getusernam(username); 167 168 if (ua != NULL) { 169 if (ua->attr != NULL) { 170 if (wantattr) 171 res = cb(NULL, ua->attr, ctxt, pres); 172 if (res == 0) { 173 res = _enum_common_p(GETPROF(ua->attr), 174 cb, ctxt, pres, wantattr, &cnt, profs); 175 } 176 } 177 free_userattr(ua); 178 if (res != 0) { 179 free_proflist(profs, cnt); 180 return (res); 181 } 182 } 183 184 if ((cnt == 0 || strcmp(profs[cnt-1], PROFILE_STOP) != 0) && 185 (kattrs = get_default_attrs(username)) != NULL) { 186 187 res = _enum_common_p(GETCONSPROF(kattrs), cb, ctxt, pres, 188 wantattr, &cnt, profs); 189 190 if (res == 0) { 191 res = _enum_common_p(GETPROF(kattrs), cb, ctxt, pres, 192 wantattr, &cnt, profs); 193 } 194 195 if (res == 0 && wantattr) 196 res = cb(NULL, kattrs, ctxt, pres); 197 198 free_default_attrs(kattrs); 199 } 200 201 free_proflist(profs, cnt); 202 203 return (res); 204 } 205 206 /* 207 * Enumerate profiles with a username argument. 208 */ 209 int 210 _enum_profs(const char *username, 211 int (*cb)(const char *, kva_t *, void *, void *), 212 void *ctxt, void *pres) 213 { 214 return (_enum_common(username, cb, ctxt, pres, B_FALSE)); 215 } 216 217 /* 218 * Enumerate attributes with a username argument. 219 */ 220 int 221 _enum_attrs(const char *username, 222 int (*cb)(const char *, kva_t *, void *, void *), 223 void *ctxt, void *pres) 224 { 225 return (_enum_common(username, cb, ctxt, pres, B_TRUE)); 226 } 227 228 229 /* 230 * Enumerate authorizations in the "auths" argument. 231 */ 232 static int 233 _enum_auths_a(const char *cauths, int (*cb)(const char *, void *, void *), 234 void *ctxt, void *pres) 235 { 236 char *auth, *last, *auths; 237 int res = 0; 238 239 if (cauths == NULL || cb == NULL) 240 return (0); 241 242 COPYTOSTACK(auths, cauths) 243 244 while (auth = strtok_r(auths, KV_SEPSTR, &last)) { 245 auths = NULL; /* For next iterations of strtok_r */ 246 247 res = cb(auth, ctxt, pres); 248 249 if (res != 0) 250 return (res); 251 } 252 return (res); 253 } 254 255 /* 256 * Magic struct and function to allow using the _enum_attrs functions to 257 * enumerate the authorizations. 258 */ 259 typedef struct ccomm2auth { 260 int (*cb)(const char *, void *, void *); 261 void *ctxt; 262 } ccomm2auth; 263 264 /*ARGSUSED*/ 265 static int 266 comm2auth(const char *name, kva_t *attr, void *ctxt, void *pres) 267 { 268 ccomm2auth *ca = ctxt; 269 char *auths; 270 271 /* Note: PROFATTR_AUTHS_KW is equal to USERATTR_AUTHS_KW */ 272 auths = kva_match(attr, PROFATTR_AUTHS_KW); 273 return (_enum_auths_a(auths, ca->cb, ca->ctxt, pres)); 274 } 275 276 /* 277 * Enumerate authorizations for username. 278 */ 279 int 280 _enum_auths(const char *username, 281 int (*cb)(const char *, void *, void *), 282 void *ctxt, void *pres) 283 { 284 ccomm2auth c2a; 285 286 if (cb == NULL) 287 return (-1); 288 289 c2a.cb = cb; 290 c2a.ctxt = ctxt; 291 292 return (_enum_common(username, comm2auth, &c2a, pres, B_TRUE)); 293 } 294 295 int 296 _auth_match_noun(const char *pattern, const char *auth, 297 size_t auth_len, const char *auth_noun) 298 { 299 size_t pattern_len; 300 char *grant; 301 char *pattern_noun; 302 char *slash; 303 304 pattern_len = strlen(pattern); 305 /* 306 * If the specified authorization has a trailing object 307 * and the current authorization we're checking also has 308 * a trailing object, the object names must match. 309 * 310 * If there is no object name failure, then we must 311 * check for an exact match of the two authorizations 312 */ 313 if (auth_noun != NULL) { 314 if ((slash = strchr(pattern, KV_OBJECTCHAR)) != NULL) { 315 pattern_noun = slash + 1; 316 pattern_len -= strlen(slash); 317 if (strcmp(pattern_noun, auth_noun) != 0) 318 return (0); 319 } else if ((auth_len == pattern_len) && 320 (strncmp(pattern, auth, pattern_len) == 0)) { 321 return (1); 322 } 323 } 324 325 /* 326 * If the wildcard is not in the last position in the string, don't 327 * match against it. 328 */ 329 if (pattern[pattern_len-1] != KV_WILDCHAR) 330 return (0); 331 332 /* 333 * If the strings are identical up to the wildcard and auth does not 334 * end in "grant", then we have a match. 335 */ 336 if (strncmp(pattern, auth, pattern_len - 1) == 0) { 337 grant = strrchr(auth, '.'); 338 if (grant != NULL) { 339 if (strncmp(grant + 1, "grant", 5) != 0) 340 return (1); 341 } 342 } 343 return (0); 344 } 345 346 int 347 _auth_match(const char *pattern, const char *auth) 348 { 349 return (_auth_match_noun(pattern, auth, strlen(auth), NULL)); 350 } 351 352 static int 353 _is_authorized(const char *auth, void *authname, void *res) 354 { 355 int *resp = res; 356 char *authname_noun; 357 char *slash; 358 size_t auth_len; 359 size_t noun_len; 360 361 auth_len = strlen(authname); 362 if ((slash = strchr(authname, KV_OBJECTCHAR)) != NULL) { 363 authname_noun = slash + 1; 364 noun_len = strlen(slash); 365 auth_len -= noun_len; 366 } else { 367 authname_noun = NULL; 368 } 369 370 if (strcmp(authname, auth) == 0) { 371 /* exact match, we're done */ 372 *resp = 1; 373 return (1); 374 } else if (noun_len || strchr(auth, KV_WILDCHAR) != NULL) { 375 if (_auth_match_noun(auth, authname, 376 auth_len, authname_noun)) { 377 *resp = 1; 378 return (1); 379 } 380 } 381 382 return (0); 383 } 384 385 int 386 chkauthattr(const char *authname, const char *username) 387 { 388 int auth_granted = 0; 389 390 if (authname == NULL || username == NULL) 391 return (0); 392 393 (void) _enum_auths(username, _is_authorized, (char *)authname, 394 &auth_granted); 395 396 return (auth_granted); 397 } 398 399 #define CONSOLE_USER_LINK "/dev/vt/console_user" 400 401 static int 402 is_cons_user(const char *user) 403 { 404 struct stat cons; 405 struct passwd pw; 406 char pwbuf[NSS_BUFLEN_PASSWD]; 407 408 if (user == NULL) { 409 return (0); 410 } 411 if (stat(CONSOLE_USER_LINK, &cons) == -1) { 412 return (0); 413 } 414 if (getpwnam_r(user, &pw, pwbuf, sizeof (pwbuf)) == NULL) { 415 return (0); 416 } 417 418 return (pw.pw_uid == cons.st_uid); 419 } 420 421 static void 422 free_default_attrs(kva_t *kva) 423 { 424 int i; 425 426 for (i = 0; i < kva->length; i++) 427 free(kva->data[i].value); 428 429 free(kva); 430 } 431 432 /* 433 * Return the default attributes; this are ignored when a STOP profile 434 * was found. 435 */ 436 static kva_t * 437 get_default_attrs(const char *user) 438 { 439 void *defp; 440 kva_t *kva; 441 int i; 442 443 kva = malloc(sizeof (kva_t) + sizeof (kv_t) * NDFLTPLY); 444 445 if (kva == NULL) 446 return (NULL); 447 448 kva->data = (kv_t *)(void *)&kva[1]; 449 kva->length = 0; 450 451 if ((defp = defopen_r(AUTH_POLICY)) == NULL) 452 goto return_null; 453 454 for (i = is_cons_user(user) ? 0 : 1; i < NDFLTPLY; i++) { 455 char *cp = defread_r(dfltply[i].defkw, defp); 456 457 if (cp == NULL) 458 continue; 459 if ((cp = strdup(cp)) == NULL) 460 goto return_null; 461 462 kva->data[kva->length].key = dfltply[i].attr; 463 kva->data[kva->length++].value = cp; 464 } 465 466 (void) defclose_r(defp); 467 return (kva); 468 469 return_null: 470 if (defp != NULL) 471 (void) defclose_r(defp); 472 473 free_default_attrs(kva); 474 return (NULL); 475 } 476