/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #define COPYTOSTACK(dst, csrc) { \ size_t len = strlen(csrc) + 1; \ dst = alloca(len); \ (void) memcpy(dst, csrc, len); \ } static kva_t *get_default_attrs(const char *); static void free_default_attrs(kva_t *); /* * Enumeration functions for auths and profiles; the enumeration functions * take a callback with four arguments: * const char * profile name (or NULL unless wantattr is false) * kva_t * attributes (or NULL unless wantattr is true) * void * context * void * pointer to the result * When the call back returns non-zero, the enumeration ends. * The function might be NULL but only for profiles as we are always collecting * all the profiles. * Both the auths and the profiles arguments may be NULL. * * These should be the only implementation of the algorithm of "finding me * all the profiles/athorizations/keywords/etc. */ #define CONSUSER_PROFILE_KW "consprofile" #define DEF_LOCK_AFTER_RETRIES "LOCK_AFTER_RETRIES=" static struct dfltplcy { char *attr; const char *defkw; } dfltply[] = { /* CONSUSER MUST BE FIRST! */ { CONSUSER_PROFILE_KW, DEF_CONSUSER}, { PROFATTR_AUTHS_KW, DEF_AUTH}, { PROFATTR_PROFS_KW, DEF_PROF}, { USERATTR_LIMPRIV_KW, DEF_LIMITPRIV}, { USERATTR_DFLTPRIV_KW, DEF_DFLTPRIV}, { USERATTR_LOCK_AFTER_RETRIES_KW, DEF_LOCK_AFTER_RETRIES} }; #define NDFLTPLY (sizeof (dfltply)/sizeof (struct dfltplcy)) #define GETCONSPROF(a) (kva_match((a), CONSUSER_PROFILE_KW)) #define GETPROF(a) (kva_match((a), PROFATTR_PROFS_KW)) /* * Enumerate profiles from listed profiles. */ static int _auth_match_noun(const char *, const char *, size_t, const char *); int _enum_common_p(const char *cprofiles, int (*cb)(const char *, kva_t *, void *, void *), void *ctxt, void *pres, boolean_t wantattr, int *pcnt, char *profs[MAXPROFS]) { char *prof, *last; char *profiles; profattr_t *pa; int i; int res = 0; if (cprofiles == NULL) return (0); if (*pcnt > 0 && strcmp(profs[*pcnt - 1], PROFILE_STOP) == 0) return (0); COPYTOSTACK(profiles, cprofiles) while (prof = strtok_r(profiles, KV_SEPSTR, &last)) { profiles = NULL; /* For next iterations of strtok_r */ for (i = 0; i < *pcnt; i++) if (strcmp(profs[i], prof) == 0) goto cont; if (*pcnt >= MAXPROFS) /* oops: too many profs */ return (-1); /* Add it */ profs[(*pcnt)++] = strdup(prof); if (strcmp(profs[*pcnt - 1], PROFILE_STOP) == 0) break; /* find the profiles for this profile */ pa = getprofnam(prof); if (cb != NULL && (!wantattr || pa != NULL && pa->attr != NULL)) res = cb(prof, pa ? pa->attr : NULL, ctxt, pres); if (pa != NULL) { if (res == 0 && pa->attr != NULL) { res = _enum_common_p(GETPROF(pa->attr), cb, ctxt, pres, wantattr, pcnt, profs); } free_profattr(pa); } if (res != 0) return (res); cont: continue; } return (res); } /* * Enumerate all attributes associated with a username and the profiles * associated with the user. */ static int _enum_common(const char *username, int (*cb)(const char *, kva_t *, void *, void *), void *ctxt, void *pres, boolean_t wantattr) { userattr_t *ua; int res = 0; int cnt = 0; char *profs[MAXPROFS]; kva_t *kattrs; if (cb == NULL) return (-1); ua = getusernam(username); if (ua != NULL) { if (ua->attr != NULL) { if (wantattr) res = cb(NULL, ua->attr, ctxt, pres); if (res == 0) { res = _enum_common_p(GETPROF(ua->attr), cb, ctxt, pres, wantattr, &cnt, profs); } } free_userattr(ua); if (res != 0) { free_proflist(profs, cnt); return (res); } } if ((cnt == 0 || strcmp(profs[cnt-1], PROFILE_STOP) != 0) && (kattrs = get_default_attrs(username)) != NULL) { res = _enum_common_p(GETCONSPROF(kattrs), cb, ctxt, pres, wantattr, &cnt, profs); if (res == 0) { res = _enum_common_p(GETPROF(kattrs), cb, ctxt, pres, wantattr, &cnt, profs); } if (res == 0 && wantattr) res = cb(NULL, kattrs, ctxt, pres); free_default_attrs(kattrs); } free_proflist(profs, cnt); return (res); } /* * Enumerate profiles with a username argument. */ int _enum_profs(const char *username, int (*cb)(const char *, kva_t *, void *, void *), void *ctxt, void *pres) { return (_enum_common(username, cb, ctxt, pres, B_FALSE)); } /* * Enumerate attributes with a username argument. */ int _enum_attrs(const char *username, int (*cb)(const char *, kva_t *, void *, void *), void *ctxt, void *pres) { return (_enum_common(username, cb, ctxt, pres, B_TRUE)); } /* * Enumerate authorizations in the "auths" argument. */ static int _enum_auths_a(const char *cauths, int (*cb)(const char *, void *, void *), void *ctxt, void *pres) { char *auth, *last, *auths; int res = 0; if (cauths == NULL || cb == NULL) return (0); COPYTOSTACK(auths, cauths) while (auth = strtok_r(auths, KV_SEPSTR, &last)) { auths = NULL; /* For next iterations of strtok_r */ res = cb(auth, ctxt, pres); if (res != 0) return (res); } return (res); } /* * Magic struct and function to allow using the _enum_attrs functions to * enumerate the authorizations. */ typedef struct ccomm2auth { int (*cb)(const char *, void *, void *); void *ctxt; } ccomm2auth; /*ARGSUSED*/ static int comm2auth(const char *name, kva_t *attr, void *ctxt, void *pres) { ccomm2auth *ca = ctxt; char *auths; /* Note: PROFATTR_AUTHS_KW is equal to USERATTR_AUTHS_KW */ auths = kva_match(attr, PROFATTR_AUTHS_KW); return (_enum_auths_a(auths, ca->cb, ca->ctxt, pres)); } /* * Enumerate authorizations for username. */ int _enum_auths(const char *username, int (*cb)(const char *, void *, void *), void *ctxt, void *pres) { ccomm2auth c2a; if (cb == NULL) return (-1); c2a.cb = cb; c2a.ctxt = ctxt; return (_enum_common(username, comm2auth, &c2a, pres, B_TRUE)); } int _auth_match_noun(const char *pattern, const char *auth, size_t auth_len, const char *auth_noun) { size_t pattern_len; char *grant; char *pattern_noun; char *slash; pattern_len = strlen(pattern); /* * If the specified authorization has a trailing object * and the current authorization we're checking also has * a trailing object, the object names must match. * * If there is no object name failure, then we must * check for an exact match of the two authorizations */ if (auth_noun != NULL) { if ((slash = strchr(pattern, KV_OBJECTCHAR)) != NULL) { pattern_noun = slash + 1; pattern_len -= strlen(slash); if (strcmp(pattern_noun, auth_noun) != 0) return (0); } else if ((auth_len == pattern_len) && (strncmp(pattern, auth, pattern_len) == 0)) { return (1); } } /* * If the wildcard is not in the last position in the string, don't * match against it. */ if (pattern[pattern_len-1] != KV_WILDCHAR) return (0); /* * If the strings are identical up to the wildcard and auth does not * end in "grant", then we have a match. */ if (strncmp(pattern, auth, pattern_len - 1) == 0) { grant = strrchr(auth, '.'); if (grant != NULL) { if (strncmp(grant + 1, "grant", 5) != 0) return (1); } } return (0); } int _auth_match(const char *pattern, const char *auth) { return (_auth_match_noun(pattern, auth, strlen(auth), NULL)); } static int _is_authorized(const char *auth, void *authname, void *res) { int *resp = res; char *authname_noun; char *slash; size_t auth_len; size_t noun_len; auth_len = strlen(authname); if ((slash = strchr(authname, KV_OBJECTCHAR)) != NULL) { authname_noun = slash + 1; noun_len = strlen(slash); auth_len -= noun_len; } else { authname_noun = NULL; } if (strcmp(authname, auth) == 0) { /* exact match, we're done */ *resp = 1; return (1); } else if (noun_len || strchr(auth, KV_WILDCHAR) != NULL) { if (_auth_match_noun(auth, authname, auth_len, authname_noun)) { *resp = 1; return (1); } } return (0); } int chkauthattr(const char *authname, const char *username) { int auth_granted = 0; if (authname == NULL || username == NULL) return (0); (void) _enum_auths(username, _is_authorized, (char *)authname, &auth_granted); return (auth_granted); } #define CONSOLE_USER_LINK "/dev/vt/console_user" static int is_cons_user(const char *user) { struct stat cons; struct passwd pw; char pwbuf[NSS_BUFLEN_PASSWD]; if (user == NULL) { return (0); } if (stat(CONSOLE_USER_LINK, &cons) == -1) { return (0); } if (getpwnam_r(user, &pw, pwbuf, sizeof (pwbuf)) == NULL) { return (0); } return (pw.pw_uid == cons.st_uid); } static void free_default_attrs(kva_t *kva) { int i; for (i = 0; i < kva->length; i++) free(kva->data[i].value); free(kva); } /* * Return the default attributes; this are ignored when a STOP profile * was found. */ static kva_t * get_default_attrs(const char *user) { void *defp; kva_t *kva; int i; kva = malloc(sizeof (kva_t) + sizeof (kv_t) * NDFLTPLY); if (kva == NULL) return (NULL); kva->data = (kv_t *)(void *)&kva[1]; kva->length = 0; if ((defp = defopen_r(AUTH_POLICY)) == NULL) goto return_null; for (i = is_cons_user(user) ? 0 : 1; i < NDFLTPLY; i++) { char *cp = defread_r(dfltply[i].defkw, defp); if (cp == NULL) continue; if ((cp = strdup(cp)) == NULL) goto return_null; kva->data[kva->length].key = dfltply[i].attr; kva->data[kva->length++].value = cp; } (void) defclose_r(defp); return (kva); return_null: if (defp != NULL) (void) defclose_r(defp); free_default_attrs(kva); return (NULL); }