add more spacing
[personal-kdebase.git] / runtime / kdesu / kdesud / handler.cpp
blob813097acae332e07c6f79ebc53f7a0e5601eed14
1 /*
2 * This file is part of the KDE project, module kdesu.
3 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
5 * handler.cpp: A connection handler for kdesud.
6 */
8 #include "handler.h"
10 #include <stdlib.h>
11 #include <string.h>
12 #include <assert.h>
13 #include <unistd.h>
14 #include <signal.h>
15 #include <errno.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
20 #include <kdebug.h>
21 #include <kdesu/su.h>
22 #include <kdesu/ssh.h>
24 #include "repo.h"
25 #include "lexer.h"
26 #include "secure.h"
28 using namespace KDESu;
31 // Global repository
32 extern Repository *repo;
33 void kdesud_cleanup();
35 ConnectionHandler::ConnectionHandler(int fd)
36 : SocketSecurity(fd), m_exitCode(0), m_hasExitCode(false), m_needExitCode(false), m_pid(0)
38 m_Fd = fd;
39 m_Priority = 50;
40 m_Scheduler = SuProcess::SchedNormal;
43 ConnectionHandler::~ConnectionHandler()
45 m_Buf.fill('x');
46 m_Pass.fill('x');
47 close(m_Fd);
51 * Handle a connection: make sure we don't block
54 int ConnectionHandler::handle()
56 int ret, nbytes;
58 // Add max 100 bytes to connection buffer
60 char tmpbuf[100];
61 nbytes = recv(m_Fd, tmpbuf, 99, 0);
63 if (nbytes < 0)
65 if (errno == EINTR)
66 return 0;
67 // read error
68 return -1;
69 } else if (nbytes == 0)
71 // eof
72 return -1;
74 tmpbuf[nbytes] = '\000';
76 if (m_Buf.length()+nbytes > 1024)
78 kWarning(1205) << "line too long";
79 return -1;
82 m_Buf.append(tmpbuf);
83 memset(tmpbuf, 'x', nbytes);
85 // Do we have a complete command yet?
86 int n;
87 QByteArray newbuf;
88 while ((n = m_Buf.indexOf('\n')) != -1)
90 newbuf = m_Buf.left(n+1);
91 m_Buf.fill('x', n+1);
92 m_Buf.remove(0, n+1);
93 ret = doCommand(newbuf);
94 if (ret < 0)
95 return ret;
98 return 0;
101 QByteArray ConnectionHandler::makeKey(int _namespace, const QByteArray &s1,
102 const QByteArray &s2, const QByteArray &s3) const
104 QByteArray res;
105 res.setNum(_namespace);
106 res += '*';
107 res += s1 + '*' + s2 + '*' + s3;
108 return res;
111 void ConnectionHandler::sendExitCode()
113 if (!m_needExitCode)
114 return;
115 QByteArray buf;
116 buf.setNum(m_exitCode);
117 buf.prepend("OK ");
118 buf.append("\n");
120 send(m_Fd, buf.data(), buf.length(), 0);
123 void ConnectionHandler::respond(int ok, const QByteArray &s)
125 QByteArray buf;
127 switch (ok) {
128 case Res_OK:
129 buf = "OK";
130 break;
131 case Res_NO:
132 default:
133 buf = "NO";
134 break;
137 if (!s.isEmpty())
139 buf += ' ';
140 buf += s;
143 buf += '\n';
145 send(m_Fd, buf.data(), buf.length(), 0);
149 * Parse and do one command. On a parse error, return -1. This will
150 * close the socket in the main accept loop.
153 int ConnectionHandler::doCommand(QByteArray buf)
155 if ((uid_t) peerUid() != getuid())
157 kWarning(1205) << "Peer uid not equal to me\n";
158 kWarning(1205) << "Peer: " << peerUid() << " Me: " << getuid() ;
159 return -1;
162 QByteArray key, command, pass, name, user, value, env_check;
163 Data_entry data;
165 Lexer *l = new Lexer(buf);
166 int tok = l->lex();
167 switch (tok)
169 case Lexer::Tok_pass: // "PASS password:string timeout:int\n"
170 tok = l->lex();
171 if (tok != Lexer::Tok_str)
172 goto parse_error;
173 m_Pass.fill('x');
174 m_Pass = l->lval();
175 tok = l->lex();
176 if (tok != Lexer::Tok_num)
177 goto parse_error;
178 m_Timeout = l->lval().toInt();
179 if (l->lex() != '\n')
180 goto parse_error;
181 if (m_Pass.isNull())
182 m_Pass = "";
183 kDebug(1205) << "Password set!\n";
184 respond(Res_OK);
185 break;
187 case Lexer::Tok_host: // "HOST host:string\n"
188 tok = l->lex();
189 if (tok != Lexer::Tok_str)
190 goto parse_error;
191 m_Host = l->lval();
192 if (l->lex() != '\n')
193 goto parse_error;
194 kDebug(1205) << "Host set to " << m_Host;
195 respond(Res_OK);
196 break;
198 case Lexer::Tok_prio: // "PRIO priority:int\n"
199 tok = l->lex();
200 if (tok != Lexer::Tok_num)
201 goto parse_error;
202 m_Priority = l->lval().toInt();
203 if (l->lex() != '\n')
204 goto parse_error;
205 kDebug(1205) << "priority set to " << m_Priority;
206 respond(Res_OK);
207 break;
209 case Lexer::Tok_sched: // "SCHD scheduler:int\n"
210 tok = l->lex();
211 if (tok != Lexer::Tok_num)
212 goto parse_error;
213 m_Scheduler = l->lval().toInt();
214 if (l->lex() != '\n')
215 goto parse_error;
216 kDebug(1205) << "Scheduler set to " << m_Scheduler;
217 respond(Res_OK);
218 break;
220 case Lexer::Tok_exec: // "EXEC command:string user:string [options:string (env:string)*]\n"
222 QByteArray options;
223 QList<QByteArray> env;
224 tok = l->lex();
225 if (tok != Lexer::Tok_str)
226 goto parse_error;
227 command = l->lval();
228 tok = l->lex();
229 if (tok != Lexer::Tok_str)
230 goto parse_error;
231 user = l->lval();
232 tok = l->lex();
233 if (tok != '\n')
235 if (tok != Lexer::Tok_str)
236 goto parse_error;
237 options = l->lval();
238 tok = l->lex();
239 while (tok != '\n')
241 if (tok != Lexer::Tok_str)
242 goto parse_error;
243 QByteArray env_str = l->lval();
244 env.append(env_str);
245 if (strncmp(env_str, "DESKTOP_STARTUP_ID=", strlen("DESKTOP_STARTUP_ID=")) != 0)
246 env_check += '*'+env_str;
247 tok = l->lex();
251 QByteArray auth_user;
252 if ((m_Scheduler != SuProcess::SchedNormal) || (m_Priority > 50))
253 auth_user = "root";
254 else
255 auth_user = user;
256 key = makeKey(2, m_Host, auth_user, command);
257 // We only use the command if the environment is the same.
258 if (repo->find(key) == env_check)
260 key = makeKey(0, m_Host, auth_user, command);
261 pass = repo->find(key);
263 if (pass.isNull()) // isNull() means no password, isEmpty() can mean empty password
265 if (m_Pass.isNull())
267 respond(Res_NO);
268 break;
270 data.value = env_check;
271 data.timeout = m_Timeout;
272 key = makeKey(2, m_Host, auth_user, command);
273 repo->add(key, data);
274 data.value = m_Pass;
275 data.timeout = m_Timeout;
276 key = makeKey(0, m_Host, auth_user, command);
277 repo->add(key, data);
278 pass = m_Pass;
281 // Execute the command asynchronously
282 kDebug(1205) << "Executing command: " << command;
283 pid_t pid = fork();
284 if (pid < 0)
286 kDebug(1205) << "fork(): " << strerror(errno);
287 respond(Res_NO);
288 break;
289 } else if (pid > 0)
291 m_pid = pid;
292 respond(Res_OK);
293 break;
296 // Ignore SIGCHLD because "class SuProcess" needs waitpid()
297 signal(SIGCHLD, SIG_DFL);
299 int ret;
300 if (m_Host.isEmpty())
302 SuProcess proc;
303 proc.setCommand(command);
304 proc.setUser(user);
305 if (options.contains('x'))
306 proc.setXOnly(true);
307 proc.setPriority(m_Priority);
308 proc.setScheduler(m_Scheduler);
309 proc.setEnvironment(env);
310 ret = proc.exec(pass.data());
311 } else
313 SshProcess proc;
314 proc.setCommand(command);
315 proc.setUser(user);
316 proc.setHost(m_Host);
317 ret = proc.exec(pass.data());
320 kDebug(1205) << "Command completed: " << command;
321 _exit(ret);
324 case Lexer::Tok_delCmd: // "DEL command:string user:string\n"
325 tok = l->lex();
326 if (tok != Lexer::Tok_str)
327 goto parse_error;
328 command = l->lval();
329 tok = l->lex();
330 if (tok != Lexer::Tok_str)
331 goto parse_error;
332 user = l->lval();
333 if (l->lex() != '\n')
334 goto parse_error;
335 key = makeKey(0, m_Host, user, command);
336 if (repo->remove(key) < 0) {
337 kDebug(1205) << "Unknown command: " << command;
338 respond(Res_NO);
340 else {
341 kDebug(1205) << "Deleted command: " << command << ", user = "
342 << user << endl;
343 respond(Res_OK);
345 break;
347 case Lexer::Tok_delVar: // "DELV name:string \n"
349 tok = l->lex();
350 if (tok != Lexer::Tok_str)
351 goto parse_error;
352 name = l->lval();
353 tok = l->lex();
354 if (tok != '\n')
355 goto parse_error;
356 key = makeKey(1, name);
357 if (repo->remove(key) < 0)
359 kDebug(1205) << "Unknown name: " << name;
360 respond(Res_NO);
362 else {
363 kDebug(1205) << "Deleted name: " << name;
364 respond(Res_OK);
366 break;
369 case Lexer::Tok_delGroup: // "DELG group:string\n"
370 tok = l->lex();
371 if (tok != Lexer::Tok_str)
372 goto parse_error;
373 name = l->lval();
374 if (repo->removeGroup(name) < 0)
376 kDebug(1205) << "No keys found under group: " << name;
377 respond(Res_NO);
379 else
381 kDebug(1205) << "Removed all keys under group: " << name;
382 respond(Res_OK);
384 break;
386 case Lexer::Tok_delSpecialKey: // "DELS special_key:string\n"
387 tok = l->lex();
388 if (tok != Lexer::Tok_str)
389 goto parse_error;
390 name = l->lval();
391 if (repo->removeSpecialKey(name) < 0)
392 respond(Res_NO);
393 else
394 respond(Res_OK);
395 break;
397 case Lexer::Tok_set: // "SET name:string value:string group:string timeout:int\n"
398 tok = l->lex();
399 if (tok != Lexer::Tok_str)
400 goto parse_error;
401 name = l->lval();
402 tok = l->lex();
403 if (tok != Lexer::Tok_str)
404 goto parse_error;
405 data.value = l->lval();
406 tok = l->lex();
407 if (tok != Lexer::Tok_str)
408 goto parse_error;
409 data.group = l->lval();
410 tok = l->lex();
411 if (tok != Lexer::Tok_num)
412 goto parse_error;
413 data.timeout = l->lval().toInt();
414 if (l->lex() != '\n')
415 goto parse_error;
416 key = makeKey(1, name);
417 repo->add(key, data);
418 kDebug(1205) << "Stored key: " << key;
419 respond(Res_OK);
420 break;
422 case Lexer::Tok_get: // "GET name:string\n"
423 tok = l->lex();
424 if (tok != Lexer::Tok_str)
425 goto parse_error;
426 name = l->lval();
427 if (l->lex() != '\n')
428 goto parse_error;
429 key = makeKey(1, name);
430 kDebug(1205) << "Request for key: " << key;
431 value = repo->find(key);
432 if (!value.isEmpty())
433 respond(Res_OK, value);
434 else
435 respond(Res_NO);
436 break;
438 case Lexer::Tok_getKeys: // "GETK groupname:string\n"
439 tok = l->lex();
440 if (tok != Lexer::Tok_str)
441 goto parse_error;
442 name = l->lval();
443 if (l->lex() != '\n')
444 goto parse_error;
445 kDebug(1205) << "Request for group key: " << name;
446 value = repo->findKeys(name);
447 if (!value.isEmpty())
448 respond(Res_OK, value);
449 else
450 respond(Res_NO);
451 break;
453 case Lexer::Tok_chkGroup: // "CHKG groupname:string\n"
454 tok = l->lex();
455 if (tok != Lexer::Tok_str)
456 goto parse_error;
457 name = l->lval();
458 if (l->lex() != '\n')
459 goto parse_error;
460 kDebug(1205) << "Checking for group key: " << name;
461 if ( repo->hasGroup( name ) < 0 )
462 respond(Res_NO);
463 else
464 respond(Res_OK);
465 break;
467 case Lexer::Tok_ping: // "PING\n"
468 tok = l->lex();
469 if (tok != '\n')
470 goto parse_error;
471 respond(Res_OK);
472 break;
474 case Lexer::Tok_exit: // "EXIT\n"
475 tok = l->lex();
476 if (tok != '\n')
477 goto parse_error;
478 m_needExitCode = true;
479 if (m_hasExitCode)
480 sendExitCode();
481 break;
483 case Lexer::Tok_stop: // "STOP\n"
484 tok = l->lex();
485 if (tok != '\n')
486 goto parse_error;
487 kDebug(1205) << "Stopping by command";
488 respond(Res_OK);
489 kdesud_cleanup();
490 exit(0);
492 default:
493 kWarning(1205) << "Unknown command: " << l->lval() ;
494 respond(Res_NO);
495 goto parse_error;
498 delete l;
499 return 0;
501 parse_error:
502 kWarning(1205) << "Parse error" ;
503 delete l;
504 return -1;