/*
 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


/*
 *  AuthorizationData.cpp
 *  Authorization
 *
 *  Created by Michael Brouwer on Thu Oct 12 2000.
 *
 */
#include <security_cdsa_utilities/AuthorizationData.h>
#include <security_cdsa_utilities/AuthorizationWalkers.h>
#include <security_cdsa_utilities/walkers.h>
#include <Security/checkpw.h>
#include <grp.h>
#include <pwd.h>


// checkpw() that uses provided struct passwd
extern "C"
{
int checkpw_internal( const struct passwd *pw, const char* password );
}


namespace Authorization {


AuthValueRef::AuthValueRef(const AuthValue &value) : 
	RefPointer<AuthValue>(new AuthValue(value)) {}

AuthValueRef::AuthValueRef(const AuthorizationValue &value) : 
	RefPointer<AuthValue>(new AuthValue(value)) {}

AuthValue::AuthValue(const AuthorizationValue &value) :
    mOwnsValue(false)
{
    mValue.length = value.length;
    mValue.data = value.data;
}

AuthValueRef::AuthValueRef(UInt32 length, void *data) : 
	RefPointer<AuthValue>(new AuthValue(length, data)) {}

AuthValue::AuthValue(UInt32 length, void *data) :
    mOwnsValue(true)
{
    mValue.length = length;
    mValue.data = new uint8_t[length];
    if (length)
        memcpy(mValue.data, data, length);
}

AuthValue::~AuthValue()
{
    if (mOwnsValue)
	{
		memset(mValue.data, 0, mValue.length);
        delete[] reinterpret_cast<uint8_t*>(mValue.data);
	}
}

AuthValue &
AuthValue::operator = (const AuthValue &other)
{
    if (mOwnsValue)
	{
		memset(mValue.data, 0 , mValue.length);
        delete[] reinterpret_cast<uint8_t*>(mValue.data);
	}
	
    mValue = other.mValue;
    mOwnsValue = other.mOwnsValue;
    other.mOwnsValue = false;
    return *this;
}

void
AuthValue::fillInAuthorizationValue(AuthorizationValue &value)
{
    value.length = mValue.length;
    value.data = mValue.data;
}

AuthValueVector &
AuthValueVector::operator = (const AuthorizationValueVector& valueVector)
{
    clear();
    for (unsigned int i=0; i < valueVector.count; i++)
        push_back(AuthValueRef(valueVector.values[i]));
    return *this;
}

void
AuthValueVector::copy(AuthorizationValueVector **data, size_t *length) const
{
    AuthorizationValueVector valueVector;
    valueVector.count = size();
    valueVector.values = new AuthorizationValue[valueVector.count];
    int i = 0;
	for (const_iterator it = begin(); it != end(); ++it, ++i)
    {
		(*it)->fillInAuthorizationValue(valueVector.values[i]);
	}

	DataWalkers::Copier<AuthorizationValueVector> flatValueVector(&valueVector);
	*length = flatValueVector.length();
	*data = flatValueVector.keep();

	delete[] valueVector.values;
}

AuthItem::AuthItem(const AuthorizationItem &item) :
    mFlags(item.flags),
    mOwnsName(true),
    mOwnsValue(true)
{
	if (!item.name)
		MacOSError::throwMe(errAuthorizationInternal);
	size_t nameLen = strlen(item.name) + 1;
	mName = new char[nameLen];
	memcpy(const_cast<char *>(mName), item.name, nameLen);

	mValue.length = item.valueLength;
	mValue.data = new uint8_t[item.valueLength];
	if (mValue.length)
		memcpy(mValue.data, item.value, item.valueLength);
}


AuthItem::AuthItem(AuthorizationString name) :
    mName(name),
    mFlags(0),
    mOwnsName(false),
    mOwnsValue(false)
{
    mValue.length = 0;
    mValue.data = NULL;
}

AuthItem::AuthItem(AuthorizationString name, AuthorizationValue value, AuthorizationFlags flags) :
    mFlags(flags),
    mOwnsName(true),
    mOwnsValue(true)
{
	if (!name)
		MacOSError::throwMe(errAuthorizationInternal);
	size_t nameLen = strlen(name) + 1;
	mName = new char[nameLen];
	memcpy(const_cast<char *>(mName), name, nameLen);

	mValue.length = value.length;
	mValue.data = new uint8_t[value.length];
	if (mValue.length)
		memcpy(mValue.data, value.data, value.length);
}

AuthItem::~AuthItem()
{
    if (mOwnsName)
        delete[] mName;
    if (mOwnsValue)
	{
		memset(mValue.data, 0, mValue.length);
        delete[] reinterpret_cast<uint8_t*>(mValue.data);
	}
}

bool
AuthItem::operator < (const AuthItem &other) const
{
    return strcmp(mName, other.mName) < 0;
}

AuthItem &
AuthItem::operator = (const AuthItem &other)
{
    if (mOwnsName)
        delete[] mName;
    if (mOwnsValue)
	{
		memset(mValue.data, 0, mValue.length);
        delete[] reinterpret_cast<uint8_t*>(mValue.data);
	}

    mName = other.mName;
    mValue = other.mValue;
    mFlags = other.mFlags;
    mOwnsName = other.mOwnsName;
    other.mOwnsName = false;
    mOwnsValue = other.mOwnsValue;
    other.mOwnsValue = false;
    return *this;
}

void
AuthItem::fillInAuthorizationItem(AuthorizationItem &item)
{
    item.name = mName;
    item.valueLength = mValue.length;
    item.value = mValue.data;
    item.flags = mFlags;
}

bool 
AuthItem::getBool(bool &value)
{
	if (mValue.length == sizeof(bool))
	{
		bool *tmpValue = (bool *)mValue.data;
		
		if (tmpValue)
		{
			value = *tmpValue;
			return true;
		}
	}
	
	return false;
}

bool
AuthItem::getString(string &value)
{
    if (mValue.data && ( *(static_cast<char*>(mValue.data)+mValue.length-1) == '\0' ))
    	value = string(static_cast<char*>(mValue.data), mValue.length - 1);
    else
    	value = string(static_cast<char*>(mValue.data), mValue.length);
	return true;
}

bool
AuthItem::getCssmData(CssmAutoData &value)
{
	value = CssmData(static_cast<uint8_t*>(mValue.data), mValue.length);
	return true;
}	


AuthItemRef::AuthItemRef(const AuthorizationItem &item) : RefPointer<AuthItem>(new AuthItem(item)) {}

AuthItemRef::AuthItemRef(AuthorizationString name) : RefPointer<AuthItem>(new AuthItem(name)) {}

AuthItemRef::AuthItemRef(AuthorizationString name, AuthorizationValue value, AuthorizationFlags flags) : RefPointer<AuthItem>(new AuthItem(name, value, flags)) {}


//
// AuthItemSet
//
AuthItemSet::AuthItemSet()
{
}

AuthItemSet::~AuthItemSet()
{
}

AuthItemSet &
AuthItemSet::operator = (const AuthorizationItemSet& itemSet)
{
    clear();

    for (unsigned int i=0; i < itemSet.count; i++)
        insert(AuthItemRef(itemSet.items[i]));

    return *this;
}

AuthItemSet::AuthItemSet(const AuthorizationItemSet *itemSet)
{
	if (itemSet)
	{
		for (unsigned int i=0; i < itemSet->count; i++)
			insert(AuthItemRef(itemSet->items[i]));
	}
}

void
AuthItemSet::copy(AuthorizationItemSet *&data, size_t &length, Allocator &alloc) const
{
    AuthorizationItemSet itemSet;
    itemSet.count = size();
    itemSet.items = new AuthorizationItem[itemSet.count];
    int i = 0;
    for (const_iterator it = begin(); it != end(); ++it, ++i)
    {
        (*it)->fillInAuthorizationItem(itemSet.items[i]);
    }

	DataWalkers::Copier<AuthorizationItemSet> flatItemSet(&itemSet, alloc);
	length = flatItemSet.length();
		
	data = flatItemSet.keep();
	// else flatItemSet disappears again

    delete[] itemSet.items;
}

AuthorizationItemSet *
AuthItemSet::copy() const
{
	AuthorizationItemSet *aCopy;
	size_t aLength;
	copy(aCopy, aLength);
	return aCopy;
}

AuthItem *
AuthItemSet::find(const char *name)
{
	AuthItemSet::const_iterator found = find_if(this->begin(), this->end(), FindAuthItemByRightName(name) );
	if (found != this->end())
		return *found;
	
	return NULL;
}

//
// CredentialImpl class
//

// only for testing whether this credential is usable
CredentialImpl::CredentialImpl(const string &username, const uid_t uid, const gid_t gid, bool shared) :
mUsername(username), mShared(shared), mUid(uid), mGid(gid), mCreationTime(CFAbsoluteTimeGetCurrent()), mValid(true)
{
}

// credential with validity based on username/password combination.
CredentialImpl::CredentialImpl(const string &username, const string &password, bool shared) :
mUsername(username), mShared(shared), mCreationTime(CFAbsoluteTimeGetCurrent()), mValid(false)
{
	// Calling into DirectoryServices can be a long term operation
//	Server::active().longTermActivity();	//@@@CONV

	// try short name first
	const char *user = username.c_str();
	struct passwd *pw = getpwnam(user);

    do {

        if (!pw)
        {
            secdebug("autheval", "user %s not found, creating invalid credential", user);
            break;
        }

		mUsername = string ( pw->pw_name );
		mUid = pw->pw_uid;
		mGid = pw->pw_gid;

        const char *passwd = password.c_str();
        int checkpw_status = checkpw_internal(pw, passwd);

        if (checkpw_status != CHECKPW_SUCCESS)
        {
				secdebug("autheval", "checkpw() for user %s failed with error %d, creating invalid credential", user, checkpw_status);
				break;
        }

		secdebug("autheval", "checkpw() for user %s succeeded, creating%s credential",
			user, mShared ? " shared" : "");

		mValid = true;

        endpwent();
    }
	while (0);
}


CredentialImpl::~CredentialImpl()
{
}

bool
CredentialImpl::operator < (const CredentialImpl &other) const
{
	if (!mShared && other.mShared)
		return true;
	if (!other.mShared && mShared)
		return false;

	return mUsername < other.mUsername;
}

// Returns true if this CredentialImpl should be shared.
bool
CredentialImpl::isShared() const
{
	return mShared;
}

// Merge with other
void
CredentialImpl::merge(const CredentialImpl &other)
{
	assert(mUsername == other.mUsername);

	if (other.mValid && (!mValid || mCreationTime < other.mCreationTime))
	{
		mCreationTime = other.mCreationTime;
		mUid = other.mUid;
		mGid = other.mGid;
		mValid = true;
	}
}

// The time at which this credential was obtained.
CFAbsoluteTime
CredentialImpl::creationTime() const
{
	return mCreationTime;
}

// Return true iff this credential is valid.
bool
CredentialImpl::isValid() const
{
	return mValid;
}

void
CredentialImpl::invalidate()
{
	mValid = false;
}

//
// Credential class
//
Credential::Credential() :
RefPointer<CredentialImpl>(NULL)
{
}

Credential::Credential(CredentialImpl *impl) :
RefPointer<CredentialImpl>(impl)
{
}

Credential::Credential(const string &username, const uid_t uid, const gid_t gid, bool shared) :
RefPointer<CredentialImpl>(new CredentialImpl(username, uid, gid, shared))
{
}

Credential::Credential(const string &username, const string &password, bool shared) :
RefPointer<CredentialImpl>(new CredentialImpl(username, password, shared))
{
}

Credential::~Credential()
{
}

bool
Credential::operator < (const Credential &other) const
{
	if (!*this)
		return other;

	if (!other)
		return false;

	return (**this) < (*other);
}



}	// end namespace Authorization
