summaryrefslogtreecommitdiffstats
path: root/kdepasswd/passwd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdepasswd/passwd.cpp')
-rw-r--r--kdepasswd/passwd.cpp284
1 files changed, 284 insertions, 0 deletions
diff --git a/kdepasswd/passwd.cpp b/kdepasswd/passwd.cpp
new file mode 100644
index 000000000..903b2830f
--- /dev/null
+++ b/kdepasswd/passwd.cpp
@@ -0,0 +1,284 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <[email protected]>
+ *
+ * passwd.cpp: Change a user's password.
+ */
+
+#include <config.h> // setenv
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <qcstring.h>
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <kdesu/process.h>
+#include "passwd.h"
+
+
+PasswdProcess::PasswdProcess(QCString user)
+{
+ struct passwd *pw;
+
+ if (user.isEmpty())
+ {
+ pw = getpwuid(getuid());
+ if (pw == 0L)
+ {
+ kdDebug(1512) << "You don't exist!\n";
+ return;
+ }
+ m_User = pw->pw_name;
+ } else
+ {
+ pw = getpwnam(user);
+ if (pw == 0L)
+ {
+ kdDebug(1512) << k_lineinfo << "User " << user << "does not exist.\n";
+ return;
+ }
+ m_User = user;
+ }
+ bOtherUser = (pw->pw_uid != getuid());
+}
+
+
+PasswdProcess::~PasswdProcess()
+{
+}
+
+
+int PasswdProcess::checkCurrent(const char *oldpass)
+{
+ return exec(oldpass, 0L, 1);
+}
+
+
+int PasswdProcess::exec(const char *oldpass, const char *newpass,
+ int check)
+{
+ if (m_User.isEmpty())
+ return -1;
+// if (check)
+// setTerminal(true);
+
+ // Try to set the default locale to make the parsing of the output
+ // of `passwd' easier.
+ setenv("LANG","C", true /* override */);
+
+ QCStringList args;
+ if(bOtherUser)
+ args += m_User;
+ int ret = PtyProcess::exec("passwd", args);
+ if (ret < 0)
+ {
+ kdDebug(1512) << k_lineinfo << "Passwd not found!\n";
+ return PasswdNotFound;
+ }
+
+ ret = ConversePasswd(oldpass, newpass, check);
+ if (ret < 0)
+ kdDebug(1512) << k_lineinfo << "Conversation with passwd failed. pid = " << pid() << endl;
+
+ if ((waitForChild() != 0) && !check)
+ return PasswordNotGood;
+
+ return ret;
+}
+
+
+/*
+ * The tricky thing is to make this work with a lot of different passwd
+ * implementations. We _don't_ want implementation specific routines.
+ * Return values: -1 = unknown error, 0 = ok, >0 = error code.
+ */
+
+int PasswdProcess::ConversePasswd(const char *oldpass, const char *newpass,
+ int check)
+{
+ QCString line, errline;
+ int state = 0;
+
+ while (state != 7)
+ {
+ line = readLine();
+ if (line.isNull())
+ {
+ return -1;
+ }
+
+ if (state == 0 && isPrompt(line, "new"))
+ // If root is changing a user's password,
+ // passwd can't prompt for the original password.
+ // Therefore, we have to start at state=2.
+ state=2;
+
+ switch (state)
+ {
+ case 0:
+ // Eat garbage, wait for prompt
+ m_Error += line+"\n";
+ if (isPrompt(line, "password"))
+ {
+ WaitSlave();
+ write(m_Fd, oldpass, strlen(oldpass));
+ write(m_Fd, "\n", 1);
+ state++;
+ break;
+ }
+ if (m_bTerminal)
+ fputs(line, stdout);
+ break;
+
+ case 1: case 3: case 6:
+ // Wait for \n
+ if (line.isEmpty())
+ {
+ state++;
+ break;
+ }
+ // error
+ return -1;
+
+ case 2:
+ m_Error = "";
+ if( line.contains("again"))
+ {
+ m_Error = line;
+ kill(m_Pid, SIGKILL);
+ waitForChild();
+ return PasswordIncorrect;
+ }
+ // Wait for second prompt.
+ errline = line; // use first line for error message
+ while (!isPrompt(line, "new"))
+ {
+ line = readLine();
+ if (line.isNull())
+ {
+ // We didn't get the new prompt so assume incorrect password.
+ if (m_bTerminal)
+ fputs(errline, stdout);
+ m_Error = errline;
+ return PasswordIncorrect;
+ }
+ }
+
+ // we have the new prompt
+ if (check)
+ {
+ kill(m_Pid, SIGKILL);
+ waitForChild();
+ return 0;
+ }
+ WaitSlave();
+ write(m_Fd, newpass, strlen(newpass));
+ write(m_Fd, "\n", 1);
+ state++;
+ break;
+
+ case 4:
+ // Wait for third prompt
+ if (isPrompt(line, "re"))
+ {
+ WaitSlave();
+ write(m_Fd, newpass, strlen(newpass));
+ write(m_Fd, "\n", 1);
+ state += 2;
+ break;
+ }
+ // Warning or error about the new password
+ if (m_bTerminal)
+ fputs(line, stdout);
+ m_Error = line + "\n";
+ state++;
+ break;
+
+ case 5:
+ // Wait for either a "Reenter password" or a "Enter password" prompt
+ if (isPrompt(line, "re"))
+ {
+ WaitSlave();
+ write(m_Fd, newpass, strlen(newpass));
+ write(m_Fd, "\n", 1);
+ state++;
+ break;
+ }
+ else if (isPrompt(line, "password"))
+ {
+ kill(m_Pid, SIGKILL);
+ waitForChild();
+ return PasswordNotGood;
+ }
+ if (m_bTerminal)
+ fputs(line, stdout);
+ m_Error += line + "\n";
+ break;
+ }
+ }
+
+ // Are we ok or do we still get an error thrown at us?
+ m_Error = "";
+ state = 0;
+ while (state != 1)
+ {
+ line = readLine();
+ if (line.isNull())
+ {
+ // No more input... OK
+ return 0;
+ }
+ if (isPrompt(line, "password"))
+ {
+ // Uh oh, another prompt. Not good!
+ kill(m_Pid, SIGKILL);
+ waitForChild();
+ return PasswordNotGood;
+ }
+ m_Error += line + "\n"; // Collect error message
+ }
+
+ kdDebug(1512) << k_lineinfo << "Conversation ended successfully.\n";
+ return 0;
+}
+
+
+bool PasswdProcess::isPrompt(QCString line, const char *word)
+{
+ unsigned i, j, colon;
+ unsigned int lineLength(line.length());
+ for (i=0,j=0,colon=0; i<lineLength; i++)
+ {
+ if (line[i] == ':')
+ {
+ j = i; colon++;
+ continue;
+ }
+ if (!isspace(line[i]))
+ j++;
+ }
+
+ if ((colon != 1) || (line[j] != ':'))
+ return false;
+ if (word == 0L)
+ return true;
+ return line.contains(word, false);
+}
+
+