Monday, March 22, 2010

File descriptors

File descriptors

A file descriptor is a handle created by a process when a file is opened. There is a limit to the amount of file descriptors per process. The default Solaris file descriptor limit is 64.

If the file descriptor limit is exceeded for a process, you may see the following errors:

"Too Many Open Files"
"Err#24 EMFILE" (in truss output)

To display a process' current file descriptor limit, run /usr/proc/bin/pfiles pid | grep rlimit on Solaris systems.

Display system file descriptor settings:
ulimit -Hn (hard limit, cannot be exceeded)
ulimit -Sn / ulimit -n (soft limit may be increased to hard limit value)

Increasing file descriptor settings for child processes (example):
$ ulimit -Hn
1024
$ ulimit -Sn
64
$ ulimit -Sn 1024
$ ulimit -Sn
1024

Solaris kernel parameters:
rlim_fd_cur: soft limit

It may be dangerous to set this value higher than 256 due to limitations with the stdio library. If programs require more file descriptors, they should use setrlimit directly.

rlim_fd_max: hard limit

It may be dangerous to set this value higher than 1024 due to limitations with select. If programs require more file descriptors, they should use setrlimit directly.

More information:
http://www.princeton.edu/~unix/Solaris/troubleshoot/filedesc.html
http://help.netscape.com/kb/corporate/19980403-12.html

Linux NFS configuration services

Linux NFS configuration services


This is specific to RedHat, but most other Linux distributions follow the same pattern.

An NFS server on linux requires 3 services to be running in order to share files:


/etc/rc.d/init.d/portmap
/etc/rc.d/init.d/nfslock
/etc/rc.d/init.d/nfs

You can start/stop/restart these services by issues the above lines with the corresponding arguments: start, stop or restart. You can also use the shortcut 'service' command, as in:

# service portmap start

You need to ensure that these 3 services start up when the system does.

First, verify the default runlevel of the computer. Check the line in /etc/inittab that starts with "id:". The next number will be the default runlevel (usually 3 for non-gui, 5 for gui). You can manage the startup scripts manually by creating symlinks in the corresponding /etc/rc.d/rcX.d directory (where X is the default runlevel) or you can use redhat's "chkconfig" command. Assuming the default runlevel is 3, these 3 commands will ensure that the services necessary for an nfs server start at boot time (these all must run as root):

# chkconfig --level 3 portmap on
# chkconfig --level 3 nfslock on
# chkconfig --level 3 nfs on


Now either reboot the box or issue these commands to start the nfs server:

# service nfs stop
# service nfslock stop
# service portmap stop
# service portmap start
# service nfslock start
# service nfs start


Order is important. Portmap must start first, followed by nfslock, followed by nfs.

The file that defines what directories are shared is /etc/exports. 'man exports' will give you the full overview of all options available for this file. Here is an example line:

/public *(rw,no_root_squash)

This says share the folder /public, allow any IP access, give read/write access, and allow the root user to connect as root.

The * wildcard can be a list or range of IP addresses. For example, if we wanted to restrict this access to the 192.168.1.0/24 Class C subnet, the line would look like this:

/public 192.168.1.0/255.255.255.0(rw,no_root_squash)

rw means read/write. no_root_squash is a setting that allows nfs clients to connect as root. Without this setting, the root user on clients that connect has the permissions of the user 'nfsnobody', uid 65534.

Anytime you make any changes to the /etc/exports file, run the command:

# exportfs -avr
on the server to update the nfs server.


On to the nfs client:

On the client, you need the 'portmap' and 'nfslock' services running. Follow the instructions above to ensure that these 2 services start with the default runlevel. Once they are running, you can mount a directory on the nfs server. If the server ip is 192.168.1.1 and the share name is public, you can do this on a client:

# mkdir /mnt/public
# mount 192.168.1.1:/public /mnt/public


You should get a prompt back. A 'mount' command should show you that the nfs share is mounted, and you should be able to cd to this directory and view the contents.

A couple of things to keep in mind with NFS: permissions are handled by UID. It is very beneficial to ensure that a user on the client has the same UID on the server. Otherwise the permissions will just confuse you. If you are just using root, and using the no_root_squash option on every mount, you don't have to worry about this.

Security: NFS should stand for No F**king Security. It's all IP based, so if anyone can spoof an IP, they can mount on of your shares. NEVER do NFS over an untrusted network (like the internet) and ALWAYS restrict your shares to at least your local subnet.

There are 2 kinds of nfs mounts : soft mounts and hard mounts. With a soft mount, if the server goes away (reboot/lost network/whatever) the client can gracefully close the connection, unmount the share and continue on. However, in order for the client to maintain this ability, it has to do more caching of the reads and writes, so performace is lower and it's possible to lose data if the server goes away unexpectedly.

A hard mount means that the client never gives up trying to hit the server. Ever. Eventually the load on the client will be so high due to backed up i/o requests, you'll have to reboot it. Not all that good, but hard mounts don't have the caching overhead soft mounts do, so you have less chance of losing data. I always use hard mounts, but then specify the 'intr' option on the mount, so I can unmount it manually if the server goes away (see example below).

Troubleshooting:
As with most things in linux, watch the log files. If you get an error on the client when trying to mount a share, look at /var/log/messages on the server. If you get an error like "RPC: program not registered" that means that the portmap service isn't running on one of the machines. Verify all the processes are running and try again.

Examples:
Here are some examples from one of my production linux nfs servers. On the server, a line from /etc/exports:

/aim/exports/clink192.168.23.0/255.255.255.0(rw,no_root_squash,async,wdelay)

Please see the manpage on exports for explanations of some of these options. Here is the corresponding /etc/fstab entry on a client that mounts this share (all on one line):

192.168.23.10:/aim/exports/clink /aim/imports/clink nfs
rsize=8192,wsize=8192,timeo=20,retrans=6,async,rw,noatime,intr 0 0


You might want to experiment with rsize and wsize parameters to get that last bit of performance. With this line in /etc/fstab, the mount is remounted at boot. I can also issue the command 'mount -t nfs -a' to mount all nfs entries in /etc/fstab with the corresponding options.

setuid on shell scripts

setuid on shell scripts

Running software as root without requiring a root password is the subject of a number of tutorials on the web, and although it may seem a little bit confusing, it's fairly simple. Inevitably, users want to run shell scripts as root, too. After all, they're considered a 'program', so why not? Unfortunately, there are unseen bumps in the road to Unix convenience.

The tutorial

Many tutorials show this method for creating a script that runs as root automatically.

  1. Open a text editor, and type up your script:
    #!/bin/sh
    program1
    program2
    ...
  2. Save the file as something.sh.
  3. Open a terminal, and enter the following commands:
    $ su
    [enter password]
    chown root:root something.sh
    chmod 4755 something.sh
    exit
  4. Then, finally run it with ./something.sh, and it'll have root access!

...or not. Most likely, you'll get the same error messages that you did before you ran those commands. If your script does actually work, go ahead and skip the rest of this tutorial. If you experience this problem, read on.

The problem

The instructions are fairly straightforward. Create the shell script that you want to execute, and change the owner and group to root (chown root:root). Now comes the command that's supposed to do the magic:

chmod 4755

Let's break this down a little bit. The 755 part means that there's read/write/execute permissions for the owner (root), and only read/execute permissions for everyone else. This makes sense because you want everyone to be able to execute the script, although you don't want everyone to be able to modify what it does.

Now for the 4 prefix. This means that the specified file will have the setuid bit set. This means that whatever is run will have the permissions of the owner. Since we set root as the owner, this will do exactly what we want. Perfect!

Except it doesn't. Well, the truth is actually that the setuid bit is disabled on a lot of *nix implementations due the massive security holes it incurs. If the method originally mentioned doesn't work for you, chances are that your Linux distribution has disabled setuid for shell scripts.

The solution(s)

One way of solving this problem is to call the shell script from a program that can use the setuid bit. For example, here is how you would accomplish this in a C program:

#include 
#include
#include
#include

int main()
{
setuid( 0 );
system( "/path/to/script.sh" );

return 0;
}

Save it as runscript.c. You'll need the gcc compiler. If you don't have it already, look for it in your package manager. You can usually the majority of your compiler tools with one large package, but many distros also offer the option of installing gcc by itself.

Once you have it, compile it at the prompt:

gcc runscript.c -o runscript
Now do the setuid on this program binary:
su
[enter password]
chown root:root runscript
chmod 4755 runscript

Now, you should be able to run it, and you'll see your script being executed with root permissions. Congratulations!

Another alternative, if you've got it installed, is to prefix all the commands in the shell script with 'sudo'. Then set up the permissions so that a password is not required to run those commands with sudo. Read the manpage for more information.

Conclusion

With all that said, running shell scripts with setuid isn't very safe, and the distro designers had a pretty good idea of what they were doing when many of them disabled it. If you're running a multiuser Unix environment and security is an asset for you, make sure that your scripts are secure. A single slip can result in the compromising of an entire network. Only use them when absolutely necessary, and make sure you know exactly what you're doing if you do decide to use them.

Sticky Bit

The sticky bit and directories

Another important enhancement involves the use of the sticky bit on directories. A directory with the sticky bit set means that only the file owner and the superuser may remove files from that directory. Other users are denied the right to remove files regardless of the directory permissions. Unlike with file sticky bits, the sticky bit on directories remains there until the directory owner or superuser explicitly removes the directory or changes the permissions.

You can gain the most security from this feature by placing the sticky bit on all public directories. These directories are writable by any non-administrator. You should train users that the sticky bit, together with the default umask of 077, solves a big problem for less secure systems. Together, both features prevent other users from altering or replacing any file you have in a public directory. The only information they can gain from the file is its name and attributes.

``Sticky bit example'' illustrates the power of such a scheme. The sticky bit is the ``t'' in the permissions for the directory.

Sticky bit example

   $ id
uid=76(slm) gid=11(guru)
$ ls -al /tmp
total 64
drwxrwxrwt 2 bin bin 1088 Mar 18 21:10 .
dr-xr-xr-x 19 bin bin 608 Mar 18 11:50 ..
-rw------- 1 blf guru 19456 Mar 18 21:18 Ex16566
-rw------- 1 blf guru 10240 Mar 18 21:18 Rx16566
-rwxr-xr-x 1 slm guru 19587 Mar 17 19:41 mine
-rw------- 1 slm guru 279 Mar 17 19:41 mytemp
-rw-rw-rw- 1 root sys 35 Mar 16 12:27 openfile
-rw------- 1 root root 32 Mar 10 10:26 protfile
$ rm /tmp/Ex16566
rm: /tmp/Ex16566 not removed. Permission denied
$ rm /tmp/protfile
rm: /tmp/protfile not removed. Permission denied
$ cat /tmp/openfile
Ha! Ha!
You can't remove me.
$ rm /tmp/openfile
rm: /tmp/openfile not removed. Permission denied
$ rm -f /tmp/openfile
$ rm /tmp/mine /tmp/mytemp
$ ls -l /tmp
drwxrwxrwt 2 bin bin 1088 Mar 18 21:19 .
dr-xr-xr-x 19 bin bin 608 Mar 18 11:50 ..
-rw------- 1 blf guru 19456 Mar 18 21:18 Ex16566
-rw------- 1 blf guru 10240 Mar 18 21:18 Rx16566
-rw-rw-rw- 1 root sys 35 Mar 16 12:27 openfile
-rw------- 1 root root 32 Mar 10 10:26 protfile
$ cp /dev/null /tmp/openfile
$ cat /tmp/openfile
$ cp /dev/null /tmp/protfile
cp: cannot create /tmp/protfile
$ ls -l /tmp
drwxrwxrwt 2 bin bin 1088 Mar 18 21:19 .
dr-xr-xr-x 19 bin bin 608 Mar 18 11:50 ..
-rw------- 1 blf guru 19456 Mar 18 21:18 Ex16566
-rw------- 1 blf guru 10240 Mar 18 21:18 Rx16566
-rw-rw-rw- 1 root sys 0 Mar 18 21:19 openfile
-rw------- 1 root root 32 Mar 10 10:26 protfile
The only files removed are those owned by user slm (the user in the example). The user slm could not remove any other file, even the accessible file /tmp/openfile. However, the mode setting of the file itself allowed slm to destroy the file contents; this is why the umask setting is important in protecting data. Conversely, the mode on /tmp/protfile, together with the sticky bit on /tmp, makes /tmp/protfile impenetrable.

All public directories should have the sticky bit set. These include, but are not limited to:

  • /tmp

  • /usr/tmp

  • /usr/spool/uucppublic
If you are unsure, it is far better to set the sticky bit on a directory than to leave it off. You can set the sticky bit on a directory with the following command, where directory is the name of the directory:

chmod u+t directory To remove the bit, replace the ``+'' with a ``-'' in the chmod command.

*******************Enjoy*********************