Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to get shadow password information under Solaris

0 views
Skip to first unread message

Matija Grabnar

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
I want ot use perl for an application which needs to verify a user's
password. However, we are running Solaris, which has shadow passwords,
and I can't get the user's password using getpwnam:

perl -e 'print join(" ",getpwnam(matija))."\n"'
matija x 10008 10 Matija Grabnar Matija Grabnar /export/home/matija /bin/bash

In C, this is done using getspnam function, but I don't see that function
in the list of Perl functions.

I could, of course, parse /etc/shadow, but the fellow who wrote the spec
vetoed that approach - he wants to be safe in case Solaris changes the
format of /etc/shadow.

So currently it looks like the backend which checks the password
will have to be written in C - as will anything that has to do
with changing or modifying the passwords. I consider this a waste...

Is there realy no support for shadow passwords in Perl?


--
"My name is Not Important. Not to friends.
But you can call me mr. Important" - Not J. Important
Matija....@arnes.si

Tom Phoenix

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
On 16 Nov 1999, Matija Grabnar wrote:

> In C, this is done using getspnam function, but I don't see that
> function in the list of Perl functions.

If you have syscall() (and you probably do), you might be able to use
that. Or you could use XS code to call getspnam.

> I could, of course, parse /etc/shadow, but the fellow who wrote the
> spec vetoed that approach - he wants to be safe in case Solaris
> changes the format of /etc/shadow.

Right on. Using getspnam properly will be faster and more reliable.

> Is there realy no support for shadow passwords in Perl?

We can't put _every_ function available on _every_ system into the core!
But that's why syscall was invented. And XS, too, although that's more
versatile.

If you make a module to implement getspnam, which shouldn't be too hard,
it would be nice to put it on CPAN for everyone to use. Of course, someone
else may have already done so, so check first. Thanks!

--
Tom Phoenix Perl Training and Hacking Esperanto
Randal Schwartz Case: http://www.rahul.net/jeffrey/ovs/

Tom Christiansen

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
[A courtesy cc of this moderated newsgroup posting was
also privately mailed to various parties.]

In comp.lang.perl.moderated, Matija....@arnes.si writes:

:matija x 10008 10 Matija Grabnar Matija Grabnar /export/home/matija /bin/bash
:
:In C, this is done using getspnam function, but I don't see that function


:in the list of Perl functions.

Many of us consider that to be a design flaw. We prefer this less
intrusive solution:

% man 3 getpwnam
....
These routines have been written to ``shadow'' the password file,
e.g., allow only certain programs to have access to the encrypted
password. If the process which calls them has an effective UID of
0, the encrypted password will be returned, otherwise, the password
field of the returned structure will point to the string `*'.

And by way of demonstration:

% perl -e 'print join(" ",getpwnam(tchrist))."\n"'
tchrist * 1001 1001 0 Tom Christiansen /home/tchrist /usr/local/bin/tcsh 0

% sudo perl -e 'print join(" ",getpwnam(tchrist))."\n"'
tchrist <LONG_STRING_OF_NOISE> 1001 1001 0 Tom Christiansen /home/tchrist /usr/local/bin/tcsh 0

And lest you think that a fluke of Perl, here's some C code:

% cat getme.c
#include <pwd.h>
main() {
struct passwd *p = getpwnam("tchrist");
printf("%s %s %d %d %s %s %s\n",
p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid,
p->pw_gecos, p->pw_dir, p->pw_shell);
}

% cc getme.c

% a.out
tchrist * 1000 1000 Tom Christiansen /home/tchrist /usr/local/bin/tcsh

% sudo a.out
tchrist <LONG_STRING_OF_NOISE> 1000 1000 Tom Christiansen /home/tchrist /usr/local/bin/tcsh

Here's the code that does this:

% cd /usr/src/lib/libc/gen
% cat getpwent.c
...
static int
__initdb()
{
static int warned;
char *p;
-> p = (geteuid()) ? _PATH_MP_DB : _PATH_SMP_DB;
_pw_db = dbopen(p, O_RDONLY, 0, DB_HASH, NULL);
if (_pw_db)
return (1);
if (!warned)
syslog(LOG_ERR, "%s: %m", p);
warned = 1;
return (0);
}

See the arrow? What's happening is that, per its documentation,
getpwnam(3) detects whether it is running under sufficient permissions,
and if so, automatically does the right thing. The reason that this
is the preferred solution by many of us is because it avoids having to
recode your program. Upgrade the system security, upgrade your C library,
but don't make people hack their code if you don't have to do so.

Now, I hold out little hope that Solaris's owners intend to fix this bug,
which, doubtless, they shall refer to as a feature. If anyone catches
wind of why SysV has decided that making programmers re-code things is
deemed more desirable than just making it work in the intuitive love to
hear the canonical fabulation.

So in Perl, we fix the Solaris bug via the following joys in pp_sys.c.
Note again the arrows:

% cd /usr/local/src/perl5.005_62/
% cat pp_sys.c
/* ..... */
#ifdef I_SHADOW
-> /* Shadow password support for solaris - p...@cs.umd.edu
-> * Not just Solaris: at least HP-UX, IRIX, Linux.
-> * the API is from SysV. --jhi */
#ifdef __hpux__
/* There is a MAXINT coming from <shadow.h> <- <hpsecurity.h> <- <values.h>
* and another MAXINT from "perl.h" <- <sys/param.h>. */
#undef MAXINT
#endif
#include <shadow.h>
#endif

/* ..... */

PP(pp_gpwent)
{
djSP;
#if defined(HAS_PASSWD) && defined(HAS_GETPWENT)
I32 which = PL_op->op_type;
register SV *sv;
struct passwd *pwent;
STRLEN n_a;
#if defined(HAS_GETSPENT) || defined(HAS_GETSPNAM)
struct spwd *spwent = NULL;
#endif

if (which == OP_GPWNAM)
pwent = getpwnam(POPpx);
else if (which == OP_GPWUID)
pwent = getpwuid(POPi);
else
pwent = (struct passwd *)getpwent();

#ifdef HAS_GETSPNAM
if (which == OP_GPWNAM) {
if (pwent)
spwent = getspnam(pwent->pw_name);
}
# ifdef HAS_GETSPUID /* AFAIK there isn't any anywhere. --jhi */
else if (which == OP_GPWUID) {
if (pwent)
spwent = getspnam(pwent->pw_name);
}
# endif
# ifdef HAS_GETSPENT
else
spwent = (struct spwd *)getspent();
# endif
#endif

/* ..... */

Hm... I'm not sure why this always calls getpwnam() and then also
getspwnam(). I'd've imagined that the latter alone would have sufficed
on such problematic systems.

Anyway, it appears that it's all of SysV and not merely Solaris that
has decided to traverse this painful route. As you might have guessed
by now, I'm not running SysV. I'm running BSD, the preferred solution
to SysV bugs. :-)

It isn't particularly flagrant, but Perl now and then displays its BSD
heritage in preference over SysV semantics. This is one area where I'm
glad it does. Can anyone spot some other, older examples where Perl
taste more BSDish than SysVish?

--tom
--
/* And you'll never guess what the dog had */
/* in its mouth... */
--Larry Wall in stab.c from the v4.0 perl source code

Martien Verbruggen

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
On 16 Nov 1999 13:31:42 GMT,

Matija Grabnar <mat...@rzenik.arnes.si> wrote:
> I want ot use perl for an application which needs to verify a user's
> password. However, we are running Solaris, which has shadow passwords,
> and I can't get the user's password using getpwnam:
>
> perl -e 'print join(" ",getpwnam(matija))."\n"'
> matija x 10008 10 Matija Grabnar Matija Grabnar /export/home/matija /bin/bash
>
> In C, this is done using getspnam function, but I don't see that function
> in the list of Perl functions.
>
> I could, of course, parse /etc/shadow, but the fellow who wrote the spec
> vetoed that approach - he wants to be safe in case Solaris changes the
> format of /etc/shadow.

And of course, only root can read the shadow password file. I doubt
the format would change much on the platform you're on, but it is not
protable between platforms. And that's where the real problem lies.

> So currently it looks like the backend which checks the password
> will have to be written in C - as will anything that has to do
> with changing or modifying the passwords. I consider this a waste...

Yes, but

perl FAQ 8 has the following to say:

How do I modify the shadow password file on a Unix system?

If perl was installed correctly, and your shadow library was
written properly, the getpw*() functions described in the
perlfunc manpage should in theory provide (read-only) access
to entries in the shadow password file. To change the file,
make a new shadow password file (the format varies from
system to system - see the passwd(5) manpage for specifics)
and use pwd_mkdb(8) to install it (see the pwd_mkdb(5)

Since you only need to read the password file, and not write it, your
perl should provide you with that access, if installed correctly. Real
life, however, isn't always that nice. And the above isn't really very
applicable to Solaris (passwd(4) and no pwd_mkdb)

Besides that, I don't think that you'd be able to get to the passwd
with the getpw* functions anyway, unless perl was compiled
specifically with the platform dependent calls in it. And since a grep
of the source tree of perl 5.005_03 does not find any 'getsp' matches
that have anything to do with shadow passwords anywhere, I doubt that
very much

CPAN lists a module that may apply:

cpan> i /Passwd::Solaris/
Module id = Passwd::Solaris
CPAN_USERID EESTABROO (Eric Estabrooks <er...@urbanrage.com>)
CPAN_VERSION 0.65
CPAN_FILE E/EE/EESTABROO/Passwd-Solaris-0.65.tar.gz
INST_FILE (not installed)

But it doesn't seem to be available from CPAN itself, and also is
absent from the module list. You could try contacting the person
listed there.

Otherwise, the best thing may just be to write a small XS interface to
the system or library calls that are necessary. It may be, as you put
it, a waste, but after you finish, you could put your module on CPAN,
so the time is wasted only one :)

Martien
--
Martien Verbruggen |
Interactive Media Division | Never hire a poor lawyer. Never buy
Commercial Dynamics Pty. Ltd. | from a rich salesperson.
NSW, Australia |

nort...@valen.eng.mindspring.net

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
mat...@rzenik.arnes.si (Matija Grabnar) writes:

> I want ot use perl for an application which needs to verify a user's
> password. However, we are running Solaris, which has shadow passwords,
> and I can't get the user's password using getpwnam:
>
> perl -e 'print join(" ",getpwnam(matija))."\n"'
> matija x 10008 10 Matija Grabnar Matija Grabnar /export/home/matija /bin/bash
>
> In C, this is done using getspnam function, but I don't see that function
> in the list of Perl functions.
>
> I could, of course, parse /etc/shadow, but the fellow who wrote the spec
> vetoed that approach - he wants to be safe in case Solaris changes the
> format of /etc/shadow.
>

> So currently it looks like the backend which checks the password
> will have to be written in C - as will anything that has to do
> with changing or modifying the passwords. I consider this a waste...
>

> Is there realy no support for shadow passwords in Perl?

Under Solaris, getpwnam has the same behavior running as root as it
does running as a normal person:

# perl -e 'print join(" ", getpwnam(northrup))."\n"'
northrup x 322 1 Dylan Northrup Dylan Northrup /home/northrup /bin/csh
# exit
soleil% perl -e 'print join(" ", getpwnam(northrup))."\n"'
northrup x 322 1 Dylan Northrup Dylan Northrup /home/northrup /bin/csh
soleil%

Under Linux, the behavior is different for root than it is for a
mortal:

valen# perl -e 'print join(" ", getpwnam(northrup))."\n"'
northrup <encrypted passwordd> 500 500 /home/northrup /bin/zsh
valen# exit
valen> perl -e 'print join(" ", getpwnam(northrup))."\n"'
northrup x 500 500 /home/northrup /bin/zsh

So, you won't be able to do this without a) manually parsing the
shadow password file, b) use SWIG or XS to interface with getspnam or
c) switch to a platform which allows perl access to the shadow
information when running as root (which may be a feature or a bug
depending on how you look at it ;-)

--
Dylan Northrup <*> nort...@pobox.com <*> http://pobox.com/~northrup <*>
d...@nwe.ufl.edu is not my e-mail address and anybody wishing to contact me
should not use it - Happy Harvesting, boys!

Tom Christiansen

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
[courtesy cc of this posting mailed to cited author]

In comp.lang.perl.moderated, Tom Phoenix <root...@redcat.com> writes:
:If you have syscall() (and you probably do), you might be able to use


:that. Or you could use XS code to call getspnam.

No, I'm sorry, but it looks like you don't understand what the Perl
syscall() function does. Please review the documentation, and if you
can kindly show me where it has misled you in this matter, I shall duly
amend it.

Just because you can get at syscall(2) and use the indices from
<syscall.h> to get at anything in the kernel jump table doesn't mean
you can in so doing, call arbitrary functions from random libraries.

:We can't put _every_ function available on _every_ system into the core!


:But that's why syscall was invented.

Nope. Here's an excerpt from the source code from
/usr/src/lib/libc/arch/i386/sys/syscall.S:

SYSENTRY(syscall)
pop %ecx /* rta */
pop %eax /* syscall number */
push %ecx
int $0x80
push %ecx /* Keep stack frame consistant */
jc err
ret
err:
jmp cerror

Please consider what that `int $0x80' up there is doing. :-)

If you would prefer to peruse the VAX version from
/usr/src/lib/libc/arch/vax/sys/syscall.S, it looks like this:

ENTRY(syscall, 0)
movl 4(ap),r0 # syscall number
subl3 $1,(ap)+,(ap) # one fewer arguments
chmk r0
jcs 1f
ret
1:
jmp cerror

In short, you have to get yourself into the *hardware*'s kernel mode--or
ring 0, or privileged mode, or whatever your processor calls it.
There's a big, big, *big* difference between intro(2) and intro(3).
In one of them you are executing in the kernel, and in the other,
well, you aren't. The syscall(2) function teleports you to this
other dimension, this higher plane of power and privilege--and often
of funny-looking addresses as well. All in all, this is a rather more
dramatic transition than simply arranging to call a mundane, non-kernel
function back in mere user space.

--tom
--
I never think of the future. It comes soon enough. --Albert Einstein

Bill Jones

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
on 11/16/1999 8:31 AM, Matija Grabnar at mat...@rzenik.arnes.si wrote:

> Is there realy no support for shadow passwords in Perl?
>

Yes, there is. :)

A basic search of http://www.perl.com turns up 3 links:

1) http://www.wcnet.org/~jzawodn/perl/AcctInfo/index.html
2) ftp://ftp.eur.nl/pub/homebrew/Shadow-0.01.tar.gz
3) http://web.fccj.org/~wcjones/WebPass.html


Not sure what you will find useful; I wrote the WebPass stuff - but I used
Expect http://expect.nist.gov (the complete development system - not the
Expect.pm module) to handle passwords...

There are many ways to resolve your password problem -
some more or less secure than others...

HTH, -Sneex- :]
____________________________________________________________________
Bill Jones * Systems Programmer * http://www.fccj.org/cgi/mail?sneex

James Peregrino

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to
nort...@valen.eng.mindspring.net writes:

> Under Solaris, getpwnam has the same behavior running as root as it
> does running as a normal person:
>
> # perl -e 'print join(" ", getpwnam(northrup))."\n"'
> northrup x 322 1 Dylan Northrup Dylan Northrup /home/northrup /bin/csh
> # exit
> soleil% perl -e 'print join(" ", getpwnam(northrup))."\n"'
> northrup x 322 1 Dylan Northrup Dylan Northrup /home/northrup /bin/csh
> soleil%
>

So now I'm confused. I've seen this behavior on my solaris
boxes and I've considered it a bug, but I haven't seen it
fixed in any version of Perl (not that I've ever reported it
:-( ). Tom's first post to this thread seems so say that this
behavior is desirable, but his example contradicts yours:

[Tom's example]


% perl -e 'print join(" ",getpwnam(tchrist))."\n"'
tchrist * 1001 1001 0 Tom Christiansen /home/tchrist /usr/local/bin/tcsh 0

% sudo perl -e 'print join(" ",getpwnam(tchrist))."\n"'
tchrist <LONG_STRING_OF_NOISE> 1001 1001 0 Tom Christiansen /home/tchrist /usr/local/bin/tcsh 0

Is Tom's example running off some other SYSV box that isn't
Solaris?

> Under Linux, the behavior is different for root than it is for a
> mortal:
>
> valen# perl -e 'print join(" ", getpwnam(northrup))."\n"'
> northrup <encrypted passwordd> 500 500 /home/northrup /bin/zsh
> valen# exit
> valen> perl -e 'print join(" ", getpwnam(northrup))."\n"'
> northrup x 500 500 /home/northrup /bin/zsh

So my expectation would be that Perl's getpwnam() on Solaris
should behave exactly like the Solaris C library function.
Especially since the perldoc for it assumes you've also read
the man pages.

In the end my approach is to read /etc/shadow manually using
the traditional split(/:/) technique.

-James
--
James Peregrino
Harvard Div. Continuing Education

Tom Christiansen

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to
[courtesy cc of this posting mailed to cited author]

In comp.lang.perl.moderated,
James Peregrino <james_p...@harvard.edu> writes:
: Is Tom's example running off some other SYSV box that isn't
: Solaris?

Yes, Linux. :-(

: So my expectation would be that Perl's getpwnam() on Solaris


: should behave exactly like the Solaris C library function.
: Especially since the perldoc for it assumes you've also read
: the man pages.

Um, no. We fixed the SysV bug by making Perl's version
behave more reasonably -- to wit, at BSD does. :-)

: In the end my approach is to read /etc/shadow manually using


: the traditional split(/:/) technique.

That's demonstrably wrong in some systems. How do you know they're
there? Oh, maybe you *only* are coding for Slolaris. Don't you
hate that?

Really, try running with the latest release of Perl. The C code
says your problem goes away.

--tom
--
"There is no such thing as an underestimate of average intelligence."
- Henry Adams

0 new messages