/* vi: ts=8 sts=4 sw=4 * * $Id$ * * This file is part of the KDE project, module kdesu. * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org> * * 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 <tqcstring.h> #include <kdebug.h> #include <kstandarddirs.h> #include <kdesu/process.h> #include "passwd.h" PasswdProcess::PasswdProcess(TQCString 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) { TQCString 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(TQCString 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); }