/* Socks Server 5
* Copyright (C) 2002 - 2006 by Matteo Ricchetti - <matteo.ricchetti@libero.it>
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* $Id: SS5Mod_authentication.c, Ver 3.7 08-2005 20:00:00 Matteo Ricchetti Exp $
*/
#include"SS5Main.h"
#include"SS5Mod_authentication.h"
#include"SS5Mod_log.h"
#include"SS5Basic.h"
#include"SS5ExternalProgram.h"
#include"SS5Pam.h"
S5RetCode InitModule( struct _module *m )
{
m->Authentication = Authentication;
m->FreeAuthCache = FreeAuthCache;
return OK;
}
S5RetCode Authentication( struct _SS5MethodInfo *mi, struct _SS5ClientInfo *ci, struct _SS5BasicData *bd, struct _SS5AuthInfo *ai )
{
register unsigned int idx;
S5RetCode err = ERR;
S5RetCode err2 = ERR;
char logString[256];
pid_t pid;
/*
* Get child/thread pid
*/
if( NOTTHREADED() )
pid = getpid();
else
pid = (unsigned int)pthread_self();
memset(ai,0,sizeof(struct _SS5AuthInfo));
strncpy(ai->Username,"\"\"",strlen("\"\""));
if( mi->Method == NOAUTH ) {
strncpy(ai->Username,"\"\"",strlen("\"\""));
return OK;
}
else if( (mi->Method == USRPWD) || (mi->Method == FAKEPWD) ) {
if( recv(ci->Socket,bd->Request,sizeof(bd->Request),0) <= 0 ) {
ERRNO(pid)
return ERR;
}
/*
* Check for buffer overflow
*/
if( ((unsigned char)bd->Request[1] == 0) || ((unsigned char)bd->Request[1] >= sizeof(ai->Username)) )
return ERR;
if( ((unsigned char)bd->Request[2+(unsigned char)bd->Request[1]] == 0)
|| ((unsigned char)bd->Request[2+(unsigned char)bd->Request[1]] >= sizeof(ai->Password)) )
return ERR;
/*
* Get credentials
*/
for(idx = 0; idx < (unsigned char)bd->Request[1]; idx++)
ai->Username[idx] = bd->Request[idx+2];
ai->Username[idx] = '\0';
for(idx = 0;idx < (unsigned char)bd->Request[2+(unsigned char)bd->Request[1]]; idx++)
ai->Password[idx] = bd->Request[3+bd->Request[1]+idx];
ai->Password[idx] = '\0';
/*
* Look for username/password into authentication cache
*/
if( THREADED() ) {
if( SS5SocksOpt.AuthCacheAge ) {
err2 = GetAuthCache(ai->Username,ai->Password);
if( err2 == ERR_EXPIRED ) {
/*
* Update the entry into authentication cache
*/
UpdateAuthCache(ai->Username,ai->Password);
if( VERBOSE() ) {
snprintf(logString,256 - 1,"[%u] [VERB] Cache authentication expired for user %s.",pid,ai->Username);
SS5Modules.mod_logging.Logging(logString);
}
}
}
}
if( err2 <= ERR ) {
if( mi->Method != FAKEPWD ) {
/*
* Evaluate how to handle basic autentication:
* 1. using an External Program (EAP)
* 2. using PAM
* 3. using local file /etc/opt/ss5/ss5.passwd
*/
switch( SS5SocksOpt.Authentication ) {
case EAP_AUTHENTICATION: err = S5AuthProgramCheck(ai, pid); break;
case PAM_AUTHENTICATION: err = S5PamCheck(ai); break;
case FILE_AUTHENTICATION:
if( S5PwdFileOpen(pid) ) {
err = S5PwdFileCheck(ai);
S5PwdFileClose(pid);
}
break;
}
}
else {
err = OK;
}
if( err ) {
if( THREADED() ) {
if( SS5SocksOpt.AuthCacheAge ) {
/*
* Add new entry into authentication cache
*/
AddAuthCache(ai->Username,ai->Password);
if( VERBOSE() ) {
snprintf(logString,256 - 1,"[%u] [VERB] Cache authentication updated for user %s.",pid,ai->Username);
SS5Modules.mod_logging.Logging(logString);
}
}
}
bd->Response[0] = 1;
bd->Response[1] = 0; /* Basic success */
if( send(ci->Socket,bd->Response,sizeof(bd->Response),SS5_SEND_OPT) == -1) {
ERRNO(pid)
return ERR;
}
return OK;
}
else {
bd->Response[0] = 1;
bd->Response[1] = 1; /* Basic failed */
if( send(ci->Socket,bd->Response,sizeof(bd->Response),SS5_SEND_OPT) == -1) {
ERRNO(pid)
return ERR;
}
}
}
else if( THREADED() ) {
if( SS5SocksOpt.AuthCacheAge ) {
/*
* Entry in cache
*/
if( VERBOSE() ) {
snprintf(logString,256 - 1,"[%u] [VERB] Cache authentication verified for user %s.",pid,ai->Username);
SS5Modules.mod_logging.Logging(logString);
}
bd->Response[0] = 1;
bd->Response[1] = 0; /* Basic success */
if( send(ci->Socket,bd->Response,sizeof(bd->Response),SS5_SEND_OPT) == -1) {
ERRNO(pid)
return ERR;
}
return OK;
}
}
}
return ERR;
}
/* ***************************** HASH for AUTHENTICATION CACHE **************************** */
inline S5Limit S5AuthCacheHash( char *u, char *p )
{
register unsigned int idx;
register unsigned int len;
register long int hashVal = 0;
char s[128];
snprintf(s,sizeof(s) - 1,"%s%s",u,p);
len=strlen(s);
for(idx=0; idx<len;idx++)
hashVal= 37*hashVal + s[idx];
hashVal %= MAXAUTHCACHELIST;
if(hashVal < 0)
hashVal +=MAXAUTHCACHELIST;
return hashVal;
}
S5RetCode GetAuthCache(char *u, char *p)
{
register unsigned int index;
struct _S5AuthCacheNode *node;
index=S5AuthCacheHash( u, p );
if( S5AuthCacheList[index]!= NULL ) {
node=S5AuthCacheList[index];
do {
if( STREQ(u,node->Usr,sizeof(node->Usr)) && STREQ(p,node->Pwd,sizeof(node->Pwd)) ) {
if( node->ttl > time(NULL) )
return OK;
else
return ERR_EXPIRED;
}
node=node->next;
} while(node != NULL );
}
return ERR;
}
S5RetCode AddAuthCache(char *u, char *p )
{
register unsigned int index;
struct _S5AuthCacheNode *node;
index=S5AuthCacheHash( u, p );
if( S5AuthCacheList[index]== NULL ) {
S5AuthCacheList[index]=(struct _S5AuthCacheNode *)calloc(1,sizeof(struct _S5AuthCacheNode));
strncpy(S5AuthCacheList[index]->Usr,u,sizeof(S5AuthCacheList[index]->Usr));
strncpy(S5AuthCacheList[index]->Pwd,p,sizeof(S5AuthCacheList[index]->Pwd));
S5AuthCacheList[index]->ttl=(time(NULL) + SS5SocksOpt.AuthCacheAge);
S5AuthCacheList[index]->next=NULL;
}
else {
node=S5AuthCacheList[index];
while( node->next != NULL ){
node=node->next;
}
node->next=(struct _S5AuthCacheNode *)calloc(1,sizeof(struct _S5AuthCacheNode));
node->next->ttl=(time(NULL) + SS5SocksOpt.AuthCacheAge);
strncpy(node->next->Usr,u,sizeof(S5AuthCacheList[index]->Usr));
strncpy(node->next->Pwd,p,sizeof(S5AuthCacheList[index]->Pwd));
node->next->next=NULL;
}
return OK;
}
S5RetCode UpdateAuthCache(char *u, char *p)
{
register unsigned int index;
struct _S5AuthCacheNode *node;
index=S5AuthCacheHash( u, p );
if( S5AuthCacheList[index]!= NULL ) {
node=S5AuthCacheList[index];
do {
if( STREQ(u,node->Usr,sizeof(node->Usr)) && STREQ(p,node->Pwd,sizeof(node->Pwd)) ) {
node->ttl=(time(NULL) + SS5SocksOpt.AuthCacheAge);
return OK;
}
node=node->next;
} while(node != NULL );
}
return ERR;
}
S5RetCode FreeAuthCache( struct _S5AuthCacheNode **node )
{
struct _S5AuthCacheNode *lnode;
struct _S5AuthCacheNode *lnode_prev=NULL;
lnode=*node;
if( lnode != NULL ) {
do {
while( lnode->next != NULL ) {
lnode_prev=lnode;
lnode=lnode->next;
}
free(lnode);
if( lnode_prev != NULL ) {
lnode_prev->next=NULL;
lnode=lnode_prev;
lnode_prev=NULL;
}
else
lnode=NULL;
} while( (lnode) != NULL );
}
*node=NULL;
return OK;
}
S5RetCode S5BrowseAuthCacheList( struct _S5AuthCacheNode *node )
{
struct _S5AuthCacheNode *lnode;
int found=0;
lnode=node;
do {
if(lnode != NULL ) {
printf("Usr: %s Pwd: %s\n",lnode->Usr,lnode->Pwd);
lnode=lnode->next;
found++;
}
} while( lnode != NULL );
return found;
}
syntax highlighted by Code2HTML, v. 0.9.1