/* Socks Server 5 * Copyright (C) 2002 - 2006 by Matteo Ricchetti - * 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; idxUsr,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; }