Posted-From: The MITRE Corporation, Bedford, MA To: sdorner@qualcomm.com (Steve Dorner) From: dll@mitre.org (Daniel L. Leavitt) Subject: Re: Change password server Date: Tue, 10 Nov 92 10:56:29 EST Sender: dll@mbunix.mitre.org Steve, Here it is. This is the first such program I've ever written so I'd appreciate any constructive comments if anyone cares to send me any. -Dan -------------- /* * 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: * * ppassd 106/tcp * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PF_PLATE "./ptmpXXXXX" /* original temporary file */ #define PF_TEMP "./ptmp" /* lock 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 FI_NULL ((FILE *) NULL) /* NULL file pointer */ #define TI_NULL ((time_t *) NULL) /* NULL time pointer */ /* * library functions */ extern char *mktemp(); /* makes a temp file name */ /* * global variables */ char *pf_name = "passwd"; /* password file name */ char *pf_tnam; /* name of temporary file */ FILE *pf_fp; /* pointer to real password file */ FILE *pf_tfp; /* pointer to temporary file */ FILE *log; time_t gNow; char gDate[BUFSIZE]; main (argc, argv) int argc; char *argv[]; { char line[BUFSIZE]; char user[BUFSIZE]; char pass[BUFSIZE]; char newpass[BUFSIZE]; char logFile[BUFSIZE]; struct passwd *pw; chdir( "/etc" ); 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); } #if 0 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: E: #endif 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 ( (pw=getpwnam(user)) == NULL ) { WriteToClient( "500 Unknown user, %s.", user); exit(1); } /* authenticate user */ if ( chkPass( user, pass, pw ) == FAILURE ) { WriteToClient( "500 Authentication failure."); exit(1); } if ( setPass( user, pass, newpass, pw ) == FAILURE ) { WriteToClient( "500 Unable to change password." ); exit(1); } WriteToClient( "200 Password changed, thank-you." ); return(1); } 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 } } 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 ); } ReadFromClient( line , suppress) char *line; int suppress; { char *sp; int i; 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 ); } 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 ); } 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, pw ) char *user; char *pass; struct passwd *pw; { /* Compare the supplied password with the password file entry */ if (strcmp (crypt (pass, pw->pw_passwd), pw->pw_passwd) != 0) return ( FAILURE ); else return ( SUCCESS ); } 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; } } setPass( user, pass, newpass, pw ) char *user; char *pass; char *newpass; struct passwd *pw; { char saltc[2]; /* the password's salt */ makesalt(saltc); strcpy( pw->pw_passwd, crypt(newpass, saltc)); if ( UpdatePasswordFile( pw ) == PW_CHANGED ) return (SUCCESS); else return (FAILURE); } UpdatePasswordFile(p) struct passwd *p; /* new password structure */ { char nbuf[BUFSIZ]; /* buffer for passwords being read */ char obuf[BUFSIZ]; /* buffer for new entry */ register char *q, *s; /* used for quick name comparison */ int retval = PW_NOCHANGE; /* return value */ /* * disable ALL signals at this point */ sigoff(); /* * open the temporary password file */ umask(022); pf_tnam = mktemp(PF_PLATE); if ((pf_tfp = fopen(pf_tnam, "w")) == FI_NULL){ retval = PW_CANTOPEN; goto cantlock; } /* * lock the password file */ if (link(pf_tnam, PF_TEMP) < 0){ retval = PW_CANTLOCK; goto cantlock; } /* * copy the new password structure */ sprintf(obuf, "%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); /* * open the password file */ if ((pf_fp = fopen(pf_name, "r")) == FI_NULL){ retval = PW_CANTOPEN; goto getout; } /* * copy the password file into the temporary one */ while(fgets(nbuf, BUFSIZ, pf_fp) != NULL){ for(s = nbuf, q = p->pw_name; *s && *s != ':'; s++, q++) if (*s != *q) break; if (*s == ':' && *q == '\0') { fputs(obuf, pf_tfp); retval = PW_CHANGED; } else fputs(nbuf, pf_tfp); } if (retval == PW_NOCHANGE) { goto getout; } /* * close the temporary file and the real one */ (void) fclose(pf_tfp); pf_tfp = FI_NULL; (void) fclose(pf_fp); pf_fp = FI_NULL; /* * now relink; note the lock file is still there */ 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 */ (void) unlink(PF_TEMP); cantlock: /* some clean up */ if (pf_tfp != FI_NULL) (void) fclose(pf_tfp); if (pf_fp != FI_NULL) (void) fclose(pf_fp); (void) unlink(pf_tnam); sigon(); /* re-enable ALL signals at this point */ return(retval); } /* * signal handling routines */ void (*trap[NSIG])(); /* values returned by signal() */ /* * disable ALL signal trapping */ 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 */ sigon() { register int i; /* counter in a for loop */ for(i = 0; i < NSIG; i++) (void) signal(i, trap[i]); } .