/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * ident	"%Z%%M%	%I%	%E% SMI"
 */
package com.sun.audit;

import java.util.Stack;
import java.io.Serializable;

public class AuditSession implements Serializable
{
	// LD_LIBRARY_PATH determines directory for libadt_jni.so.
	// When you get an UnsatisfiedLinkError, and have determined
	// the path is right, the problem is probably in the library
	// itself, but Java doesn't say what it is.  Set up a cc
	// command to link the library to see what the actual error
	// is.

	static private boolean library_loaded = false;
	static {
		try {
			System.loadLibrary("adt_jni");
			library_loaded = true;
		} catch (Exception ex) {
			library_loaded = false;
		} catch (java.lang.UnsatisfiedLinkError ul) {
			library_loaded = false;
		}
	}
	private native boolean bsmAuditOn();
	private native byte[] startSession(
	    byte[] context, long flags)
	    throws Error;
	private native byte[] dupSession(
	    byte[] source)
	    throws Error;
	private native void endSession(byte[] sessionHandle)
	    throws Error;
	private native String getSessionId(byte[] sessionHandle)
	    throws Error;
	private native byte[] exportSessionData(byte[] sessionHandle)
	    throws Error;
	private native void sessionAttr(byte[] sessionHandle,
	    int euid, int egid, int ruid, int rgid,
            String hostname, int context)
	    throws Error;
//TSOL only
//	private native void setSL(byte[] sessionHandle, String label);
//end TSOL

	private byte[] sh;  // current session handle

	private Stack stateStack = new Stack();  // for push/pop

	boolean AuditIsOn = true;		// Underlying BSM state
	boolean ValidSession = true;		// Session object state

	// Create an audit session.
	// The fixed length of 8 corresponds to a 64 bit pointer;
	// valid overkill on 32 bit systems.
	// Even if bsmAuditOn returns false, need to create a session.

	public AuditSession(byte[] context) {

		if (!library_loaded) {
			ValidSession = false;
			AuditIsOn = false;
			sh = new byte[8];  // NULL pointer in C
			return;
		}
		AuditIsOn = bsmAuditOn();
		try {
			sh = startSession(context, 0);
		}
		catch (java.lang.Exception e) {
			ValidSession = false;
			sh = new byte[8];
		}
		catch (java.lang.Error e) { 
			ValidSession = false;
			sh = new byte[8];
			throw e;
		}
	}

	// getSession() is for use by AuditEvent, not much use to caller of
	// AuditSession "package protected"  == not public
	// 
	// If you think you need this C pointer (sh), see
	// exportSession() and the "context" parameter to
	// startSession() for a way to pass an audit thread from one
	// process to another or from one language to another.

	byte[] getSession() {
		return sh;
	}

	public String getSessionId() throws Exception {
		String	sessionId;

		if (ValidSession) {
			try {
				sessionId = getSessionId(sh);
			}
			catch (Exception e) {
				sessionId = null;
				throw e;
			}
			catch (Error e) {
				sessionId = null;
				throw e;
			}
		} else {
			sessionId = null;
		}
		return sessionId;
	}

	// auditOn: The return value does not reveal whether or
	// auditing is on, but whether or not the current audit
	// session was created ok.

	public boolean auditOn() {
		return (ValidSession);
	}

	public void finalize() {
		byte[]	state;

		while (!stateStack.empty()) {
			state = (byte[])stateStack.pop();
			endSession(state);
		}
		endSession(sh);
	}

	// Returns export data even if auditing is off.  If the
	// session is invalid (no jni library, memory error in
	// startSession), returns null.
	//
	// If you use exportSession(), it is important that you first
	// call setUser() even when auditOn() returns false; otherwise
	// the exported session will result in remote processes being
	// unable to generate an valid audit trail.

	public byte[] exportSession() throws Exception {
		byte[]	exportedData;

		if (ValidSession) {
			try {
				exportedData = exportSessionData(sh);
			}
			catch (java.lang.Exception e) {
				throw e;
			}
		} else {
			exportedData = null;
		}
		return exportedData;
	}

	// ADT_NEW, ADT_UPDATE and ADT_USER are the only valid values
	// for the context input to setUser().  If the user has
	// completed initial authentication, use ADT_NEW; if the user
	// is to change ids, such as to a role or to root, use
	// ADT_UPDATE.  If the process audit context is already set,
	// use ADT_USER.

	// If a uid or gid is unknown (e.g., unrecognized login id)
	// then use ADT_NO_ATTRIB for the uid/gid.
	//
	// For ADT_UPDATE only, use ADT_NO_CHANGE for any uid or gid
	// that you don't wish to change.

	public static final int ADT_NEW = 0;
	public static final int ADT_UPDATE = 1;
	public static final int ADT_USER = 2;
	public static final int ADT_NO_ATTRIB = -1;
	public static final int ADT_NO_CHANGE = -2;

	public void setUser(int euid, int egid, int ruid, int rgid,
			     String hostname, int context) {

		if (ValidSession) {
			try {
				sessionAttr(sh, euid, egid, ruid, rgid,
				    hostname, context);
			}
			catch (java.lang.Error e) {
				throw e;
			}
		}
	}

	// pushState duplicates the session handle, puts the source
	// handle on a stack, and makes the duplicate the current
	// handle dupSession throws an out of memory error to be
	// caught higher up.

	public void pushState() throws Exception {
		byte[]		copy;
		int		i;

		copy = dupSession(sh);
		stateStack.push(sh);
		sh = copy;
	}

	// popState frees the current handle and pops a handle off a 
	// stack to become the new current handle.
	// As with pushState, it lets the caller deal with any exceptions.

	public void popState() throws Exception {

		endSession(sh);
		sh = (byte[])stateStack.pop();
	}
//TSOL -- stub for base Solaris; should be called even if auditOn is
//false.
	public void setLabel(String label) throws Exception {
	//	if (ValidSession)
	//		setSL(sh, label);
	}
//end TSOL
}
