Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / apps / kdepasswd / passwd.cpp
blob1b750fae5ebc909fc96f00fee5c836779cf3fbcc
1 /* vi: ts=8 sts=4 sw=4
3 * This file is part of the KDE project, module kdesu.
4 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
6 Permission to use, copy, modify, and distribute this software
7 and its documentation for any purpose and without fee is hereby
8 granted, provided that the above copyright notice appear in all
9 copies and that both that the copyright notice and this
10 permission notice and warranty disclaimer appear in supporting
11 documentation, and that the name of the author not be used in
12 advertising or publicity pertaining to distribution of the
13 software without specific, written prior permission.
15 The author disclaim all warranties with regard to this
16 software, including all implied warranties of merchantability
17 and fitness. In no event shall the author be liable for any
18 special, indirect or consequential damages or any damages
19 whatsoever resulting from loss of use, data or profits, whether
20 in an action of contract, negligence or other tortious action,
21 arising out of or in connection with the use or performance of
22 this software.
24 * passwd.cpp: Change a user's password.
27 #include "passwd.h"
29 #include <config-apps.h> // setenv
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <pwd.h>
37 #include <signal.h>
38 #include <errno.h>
39 #include <fcntl.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
44 #include <kdebug.h>
45 #include <kstandarddirs.h>
46 #include <kdefakes.h>
48 #include <kdesu/process.h>
51 PasswdProcess::PasswdProcess(const QByteArray &user)
53 struct passwd *pw;
55 if (user.isEmpty())
57 pw = getpwuid(getuid());
58 if (pw == 0L)
60 kDebug(1512) << "You don't exist!\n";
61 return;
63 m_User = pw->pw_name;
64 } else
66 pw = getpwnam(user);
67 if (pw == 0L)
69 kDebug(1512) << k_lineinfo << "User " << user << "does not exist.\n";
70 return;
72 m_User = user;
74 bOtherUser = (pw->pw_uid != getuid());
78 PasswdProcess::~PasswdProcess()
83 int PasswdProcess::checkCurrent(const char *oldpass)
85 return exec(oldpass, 0L, 1);
89 int PasswdProcess::exec(const char *oldpass, const char *newpass,
90 int check)
92 if (m_User.isEmpty())
93 return -1;
94 // if (check)
95 // setTerminal(true);
97 // Try to set the default locale to make the parsing of the output
98 // of `passwd' easier.
99 setenv("LANG","C", true /* override */);
101 QList<QByteArray> args;
102 if(bOtherUser)
103 args += m_User;
104 int ret = KDESu::PtyProcess::exec("passwd", args);
105 if (ret < 0)
107 kDebug(1512) << k_lineinfo << "Passwd not found!\n";
108 return PasswdNotFound;
111 ret = ConversePasswd(oldpass, newpass, check);
112 if (ret < 0)
113 kDebug(1512) << k_lineinfo << "Conversation with passwd failed. pid = " << pid();
115 if ((waitForChild() != 0) && !check)
116 return PasswordNotGood;
118 return ret;
123 * The tricky thing is to make this work with a lot of different passwd
124 * implementations. We _don't_ want implementation specific routines.
125 * Return values: -1 = unknown error, 0 = ok, >0 = error code.
128 int PasswdProcess::ConversePasswd(const char *oldpass, const char *newpass,
129 int check)
131 QByteArray line, errline;
132 int state = 0;
134 while (state != 7)
136 line = readLine();
137 if (line.isNull())
139 return -1;
142 if (state == 0 && isPrompt(line, "new"))
143 // If root is changing a user's password,
144 // passwd can't prompt for the original password.
145 // Therefore, we have to start at state=2.
146 state=2;
148 switch (state)
150 case 0:
151 // Eat garbage, wait for prompt
152 m_Error += line+'\n';
153 if (isPrompt(line, "password"))
155 WaitSlave();
156 write(fd(), oldpass, strlen(oldpass));
157 write(fd(), "\n", 1);
158 state++;
159 break;
161 if (m_bTerminal)
162 fputs(line, stdout);
163 break;
165 case 1: case 3: case 6:
166 // Wait for \n
167 if (line.isEmpty())
169 state++;
170 break;
172 // error
173 return -1;
175 case 2:
176 m_Error = "";
177 // Wait for second prompt.
178 errline = line; // use first line for error message
179 while (!isPrompt(line, "new"))
181 line = readLine();
182 if (line.isNull())
184 // We didn't get the new prompt so assume incorrect password.
185 if (m_bTerminal)
186 fputs(errline, stdout);
187 m_Error = errline;
188 return PasswordIncorrect;
192 // we have the new prompt
193 if (check)
195 kill(m_Pid, SIGKILL);
196 waitForChild();
197 return 0;
199 WaitSlave();
200 write(fd(), newpass, strlen(newpass));
201 write(fd(), "\n", 1);
202 state++;
203 break;
205 case 4:
206 // Wait for third prompt
207 if (isPrompt(line, "re"))
209 WaitSlave();
210 write(fd(), newpass, strlen(newpass));
211 write(fd(), "\n", 1);
212 state += 2;
213 break;
215 // Warning or error about the new password
216 if (m_bTerminal)
217 fputs(line, stdout);
218 m_Error = line + '\n';
219 state++;
220 break;
222 case 5:
223 // Wait for either a "Reenter password" or a "Enter password" prompt
224 if (isPrompt(line, "re"))
226 WaitSlave();
227 write(fd(), newpass, strlen(newpass));
228 write(fd(), "\n", 1);
229 state++;
230 break;
232 else if (isPrompt(line, "password"))
234 kill(m_Pid, SIGKILL);
235 waitForChild();
236 return PasswordNotGood;
238 if (m_bTerminal)
239 fputs(line, stdout);
240 m_Error += line + '\n';
241 break;
245 // Are we ok or do we still get an error thrown at us?
246 m_Error = "";
247 state = 0;
248 while (state != 1)
250 line = readLine();
251 if (line.isNull())
253 // No more input... OK
254 return 0;
256 if (isPrompt(line, "password"))
258 // Uh oh, another prompt. Not good!
259 kill(m_Pid, SIGKILL);
260 waitForChild();
261 return PasswordNotGood;
263 m_Error += line + '\n'; // Collect error message
266 kDebug(1512) << k_lineinfo << "Conversation ended successfully.\n";
267 return 0;
271 bool PasswdProcess::isPrompt(const QByteArray &line, const char *word)
273 unsigned i, j, colon;
274 unsigned int lineLength(line.length());
275 for (i=0,j=0,colon=0; i<lineLength; i++)
277 if (line[i] == ':')
279 j = i; colon++;
280 continue;
282 if (!isspace(line[i]))
283 j++;
286 if ((colon != 1) || (line[j] != ':'))
287 return false;
288 if (word == 0L)
289 return true;
290 return line.toLower().contains(word);