home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: alt.hackers
- Path: sparky!uunet!zaphod.mps.ohio-state.edu!sol.ctr.columbia.edu!csa!kibirev
- From: kibirev@csa.bu.edu (oleg kibirev)
- Subject: Debugging program on mailing machine
- Organization: Computer Science Department, Boston University, Boston, MA, USA
- Originator: kibirev@csa
- Message-ID: <1992Jul25.012355.11106@ctr.columbia.edu>
- Sender: news@ctr.columbia.edu (The Daily Lose)
- Approved: The King Of Flame Wars
- Date: Sat, 25 Jul 1992 01:23:55 GMT
- X-Posted-From: csa.bu.edu
- X-Posted-Through: sol.ctr.columbia.edu
- Lines: 687
-
-
- As I already mentioned in some ObHack, I am writing a program to allow access
- to Internet host (shell, maybe even line editor like ed(1)) via e-mail only
- (if you have nothing else). Anyway, the problem was that we have centrilized
- mailing host that delivers letters through NFS. It's incompatible with my host
- and can't execute programs compiled here. And, I can't login to mailing machine
- directly (/bin/nologin).
-
- I managed to compile a program for correct CPU by having sendmail run a shell
- script that does it. But, alas, gdb can't be used this way. I can't imagine my
- life w/o gdb, so here is a solution: I wrote a daemon that recieves all mail and
- looks for a special subject. It then accepts your connection to a well-known
- port and starts session similar to rsh but with tty on remote host (so that I can
- ^Z and ^C things).
-
- Not much fun? Well, you can do some interesting (but harmless) things with this
- program (Shhh... I didn't say I'll approve this, but for your information only :) )
-
- * If you have a cluster of hosts connected with [N,A]FS but with no centrilized
- mail delivery, you can use mhook @hostname to explore all of them this way.
-
- * mhookd niether makes an entry in /etc/[u,w]tmp nor chowns tty (I wish I could...).
- So, you can use it for stealth logins (last login time is not modified either).
-
- Programs below may need to be hacked a bit to work under UNIXes other than SunOS.
-
- Happy hacking,
- Oleg Kibirev
- ---------mhookd.c-------------------------------------------------------
-
- /***************************************************************************************
- * Connection daemon. This program scans incoming mail for subject "MailHook-Session" *
- * (case-sensative). When found, it triggers by binding a socket to port 7815 of local *
- * host and accepting connection from mhook. It then opens a pseudo-terminal, sets *
- * its parameters to match peers tty and runs user's login shell, passing it data from *
- * the socket. *
- * *
- * To activate, create the following ~/.forward on target host: *
- * \yourname,"|/your/home/dir/mhookd" *
- * *
- * Compiling: *
- * cc -o mhookd -O mhookd.c *
- * strip mhookd *
- * *
- * If you can't directly login to target host, but can put .forward to it (through *
- * NFS), you can place these lines in a shell script and have sendmail on target *
- * pipe the letter through this script. *
- ***************************************************************************************/
-
- char copyright[]="@(#) Copyleft 1992 Oleg Kibirev.\n";
- static char sccsid[]="@(#)mhookd.c 1.0 07/24/92";
-
- # include "stdio.h"
- # include "sys/types.h"
- # include "sys/socket.h"
- # include "sys/dir.h"
- # include "netinet/in.h"
- # include "string.h"
- # include "sys/time.h"
- # include "sys/resource.h"
- # include "sys/stat.h"
- # include "sys/ioctl.h"
- # include "fcntl.h"
- # include "signal.h"
- # include "netdb.h"
- # include "sys/wait.h"
- # include "pwd.h"
- # include "unistd.h"
-
- char *crypt(); /* Surprisingly, not found in SunOS 4.1 include files */
-
- # define ltrim(s) (s+strspn(s," \t\b\a\r\n"))
-
- static char from[128];
- static char buf[1024];
- static int kill0,kill1;
-
- extern int errno;
- extern char *sys_errlist[];
- extern int sys_nerr;
-
- int chkpty(pty) /* Selects files for dir. list function */
- struct direct *pty;
- {
- return(!strncmp(pty->d_name,"pty",3));
- }
-
- /* The most annoying problem to debug was processes hanging around after
- shell exits. Before long, I have figured out that read on a hang
- socket will wait forever rather than sending SIGPIPE or at least
- returning an error. So will read on a tty with closed pty counterpart.
- Time for (really) brute force approach: make sure that when a process
- dies, it takes relitives with it.
- */
-
- void die()
- {
- if(kill0)
- kill(kill0,SIGHUP); /* Soft kill-let an editor save text, etc. */
-
- if(kill1)
- kill(kill1,SIGHUP);
-
- exit(0);
- }
-
- /* Since we run detached, there is nobody to witness perror(). Here is a
- replacement that sends mail back to originator.
- */
-
- void p_error(msg)
- char *msg;
- {
- static char letter[]="Subject: MailHook: %s: %s\nTo: %s\n.\n";
- static int fd[2];
- int pid;
-
- if(!*from) /* We haven't figured out who is calling us yet... */
- die();
-
- sprintf(buf,letter,msg,(errno<=sys_nerr)?sys_errlist[errno]:"Unknown error",from);
- if(pipe(fd) || (pid=fork())<0)
- die(); /* Nothing else can be done to let them know */
-
- if(pid)
- {
- write(fd[1],buf,strlen(buf)); /* Pipe message to sendmail */
- close(fd[1]); /* This should force EOF on read */
- wait(NULL);
- }
- else
- {
- dup2(fd[0],0);
- dup2(open("/dev/null",O_WRONLY),1); /* Don't really care if this fails */
- dup2(1,2);
- execl("/usr/lib/sendmail","/usr/lib/sendmail","-t",NULL); /* Tell sendmail that target is in "To:" line */
- }
- die();
- }
-
- main(argc,argv)
- int argc;
- char **argv;
- {
- int true=1,i,s,master,slave,death=0;
- char *p;
- static struct rlimit rlp;
- static struct sockaddr_in myname={AF_INET,7815};
- static struct winsize win;
- struct passwd *user;
- struct direct **ptys;
-
- while(fgets(buf,1023,stdin))
- {
- p=ltrim(buf);
- if(!*p)
- break;
-
- if(!strncasecmp(p,"From:",5))
- {
- strncpy(from,ltrim(p+5),127);
- continue;
- }
-
- if(strncasecmp(p,"Subject:",8))
- continue;
- if(!strncmp(p=ltrim(p+8),"MailHook-Session",16))
- goto run;
-
- /* Sending mail with Subject: MailHook-Cleanup is a quick way to
- get rid of all processes on target host. Helps a lot when your
- pgm fork()s the system */
-
- if(!strncmp(p,"MailHook-Clean",14))
- {
- death=1;
- goto run;
- }
-
- break;
- }
- exit(0);
-
-
- run:;
- #ifdef xyzzy
- if(fork()) /* Let sendmail go */
- exit(0);
- #endif
- signal(SIGPIPE,die); /* redefine deadly signals to kill all mhookd's, not */
- signal(SIGSEGV,die); /* just one that triggered it */
- signal(SIGBUS,die);
-
- signal(SIGXCPU,SIG_IGN);
- signal(SIGQUIT,SIG_IGN);
- signal(SIGTSTP,SIG_IGN); /* No good for us to get stopped */
- signal(SIGTTOU,SIG_IGN);
-
- umask(022); /* Don't make files world-writable ... */
- getrlimit(RLIMIT_CORE,&rlp);
- rlp.rlim_cur=0;
- setrlimit(RLIMIT_CORE,&rlp); /* ... or dump core if we shall be murdered */
- while(*argv)
- *(argv++)=NULL;
-
- close(0);
- if((s=socket(PF_INET,SOCK_STREAM,0))<0)
- p_error("socket()");
-
- gethostname(buf,1024);
-
- /* I wonder how compatible would be just using localhost (127.0.0.1) */
- (myname.sin_addr).s_addr=**(unsigned long **)(gethostbyname(buf)->h_addr_list);
- if(bind(s,&myname,sizeof(myname)))
- p_error("bind()");
-
- if(listen(s,1))
- p_error("listen()");
-
- /* Won't wait forever for them to answer. The net/mhook could get hang */
-
- signal(SIGALRM,die);
- alarm(4*60);
- argc=sizeof(myname);
- while((i=accept(s,&myname,&argc))<0);
- close(s); /* Release "well-know port" - now ready for another connection */
- /* By the way, change port number to smth large, random and different from */
- /* 7518, so that you don't have clashes with other users on your host running */
- /* this program. */
-
- /* We are about to launch user's shell and don't know yet who is another party. */
- /* The first thing is to ask their password. mhook will get and send it. It */
- /* should be at most 8 chars and zero-padded. Rather hard to enter using standard */
- /* telnet (bug?) */
-
- /* When I give this program to hackers less experienced than you, I also include */
- /* explicit check for my own password */
-
- if(read(i,buf,8)!=8)
- exit(0);
-
- /* To make life harder to someone without a clue trying to figure out */
- /* What mhookd in my .forward does, I don't report any errors found */
- /* when checking password */
-
- alarm(0);
- setsockopt(i,SOL_SOCKET,SO_KEEPALIVE,&true,sizeof(int)); /* Close hang connection */
- setsockopt(i,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int)); /* Ok to bind 7518 again */
-
- if(!(user=getpwuid(getuid())))
- exit(0); /* I am confused */
-
- buf[8]='\0';
- buf[9]=user->pw_passwd[0]; /* Salt from my password */
- buf[10]=user->pw_passwd[1];
- buf[11]='\0';
- if(strcmp(crypt(buf,buf+9),user->pw_passwd))
- exit(0); /* Shame on you! */
-
- strcpy(buf,"Password"); /* Don't like my text password hanging around */
- if(death)
- kill(9,-1); /* Pity! */
-
- if((s=scandir("/dev/",&ptys,chkpty,NULL))<0) /* Get list of ptys */
- p_error("scandir");
-
- strcpy(buf,"/dev/");
- while(s--)
- {
- strcpy(buf+5,(*(ptys++))->d_name);
- if((master=open(buf,O_RDWR))<0)
- continue;
- buf[5]='t';
- if((slave=open(buf,O_RDWR))<0)
- {
- close(master); /* Shouldn't happen, but it does */
- continue;
- }
- goto pty_ok;
- }
- p_error("open pty");
-
- /* Set pseudo terminal like their own. Format of ioctl structures */
- /* appears to be incompatible across different UNIXes, so I use */
- /* /bin/stty that seams to work better. By now, the client have sent us */
- /* output of stty -g. Note that it's report doesn't include window size */
- /* so I had to transmit this structure alone. */
-
- pty_ok: if(read(i,&win,sizeof(win))!=sizeof(win))
- goto noparms;
-
-
- if(read(i,buf+64,1023-64)<0) /* the first few bytes of buf still hold the name */
- noparms: p_error("mhook: read tty parms"); /* of slave tty */
-
- if(p=strchr(buf+64,'\n'))
- *p='\0';
-
- if(!(kill0=fork()))
- {
- kill0=getppid(); /* Processes are ready to kill each other */
- dup2(slave,0); /* if anything happens */
- dup2(slave,1);
- dup2(slave,2); /* Not that stty doesn't need controlling tty */
- execl("/bin/stty","/bin/stty",buf+64,NULL); /* It just uses stdout */
- _exit(0);
- }
- else if(kill0>0) /* If stty fails, we can still live with it, so just */
- wait(NULL); /* go on if fork() is negative */
-
- signal(SIGCHLD,die); /* couldn't do it before because of above fork() */
-
-
- /* Server (mhookd) Socket Client (mhook)
- ____________ pair
- /Shell Output\____________\___ | ______
- \____________/ / \ | __\___/Output\__\___ ########
- / ^ \ \______ | _____/ / \______/ / # Your #
- / | \ ______ | _____ _____ # Local#
- _____ | ___________ / | \__/___/Input\___/___ # tty #
- /Shell\ | /Shell Input\__/___/ | \ \_____/ \ ########
- \_____/ | \___________/ \ |
- ^ | | | |
- | v \____ v |
- ######### ######### |
- # ttyXX ########### ptyXX # |
- ######### ######### |
-
- *** Flame on
- UNIX is not right requiring multitasking just to redirect I/O. 4 out of
- 5 processes here are doing nothing but reading one descriptor and writing another.
- Under DOS, the above would be done just by intercepting INT 21H. Supporting
- multiple processes is sure a good thing, but not enforcing them
- *** Flame off
-
- */
-
- if((kill0=fork())<0)
- {
- kill0=0;
- p_error("fork()");
- }
-
- if(!kill0)
- {
- kill0=getppid();
- setsid(); /* Try TIOCNOTTY ioctl with BSD-derived UNIXes that don't have this */
-
- /* Note that I have to open another descripor for slave although I already have one */
- /* Simple dup2 won't assign controlling tty */
-
- if((s=open(buf,O_RDWR))<0)
- p_error("open");
-
- close(master); /* Try to be compatible with in.telnetd and pass only 3 descriptors */
- close(i); /* to the shell */
-
- dup2(s,0);
- dup2(s,1);
- dup2(s,2);
- close(s);
-
- /* The nightmare: be sure to close the old descriptor only after the new */
- /* one is opened. If you don't, the process will get "device busy" (???) on*/
- /* open - that is despite the fact that our parent still has a handle */
-
- close(slave);
-
- ioctl(0,TIOCSWINSZ,&win);
-
- /* Make sure that they start in readable dir. *csh get's very confused if not */
- /* (csh: cwd: can't stat\nConnection closed by foreign host) */
-
- if(!access(user->pw_dir,R_OK|W_OK|X_OK) && chdir(user->pw_dir))
- chdir("/");
-
- for(i=0;i<32;i++)
- signal(i,SIG_DFL);
-
- /* Won't run /bin/nologin or /dev/null. If I don't know your shell, I will */
- /* choose my own. Add your local shells, like /bin/ksh, as another strcmp */
-
- if(!strcmp(user->pw_shell,"/bin/sh")||!strcmp(user->pw_shell,"/bin/csh")||!strcmp(user->pw_shell,"/bin/tcsh")||
- !strcmp(user->pw_shell,"/usr/local/bin/tcsh"))
- {
- strcpy(buf,user->pw_shell);
- *buf='-';
- execl(user->pw_shell,buf,"-f",NULL);
- }
-
- execl("/bin/csh","-sh","-f",NULL);
- execl("/bin/sh","-h","-f",NULL); /* ;? */
- die(); /* :( */
- }
-
- if((kill1=fork())<0)
- {
- kill1=0;
- die();
- }
-
- if(!kill1)
- {
- close(slave);
- kill1=getppid();
- while((s=read(i,buf,1024))>0 && write(master,buf,s)==s); /* socket->shell input */
- die();
- }
-
- close(slave);
- while((s=read(master,buf,1024))>0 && write(i,buf,s)==s); /* shell output->socket */
- die();
- }
-
- ---------mhook.c--------------------------------------------------
-
- /****************************************************************************
- * This program talks to mhookd. It sends mail to specified target and wait *
- * for deamon activated my sendmail to bind port 7815. After connection is *
- * established it's blindly copying output to the socket. Bthw, this means *
- * that it won't close connection by itself; server must do it. But doesn't *
- * rlogin? Also, since I use this program only to run *cc and gdb in emacs *
- * shell window, it's Ok for me -- add escape sequence if you need it. *
- * BUG: closed connection is detected only after you press Enter. *
- * *
- * Usage: mhook user@host to connect as this identity. User's .forward *
- * must pipe output through mhookd. If either (or both) part's of the *
- * address are omited, caller's uid and host are used instead. Use -k *
- * to quickly kill all your processes on target machine. You may have to *
- * to wait a while for connection, depending on your mailing system. *
- ****************************************************************************/
-
- char copyright[]="@(#) Copyleft 1992 Oleg Kibirev.\n";
- static char sccsid[]="@(#)mhook.c 1.0 07/24/92";
-
- # include "stdio.h"
- # include "errno.h"
- # include "netdb.h"
- # include "signal.h"
- # include "sys/types.h"
- # include "sys/socket.h"
- # include "sysexits.h"
- # include "sys/stat.h"
- # include "sys/ioctl.h"
- # include "fcntl.h"
- # include "netinet/in.h"
- # include "pwd.h"
- # include "string.h"
-
- extern int errno;
- static char me[128];
- static struct sockaddr_in target={AF_INET,7815};
- static struct hostent *host;
- static char buf[1024];
- static int killme=0;
- static struct sgttyb mode,patch;
- static struct winsize win;
-
- void die() /* The same brother-killing... */
- {
- ioctl(2,TIOCSETP,&mode);
- /* Well, the message below is correct in MOST cases */
- fputs("\nConnection closed by foreign host\n",stderr);
-
- if(killme)
- kill(killme,SIGKILL);
-
- _exit(0);
- }
-
- void myname()
- {
- struct passwd *mypass;
- if(!(mypass=getpwuid(getuid())))
- {
- perror("mhook: getpwuid");
- fputs("mhook: If you don't have an account, specify destination explicitely\n",stderr); /* :) */
- exit(1);
- }
- strcpy(me,mypass->pw_name);
- }
-
- void myhost()
- {
- if(gethostname(buf,1024) || !(host=gethostbyname(buf)))
- {
- fputs("mhook: Our host doesn't know itself\n",stderr);
- exit(1);
- }
- strcat(me,"@");
- strcat(me,buf);
- target.sin_addr=*(struct in_addr *)*(host->h_addr_list);
- }
-
- main(argc,argv)
- int argc;
- char **argv;
- {
- static char message[]="To: %s\nSubject: %s\n\n.\n";
- char *subject="MailHook-Session";
- int death=0,s,i,true=1;
- char *p;
- static int fd[2];
-
- /* Some awfull code to process options. Niether I care about this part being optimal */
- if(argc>3)
- goto syntax;
-
- if(argc==3)
- {
- if(!strcmp(argv[1],"-k"))
- {
- argv++;
- goto killmode;
- }
- if(!strcmp(argv[2],"-k"))
- {
- killmode: argc--;
- subject="MailHook-Cleanup";
- death=1;
- }
- else
- {
- syntax: fputs("Usage: mhook [-k] [user@hostname]\n",stderr);
- exit(1);
- }
- }
-
- if(argc!=2)
- {
- myname();
- myhost();
- }
- else
- {
- if(!(p=strchr(argv[1],'@')))
- {
- strcpy(me,argv[1]);
- myhost();
- }
- else if(*argv[1]=='@')
- {
- myname();
- sethost: if(!(host=gethostbyname(argv[1]+1)))
- {
- perror("mhook: host lookup");
- exit(1);
- }
- strcat(me,argv[1]);
- target.sin_addr=*(struct in_addr *)*(host->h_addr_list);
- }
- else
- {
- *p='\0';
- strcpy(me,argv[1]);
- *p='@';
- argv[1]=p;
- goto sethost;
- }
- }
-
- if(pipe(fd)<0)
- {
- perror("mhook: pipe");
- exit(1);
- }
-
- fputs("Sending mail to remote host\n",stdout);
- if((i=fork())<0)
- {
- perror("mhost: fork()");
- exit(1);
- }
-
- if(!i)
- {
- dup2(fd[0],0);
- execl("/usr/lib/sendmail","/usr/lib/sendmail","-t",NULL); /* Let remote host know I want to talk */
- exit(EX_UNAVAILABLE);
- }
- else
- {
- sprintf(buf,message,me,subject);
- write(fd[1],buf,strlen(buf));
- close(fd[1]);
- wait(&i);
- if((i!=EX_OK<<8)&&(i!=EX_TEMPFAIL<<8))
- {
- fputs("mhook: Can't send mail to your target\n",stderr);
- exit(1);
- }
- }
-
- ioctl(2,TIOCGETP,&mode);
-
- signal(SIGPIPE,die);
- signal(SIGBUS,die);
- signal(SIGSEGV,die);
- signal(SIGSYS,die);
- signal(SIGHUP,die);
-
- fputs("Waiting your site to respond\n",stdout);
- /* I have no idea how long it may take. On our system it's a few seconds */
- /* But if mail goes through some host currently down, I will wait for hours */
- /* days and centuries. Just ^C me to give up. */
-
- for(;;) /* eternity here */
- {
- if((s=socket(PF_INET,SOCK_STREAM,0))<0)
- {
- perror("mhost: socket");
- exit(1);
- }
-
- setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,&true,sizeof(int));
-
- if(connect(s,&target,sizeof(target))>=0)
- break;
-
- close(s); /* socket may be spoiled if connection is refused */
- if(errno!=ETIMEDOUT && errno!=ECONNREFUSED)
- {
- perror("mhook: connect");
- exit(1);
- }
- }
-
- fputs("Connection established\n",stdout);
- if(write(s,getpass("Password: "),8)!=8)
- {
- fputs("Unable to send password\n");
- exit(1);
- }
-
- if(death)
- exit(0);
-
- signal(SIGQUIT,SIG_IGN); /* ^C and ^Z remote host, not me! */
- signal(SIGTSTP,SIG_IGN);
- patch=mode; /* Let the server do all I/O processing */
- patch.sg_flags=(patch.sg_flags|RAW)&~ECHO;
-
-
- ioctl(2,TIOCGWINSZ,&win);
- if(write(s,&win,sizeof(win))!=sizeof(win))
- {
- perror("mhook: write()");
- die();
- }
-
- if((killme=fork())<0)
- perror("mhook:fork()");
-
- if(!killme)
- {
- killme=getppid();
- dup2(s,1);
- execl("/bin/stty","/bin/stty","-g",NULL);
- perror("mhook: execl()");
- die();
- }
-
- wait(NULL);
- ioctl(2,TIOCSETP,&patch);
-
- if((killme=fork())<0)
- {
- nofork: killme=0;
- perror("mhook: fork()");
- die();
- }
-
- if(!killme)
- {
- killme=getppid();
- while((i=read(0,buf,1024))>0 && write(s,buf,i)==i);
- die();
- }
-
- while((i=read(s,buf,1024))>0 && write(1,buf,i)==i);
- die();
- }
-
-
-
-
-
-