Validating A User/Password Pair + Getting Groups On Unix

T

Tim Daneliuk

OK, I've Googled for this and cannot seem to quite find what I need.
So, I turn to the Gentle Geniuses here for help. Here is what I
need to do from within a script:

Given a username and a password (plain text):

1) Validate that the password is correct for that user *without actually logging in*.

2) If the password is valid, return a list of all the groups the user belongs to.
Otherwise, return some error string.

I seem to not be able to crack how to do 1.

I can do 2) by brute force - just parse through /etc/group - but this
misses the primary group a given user may belong to - and that requires
also scanning /etc/passwd and then looking up the corresponding primary
group in /etc/group. Is there a better way?

TIA,
 
K

Kanenas

On 28 Feb 2005 20:17:58 EST, Tim Daneliuk <[email protected]>
wrote:

[...]
Given a username and a password (plain text):

1) Validate that the password is correct for that user *without actually logging in*.
The 'pwd' module probably won't be able (and won't try) to read the
shadow password file, so 'pwd' won't be of use. There may not be a
Python module which handles your local authentication scheme (there's
a 'krb5' module for Kerberos authentication), so you may need to write
one. The best approach may be to write an extension module in C or
C++ which wraps around whatever local authentication functions are
appropriate (e.g. a 'pam' module for PAM, an 'auth' module for BSD).
You'd only need to wrap the functions needed for simple pass/fail
authentication (e.g. auth_userokay), but the other functions could
easily be added to the extension later if needed.

The process that calls the authentication functions will probably need
special access privileges so that the functions can succesfully accept
or reject the password. The man pages for the authentication
functions should have details. For example, auth_userokay calls
getpwnam, which requires the effective uid to be 0 (or, on some
systems, the user to be in the "_shadow" group) for it to include the
encrypted password in the passwd entry.

If you're not sure what authentication scheme your system uses, try
`man -s 3 authenticate` or examine "/usr/src/usr.bin/login/login.c".

extending Python:
http://www.python.org/doc/2.4/ext/ext.html

Python/C API:
http://www.python.org/doc/2.4/api/api.html

Information on Linux-PAM
http://www.kernel.org/pub/linux/libs/pam/

You could even add support for the full authentication API to your
module and contribute the extension to the Python community.
http://www.python.org/download/Contributed.html.

2) If the password is valid, return a list of all the groups the user belongs to.
Otherwise, return some error string.
[...]
I can do 2) by brute force - just parse through /etc/group - but this
misses the primary group a given user may belong to - and that requires
also scanning /etc/passwd and then looking up the corresponding primary
group in /etc/group. Is there a better way?
Slightly better would be to use the grp and pwd modules:
http://www.python.org/doc/2.4/lib/module-grp.html
http://www.python.org/doc/2.4/lib/module-pwd.html

Even better would be to write an extension or add to the grp module to
wrap around local group database access functions (e.g. getgrouplist).
See the 'getgrouplist' man page for more information and examine the
source of the `groups` command (probably
"/usr/src/usr.bin/groups/groups.c") or `id` command (should be
"/usr/src/usr.bin/id/id.c") for other group DB access functions.

You could also call the `groups` command via 'os.popen(...)'.
 
K

Kanenas

On 28 Feb 2005 20:17:58 EST, Tim Daneliuk <[email protected]>
wrote:

[...]
Given a username and a password (plain text):

1) Validate that the password is correct for that user *without actually logging in*.
The naive solution is to use the 'crypt' module to encrypt the alleged
password, use 'pwd.getpwuid' or 'pwd.getpwnam' to get the user's
encrypted password (assuming the python process has appropriate access
privileges) and compare the two. This is naive in that:
* 'pwd.getpw*' may not retrieve the encrypted password even though the
current process has appropriate access privileges
* the password may be for an encryption or authentication scheme other
than that provided by 'crypt'.
Using the local authentication scheme shouldn't have these
shortcomings.

There may not be a Python module which handles your local
authentication scheme (there's a 'krb5' module for Kerberos
authentication), so you may need to write one. This could be done by
an extension module in C or C++ which wraps around whatever local
authentication functions are appropriate (e.g. a 'pam' module for PAM,
an 'auth' module for BSD). You'd only need to wrap the functions
needed for simple pass/fail authentication (e.g. 'auth_userokay'), but
the other functions could easily be added to the extension later if
needed.

If you're not sure what authentication scheme your system uses, try
`man -s 3 authenticate` or examine "/usr/src/usr.bin/login/login.c".

Whichever approach you use, the process that calls the authentication
functions needs special access privileges so that the functions can
succesfully accept or reject the password. The man pages for the
authentication functions should have details. For example, 'getpwnam'
(used by 'auth_userokay' and the 'pwd' module) requires the effective
uid to be 0 (or, on some systems, the user to be in the "_shadow"
group) for it to include the encrypted password in the returned passwd
entry.

'crypt' and 'pwd' modules:
http://www.python.org/doc/2.4/lib/module-crypt.html
http://www.python.org/doc/2.4/lib/module-pwd.html

extending Python:
http://www.python.org/doc/2.4/ext/ext.html

Python/C API:
http://www.python.org/doc/2.4/api/api.html

Information on Linux-PAM
http://www.kernel.org/pub/linux/libs/pam/

You could even add support for the full authentication API to your
module and contribute the extension to the Python community.
http://www.python.org/download/Contributed.html.

2) If the password is valid, return a list of all the groups the user belongs to.
Otherwise, return some error string.
[...]
I can do 2) by brute force - just parse through /etc/group - but this
misses the primary group a given user may belong to - and that requires
also scanning /etc/passwd and then looking up the corresponding primary
group in /etc/group. Is there a better way?
Slightly better would be to use the 'grp' and 'pwd' modules. One
advantage of this is it should support networked user databases (such
as YP).
http://www.python.org/doc/2.4/lib/module-grp.html
http://www.python.org/doc/2.4/lib/module-pwd.html

If you've grabbed the password entry for a user during authentication,
you've already got the login group but you'll still need to check for
additional groups. You could create a dictionary which maps user
names or IDs to groups. This would still require processing all
groups (via 'grp.getpwall()'), but is more efficient if you need to
fetch the groups of more than one user in the life of the process
(from the outline, I'm guessing this will only be the case if the
program is a server of some sort). Just make sure you have a method
to re-process the group database into the group dictionary in case the
group file changes.

Even better would be to write an extension or add to the grp module to
wrap around local group database access functions (e.g. getgrouplist).
See the 'getgrouplist' man page for more information and examine the
source of the `groups` command (probably
"/usr/src/usr.bin/groups/groups.c") or `id` command (should be
"/usr/src/usr.bin/id/id.c") for other group DB access functions.

You could also call the `groups` command via 'os.popen(...)'.
 
S

Skip Montanaro

1) Validate that the password is correct for that user *without Kanenas> The 'pwd' module probably won't be able (and won't try) to read
Kanenas> the shadow password file, so 'pwd' won't be of use.

Note that an spwd module was recently added to Python's CVS repository. I
imagine it will be in 2.5.

Skip
 
K

Kanenas

Kanenas> The 'pwd' module probably won't be able (and won't try) to read
Kanenas> the shadow password file, so 'pwd' won't be of use.

Note that an spwd module was recently added to Python's CVS repository. I
imagine it will be in 2.5.

Skip

It turns out 'pwd' uses the system 'getpwuid' and 'getpwnam' rather
than parsing /etc/passwd, so it can get the encrypted password if the
getpw* functions read the shadow passwd and the Python process has
EUID 0 (or whatever access rights getpw* use to determine when to
return the encrypted passwd). I misread (was misled by?) the 'pwd'
documentation:

"However most modern unices use a so-called shadow password
system. On those unices the field pw_passwd only contains a asterisk
('*') or the letter "x" where the encrypted password is stored in a
file /etc/shadow which is not world readable."

This is true if the getpw* don't read from the shadow passwd, which is
the case for Solaris and Linux. Linux and Solaris use getsp*, which
'spwd' is based on, to manage the shadow passwd. On OpenBSD and
FreeBSD, getpw* read from the shadow passwd and the getsp* don't
exist.

In summation, use 'pwd' to retrieve encrypted password on OpenBSD and
FreeBSD (and others?), 'spwd' on Linux and Solaris (and others?).
Assuming one goes this route.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top