2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "lint.h"
2N/A#include <alloca.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <sys/stat.h>
2N/A#include <pwd.h>
2N/A#include <nss_common.h>
2N/A#include <nss_dbdefs.h>
2N/A#include <deflt.h>
2N/A#include <auth_attr.h>
2N/A#include <prof_attr.h>
2N/A#include <user_attr.h>
2N/A
2N/A#define COPYTOSTACK(dst, csrc) { \
2N/A size_t len = strlen(csrc) + 1; \
2N/A dst = alloca(len); \
2N/A (void) memcpy(dst, csrc, len); \
2N/A }
2N/A
2N/Astatic kva_t *get_default_attrs(void);
2N/Astatic void free_default_attrs(kva_t *);
2N/Astatic int is_cons_user(uid_t);
2N/Astatic const char solaris[] = "solaris.*";
2N/A
2N/A/*
2N/A * Enumeration functions for auths and profiles; the enumeration functions
2N/A * take a callback with four arguments:
2N/A * const char * profile name (or NULL unless wantattr is false)
2N/A * kva_t * attributes (or NULL unless wantattr is true)
2N/A * void * context
2N/A * void * pointer to the result
2N/A * When the call back returns non-zero, the enumeration ends.
2N/A * The function might be NULL but only for profiles as we are always collecting
2N/A * all the profiles.
2N/A * Both the auths and the profiles arguments may be NULL.
2N/A *
2N/A * These should be the only implementation of the algorithm of "finding me
2N/A * all the profiles/authorizations/keywords/etc."
2N/A */
2N/A
2N/A#define CONSUSER_PROFILE_KW "consprofile"
2N/A#define DEF_LOCK_AFTER_RETRIES "LOCK_AFTER_RETRIES="
2N/A
2N/Astatic struct dfltplcy {
2N/A char *attr;
2N/A const char *defkw;
2N/A} dfltply[] = {
2N/A { CONSUSER_PROFILE_KW, DEF_CONSUSER},
2N/A { PROFATTR_AUTHS_KW, DEF_AUTH},
2N/A { PROFATTR_PROFS_KW, DEF_PROF},
2N/A { USERATTR_LIMPRIV_KW, DEF_LIMITPRIV},
2N/A { USERATTR_DFLTPRIV_KW, DEF_DFLTPRIV},
2N/A { USERATTR_LOCK_AFTER_RETRIES_KW, DEF_LOCK_AFTER_RETRIES}
2N/A};
2N/A
2N/A#define NDFLTPLY (sizeof (dfltply)/sizeof (struct dfltplcy))
2N/A#define GETCONSPROF(a) (kva_match((a), CONSUSER_PROFILE_KW))
2N/A#define GETPROF(a) (kva_match((a), PROFATTR_PROFS_KW))
2N/A
2N/A/*
2N/A * Enumerate profiles from listed profiles.
2N/A */
2N/Astatic int _auth_match_noun(const char *, const char *, size_t, const char *);
2N/A
2N/Aint
2N/A_enum_common_p(const char *cprofiles,
2N/A int (*cb)(const char *, kva_t *, void *, void *),
2N/A void *ctxt, void *pres, boolean_t wantattr,
2N/A int *pcnt, char *profs[MAXPROFS])
2N/A{
2N/A char *prof, *last;
2N/A char *profiles;
2N/A profattr_t *pa;
2N/A int i;
2N/A int res = 0;
2N/A
2N/A if (cprofiles == NULL)
2N/A return (0);
2N/A
2N/A if (*pcnt > 0 && strcmp(profs[*pcnt - 1], PROFILE_STOP) == NULL)
2N/A return (0);
2N/A
2N/A COPYTOSTACK(profiles, cprofiles)
2N/A
2N/A while (prof = strtok_r(profiles, KV_SEPSTR, &last)) {
2N/A
2N/A profiles = NULL; /* For next iterations of strtok_r */
2N/A
2N/A for (i = 0; i < *pcnt; i++)
2N/A if (strcmp(profs[i], prof) == 0)
2N/A goto cont;
2N/A
2N/A if (*pcnt >= MAXPROFS) /* oops: too many profs */
2N/A return (-1);
2N/A
2N/A /* Add it, fail if no memory. */
2N/A if ((profs[(*pcnt)++] = strdup(prof)) == NULL)
2N/A return (-1);
2N/A
2N/A if (strcmp(profs[*pcnt - 1], PROFILE_STOP) == 0)
2N/A break;
2N/A
2N/A /* find the profiles for this profile */
2N/A pa = getprofnam(prof);
2N/A
2N/A if (cb != NULL && (!wantattr || pa != NULL && pa->attr != NULL))
2N/A res = cb(prof, pa ? pa->attr : NULL, ctxt, pres);
2N/A
2N/A if (pa != NULL) {
2N/A if (res == 0 && pa->attr != NULL) {
2N/A res = _enum_common_p(GETPROF(pa->attr), cb,
2N/A ctxt, pres, wantattr, pcnt, profs);
2N/A }
2N/A free_profattr(pa);
2N/A }
2N/A if (res != 0)
2N/A return (res);
2N/Acont:
2N/A continue;
2N/A }
2N/A return (res);
2N/A}
2N/A
2N/A/*
2N/A * Enumerate all attributes associated with a username and the profiles
2N/A * associated with the user.
2N/A */
2N/Astatic int
2N/A_enum_common(const char *username,
2N/A int (*cb)(const char *, kva_t *, void *, void *),
2N/A void *ctxt, void *pres, boolean_t wantattr)
2N/A{
2N/A userattr_t *ua;
2N/A int res = 0;
2N/A int cnt = 0;
2N/A char *profs[MAXPROFS];
2N/A profattr_t *dp;
2N/A struct passwd pw;
2N/A char pwbuf[NSS_BUFLEN_PASSWD];
2N/A
2N/A if (cb == NULL)
2N/A return (-1);
2N/A
2N/A ua = getusernam(username);
2N/A
2N/A if (ua != NULL) {
2N/A if (ua->attr != NULL) {
2N/A if (wantattr)
2N/A res = cb(NULL, ua->attr, ctxt, pres);
2N/A if (res == 0) {
2N/A res = _enum_common_p(GETPROF(ua->attr),
2N/A cb, ctxt, pres, wantattr, &cnt, profs);
2N/A }
2N/A }
2N/A free_userattr(ua);
2N/A if (res != 0) {
2N/A free_proflist(profs, cnt);
2N/A return (res);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Find the default profiles if this is a valid user and we
2N/A * didn't encounter the Stop profile.
2N/A */
2N/A if (username != NULL &&
2N/A (cnt == 0 || strcmp(profs[cnt-1], PROFILE_STOP) != 0) &&
2N/A getpwnam_r(username, &pw, pwbuf, sizeof (pwbuf)) != NULL &&
2N/A (dp = getprofnam(AUTH_POLICY)) != NULL) {
2N/A
2N/A if (is_cons_user(pw.pw_uid)) {
2N/A res = _enum_common_p(GETCONSPROF(dp->attr), cb,
2N/A ctxt, pres, wantattr, &cnt, profs);
2N/A }
2N/A
2N/A if (res == 0) {
2N/A res = _enum_common_p(GETPROF(dp->attr), cb, ctxt,
2N/A pres, wantattr, &cnt, profs);
2N/A }
2N/A
2N/A if (res == 0 && wantattr)
2N/A res = cb(NULL, dp->attr, ctxt, pres);
2N/A
2N/A free_profattr(dp);
2N/A }
2N/A
2N/A free_proflist(profs, cnt);
2N/A
2N/A return (res);
2N/A}
2N/A
2N/A/*
2N/A * Enumerate profiles with a username argument.
2N/A */
2N/Aint
2N/A_enum_profs(const char *username,
2N/A int (*cb)(const char *, kva_t *, void *, void *),
2N/A void *ctxt, void *pres)
2N/A{
2N/A return (_enum_common(username, cb, ctxt, pres, B_FALSE));
2N/A}
2N/A
2N/A/*
2N/A * Enumerate attributes with a username argument.
2N/A */
2N/Aint
2N/A_enum_attrs(const char *username,
2N/A int (*cb)(const char *, kva_t *, void *, void *),
2N/A void *ctxt, void *pres)
2N/A{
2N/A return (_enum_common(username, cb, ctxt, pres, B_TRUE));
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Magic struct and function to allow using the _enum_attrs functions to
2N/A * enumerate the authorizations. In order to make the system survive
2N/A * bad configuration, we make sure that root always has the "solaris.*"
2N/A * authorization. This is implemented by first marking "wantdef" to
2N/A * true when the user is root; while we enumerating the auths, we compare
2N/A * and set wantdef to false if "solaris.*" is encountered. If wantdef
2N/A * remains true and callback hasn't short circuited, call the callback with
2N/A * "solaris.*".
2N/A */
2N/Atypedef struct ccomm2auth {
2N/A int (*cb)(const char *, void *, void *);
2N/A void *ctxt;
2N/A boolean_t wantdef;
2N/A} ccomm2auth;
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Acomm2auth(const char *name, kva_t *attr, void *ctxt, void *pres)
2N/A{
2N/A ccomm2auth *ca = ctxt;
2N/A char *cauths;
2N/A char *auth, *last, *auths;
2N/A int res = 0;
2N/A
2N/A /* Note: PROFATTR_AUTHS_KW is equal to USERATTR_AUTHS_KW */
2N/A cauths = kva_match(attr, PROFATTR_AUTHS_KW);
2N/A
2N/A if (cauths == NULL)
2N/A return (0);
2N/A
2N/A COPYTOSTACK(auths, cauths)
2N/A
2N/A while (auth = strtok_r(auths, KV_SEPSTR, &last)) {
2N/A auths = NULL; /* For next iterations of strtok_r */
2N/A
2N/A res = ca->cb(auth, ca->ctxt, pres);
2N/A
2N/A if (res != 0)
2N/A return (res);
2N/A
2N/A if (ca->wantdef && strcmp(auth, solaris) == 0)
2N/A ca->wantdef = B_FALSE;
2N/A }
2N/A return (res);
2N/A}
2N/A
2N/A/*
2N/A * Enumerate authorizations for username.
2N/A */
2N/Aint
2N/A_enum_auths(const char *username,
2N/A int (*cb)(const char *, void *, void *),
2N/A void *ctxt, void *pres)
2N/A{
2N/A ccomm2auth c2a;
2N/A int res;
2N/A
2N/A if (cb == NULL)
2N/A return (-1);
2N/A
2N/A c2a.cb = cb;
2N/A c2a.ctxt = ctxt;
2N/A c2a.wantdef = strcmp(username, "root") == 0;
2N/A
2N/A res = _enum_common(username, comm2auth, &c2a, pres, B_TRUE);
2N/A
2N/A if (res == 0 && c2a.wantdef)
2N/A res = cb(solaris, ctxt, pres);
2N/A return (res);
2N/A}
2N/A
2N/Aint
2N/A_auth_match_noun(const char *pattern, const char *auth,
2N/A size_t auth_len, const char *auth_noun)
2N/A{
2N/A size_t pattern_len;
2N/A char *pattern_noun;
2N/A char *slash;
2N/A
2N/A pattern_len = strlen(pattern);
2N/A /*
2N/A * If the specified authorization has a trailing object
2N/A * and the current authorization we're checking also has
2N/A * a trailing object, the object names must match.
2N/A *
2N/A * If there is no object name failure, then we must
2N/A * check for an exact match of the two authorizations
2N/A */
2N/A if (auth_noun != NULL) {
2N/A if ((slash = strchr(pattern, KV_OBJECTCHAR)) != NULL) {
2N/A pattern_noun = slash + 1;
2N/A pattern_len -= strlen(slash);
2N/A if (strcmp(pattern_noun, auth_noun) != 0)
2N/A return (0);
2N/A } else if ((auth_len == pattern_len) &&
2N/A (strncmp(pattern, auth, pattern_len) == 0)) {
2N/A return (1);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If the wildcard is not in the last position in the string, don't
2N/A * match against it.
2N/A */
2N/A if (pattern[pattern_len-1] != KV_WILDCHAR)
2N/A return (0);
2N/A
2N/A /*
2N/A * If the strings are identical up to the wildcard
2N/A * then we have a match.
2N/A * For more info see LSARC 2008/332.
2N/A */
2N/A if (strncmp(pattern, auth, pattern_len - 1) == 0)
2N/A return (1);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/A_auth_match(const char *pattern, const char *auth)
2N/A{
2N/A return (_auth_match_noun(pattern, auth, strlen(auth), NULL));
2N/A}
2N/A
2N/Astatic int
2N/A_is_authorized(const char *auth, void *authname, void *res)
2N/A{
2N/A int *resp = res;
2N/A char *authname_noun;
2N/A char *slash;
2N/A size_t auth_len;
2N/A size_t noun_len;
2N/A
2N/A auth_len = strlen(authname);
2N/A if ((slash = strchr(authname, KV_OBJECTCHAR)) != NULL) {
2N/A authname_noun = slash + 1;
2N/A noun_len = strlen(slash);
2N/A auth_len -= noun_len;
2N/A } else {
2N/A authname_noun = NULL;
2N/A }
2N/A
2N/A if (strcmp(authname, auth) == 0) {
2N/A /* exact match, we're done */
2N/A *resp = 1;
2N/A return (1);
2N/A } else if (noun_len || strchr(auth, KV_WILDCHAR) != NULL) {
2N/A if (_auth_match_noun(auth, authname,
2N/A auth_len, authname_noun)) {
2N/A *resp = 1;
2N/A return (1);
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Achkauthattr(const char *authname, const char *username)
2N/A{
2N/A int auth_granted = 0;
2N/A
2N/A if (authname == NULL || username == NULL)
2N/A return (0);
2N/A
2N/A (void) _enum_auths(username, _is_authorized, (char *)authname,
2N/A &auth_granted);
2N/A
2N/A return (auth_granted);
2N/A}
2N/A
2N/A#define CONSOLE_USER_LINK "/dev/vt/console_user"
2N/A
2N/Astatic int
2N/Ais_cons_user(uid_t uid)
2N/A{
2N/A struct stat cons;
2N/A
2N/A if (stat(CONSOLE_USER_LINK, &cons) == -1)
2N/A return (0);
2N/A
2N/A return (uid == cons.st_uid);
2N/A}
2N/A
2N/Astatic void
2N/Afree_default_attrs(kva_t *kva)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < kva->length; i++)
2N/A free(kva->data[i].value);
2N/A
2N/A free(kva);
2N/A}
2N/A
2N/A/*
2N/A * Return the default attributes; these are ignored when a STOP profile
2N/A * was found.
2N/A */
2N/Astatic kva_t *
2N/Aget_default_attrs(void)
2N/A{
2N/A void *defp;
2N/A kva_t *kva;
2N/A int i;
2N/A
2N/A kva = malloc(sizeof (kva_t) + sizeof (kv_t) * NDFLTPLY);
2N/A
2N/A if (kva == NULL)
2N/A return (NULL);
2N/A
2N/A kva->data = (kv_t *)(void *)&kva[1];
2N/A kva->length = 0;
2N/A
2N/A if ((defp = defopen_r(AUTH_POLICY)) == NULL)
2N/A goto return_null;
2N/A
2N/A for (i = 0; i < NDFLTPLY; i++) {
2N/A char *cp = defread_r(dfltply[i].defkw, defp);
2N/A
2N/A if (cp == NULL)
2N/A continue;
2N/A if ((cp = strdup(cp)) == NULL)
2N/A goto return_null;
2N/A
2N/A kva->data[kva->length].key = dfltply[i].attr;
2N/A kva->data[kva->length++].value = cp;
2N/A }
2N/A
2N/A (void) defclose_r(defp);
2N/A return (kva);
2N/A
2N/Areturn_null:
2N/A if (defp != NULL)
2N/A (void) defclose_r(defp);
2N/A
2N/A free_default_attrs(kva);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Map the contents of AUTH_POLICY to a profile called
2N/A * AUTH_POLICY. Even when we're not nscd we just cache
2N/A * the data: it's not that much and usually this code
2N/A * is only run inside nscd.
2N/A */
2N/Anss_status_t
2N/A__get_default_prof(const char *prof, nss_XbyY_args_t *args)
2N/A{
2N/A static struct stat lasttime;
2N/A static char *kvstr;
2N/A static mutex_t defprof = DEFAULTMUTEX;
2N/A
2N/A struct stat stbuf;
2N/A kva_t *kva;
2N/A char buf[NSS_BUFLEN_PROFATTR];
2N/A size_t len;
2N/A nss_status_t res;
2N/A
2N/A if (strcmp(prof, AUTH_POLICY) != 0)
2N/A return (NSS_NOTFOUND);
2N/A
2N/A if (stat(AUTH_POLICY, &stbuf) == -1)
2N/A return (NSS_NOTFOUND);
2N/A
2N/A (void) mutex_lock(&defprof);
2N/A
2N/A if (kvstr == NULL || stbuf.st_mtime != lasttime.st_mtime ||
2N/A stbuf.st_size != lasttime.st_size) {
2N/A int kres;
2N/A
2N/A kva = get_default_attrs();
2N/A if (kva == NULL) {
2N/A (void) mutex_unlock(&defprof);
2N/A return (NSS_NOTFOUND);
2N/A }
2N/A
2N/A len = snprintf(buf, sizeof (buf), "%s::::", prof);
2N/A kres = _kva2strx(kva, buf + len, sizeof (buf) - len,
2N/A KV_ASSIGN, KV_DELIMITER, KV_TOKEN_DELIMIT);
2N/A
2N/A free_default_attrs(kva);
2N/A
2N/A if (kres != 0) {
2N/A (void) mutex_unlock(&defprof);
2N/A return (NSS_NOTFOUND);
2N/A }
2N/A
2N/A if (kvstr != NULL)
2N/A free(kvstr);
2N/A
2N/A kvstr = strdup(buf);
2N/A if (kvstr == NULL) {
2N/A (void) mutex_unlock(&defprof);
2N/A return (NSS_NOTFOUND);
2N/A }
2N/A lasttime = stbuf;
2N/A }
2N/A
2N/A len = strlen(kvstr);
2N/A res = args->str2ent(kvstr, len, args->buf.result,
2N/A args->buf.buffer, args->buf.buflen);
2N/A
2N/A (void) mutex_unlock(&defprof);
2N/A
2N/A if (res != NSS_STR_PARSE_SUCCESS)
2N/A return (NSS_NOTFOUND);
2N/A
2N/A args->returnval = (args->buf.result != NULL) ?
2N/A args->buf.result : args->buf.buffer;
2N/A args->returnlen = len;
2N/A
2N/A return (NSS_SUCCESS);
2N/A}