Basic limiting users

By Kurt Seifried, [email protected]

Many security problems arise from the simple fact that you must allow user access to your systems. In some cases users can be malicious (i.e. internal attacker, university students, etc.) or they may accidentally expose their password to an attacker or simply use a weak password (like their username). In any event it is all to possible for users to attack a system, so it is advisable to monitor users, and limit the amount of damage they can do.


Limiting user commands

Most UNIX systems allow all users to execute all commands. For example the "route" command, any user can execute it, however to use it to alter the routing tables (i.e. add, delete or modify routes) you must be root:

bash-2.05$ id
uid=1000(seifried) gid=1000(seifried) groups=1000(seifried)
bash-2.05$ route delete
route: must be root to alter routing table: Permission denied
bash-2.05$ route show
Routing tables

Destination      Gateway            Flags 
default            UG         link#2             U     

It is advisable to remove the execute bit for "other" (i.e. chmod o-x) on potentially dangerous binaries, especially system utilities (such as the contents of /sbin/*).


Restricting access to cron and at

Users can sometimes avoid execution restrictions and other limits by scheduling jobs to run via cron or at. For example if you place a CPU limit on users if they leave an IRC boot running the system will eventually kill it. If the user has access to cron or at however they can simply schedule a job to run once a minute that checks to see if the IRC bot is running, and if it is not running it will be started. Thus whenever the system kills the bot it will be restarted less then a minute later automatically, allowing it to consume resources with ease.


Restricting access to cron

You can restrict access to cron in two ways, either by explicitly allowing only certain users to use it, or by explicitly denying certain users. To deny certain users simply list them in "/var/cron/deny", for example:

bash-2.05# cat /var/cron/deny 

To deny all users and only allow certain users simply list the users in "/var/cron/allow":

bash-2.05# cat /var/cron/allow 

You can view all the crontab entries in "/var/cron/tabs/"


Restricting access to at

You can restrict access to at in two ways, either by explicitly allowing only certain users to use it, or by explicitly denying certain users. To deny certain users simply list them in "/var/at/at.deny", for example:

bash-2.05# cat /var/at/at.deny 

To deny all users and only allow certain users simply list the users in "/var/at/at.allow":

bash-2.05# cat /var/at/at.allow 

You can view all the at entries in "/var/at/jobs/"


Limiting user resources

Users require resources like CPU time, memory and drive space to do their work. On many systems it is possible for users to hog resources, reducing the usefulness of the system to other users or in some cases actually bringing the server to a slow halt or crashing it. Users can also inadvertently use up more resources then they mean to, limiting their resources can prevent several classes of problems. Programs can crash. generating huge core dumps, or go crazy and user up all the available memory. Something to remember: global limits apply to root, so be careful! If you do not allow root to run enough processes for example cron jobs may fail or you may not even be able to log in to fix any problems.



OpenBSD does not use PAM (Pluggable Authentication Modules) to authenticate logins and enforce user limits, instead it uses login.conf. login.conf in OpenBSD provides most of the same features PAM does with a few notable exceptions, the largest being the inability to limit the number of user logins by user or group.

You can limit user access to resources by editing "/etc/login.conf", limits can be placed on individual users, groups or "default" (any users without a valid login class).

The available limits are:

coredumpsize -- Limits the core file size; usually set to 0 for most users to prevent core dumps.
datasize -- Maximum data size
filesize -- Maximum file size
memorylocked -- Maximum locked-in core memory address space (KB).
memoryuse -- Maximum core memory address space in use (KB).
openfiles -- Maximum number of open files per process.
stacksize -- Maximum stack size
cputime -- Maximum CPU time
maxproc -- Maximum number of processes.
priority -- The priority to run user process with.

Limits can be set with "-max" and "-cur", essentially a hard and soft limit, simply append "-max" or "-cur" to the capability name. Please see login.conf(5) here.



Bash has built in limits, accessed via “ulimit”. Any hard limits cannot be set higher, so if you have limits defined in /etc/profile, or in the users .bash_profile (assuming they cannot edit/delete .bash_profile) you can enforce limits on users with Bash shells. You must also ensure that the user cannot change their login shell, if they use "chsh" to change their shell to ksh for example the next time they login they will have no limits (assuming you cave not put limits on ksh). Documentation is available on ulimit, log in using bash and issue:

[[email protected] /root]# help ulimit
ulimit: ulimit [-SHacdflmnpstuv] [limit]
    Ulimit provides control over the resources available to processes
    started by the shell, on systems that allow such control.  If an
    option is given, it is interpreted as follows:
        -S      use the `soft' resource limit
        -H      use the `hard' resource limit
        -a      all current limits are reported
        -c      the maximum size of core files created
        -d      the maximum size of a process's data segment
        -f      the maximum size of files created by the shell
        -l      the maximum size a process may lock into memory
        -m      the maximum resident set size
        -n      the maximum number of open file descriptors
        -p      the pipe buffer size
        -s      the maximum stack size
        -t      the maximum amount of cpu time in seconds
        -u      the maximum number of user processes
        -v      the size of virtual memory
    If LIMIT is given, it is the new value of the specified resource.
    Otherwise, the current value of the specified resource is printed.
    If no option is given, then -f is assumed.  Values are in 1024-byte
    increments, except for -t, which is in seconds, -p, which is in
    increments of 512 bytes, and -u, which is an unscaled number of

To disallow core files (by setting the maximum size to 0) for example you would add:

ulimit -Hc 0

To set limits globally you would need to edit "/etc/profile", of course this will also affect root, so be careful! To set limits on groups you would need to add scripting to "/etc/profile" that would check for the user's membership in a group and then apply the statements.


Disk usage quota's

Quota allows you to enforce user and group limits on disk usage. Quotas are most often used to prevents users from hogging disk space that is shared with other users (i.e. "/home"). There are several benefits to using quota instead of shell limitations; the first being that you can apply it selectively to disk systems, i.e. you can place a limit on /home/ but not on /tmp/. Additionally you can place limits on the number of inodes used as well as space, running out of inodes is as bad as running out of space since you won't be able to create new files. To see how much space a disk has (in kilobytes):

bash-2.05# df -k
Filesystem  1K-blocks     Used    Avail Capacity  Mounted on
/dev/wd0a     3969655   866510  2904663    23%    /

And to show inode usage:

bash-2.05# df -i
Filesystem  512-blocks     Used    Avail Capacity iused   ifree  %iused  Mounted on
/dev/wd0a      7939310  1733020  5809326    23%   85472  887582     9%   /

To enable quota's you first need to edit /etc/fstab and add a "userquota" or "groupquota" directive to the options for each filesystem you wish to enable quotas on, for example:

/dev/wd0a / ffs rw 1 1
/dev/wd0b /tmp ffs rw 1 1
/dev/wd0c /home ffs rw 1 1

Let's say you want to add quota support to /tmp for groups only, and quota support to /home for users and groups:

/dev/wd0a / ffs rw 1 1
/dev/wd0b /tmp ffs rw,groupquota=/admin/ 1 1
/dev/wd0c /home ffs rw,userquota=/home/quota.user,groupquota=/home/ 1 1

After editing "/etc/fstab" you will need to turn quotas on for each filesystem:

quotaon /dev/wd0b
quotaon /dev/wd0c

You should also ensure that "check_quotas" in "/etc/rc.conf" is set to "YES":


You can then edit user and group quotas with "edquota", it will drop you into your default editor (typically vi) and allow you to manipulate the disk quota's, both space allowed and inodes. If you permit a large enough amount of space and do not restricted the number of inodes a user can use they may be able to enough inodes to cause problems. The simplest example is if a user touches a large number of files, thus creating empty files that take up an inode, and use very little space.

bash-2.05a$ quota
Disk quotas for user seifried (uid 1000): 
     Filesystem  blocks   quota   limit   grace   files   quota   limit   grace
              /     155 1000000 1000000           18334 1000000 1000000        

As you can see the number of inodes used is disproportionate to the size of the files.

Typically you will only need to place quota on file systems directly writeable to the user, usually this consists of:


Soft limits will generate warnings and hard limits will stop the user from using more disk space or inodes.


Monitoring users

Monitoring users may be a requirement of your security policy, or you may have discovered an account that has been compromised and you wish to monitor the attacker when they use it. Unfortunately while this used to be relatively easy there are now a number of new issues that make monitoring users problematic. The increased usage of encrypted protocols like SSH and SSL mean you can no longer simply run a packet sniffer and monitor the attacker. Typically the best way to monitor users is transparently, packet sniffers are ideal for this as you need not make any changes on the server at all. The second best is to use some form of monitoring on the server, this used to be possible with ttysnoop however ttysnoop does not appear to be actively maintained anymore. While you can use tricks like shell logging this will be obvious to a skilled attacker as the first thing they usually do is disable shell logging. If you are monitoring legitimate users you should inform them (this is a legal requirement in some countries and states). If you are monitoring an attacker and intend to use the log files as evidence you should probably consult a lawyer to make sure it is done correctly.



This is actually a really useful tool I stumbled across. It allows you to monitor all traffic going in and coming out of a process, such as an interactive shell. The bad news is that it does not appear to be completely reliable. While testing it sshsniff reliably captured keystrokes sent to the shell and the replies, however when files were cat'ed, i.e. "cat /etc/passwd" the file was not always displayed on the monitoring shell:

[[email protected] test]$ ???\033???[???Acat /etc/passwd???
--- SIGCHLD (Child exited) ---
[[email protected] test]$ ???\033???[???Acat /etc/passwd???
--- SIGCHLD (Child exited) ---
[[email protected] test]$ ???\033???[???Acat /etc/passwd???

[pid 1417]
ftp:x:14:50:FTP User:/home/ftp:/bin/true
rpc:x:32:32:Portmapper RPC user:/:/bin/false
+++ exited (status 0) +++

[pid 1380]
--- SIGCHLD (Child exited) ---

sshsniff is not perfect but it is able to bind to any PID and not just terminals, making it very flexible. You can download it from: One note there is a file called col.c that contains an entry for /etc/passwd, it does not appear to be used but it does raise some questions. If you plan to use this code I strongly suggest you audit it.



With protocols like SSH and SSL being widely deployed simple packet sniffers are no longer as useful as they once were. So people started writing more complex packet sniffers, like dsniff. dsniff is capable of doing man in the middle attacks against SSL and SSH. If you control the server then you can easily proxy connections (as you have access to the secret keys on the server) and thus decrypt the attackers SSH session and monitor what is going on. dsniff is not capable of dealing with all version of the SSH protocol however, so forcing only protocol 1 support on the server can solve this, in your sshd_config file:

Protocol 1

is specified and not:

Protocol 2,1

dsniff is available from:


Account lockout

OpenBSD supports account lockouts using a variety of methods. You can set the user's shell to a non existent shell such as /bin/false (check /etc/shells first however), most daemons will honor this and refuse to let the user log in. Alternatively setting user login restrictions in /etc/login.conf can be used to restrict a user's access. Unfortunately there is no easy way to institute a user account lockout policy based on bad login attempts, and due to the nature of most OpenBSD machines this is largely unnecessary. OpenBSD enfoces strong passwords by default, and because most OpenBSD machines are on public networks it would be trivial for an attacker to abuse an automated account lockout policy, denying legitimate users access to the server.



Last updated on 6/6/2002

Copyright Kurt Seifried 2002 [email protected]