audit.c revision 56d7adc646bc459090594b76f6e6a050ca65216f
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, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * 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 2005 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A *
2N/A * Audit interfaces. Auditing can be enabled in two ways:
2N/A *
2N/A * o Using the LD_AUDIT environment variable
2N/A *
2N/A * o From individual objects containing a DT_DEPAUDIT entry
2N/A * (see ld(1) -P/-p options).
2N/A *
2N/A * The former establishes a global set of audit libraries which can inspect all
2N/A * objects from a given process. The latter establishes a local set of audit
2N/A * libraries which can inspect the immediate dependencies of the caller.
2N/A *
2N/A * Audit library capabilities are indicated by flags within the link-map list
2N/A * header (for global auditing), see LML_TFLG_AUD_* flags, or by the same flags
2N/A * within the individual link-map (for local auditing). Although both sets of
2N/A * flags can occur in different data items they are defined as one to simplify
2N/A * audit interface requirements. The basic test for all audit interfaces is:
2N/A *
2N/A * if (((lml->lm_tflags | FLAGS1(lmp)) & LML_TFLG_AUD_MASK) &&
2N/A * (lml == LIST(lmp)))
2N/A *
2N/A * The latter link-map list equivalence test insures that auditors themselves
2N/A * (invoked through DT_DEPAUDIT) are not audited.
2N/A */
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <stdio.h>
2N/A#include <sys/types.h>
2N/A#include <sys/lwp.h>
2N/A#include <stdio.h>
2N/A#include <stdarg.h>
2N/A#include <dlfcn.h>
2N/A#include <string.h>
2N/A#include "debug.h"
2N/A#include "_rtld.h"
2N/A#include "_audit.h"
2N/A#include "_elf.h"
2N/A#include "msg.h"
2N/A
2N/Auint_t audit_flags = 0; /* Copy of specific audit flags to */
2N/A /* simplify boot_elf.s access. */
2N/A
2N/Astatic Audit_client *
2N/A_audit_client(Audit_info * aip, Rt_map * almp)
2N/A{
2N/A int ndx;
2N/A
2N/A if (aip == 0)
2N/A return (0);
2N/A
2N/A for (ndx = 0; ndx < aip->ai_cnt; ndx++) {
2N/A if (aip->ai_clients[ndx].ac_lmp == almp)
2N/A return (&(aip->ai_clients[ndx]));
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * la_filter() caller. Traverses through all audit libraries and call any
2N/A * la_filter() entry points found. A zero return from an auditor indicates
2N/A * that the filtee should be ignored.
2N/A */
2N/Astatic int
2N/A_audit_objfilter(List *list, Rt_map *frlmp, const char *ref, Rt_map *felmp,
2N/A uint_t flags)
2N/A{
2N/A Audit_list *alp;
2N/A Listnode *lnp;
2N/A
2N/A for (LIST_TRAVERSE(list, lnp, alp)) {
2N/A Audit_client *fracp, *feacp;
2N/A
2N/A if (alp->al_objfilter == 0)
2N/A continue;
2N/A if ((fracp = _audit_client(AUDINFO(frlmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A if ((feacp = _audit_client(AUDINFO(felmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A
2N/A leave(LIST(alp->al_lmp));
2N/A if ((*alp->al_objfilter)(&(fracp->ac_cookie), ref,
2N/A &(feacp->ac_cookie), flags) == 0)
2N/A return (0);
2N/A (void) enter();
2N/A }
2N/A return (1);
2N/A}
2N/A
2N/Aint
2N/Aaudit_objfilter(Rt_map *frlmp, const char *ref, Rt_map *felmp, uint_t flags)
2N/A{
2N/A int appl = 0, respond = 1;
2N/A
2N/A if ((rtld_flags & RT_FL_APPLIC) == 0)
2N/A appl = rtld_flags |= RT_FL_APPLIC;
2N/A
2N/A if (auditors && (auditors->ad_flags & LML_TFLG_AUD_OBJFILTER))
2N/A respond = _audit_objfilter(&(auditors->ad_list), frlmp,
2N/A ref, felmp, flags);
2N/A if (respond && AUDITORS(frlmp) &&
2N/A (AUDITORS(frlmp)->ad_flags & LML_TFLG_AUD_OBJFILTER))
2N/A respond = _audit_objfilter(&(AUDITORS(frlmp)->ad_list), frlmp,
2N/A ref, felmp, flags);
2N/A
2N/A if (appl)
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A
2N/A return (respond);
2N/A}
2N/A
2N/A/*
2N/A * la_objsearch() caller. Traverses through all audit libraries and call any
2N/A * la_objsearch() entry points found.
2N/A *
2N/A * Effectively any audit library can change the name we're working with, so we
2N/A * continue to propagate the new name to each audit library. Any 0 return
2N/A * terminates the search.
2N/A */
2N/Astatic char *
2N/A_audit_objsearch(List *list, char *name, Rt_map *clmp, uint_t flags)
2N/A{
2N/A Audit_list *alp;
2N/A Listnode *lnp;
2N/A char *nname = (char *)name;
2N/A
2N/A for (LIST_TRAVERSE(list, lnp, alp)) {
2N/A Audit_client *acp;
2N/A
2N/A if (alp->al_objsearch == 0)
2N/A continue;
2N/A if ((acp = _audit_client(AUDINFO(clmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A
2N/A leave(LIST(alp->al_lmp));
2N/A nname = (*alp->al_objsearch)(nname, &(acp->ac_cookie), flags);
2N/A (void) enter();
2N/A if (nname == 0)
2N/A break;
2N/A }
2N/A return (nname);
2N/A}
2N/A
2N/Achar *
2N/Aaudit_objsearch(Rt_map *clmp, const char *name, uint_t flags)
2N/A{
2N/A char *nname = (char *)name;
2N/A int appl = 0;
2N/A
2N/A if ((rtld_flags & RT_FL_APPLIC) == 0)
2N/A appl = rtld_flags |= RT_FL_APPLIC;
2N/A
2N/A if (auditors && (auditors->ad_flags & LML_TFLG_AUD_OBJSEARCH))
2N/A nname = _audit_objsearch(&(auditors->ad_list), nname,
2N/A clmp, flags);
2N/A if (nname && AUDITORS(clmp) &&
2N/A (AUDITORS(clmp)->ad_flags & LML_TFLG_AUD_OBJSEARCH))
2N/A nname = _audit_objsearch(&(AUDITORS(clmp)->ad_list), nname,
2N/A clmp, flags);
2N/A
2N/A if (appl)
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A
2N/A DBG_CALL(Dbg_libs_audit(name, nname));
2N/A return (nname);
2N/A}
2N/A
2N/A/*
2N/A * la_activity() caller. Traverses through all audit library and calls any
2N/A * la_activity() entry points found.
2N/A */
2N/Astatic void
2N/A_audit_activity(List * list, Rt_map * clmp, uint_t flags)
2N/A{
2N/A Audit_list * alp;
2N/A Listnode * lnp;
2N/A
2N/A for (LIST_TRAVERSE(list, lnp, alp)) {
2N/A Audit_client * acp;
2N/A
2N/A if (alp->al_activity == 0)
2N/A continue;
2N/A if ((acp = _audit_client(AUDINFO(clmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A
2N/A leave(LIST(alp->al_lmp));
2N/A (*alp->al_activity)(&(acp->ac_cookie), flags);
2N/A (void) enter();
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Aaudit_activity(Rt_map * clmp, uint_t flags)
2N/A{
2N/A int appl = 0;
2N/A
2N/A /*
2N/A * We want to trigger the first addition or deletion only. Ignore any
2N/A * consistent calls unless a previous addition or deletion occurred.
2N/A */
2N/A if ((flags == LA_ACT_ADD) || (flags == LA_ACT_DELETE)) {
2N/A if (rtld_flags & RT_FL_AUNOTIF)
2N/A return;
2N/A rtld_flags |= RT_FL_AUNOTIF;
2N/A } else {
2N/A if ((rtld_flags & RT_FL_AUNOTIF) == 0)
2N/A return;
2N/A rtld_flags &= ~RT_FL_AUNOTIF;
2N/A }
2N/A
2N/A if ((rtld_flags & RT_FL_APPLIC) == 0)
2N/A appl = rtld_flags |= RT_FL_APPLIC;
2N/A
2N/A if (auditors && (auditors->ad_flags & LML_TFLG_AUD_ACTIVITY))
2N/A _audit_activity(&(auditors->ad_list), clmp, flags);
2N/A if (AUDITORS(clmp) &&
2N/A (AUDITORS(clmp)->ad_flags & LML_TFLG_AUD_ACTIVITY))
2N/A _audit_activity(&(AUDITORS(clmp)->ad_list), clmp, flags);
2N/A
2N/A if (appl)
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A}
2N/A
2N/A/*
2N/A * la_objopen() caller. Create an audit information structure for the indicated
2N/A * link-map, regardless of an la_objopen() entry point. This structure is used
2N/A * to supply information to various audit interfaces (see LML_MSK_AUDINFO).
2N/A * Traverses through all audit library and calls any la_objopen() entry points
2N/A * found.
2N/A */
2N/Astatic int
2N/A_audit_objopen(List *list, Rt_map *nlmp, Lmid_t lmid, Audit_info *aip,
2N/A int *ndx)
2N/A{
2N/A Audit_list *alp;
2N/A Listnode *lnp;
2N/A
2N/A for (LIST_TRAVERSE(list, lnp, alp)) {
2N/A uint_t flags;
2N/A Audit_client *acp;
2N/A
2N/A /*
2N/A * Associate a cookie with the audit library, and assign the
2N/A * initial cookie as the present link-map.
2N/A */
2N/A acp = &aip->ai_clients[(*ndx)++];
2N/A acp->ac_lmp = alp->al_lmp;
2N/A acp->ac_cookie = (uintptr_t)nlmp;
2N/A
2N/A if (alp->al_objopen == 0)
2N/A continue;
2N/A
2N/A DBG_CALL(Dbg_audit_object(alp->al_libname, NAME(nlmp)));
2N/A
2N/A leave(LIST(alp->al_lmp));
2N/A flags = (*alp->al_objopen)((Link_map *)nlmp, lmid,
2N/A &(acp->ac_cookie));
2N/A (void) enter();
2N/A
2N/A if (flags & LA_FLG_BINDTO)
2N/A acp->ac_flags |= FLG_AC_BINDTO;
2N/A
2N/A if (flags & LA_FLG_BINDFROM) {
2N/A ulong_t pltcnt;
2N/A
2N/A acp->ac_flags |= FLG_AC_BINDFROM;
2N/A /*
2N/A * We only need dynamic plt's if a pltenter and/or a
2N/A * pltexit() entry point exist in one of our auditing
2N/A * libraries.
2N/A */
2N/A if (aip->ai_dynplts || (JMPREL(nlmp) == 0) ||
2N/A ((audit_flags & (AF_PLTENTER | AF_PLTEXIT)) == 0))
2N/A continue;
2N/A
2N/A /*
2N/A * Create one dynplt for every 'PLT' that exists in the
2N/A * object.
2N/A */
2N/A pltcnt = PLTRELSZ(nlmp) / RELENT(nlmp);
2N/A if ((aip->ai_dynplts = calloc(pltcnt,
2N/A dyn_plt_ent_size)) == 0)
2N/A return (0);
2N/A }
2N/A }
2N/A return (1);
2N/A}
2N/A
2N/Aint
2N/Aaudit_objopen(Rt_map * clmp, Rt_map * nlmp)
2N/A{
2N/A Lmid_t lmid = get_linkmap_id(LIST(nlmp));
2N/A int appl = 0, respond = 1, ndx = 0;
2N/A uint_t clients = 0;
2N/A Audit_info * aip;
2N/A
2N/A /*
2N/A * Determine the total number of audit libraries in use. This provides
2N/A * the number of client structures required for this object.
2N/A */
2N/A if (auditors)
2N/A clients = auditors->ad_cnt;
2N/A if (AUDITORS(clmp))
2N/A clients += AUDITORS(clmp)->ad_cnt;
2N/A if ((nlmp != clmp) && AUDITORS(nlmp))
2N/A clients += AUDITORS(nlmp)->ad_cnt;
2N/A
2N/A /*
2N/A * The initial allocation of the audit information structure includes
2N/A * an array of audit clients, 1 per audit library presently available.
2N/A *
2N/A * ---------------
2N/A * | ai_cnt |
2N/A * Audit_info | ai_clients |-------
2N/A * | ai_dynplts | |
2N/A * |---------------| |
2N/A * Audit_client | 1 |<------
2N/A * |---------------|
2N/A * | 2 |
2N/A * .........
2N/A */
2N/A if ((AUDINFO(nlmp) = aip = calloc(1, sizeof (Audit_info) +
2N/A (sizeof (Audit_client) * clients))) == 0)
2N/A return (0);
2N/A
2N/A aip->ai_cnt = clients;
2N/A aip->ai_clients = (Audit_client *)((uintptr_t)aip +
2N/A sizeof (Audit_info));
2N/A
2N/A if ((rtld_flags & RT_FL_APPLIC) == 0)
2N/A appl = rtld_flags |= RT_FL_APPLIC;
2N/A
2N/A if (auditors)
2N/A respond = _audit_objopen(&(auditors->ad_list), nlmp,
2N/A lmid, aip, &ndx);
2N/A if (respond && AUDITORS(clmp))
2N/A respond = _audit_objopen(&(AUDITORS(clmp)->ad_list), nlmp,
2N/A lmid, aip, &ndx);
2N/A if (respond && (nlmp != clmp) && AUDITORS(nlmp))
2N/A respond = _audit_objopen(&(AUDITORS(nlmp)->ad_list), nlmp,
2N/A lmid, aip, &ndx);
2N/A
2N/A if (appl)
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A
2N/A return (respond);
2N/A}
2N/A
2N/A/*
2N/A * la_objclose() caller. Traverses through all audit library and calls any
2N/A * la_objclose() entry points found.
2N/A */
2N/Avoid
2N/A_audit_objclose(List * list, Rt_map * lmp)
2N/A{
2N/A Audit_list * alp;
2N/A Listnode * lnp;
2N/A
2N/A for (LIST_TRAVERSE(list, lnp, alp)) {
2N/A Audit_client * acp;
2N/A
2N/A if (alp->al_objclose == 0)
2N/A continue;
2N/A if ((acp = _audit_client(AUDINFO(lmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A
2N/A leave(LIST(alp->al_lmp));
2N/A (*alp->al_objclose)(&(acp->ac_cookie));
2N/A (void) enter();
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Aaudit_objclose(Rt_map * clmp, Rt_map * lmp)
2N/A{
2N/A int appl = 0;
2N/A
2N/A if ((rtld_flags & RT_FL_APPLIC) == 0)
2N/A appl = rtld_flags |= RT_FL_APPLIC;
2N/A
2N/A if (auditors && (auditors->ad_flags & LML_TFLG_AUD_OBJCLOSE))
2N/A _audit_objclose(&(auditors->ad_list), lmp);
2N/A if (AUDITORS(clmp) &&
2N/A (AUDITORS(clmp)->ad_flags & LML_TFLG_AUD_OBJCLOSE))
2N/A _audit_objclose(&(AUDITORS(clmp)->ad_list), lmp);
2N/A
2N/A if (appl)
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A}
2N/A
2N/A/*
2N/A * la_pltenter() caller. Traverses through all audit library and calls any
2N/A * la_pltenter() entry points found. NOTE: this routine is called via the
2N/A * glue code established in elf_plt_trace_write(), the symbol descriptor is
2N/A * created as part of the glue and for 32bit environments the st_name is a
2N/A * pointer to the real symbol name (ie. it's already been adjusted with the
2N/A * objects base offset). For 64bit environments the st_name remains the
2N/A * original symbol offset and in this case it is used to compute the real name
2N/A * pointer and pass as a separate argument to the auditor.
2N/A */
2N/Astatic void
2N/A_audit_pltenter(List *list, Rt_map *rlmp, Rt_map *dlmp, Sym *sym,
2N/A uint_t ndx, void *regs, uint_t *flags)
2N/A{
2N/A Audit_list *alp;
2N/A Listnode *lnp;
2N/A#if defined(_ELF64)
2N/A const char *name = (const char *)(sym->st_name + STRTAB(dlmp));
2N/A#else
2N/A const char *name = (const char *)(sym->st_name);
2N/A#endif
2N/A
2N/A for (LIST_TRAVERSE(list, lnp, alp)) {
2N/A Audit_client *racp, *dacp;
2N/A Addr prev = sym->st_value;
2N/A
2N/A if (alp->al_pltenter == 0)
2N/A continue;
2N/A if ((racp = _audit_client(AUDINFO(rlmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A if ((dacp = _audit_client(AUDINFO(dlmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A if (((racp->ac_flags & FLG_AC_BINDFROM) == 0) ||
2N/A ((dacp->ac_flags & FLG_AC_BINDTO) == 0))
2N/A continue;
2N/A
2N/A leave(LIST(alp->al_lmp));
2N/A sym->st_value = (Addr)(*alp->al_pltenter)(sym, ndx,
2N/A &(racp->ac_cookie), &(dacp->ac_cookie), regs,
2N/A#if defined(_ELF64)
2N/A flags, name);
2N/A#else
2N/A flags);
2N/A#endif
2N/A (void) enter();
2N/A
2N/A DBG_CALL(Dbg_audit_symval(alp->al_libname,
2N/A MSG_ORIG(MSG_AUD_PLTENTER), name, prev, sym->st_name));
2N/A }
2N/A}
2N/A
2N/AAddr
2N/Aaudit_pltenter(Rt_map *rlmp, Rt_map *dlmp, Sym *sym, uint_t ndx,
2N/A void *regs, uint_t *flags)
2N/A{
2N/A Sym _sym = *sym;
2N/A int _appl = 0;
2N/A
2N/A /*
2N/A * We're effectively entering ld.so.1 from user (glue) code.
2N/A */
2N/A (void) enter();
2N/A if ((rtld_flags & RT_FL_APPLIC) == 0)
2N/A _appl = rtld_flags |= RT_FL_APPLIC;
2N/A
2N/A if (auditors && (auditors->ad_flags & LML_TFLG_AUD_PLTENTER))
2N/A _audit_pltenter(&(auditors->ad_list), rlmp, dlmp, &_sym,
2N/A ndx, regs, flags);
2N/A if (AUDITORS(rlmp) &&
2N/A (AUDITORS(rlmp)->ad_flags & LML_TFLG_AUD_PLTENTER))
2N/A _audit_pltenter(&(AUDITORS(rlmp)->ad_list), rlmp, dlmp, &_sym,
2N/A ndx, regs, flags);
2N/A
2N/A if (_appl)
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A leave(LIST(rlmp));
2N/A
2N/A return (_sym.st_value);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * la_pltexit() caller. Traverses through all audit library and calls any
2N/A * la_pltexit() entry points found. See notes above (_audit_pltenter) for
2N/A * discussion on st_name.
2N/A */
2N/Astatic Addr
2N/A_audit_pltexit(List *list, uintptr_t retval, Rt_map *rlmp, Rt_map *dlmp,
2N/A Sym *sym, uint_t ndx)
2N/A{
2N/A Audit_list *alp;
2N/A Listnode *lnp;
2N/A#if defined(_ELF64)
2N/A const char *name = (const char *)(sym->st_name + STRTAB(dlmp));
2N/A#endif
2N/A
2N/A for (LIST_TRAVERSE(list, lnp, alp)) {
2N/A Audit_client *racp, *dacp;
2N/A
2N/A if (alp->al_pltexit == 0)
2N/A continue;
2N/A if ((racp = _audit_client(AUDINFO(rlmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A if ((dacp = _audit_client(AUDINFO(dlmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A if (((racp->ac_flags & FLG_AC_BINDFROM) == 0) ||
2N/A ((dacp->ac_flags & FLG_AC_BINDTO) == 0))
2N/A continue;
2N/A
2N/A leave(LIST(alp->al_lmp));
2N/A retval = (*alp->al_pltexit)(sym, ndx,
2N/A &(racp->ac_cookie), &(dacp->ac_cookie),
2N/A#if defined(_ELF64)
2N/A retval, name);
2N/A#else
2N/A retval);
2N/A#endif
2N/A (void) enter();
2N/A }
2N/A return (retval);
2N/A}
2N/A
2N/AAddr
2N/Aaudit_pltexit(uintptr_t retval, Rt_map *rlmp, Rt_map *dlmp, Sym *sym,
2N/A uint_t ndx)
2N/A{
2N/A uintptr_t _retval = retval;
2N/A int _appl = 0;
2N/A
2N/A /*
2N/A * We're effectively entering ld.so.1 from user (glue) code.
2N/A */
2N/A (void) enter();
2N/A if ((rtld_flags & RT_FL_APPLIC) == 0)
2N/A _appl = rtld_flags |= RT_FL_APPLIC;
2N/A
2N/A if (auditors && (auditors->ad_flags & LML_TFLG_AUD_PLTEXIT))
2N/A _retval = _audit_pltexit(&(auditors->ad_list), _retval,
2N/A rlmp, dlmp, sym, ndx);
2N/A if (AUDITORS(rlmp) && (AUDITORS(rlmp)->ad_flags & LML_TFLG_AUD_PLTEXIT))
2N/A _retval = _audit_pltexit(&(AUDITORS(rlmp)->ad_list), _retval,
2N/A rlmp, dlmp, sym, ndx);
2N/A
2N/A if (_appl)
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A leave(LIST(rlmp));
2N/A
2N/A return (_retval);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * la_symbind() caller. Traverses through all audit library and calls any
2N/A * la_symbind() entry points found.
2N/A */
2N/Astatic Addr
2N/A_audit_symbind(List *list, Rt_map *rlmp, Rt_map *dlmp, Sym *sym, uint_t ndx,
2N/A uint_t *flags, int *called)
2N/A{
2N/A Audit_list *alp;
2N/A Listnode *lnp;
2N/A#if defined(_ELF64)
2N/A const char *name = (const char *)(sym->st_name + STRTAB(dlmp));
2N/A#else
2N/A const char *name = (const char *)(sym->st_name);
2N/A#endif
2N/A
2N/A for (LIST_TRAVERSE(list, lnp, alp)) {
2N/A Audit_client *racp, *dacp;
2N/A Addr prev = sym->st_value;
2N/A uint_t lflags;
2N/A
2N/A if (alp->al_symbind == 0)
2N/A continue;
2N/A if ((racp = _audit_client(AUDINFO(rlmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A if ((dacp = _audit_client(AUDINFO(dlmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A if (((racp->ac_flags & FLG_AC_BINDFROM) == 0) ||
2N/A ((dacp->ac_flags & FLG_AC_BINDTO) == 0))
2N/A continue;
2N/A
2N/A /*
2N/A * The la_symbind interface is only called when the calling
2N/A * object has been identified as BINDFROM, and the destination
2N/A * object has been identified as BINDTO. Use a local version of
2N/A * the flags, so that any user update can be collected.
2N/A */
2N/A called++;
2N/A lflags = (*flags & ~(LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT));
2N/A
2N/A leave(LIST(alp->al_lmp));
2N/A sym->st_value = (*alp->al_symbind)(sym, ndx,
2N/A &(racp->ac_cookie), &(dacp->ac_cookie),
2N/A#if defined(_ELF64)
2N/A &lflags, name);
2N/A#else
2N/A &lflags);
2N/A#endif
2N/A (void) enter();
2N/A
2N/A /*
2N/A * If the auditor indicated that they did not want to process
2N/A * pltenter, or pltexit audits for this symbol, retain this
2N/A * information. Also retain whether an alternative symbol value
2N/A * has been supplied.
2N/A */
2N/A *flags |= (lflags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT));
2N/A if ((prev != sym->st_value) && (alp->al_vernum >= LAV_VERSION2))
2N/A *flags |= LA_SYMB_ALTVALUE;
2N/A
2N/A DBG_CALL(Dbg_audit_symval(alp->al_libname,
2N/A MSG_ORIG(MSG_AUD_SYMBIND), name, prev, sym->st_value));
2N/A }
2N/A return (sym->st_value);
2N/A}
2N/A
2N/AAddr
2N/Aaudit_symbind(Rt_map *rlmp, Rt_map *dlmp, Sym *sym, uint_t ndx, Addr value,
2N/A uint_t *flags)
2N/A{
2N/A Sym _sym;
2N/A int _appl = 0, called = 0;
2N/A
2N/A /*
2N/A * Construct a new symbol from that supplied but with the real address.
2N/A * In the 64-bit world the st_name field is only 32-bits which isn't
2N/A * big enough to hold a character pointer. We pass this pointer as a
2N/A * separate parameter for 64-bit audit libraries.
2N/A */
2N/A _sym = *sym;
2N/A _sym.st_value = value;
2N/A
2N/A#if !defined(_ELF64)
2N/A _sym.st_name += (Word)STRTAB(dlmp);
2N/A#endif
2N/A if ((rtld_flags & RT_FL_APPLIC) == 0)
2N/A _appl = rtld_flags |= RT_FL_APPLIC;
2N/A
2N/A if (auditors && (auditors->ad_flags & LML_TFLG_AUD_SYMBIND))
2N/A _sym.st_value = _audit_symbind(&(auditors->ad_list),
2N/A rlmp, dlmp, &_sym, ndx, flags, &called);
2N/A if (AUDITORS(rlmp) && (AUDITORS(rlmp)->ad_flags & LML_TFLG_AUD_SYMBIND))
2N/A _sym.st_value = _audit_symbind(&(AUDITORS(rlmp)->ad_list),
2N/A rlmp, dlmp, &_sym, ndx, flags, &called);
2N/A
2N/A /*
2N/A * If no la_symbind() was called for this interface, fabricate that no
2N/A * la_pltenter, or la_pltexit is required. This helps reduce the glue
2N/A * code created for further auditing.
2N/A */
2N/A if (caller == 0)
2N/A *flags |= (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
2N/A
2N/A if (_appl)
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A
2N/A return (_sym.st_value);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * la_preinit() caller. Traverses through all audit libraries and calls any
2N/A * la_preinit() entry points found.
2N/A */
2N/Astatic void
2N/A_audit_preinit(List *list, Rt_map *clmp)
2N/A{
2N/A Audit_list *alp;
2N/A Listnode *lnp;
2N/A
2N/A for (LIST_TRAVERSE(list, lnp, alp)) {
2N/A Audit_client *acp;
2N/A
2N/A if (alp->al_preinit == 0)
2N/A continue;
2N/A if ((acp = _audit_client(AUDINFO(clmp), alp->al_lmp)) == 0)
2N/A continue;
2N/A
2N/A leave(LIST(alp->al_lmp));
2N/A (*alp->al_preinit)(&(acp->ac_cookie));
2N/A (void) enter();
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Aaudit_preinit(Rt_map *clmp)
2N/A{
2N/A int appl = 0;
2N/A
2N/A if ((rtld_flags & RT_FL_APPLIC) == 0)
2N/A appl = rtld_flags |= RT_FL_APPLIC;
2N/A
2N/A if (auditors && (auditors->ad_flags & LML_TFLG_AUD_PREINIT))
2N/A _audit_preinit(&(auditors->ad_list), clmp);
2N/A if (AUDITORS(clmp) && (AUDITORS(clmp)->ad_flags & LML_TFLG_AUD_PREINIT))
2N/A _audit_preinit(&(AUDITORS(clmp)->ad_list), clmp);
2N/A
2N/A if (appl)
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Clean up (free) an audit descriptor. First, gather a list of all handles,
2N/A * and then close each one down. This is done rather than using the handles
2N/A * directly from the auditors, as the audit list can be torn down as a result
2N/A * of the dlclose. In other words, what you're pointing at can be removed
2N/A * while your still pointing at it.
2N/A */
2N/Avoid
2N/Aaudit_desc_cleanup(Audit_desc *adp, Rt_map *clmp)
2N/A{
2N/A Audit_list *alp;
2N/A Listnode *lnp, *olnp;
2N/A Alist *ghalp = 0;
2N/A
2N/A if (adp == 0)
2N/A return;
2N/A if (adp->ad_name)
2N/A free(adp->ad_name);
2N/A
2N/A olnp = 0;
2N/A for (LIST_TRAVERSE(&(adp->ad_list), lnp, alp)) {
2N/A (void) alist_append(&ghalp, &(alp->al_ghp), sizeof (Grp_hdl *),
2N/A AL_CNT_GROUPS);
2N/A
2N/A if (olnp)
2N/A free(olnp);
2N/A olnp = lnp;
2N/A }
2N/A if (olnp)
2N/A free(olnp);
2N/A
2N/A if (ghalp) {
2N/A Grp_hdl ** ghpp;
2N/A Aliste off;
2N/A
2N/A for (ALIST_TRAVERSE(ghalp, off, ghpp))
2N/A (void) dlclose_intn(*ghpp, clmp);
2N/A free(ghalp);
2N/A }
2N/A free(adp);
2N/A}
2N/A
2N/A/*
2N/A * Clean up (free) an audit information structure.
2N/A */
2N/Avoid
2N/Aaudit_info_cleanup(Audit_info *aip)
2N/A{
2N/A if (aip == 0)
2N/A return;
2N/A
2N/A if (aip->ai_dynplts)
2N/A free(aip->ai_dynplts);
2N/A free(aip);
2N/A}
2N/A
2N/A/*
2N/A * Create a data structure of symbol lookup names and associated flags to help
2N/A * simplify audit_symget() use.
2N/A */
2N/Atypedef struct {
2N/A Msg sname;
2N/A uint_t alflag;
2N/A uint_t auflag;
2N/A} Aud_info;
2N/A
2N/Astatic const Aud_info aud_info[] = {
2N/A { MSG_SYM_LAVERSION, 0 }, /* MSG_ORIG(MSG_SYM_LAVERSION) */
2N/A { MSG_SYM_LAPREINIT, /* MSG_ORIG(MSG_SYM_LAPREINIT) */
2N/A LML_TFLG_AUD_PREINIT, 0 },
2N/A { MSG_SYM_LAOBJSEARCH, /* MSG_ORIG(MSG_SYM_LAOBJSEARCH) */
2N/A LML_TFLG_AUD_OBJSEARCH, 0 },
2N/A { MSG_SYM_LAOBJOPEN, /* MSG_ORIG(MSG_SYM_LAOBJOPEN) */
2N/A LML_TFLG_AUD_OBJOPEN, 0 },
2N/A { MSG_SYM_LAOBJFILTER, /* MSG_ORIG(MSG_SYM_LAOBJFILTER */
2N/A LML_TFLG_AUD_OBJFILTER, 0 },
2N/A { MSG_SYM_LAOBJCLOSE, /* MSG_ORIG(MSG_SYM_LAOBJCLOSE) */
2N/A LML_TFLG_AUD_OBJCLOSE, 0 },
2N/A { MSG_SYM_LAACTIVITY, /* MSG_ORIG(MSG_SYM_LAACTIVITY) */
2N/A LML_TFLG_AUD_ACTIVITY, 0 },
2N/A
2N/A#if defined(_ELF64)
2N/A { MSG_SYM_LASYMBIND_64, /* MSG_ORIG(MSG_SYM_LASYMBIND_64) */
2N/A#else
2N/A { MSG_SYM_LASYMBIND, /* MSG_ORIG(MSG_SYM_LASYMBIND) */
2N/A#endif
2N/A LML_TFLG_AUD_SYMBIND, 0 },
2N/A
2N/A#if defined(__sparcv9)
2N/A { MSG_SYM_LAV9PLTENTER, /* MSG_ORIG(MSG_SYM_LAV9PLTENTER) */
2N/A#elif defined(__sparc)
2N/A { MSG_SYM_LAV8PLTENTER, /* MSG_ORIG(MSG_SYM_LAV8PLTENTER) */
2N/A#elif defined(__amd64)
2N/A { MSG_SYM_LAAMD64PLTENTER, /* MSG_ORIG(MSG_SYM_LAAMD64PLTENTER) */
2N/A#elif defined(i386)
2N/A { MSG_SYM_LAX86PLTENTER, /* MSG_ORIG(MSG_SYM_LAX86PLTENTER) */
2N/A#else
2N/A#error platform not defined!
2N/A#endif
2N/A LML_TFLG_AUD_PLTENTER, AF_PLTENTER },
2N/A
2N/A#if defined(_ELF64)
2N/A { MSG_SYM_LAPLTEXIT_64, /* MSG_ORIG(MSG_SYM_LAPLTEXIT_64) */
2N/A#else
2N/A { MSG_SYM_LAPLTEXIT, /* MSG_ORIG(MSG_SYM_LAPLTEXIT) */
2N/A#endif
2N/A LML_TFLG_AUD_PLTEXIT, AF_PLTEXIT }
2N/A};
2N/A
2N/A#define AI_LAVERSION 0
2N/A#define AI_LAPREINIT 1
2N/A#define AI_LAOBJSEARCH 2
2N/A#define AI_LAOBJOPEN 3
2N/A#define AI_LAOBJFILTER 4
2N/A#define AI_LAOBJCLOSE 5
2N/A#define AI_LAACTIVITY 6
2N/A#define AI_LASYMBIND 7
2N/A#define AI_LAPLTENTER 8
2N/A#define AI_LAPLTEXIT 9
2N/A
2N/Astatic Addr
2N/Aaudit_symget(Audit_list * alp, int info)
2N/A{
2N/A Rt_map *_lmp, *lmp = alp->al_lmp;
2N/A const char *sname = MSG_ORIG(aud_info[info].sname);
2N/A uint_t alflag = aud_info[info].alflag;
2N/A uint_t auflag = aud_info[info].auflag;
2N/A uint_t binfo;
2N/A Sym *sym;
2N/A Slookup sl;
2N/A
2N/A sl.sl_name = sname;
2N/A sl.sl_cmap = lml_rtld.lm_head;
2N/A sl.sl_imap = lmp;
2N/A sl.sl_hash = 0;
2N/A sl.sl_rsymndx = 0;
2N/A sl.sl_flags = LKUP_FIRST;
2N/A
2N/A if (sym = LM_LOOKUP_SYM(lmp)(&sl, &_lmp, &binfo)) {
2N/A Addr addr = sym->st_value;
2N/A
2N/A if (!(FLAGS(lmp) & FLG_RT_FIXED))
2N/A addr += ADDR(lmp);
2N/A
2N/A if (alflag)
2N/A alp->al_flags |= alflag;
2N/A if (auflag)
2N/A audit_flags |= auflag;
2N/A
2N/A DBG_CALL(Dbg_audit_interface(alp->al_libname, sname));
2N/A return (addr);
2N/A } else
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Centralize cleanup routines.
2N/A */
2N/Astatic int
2N/Aaudit_disable(char *name, Rt_map *clmp, Grp_hdl *ghp, Audit_list *alp)
2N/A{
2N/A eprintf(ERR_FATAL, MSG_INTL(MSG_AUD_DISABLED), name);
2N/A if (ghp)
2N/A (void) dlclose_intn(ghp, clmp);
2N/A if (alp)
2N/A free(alp);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Given a list of one or more audit libraries, open each one and establish a
2N/A * a descriptor representing the entry points it provides.
2N/A */
2N/Aint
2N/Aaudit_setup(Rt_map *clmp, Audit_desc *adp, uint_t orig)
2N/A{
2N/A char *ptr, *next;
2N/A int error = 1;
2N/A
2N/A DBG_CALL(Dbg_audit_lib(adp->ad_name));
2N/A
2N/A /*
2N/A * Mark that we have at least one auditing link map
2N/A */
2N/A rtld_flags2 |= RT_FL2_HASAUDIT;
2N/A
2N/A /*
2N/A * The audit definitions may be a list (which will already have been
2N/A * dupped) so split it into individual tokens.
2N/A */
2N/A for (ptr = strtok_r(adp->ad_name, MSG_ORIG(MSG_STR_DELIMIT), &next);
2N/A ptr; ptr = strtok_r(NULL, MSG_ORIG(MSG_STR_DELIMIT), &next)) {
2N/A Grp_hdl *ghp;
2N/A Rt_map *lmp;
2N/A Rt_map **tobj;
2N/A Audit_list *alp;
2N/A
2N/A /*
2N/A * Open the audit library on its own link-map.
2N/A */
2N/A if ((ghp = dlmopen_intn((Lm_list *)LM_ID_NEWLM, ptr,
2N/A (RTLD_FIRST | RTLD_GLOBAL | RTLD_WORLD), clmp,
2N/A FLG_RT_AUDIT, orig, 0)) == 0) {
2N/A error = audit_disable(ptr, clmp, 0, 0);
2N/A continue;
2N/A }
2N/A lmp = ghp->gh_owner;
2N/A
2N/A /*
2N/A * If this auditor has already been loaded, reuse it.
2N/A */
2N/A if ((alp = LIST(lmp)->lm_alp) != 0) {
2N/A if (list_append(&(adp->ad_list), alp) == 0)
2N/A return (audit_disable(ptr, clmp, ghp, alp));
2N/A
2N/A adp->ad_cnt++;
2N/A DBG_CALL(Dbg_audit_version(alp->al_libname,
2N/A alp->al_vernum));
2N/A adp->ad_flags |= alp->al_flags;
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * If we are not running in the environment where
2N/A * libc/libthread are merged, we hold on to rtld lock
2N/A * upon leave() function.
2N/A *
2N/A * There is a possibility that libc is not mapped in yet.
2N/A * We may later find out that we will be running in
2N/A * libc/libthread merged enviornment. Refer to:
2N/A * get_lcinterface() in mutex.c.
2N/A */
2N/A if ((rtld_flags2 & RT_FL2_UNIFPROC) == 0)
2N/A LIST(lmp)->lm_flags |= LML_FLG_HOLDLOCK;
2N/A
2N/A /*
2N/A * Allocate an audit list descriptor for this object and
2N/A * search for all known entry points.
2N/A */
2N/A if ((alp = calloc(1, sizeof (Audit_list))) == 0)
2N/A return (audit_disable(ptr, clmp, ghp, 0));
2N/A
2N/A alp->al_libname = NAME(lmp);
2N/A alp->al_lmp = lmp;
2N/A alp->al_ghp = ghp;
2N/A
2N/A /*
2N/A * All audit libraries must handshake through la_version().
2N/A * Determine that the symbol exists, finish initializing the
2N/A * object, and then call the function.
2N/A */
2N/A if ((alp->al_version =
2N/A (uint_t(*)())audit_symget(alp, AI_LAVERSION)) == 0) {
2N/A eprintf(ERR_FATAL, MSG_INTL(MSG_GEN_NOSYM),
2N/A MSG_ORIG(MSG_SYM_LAVERSION));
2N/A error = audit_disable(ptr, clmp, ghp, alp);
2N/A continue;
2N/A }
2N/A
2N/A if ((tobj = tsort(lmp, LIST(lmp)->lm_init, RT_SORT_REV)) ==
2N/A (Rt_map **)S_ERROR)
2N/A return (audit_disable(ptr, clmp, ghp, alp));
2N/A
2N/A rtld_flags |= RT_FL_APPLIC;
2N/A if (tobj != (Rt_map **)NULL)
2N/A call_init(tobj, DBG_INIT_SORT);
2N/A
2N/A alp->al_vernum = alp->al_version(LAV_CURRENT);
2N/A rtld_flags &= ~RT_FL_APPLIC;
2N/A
2N/A if ((alp->al_vernum < LAV_VERSION1) ||
2N/A (alp->al_vernum > LAV_CURRENT)) {
2N/A eprintf(ERR_FATAL, MSG_INTL(MSG_AUD_BADVERS),
2N/A LAV_CURRENT, alp->al_vernum);
2N/A error = audit_disable(ptr, clmp, ghp, alp);
2N/A continue;
2N/A }
2N/A
2N/A if (list_append(&(adp->ad_list), alp) == 0)
2N/A return (audit_disable(ptr, clmp, ghp, alp));
2N/A
2N/A adp->ad_cnt++;
2N/A DBG_CALL(Dbg_audit_version(alp->al_libname, alp->al_vernum));
2N/A
2N/A /*
2N/A * Collect any remaining entry points.
2N/A */
2N/A alp->al_preinit =
2N/A (void(*)())audit_symget(alp, AI_LAPREINIT);
2N/A alp->al_objsearch =
2N/A (char *(*)())audit_symget(alp, AI_LAOBJSEARCH);
2N/A alp->al_objopen =
2N/A (uint_t(*)())audit_symget(alp, AI_LAOBJOPEN);
2N/A alp->al_objfilter =
2N/A (int(*)())audit_symget(alp, AI_LAOBJFILTER);
2N/A alp->al_objclose =
2N/A (uint_t(*)())audit_symget(alp, AI_LAOBJCLOSE);
2N/A alp->al_activity =
2N/A (void (*)())audit_symget(alp, AI_LAACTIVITY);
2N/A alp->al_symbind =
2N/A (uintptr_t(*)())audit_symget(alp, AI_LASYMBIND);
2N/A alp->al_pltenter =
2N/A (uintptr_t(*)())audit_symget(alp, AI_LAPLTENTER);
2N/A alp->al_pltexit =
2N/A (uintptr_t(*)())audit_symget(alp, AI_LAPLTEXIT);
2N/A
2N/A /*
2N/A * Collect the individual object flags, and assign this audit
2N/A * list descriptor to its associated link-map list.
2N/A */
2N/A adp->ad_flags |= alp->al_flags;
2N/A LIST(lmp)->lm_alp = alp;
2N/A }
2N/A
2N/A /*
2N/A * Free the original audit string, as this descriptor may be used again
2N/A * to add additional auditing.
2N/A */
2N/A free(adp->ad_name);
2N/A adp->ad_name = 0;
2N/A
2N/A return (error);
2N/A}
2N/A