ClearBox Server™ v1.2 Developer's Guide

Step 8. Implementing RADIUS and TACACS+ Authorization

On this step we implement IRADIUSAuthorization and ITACACSAuthorization. They are used to check if a user is authorized to use some resources provided by a NAS and possibly return attributes in response packet. We check if a user has a limited time quota. If this quota is over, user is rejected. If a user has not reached his time limit yet, session-timeout value is applied to his session.

1. Right-click CExtension class, select Add -> Implement Interface..., select File option, and locate <installation path>\SDK\TACRADServer.tlb. Select and move IRADIUSAuthorization and ITACACSAuthorization to the right column. Click Finish.

2. With the Class View window find the following CExtension methods declarations and make them return S_OK instead of S_NOTIMPL: GetAutorejectAttributes, GetRequestMatchAttributes, NeedAllAttributes. We need not to implement them, and while they return E_NOTIMPL errors will be logged.

3. Change DefaultBehavior method implementation:

STDMETHOD(DefaultBehavior)(long tag, TAC_AUTHORPARAMS * authorParams, BSTR service, BSTR protocol, VARIANT_BOOL * permitMandatory, VARIANT_BOOL * permitOptional)
{
	*permitMandatory=VARIANT_TRUE;
	*permitOptional=VARIANT_TRUE;
	return S_OK;
}
	  

It's necessary to make server authorize all AV pairs offered by the NAS.

4. Now we create a function in CExtension class used both by RADIUS and TACACS-specific methods. It queries a database to get time credit of a user. Set the following parameters and code:

  • Return value - HRESULT
  • Access type - protected
  • Function name - GetTimeCredit
  • Parameter 1 - CString userName
  • Parameter 2 - int& timeCredit

It returns S_OK if user time limit is not exceeded, S_FALSE otherwise. If no limit is set then timeCredit is set to 0, otherwise it's the number of seconds allowed to a user.

HRESULT GetTimeCredit(CString userName, int& timeCredit)
{
	CCommand cmd;
	CString cmdStr;
	cmdStr.Format(_T("select TimeCredit from Users where Name='%s'"),userName);
	HRESULT hr=cmd.Open(m_DB.Session(),cmdStr);
	if (FAILED(hr))
		return hr;
	if (cmd.MoveFirst()!=S_OK)
		return S_OK;
	int* pCredit=(int*)cmd.GetValue(1);
	if (*pCredit < 0)
		return S_FALSE;
	timeCredit=*pCredit;
	return S_OK;
}
		

5. Double-click GetResponseAttributes method of CExtension class and change its code:

STDMETHOD(GetResponseAttributes)(long tag, USERINFOLITE * userInf, unsigned long attrNumIn, 
	RADIUS_ATTRIBUTE * inpAttributes, unsigned long * attrNumOut, RADIUS_ATTRIBUTE * * outpAttributes, 
	BSTR * explainString, RADAUTHENREPLY * replyType)
{	
	USES_CONVERSION;
	*replyType=ACCESS_REJECT;
	int sessionTimeout=0;
	if (m_AuthenType==db)
	{
		HRESULT hr=GetTimeCredit(W2T(userInf->userName),sessionTimeout);
		if (FAILED(hr))
			return hr;
		if (hr==S_FALSE)
		{
			*explainString=SysAllocString(L"No time left");
			return S_OK;
		}
	}

	int numAttr=(sessionTimeout==0)?2:3;
	*attrNumOut=numAttr;
	*outpAttributes=(RADIUS_ATTRIBUTE*)CoTaskMemAlloc(sizeof(RADIUS_ATTRIBUTE)*numAttr);
	(*outpAttributes)[0].flags=F_ECHO;
	(*outpAttributes)[0].type=6; //Service-Type
	(*outpAttributes)[0].valType=RADINT;
	(*outpAttributes)[0].dwValue=2; // Framed
	
	(*outpAttributes)[1].flags=F_ECHO;
	(*outpAttributes)[1].type=7; //Framed-Protocol
	(*outpAttributes)[1].valType=RADINT;
	(*outpAttributes)[1].dwValue=1; //PPP

	if (numAttr==3)
	{
		(*outpAttributes)[2].flags=0;
		(*outpAttributes)[2].type=27; //Session-Timeout
		(*outpAttributes)[2].valType=RADINT;
		(*outpAttributes)[2].dwValue=sessionTimeout;
	}

	*replyType=ACCESS_ACCEPT;
	return S_OK;
}
		

This method calls the function we've created. If user is not authorized, he is rejected. Then Framed service and PPP protocol attributes are included into the Access-Response packet. The flag F_ECHO means that if a request packet has these attributes but with other values, then they override Framed and PPP values. Finally, Session-Timeout attribute is included to control user session length if it's required.

6. Double-click GetAVPairs child node of CExtension class and replace this method definition with the following code:

STDMETHOD(GetAVPairs)(long tag, TAC_AUTHORPARAMS * authorParams, BSTR service, 
	BSTR protocol, unsigned long * outpSize, AVPAIR * * outpPairs, 
	BSTR * explainString, unsigned char * tacacsStatus)
{
	USES_CONVERSION;

	if (wcsicmp(L"ppp",service)!=0 || protocol==NULL 
		|| wcsicmp(L"lcp",protocol)!=0)
	{
		*tacacsStatus=TAC_PLUS_AUTHOR_STATUS_PASS_ADD;
		return S_OK;
	}

	*tacacsStatus=TAC_PLUS_AUTHOR_STATUS_FAIL;
	int sessionTimeout=0;
	if (m_AuthenType==db)
	{
		HRESULT hr=GetTimeCredit(W2T(authorParams->user),sessionTimeout);
		if (FAILED(hr))
			return hr;
		if (hr==S_FALSE)
		{
			*explainString=SysAllocString(L"No time left");
			return S_OK;
		}
	}

	if (sessionTimeout!=0)
	{
		*outpSize=1;
		*outpPairs=reinterpret_cast&kt;AVPAIR*>(
			CoTaskMemAlloc(sizeof(AVPAIR)));
		(*outpPairs)[0].access=A_PERMIT;
		(*outpPairs)[0].mandType=M_MANDATORY;
		(*outpPairs)[0].attribute=SysAllocString(L"timeout");

		WCHAR wbuff[11];
		_itow(sessionTimeout,wbuff,10);
		(*outpPairs)[0].value=SysAllocString(wbuff);
	}
	
	*tacacsStatus=TAC_PLUS_AUTHOR_STATUS_PASS_ADD;
	return S_OK;
}
		

First, this method authorizes requests with service other than PPP and protocol other than LCP. Otherwise if authentication is performed via database then user time credit is checked. If it is less than zero then authorization is rejected. If time credit is more than zero then it's set as a user session timeout via timeout= attribute-value pair.

Go to the next step.


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

Created by chm2web html help conversion utility.