7. dial_pppTo get PPP working, you have to customise your own chat script. Whilst in deep hack mode, you might like create a wrapper to call it properly. Here's mine ...
It allows a 'plain user' to bring a ppp link up or down without knowing the root password. It does this using a suid wrapper. If you don't like that, simply adjust it to your requirements and call it when you are root (or get sudo to do so). Some people know about and use the /usr/sbin/ppp-on and ppp-off commands. If other apps try to use them, provide them as wrappers to dial_ppp. Remember that it's the |
Add a new group, called dial_ppp. Members of the group are allowed to start and stop pre-configured ppp links. Add those users to the /etc/group line:
dial_ppp::19:gps,fred,kt
You don't have to, but now is a good time to add a new group, called "phone". Members are allowed to use the modem for other things. This presumes they are permitted to use it at any time of the day for sesson type calls (eg minicom) and to send faxes.
The dial_ppp and phone groups have nothing to do with each other, except they are both related to the modem. You need write permission to the device to make a fax (any user-id), you need root permission for the kernel to setup a PPP link.
/etc/ppp/dial_ppp
stops non-members of the dial_ppp
group group from running the binary, by having the
correct permission set on the file. It has to be
the group permission, because the user permission
(root) is needed for the SUID. If you are not
listed in the /etc/group
line, you can't even start
the binary, so you won't get the suid-root benefit.
The suid-bit and root ownership of the binary, is how a plain user gets 'promoted' to root, though the binary itself has to request the facility (see the code below). You can't simply set the suid-bit on any binary, unless that binary expects it.
That gives root uid and then exec's a "semi-safe" script passing argv through untouched. You do have to be careful when calling shell scripts, to prevent any security holes appearing, but if you are careful, they should be safe. The main risk, is that you are running /bin/sh, a large complex program, with possible flaws. The second risk is the contents of the script, and the general configuration.
After editing /etc/group, those users will have to logout/login, for it to take effect.
Here is a program. Type it in. Compile it.
/* /etc/ppp/dial_ppp.c */ #include#include #include main( int argc, char ** argv, char ** envp ) { if( setgid(getegid()) ) perror( "setgid" ); if( setuid(geteuid()) ) perror( "setuid" ); envp = 0; /* blocks IFS attack on non-bash shells */ execve( "/etc/ppp/dial_ppp.sh", argv, envp ); perror( argv[0] ); return errno; }
You don't need the setgid() line, but as a generic, educational wrapper, there is is. If you don't run an unsafe program, you don't need to flatten envp.
If you're not in the dial_ppp group you can't run the program (see below). If you are, the kernel runs the elf binary (this also works on a.out systems).
The SUID bit on the binary file tells the kernel to provide a special permission to the running process: the euid number or effective user id (Normally euid == uid ). The kernel only does that for binaries, not for scripts!
The code can make use of the euid
, by asking the
kernel to set it's active uid to that value, using
the setuid(2) system call.
Actually a root
uid process can switch to any uid, a non-root user
can only switch to the euid (or the uid!).
The euid isn't used if the code doesn't use it, and the
option is lost on the next exec. That prevents a hacker putting
SUID option on one binary, and using it in another, If the
euid
is used, and converted to the uid
, the
child process gets the full access.
By calling the
getuid(2) sys-call, the code can be reused in a
different program and different file-owner, to get
a different uid. The getuid
call simply tells the
program what uid
it can request (the root uid
is always zero).
Now that the uid is root (or whatever was set on the file, the above is a generic example), you can make any system calls you like, or call a program that does so. So the program exec's the shell script.
The program passes the environment (or 0 for none!) and also the argv vector of command line argument words. Here we pass on the ones we received, in a more complex program, you would filter out what you think is acceptable.
The kernel can't run a shell script directly, but the
initial #!/bin/sh
in /etc/ppp/dial_ppp.sh
tells the kernel to run a shell to run the script.
That script does what it does.
The call to the exec()
system call never returns,
except when the kernel can't run the program,
eg bad permissions, can't find /bin/sh, etc.
In which case it does return and tells us.
NOTE: the exit code from this program will be errno
(if the exec failed) or it will be the exit code from the
exec'd binary (which runs in the same process-id).
With PPPD the exit code is rarely interesting, because it
fork
s off it's own child process which might fail long after
the parent has exited.
perror
prints any error message. Note the above code
"ploughs on", even after an error was detected. In this case
it's fairly harmless, and you get the benefit of a later error
message (pppd failed - ENOPERM).
If someone somehow hijacked my user-id, eg through a
Netscape browser plugin, or simply by tempting me to
download and run their code, they would be able to
run dial_ppp
However, they would not be able to establish arbitrary PPP configurations, just make or break the ones that I have configured.
The list of permitted actions is in the shell script.
Setting envp
to 0 means that no environment
variables are exported what so ever. This blocks the
IFS
attack, where the cracker defines "/" to be
treated as a space changing the meaning of some well
formed line. However this also drops all other
environment variables. I leave the envp
mechanism
for you to see, particularly for non suid programs,
though you should also look at the man pages for
fork(2) and wait(2).
You could write a bit more code and check the array of strings held in envp, exiting if you don't like anything.
Note that with bash-1.14.5(1), and maybe others, the IFS attack is blocked: the binary does what the specifiaction should have been, and resets IFS, however older SVR4 and other unix's do the "standard" thing which is both stupid and wrong.
Setting up such an attach does require a local shell, and so is a greatly reduced risk.
If you allow the user to provide the PATH, and your
script calls an innocent command like sync
, a
cracker could create a sync shell script and set the
PATH to point to it first. To avoid that, you can
use absolute pathnames, or prefix the PATH with the
system directories first.
PATH="${PATH:-/bin}" # else null PATH adds . PATH="/usr/bin:$PATH" # third PATH="/bin:$PATH" # second PATH="/sbin:$PATH" # first
In particular beware of any command that you have
mis-typed and says "command not found", a helpful
hacker might correct that error ... Also check
that commands you do run are not in a writable
directory or a writable file. /usr/local/bin
is
often a writable directory for some users. (I used
to have it group writable by myself, but that opens
up problems with a hijacked session).
If you pass ARGV to programs that pass it to shell, the shell might interpret them in ways you didn't expect. For example back-quotes, run the command in them. $(cmd) is another form of backquotes!
With older shells, you should also put everything in "quotes" and if a command allows "--" to end all options, use it. Similarly, distinguish between "$@" and $* RTFM: bash(1)
If you design your application to use names which the script then goes and fetches the values for, you can simply reject any line or word that isn't a simple alphanumeric string.
The dial_ppp
script doesn't have this problem, but
it does show you how to use names, to select
actions.
Another reason for using names at the top level, is
where a hacker uses excessively long parameter strings to
overflow a buffer somewhere.
And then there's filenames. Firstly: do you need to allow ".." in a pathname? If not reject it. Secondly do you need absolute or full filenames (again linking back to names). The script runs as root (or as lp or ...), which is a different user than the person who invoked it. If you accept STDIN/STDOUT you at least know that the user had permission to open the file themselves, you are not granting that facility.
Not all shell script security problems involve SUID-wrappers. Plain scripts that cron regularly runs as root, or as a specific user, may also leave you exposed. "Command not found" ...
This is where the group permission is set and used.
Along with chmod, it grants users in the dial_ppp
group
permission to run this binary.
If you leave the binary in /etc/ppp, you may also need group access to the directory, though some (non-Linux) kernels allow access if you know the exact filename.
The owner of the file will be the process UID when it is run, provided the SUID bit is set.
It isn't 6755, because we only want members of the group to be able to run this command, and that zero stops others.
It can be 4750 or 6750, it makes no difference here.
The suid bit is fragile! If you chgrp or chown the file, you will have to chmod it again. Similarly recompiling needs a new chmod. If there is a Makefile, eg dial_ppp.mk, it should do that for you.
make -f dial_ppp.mk
You might wish to allow the command to appear in /bin, /usr/bin, or /usr/sbin so that users can run it by accident, when browsing for gif converters. If you leave it in /etc/ppp/ you will need to allow dial_ppp group members access to the directory. If you move the binary where they can reach it, you will have to remember to move it's replacement when you recompile, or create a hard link as follows:
ln /etc/ppp/dial_ppp /bin
This is the command that gets run. It is a suid shell script so be careful.
Edit the parts that have "tdc", that's the name of
the isp. Put your details there. The version in the
.tgz file actually allows you to simply create
/etc/ppp/chat_NAME, and call dial_ppp NAME
.
The big thing to look out for is the "$loc:$rem"
parameter to pppd. If you set loc=""
(ditto
likewise for rem), that tells PPPD to allow the
remote to choose the IP address. When dircon changed
all our IP addresses, I picked up the new address
automatically. Even though it didn't appear in any
file on may machine. A big win!
The downside of not setting rem=addr
, is that a
non-trusted ISP could try to become an IP address
that I place too much trust in. If you know who you
are calling, setting the rem address in the script
will stop them.
Not specifying an address, makes PPPD offer a suggestion to the remote, typically the IP_ADDR of the ethernet interface, or the first hostname in /etc/hosts. That (mild) suggestion is easily and quickly overridden by the ISP, who knows what IP address they want you to be, but it causes a bit more PPP dialog, and maybe an entry in their log files.
Users of dynamic IP addresses, will almost certainly want to allow the ISP to set the IP address at connetc time.
#!/bin/sh # /etc/ppp/dial_ppp.sh # echo "Running: $0 $* (\$#=$#)" loc="tdc_me" rem="" speed=38400 modem=/dev/modem OPTIONS="debug lock crtscts modem defaultroute persist" PATH="/usr/sbin:$PATH" case "$1" in help) echo "$0" "$@" "# see /etc/ppp/." exit 1 ;; id) id ;; cut) killall pppd ;; dip_tdc) exec /etc/ppp/dip_tdc ;; tdc) rem="" chat_script=/etc/ppp/chat_tdc # rem="tdc_gw" # allow remote to supply ip_addr set -x pppd connect "chat -v -f $chat_script" \ "$modem" $speed $OPTIONS \ "$loc:$rem" ;; list) echo "tdc # PPP link to dircon" # used by menu ;; *) echo "BAD USAGE: $0 $*" exit 1 esac
ABORT BUSY ABORT 'NO CARRIER' ABORT 'NO DIAL TONE' REPORT CONNECT '' ATZ TIMEOUT 5 OK 'AT Q0 V1 E1 X4 L1 W1 S95=47 M1 I4' OK 'ATDT 0181 265 2211' TIMEOUT 45 CONNECT '' TIMEOUT 10 login: trix Password: password Protocol: pppchat does the actual modem initialisation and dialing. This script is alternating: on
prompt
send keystrokes
along with
a few ABORT
and TIMEOUT
rules. most people put the prompt
and mkeystrokes pairs on the same line.
I wish chat had a spectial line-string character such as @ that didn't echo the password into the trace files! You may need chmod 640 /var/log/messages to keep it's contents private.
This is another shell script that also gets run as root, as does
/etc/ppp/ip-down
I don't have to fetch email,
but running finger @mailhost
wakes the remote sendmail up,
and tells me when it's finished. That's local. You may need POP3
to actively fetch your email.
#!/bin/sh mailq sleep 2 sendmail -q finger @felix # seems to leave one in queue finger @felix # you might use popclient - faster echo "$0 $* exit $?" >> /var/log/messages
Mine is blank, but you can put the modem baud rate and options in there. Much tidier if you do.
Your ISP's PPP setup may be different than Dircon's ("The Direct Connection"). You might have to negociate your secret key challenge, (PPP has a choice of configurations, and security options). Your ISP should tell you what settings you need.
This option tells pppd, to redial if the connecton drops.
Note that if the chat script fails (eg BUSY), it won't redial again,
if that is a problem, look at diald
.
If you specify them as names to pppd, you must have "tdc_me" and tdc_gw in /etc/hosts. These are alias names for the IP addresses of the two ends of the ppp link.
If you don't specify them, you can't complain about what the other end thinks it's called, or request it to become what you want.
You need a menu to call your new command. I used tcl+tk. The lines you have to call are:
dial_ppp tdc - to connect to the tdc isp dial_ppp cut - to cut the ppp link
I'm not sure what is the correct way to bring a pppd link down, as I
only have one, so I kill all of them. There is probably a better way
of recording the pid and naming it later, or recording the
interface and ifconfig ppp0 down
If you flit between different ISP's, you will need to change your selected nameserver to avoid timeouts. The ppp_script would also switch over the necessary /etc/resolv.conf file.
You probably want ppp-2.2.0f or better, if you are running a 1.3 or 2.0 kernel. If you want to use dip instead (for a SLIP link), you may need to edit the scripts so that it isn't called "dial_ppp"
dial_ppp is a nifty utility, I use it every day!
To make dial_ppp avaiable to everybody, without requiring them to purchase Raven Issue-3, I am created a .tgz file, and placed it on sunsite.unc.edu as Free software. system/Network/Serial
Noone has send any feedback, so I guess it needs no update. Please feel free to generalise it.
Hopefully you now have a working TCP/IP link with the internet,
you now have to make a few apps work to use it. Most distributions
put a working ftp client on your disk, or you could use lynx
(a text mode web browser) so try this:
lynx http://sunsite.unc.edu/pub/Linux/docs/Raven/
Or if you have mc
installed, and running, try
cd ftp://sunsite.unc.edu/pub/Linux/docs