Shadow API and memmory

James R. Leu (jleu@coredcs.com)
Tue, 23 Apr 1996 17:30:30 -0500 (CDT)


I know this is not the right place, but I figured many of you have work on
converting no/shadow to shadowed programs.

First this, if anyone knows where I can find a version of poppassd that is
shadowed that will help a lot.

Since I was unable to find such a version, I wnet ahead and downloaded the
one from qualcomm's site, and proceeded to "shadow it".

I converted all the file locking and opening and stuff ( simple with setspent
and lckspwdp ) then created the new shadowed entry, and proceed to read
through the shadow file by using getspent. I wrote out the entries to a new
temp file, with putspent. The difficulty came in the previous step.
When comparing the name of the newly created entry with the one being read from the
file, they were always equal. What was happening is somehow the memmory was
the same ( even though I had done a bcopy from the returned address to a
malloced address ) iHas anyone seen this before?? I will add the suspiscious
code at that bottom. ( in the function UpdatePasswdFile )

Thanks in advance!

Jim

--
James R. Leu
Network Administrator
CORE Digital Communication Services
jleu@coredcs.com

----------------------------------- cut here ---------------------------------

/* THIS FILE HAS BEEN PORTED TO LINUX W/O Shadow Passwords * ( Not any more - JRL port to Shadow 4/22/96 ) * Ported by Forrest W. Christian "Finished" 10/15/95 * If someone's using shadow passwords and would like a port * let me know. It should take me about an hour. * * Thanks to the original author of this code for providing * a framework for me to port. * * If you use this port I'd like to hear about it! * * Drop any comments, questions, or complaints to * forrestc@imach.com * * Tested on Slackware linux, kernel 1.2.13. * compiled with: * * gcc -o poppassd poppassd.c * */

/* * poppassd.c Update passwords on the UNIX system from the Eudora "Change Password" * client. Keeps a log of change attempts in /etc/poppassd.log but * suppresses recording of passwords given by the client. * * Must be owned by root, and executable only by root. It can be started * with an entry in /etc/inetd.conf such as the following: * * poppassd stream tcp nowait root /usr/local/poppassd/poppassd poppassd * * and in /etc/services: * * poppassd 106/tcp * */

#include <stdio.h> #include <string.h> #include <pwd.h> #include <signal.h> #include <unistd.h> #include <varargs.h> #include <sys/wait.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> #include <netdb.h> #include <sys/types.h> #include <sys/file.h> #include <time.h> #include <shadow.h> /* added 4/22/96 */

#define PF_PLATE "/etc/ptmp%06x" /* original temporary file */

#define PW_CHANGED 0 /* a OK */ #define PW_CANTLOCK -1 /* error code -- locking failed */ #define PW_CANTOPEN -2 /* error code -- open failed */ #define PW_CANTRENAME -3 /* error code -- rename failed */ #define PW_NOCHANGE -4 /* error code -- no passwd entry */

#define SUCCESS 1 #define FAILURE 0 #define BUFSIZE 255 #define MAXARGS 32 #define SUPPRESS 1

#define TI_NULL ((time_t *) NULL) /* NULL time pointer */

void WhoAreYou(); void WriteToClient( va_alist ); void ReadFromClient( char* , int ); void UpdateTime(); void WriteLog( va_alist ); void makesalt( char[] ); int UpdatePasswordFile(struct spwd*); int setPass( char*, char*, char*, struct spwd* ); int chkPass( char*, char*, struct spwd* ); void sigon(); void sigoff();

/* * global variables */ char *pf_name = "/etc/shadow"; /* password file name -changed JRL */ char pf_tnam[128]; /* name of temporary file */

FILE *pf_tfp; /* pointer to temporary file */ FILE *log; time_t gNow; char gDate[BUFSIZE];

int main (argc, argv) int argc; char *argv[]; {

char line[BUFSIZE]; char user[BUFSIZE]; char pass[BUFSIZE]; char newpass[BUFSIZE]; char logFile[BUFSIZE]; struct spwd *spw; /* changed from struct passwd */

sprintf(logFile, "/etc/%s.log", argv[0]); strcpy( user, "" ); strcpy( pass, "" ); strcpy( newpass, "" );

/* * Open the logfile for append */ if ( !( log = fopen( logFile, "a+" )) ) { WriteToClient( "500 Can't open %s.", logFile ); exit(1); } /* The server's responses should be like an FTP server's responses; 1xx for in progress, 2xx for success, 3xx for more information needed, 4xx for temporary failure, and 5xx for permanent failure. Putting it all together, here's a sample conversation:

S: 200 hello\r\n E: user yourloginname\r\n S: 300 please send your password now\r\n E: pass yourcurrentpassword\r\n S: 200 My, that was tasty\r\n E: newpass yournewpassword\r\n S: 200 Happy to oblige\r\n E: quit\r\n S: 200 Bye-bye\r\n S: <closes connection> E: <closes connection> */

WhoAreYou();

WriteToClient( "200 hello, who are you?" ); ReadFromClient( line, 0 ); sscanf(line, "user %s", user) ; if ( strlen(user) == 0 ) { WriteToClient( "500 username required." ); exit(1); }

WriteToClient( "200 your password please." ); ReadFromClient( line, SUPPRESS ); sscanf( line, "pass %s", pass) ; if ( strlen(pass) == 0 ) { WriteToClient( "500 password required." ); exit(1); }

WriteToClient( "200 your new password please." ); ReadFromClient( line, SUPPRESS ); sscanf( line, "newpass %s", newpass );

/* new pass required */ if ( strlen(newpass) == 0 ) { WriteToClient( "500 new password required." ); exit(1); } /* new pass must be 6 char or longer */ else if ( strlen(newpass) < 6 ) { WriteToClient( "500 New password too short" ); exit(1); } /* new pass must be different from old pass */ else if ( !strcmp(pass, newpass) ) { WriteToClient( "500 New password must be different." ); exit(1); }

/* test for valid user */ if ((spw=getspnam(user)) == NULL ) { WriteToClient( "500 Unknown user, %s.", user); exit(1); }

/* authenticate user */ if ( chkPass( user, pass, spw ) == FAILURE ) { WriteToClient( "500 Authentication failure."); exit(1); } if ( setPass( user, pass, newpass, spw ) == FAILURE ) { WriteToClient( "500 Unable to change password." ); exit(1); }

WriteToClient( "200 Password changed, thank-you." ); return(1); }

void WhoAreYou() { struct sockaddr_in bobby; int bobbyLen = sizeof(struct sockaddr); struct passwd *pw; char *inet_ntoa(); char *ttyname();

fputs( "\n", log );

if (isatty(fileno(stdin))) { pw = getpwuid(getuid()); WriteLog( "Connection on %s by %s", ttyname(fileno(stdin)), pw->pw_name); }

else if (lseek(fileno(stdin), 0, L_INCR) >= 0) { pw = getpwuid(getuid()); WriteLog( "Connection on FILE by %s", pw->pw_name); }

else if (getpeername(fileno(stdin), (struct sockaddr *)&bobby, &bobbyLen) < 0) { pw = getpwuid(getuid()); WriteLog( "Connection on PIPE by %s", pw->pw_name); }

else { # if defined(sparc) && __GNUC__ == 1 WriteLog( "NET connection from %s", inet_ntoa(&bobby.sin_addr)); # else WriteLog( "NET connection from %s", inet_ntoa(bobby.sin_addr)); # endif } }

void WriteToClient( va_alist ) va_dcl { va_list ap; char *fmt; char *args[MAXARGS]; int argno = 0; char string[BUFSIZE]; UpdateTime( );

va_start( ap ); fmt = va_arg(ap, char *); while ((args[argno++] = va_arg(ap, char *)) != (char *)0) ;

vfprintf(stdout, fmt, args); fputs("\r\n", stdout ); fflush(stdout);

vsprintf(string, fmt, args); WriteLog( "Server> %s", string );

va_end( ap ); }

void ReadFromClient( line , suppress) char *line; int suppress; { char *sp;

UpdateTime(); strcpy(line, "" ); fgets( line, BUFSIZE, stdin ); if (( sp = strchr(line, '\n')) != NULL ) *sp = '\0'; if (( sp = strchr(line, '\r')) != NULL ) *sp = '\0'; if ( suppress ) WriteLog( "Client> ..." ); else WriteLog( "Client> %s", line ); }

void UpdateTime( ) { struct tm *date; gNow = time( NULL ); /* get current calendar time */ date = localtime( &gNow ); /* convert it to a broken-down time */ strftime( gDate, BUFSIZE, "%b %d %H:%M:%S", date ); }

void WriteLog( va_alist ) va_dcl { va_list ap; char *fmt; char *args[MAXARGS]; int argno = 0; UpdateTime( );

va_start( ap ); fmt = va_arg(ap, char *); while ((args[argno++] = va_arg(ap, char *)) != (char *)0) ; fprintf( log, "%s: ", gDate ); vfprintf(log, fmt, args); fputs( "\n", log ); fflush(log); va_end( ap ); }

int chkPass( user, pass, spw ) char *user; char *pass; struct spwd *spw; { /* Compare the supplied password with the password file entry */ if (strcmp (crypt (pass, spw->sp_pwdp), spw->sp_pwdp) != 0) return ( FAILURE ); else return ( SUCCESS ); }

void makesalt( c ) char c[2]; /* salt characters */ { register long salt; /* used to compute a salt */ register int i; /* counter in a for loop */

/* * just mix a few things up for the salt ... * no rhyme or reason here */ salt = (((long) time(TI_NULL))&0x3f) | (getpid() << 5);

/* * use the bottom 12 bits and map them into the legal alphabet */ for(i = 0; i < 2; i++){ c[i] = (salt & 0x3f) + '.'; if (c[i] > '9') c[i] += 7; if (c[i] > 'Z') c[i] += 6; salt >>= 6; } }

int setPass( user, pass, newpass, spw ) char *user; char *pass; char *newpass; struct spwd *spw; {

char saltc[2]; /* the password's salt */

makesalt(saltc); strcpy( spw->sp_pwdp, crypt(newpass, saltc)); if ( UpdatePasswordFile( spw ) == PW_CHANGED ) return (SUCCESS); else return (FAILURE); }

int UpdatePasswordFile(sp) struct spwd *sp; /* new password structure */ { struct spwd *nsp; /* buffer for passwords being read */ struct spwd perm; int retval = PW_NOCHANGE; /* return value */

/* * disable ALL signals at this point */ sigoff();

/* * Copy spwd entry to perm memory */ /* this should gureentee that a read from getspent will not overwrite this memory */

bcopy( sp, &perm, sizeof( struct spwd ));

/* * open the temporary password file */ umask(077);

sprintf(pf_tnam,PF_PLATE,getpid()); pf_tfp = fopen(pf_tnam, "w"); if(pf_tfp == NULL){ retval = PW_CANTOPEN; goto cantlock; }

/* * lock the password file */ if (lckpwdf() < 0){ retval = PW_CANTLOCK; goto cantlock; } /* * open the password file */

setspent(); /* * copy the password file into the temporary one */ while((nsp = getspent()) != NULL) { if( !strcmp( nsp->sp_namp, sp->sp_namp )) { putspent(nsp, pf_tfp); retval = PW_CHANGED; } else putspent(nsp, pf_tfp); }

if (retval == PW_NOCHANGE) { goto getout; }

/* * close the temporary file and the real one */ (void) fclose(pf_tfp); pf_tfp = NULL;

/* * now change links */

if ( unlink(pf_name) >= 0 && link(pf_tnam, pf_name) >= 0 && unlink(pf_tnam) >= 0){ retval = PW_CHANGED; } else retval = PW_CANTRENAME;

getout: /* * Only remove lock file if this program obtained it */ ulckpwdf();

cantlock: /* some clean up */ if (pf_tfp != NULL) (void) fclose(pf_tfp);

sigon(); /* re-enable ALL signals at this point */

return(retval); }

/* * signal handling routines */ void (*trap[NSIG])(); /* values returned by signal() */

/* * disable ALL signal trapping */ void sigoff() { register int i; /* counter in a for loop */

for(i = 0; i < NSIG; i++) (void) signal(i, SIG_IGN); }

/* * restore to the initial setting all signals */ void sigon() { register int i; /* counter in a for loop */

for(i = 0; i < NSIG; i++) (void) signal(i, trap[i]);

}