----- Original Message -----
From: "Buchan Milne" <
bgmilne@...>
To: "Markus Moeller" <
huaraz@...>
Cc: <
nssldap@...>; <
ldap-nis@...>
Sent: Wednesday, January 09, 2008 4:24 PM
Subject: Re: [nssldap] Question about getspnam/getpwnam and ldap
> On Wednesday 09 January 2008 15:03:07 Markus Moeller wrote:
>> Hi,
>>
>> I am looking into replacing NIS with LDAP and came to a point where I
>> still need to support getspnam/getpwnam for password checks as I have
>> legacy applications which can not be changed.
>>
>> The question I have is how rfc 2307 is and should be enforced as I
>> noticed
>> differences between Linux and Solaris. The rfc says:
>>
>> userPassword values which do not adhere to this syntax MUST NOT be
>> used for authentication. The DUA MUST iterate through the values of
>> the attribute until a value matching the above syntax is found. Only
>> if encryptedpassword is an empty string does the user have no
>> password. DUAs are not required to consider encryption schemes which
>> the client will not recognize; in most cases, it may be sufficient to
>> consider only "crypt".
>>
>>
>> Firstly does/should getspnam enforce "userPassword values which do not
>> adhere to this syntax MUST NOT be used for authentication" by rejecting
>> userpassword entries which don't follow rfc 2307 or is it up to the
>> application since the rfc states "DUAs are not required to consider
>> encryption schemes which the client will not recognize; in most cases,
>> it
>> may be sufficient to consider only "crypt"." too (assuming getspnam in
>> nss_ldap is the DUA) ? Solaris seems to enforce it in getspnam whereas
>> Linux doesn't.
>
> Can you be more specific ? Do you mean you are using something besides
> clear
> (no encryption scheme identifier) or crypt, and seeing the password hash ?
>
The unixuserpassword attribute in AD does not use an encryption scheme
identifier (but stores the crypt password). It has the same value as a
"normal" /etc/shadow entry would have in the password field (e.g.
1c8n1k6cGDbSo).
A getent shadow markus (with markus being an ldap user in AD) gives on
Linux:
markus:1c8n1k6cGDbSo:13565::::::
and the following checkpass succeeds:
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <shadow.h>
#include <crypt.h>
#include <string.h>
int main (int argc,char **argv)
{
struct passwd *pwd_info;
struct spwd *spwd_info;
char *ct_passwd;
char *enc_passwd;
char *password;
int i;
if ( argc < 2) {
exit(1);
} else {
if ((pwd_info = getpwnam(argv[1])) == NULL ) {
fprintf (stderr, "Call to getpwuid failed\n");
exit (1);
}
}
for (i=0;i<3;i++) {
ct_passwd = getpass("Enter password: ");
if ((spwd_info = getspnam (pwd_info -> pw_name)) == NULL) {
fprintf (stderr, "Call to getspnam failed. Try password field
from getpwnam\n");
password=pwd_info->pw_passwd;
} else {
password=spwd_info->sp_pwdp;
}
enc_passwd = crypt (ct_passwd, password);
if (strcmp (enc_passwd, password) == 0) {
printf ("Passwords match.\n");
break;
} else
printf ("Passwords don't match.\n");
}
return (0);
}
A getent shadow does not work on OpenSolaris. But the below test program
(using OpenSolaris native nss_ldap libraries) gives:
./test_shadow markus
passwd: name=markus
passwd: passwd=x
passwd: uid=500
passwd: gid=10000
passwd: gecos=Markus Moeller
passwd: homedir=/export/home/markus
passwd: shell=/bin/ksh
shadow: name=markus
shadow: passwd=*NP*
shadow: lastchg=-1(Wed Dec 31 01:00:00 1969)
shadow: min=-1
shadow: max=-1
shadow: warn=-1
shadow: inactive=-1
shadow: expire=-1(Wed Dec 31 01:00:00 1969)
If I modify the unixuserpassword value in AD from 1c8n1k6cGDbSo to
{crypt}1c8n1k6cGDbSo OpenSolaris gives:
./test_shadow markus
passwd: name=markus
passwd: passwd=x
passwd: uid=500
passwd: gid=10000
passwd: gecos=Markus Moeller
passwd: homedir=/export/home/markus
passwd: shell=/bin/ksh
shadow: name=markus
shadow: passwd=1c8n1k6cGDbSo
shadow: lastchg=-1(Wed Dec 31 01:00:00 1969)
shadow: min=-1
shadow: max=-1
shadow: warn=-1
shadow: inactive=-1
shadow: expire=-1(Wed Dec 31 01:00:00 1969)
and the checkpass succeeds too.
test_shadow.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <pwd.h>
#include <shadow.h>
int main(int argc, char ** argv) {
char *user=NULL;
char *date=NULL,*dp;
time_t time;
struct passwd *pw=NULL;
struct spwd *spw=NULL;
if ( argc > 1 ) {
user = argv[1];
} else {
exit(99);
}
pw = getpwnam(user);
spw = getspnam(user);
if ( pw ) {
printf("passwd: name=%s\n",pw->pw_name);
printf("passwd: passwd=%s\n",pw->pw_passwd);
printf("passwd: uid=%d\n",pw->pw_uid);
printf("passwd: gid=%d\n",pw->pw_gid);
printf("passwd: gecos=%s\n",pw->pw_gecos);
printf("passwd: homedir=%s\n",pw->pw_dir);
printf("passwd: shell=%s\n",pw->pw_shell);
printf("\n\n");
}
if ( spw ) {
printf("shadow: name=%s\n",spw->sp_namp);
printf("shadow: passwd=%s\n",spw->sp_pwdp);
time=spw->sp_lstchg*3600*24;
date=ctime(&time);
if ((dp=strchr(date,'\n')) != NULL ) {
*dp = '\0';
}
printf("shadow: lastchg=%ld(%s)\n",spw->sp_lstchg,date);
printf("shadow: min=%d\n",spw->sp_min);
printf("shadow: max=%d\n",spw->sp_max);
printf("shadow: warn=%d\n",spw->sp_warn);
printf("shadow: inactive=%d\n",spw->sp_inact);
time=spw->sp_expire*3600*24;
date=ctime(&time);
if ((dp=strchr(date,'\n')) != NULL ) {
*dp = '\0';
}
printf("shadow: expire=%d(%s)\n",spw->sp_expire,date);
}
}
My problem is now that AD does not have the {crypt} prefix and Solaris
requires it and I wanted to understand if the RFC is the reason for
requiring the {crypt} prefix. From how I read it is optional.
At the end it may mean I have to use AD as a NIS server and not as an ldap
server.
> I tested with just {ssha}, and 'getent shadow bgmilne' did not show a
> hash:
>
> [root@tiger ~]# getent shadow bgmilne
> bgmilne:*:12920::99999:7:::0
>
> I added a {crypt} password (so I have both {ssha} and {crypt}), and
> then 'getent shadow bgmilne' showed just the {crypt} hash in place of the
> *.
> I did not test clear text passwords (with no identifier).
>
>> Also Windows 2003 R2 uses unixuserpassword (this entry can
>> be synchronised with the Kerberos password in AD and is therefore
>> preferred) which does not follow rfc2307 as it contains the hash without
>> {crypt}.
>
> Maybe they expect you to type the hash :-P.
>
>> The rfc states also:
>>
>> A DUA MAY utilise the attributes in the shadowAccount class to
>> provide shadow password service (getspnam() and getspent()). In such
>> cases, the DUA MUST NOT make use of the userPassword attribute for
>> getpwnam() et al, and MUST return a non-matchable password (such as
>> "x") to the client instead.
>>
>> I noticed on Linux that provides the user with both the userpassword in
>> getpwnam and in getspnam.
>
> I tested as follows:
>
> $ perl -e 'my @foo = getpwnam("bgmilne");print "$foo[1]\n";'
>
> and I get the password hash if the proxyuser (binddn in nss_ldap's
> ldap.conf)
> has read access to userPassword (which usually isn't required or
> desirable).
> Otherwise, I get:
>
> [bgmilne@tiger ~]$ perl -e 'my @foo = getpwnam("bgmilne");print
> "$foo[1]\n";'
> *
>
> If under these conditions, the rootbinddn has read access to userPassword,
> root will still get the hash via getpwnam. I haven't tested with getspnam,
> as
> there is no perl builtin ...
>
>> Is that against RFC 2307 ? Does this mean a
>> getpwnam call need internally do a getspnam call to check if a password
>> is
>> provided before returning to the application ?
>
> Please provide more information on how you arrive at your conclusions
> (including your configuration - at least what the DNs you use for binddn
> and
> rootbinddn have read access to).
>
> In the typical configuration, where the proxy user does not have read
> access
> to userPassword, it seems nss_ldap under Linux behaves (mostly) correctly.
>
In my case the proxy user has access to the userpassword attribute
(unixuserpassword in AD) and my test_shadow test would give:
./test_shadow markus
passwd: name=markus
passwd: passwd=1c8n1k6cGDbSo
passwd: uid=500
passwd: gid=10000
passwd: gecos=Markus Moeller
passwd: homedir=/export/home/markus
passwd: shell=/bin/ksh
shadow: name=markus
shadow: passwd=1c8n1k6cGDbSo
shadow: lastchg=-1(Wed Dec 31 01:00:00 1969)
shadow: min=-1
shadow: max=-1
shadow: warn=-1
shadow: inactive=-1
shadow: expire=-1(Wed Dec 31 01:00:00 1969)
and if I understand the rfc right getpwnam should return x and not the
password.
> Regards,
> Buchan
>
Regards
Markus