sources-request@mirror.UUCP (02/20/87)
Submitted by: mit-eddie!cdx39!jc (John Chambers) Mod.sources: Volume 8, Issue 73 Archive-name: uutty/Part02 [ Sorry for the mixed-mode address on the previous article... --r$ ] : This is a shar archive. Extract with sh, not csh echo file: lastfield.c cat > lastfield.c << '\!Funky\!Stuff\!' /* ** Find the last field of a string. */ char * lastfield(p,c) char *p; /* Null-terminated string to scan */ int c; /* Separator char, usually '/' */ { char *r; r = p; while (*p) /* Find the last field of the name */ if (*p++ == c) r = p; return r; } \!Funky\!Stuff\! echo file: lockname.c cat > lockname.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Build name for a lockfile. */ lockname() { char *dp, *lp, *rp; D5("lockname(\"%s\")",device); devfld = dp = lastfield(device,'/'); D4("devfld=\"%s\" device=\"%s\"",devfld,device); lp = lockfile; /* Place to build lockfile name */ rp = lockroot; /* Place to build lockfile name */ while (*rp) *lp++ = *rp++; /* Copy root to lockname */ while (*dp) *lp++ = *dp++; /* Append the device name */ *lp = 0; D4("lockname:lockfile=\"%s\"",lockfile); } \!Funky\!Stuff\! echo file: lockup.c cat > lockup.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** The lockfile exists; hang until it disappears. */ lockup() { int n; n = 0; do { if ((n==0 && debug>=2) || debug>=3) P("%s Lockfile \"%s\" exists.",getime(),lockfile); if (lsleep > 0) sleep(lsleep); ++n; } while (stat(lockfile,&status) >= 0); D3("locked: Lockfile \"%s\" freed.",lockfile); if (debug>=2) P("%s Lockfile \"%s\" gone.",getime(),lockfile); return n; } \!Funky\!Stuff\! echo file: lockwait.c cat > lockwait.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Wait on a lockfile. */ lockwait() { unsigned n; D5("lockwait()"); if (lockfile[0] == 0) lockname(); D4("lockwait:lockfile=\"%s\"",lockfile); n = 0; if (stat(lockfile,&status) >= 0) { D4("%s Lockfile \"%s\" exists.",getime(),lockfile); n = lockup(); D4("%s Lockfile \"%s\" gone.",getime(),lockfile); } if (n) { /* Port may be screwed up */ opendev(); /* Close and reopen it */ restdev(); /* Get it into proper state */ } D4("lockwait:Returned after %d waits.",n); return n; } \!Funky\!Stuff\! echo file: main.c cat > main.c << '\!Funky\!Stuff\!' #include "uutty.h" #include <signal.h> static char id[] = "@(#)RELEASE: 1.0 Sep 20 1986 ~jc/uutty"; /* ** uutty device [option]... ** ** This is John Chambers' own personal serial-port daemon, ** to watch a port, determine what sort of critter is at the ** other end of the line, and engage it in a semi-intelligent ** dialog. The primary goal of this version, uutty, is to ** make it possible to use direct links (null modems) freely in ** both directions, without the problems caused by the getty(1) ** program. In particular, it allows the use of the uucp package ** (including uux and cu) in both directions across a line. ** ** Another thing uutty is good for is using a modem in either ** without needing any special actions. ** ** Uutty is a thinly-disguised "state machine" whose basic ** behavior is determined by a global "state variable" ss ** and a string of input characters. The state variable is ** simply an integer that encodes the last action performed. ** An input routine is called, and when it returns, the input ** is examined to determine the program's next action. Normally, ** this action is to produce an output string, change the state ** variable, and wait for the next input. ** ** The major intent of this program is to do a login interview, ** but to ignore anyone at the other end of the line that acts ** like it's trying to log us in. If getty or this program is ** at the other end, the result will be silence until someone ** sends a CR or LF (which elicits "login:") or something that ** is acceptable as a login id. Note in particular that input ** containing colons and spaces will be ignored. ** ** The other major intent is to cooperate with any software that ** sets the uucp lockfiles ("/usr/spool/uucp/LCK..<device>"), by ** checking for such lockfiles frequently, and sleeping any time ** they are found. This program never creates a lockfile; it just ** sleeps when someone else creates one. Thus, this program may ** be left running on a port used by uucp, cu, or other such serial- ** port users. There is a slight possibility that this program ** may eat up part of the first response from the port, but that ** is rarely much of a problem. One or two CRs will usually suffice ** to elicit a "gin:" prompt from the other end. ** ** A third, minor intent is to recognize overly-intelligent modems, ** and refuse to become engaged in a conversations with them. This ** is done primarily by rejecting input that contains whitespace or ** special characters, or which contains only upper-case letters. ** This will handle the error messages produced by most commercial ** modems. However, users may wish to add to the code that examines ** login ids and passwords, and add more checks for specific modems. ** ** You may notice that this program is full of Dx(...) debug calls. ** These are defined in dbg.h, and may be suppressed fairly easily. ** Most C compilers will react to, for instance: ** #define D9 if(0) dmsg ** by generating no code at all. You might think this would make ** your program smaller and faster, and you'd be partly right. It ** will definitely be smaller, but probably not much faster. Anyhow, ** the speed of this program isn't much of an issue; if it gets into ** a feedback loop with a modem, you don't much care how efficiently ** it brings your system to its knees! It's probably a wiser idea ** to leave most of the diagnostic junk in. You'll be surprised at ** how useful it can be to have that stuff in an audit trail. ** ** Some of the options recognized are: ** ** -b<n> use a baud rate of <n>. Final "00" may be omitted. ** ** -c<n> slow down by counting to <n> between output chunks. ** ** -d<n> debug level <n>; default is -d1. ** ** -e<s> exit string <s> to send when terminating. ** ** -f fork a subprocess for starting shells. ** ** -i<s> initialization string <s> is sent whenever it is ** decided that the other end is insane or jabbering. ** ** -l create uucp lockfiles before starting a shell; delete ** them afterwards. This option implies the -f option. ** ** -n<s> nudge string <s> is sent when this program wants a response. ** ** -p<f> port to use, where <f> is usually /dev/tty??. This is ** the same as using <f> without any prefix. ** ** -s<n> sleep <n> seconds between output chunks. ** ** -x<n> debug level <n>. ** ** To be responsible, I should repeat the warning stated elsewhere: ** with debug level -d2 or higher, uutty writes everything it sees, ** including unencrypted login ids and passwords, to its diagnostic ** output stream (audit trail). This makes it a Trojan Horse of the ** first kind. If you are security-conscious, you might take steps ** to hide the audit trail, and purge it frequently. ** ** In any case, the audit trail grows without bounds, so you will want ** to take steps to keep it within bounds. The easiest way is with an ** entry in crontab that starts up a daily cleanup script (/etc/cleanup ** is a real good name for it). This script can move the uutty audit ** trails to a different name, to produce one level of backup. */ main(ac,av) char**av; { int i, r; time(&currtime); /* Note the start time */ progname = av[0]; /* For debug output */ prgnam = lastfield(progname,'/'); D4("prgnam=\"%s\" progname=\"%s\"",prgnam,progname); euid = geteuid(); egid = getegid(); ruid = getuid(); rgid = getgid(); D3("euid=%d egid=%d ruid=%d rgid=%d",euid,egid,ruid,rgid); args(ac,av); /* Process command-line args */ if (debug) { /* Generate first entries in audit trail */ P("+--------------------------------------------------------------------+"); P("%s %s started.",getime(),progname); } timeout = 255; /* Use max timeout on reads */ sig(); /* Intercept all the signals */ slowfl = (count > 0) || (slow > 0); opendev(); /* Open the port which we are to use */ if (dev != 0) {close(0); i = dup(dev); D3("File %d=\"%s\"",i,device);} if (dev != 1) {close(1); i = dup(dev); D3("File %d=\"%s\"",i,device);} lockwait(); /* First check to see if it's busy */ /* ** The code to handle raw, 8-bit communication get a bit weird from ** system to system. Here, we try to trap the initial state of the ** port, so that when we die, we can first restore it. */ #ifdef SYS5 if (raw && isatty(dev)) { /* We will want to restore its initial state */ D5("main:before ioctl(%d,%d,%06lX)",dev,TCGETA,&trminit); i = ioctl(dev,TCGETA,&trminit); /* Note initial state of terminal */ D5("main: after ioctl(%d,%d,%06lX)=%d",dev,TCGETA,&trminit,i); D7("main: %d:\tcflag=%06o",dev,trminit.c_cflag); D7("main: %d:\tiflag=%06o",dev,trminit.c_iflag); D7("main: %d:\tlflag=%06o",dev,trminit.c_lflag); D7("main: %d:\toflag=%06o",dev,trminit.c_oflag); termfl = 1; /* crlf = "\n\r"; ** Not needed on Unix systems */ } #endif restdev(); /* Get port to desired (raw, very public) state */ #ifdef SYS5 /* ** The following functions suffice to find, update, and write ** the appropriate entry in the /etc/utmp file, which on SYS/V ** is the record of logged-in users. This may well have to be ** changed for other systems. The hope here is that this is a ** reasonable way to modularize the job, though the names may ** not be ideal... */ D8("main:before findutmp()"); up = findutmp(); /* Try to locate our /etc/utmp entry */ D7("main: after findutmp()=%06lX",up); D8("main:before fillutmp(\"%s\",%X,\"%s\",%d)",prgnam,(char*)0,devfld,LOGIN_PROCESS); fillutmp(prgnam,(char*)0,devfld,LOGIN_PROCESS); D7("main: after fillutmp()"); D8("pswd:before pututline(%06lX)",up); pututline(up); /* Put modified line into /etc/utmp */ D7("pswd: after pututline(%06lX)",up); #endif r = talk(); /* Attempt a conversation */ die(r); /* If we should ever decide to stop */ return 0; /* Paranoia! */ } \!Funky\!Stuff\! echo file: makeraw.c cat > makeraw.c << '\!Funky\!Stuff\!' /* Tell the device driver to do raw I/O on the device. * Unfortunately, this is done in different ways on different * brands of Unix. Make sure that your system is included * somewhere in the following list. */ #include <dbg.h> #ifdef BERK # include <sgtty.h> int ldisc = NETLDISC; struct sgttyb sgttyb; #endif #ifdef PCIX # include <termio.h> struct termio termio; #endif #ifdef SYS5 # include <termio.h> struct termio termio; #endif extern int baudmask; /* CBAUD mask, if baud rate specified */ extern int errno; int flowcontrol = 1; uint timeout = 100; /* Timeout in 0.1 second quanta */ makeraw(fn) int fn; /* File number */ { int i; D5("makeraw(%d)",fn); errno = 0; #ifdef SYS5 i = ioctl(fn,TCGETA,&termio); D4("makeraw: %d:\tcflag=%06o [old]",fn,termio.c_cflag); D4("makeraw: %d:\tiflag=%06o [old]",fn,termio.c_iflag); D4("makeraw: %d:\tlflag=%06o [old]",fn,termio.c_lflag); D4("makeraw: %d:\toflag=%06o [old]",fn,termio.c_oflag); D5("makeraw: ioctl(fn=%d,TCGETA=%d,&termio=%08X)=%d",fn,TCGETA,&termio,i); if (baudmask) /* Wipe out all of cflag but speed */ termio.c_cflag = baudmask; else termio.c_cflag &= CBAUD; termio.c_cflag |= CS8 | CREAD | HUPCL; termio.c_iflag = flowcontrol? (IXON | IXANY | IXOFF): 0; termio.c_lflag = 0; termio.c_oflag = 0; termio.c_cc[4] = 0; /* Number of bytes to buffer up */ termio.c_cc[5] = timeout; /* Timeout in 0.1 sec units */ D4("makeraw: %d:\tcflag=%06o [new]",fn,termio.c_cflag); D4("makeraw: %d:\tiflag=%06o [new]",fn,termio.c_iflag); D4("makeraw: %d:\tlflag=%06o [new]",fn,termio.c_lflag); D4("makeraw: %d:\toflag=%06o [new]",fn,termio.c_oflag); i = ioctl(fn,TCSETA,&termio); #endif #ifdef SYS3 i = ioctl(fn,TCGETA,&termio); termio.c_iflag |= IGNCR | IXON | IXANY | IXOFF; termio.c_lflag = 0; D5("makeraw: \tcflag=%06o",termio.c_cflag); D5("makeraw: \tiflag=%06o",termio.c_iflag); D5("makeraw: \tlflag=%06o",termio.c_lflag); D5("makeraw: \toflag=%06o",termio.c_oflag); i = ioctl(fn,TCSETA,&termio); #endif #ifdef BERK /* Berkeley Unix has its own conventions */ i = ioctl(fn,TIOCGETP,&sgttyb); sgttyb.sg_flags = RAW | TANDEM; ioctl(fn,TIOCSETP,&sgttyb); if (errno) { E("makeraw: Can't do raw i/o on \"%s\"",fnnam); exit(-1); } #endif D5("makeraw: %d is now raw",fn); } \!Funky\!Stuff\! echo file: makesane.c cat > makesane.c << '\!Funky\!Stuff\!' /* Tell the device driver to do normal I/O on the device. * Unfortunately, this is done in different ways on different * brands of Unix. Make sure that your system is included * somewhere in the following list. */ #include <dbg.h> #ifdef BERK # include <sgtty.h> int ldisc = NETLDISC; struct sgttyb sgttyb; #endif #ifdef PCIX # include <termio.h> struct termio termio; #endif #ifdef SYS5 # include <termio.h> static struct termio trmstat; #endif extern int errno; extern int baudmask; /* CBAUD mask if baud rate specified */ makesane(fn) int fn; /* File number */ { int i; D5("makesane(%d)",fn); errno = 0; #ifdef SYS5 i = ioctl(fn,TCGETA,&trmstat); if (debug >= 4) { P("makesane: %d:\tcflag=%06o [old]",fn,trmstat.c_cflag); P("makesane: %d:\tiflag=%06o [old]",fn,trmstat.c_iflag); P("makesane: %d:\tlflag=%06o [old]",fn,trmstat.c_lflag); P("makesane: %d:\toflag=%06o [old]",fn,trmstat.c_oflag); } D5("makesane: ioctl(fn=%d,TCGETA=%d,&trmstat=%08X)=%d",fn,TCGETA,&trmstat,i); /* ** The following was given by "stty -a" on one SYS/V machine: ** speed 9600 baud; line = 2; intr = ^c; quit = ^\; erase = ^H; kill = ^x; eof = ^d; eol = ^@;susp = ^z;dsus = ^y ** -parenb -parodd cs8 -cstopb -hupcl cread -clocal -tostop ** -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc ** ixon -ixany -ixoff ** isig icanon -xcase echo echoe echok -echonl -noflsh ** opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3 speed 9600 baud; line = 2; intr = ^c; quit = ^\; erase = DEL; kill = ^x; eof = ^d; eol = ^@;susp = ^z;dsus = ^y -parenb -parodd cs8 -cstopb -hupcl cread -clocal -tostop -ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc ixon -ixany -ixoff isig icanon -xcase echo echoe echok -echonl -noflsh opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3 */ trmstat.c_cflag &= CBAUD; /* Save the speed */ if (baudmask) { D4("makesane: baudmask=0%o",baudmask); trmstat.c_cflag = baudmask; /* Set a different speed */ } trmstat.c_cflag |= HUPCL | CREAD | CS8; trmstat.c_iflag = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; trmstat.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK; trmstat.c_oflag = OPOST | ONLCR | TAB3; trmstat.c_cc[0] = 0x03; /* INTR = ^C */ trmstat.c_cc[1] = 0x1C; /* QUIT = ^\ */ trmstat.c_cc[2] = 0x10; /* ERASE= ^H [BS] */ trmstat.c_cc[3] = 0x18; /* KILL = ^X */ trmstat.c_cc[4] = 0x04; /* EOF = ^D */ trmstat.c_cc[5] = 0; /* EOL char */ trmstat.c_cc[6] = 0; /* EOL2 char */ /* trmstat.c_cc[7] = __; ** Reserved */ trmstat.c_cc[8] = 0x1A; /* SUSP = ^Z */ trmstat.c_cc[9] = 0x19; /* SUSP = ^Y */ if (debug >= 5) { P("makesane: %d:\tcflag=%06o [new]",fn,trmstat.c_cflag); P("makesane: %d:\tiflag=%06o [new]",fn,trmstat.c_iflag); P("makesane: %d:\tlflag=%06o [new]",fn,trmstat.c_lflag); P("makesane: %d:\toflag=%06o [new]",fn,trmstat.c_oflag); Hexdnm(&trmstat.c_cc[0],NCC,"c_cc:"); } i = ioctl(fn,TCSETA,&trmstat); #endif #ifdef SYS3 /* Note: Tested only on PC/IX */ i = ioctl(fn,TCGETA,&trmstat); trmstat.c_cflag |= HUPCL | PARENB | CREAD | CS7; trmstat.c_iflag = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; trmstat.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK; trmstat.c_oflag = OPOST | ONLCR | TAB3; D5("makesane: \tcflag=%06o",trmstat.c_cflag); D5("makesane: \tiflag=%06o",trmstat.c_iflag); D5("makesane: \tlflag=%06o",trmstat.c_lflag); D5("makesane: \toflag=%06o",trmstat.c_oflag); i = ioctl(fn,TCSETA,&trmstat); #endif #ifdef BERK /* Note: Tested only on 4.2 */ i = ioctl(fn,TIOCGETP,&sgttyb); sgttyb.sg_flags = SANE | TANDEM; ioctl(fn,TIOCSETP,&sgttyb); if (errno) { E("makesane: Can't do sane i/o on \"%s\"",fnnam); exit(-1); } #endif D5("makesane: %d is now sane.",fn); } \!Funky\!Stuff\! echo file: nextbyte.c cat > nextbyte.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Extract one byte from the port's input buffer, triggering ** a read if necessary. If no data is available within the ** timeout limit, -1 is returned. All other return values ** should be small positive integers, in [1,255]. */ nextbyte() { int i; D9("nextbyte()"); loop: if (ibfa >= ibfz) { errno = 0; lockwait(); D9("nextbyte:before read(%d,%06lX,%d)",dev,ibuf,IBUF); if ((i = read(dev,ibuf,IBUF)) <= 0) { D9("nextbyte: read(%d,%06lX,%d)=%d\t[errno=%d]" ,dev,ibuf,IBUF,i,errno); Fail; } D8("nextbyte: after read(%d,%06lX,%d)=%d\t[errno=%d]" ,dev,ibuf,IBUF,i,errno); if (debug >= 4) { dbgtimep = getime(); Hexdnm(ibuf,i,"Read:"); } if (echoing) { write(dev,ibuf,i); } ibfa = ibuf; ibfz = ibuf + i; *ibfz = '\0'; } i = ASCII(*ibfa++); D9("nextbyte()=%02X='%c'",i,dsp(i)); if (i == 0) Loop; /* Don't return nulls */ return i; fail: D8("nextbyte()=-1 [FAILURE]"); return -1; } \!Funky\!Stuff\! echo file: opendev.c cat > opendev.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Close and re-open the port. */ opendev() { D6("opendev()"); if (dev >= 0) close(dev); if (device) { if ((dev = open(device,2)) < 0) { E("FATAL ERROR\n%s Can't open \"%s\"\t[errno=%d]",getime(),device,errno); die(1); } if (debug) P("%s Opened dev=%d=\"%s\".",getime(),dev,device); } return dev; } \!Funky\!Stuff\! echo file: option.c cat > option.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Process an option string. Note that we get ** a pointer to the initial char, usually '-'. */ extern int baudmask; /* CBAUD mask, if baud rate specified */ extern int baudrate; /* Actual baud rate */ option(op) char*op; { int n; D4("option(\"%s\")",op); switch (op[1]) { /* Which option? */ default: sprintf(stderr,"Unknown option \"%s\" ignored.\n",op); break; case 'B': /* Set baud rate */ case 'b': n = sscanf(op+2,"%u",&baudrate); if (n < 1) baudrate = 1; D3("baudrate=%u\n",baudrate); switch (baudrate) { case 12: case 1200: baudmask = B1200; break; case 24: case 2400: baudmask = B2400; break; case 48: case 4800: baudmask = B4800; break; case 96: case 9600: baudmask = B9600; break; default: E("Can't handle baud rate of %d",baudrate); baudrate = baudmask = 0; } break; case 'C': /* Count between I/O operations */ case 'c': n = sscanf(op+2,"%u",&count); if (n < 1) count = 1; D3("count=%u\n",count); slowfl = count || slow; break; case 'D': /* Set debug level */ case 'd': case 'X': /* Set debug level */ case 'x': n = sscanf(op+2,"%u",&debug); if (n < 1) debug = 1; D3("debug=%d\n",debug); break; case 'E': case 'e': /* Exit message */ if (op[2]) { /* -e"msg" is exit message */ m_exit = op + 2; D3("m_exit=\"%s\"",m_exit); } else { /* -e turns on echoing */ echoing = echofl = 1; D3("echofl=%d",echofl); } break; case 'F': case 'f': /* Fork subprocesses for a shells */ forkfl++; D3("forkfl=%d",forkfl); break; case 'H': case 'h': /* Display "help" messages */ help(); break; case 'I': case 'i': /* Initialization message */ m_init = op + 2; D3("m_init=\"%s\"",m_init); break; case 'L': case 'l': /* Create lockfile on login */ lockfl++; forkfl++; D3("forkfl=%d lockfl=%d",forkfl,lockfl); break; case 'N': case 'n': /* Nudge message */ m_nudge = op + 2; D3("m_nudge=\"%s\"",m_nudge); break; case 'P': case 'p': /* Port name */ device = op + 2; D3("device=\"%s\"",device); break; case 'R': case 'r': /* Raw I/O [default=TRUE] */ n = sscanf(op+2,"%d",&raw); if (n < 1) raw = 1; D3("raw=%d\n",raw); break; case 'S': case 's': /* Slow output, sleep(slow) between buffers */ n = sscanf(op+2,"%d",&slow); if (n < 1) slow = 1; D3("slow=%d\n",slow); break; } return 0; } \!Funky\!Stuff\! echo file: pread.c cat > pread.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Read a response from the debugger into a buffer. Note the three ** stop conditions: finding an EOL char; filling the buffer; timeout. */ pread(stopch,bp,bs) int stopch; char*bp; int bs; { int c, n; uint reads; D4("pread(stopch=%02X,bp=%06lX,bs=%d)",stopch,bp,bs); n = reads = 0; while (n < bs) { c = nextbyte(); /* Next char from port */ if (c <= 0) { /* No data returned? */ if (debug >= 4) if (reads) P("pread: %d reads",reads); if (++reads > l_reads) { errno = ETIMEOUT; /* Timeout if too many */ D3("Input timeout..."); Fail; /* Timeout; return what we have */ } continue; } D9("pread: c=%02X='%c' bp=%06lX bs=%d",c,dsp(c),bp,bs); c &= iomask; /* Trim it to 7 bits */ if (c == 0) continue; /* Ignore nulls */ *bp++ = c; /* Note the EOL char is returned */ ++n; /* Count the input chars */ --bs; /* Decr count of bytes wanted */ if (c == stopch) Done; /* Assorted EOL chars */ switch (c) { case 0x03: /* ^C = ETX */ if (debug) P("%s: ^C in input, quitting [id]",getime()); die(0); case '>' : case '\r': case '\n': case ':' : case '%' : case 0x04: /* ^D = EOT */ case 0x06: /* ^D = ACK */ case 0x15: /* ^U = NAK */ Done; } reads = 0; /* Got data; reset timeout counter */ } done: *bp = 0; /* Final null for debugging */ fail: return n; } \!Funky\!Stuff\! echo file: pswd.c cat > pswd.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** We have received something that is believed to be a password. ** It is this routine's job to combine it with the current userid, ** and determine whether the combination is acceptable. This ** routine should work on most Unix systems, but who knows? */ pswd(rp) char *rp; { char *p, *q; int i; struct passwd *pp; D4("pswd: r=\"%s\" ss=%d",rp,ss); for (p=rp; *p; ++p) { /* Examine the chars for acceptability */ switch(*p) { case ':': /* Colons aren't legal */ D3("Invalid char '%c' in password",*p); Fail; case '\r': case '\n': *p = 0; goto gotit; case '!': /* Special goodie for killing daemon */ if (p[1] == 'Q') { if (debug) P("%s: !Q in input, quitting [id]",getime()); die(0); } default: continue; } } gotit: /* Make a copy of the supposed id */ p = rp; q = passwd; while (*p && q<passwd+PASSWD) *q++ = *p++; *q = 0; if (debug >= 3) P("%s PASSWD=\"%s\"",getime(),passwd); D3("userid:\"%s\"",userid); D3("pswd:\"%s\"",passwd); pp = getpwnam(userid); if (pp == 0) { D1("Login \"%s\" incorrect.",userid); D3("userid \"%s\" not found.",userid); Fail; } if (debug >= 6) Hexdnm(pp,sizeof(*pp),"Passwd:"); D4("pw_name =\"%s\"",pp->pw_name); D4("pw_passwd=\"%s\"",pp->pw_passwd); D4("pw_dir =\"%s\"",pp->pw_dir); D4("pw_shell =\"%s\"",pp->pw_shell); D5("pswd:before crypt(\"%s\",\"%s\")",passwd,pp->pw_passwd); p = crypt(passwd,pp->pw_passwd); D5("pswd: after crypt(\"%s\",\"%s\")=\"%s\"",passwd,pp->pw_passwd,p); if (strcmp(p,pp->pw_passwd)) { Pwrite("Login incorrect.\r\n"); D3("pswd \"%s\" not correct.",passwd); Fail; } D3("Login: uid=%d=\"%s\" group=%d accepted.",pp->pw_uid,pp->pw_name,pp->pw_gid); /* ** To do the next few changes, we probably need to be a super-user: */ #ifdef SYS5 /* ** Attempt to build a utmp structure. */ errno = 0; up = 0; D5("before ttyslot()"); i = ttyslot(); /* Identify our /etc/utmp line */ D4("ttyslot()=%d\t[errno=%d]",i,errno); findutmp(); p = 0; fillutmp(pp->pw_name,p,devfld,USER_PROCESS); D4("pswd:before pututline(%06lX)",up); pututline(up); #endif errno = 0; i = chmod(device,0644); /* Restrict terminal access to owner */ D4("chmod(\"%s\",0%o)=%d\t[errno=%d]",device,0644,i,errno); D3("Change \"%s\" to user %d=%s, group %d, permissions 644.",device,pp->pw_uid,pp->pw_name,pp->pw_gid); i = chown(device,pp->pw_uid,pp->pw_gid); /* Change terminal's group */ D4("chown(\"%s\",%d,%d)=%d",device,pp->pw_uid,pp->pw_gid,i); D3("New group %d.",pp->pw_gid); i = setgid(pp->pw_gid); /* Change terminal's owner */ D4("setgid(%d)=%d",pp->pw_gid,i); if (i < 0) Fail; D3("New user %d.",pp->pw_uid); i = setuid(pp->pw_uid); /* Change to login id */ D4("setuid(%d)=%d",pp->pw_uid,i); if (i < 0) Fail; D3("New directory \"%s\"",pp->pw_dir); i = chdir(pp->pw_dir); /* Move to login directory */ D4("chdir(\"%s\")=%d",pp->pw_dir,i); if (i < 0) Fail; /* ** Invoke the login shell. */ D5("pswd:before exec(1,\"%s\",%lX)",pp->pw_shell,pp); exec(1,pp->pw_shell,pp); /* Start up a shell */ D5("pswd: after exec(1,\"%s\",%lX)",pp->pw_shell,pp); target = "?"; /* We shouldn't get here */ fail: D4("pswd(\"%s\") FAILED.",rp); if (echofl ) Awrite("\r\nLogin incorrect."); if (m_login) { Awrite(m_login); ss = S_LOGIN; /* Note login prompt sent */ D4("State %d=%s",ss,gestate()); } sleep(1); return 0; } \!Funky\!Stuff\! echo file: pwrite.c cat > pwrite.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Write a character string to the port, stopping at the first null. ** This will be done slowly iff slowfl is turned on. */ pwrite(msg) char*msg; { int i, n; char c, *p; D5("pwrite(%08lX)",msg); n = strlen(msg); if (debug) { dbgtimep = getime(); if (debug >= 2) Ascdnm(msg,n,"Send:"); if (debug >= 4) Hexdnm(msg,n,"Send:"); } if (slowfl) { D8("port_wr:slow=%d",slow); p = msg; while (c = *p++) { Slowly; D9("port_wr:before write(%d,%06lX,%d)",dev,&c,1); i = write(dev,&c,1); D9("port_wr: after write(%d,%06lX,%d)=%d",dev,&c,1,i); if (i <= 0) { if (debug) P("%s: write failed, quitting.",getime()); die(2); } } } else { D9("port_wr:before write(%d,\"%s\",%d)",dev,msg,n); i = write(dev,msg,n); D9("port_wr: after write()=%d\t[errno=%d]",i,errno); } } \!Funky\!Stuff\! echo file: restdev.c cat > restdev.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Set the device to our desired (raw) state. This may only ** work if we are the super-user. This routine, such as it is, ** should work on just about any Unix system. See makeraw.c ** for the real system-dependent stuff. */ restdev() { int i; D6("restdev()"); makeraw(dev); /* We want to do raw I/O */ errno = 0; D2("Change \"%s\" to user %d, group %d, permissions 666.",device,euid,egid); i = chown(device,euid,egid); /* Try to get ownership */ D4("restdev: chown(\"%s\",%d,%d)=%d",device,euid,egid,i); errno = 0; i = chmod(device,0666); /* Make it publicly accessible */ D4("restdev: chmod(\"%s\",0%o)=%d\t[errno=%d]",device,0666,i,errno); return i; } \!Funky\!Stuff\! echo file: resync.c cat > resync.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Discard buffered input, and try to get us to a start where ** the next input will be a response to the most recent (or next) ** output. */ resync() { if (ibfa < ibfz) { dbgtimep = getime(); if (debug >= 2) Ascdnm(ibfa,ibfz-ibfa,"Drop:"); if (debug >= 4) Hexdnm(ibfa,ibfz-ibfa,"Drop:"); restdev(); /* Make sure the device is OK */ sleep(5); /* Try not to respond to garbage */ } ibfa = ibfz+1; } \!Funky\!Stuff\! echo file: sendbrk.c cat > sendbrk.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** If fd is a terminal-type device, and ioctl(-,TCSBRK,0) works, ** this will send a break signal down the line, which may (or may ** not) get the attention of whatever is at the other end. */ sendbrk(fd) { int i; D4("BREAK"); errno = 0; D5("sendbrk:before ioctl(fd=%d,TCSBRK=%d,0)",fd,TCSBRK); i = ioctl(fd,TCSBRK,0); D5("sendbrk: after ioctl(fd=%d,TCSBRK=%d,0)=%d [errno=%d]",fd,TCSBRK,i,errno); if (i<0 || errno) D2("ioctl(%d,TCSBRK=%d,0)=%d\t[errno=%d]",fd,TCSBRK,i,errno); return i; } \!Funky\!Stuff\! echo file: shprompt.c cat > shprompt.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** We seem to have gotten a shell prompt, but it's kinda hard to be sure. ** This routine is somewhat of a relic in uutty, but has been left here ** because you might want to deal with this case. What we try to do ** basically is to get the shell to logout. */ shprompt(rp) char *rp; { D5("shprompt(\"%s\")",rp); target = "shell"; switch(ss) { case S_INIT: case S_PASSWD: default: E("****Can't handle shell prompt \"%s\" in state %d=%s.",rp,ss,gestate()); Awrite("exit\r"); /* This terminates most shells */ Awrite("logout\r"); /* This terminates other shells */ Awrite("\4\3"); /* This works with still others */ if (m_exit) Awrite(m_exit); /* Other optional exit message */ if (m_init) Awrite(m_init); /* Try to tell the modem to quit */ ss = S_IDLE; D4("State %d=%s",ss,gestate()); } } \!Funky\!Stuff\! echo file: sig.c cat > sig.c << '\!Funky\!Stuff\!' #include "uutty.h" #include <signal.h> sig_1() {P("Signal 1 [SIGHUP]" ); fflush(stdout); die( 1);} sig_2() {P("Signal 2 [SIGINT]" ); fflush(stdout); die( 2);} sig_3() {P("Signal 3 [SIGQUIT]"); fflush(stdout); die( 3);} sig_4() {P("Signal 4 [SIGILL]" ); fflush(stdout); die( 4);} sig_5() {P("Signal 5 [SIGTRAP]"); fflush(stdout); die( 5);} sig_6() {P("Signal 6 [SIGIOT]" ); fflush(stdout); die( 6);} sig_7() {P("Signal 7 [SIGEMT]" ); fflush(stdout); die( 7);} sig_8() {P("Signal 8 [SIGFPE]" ); fflush(stdout); die( 8);} sig_9() {P("Signal 9 [SIGKILL]"); fflush(stdout); die( 9);} sig10() {P("Signal 10 [SIGBUS]" ); fflush(stdout); die(10);} sig11() {P("Signal 11 [SIGSEGV]"); fflush(stdout); die(11);} sig12() {P("Signal 12 [SIGSYS]" ); fflush(stdout); die(12);} sig13() {P("Signal 13 [SIGPIPE]"); fflush(stdout); die(13);} sig14() {P("Signal 14 [SIGALRM]"); fflush(stdout); die(14);} sig15() {P("Signal 15 [SIGTERM]"); fflush(stdout); die(15);} sig16() {P("Signal 16 [SIGADDR]"); fflush(stdout); die(16);} sig17() {P("Signal 17 [SIGZERO]"); fflush(stdout); die(17);} sig18() {P("Signal 18 [SIGCHK]" ); fflush(stdout); die(18);} sig19() {P("Signal 19 [SIGOVER]"); fflush(stdout); die(19);} sig20() {P("Signal 20 [SIGPRIV]"); fflush(stdout); die(20);} sig21() {P("Signal 21 [SIGUSR1]"); fflush(stdout); die(21);} sig22() {P("Signal 22 [SIGUSR2]"); fflush(stdout); die(22);} /* Catch all the signals we can: */ sig() { signal( 1,sig_1); signal( 2,sig_2); signal( 3,sig_3); signal( 4,sig_4); signal( 5,sig_5); signal( 6,sig_6); signal( 7,sig_7); signal( 8,sig_8); signal( 9,sig_9); signal(10,sig10); signal(11,sig11); signal(11,sig11); signal(12,sig12); signal(13,sig13); signal(14,sig14); signal(15,sig15); signal(16,sig16); signal(17,sig17); signal(18,sig18); signal(19,sig19); signal(20,sig20); signal(21,sig21); signal(22,sig22); } \!Funky\!Stuff\! echo file: slowly.c cat > slowly.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** This routine handles requests to do things slowly. ** Note the two ways of delaying: sleeping for "slow" ** seconds, or counting down from "count" to 1. */ slowly() { uint u; D8("slowly() slow=%d count=%d",slow,count); if (slow > 0) { D8("slowly: slow=%d",slow); sleep(slow); } for (u=count; u; u--) ; D8("slowly() done"); } \!Funky\!Stuff\! echo file: st.c cat > st.c << '\!Funky\!Stuff\!' #include "dbg.h" /* ** Assorted string-manipulation subroutines. */ /* Match a null-terminated string against the initial portion ** of another string. If the match succeeds, return its length; ** else return 0. */ int st_init(x,y) char *x; char *y; { char *s; char *t; s = x; t = y; while (*s) if (*s++ != *t++) goto fail; D9("st_init(\"%s\",\"%s\")=%d",x,y,s-x); return(s - x); fail: D9("st_init(\"%s\",\"%s\")=%d",x,y,0); return 0; } /* Return the int value of the trailing digits of a null-terminated string. ** Note that non-numeric chars reset the value, so only digits that follow ** all non-numerics are used. Also, only the last '-' is effective. Thus ** "15-x-37" gives the value 37. */ int st_ival(s) char *s; { int c, val, sign; sign = val = 0; while (c = *s++) if ('0'<=c && c<='9') val = (val * 10) + (c - '0'); else if (c == '-') sign = c; else sign = val = 0; return(sign ? -val : val); } /* Convert a long to an ASCII string. ** Return pointer to byte just after the value. */ char * st_ltoa(l,a) long l; char *a; { if (l < 0) {*a++ = '-'; l = -l;} if (l > 9) a = st_ltoa((l/10),a); *a++ = '0' + (l % 10); return a; } /* Return the long value of the trailing digits of a null-terminated string. ** Note that non-numeric chars reset the value, so only digits that follow ** all non-numerics are used. Also, only the last '-' is effective. Thus ** "15-x-37" gives the value 37. */ long st_lval(s) char *s; { int c, sign; long val; val = sign = 0; D4("st_lval: sign=%d val=%ld",sign,val); while (c = *s++) { if ('0'<=c && c<='9') val = (val * 10) + (c - '0'); else if (c == '-') sign = c; else val = sign = 0; D4("st_lval: sign=%d val=%ld c=%02x='%c'",sign,val,c,dsp(c)); } return(sign ? -val : val); } \!Funky\!Stuff\! echo file: talk.c cat > talk.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** This routine assumes that it is talking to a port connected ** to a VMEbus device with an on-board debugger. It exchanges ** some pleasantries to verify this fact, then procedes to ask ** the debugger to show it the requested chunk of its memory. */ talk() { int c, i, n, r; int baddies, lowers, uppers; int messages; D4("talk()"); r = 0; ss = S_INIT; D4("State %d=%s",ss,gestate()); lockwait(); /* Try to avoid collisions */ if (device) target = device; /* ** We have a long list of initialization strings here. ** This has come in handy in a few cases, but usually ** they are mostly null. */ if (m_init ) Awrite(m_init); if (m_init1) {Awrite(m_init1); sleep(SLEEP1);} if (m_init2) {Awrite(m_init2); sleep(SLEEP2);} if (m_init3) {Awrite(m_init3); sleep(SLEEP3);} ss = S_INIT; D4("State %d=%s",ss,gestate()); if (m_login) Awrite(m_login); ss = S_LOGIN; D4("State %d=%s",ss,gestate()); Response; nudge: /* Try to elicit a response */ D3("Nudge."); if (++nudges > Nudges) { E("Too many nudges."); r = 1; Dead; } lockwait(); /* Try to avoid interference */ Resync; if (m_nudge) /* Is a nudge message defined? */ Awrite(m_nudge); /* If so, send it */ idle: /* We will now accept anything */ D3("Idle."); ss = S_IDLE; /* Note we're awaiting a response */ D4("State %d=%s",ss,gestate()); messages = 0; /* Counter to trigger nudges */ response: /* Read a response */ D4("Response: state=%d=%s",ss,gestate()); if (debug > 0 && target != oldtarg) { if (debug >= 3) P("%s Talking to %s.",getime(),target); oldtarg = target; } /* ** Check for any of a list of known debugger prompts. ** If we don't get one of then, try to nudge the debugger, ** and try again, giving up after some number of tries. */ errno = 0; D4("talk:l_tries=%d",l_tries); sleep(1); /* Sheer laziness */ lockwait(); if (ss != S_PASSWD) /* If not expecting password, do echoing if requested */ echoing = echofl; while ((n = Pread(eol0,rsp,rspmax)) <= 0) { D3("No response."); /* ** Note this program loops forever waiting for something to come down the line. ** You might want to do something else here if there's no input. */ } D4("talk:Pread()=%d",n); rsp[n] = 0; ++messages; if (debug) { dbgtimep = getime(); if (debug >= 2) Ascdnm(rsp,n,"Got:"); if (debug >= 4) Hexdnm(rsp,n,"Got:"); } /* ** Next, we do some preliminary checking for sanity of the input. ** Since this program's duty is to do login interviews, the only ** input we really want is something that might be a login id or ** a password. Another likely input is a nudge from a counterpart ** on the other end, to which we respond with a prompt. */ if (n < 3) { /* Just "\r", "\n", or "\r\n" is a nudge */ D4("Short response, %d chars.",n); switch (c = rsp[0]) { case 0x03: /* ^C = ETX (usually means "die") */ if (debug) P("%s: ^C in input, quitting.",getime()); die(c); case '\n': /* ^J = LF */ case '\r': /* ^M = CR */ case 0x04: /* ^D = EOT */ case 0x02: /* ^B = STX */ case 0x01: /* ^A = SOH */ ss = S_IDLE; /* Flush garbage from buffer */ D4("State %d=%s",ss,gestate()); lockwait(); if (ibfa < ibfz) { /* Is there input waiting? */ D3("Nudge plus garbage received, ignored."); Resync; /* Flush garbage from buffer */ } else { /* No input waiting */ D3("Nudged; send login prompt \"%s\"",m_login); Awrite(m_login); /* Send them a login prompt */ ss = S_LOGIN; /* Note that we did it */ D4("State %d=%s",ss,gestate()); messages = 0; } Response; /* See if there's a response */ case 0x10: /* This is a uucp Start-of-message */ D3("We seem to be talking to a UUCP demon."); makeraw(dev); /* Paranoia! */ Awrite("OOOOOOOO\r"); /* Try to get it to stop */ Resync; /* Discard the rest of the message */ if (m_init) Awrite(m_init); ss = S_INIT; /* Try to get back to idle state */ D4("State %d=%s",ss,gestate()); Response; } } baddies = lowers = uppers = 0;/* These are for counting letters */ D4("Scan id for char classes..."); for (i=0; i<n; i++) { /* Examine the response for nasty stuff */ c = rsp[i]; /* One byte at a time */ if (islower(c)) { ++lowers; /* Lower-case letters are desirable */ D6("c=%02X-'%c' lowers=%d",c,dsp(c),lowers); } else if (isupper(c)) { ++uppers; /* Upper-case letters are acceptable */ D6("c=%02X-'%c' uppers=%d",c,dsp(c),uppers); } else /* Everything else is dubious */ switch (c) { case 0x03: /* Special goody to let others kill us */ case 0x02: case 0x01: if (debug) P("%s: %02X in input, quitting [id]",getime(),c); die(c); case '#': /* These are likely shell prompts */ case '$': /* Default Bourne shell prompt */ case '%': /* Default C- shell prompt */ case '>': /* Popular prompt in some circles */ if (i >= n-2) { /* Likely only if at end of input */ shprompt(rsp); /* Can we handle it? */ Response; } /* If earlier in string, reject it */ case 0x00: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0B: case 0x0C: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: case ' ' : case ':' : /* None of these are acceptable in ids or passwords */ D3("Invalid char %02x='%c' in input.",c,dsp(c)); ++baddies; break; } } if (baddies || ((lowers+uppers) == 0)) { /* Is it acceptable? */ D4("Unacceptable; baddies=%d lowers=%d uppers=%d.",baddies,lowers,uppers); Resync; /* No; drop buffered input */ lockwait(); if (baddies && messages > THRESH) { makeraw(dev); /* Others may have munged the driver */ if (m_init) { Awrite(m_init); ss = S_INIT; D4("State %d=%s",ss,gestate()); } else { Awrite(m_login); ss = S_LOGIN; D4("State %d=%s",ss,gestate()); } messages = 0; } ss = S_IDLE; D4("State %d=%s",ss,gestate()); Response; } switch (ss) { /* Special actions depending on state */ case S_EXIT: /* We are trying to exit */ if (m_exit) { /* Is there an exit command? */ Awrite(m_exit); /* If so, send it */ ss = S_EXIT; /* Note again that we're trying to quit */ D4("State %d=%s",ss,gestate()); } Response; case S_IDLE: /* We're waiting for the other end to act */ D4("Got id while idle."); c = rsp[0]; /* First char of response is sometimes special */ goto maybeid; case S_LOGIN: /* We just sent a login prompt */ D4("Got response to login prompt."); maybeid: /* We may have a login id */ D4("Got possible login id."); if (lowers == 0) { /* No lower-case letters; it's not an id */ lockwait(); /* Make sure we're not interfering */ makeraw(dev); /* Paranoia! */ if (m_init) { /* Do we have a special init string? */ Awrite(m_init); ss = S_INIT; D4("State %d=%s",ss,gestate()); } Response; /* Go wait for next input */ } i = checkid(rsp); /* Put it through final tests */ if (i <= 0) { /* If it fails... */ if (debug) { dbgtimep = getime(); if (debug >= 2) Ascdnm(rsp,n,"Bad id:"); if (debug >= 4) Hexdnm(rsp,n,"Bad id:"); } Resync; /* Get port into known state */ sleep(5); /* Extra delay for safety's sake */ ss = S_IDLE; D4("State %d=%s",ss,gestate()); Response; /* Go wait for next input */ } /* It sure looks like an id */ D3("Send password prompt \"%s\"",m_passwd); echoing = 0; Awrite(m_passwd); ss = S_PASSWD; D4("State %d=%s",ss,gestate()); Response; case S_PASSWD: /* We just sent a password prompt */ D4("Got response to password prompt."); i = pswd(rsp); /* Check it out to see if it's a good guy */ if (i <= 0) { /* Successful call won't return */ D3("Unacceptable password \"%s\"",rsp); Response; } E("pswd(\"%s\")=%d Shouldn't happen.",rsp,i); Response; default: D4("ss=%d not special",ss); } if (debug) { dbgtimep = getime(); if (debug >= 2) Ascdnm(rsp,n,"Ignore:"); if (debug >= 4) Hexdnm(rsp,n,"Ignore:"); } Response; dead: P("Giving up; %s seems to be dead.",target); return r; } \!Funky\!Stuff\! echo file: unlock.c cat > unlock.c << '\!Funky\!Stuff\!' #include "uutty.h" /* ** Remove the lockfile. */ unlock() { D2("%s: Delete lockfile %d=\"%s\".",getime(),lockfn,lockfile); unlink(lockfile); D4("Close lockfn=%d.",lockfn); close(lockfn); lockfn = -1; locked = 0; } \!Funky\!Stuff\!