Example #1
0
void CAeonEngine::MsgOnMachineStart (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgOnMachineStart
//
//	Exarch.onMachineStart

	{
	CSmartLock Lock(m_cs);

	//	If we've already started, then nothing to do.

	if (m_bMachineStarted)
		return;

	//	Check to see if we're listed in the module list. If we're not, then we 
	//	continue waiting.

	if (!Msg.dPayload.IsNil() && !Msg.dPayload.Find(GetProcessCtx()->GetModuleName()))
		return;

	//	Aeon starting

	Log(MSG_LOG_INFO, STR_AEON_STARTING);

	//	Make a list of local volumes

	if (!OpenLocalVolumes())
		return;

	//	We've at least gotten to onMachineStart. From now on if we get any 
	//	storage added, we have to deal with it differently.

	m_bMachineStarted = true;

	//	If we have no local volumes then we cannot proceed. We wait until we
	//	have storage to send Aeon.onStart.

	if (m_LocalVolumes.GetCount() == 0)
		{
		Log(MSG_LOG_INFO, STR_ERROR_NO_LOCAL_STORAGE);
		return;
		}

	//	Make sure the database is open

	if (!Open())
		return;

	//	Tell any listeners that the database is ready

	SendMessageNotify(ADDRESS_AEON_NOTIFY, MSG_AEON_ON_START, CDatum());

	//	Done

	Log(MSG_LOG_INFO, STR_DATABASE_OPEN);
	}
Example #2
0
bool CAeonEngine::OpenTableDefinitions (void)

//	OpenTableDefinitions
//
//	Opens and reads all tables in all volumes. Note that we have no idea what
//	could have happened since our last boot--someone could have copied files
//	all over the place. We make almost no assumptions.

	{
	CSmartLock Lock(m_cs);

	int i, j;
	CString sError;

	//	Loop over all volumes and end up with a list of tables and volumes

	TSortMap<CString, TArray<CString>> Tables;
	for (i = 0; i < m_LocalVolumes.GetCount(); i++)
		{
		CString sVolume = m_LocalVolumes.GetVolume(i);

		TArray<CString> Dirs;
		fileGetFileList(fileAppend(m_LocalVolumes.GetPath(i), FILESPEC_TABLE_DIR_FILTER), FFL_FLAG_DIRECTORIES_ONLY | FFL_FLAG_RELATIVE_FILESPEC, &Dirs);

		for (j = 0; j < Dirs.GetCount(); j++)
			{
			TArray<CString> *pList = Tables.SetAt(Dirs[j]);
			pList->Insert(sVolume);
			}
		}

	//	Open all tables

	for (i = 0; i < Tables.GetCount(); i++)
		{
		CString sName = Tables.GetKey(i);

		CAeonTable *pTable = new CAeonTable;
		if (!pTable->Open(GetProcessCtx(), &m_LocalVolumes, sName, Tables[i], &sError))
			{
			Log(MSG_LOG_ERROR, strPattern("Unable to load %s: %s", sName, sError));
			delete pTable;
			continue;
			}

		m_Tables.Insert(sName, pTable);
		}

	//	Done

	return true;
	}
Example #3
0
bool CAeonEngine::OpenLocalVolumes (void)

//	OpenLocalVolumes
//
//	Initializes the m_LocalVolumes list. Returns TRUE if successful.

	{
	//	Get the list of volumes in the entire arcology

	CString sError;
	if (!m_LocalVolumes.Init(GetProcessCtx(), STR_AEON_FOLDER, &sError))
		{
		Log(MSG_LOG_ERROR, sError);
		return false;
		}

	//	Done

	return true;
	}
Example #4
0
bool CUserInfoSession::OnProcessMessage (const SArchonMessage &Msg)

//	OnProcessMessage
//
//	We received a reply from Aeon

	{
	int i;

	//	If this is an error, then we return the error back to the client

	if (IsError(Msg))
		{
		SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, Msg.dPayload);
		return false;
		}

	//	If we're waiting for the user record, then see if we can process it now.

	if (m_iState == stateWaitingForUserRecord)
		{
		//	Cryptosaur.getUser

		if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_GET_USER))
			{
			CDatum dUserData = Msg.dPayload;

			//	If the user does not exist, then we return Nil

			if (dUserData.IsNil())
				{
				SendMessageReply(MSG_REPLY_DATA, CDatum());
				return false;
				}

			//	Generate a sanitized user record

			CComplexStruct *pReply = new CComplexStruct;
			pReply->SetElement(FIELD_USERNAME, dUserData.GetElement(FIELD_USERNAME));

			//	Sanitize rights

			CDatum dRights = dUserData.GetElement(FIELD_RIGHTS);
			if (!m_sScope.IsEmpty())
				{
				CComplexArray *pRights = new CComplexArray;

				for (i = 0; i < dRights.GetCount(); i++)
					if (strStartsWith(dRights.GetElement(i), m_sScope))
						pRights->Insert(dRights.GetElement(i));

				pReply->SetElement(FIELD_RIGHTS, CDatum(pRights));
				}
			else
				pReply->SetElement(FIELD_RIGHTS, dRights);

			//	Done

			SendMessageReply(MSG_REPLY_DATA, CDatum(pReply));
			return false;
			}

		//	If we get back nil then the user does not exist.

		else if (Msg.dPayload.IsNil())
			{
			SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, strPattern(ERR_UNKNOWN_USERNAME, m_sUsername));
			return false;
			}

		//	Otherwise, we handle the result based on the original message

		else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_CHECK_PASSWORD_SHA1))
			{
			//	Get the parameters from the original message

			CDatum dChallenge = GetOriginalMsg().dPayload.GetElement(1);
			CDatum dResponse = GetOriginalMsg().dPayload.GetElement(2);

			//	Get the password has from the response

			CDatum dAuthDesc = Msg.dPayload.GetElement(FIELD_AUTH_DESC);
			CDatum dPasswordHash = dAuthDesc.GetElement(FIELD_CREDENTIALS);

			//	Create a response to the challenge based on the password hash that
			//	we have stored.

			CDatum dCorrect = CAI1Protocol::CreateSHAPasswordChallengeResponse(dPasswordHash, dChallenge);

			//	Compare the correct response to the actual

			if ((const CIPInteger &)dResponse == (const CIPInteger &)dCorrect)
				return UpdateLoginSuccess(stateWaitingForSuccessUpdate);
			else
				return UpdateLoginFailure();
			}

		//	Cryptosaur.hasRights

		else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_HAS_RIGHTS))
			{
			CDatum dRights = Msg.dPayload.GetElement(FIELD_RIGHTS);
			CDatum dRightsRequired = m_dPayload.GetElement(1);

			//	Get the rights from the user

			CAttributeList Rights;
			dRights.AsAttributeList(&Rights);

			//	Check

			for (i = 0; i < dRightsRequired.GetCount(); i++)
				{
				if (!Rights.HasAttribute(dRightsRequired.GetElement(i)))
					{
					SendMessageReply(MSG_REPLY_DATA, CDatum());
					return false;
					}
				}

			//	We have all rights

			SendMessageReply(MSG_REPLY_DATA, CDatum(CDatum::constTrue));
			return false;
			}

		//	Cryptosaur.loginUser

		else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_LOGIN_USER))
			{
			//	Get the parameters from the original message

			CDatum dRequestAuthDesc = GetOriginalMsg().dPayload.GetElement(1);
			CDatum dCredentials = dRequestAuthDesc.GetElement(FIELD_CREDENTIALS);
			CDatum dChallengeCredentials = dRequestAuthDesc.GetElement(FIELD_CHALLENGE_CREDENTIALS);
			CDatum dPassword = dRequestAuthDesc.GetElement(FIELD_PASSWORD);
			m_bActual = !dRequestAuthDesc.GetElement(FIELD_ACTUAL).IsNil();

			if (!dRequestAuthDesc.GetElement(FIELD_AUTH_TOKEN_INFINITE).IsNil())
				m_dwAuthTokenLifetime = 0;
			else
				{
				m_dwAuthTokenLifetime = (DWORD)(int)dRequestAuthDesc.GetElement(FIELD_AUTH_TOKEN_LIFETIME);
				if (m_dwAuthTokenLifetime == 0)
					m_dwAuthTokenLifetime = DEFAULT_AUTH_TOKEN_TIMEOUT;
				}

			//	If we're not actual and have no scope, then we can't continue

			if (!m_bActual && m_sScope.IsEmpty())
				{
				SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, ERR_SCOPE_REQUIRED);
				return false;
				}

			//	User data

			CDatum dUserData = Msg.dPayload;
			CDatum dAuthDesc;
			if (m_bActual)
				dAuthDesc = dUserData.GetElement(FIELD_AUTH_DESC);
			else
				dAuthDesc = dUserData.GetElement(strPattern("%s%s", m_sScope, FIELD_AUTH_DESC));

			//	If we have no authdesc, then we can't continue. This is likely 
			//	because the client is in a sandbox that the user has not registered
			//	with. We treat it the same as a username/password failure.

			if (dAuthDesc.IsNil())
				{
				SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_INVALID_USERNAME_OR_PASSWORD);
				return false;
				}

			//	If we've failed more than 5 consecutive times, we may need to delay
			//	the next login attempt.

			if ((int)dUserData.GetElement(FIELD_LOGIN_FAILURE_COUNT) > MAX_LOGIN_ATTEMPTS)
				{
				CDateTime LastLoginFailure = dUserData.GetElement(FIELD_LAST_LOGIN_FAILURE_ON);
				CTimeSpan TimeSinceLastFailure = timeSpan(LastLoginFailure, CDateTime(CDateTime::Now));

				//	If it has not been at least 1 hour, we return an error.

				if (TimeSinceLastFailure.Days() == 0 && TimeSinceLastFailure.Seconds() < LOGIN_TIMEOUT)
					{
					//	Timeout

					SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_FAILURE_TIMEOUT);
					return false;
					}
				}

			//	If we have straight credentials, then just compare

			bool bSuccess;
			if (!dCredentials.IsNil())
				bSuccess = ((const CIPInteger &)dCredentials == (const CIPInteger &)dAuthDesc.GetElement(FIELD_CREDENTIALS));

			//	Otherwise, we compare against the challenge

			else if (!dChallengeCredentials.IsNil())
				{
				//	Get the challenge. If not provided then we get it from the user
				//	record.

				CDatum dChallenge = GetOriginalMsg().dPayload.GetElement(2);
				if (dChallenge.IsNil())
					{
					//	Get the expiration time of the challenge

					const CDateTime &Expires = dAuthDesc.GetElement(FIELD_CHALLENGE_EXPIRATION);
					if (Expires < CDateTime(CDateTime::Now))
						{
						SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_INVALID_USERNAME_OR_PASSWORD);
						return false;
						}

					dChallenge = dAuthDesc.GetElement(FIELD_CHALLENGE);
					}

				//	Create a response to the challenge based on the password hash that
				//	we have stored.

				CDatum dCorrectChallenge = CAI1Protocol::CreateSHAPasswordChallengeResponse(
						dAuthDesc.GetElement(FIELD_CREDENTIALS),
						dChallenge
						);

				bSuccess = ((const CIPInteger &)dChallengeCredentials == (const CIPInteger &)dCorrectChallenge);
				}

			//	Otherwise we expect a clear text password

			else if (!dPassword.IsNil())
				{
				//	We have to hash the password to compare with credentials.

				CIPInteger Credentials;
				CCryptosaurInterface::CreateCredentials(dUserData.GetElement(FIELD_USERNAME), dPassword, &Credentials);

				//	Compare

				bSuccess = (Credentials == (const CIPInteger &)dAuthDesc.GetElement(FIELD_CREDENTIALS));
				}
			else
				bSuccess = false;

			//	Success or failure

			if (bSuccess)
				return UpdateLoginSuccess(stateWaitingForCredentials);
			else
				return UpdateLoginFailure();
			}

		//	Can never get here.

		else
			{
			ASSERT(false);
			return false;
			}
		}

	//	Otherwise, if we're waiting for the user record update, then continue

	else if (m_iState == stateWaitingForSuccessUpdate)
		{
		//	Since we succeeded, we send the user sanitized user record back.

		SendMessageReply(MSG_REPLY_DATA, CreateSanitizedUserRecord(Msg.dPayload));
		return false;
		}

	//	If we're waiting for credentials, compose them

	else if (m_iState == stateWaitingForCredentials)
		{
		//	The mutation returns the full record

		CDatum dUserData = Msg.dPayload;

		//	Compute the result

		CComplexStruct *pAuthToken = new CComplexStruct;
		pAuthToken->SetElement(FIELD_USERNAME, dUserData.GetElement(FIELD_USERNAME));
		pAuthToken->SetElement(FIELD_RIGHTS, dUserData.GetElement(FIELD_RIGHTS));
		if (!m_bActual)
			pAuthToken->SetElement(FIELD_SCOPE, m_sScope);

		CDatum dAuthToken = m_pEngine->GenerateAuthToken(CDatum(pAuthToken), m_dwAuthTokenLifetime);

		//	Compose a basic user record

		CComplexStruct *pReply = new CComplexStruct;
		pReply->SetElement(FIELD_AUTH_TOKEN, dAuthToken);
		pReply->SetElement(FIELD_RIGHTS, dUserData.GetElement(FIELD_RIGHTS));
		pReply->SetElement(FIELD_USERNAME, dUserData.GetElement(FIELD_USERNAME));

		//	Send the reply

		SendMessageReply(MSG_REPLY_DATA, CDatum(pReply));

		//	Done

		return false;
		}

	//	Otherwise, failure

	else if (m_iState == stateWaitingForFailureUpdate)
		{
		CDatum dUserData = Msg.dPayload;

		//	If we've exceeded our limit, log it

		int iAttempts = (int)dUserData.GetElement(FIELD_LOGIN_FAILURE_COUNT);
		if (iAttempts > MAX_LOGIN_ATTEMPTS)
			GetProcessCtx()->Log(MSG_LOG_INFO, strPattern(ERR_USERNAME_TIMEOUT, m_sUsername, iAttempts));

		//	Send a failure

		SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_INVALID_USERNAME_OR_PASSWORD);
		return false;
		}

	//	Can never get here

	else
		{
		ASSERT(false);
		return false;
		}
	}
Example #5
0
void CAeonEngine::MsgFileUpload (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgFileUpload
//
//	Aeon.fileUpload {filePath} {fileUploadDesc} {data}

	{
	AEONERR error;
    CMsgProcessCtx Ctx(*GetProcessCtx(), Msg, pSecurityCtx);

	//	Get the filePath

	CString sError;
	CString sTable;
	CString sFilePath;
	if (!CAeonTable::ParseFilePathForCreate(Msg.dPayload.GetElement(0), &sTable, &sFilePath, &sError))
		{
		SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_PARSING_FILE_PATH, sError), Msg);
		return;
		}

	//	Make sure we are allowed access to this table

	if (!ValidateTableAccess(Msg, pSecurityCtx, sTable))
		return;

	//	Get other parameters

	CDatum dUploadDesc = Msg.dPayload.GetElement(1);
	CDatum dData = Msg.dPayload.GetElement(2);

	//	Lock while we find or create a table

	CSmartLock Lock(m_cs);

	//	If the table doesn't exist, then we create a new one inside the lock

	CAeonTable *pTable;
	if (!m_Tables.Find(sTable, &pTable))
		{
		//	Compose a table descriptor

		CDatum dTableDesc;
		if (!CDatum::Deserialize(CDatum::formatAEONScript, CBuffer(strPattern(STR_FILE_TABLE_DESC_PATTERN, sTable)), &dTableDesc))
			{
			SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_INVALID_TABLE_NAME, sTable), Msg);
			return;
			}

		//	Create the table

		if (!CreateTable(dTableDesc, &pTable, NULL, &sError))
			{
			SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, sError, Msg);
			return;
			}
		}

	//	Unlock

	Lock.Unlock();

	//	Generate a unique session ID from the message

	CString sSessionID = strPattern("%s/%x%s", Msg.sReplyAddr, Msg.dwTicket, sFilePath);

#ifdef DEBUG_FILE_UPLOAD
    DWORD dwStart = ::sysGetTickCount();
    Log(MSG_LOG_INFO, strPattern("Aeon.fileUpload %s [%d bytes]", sFilePath, dData.GetBinarySize()));
#endif

	//	Let the table handle the rest

	int iComplete;
	if (error = pTable->UploadFile(Ctx, sSessionID, sFilePath, dUploadDesc, dData, &iComplete, &sError))
		{
		if (error == AEONERR_OUT_OF_DATE)
			SendMessageReplyError(MSG_ERROR_OUT_OF_DATE, sError, Msg);
		else
			SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, sError, Msg);
		return;
		}

#ifdef DEBUG_FILE_UPLOAD
    Log(MSG_LOG_INFO, strPattern("Aeon.fileUpload complete: %d seconds.", ::sysGetTicksElapsed(dwStart) / 1000));
#endif

	//	Reply

	if (iComplete == 100)
		SendMessageReply(MSG_OK, CDatum(), Msg);
	else
		{
		CDatum dReceipt;

		//	LATER: Fill in receipt

		SendMessageReply(MSG_AEON_FILE_UPLOAD_PROGRESS, dReceipt, Msg);
		}
	}
Example #6
0
bool CAeonEngine::CreateTable (CDatum dDesc, CAeonTable **retpTable, bool *retbExists, CString *retsError)

//	CreateTable
//
//	Creates a new table.

	{
	//	Prefill

	if (retbExists)
		*retbExists = false;

	//	No storage

	if (m_LocalVolumes.GetCount() == 0)
		{
		*retsError = STR_ERROR_NO_LOCAL_STORAGE;
		return false;
		}

	//	Check to see if this is a valid name

	const CString &sName = dDesc.GetElement(FIELD_NAME);
	if (!CAeonTable::ValidateTableName(sName))
		{
		if (retsError)
			*retsError = strPattern(STR_ERROR_INVALID_TABLE_NAME, sName);
		return false;
		}

	//	Add the table (if it doesn't already exist)

	CSmartLock Lock(m_cs);

	CAeonTable *pTable;
	if (m_Tables.Find(sName, &pTable))
		{
		//	If the table already exists see if we need to re-initialized it
		//	based on a new descriptor

		bool bUpdated;
		if (!pTable->Recreate(GetProcessCtx(), dDesc, &bUpdated, retsError))
			return false;

		//	If we updated the table then we succeeded

		if (bUpdated)
			{
			if (retpTable)
				*retpTable = pTable;
			return true;
			}

		//	Otherwise we reply that the table already exists.

		if (retsError)
			*retsError = strPattern(STR_ERROR_TABLE_ALREADY_EXISTS, sName);

		if (retbExists)
			*retbExists = true;

		return false;
		}

	//	Create a new table.
	//	NOTE: We rely on the fact that we've locked the engine to prevent
	//	local storage from changing while the table is created.

	pTable = new CAeonTable;
	if (!pTable->Create(GetProcessCtx(), &m_LocalVolumes, dDesc, retsError))
		{
		delete pTable;
		return false;
		}

	int iNewTableIndex = m_Tables.GetCount();
	m_Tables.Insert(sName, pTable);

	//	Done

	if (retpTable)
		*retpTable = pTable;

	return true;
	}
Example #7
0
void CAeonEngine::MsgOnMnemosynthModified (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgOnMnemosynthModified
//
//	Mnemosynth.onModified {updates}

	{
	int i;

	//	If we haven't yet started we don't care about this message.

	if (!m_bMachineStarted)
		return;

	//	Get the updates

	CDatum dCollections = Msg.dPayload.GetElement(FIELD_COLLECTIONS);
	CDatum dEntries = Msg.dPayload.GetElement(FIELD_ENTRIES);

	//	See what changed

	bool bStorageChanged = false;
	for (i = 0; i < dEntries.GetCount(); i++)
		{
		CDatum dEntry = dEntries.GetElement(i);

		const CString &sCollection = dCollections.GetElement((int)dEntry.GetElement(0));
		if (strEquals(sCollection, MNEMO_ARC_STORAGE))
			bStorageChanged = true;
		}

	//	If storage changed then update local storage (which will tell us what
	//	storage was added or deleted).

	if (bStorageChanged)
		{
		CSmartLock Lock(m_cs);

		CString sError;
		TArray<CString> VolumesAdded;
		TArray<CString> VolumesDeleted;
		if (!m_LocalVolumes.Reinit(GetProcessCtx(), &VolumesAdded, &VolumesDeleted, &sError))
			{
			//	LATER: We don't know how to recover from this, so we should go into
			//	safe mode.
			Log(MSG_LOG_ERROR, sError);
			return;
			}

		//	Tell every table that volumes were added and deleted so that they can
		//	change their primary and backup volumes.

		if (VolumesAdded.GetCount() > 0 || VolumesDeleted.GetCount() > 0)
			{
			for (i = 0; i < m_Tables.GetCount(); i++)
				m_Tables[i]->OnVolumesChanged(VolumesDeleted);

			//	LATER: See if there are any existing tables in the new volume.
			}
		}
	}