Fix crash if key bindings specified in profile cannot be found. Improve
[personal-kdebase.git] / apps / kdepasswd / passwd.cpp
blobfdefab5fc7426677ae219a7b59cf977d21e9f10e
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 if( line.contains("again"))
179 m_Error = line;
180 kill(m_Pid, SIGKILL);
181 waitForChild();
182 return PasswordIncorrect;
184 // Wait for second prompt.
185 errline = line; // use first line for error message
186 while (!isPrompt(line, "new"))
188 line = readLine();
189 if (line.isNull())
191 // We didn't get the new prompt so assume incorrect password.
192 if (m_bTerminal)
193 fputs(errline, stdout);
194 m_Error = errline;
195 return PasswordIncorrect;
199 // we have the new prompt
200 if (check)
202 kill(m_Pid, SIGKILL);
203 waitForChild();
204 return 0;
206 WaitSlave();
207 write(fd(), newpass, strlen(newpass));
208 write(fd(), "\n", 1);
209 state++;
210 break;
212 case 4:
213 // Wait for third prompt
214 if (isPrompt(line, "re"))
216 WaitSlave();
217 write(fd(), newpass, strlen(newpass));
218 write(fd(), "\n", 1);
219 state += 2;
220 break;
222 // Warning or error about the new password
223 if (m_bTerminal)
224 fputs(line, stdout);
225 m_Error = line + '\n';
226 state++;
227 break;
229 case 5:
230 // Wait for either a "Reenter password" or a "Enter password" prompt
231 if (isPrompt(line, "re"))
233 WaitSlave();
234 write(fd(), newpass, strlen(newpass));
235 write(fd(), "\n", 1);
236 state++;
237 break;
239 else if (isPrompt(line, "password"))
241 kill(m_Pid, SIGKILL);
242 waitForChild();
243 return PasswordNotGood;
245 if (m_bTerminal)
246 fputs(line, stdout);
247 m_Error += line + '\n';
248 break;
252 // Are we ok or do we still get an error thrown at us?
253 m_Error = "";
254 state = 0;
255 while (state != 1)
257 line = readLine();
258 if (line.isNull())
260 // No more input... OK
261 return 0;
263 if (isPrompt(line, "password"))
265 // Uh oh, another prompt. Not good!
266 kill(m_Pid, SIGKILL);
267 waitForChild();
268 return PasswordNotGood;
270 m_Error += line + '\n'; // Collect error message
273 kDebug(1512) << k_lineinfo << "Conversation ended successfully.\n";
274 return 0;
278 bool PasswdProcess::isPrompt(const QByteArray &line, const char *word)
280 unsigned i, j, colon;
281 unsigned int lineLength(line.length());
282 for (i=0,j=0,colon=0; i<lineLength; i++)
284 if (line[i] == ':')
286 j = i; colon++;
287 continue;
289 if (!isspace(line[i]))
290 j++;
293 if ((colon != 1) || (line[j] != ':'))
294 return false;
295 if (word == 0L)
296 return true;
297 return line.toLower().contains(word);