ClearBox Server™ v1.2 Developer's Guide

Step 6. Implementing Common Authentication

On this step we implement ICommonAuthentication interface to make server extension authenticate users with their passwords.

1. Right-click CExtension class in the Class View window and select Add -> Implement Interface... in the context menu. Select from file option, and locate <server installation path>\SDK\TACRADServer.tlb in the location box. Move ICommonAuthentication, IRADIUSAuthentication and ITACACSAuthentication to the right list "Implement interfaces".

2. Double-click GetUserPassword child node of CExtension class in Class View and open this method definition.

Now we should change it so user password are read from a database. If authentication type if ActiveDirectory or NT SAM file, passwords are stored encrypted and can be checked through ICommonAuthentication::CheckPassword only. It should now be the following:

STDMETHOD(GetUserPassword)(long tag, USERINFO * userInf, VARIANT_BOOL * clearTextPassword,
	 VARIANT_BOOL * caseSensitive, VARIANT_BOOL * ignorePassword, VARIANT_BOOL * userExist)
{
	USES_CONVERSION;
	*userExist=VARIANT_FALSE;
	*ignorePassword=VARIANT_FALSE;
	*caseSensitive=VARIANT_TRUE;

	if (m_AuthenType!=db)
	{
		*clearTextPassword=VARIANT_FALSE;
		*userExist=VARIANT_TRUE;
		return S_OK;
	}

	*clearTextPassword=VARIANT_TRUE;
	CCommand<CDynamicAccessor> cmd;
	CString cmdString;
	cmdString.Format(_T("select Password from Users where Name='%s'"),W2T(userInf->userName));

	HRESULT hRes=cmd.Open(m_DB.Session(),cmdString);
	if (FAILED(hRes))
		return hRes;
	if (cmd.MoveFirst()!=S_OK)
		return S_OK;

	*userExist=VARIANT_TRUE;
	userInf->userPassword=SysAllocString((LPCWSTR)cmd.GetValue(1));

	return S_OK;
}
		

This method uses ATL templates to issue a sql query and retrieve user password if a user is authenticated with a database.

3. Create new function with the wizard. Right-click CExtension class, select Add -> Add Function... and specify the following parameters:

  • Return type - HRESULT
  • Function name - AdjustPrivileges
  • Access - protected

Click Finish. Set the function body to the following code:

HRESULT AdjustPrivileges(void)
{
	if (m_AuthenType!=ntsam)
		return S_OK;

	OSVERSIONINFO vi;
	vi.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
	GetVersionEx(&vi);
	if (vi.dwMajorVersion >=5 && vi.dwMinorVersion>0)
		return S_OK;

	HANDLE hToken=NULL;

	try
	{
		if(!OpenProcessToken(GetCurrentProcess(),
			TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken)) 
			throw HRESULT_FROM_WIN32(GetLastError()); 

		TOKEN_PRIVILEGES tp;
		LUID luid;

		if (!LookupPrivilegeValue(NULL,SE_TCB_NAME,&luid))
			throw HRESULT_FROM_WIN32(GetLastError()); 

		tp.PrivilegeCount = 1;
		tp.Privileges[0].Luid = luid;
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

		if (!AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(TOKEN_PRIVILEGES), 
			(PTOKEN_PRIVILEGES) NULL,(PDWORD) NULL))
			throw HRESULT_FROM_WIN32(GetLastError());
	} 
	catch(HRESULT hr)
	{
		m_pServ2->LogExtensionError(hr,
			L"AuthenType is set to NTSAM but the process has no necessary\
\"Act as a part of the Operating System\" privilege.");
	}
	return S_OK;
}
	  

It's used to adjust privileges of the server process. First, it's used for NTSAM authentication only. Next, OS version is queried. Windows XP or later does not require any special privileges to call LogonUser that is used to perform authentication. Finally. AdjustTokenPrivileges Win32 API is used. Note, that the process should have the privilege "Act as a part of the Operating System" (local system account always has it).

4. Open stdafx.h with SolutionExplorer and append to its end the following lines:

#include <activeds.h>
#pragma comment(lib,"ActiveDS.lib")
#pragma comment(lib,"adsiid.lib")
	  

They are needed to call AdsOpenObject Win32 API function.
[Important note. This function is not present in Windows NT 4 systems]

5. Double-click CheckPassword child node of CExtension class in the Class View window. ICommonAuthentication::CheckPassword method implementation is opened. Change it to the following code:

STDMETHOD(CheckPassword)(long tag, USERINFO * userInf, VARIANT_BOOL * checkOK)
{
	*checkOK=VARIANT_FALSE;

	USES_CONVERSION;

	if (m_AuthenType==ntsam)
	{
		HANDLE tok=NULL;
		BOOL res=LogonUser(W2T(userInf->userName),
		const_cast<LPTSTR>((LPCTSTR)m_AuthenDomain),W2T(userInf->userPassword),
			LOGON32_LOGON_NETWORK,LOGON32_PROVIDER_DEFAULT,&tok);
		CloseHandle(tok);

		*checkOK=(res)?VARIANT_TRUE:VARIANT_FALSE;
		return S_OK;
	}
	else
	{
		IADs* pObj=NULL;
		CString ts;
		ts.Format(_T("LDAP://%s"),m_AuthenDomain);

		HRESULT hr=ADsOpenObject(CT2W(ts),
			userInf->userName,
			userInf->userPassword,
			ADS_SECURE_AUTHENTICATION|ADS_READONLY_SERVER,
			IID_IADs,
			(void**)&pObj);
		if (pObj!=NULL)
			pObj->Release();

		*checkOK=(hr==S_OK)?VARIANT_TRUE:VARIANT_FALSE;
		return S_OK;
	}
}
	

Active Directory authentication is done with the help of ADsOpenObject Win32 API function. NT SAM authentication is performed with LogonUser function.

6. Finalize implementing ICommonAuthentication and ICommonExtender interfaces.

With the help of Class View locate RequestTimeCall method of CExtension class and make it return S_FALSE instead of E_NOTIMPL.

In the same way make LogonStatus method return S_OK.

Go to the next step.


© 2001-2003 XPerience Technologies. www.xperiencetech.com

Created by chm2web html help conversion utility.