681 lines
14 KiB
C
681 lines
14 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <utmp.h>
|
|
#include <locale.h>
|
|
#include <libgen.h>
|
|
#include <sys/inotify.h>
|
|
#include <errno.h>
|
|
|
|
#define HOSTNAME "localhost"
|
|
#define EVENT_SIZE (sizeof(struct inotify_event))
|
|
#define NONE 0
|
|
#define WARNING 1
|
|
#define INFO 2
|
|
#define DEBUG 3
|
|
|
|
struct connexion
|
|
{
|
|
int pid;
|
|
char cmd[24];
|
|
char cmdline[1000];
|
|
char user[24];
|
|
char hostname[128];
|
|
char host_ip[42];
|
|
char host_ipv6[42];
|
|
char date[60];
|
|
|
|
};
|
|
|
|
struct config
|
|
{
|
|
char commande[1024];
|
|
char logfile[4096];
|
|
char hostname[128];
|
|
char configfile[4096];
|
|
};
|
|
|
|
struct notify_config
|
|
{
|
|
int fd;
|
|
int wd;
|
|
};
|
|
|
|
struct config cfg = {"","","",""};
|
|
int loglevel = DEBUG;
|
|
|
|
// return date in localized format
|
|
char * frtime(const time_t timet)
|
|
{
|
|
struct tm *date_tm;
|
|
static char result[40];
|
|
|
|
date_tm = localtime(&timet);
|
|
strftime(result, 40, "%c", date_tm);
|
|
return result;
|
|
}
|
|
|
|
int printlog(char str[], int level, int errnum)
|
|
{
|
|
FILE * fh = NULL;
|
|
time_t now = 0;
|
|
char tmp[128];
|
|
int retval = EXIT_SUCCESS;
|
|
|
|
if ( level <= loglevel )
|
|
{
|
|
if (errnum != 0)
|
|
{
|
|
sprintf( tmp, "%s: %s %s\n", frtime(now), str, strerror(errnum));
|
|
}else
|
|
{
|
|
sprintf( tmp, "%s: %s\n", frtime(now), str);
|
|
}
|
|
if ( (fh = fopen(cfg.logfile, "a")) == NULL)
|
|
{
|
|
perror(cfg.logfile);
|
|
retval = EXIT_FAILURE;
|
|
}
|
|
if ( retval == EXIT_SUCCESS )
|
|
{
|
|
fprintf(fh, "%s", tmp);
|
|
fclose(fh);
|
|
}
|
|
printf("%s", tmp);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
int explode( char * str, char * separator, size_t m, size_t n, char exploded[m][n] )
|
|
{
|
|
char * pch;
|
|
int x=0 ;
|
|
|
|
pch = strtok( str, separator );
|
|
while( pch != NULL )
|
|
{
|
|
printlog( pch, DEBUG, 0);
|
|
strcpy( exploded[x++], pch) ;
|
|
pch = strtok( NULL , separator );
|
|
}
|
|
return x;
|
|
}
|
|
|
|
// config function
|
|
int readconfig( struct config * cfg )
|
|
{
|
|
FILE * fh = NULL;
|
|
FILE * fh1 = NULL;
|
|
char path[2][30] = {"/etc/sshdetect.conf", ""};
|
|
int x;
|
|
int retval=0;
|
|
char str[1024];
|
|
char strlog[128];
|
|
char exploded[2][1024];
|
|
char * homepath;
|
|
char * buff;
|
|
char logfilepath[1024];
|
|
|
|
homepath = getenv("HOME");
|
|
if ( homepath != NULL )
|
|
{
|
|
sprintf( path[1], "%s%s", homepath, "/.config/sshdetect.conf" );
|
|
}
|
|
sprintf( logfilepath, "%s%s", homepath, "/.local/share/sshdetect.log");
|
|
for(x=0;x<2;x++)
|
|
{
|
|
if ((fh = fopen( path[x], "r")) == NULL)
|
|
{
|
|
printlog( path[x], WARNING, errno );
|
|
retval = -1;
|
|
}else
|
|
{
|
|
strcpy( cfg->configfile, path[x]);
|
|
sprintf(strlog, "Found config file: %s\n", path[x]);
|
|
printlog( strlog, WARNING, 0);
|
|
x = 3;
|
|
retval = 0;
|
|
}
|
|
}
|
|
if (fh != NULL)
|
|
{
|
|
while(fgets(str, 1024, fh) != NULL)
|
|
{
|
|
explode(str, "= \n", 2, 1024, exploded);
|
|
if ( strcmp( exploded[0], "commande") == 0 )
|
|
{
|
|
if ( (fh1 = fopen(exploded[1],"r")) != NULL)
|
|
{
|
|
strcpy( cfg->commande, exploded[1] );
|
|
sprintf(strlog, "Found command: %s", cfg->commande);
|
|
printlog(strlog, INFO, 0);
|
|
fclose(fh1);
|
|
}else
|
|
{
|
|
printlog( exploded[1], INFO, errno);
|
|
}
|
|
}else if( strcmp( exploded[0], "logfile") == 0)
|
|
{
|
|
if ( (fh1 = fopen(exploded[1], "a")) != NULL )
|
|
{
|
|
strcpy( cfg->logfile, exploded[1] );
|
|
sprintf( strlog, "Found logfile: %s", cfg->logfile);
|
|
printlog(strlog, INFO, 0);
|
|
fclose(fh1);
|
|
}
|
|
}else if( strcmp( exploded[0], "hostname") == 0 )
|
|
{
|
|
strcpy( cfg->hostname, exploded[1] );
|
|
sprintf(strlog, "Found hostname: %s", cfg->hostname);
|
|
printlog(strlog, INFO, 0);
|
|
}
|
|
}
|
|
}
|
|
if ( cfg->logfile[0] == 0 )
|
|
{
|
|
if ( (fh1 = fopen("/var/log/sshdetect.log", "a")) != NULL )
|
|
{
|
|
strcpy( cfg->logfile, "/var/log/sshdetect.log" );
|
|
fclose(fh1);
|
|
}else if ( (fh1 = fopen(logfilepath, "a")) != NULL )
|
|
{
|
|
strcpy( cfg->logfile, logfilepath );
|
|
fclose(fh1);
|
|
}else
|
|
{
|
|
printlog( logfilepath, WARNING, errno);
|
|
strcpy(cfg->logfile, "/dev/null");
|
|
retval += 2;
|
|
}
|
|
printf("logfile not found, defaulting to %s\n", cfg->logfile);
|
|
}
|
|
if (cfg->hostname[0] == 0 )
|
|
{
|
|
buff = getenv("HOSTNAME");
|
|
if ( buff != NULL)
|
|
{
|
|
strcpy(cfg->hostname, buff);
|
|
}else
|
|
{
|
|
strcpy(cfg->hostname, HOSTNAME);
|
|
}
|
|
printf("hostname not found, defaulting to %s\n", cfg->hostname);
|
|
}
|
|
if (cfg->commande[0] == 0)
|
|
{
|
|
printf("command not found in config file: no command will be executed\n");
|
|
retval += 4;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
//test if pid is in list of known sshd processus
|
|
// return number of pid
|
|
int isinarray( int pid, int array[], int n )
|
|
{
|
|
int x;
|
|
char strlog[128];
|
|
|
|
for(x=1;x<=n;x++)
|
|
{
|
|
if( pid == array[x])
|
|
{
|
|
if (loglevel >= DEBUG )
|
|
{
|
|
sprintf(strlog, "pid %i is in array", pid);
|
|
printlog(strlog, DEBUG, 0 );
|
|
}
|
|
return x;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// initialize config file watching
|
|
int init_config_watch( char config_file[], struct notify_config * ncc )
|
|
{
|
|
ncc->fd = inotify_init();
|
|
if ( ncc->fd < 0 )
|
|
{
|
|
printlog("inotify_init", WARNING, errno );
|
|
return -1;
|
|
}
|
|
ncc->wd = inotify_add_watch( ncc->fd, config_file, IN_MODIFY | IN_DELETE );
|
|
if (ncc->wd == -1)
|
|
{
|
|
printlog(config_file, WARNING, errno);
|
|
return -2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int notify_config_change(struct notify_config * ncc, char config_file[])
|
|
{
|
|
int length=0;
|
|
int i=0;
|
|
int buff_length = (1024 * (EVENT_SIZE + 16));
|
|
char buff[buff_length];
|
|
char strlog[128];
|
|
fd_set rfds;
|
|
struct timeval tv = {1,0};
|
|
int retval;
|
|
|
|
FD_ZERO(&rfds);
|
|
FD_SET(ncc->fd, &rfds);
|
|
retval = select(ncc->fd+1, &rfds, NULL, NULL, &tv);
|
|
if (retval == -1)
|
|
{
|
|
printlog("select()", WARNING, errno);
|
|
}
|
|
else if (retval)
|
|
{
|
|
length = read(ncc->fd, buff, buff_length);
|
|
if (length < 0)
|
|
{
|
|
printlog("reading", WARNING, errno);
|
|
return -1;
|
|
}
|
|
while (i < length)
|
|
{
|
|
struct inotify_event *event = (struct inotify_event *) &buff[i];
|
|
if (event->mask & IN_DELETE)
|
|
{
|
|
sprintf(strlog, "The file %s was deleted.", event->name);
|
|
printlog(strlog, INFO, 0);
|
|
init_config_watch( config_file, ncc);
|
|
} else if (event->mask & IN_MODIFY)
|
|
{
|
|
sprintf(strlog, "The file %s was modified.", event->name);
|
|
printlog(strlog, INFO, 0);
|
|
readconfig(&cfg);
|
|
}
|
|
i += EVENT_SIZE + event->len;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//get utmp datas
|
|
void getutmp( struct connexion * conn, time_t * time )
|
|
{
|
|
struct utmp * utmp;
|
|
int ipv6;
|
|
int ipv4;
|
|
int x;
|
|
char str[6];
|
|
char strlog[128];
|
|
|
|
conn->host_ip[0]='\0';
|
|
conn->host_ipv6[0]='\0';
|
|
setutent();
|
|
while ( (utmp = getutent()) != NULL )
|
|
{
|
|
if ( utmp->ut_pid == conn->pid )
|
|
{
|
|
sprintf(conn->user, "%s", utmp->ut_user); //got user login
|
|
printlog(conn->user, DEBUG, 0);
|
|
sprintf(conn->hostname, "%s", utmp->ut_host); //got ip of origin
|
|
printlog(conn->hostname, DEBUG, 0);
|
|
if((utmp->ut_addr_v6[1] || utmp->ut_addr_v6[2] || utmp->ut_addr_v6[3]) == 0)
|
|
{
|
|
ipv4 = utmp->ut_addr_v6[0] & 0x00000000000000ff;
|
|
sprintf( str, "%d.", ipv4);
|
|
strcat(conn->host_ip, str);
|
|
ipv4 = (utmp->ut_addr_v6[0] & 0x000000000000ff00) >> 8;
|
|
sprintf( str, "%d.", ipv4);
|
|
strcat(conn->host_ip, str);
|
|
ipv4 = (utmp->ut_addr_v6[0] & 0x0000000000ff0000) >> 16;
|
|
sprintf( str, "%d.", ipv4);
|
|
strcat(conn->host_ip, str);
|
|
ipv4 = (utmp->ut_addr_v6[0] & 0x00000000ff000000) >> 24;
|
|
sprintf( str, "%d", ipv4);
|
|
strcat(conn->host_ip, str);
|
|
printlog(conn->host_ip, DEBUG, 0);
|
|
}else
|
|
{
|
|
for (x=0;x<4;x++)
|
|
{
|
|
ipv6 = utmp->ut_addr_v6[x] & 0x000000000000ffff;
|
|
sprintf( str, "%x:", ipv6);
|
|
strcat(conn->host_ipv6, str);
|
|
ipv6 = (utmp->ut_addr_v6[x] & 0x00000000ffff0000) >> 16;
|
|
sprintf( str, "%x:", ipv6);
|
|
strcat(conn->host_ipv6, str);
|
|
ipv6 = (utmp->ut_addr_v6[x] & 0x0000ffff00000000) >> 32;
|
|
sprintf( str, "%x:", ipv6);
|
|
strcat(conn->host_ipv6, str);
|
|
ipv6 = (utmp->ut_addr_v6[x] & 0xffff000000000000) >> 48;
|
|
sprintf( str, "%x:", ipv6);
|
|
strcat(conn->host_ipv6, str);
|
|
}
|
|
conn->host_ipv6[strlen(conn->host_ipv6)-1] = '\0';
|
|
printlog(conn->host_ipv6, DEBUG, 0);
|
|
}
|
|
*time = (time_t) utmp->ut_tv.tv_sec; //got connexion time
|
|
sprintf(strlog, "heure de connexion - %s", frtime( *time ) );
|
|
break;
|
|
}
|
|
}
|
|
endutent();
|
|
}
|
|
|
|
//replace null characters by space
|
|
int null2space( char str[] )
|
|
{
|
|
int flag =0;
|
|
int x = 0;
|
|
|
|
while ( flag == 0 )
|
|
{
|
|
if ( (int) str[x] == 0 )
|
|
{
|
|
if ( (int) str[x+1] != 0 )
|
|
{
|
|
str[x] = ' ';
|
|
}else
|
|
|
|
{
|
|
flag = 1;
|
|
}
|
|
}
|
|
x++;
|
|
}
|
|
return x-1 ;
|
|
}
|
|
|
|
// get the childs pids
|
|
int getpids(int pid, int exploded[])
|
|
{
|
|
FILE *fh;
|
|
char * pch;
|
|
char path[1024];
|
|
char str[4096];
|
|
char strlog[128];
|
|
char separator[] = " ";
|
|
int x = 0;
|
|
|
|
sprintf( path, "/proc/%d/task/%d/children", pid, pid);
|
|
sprintf(strlog, "process path: %s", path);
|
|
printlog(strlog, DEBUG,0);
|
|
if ((fh = fopen( path, "r")) == NULL)
|
|
{
|
|
printlog(path, WARNING, errno);
|
|
return -1;
|
|
}
|
|
if ( fgets( str, 40, fh ) != NULL )
|
|
{
|
|
pch = strtok( str, separator );
|
|
while( pch != NULL )
|
|
{
|
|
printlog(pch, DEBUG, 0);
|
|
exploded[x++] = atoi( pch );
|
|
pch = strtok( NULL , separator );
|
|
}
|
|
fclose(fh);
|
|
return x;
|
|
}else
|
|
{
|
|
fclose(fh);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// get informations on processus
|
|
int getprocinfo( struct connexion * conn )
|
|
{
|
|
FILE *fh1;
|
|
char child_path[128];
|
|
char str[1024];
|
|
int child_pid[10];
|
|
int flag = 0;
|
|
int r;
|
|
int level = 0;
|
|
int retval = 0;
|
|
//char tab[128];
|
|
time_t timet=0;
|
|
|
|
//get connexion time
|
|
getutmp( conn, &timet );
|
|
if ( timet == 0)
|
|
{
|
|
time( &timet );
|
|
}
|
|
sprintf( conn->date, "%s", frtime(timet) );
|
|
|
|
//get the pid of the last processus
|
|
while ( flag == 0)
|
|
{
|
|
r = getpids( conn->pid, child_pid );
|
|
if ( r != -1 )
|
|
{
|
|
level++;
|
|
//strcat(tab," ");
|
|
conn->pid = child_pid[0];
|
|
}else
|
|
{
|
|
flag = 1;
|
|
}
|
|
}
|
|
|
|
// get the command parameters
|
|
sprintf( child_path, "/proc/%d/cmdline", conn->pid );
|
|
if ( (fh1 = fopen( child_path, "r" )) == NULL)
|
|
{
|
|
printlog(child_path, WARNING, errno);
|
|
return 2;
|
|
}
|
|
fgets( str, 1024, fh1);
|
|
null2space( str );
|
|
sprintf(conn->cmdline, "%s", str);
|
|
fclose(fh1);
|
|
printlog(conn->cmdline, DEBUG, 0);
|
|
if ((strstr(conn->cmdline, "pam") || strstr(conn->cmdline, "net") || strstr(conn->cmdline, "accepted")) != 0)
|
|
{
|
|
printlog("comdline is pam or net or accepted", DEBUG, 0);
|
|
retval = -1;
|
|
}
|
|
// get the command name
|
|
sprintf( child_path, "/proc/%d/comm", conn->pid );
|
|
if ( (fh1= fopen(child_path, "r" )) == NULL)
|
|
{
|
|
printlog(child_path, WARNING, errno);
|
|
return 3;
|
|
}
|
|
fscanf( fh1, "%s", conn->cmd );
|
|
fclose(fh1);
|
|
|
|
return retval;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
FILE *fh = NULL;
|
|
FILE *fh1;
|
|
int n_ssh=10;
|
|
int id;
|
|
int pid;
|
|
int y=0;
|
|
int r=0;
|
|
int i;
|
|
int j;
|
|
int n;
|
|
int n_pid=0;
|
|
int start=1;
|
|
int childrens[n_ssh];
|
|
int pids[n_ssh];
|
|
int flag[n_ssh];
|
|
int rinfo;
|
|
int status;
|
|
char ip[42]="";
|
|
char str[1024];
|
|
char date[60];
|
|
time_t now = 0;
|
|
char * language;
|
|
char strlog[128];
|
|
// char * buff;
|
|
struct connexion conn;
|
|
struct connexion connexions[n_ssh];
|
|
|
|
struct notify_config ncc;
|
|
//char * ptr;
|
|
|
|
// Loading configuration
|
|
readconfig( &cfg );
|
|
|
|
//localizing
|
|
if ( (language = getenv("LANGUAGE")) != NULL)
|
|
{
|
|
strtok (language, ":");
|
|
}else if ( (language = getenv("LC_ALL")) == NULL )
|
|
{
|
|
language="";
|
|
}
|
|
setlocale(LC_ALL,language);
|
|
|
|
time( &now );
|
|
sprintf( date, "%s", frtime(now));
|
|
printlog("Démarrage de sshdetect", INFO, 0);
|
|
sprintf( str, "%s \"%s - %s: Démarrage de sshdetect\"", cfg.commande, cfg.hostname, date );
|
|
id=fork();
|
|
if(id == 0)
|
|
{
|
|
if (cfg.commande[0] != 0)
|
|
{
|
|
r = system( str );
|
|
}else
|
|
{
|
|
printlog("no command defined: no command launched", WARNING, 0);
|
|
}
|
|
exit(r);
|
|
}else if( id<0 )
|
|
{
|
|
sprintf(strlog, "erreur de création du fork: %s", str);
|
|
printlog(strlog, WARNING, 0);
|
|
}
|
|
init_config_watch( cfg.configfile, &ncc);
|
|
while (1)
|
|
{
|
|
memset(&conn, 0, sizeof(conn));
|
|
// get the sshd process ID (PID)
|
|
if ( (fh = fopen("/run/sshd.pid", "r" )) == NULL)
|
|
{
|
|
printlog("/run/sshd.pid", WARNING, errno);
|
|
return 1;
|
|
}
|
|
if ( fscanf(fh, "%i", &pid) == 0)
|
|
{
|
|
printlog("erreur fscanf: /run/sshd.pid", WARNING, 0 );
|
|
return 10;
|
|
}
|
|
fclose(fh);
|
|
sprintf(strlog, "%i", pid);
|
|
printlog(strlog, DEBUG, 0);
|
|
|
|
//get the list of children
|
|
if ( (n=getpids( pid, pids )) != -1)
|
|
{
|
|
for ( y=0; y<n; y++)
|
|
{
|
|
pid = pids[y];
|
|
sprintf( strlog, "pid %i", pid);
|
|
printlog(strlog, DEBUG, 0);
|
|
r = isinarray(pid, childrens, n_pid);
|
|
if( r == 0 )
|
|
{
|
|
conn.user[0]='\0';
|
|
conn.pid=pid;
|
|
rinfo = getprocinfo( &conn );
|
|
if( rinfo == 0 )
|
|
{
|
|
if (conn.host_ip[0] != '\0')
|
|
{
|
|
strcpy(ip,conn.host_ip);
|
|
}else if (conn.host_ipv6[0] != '\0')
|
|
{
|
|
strcpy(ip,conn.host_ipv6);
|
|
}
|
|
n_pid++;
|
|
childrens[n_pid] = pid;
|
|
flag[n_pid] = 1;
|
|
connexions[n_pid] = conn;
|
|
// date of connexion
|
|
if (conn.user[0] == '\0')
|
|
{
|
|
sprintf( str, "%s: %s \"%s: tunnel ouvert depuis %s pid: %d avec la commande: %s %s\"", conn.date, cfg.commande, cfg.hostname, ip, conn.pid, conn.cmd, conn.cmdline );
|
|
}else
|
|
{
|
|
sprintf( str, "%s: %s \"%s: %s s'est connecté depuis %s pid: %d avec la commande: %s %s\"", conn.date, cfg.commande, cfg.hostname, conn.user, ip, conn.pid, conn.cmd, conn.cmdline );
|
|
}
|
|
if ( start != 1 )
|
|
{
|
|
id=fork();
|
|
if(id > 0)
|
|
{
|
|
sprintf(strlog, "%s: Connexion de %s depuis %s commande: %s %s", conn.date, conn.user, ip, conn.cmd, conn.cmdline);
|
|
}else if (id < 0)
|
|
{
|
|
sprintf(strlog, "erreur de création du fork: %s", str);
|
|
printlog(strlog, WARNING, 0);
|
|
}else
|
|
{
|
|
if (cfg.commande[0] != 0)
|
|
{
|
|
printlog(str, INFO, 0);
|
|
r = system( str );
|
|
}else
|
|
{
|
|
printlog("no command defined: no command launched", WARNING, 0);
|
|
}
|
|
exit(r);
|
|
}
|
|
}else
|
|
{
|
|
sprintf(strlog, "%s: %s Connecté depuis %s - %s %s\n", conn.date, conn.user, ip, conn.cmd, conn.cmdline);
|
|
printlog(strlog, INFO, 0);
|
|
}
|
|
}else if (rinfo == -1)
|
|
{
|
|
|
|
sprintf(strlog, "%i => 2 pids : en cours de connexion\n", conn.pid);
|
|
printlog(strlog, INFO, 0);
|
|
}
|
|
}else
|
|
{
|
|
flag[r] = 1;
|
|
}
|
|
}
|
|
}
|
|
for(i=1;i<=n_pid;i++)
|
|
{
|
|
if (flag[i] == 0 )
|
|
{
|
|
time( &now );
|
|
sprintf( date, "%s", frtime(now) );
|
|
sprintf(strlog, "%s: Session %d de %s terminée\n", date, connexions[i].pid, connexions[i].user );
|
|
printlog(strlog, INFO, 0);
|
|
sprintf(strlog, "%s: %s - pid %d - Connexion de %s terminée", connexions[i].date, cfg.hostname, connexions[i].pid, connexions[i].user);
|
|
printlog(strlog, INFO, 0);
|
|
for( j=i; j<n_pid; j++ )
|
|
{
|
|
childrens[j] = childrens[j+1];
|
|
flag[j] = flag[j+1];
|
|
}
|
|
i--;
|
|
n_pid--;
|
|
}else
|
|
{
|
|
flag[i] = 0;
|
|
}
|
|
}
|
|
waitpid(-1, &status ,WNOHANG);
|
|
// sleep(1);
|
|
notify_config_change(&ncc, cfg.configfile);
|
|
start = 0;
|
|
}
|
|
return 0;
|
|
}
|