I just installed poppassd this past Friday on 2 of our mail servers.
Both use shadow passwords & are running RedHat Linux 6.x I used the
2.2.17 kernel headers when compiling this program.
It supports the shadow files, and even has builtin requirements for the
sumitted password (can't be dictionary based, and can't be too short).
Hope this helps
-Rob
> I know this comes up every so often, but so far searching at google.com
> hasn't been very fruitful - found lots of requests but few answers.
>
> Does anyone here have a version of poppassd that runs under Linux, has
> support for shadow passwords and/or APOP auth (based on whether the user
> is listed in the pop.auth file)? If not, I'll hack my own, but I'd much
> rather start with a good base if possible. Everything I've found is
> six or seven years old, or doesn't support shaodw, or doesn't support
> APOP - I'd like to be able to use either.
>
> Thanks,
> Steve
>
> PS: Qualcomm, there is no such thing as "Linux 7", but the references
> to it are peppered throughout your FAQ and INSTALL documentation.
>
> --
> tired of being an underappreciated functionary in a soulless machine?
> hesketh.com is hiring: http://www.hesketh.com/careers/
/*
* poppassd.c
*
* A Eudora and NUPOP change password server.
*
* John Norstad
* Academic Computing and Network Services
* Northwestern University
* [EMAIL PROTECTED]
*
* Based on earlier versions by Roy Smith <[EMAIL PROTECTED]> and Daniel
* L. Leavitt <dll.mitre.org>.
*
* Doesn't actually change any passwords itself. It simply listens for
* incoming requests, gathers the required information (user name, old
* password, new password) and executes /bin/passwd, talking to it over
* a pseudo-terminal pair. The advantage of this is that we don't need
* to have any knowledge of either the password file format (which may
* include dbx files that need to be rebuilt) or of any file locking
* protocol /bin/passwd and cohorts may use (and which isn't documented).
*
* The current version has been tested at NU under SunOS release 4.1.2
* and 4.1.3, and under HP-UX 8.02 and 9.01. We have tested the server
* with both Eudora 1.3.1 and NUPOP 2.0.
*
* Other sites report that this version also works under AIX and NIS,
* and with PC Eudora.
*
* Note that unencrypted passwords are transmitted over the network. If
* this bothers you, think hard about whether you want to implement the
* password changing feature. On the other hand, it's no worse than what
* happens when you run /bin/passwd while connected via telnet or rlogin.
* Well, maybe it is, since the use of a dedicated port makes it slightly
* easier for a network snooper to snarf passwords off the wire.
*
* NOTE: In addition to the security issue outlined in the above paragraph,
* you should be aware that this program is going to be run as root by
* ordinary users and it mucks around with the password file. This should
* set alarms off in your head. I think I've devised a pretty foolproof
* way to ensure that security is maintained, but I'm no security expert and
* you would be a fool to install this without first reading the code and
* ensuring yourself that what I consider safe is good enough for you. If
* something goes wrong, it's your fault, not mine.
*
* The front-end code (which talks to the client) is directly
* descended from Leavitt's original version. The back-end pseudo-tty stuff
* (which talks to /bin/password) is directly descended from Smith's
* version, with changes for SunOS and HP-UX by Norstad (with help from
* sample code in "Advanced Programming in the UNIX Environment"
* by W. Richard Stevens). The code to report /bin/passwd error messages
* back to the client in the final 500 response, and a new version of the
* code to find the next free pty, is by Norstad.
*
* Should 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/bin/poppassd poppassd
*
* and in /etc/services:
*
* poppassd 106/tcp
*
* Logs to the local2 facility. Should have an entry in /etc/syslog.conf
* like the following:
*
* local2.err /var/adm/poppassd-log
*/
/* Modification history.
*
* 06/09/93. Version 1.0.
*
* 06/29/93. Version 1.1.
* Include program name 'poppassd' and version number in initial
* hello message.
* Case insensitive command keywords (user, pass, newpass, quit).
* Fixes problem reported by Raoul Schaffner with PC Eudora.
* Read 'quit' command from client instead of just terminating after
* password change.
* Add new code for NIS support (contributed by Max Caines).
*
* 08/31/93. Version 1.2.
* Generalized the expected string matching to solve several problems
* with NIS and AIX. The new "*" character in pattern strings
* matches any sequence of 0 or more characters.
* Fix an error in the "getemess" function which could cause the
* program to hang if more than one string was defined in the
* P2 array.
* 6/4/20000 version 1.6a
* Updated Code to use PIPES, and also added save passwd output to file.
*/
/* Steve Dorner's description of the simple protocol:
*
* 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 machine_name popassd v1.4 hello, who are you?\r\n
* E: user yourloginname\r\n
* S: 200 your password please.
* E: pass yourcurrentpassword\r\n
* S: 200 your new password please.\r\n
* E: newpass yournewpassword\r\n
* 200 Password changed, thank-you.\r\n
* E: quit\r\n
* S: 200 Bye-bye\r\n
* S: <closes connection>
* E: <closes connection>
*/
#define HAS_SHADOW
#define FRED
/* #define AIX */
#define RED_HAT_LINUX_6_0
/* #define BSD_2_1 */
#define VERSION "1.6a"
#define SUCCESS 1
#define FAILURE 0
#define BUFSIZE 512
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <strings.h>
#include <errno.h>
#include <stdarg.h>
#include <pwd.h>
#include <string.h>
#include <termios.h>
#include <dirent.h>
#ifndef BSD_2_1
#ifndef AIX
# include <getopt.h>
#endif
#endif
#ifdef HAS_SHADOW
# include <shadow.h>
/*# include <shadow/pwauth.h> */
# ifndef PW_PPP
# define PW_PPP PW_LOGIN
# endif
# ifndef RED_HAT_LINUX_6_0
char *pw_encrypt (char *, char *); /* To permit long shadow
passwords */
# define crypt pw_encrypt /* for short passwords as well. */
# endif
#endif
/*
* Prototypes
*/
int main (int argc, char *argv[]);
int process(int *ToChild, int *FromChild);
int dochild (char *slavedev, char *user);
int findpty (char **slave);
void writestring (int fd, char *s);
char *talktochild ( int master_read, int master_write, char *user, char *oldpass,
char *newpass, char *emess);
int match (char *str, char *pat);
char *expect (int master, char **expected, char *buf);
int getemess (int master, char **expected, char *buf);
void WriteToClient (char *fmt, ...);
void ReadFromClient (char *line);
int chkPass (char *user, char *pass, struct passwd *pw);
/* Prompt strings expected from the "passwd" command. If you want
* to port this program to yet another flavor of UNIX, you may need to add
* more prompt strings here.
*
* Each prompt is defined as an array of pointers to alternate
* strings, terminated by an empty string. In the strings, '*'
* matches any sequence of 0 or more characters. Pattern matching
* is forced to lower case so enter only lower case letters.
*/
static char *P1[] =
{
"password: ",
"changing password for *\nenter old password: ",
#if 0
"changing nis password for * on *.\nold password: ",
#endif
"changing local password for *", /* BSD v2.1 */
""
};
static char *P2[] =
{
"Error changing password for *\nchanging password for *",
/* IBM's AIX 4.3.3. */
"new password:",
"new unix password:",
"changing password for *\nenter new password:",/* Cobalt Linux 4.0 (mips) */
"changing password for *\nnew unix password: ",/* Red Hat Linux 6.0 */
"changing password for *\nnew unix password:", /* RedHat Linux 5.2 */
"changing password for *\n*'s new password:", /* IBM's AIX 4.2.1. */
"enter new password:", /*
non-shadow passwords */
"changing password for *\nnew password:", /* shadow passwords */
"New password (8 significant characters):", /* BSD v2.1 */
""
};
/* New password (8 significant characters):
Please don't use an all-lower case password.
Unusual capitalization, control charactersor digits are suggested.
New password (8 significant characters):*/
static char *P3[] =
{
"new password (again):",
"retype new unix password:", /* RedHat Linux 5.2 */
"re-enter *'s new password:", /* IBM's AIX 4.2.1. */
"re-type new password:", /* non-shadow passwords */
"re-enter new password:", /* shadow passwords */
"retype new password:", /* BSD v2.1 */
""
};
static char *P4[] =
{
"password changed",
"nis entry changed on *",
"passwd: all authentication tokens updated successfully", /* RedHat
Linux 5.2 + 6.0 */
"passwd:*updating passwd database*passwd:*done*", /* BSD
v2.1 */
""
};
int verbose = 0;
int debug_mode = 0;
FILE *logfile = NULL;
struct passwd *pw;
char user[BUFSIZE];
int main (int argc, char *argv[])
{
int res;
static int ToChild[2], FromChild[2];
int i;
/* Check for Args */
for (i=1;i<argc;i++) {
switch (*argv[i]) {
case 'v':
verbose = 1;
break;
case 'd':
fprintf (stderr, "Debug mode on\n");
debug_mode = 1;
/* verbose = 1; */
break;
case 'l':
fprintf (stderr, "Lower Debug mode on\n");
debug_mode = -1;
/* verbose = 1; */
break;
case 'f':
logfile = fopen("temp.log","wb");
break;
default:
fprintf (stderr, "invalid option {%s}\n", argv[i]);
exit (1);
}
}
/* Setup input/output to child using pipes. */
/* **************************************** */
if (pipe(ToChild) != 0 || pipe(FromChild) != 0) {
WriteToClient("500 Can't create pipes. {%s}",strerror(errno));
return 1;
}
else if (debug_mode > 0) {
printf("Pipe 1: {%d/%d}\n",ToChild[0],ToChild[1]);
printf("Pipe 2: {%d/%d}\n",FromChild[0],FromChild[1]);
}
res = process(ToChild,FromChild);
/* Close Pipes */
close(ToChild[0]);
close(ToChild[1]);
close(FromChild[0]);
close(FromChild[1]);
if (logfile)
fclose(logfile);
return res;
}
int process(int *ToChild, int *FromChild)
{
/* PIPE stuff*/
char *error_str;
char line[BUFSIZE];
char oldpass[BUFSIZE];
char newpass[BUFSIZE];
char emess[BUFSIZE];
char *slavedev = NULL;
int c;
/* int master; */
pid_t pid, wpid;
int wstat;
#ifdef HAS_SHADOW
struct spwd *spwd;
struct spwd *getspnam();
#endif
*user =
*oldpass =
*newpass = 0;
openlog ("poppassd", LOG_PID, LOG_LOCAL2);
gethostname(line, sizeof (line));
WriteToClient ("200 %s poppassd v%s hello, who are you?", line, VERSION);
ReadFromClient (line);
sscanf (line, "user %s", user) ;
if (strlen (user) == 0) {
WriteToClient ("500 Username required.");
return(1);
}
WriteToClient ("200 your password please.");
ReadFromClient (line);
sscanf (line, "pass %s", oldpass) ;
if (strlen (oldpass) == 0) {
WriteToClient ("500 Password required.");
return(1);
}
if ((pw = getpwnam (user)) == NULL) {
WriteToClient ("500 Invalid user or password");
return(1);
}
#ifdef HAS_SHADOW
if ((spwd = getspnam(user)) == NULL)
pw->pw_passwd = "";
else
pw->pw_passwd = spwd->sp_pwdp;
#endif
if (chkPass (user, oldpass, pw) == FAILURE) {
sleep(3);
WriteToClient ("500 Invalid user or password");
return(1);
}
WriteToClient ("200 your new password please.");
ReadFromClient (line);
sscanf (line, "newpass %s", newpass);
/* new pass required */
if (strlen (newpass) == 0) {
WriteToClient ("500 New password required.");
return(1);
}
/* fork child process to talk to password program */
if ((pid = fork()) < 0) /* Error, can't fork */
{
syslog (LOG_ERR, "can't fork for passwd: %m");
WriteToClient ("500 Server error (can't fork passwd), get help!");
return (1);
}
if (pid) /* Parent */
{
error_str = talktochild (FromChild[0], ToChild[1], user, oldpass,
newpass, emess);
if ( error_str ) {
if (debug_mode > 0) {
if (*emess == '\0')
printf("Password Failed {%s}\n",error_str);
else
printf("Password Failed {%s}\n",emess);
}
syslog (LOG_ERR, "failed attempt by %s", user);
if (*emess == '\0')
WriteToClient ("500 '%s'.",error_str );
else
WriteToClient ("500 '%s'", emess);
return(1);
}
if (debug_mode > 0)
printf("Finished Talking to Child\n");
#ifndef BSD_2_1
if ((wpid = waitpid (pid, &wstat, 0)) < 0) {
syslog (LOG_ERR, "wait for /bin/passwd child failed: %m");
WriteToClient ("500 Server error (wait failed), get help!");
return (1);
}
if (debug_mode > 0)
printf("Checking Child Process ID\n");
if (pid != wpid) {
syslog (LOG_ERR, "wrong child (/bin/passwd waited for!");
WriteToClient ("500 Server error (wrong child), get help!");
return (1);
}
if (WIFEXITED (wstat) == 0) {
syslog (LOG_ERR, "child (/bin/passwd) killed?");
WriteToClient ("500 Server error (funny wstat), get help!");
return (1);
}
if (WEXITSTATUS (wstat) != 0) {
syslog (LOG_ERR, "child (/bin/passwd) exited abnormally");
WriteToClient ("500 Server error (abnormal exit), get help!");
return (1);
}
#endif
if (debug_mode > 0)
printf("Child Process has finished\n");
syslog (LOG_ERR, "password changed for %s", user);
WriteToClient ("200 Password changed, thank-you.");
ReadFromClient (line);
if (strncmp(line, "quit", 4) != 0) {
WriteToClient("500 Quit required.");
return (1);
}
WriteToClient("200 Bye.");
return (0);
}
else /* Child */
{
/* Setup Child input/output */
dup2(ToChild[0],STDIN_FILENO);
/* dup2(FromChild[1],STDOUT_FILENO); */
dup2(FromChild[1],STDERR_FILENO);
dochild (slavedev, user);
}
}
/*
* dochild
*
* Do child stuff - set up slave pty and execl /bin/passwd.
*
* Code adapted from "Advanced Programming in the UNIX Environment"
* by W. Richard Stevens.
*
*/
int dochild (char *slavedev, char *user)
{
int slave;
struct termios stermios;
/* Start new session - gets rid of controlling terminal. */
if (setsid() < 0) {
syslog(LOG_ERR, "setsid failed: %m");
return(0);
}
/* Do some simple changes to ensure that the daemon does not mess */
/* things up. */
if (!debug_mode)
chdir ("/");
umask (0);
/*
* Shadow password suite looks the user in the login database. Since
* poppassd does not 'login', it will fail. So, cheat. Keep root status
* and pass the user on the command line.
*/
if (debug_mode > 0)
execl("./dummy", "dummy", user,
(char*)0);
#ifdef HAS_SHADOW
execl("/bin/passwd", "passwd", user, (char*)0);
execl("/usr/bin/passwd", "passwd", user, (char*)0);
#else
#ifdef FRED
execl("/bin/passwd", "passwd", user, (char*)0);
execl("/usr/bin/passwd", "passwd", user, (char*)0);
#else
/*
* Without the shadow password suite, the standard password program
* looks at the uid for the user. Become the user and don't pass it
* on the command line.
*/
setregid (pw->pw_gid, pw->pw_gid);
setreuid (pw->pw_uid, pw->pw_uid);
execl("/bin/passwd", "passwd", (char*)0);
execl("/usr/bin/passwd", "passwd", (char*)0);
#endif
#endif
syslog(LOG_ERR, "can't exec /bin/passwd: %m");
return(0);
}
/*
* findpty()
*
* Finds the first available pseudo-terminal master/slave pair. The master
* side is opened and a fd returned as the function value. A pointer to the
* name of the slave side (i.e. "/dev/ttyp0") is returned in the argument,
* which should be a char**. The name itself is stored in a static buffer.
*
* A negative value is returned on any sort of error.
*
* Modified by Norstad to remove assumptions about number of pty's allocated
* on this UNIX box.
*/
int findpty (char **slave)
{
int master;
static char line[] = "/dev/ptyXX";
DIR *dirp;
struct dirent *dp;
dirp = opendir("/dev");
while ((dp = readdir(dirp)) != NULL) {
if (strncmp(dp->d_name, "pty", 3) == 0 && strlen(dp->d_name) == 5) {
line[8] = dp->d_name[3];
line[9] = dp->d_name[4];
if ((master = open(line, O_RDWR)) >= 0) {
line[5] = 't';
*slave = line;
closedir(dirp);
return (master);
}
}
}
closedir(dirp);
return (-1);
}
/*
* writestring()
*
* Write a string in a single write() system call.
*/
void writestring (int fd, char *s)
{
int l;
l = strlen (s);
write (fd, s, l);
if (verbose)
syslog(LOG_DEBUG, "write: %s", s);
}
/*
* talktochild()
*
* Handles the conversation between the parent and child (password program)
* processes.
*
* Returns SUCCESS is the conversation is completed without any problems,
* FAILURE if any errors are encountered (in which case, it can be assumed
* that the password wasn't changed).
*/
char *talktochild ( int master_read, int master_write, char *user, char *oldpass, char
*newpass,
char *emess)
{
char *error_str;
static char buf[BUFSIZE];
char pswd[BUFSIZE+1];
int m, n;
*emess = 0;
#ifndef HAS_SHADOW
#ifndef FRED
if (debug_mode)
printf("Stage 1.\n");
if (logfile) {
fprintf(logfile,"Stage 1.\n");
fflush(logfile);
}
error_str = expect(master_read, P1, buf);
if (error_str) return error_str;
sprintf(pswd, "%s\n", oldpass);
writestring(master_write, pswd);
#endif
#endif
if (debug_mode)
printf("Stage 2.\n");
if (logfile) {
fprintf(logfile,"Stage 2.\n");
fflush(logfile);
}
error_str = expect(master_read, P2, buf);
if (error_str) return error_str;
if (debug_mode > 0)
printf("Sending New Password...");
sprintf(pswd, "%s\n", newpass);
writestring(master_write, pswd);
if (debug_mode > 0)
printf("done\n");
if (debug_mode)
printf("Stage 3.\n");
if (logfile) {
fprintf(logfile,"Stage 3.\n");
fflush(logfile);
}
error_str = expect(master_read, P3, buf);
if (error_str) {
int OK = 0;
#ifdef BSD_2_1
char **s = P2, *p = error_str;
if (debug_mode)
printf("Checking Stage 2 again.\n");
if (logfile) {
fprintf(logfile,"Checking Stage 2 again.\n");
fflush(logfile);
}
for (s = P2; **s != 0; s++) {
if (debug_mode > 0)
printf(" '%s'=='%s'??\n",p,*s);
if (logfile) {
fprintf(logfile," '%s'=='%s'??\n",p,*s);
fflush(logfile);
}
switch (match(p, *s)) {
case 2:
if (verbose)
syslog (LOG_DEBUG, "expect: succes\n");
OK = 1;
}
}
if (!OK) {
if (debug_mode)
printf("Stage 3 FAILED. {%s}\n",buf);
/* getemess(master_read, P2, buf); */
if (logfile) {
fprintf(logfile,"Stage 3 FAILED. {%s}\n",buf);
fflush(logfile);
}
strcpy(emess, buf);
return error_str;
}
else {
if (debug_mode > 0)
printf("Sending New Password...");
writestring(master_write, pswd);
if (debug_mode > 0)
printf("done\n");
error_str = expect(master_read, P3, buf);
if (error_str)
OK = 1;
}
#endif
if (!OK) {
if (debug_mode)
printf("Stage 3 FAILED. {%s}\n",buf);
/* getemess(master_read, P2, buf); */
if (logfile) {
fprintf(logfile,"Stage 3 FAILED. {%s}\n",buf);
fflush(logfile);
}
strcpy(emess, buf);
return error_str;
}
}
if (debug_mode > 0)
printf("Sending New Password...");
writestring(master_write, pswd);
if (debug_mode > 0)
printf("done\n");
#ifndef HAS_SHADOW /* shadow prints no success message :( */
#ifndef AIX /* AIX prints no success message :( */
error_str = expect(master_read, P4, buf);
if (error_str) return error_str;
#endif
#endif
#ifdef RED_HAT_LINUX_6_0
error_str = expect(master_read, P4, buf);
if (error_str) return error_str;
#endif
if (logfile) {
fprintf(logfile,"Finished.\n");
fflush(logfile);
}
return NULL;
}
/*
* match ()
*
* Matches a string against a pattern. Wild-card characters '*' in
* the pattern match any sequence of 0 or more characters in the string.
* The match is case-insensitive.
*
* Entry: str = string.
* pat = pattern.
*
* Exit: function result =
* 0 if no match.
* 1 if the string matches some initial segment of
* the pattern.
* 2 if the string matches the full pattern.
*/
#define NO_MATCH 0
#define PART_MATCH 1
#define MATCH 2
int match(char *text, char *p)
{
register int last;
register int matched;
register int reverse;
for ( ; *p; text++, p++) {
if (*text == '\0' && *p != '*')
return PART_MATCH;
switch (*p) {
case '\n':
case '\r':
text--;
continue;
default:
if (tolower(*text) != tolower(*p))
return NO_MATCH;
continue;
case '?':
/* Match anything. */
continue;
case '*':
while (*++p == '*')
/* Consecutive stars act just like one. */
continue;
if (*p == '\0')
/* Trailing star matches everything. */
return MATCH;
while (*text)
if ((matched = match(text++, p)) != NO_MATCH)
return matched;
return PART_MATCH;
}
}
if (*text == '\0')
return MATCH;
return PART_MATCH;
}
/*
* expect ()
*
* Reads 'passwd' command output and compares it to expected output.
*
* Entry: master = fid of master pty.
* expected = pointer to array of pointers to alternate expected
* strings, terminated by an empty string.
* buf = pointer to buffer.
*
* Exit: function result = SUCCESS if output matched, FAILURE if not.
* buf = the text read from the slave.
*
* Text is read from the slave and accumulated in buf. As long as
* the text accumulated so far is an initial segment of at least
* one of the expected strings, the function continues the read.
* As soon as one of full expected strings has been read, the
* function returns SUCCESS. As soon as the text accumulated so far
* is not an initial segment of or exact match for at least one of
* the expected strings, the function returns FAILURE.
*/
char *expect (int master_read, char **expected, char *buf)
{
char *error_str;
int n, m, count = 0;
char **s;
char *p;
int initialSegment = 0;
errno = 0;
buf[0] = 0;
while (1) {
n = strlen (buf);
if (n >= BUFSIZE-1) {
syslog(LOG_ERR, "buffer overflow on read from child");
return buf;
}
if (debug_mode > 0)
printf("Reading from 'passwd'\n");
if (logfile) {
fprintf(logfile,"Reading from 'passwd'\n");
fflush(logfile);
}
m = read (master_read, &buf[n], BUFSIZ-1-n);
if (debug_mode > 0)
printf("Read %d bytes\n",m);
if (logfile) {
fprintf(logfile,"Read %d bytes\n",m);
fflush(logfile);
}
if (m < 0) {
if (debug_mode > 0)
printf("Error Reading %s\n",strerror(errno));
if (logfile) {
fprintf(logfile,"Error Reading %s\n",strerror(errno));
fflush(logfile);
}
syslog(LOG_ERR, "read error from child: %m");
return buf;
}
buf[n+m] = '\0';
/* remove '\r' and '\n' and ' ' (spaces)*/
p = &buf[n] + strlen(&buf[n]) - 1;
while (*p == '\r' || *p == '\n' || (*p == ' ' && !initialSegment)) p--;
*++p = '\0';
if (verbose)
syslog (LOG_DEBUG, "read: %s\n", &buf[n]);
if (debug_mode > 0)
printf("read: '%s'\n", buf);
if (logfile) {
fprintf(logfile,"read: '%s'\n", buf);
fflush(logfile);
}
/* Ignore leading whitespace. It gets in the way. */
p = buf;
while (isspace (*p))
++p;
if (*p == '\0') {
count++;
if (count == 100)
return "Failed to read any data from passwd";
continue;
}
initialSegment = 0;
for (s = expected; **s != 0; s++) {
if (debug_mode > 0)
printf(" '%s'=='%s'??\n",p,*s);
if (logfile) {
fprintf(logfile," '%s'=='%s'??\n",p,*s);
fflush(logfile);
}
switch (match(p, *s)) {
case 2:
if (verbose)
syslog (LOG_DEBUG, "expect: succes\n");
if (debug_mode > 0)
printf(" - Located Match (%s)\n",p);
return NULL;
case 1:
initialSegment = 1;
default:
break;
}
}
if (!initialSegment) {
if (verbose)
syslog (LOG_DEBUG, "expect: failure\n");
if (debug_mode > 0)
printf(" - Failed to Match\n");
return buf;
}
}
}
/*
* getemess()
*
* This function accumulates a 'passwd' command error message issued
* after the first copy of the password has been sent.
*
* Entry: master = fid of master pty.
* expected = pointer to array of pointers to alternate expected
* strings for first password prompt, terminated by an
* empty string.
* buf = pointer to buffer containing text read so far.
*
* Exit: buf = the error message read from the slave.
*
* Text is read from the slave and accumulated in buf until the text
* at the end of the buffer is an exact match for one of the expected
* prompt strings. The expected prompt string is removed from the buffer,
* returning just the error message text. Newlines in the error message
* text are replaced by spaces.
*/
int getemess (int master_read, char **expected, char *buf)
{
int n, m;
char **s;
char *p, *q;
n = strlen(buf);
while (1) {
for (s = expected; **s != 0; s++) {
for (p = buf; *p; p++) {
if (match(p, *s) == 2) {
*p = 0;
for (q = buf; *q; q++) if (*q == '\n') *q = ' ';
return;
}
}
}
if (n >= BUFSIZE-1) {
syslog(LOG_ERR, "buffer overflow on read from child");
return;
}
m = read(master_read, buf+n, BUFSIZE+1-n);
if (m < 0) {
syslog(LOG_ERR, "read error from child: %m");
return;
}
n += m;
buf[n] = 0;
if (verbose)
syslog (LOG_DEBUG, "read: %s\n", buf);
}
}
void WriteToClient (char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf (stdout, fmt, ap);
fputs ("\r\n", stdout );
fflush (stdout);
va_end (ap);
}
void ReadFromClient (char *line)
{
char *sp;
int i;
strcpy (line, "");
fgets (line, BUFSIZE, stdin);
if ((sp = strchr(line, '\n')) != NULL) *sp = '\0';
if ((sp = strchr(line, '\r')) != NULL) *sp = '\0';
/* convert initial keyword on line to lower case. */
for (sp = line; isalpha(*sp); sp++) *sp = tolower(*sp);
}
#ifndef crypt
char *crypt(const char *key, const char *salt);
#endif
int chkPass (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);
}