Question about getspnam/getpwnam and ldap

View: New views
4 Messages — Rating Filter:   Alert me  

Question about getspnam/getpwnam and ldap

by Markus Moeller :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


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. 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}.


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.  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 ?

Thank you
Markus



Re: Question about getspnam/getpwnam and ldap

by Buchan Milne-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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 ?

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.

Regards,
Buchan

Re: Question about getspnam/getpwnam and ldap

by Markus Moeller :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


----- 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


Re: Question about getspnam/getpwnam and ldap

by Buchan Milne-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wednesday 09 January 2008 21:13:59 Markus Moeller wrote:

> 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::::::

[...]

> 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)

Your initial description (and the fact that you've sent the question to this
list) seemed to imply the nss_ldap behaviour was wrong.

>
> 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.

[...]

> 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.

Alternatively, you may also consider using nss_ldap on Solaris.

> 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.

Right, but this is a configuration issue (the software would be RFC compliant
in a more typical configuration).

Regards,
Buchan