/*
* 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 <alloca.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <pwd.h>
#include <nss_dbdefs.h>
#include <deflt.h>
#include <auth_attr.h>
#include <prof_attr.h>
#include <user_attr.h>
#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) == NULL)
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) != NULL)
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);
}