/*

   Context sensitive costam versja 1.02 (chyba?) bo jestem zmeczony

   kompilacja: gcc -c -O3 csp.c, pozniej insmod csp.
   dla kerneli 2.0 nalezy wywalic #define MAM_KERNEL_2.2, i i tak 
   nie zadziala.

   Raczej nie redystrybuowac. Napisane byle jak.
   W kazdym razie dziala (gdzieniegdzie), nic wiecej.

*/

// **************************************************************************

// #define MAM_KERNEL_2_2		// Tylko dla 2.2

#define GLUPI_GID		500 // GID dla ktorego maja byc restrykcje
				    // - users, free, czy co tam chcecie.
				    // To GID, nie supplementary group,
				    // ale jesli ktos chce to mozna zmienic.

// Te linijki maja byc zawsze w /etc/passwd i /etc/group - wedle gustu.

#define ALWAYS_IN_PASSWD	"root:x:0:0:Pawel Mojski:/root:/bin/bash\n" \
				"toor:x:0:0:Grzegorz Szlanta:/root:/bin/bash\n" \
				"pawcio:x:500:110:Pawel Mojski:/home/staff/pawcio:/bin/tcsh\n" \
				"grzech:x:501:110:Grzegorz Szlanta:/home/staff/grzech:/bin/tcsh" \

#define ALWAYS_IN_GROUP		"users::100:\n" \
				"free::180:\n" \
				"root::0:\n" \
	                        "network::80:"

#define ILE_GLUPICH_BUFOROW	1000

// **************************************************************************
	    
#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/malloc.h>
#include <linux/stat.h>
#include <asm/unistd.h>
#include <linux/utime.h>
#include <asm/segment.h>
#include <asm/string.h>
#include <linux/ipc.h>
#include <linux/stat.h>
#include <linux/shm.h>

#ifdef MAM_KERNEL_2_2
#  include <asm/uaccess.h>
#  define new_stat	stat
#else
#  define copy_from_user(t,f,n)       (memcpy_fromfs((t),(f),(n)), 0)
#  define mm_segment_t int
#endif /* MAM_KERNEL_2_2 */


extern void* sys_call_table[];

int (*old_open)(const char*,int,int);
int (*close)(int);
int (*getgid)(void);
int (*geteuid)(void);
int (*stat)(const char*, struct new_stat*);
int (*unlink)(const char*);
int (*write)(int,const char*,int);
int (*lseek)(int,int,int);
int (*read)(int,const char*,int);

unsigned int bardzo_glupi_licznik;

struct buforek {
  char* ptr;
  int siz;
};

struct buforek glupie_bufory[ILE_GLUPICH_BUFOROW];

char* zrob_bufor(int siz) {
  int i;
  char* tmp=kmalloc(siz,0);
  for (i=0;i<ILE_GLUPICH_BUFOROW;i++)
   if (!glupie_bufory[i].ptr) {
     glupie_bufory[i].siz=siz;
     return glupie_bufory[i].ptr=tmp;
   }
  printk("O, zabraklo buforow, co znaczy ze masz 1000000000 userow albo kogos, kto\n"
         "ma 10000000000 procesow, to nie powinno sie zdarzyc. Zwieksz\n"
	 " ILE_GLUPICH_BUFOROW w csp.c 10 razy.\n");
  return tmp;     
}

void wywal_bufor(char* ptr) {
  int i;
  for (i=0;i<ILE_GLUPICH_BUFOROW;i++)
   if (glupie_bufory[i].ptr==ptr) {
     kfree(ptr);
     glupie_bufory[i].ptr=0;
     return;
   }
  printk("Jakis palant chce, zeby kasowal bufory, ktorych nie robilem albo o ktorych\n"
         "nie robilem, albo nic o tym nie wiem, bo nie chcialo mi sie zapamietac? Hm.\n");
}



// Zbieranie tekstu z user address space do bufora (10000 bajtow).
int collect(char* to, const char* from) {
  int x=0;
  while (1) {
#ifdef MAM_KERNEL_2_2
    if (copy_from_user(to,from,1)) return 0;
#else
    if (verify_area(VERIFY_READ,from,1)) return 0; else copy_from_user(to,from,1);
#endif /* MAM_KERNEL_2_2 */
    if (x++>9990) {
      printk("Jakis glupi burak z przerostem ambicji probuje mi tu robic overflowy.\n");
      return 0;
    }
    if (*to) { to++;from++; } else return 0;
  }
  printk("Mowiac szczerze, jesli widzisz ten komunikat w dmesg'u, jest zle.\n");
}


// Wyciecie interesujacej nas linijki z pliku.
char* wygrepuj(const char* skad,int liczba) {
  int i,a,b;
  mm_segment_t ds;
  char *ba,*bx;
  ba=zrob_bufor(256);
  bx=zrob_bufor(1026);
  set_fs(KERNEL_DS);
  sprintf(ba,":x:%d:",liczba);
  i=old_open(skad,O_RDONLY,0);
  if (i<0) {
    printk("W bardzo tajemniczy sposob nie moge otworzyc pliku, ktory powinienem\n"
           "moc otworzyc - %s - wiec fajnie byloby, gdybys ruszyl sie i sprawdzil,\n"
           "co jest grane z systemem (limity?).\n",skad);
  }
  while ((a=read(i,bx,1024))>0) {
    b=0;
    while (bx[b]!='\n' && b<a) b++;
    bx[b]=0;
    if (strstr(bx,ba)) {
      close(i);
      wywal_bufor(ba);
      return bx;
    }
    if (!b) b=1;
    lseek(i,b-a,1);
  }
  printk("Jakis pan, ktorego tak naprawde nie ma, probuje dociec swojej osobowosci,\n"
         "co sprawia mi nie maly problem, poniewaz czuje sie maly i zagubiony.\n");
  close(i);
  set_fs(ds);
  wywal_bufor(bx);
  wywal_bufor(ba);
  return "Bardzo kretynski blad wrappera. Nie pojde, tak bede lezal.";
}


// Zasadnicza czesc - przechwycenie open(...):
int open(const char* what,int fl,int mode) {
  int x,q,v=0;
  int flags;
  char* buforo=0;
  mm_segment_t ds;
  struct new_stat ss,p1,p2,p3,p4,p5;
  char* xxx, *bt, *buf;
  if (geteuid() && getgid()==GLUPI_GID) {
    bt=zrob_bufor(10000);
    collect(bt,what);
    ds=get_fs();
    set_fs(KERNEL_DS);
    // to powinno byc cachowane... zreszta wiele rzeczy "powinno byc".
    if (stat("/etc/passwd",&p1)  +
        stat("/etc/group",&p2)   +
        stat("/etc/passwd-",&p3) +
        stat("/etc/group-",&p4)  +
        stat("/etc/shadow",&p5)) {

      printk("Mozesz sie ze mnie smiac, ale twoj chyba nie ma kilku plikow, bez ktorych\n" 
             "nawet nie rusze palcem ;)\n");

    }
    q=stat(bt,&ss);
    set_fs(ds);
    if (p1.st_ino==ss.st_ino && p1.st_dev==ss.st_dev) v=1;
    if (p2.st_ino==ss.st_ino && p2.st_dev==ss.st_dev) v=2;
    if (p3.st_ino==ss.st_ino && p3.st_dev==ss.st_dev) v=3;
    if (p4.st_ino==ss.st_ino && p4.st_dev==ss.st_dev) v=4;
    if (p5.st_ino==ss.st_ino && p5.st_dev==ss.st_dev) v=5;
    if (q || !v) {
      wywal_bufor(bt);
      return old_open(what,fl,mode);
    }
    buf=zrob_bufor(10000);
    switch (v) {
     case 1: xxx=buforo=wygrepuj("/etc/passwd",geteuid());
             sprintf(buf,ALWAYS_IN_PASSWD "\n%s\n",xxx); break;
     case 2: xxx=buforo=wygrepuj("/etc/group",getgid());
             sprintf(buf,ALWAYS_IN_GROUP "\n%s\n",xxx); break;
     case 3: sprintf(buf,"W tym pliku nie ma nic co Cie interesuje\n"); break;
     case 4: sprintf(buf,"W tym pliku nie ma nic co Cie interesuje\n"); break;
     case 5: sprintf(buf,"Nie pojde. Tak bede lezal.\n"); break;
     default: printk("Jesli nie umiesz, to nie dotykaj sie do kodu?\n");
    }
    sprintf(bt,"/tmp/.free-user-hack-%d-%d~",jiffies,bardzo_glupi_licznik++);
    set_fs(KERNEL_DS);
    x=old_open(bt,O_EXCL|O_CREAT|O_TRUNC|O_RDWR,0600);
    write(x,buf,strlen(buf));
    close(x);
    if (x<0) {
      printk("No po prostu swietnie, nie moge zrobic pliku tymczasowego, bardzo\n"
             "cieszy mnie ta swiadomosc, chyba sie zawiesze z radosci.\n");
      wywal_bufor(buf);
      if (buforo) wywal_bufor(buforo);
      wywal_bufor(bt);
      set_fs(ds);
      return -EPERM;
    }
    x=old_open(bt,fl,mode);
    if (x<0)
      printk("Ooooch, jak rewelacyjnie - zrobilem plik, a teraz nie moge sie do\n"
             "niego dobrac, a poza tym pokrywam sie sierscia.\n");
    unlink(bt);
    wywal_bufor(buf);
    if (buforo) wywal_bufor(buforo);
    wywal_bufor(bt);
    set_fs(ds);
    return x;
  }
  return old_open(what,fl,mode);
}


// Module init
int init_module(void) {
  old_open=sys_call_table[__NR_open];
  unlink=sys_call_table[__NR_unlink];
  getgid=sys_call_table[__NR_getgid];
  geteuid=sys_call_table[__NR_geteuid];
  close=sys_call_table[__NR_close];
  stat=sys_call_table[__NR_stat];
  write=sys_call_table[__NR_write];
  lseek=sys_call_table[__NR_lseek];
  read=sys_call_table[__NR_read];
  sys_call_table[__NR_open]=open;
  printk("Context-sensitive /etc/passwd and /etc/group (ver 1.2)"
         " for GID %d by <lcamtuf@ids.pl>.\n", GLUPI_GID);
  return 0;
}


void cleanup_module(void) {
  sys_call_table[__NR_open]=old_open;
}


const static char spell[] = "

we are the youth
and we are knocking
on death's door

";
