Limiting users to one login session (no coding required)
By Darrenmoffat-Oracle on Apr 18, 2007
I've often seen a request for restricting users to having a single login session to a given machine at a time. I've also seen requests for having a single login session network wide.
My response to the first of these is usually, write a PAM module to do it, probably using the data stored in utmpx/wtmpx or have the module keep its own state.
My response to the second is usually - this is a much harder problem to implement client side and it is probably better to solve this in a networked authentication service such as in the LDAP directory by having it deny the login. However that can cause other problems; particularly in large deployments where there are multiple directory servers and where users need to authenticate to the LDAP server not just for initial login but for viewing the address book or for IMAP or SMTP authentication.
The first of these came up again yesterday on the general OpenSolaris discussion alias. Instead of my usual "write a PAM module" response I worked out how to do this using the OpenSolaris resource control framework and no coding.
Here's how to do it:
Put each user into their own resource control project, see project(4) and resource_control(5) man pages for more information.
For each of those user projects set the maximum number of tasks that can be created to be 1.
For my example user 'jru' add this to /etc/project (or the project(4) database in your nameservice) to limit them to one login session.
Either edit /etc/project with an entry like this
or use the projadd(1M) command like this:
# projadd -K 'project.max-tasks=(privileged,1,deny)' user.jru
This uses the special 'user.' syntax which also makes this the initial project for the user jru. For this to work you also need to make sure that the user is NOT part of the special 'default' project, otherwise they would be able to use newtask(1) to create more tasks. To do that make the 'default' project not contain any users at all by setting the list of users in the project to '!\*' eg:
This requires that all users on the system explicitly be a member of some project, either one for their user or assigned one they share with other users using the project keyword in user_attr(4) or a 'group.' project that is implicit based on their unix group membership. This is required since users are normally always in the 'default' project that we have just excluded all of them from. If you don't do this they won't be able to login at all.
Note that the root user already has a special 'user.root' project defined for them in the standard /etc/project file.
This is how it will look to users:
An attempt to login a second time (ie create a new task in the project) will fail, eg:
$ ssh jru@localhost
Resource control limit has been reached
An attempt to create more processes than is allowed will fail something like this:
$ sleep 500 &
zsh: fork failed: resource temporarily unavailable
All pretty easy and remember that you can do this network wide by putting the project(4) database in your nameservice of choice (NIS, NIS+, LDAP).
Update With respect to the comment from James: tasks are NOT UNIX processes.
Limiting the number of tasks a user can create does NOT limit the number of processes, so it doesn't
impact using pipes. In normal operation the only time a new task is created is at login time or by
explicitly running newtask(1) to create one or change the task a give process is in. tasks aren't a UNIX thing
but are part of the Solaris extended accounting system. Maybe I confused the issue by mentioning that
for a given project you can also limit the number of processes a user can have. To avoid that confusion
in the future I've removed that part so we only talk about tasks now.