File indexing completed on 2024-05-05 03:54:29
0001 /* 0002 This file is part of the KDE project, module kdesu. 0003 SPDX-FileCopyrightText: 1999, 2000 Geert Jansen <jansen@kde.org> 0004 0005 handler.cpp: A connection handler for kdesud. 0006 */ 0007 0008 #include "handler.h" 0009 0010 #include <ksud_debug.h> 0011 0012 #include <assert.h> 0013 #include <cerrno> 0014 #include <signal.h> 0015 #include <stdlib.h> 0016 #include <string.h> 0017 #include <unistd.h> 0018 0019 #include <sys/socket.h> 0020 0021 #include <sshprocess.h> 0022 #include <suprocess.h> 0023 0024 #include "lexer.h" 0025 #include "repo.h" 0026 0027 using namespace KDESu; 0028 0029 #define BUF_SIZE 1024 0030 0031 // Global repository 0032 extern Repository *repo; 0033 void kdesud_cleanup(); 0034 0035 ConnectionHandler::ConnectionHandler(int fd) 0036 : SocketSecurity(fd) 0037 , m_exitCode(0) 0038 , m_hasExitCode(false) 0039 , m_needExitCode(false) 0040 , m_pid(0) 0041 { 0042 m_Fd = fd; 0043 m_Priority = 50; 0044 m_Scheduler = SuProcess::SchedNormal; 0045 } 0046 0047 ConnectionHandler::~ConnectionHandler() 0048 { 0049 m_Buf.fill('x'); 0050 m_Pass.fill('x'); 0051 close(m_Fd); 0052 } 0053 0054 /* 0055 * Handle a connection: make sure we don't block 0056 */ 0057 0058 int ConnectionHandler::handle() 0059 { 0060 int ret; 0061 int nbytes; 0062 0063 m_Buf.reserve(BUF_SIZE); 0064 nbytes = recv(m_Fd, m_Buf.data() + m_Buf.size(), BUF_SIZE - 1 - m_Buf.size(), 0); 0065 0066 if (nbytes < 0) { 0067 if (errno == EINTR) { 0068 return 0; 0069 } 0070 // read error 0071 return -1; 0072 } else if (nbytes == 0) { 0073 // eof 0074 return -1; 0075 } 0076 0077 m_Buf.resize(m_Buf.size() + nbytes); 0078 if (m_Buf.size() == BUF_SIZE - 1) { 0079 qCWarning(KSUD_LOG) << "line too long"; 0080 return -1; 0081 } 0082 0083 // Do we have a complete command yet? 0084 int n; 0085 while ((n = m_Buf.indexOf('\n')) != -1) { 0086 n++; 0087 QByteArray newbuf = QByteArray(m_Buf.data(), n); // ensure new detached buffer for simplicity 0088 int nsize = m_Buf.size() - n; 0089 ::memmove(m_Buf.data(), m_Buf.data() + n, nsize); 0090 ::memset(m_Buf.data() + nsize, 'x', n); 0091 m_Buf.resize(nsize); 0092 ret = doCommand(newbuf); 0093 if (newbuf.isDetached()) { // otherwise somebody else will clear it 0094 newbuf.fill('x'); 0095 } 0096 if (ret < 0) { 0097 return ret; 0098 } 0099 } 0100 0101 return 0; 0102 } 0103 0104 QByteArray ConnectionHandler::makeKey(int _namespace, const QByteArray &s1, const QByteArray &s2, const QByteArray &s3) const 0105 { 0106 QByteArray res; 0107 res.setNum(_namespace); 0108 res += '*'; 0109 res += s1 + '*' + s2 + '*' + s3; 0110 return res; 0111 } 0112 0113 void ConnectionHandler::sendExitCode() 0114 { 0115 if (!m_needExitCode) { 0116 return; 0117 } 0118 QByteArray buf; 0119 buf.setNum(m_exitCode); 0120 buf.prepend("OK "); 0121 buf.append("\n"); 0122 0123 send(m_Fd, buf.data(), buf.length(), 0); 0124 } 0125 0126 void ConnectionHandler::respond(int ok, const QByteArray &s) 0127 { 0128 QByteArray buf; 0129 0130 switch (ok) { 0131 case Res_OK: 0132 buf = "OK"; 0133 break; 0134 case Res_NO: 0135 default: 0136 buf = "NO"; 0137 break; 0138 } 0139 0140 if (!s.isEmpty()) { 0141 buf += ' '; 0142 buf += s; 0143 } 0144 0145 buf += '\n'; 0146 0147 send(m_Fd, buf.data(), buf.length(), 0); 0148 } 0149 0150 /* 0151 * Parse and do one command. On a parse error, return -1. This will 0152 * close the socket in the main accept loop. 0153 */ 0154 0155 int ConnectionHandler::doCommand(QByteArray buf) 0156 { 0157 if ((uid_t)peerUid() != getuid()) { 0158 qCWarning(KSUD_LOG) << "Peer uid not equal to me\n"; 0159 qCWarning(KSUD_LOG) << "Peer: " << peerUid() << " Me: " << getuid(); 0160 return -1; 0161 } 0162 0163 QByteArray key; 0164 QByteArray command; 0165 QByteArray pass; 0166 QByteArray name; 0167 QByteArray user; 0168 QByteArray value; 0169 QByteArray env_check; 0170 Data_entry data; 0171 0172 Lexer *l = new Lexer(buf); 0173 int tok = l->lex(); 0174 switch (tok) { 0175 case Lexer::Tok_pass: // "PASS password:string timeout:int\n" 0176 tok = l->lex(); 0177 if (tok != Lexer::Tok_str) { 0178 goto parse_error; 0179 } 0180 m_Pass.fill('x'); 0181 m_Pass = l->lval(); 0182 tok = l->lex(); 0183 if (tok != Lexer::Tok_num) { 0184 goto parse_error; 0185 } 0186 m_Timeout = l->lval().toInt(); 0187 if (l->lex() != '\n') { 0188 goto parse_error; 0189 } 0190 if (m_Pass.isNull()) { 0191 m_Pass = ""; 0192 } 0193 qCDebug(KSUD_LOG) << "Password set!\n"; 0194 respond(Res_OK); 0195 break; 0196 0197 case Lexer::Tok_host: // "HOST host:string\n" 0198 tok = l->lex(); 0199 if (tok != Lexer::Tok_str) { 0200 goto parse_error; 0201 } 0202 m_Host = l->lval(); 0203 if (l->lex() != '\n') { 0204 goto parse_error; 0205 } 0206 qCDebug(KSUD_LOG) << "Host set to " << m_Host; 0207 respond(Res_OK); 0208 break; 0209 0210 case Lexer::Tok_prio: // "PRIO priority:int\n" 0211 tok = l->lex(); 0212 if (tok != Lexer::Tok_num) { 0213 goto parse_error; 0214 } 0215 m_Priority = l->lval().toInt(); 0216 if (l->lex() != '\n') { 0217 goto parse_error; 0218 } 0219 qCDebug(KSUD_LOG) << "priority set to " << m_Priority; 0220 respond(Res_OK); 0221 break; 0222 0223 case Lexer::Tok_sched: // "SCHD scheduler:int\n" 0224 tok = l->lex(); 0225 if (tok != Lexer::Tok_num) { 0226 goto parse_error; 0227 } 0228 m_Scheduler = l->lval().toInt(); 0229 if (l->lex() != '\n') { 0230 goto parse_error; 0231 } 0232 qCDebug(KSUD_LOG) << "Scheduler set to " << m_Scheduler; 0233 respond(Res_OK); 0234 break; 0235 0236 case Lexer::Tok_exec: // "EXEC command:string user:string [options:string (env:string)*]\n" 0237 { 0238 QByteArray options; 0239 QList<QByteArray> env; 0240 tok = l->lex(); 0241 if (tok != Lexer::Tok_str) { 0242 goto parse_error; 0243 } 0244 command = l->lval(); 0245 tok = l->lex(); 0246 if (tok != Lexer::Tok_str) { 0247 goto parse_error; 0248 } 0249 user = l->lval(); 0250 tok = l->lex(); 0251 if (tok != '\n') { 0252 if (tok != Lexer::Tok_str) { 0253 goto parse_error; 0254 } 0255 options = l->lval(); 0256 tok = l->lex(); 0257 while (tok != '\n') { 0258 if (tok != Lexer::Tok_str) { 0259 goto parse_error; 0260 } 0261 QByteArray env_str = l->lval(); 0262 env.append(env_str); 0263 if (strncmp(env_str.constData(), "DESKTOP_STARTUP_ID=", strlen("DESKTOP_STARTUP_ID=")) != 0) { 0264 env_check += '*' + env_str; 0265 } 0266 tok = l->lex(); 0267 } 0268 } 0269 0270 QByteArray auth_user; 0271 if ((m_Scheduler != SuProcess::SchedNormal) || (m_Priority > 50)) { 0272 auth_user = "root"; 0273 } else { 0274 auth_user = user; 0275 } 0276 key = makeKey(2, m_Host, auth_user, command); 0277 // We only use the command if the environment is the same. 0278 if (repo->find(key) == env_check) { 0279 key = makeKey(0, m_Host, auth_user, command); 0280 pass = repo->find(key); 0281 } 0282 if (pass.isNull()) // isNull() means no password, isEmpty() can mean empty password 0283 { 0284 if (m_Pass.isNull()) { 0285 respond(Res_NO); 0286 break; 0287 } 0288 data.value = env_check; 0289 data.timeout = m_Timeout; 0290 key = makeKey(2, m_Host, auth_user, command); 0291 repo->add(key, data); 0292 data.value = m_Pass; 0293 data.timeout = m_Timeout; 0294 key = makeKey(0, m_Host, auth_user, command); 0295 repo->add(key, data); 0296 pass = m_Pass; 0297 } 0298 0299 // Execute the command asynchronously 0300 qCDebug(KSUD_LOG) << "Executing command: " << command; 0301 pid_t pid = fork(); 0302 if (pid < 0) { 0303 qCDebug(KSUD_LOG) << "fork(): " << strerror(errno); 0304 respond(Res_NO); 0305 break; 0306 } else if (pid > 0) { 0307 m_pid = pid; 0308 respond(Res_OK); 0309 break; 0310 } 0311 0312 // Ignore SIGCHLD because "class SuProcess" needs waitpid() 0313 signal(SIGCHLD, SIG_DFL); 0314 0315 int ret; 0316 if (m_Host.isEmpty()) { 0317 SuProcess proc; 0318 proc.setCommand(command); 0319 proc.setUser(user); 0320 if (options.contains('x')) { 0321 proc.setXOnly(true); 0322 } 0323 proc.setPriority(m_Priority); 0324 proc.setScheduler(m_Scheduler); 0325 proc.setEnvironment(env); 0326 ret = proc.exec(pass.data()); 0327 } else { 0328 SshProcess proc; 0329 proc.setCommand(command); 0330 proc.setUser(user); 0331 proc.setHost(m_Host); 0332 ret = proc.exec(pass.data()); 0333 } 0334 0335 qCDebug(KSUD_LOG) << "Command completed: " << command; 0336 _exit(ret); 0337 } 0338 0339 case Lexer::Tok_delCmd: // "DEL command:string user:string\n" 0340 tok = l->lex(); 0341 if (tok != Lexer::Tok_str) { 0342 goto parse_error; 0343 } 0344 command = l->lval(); 0345 tok = l->lex(); 0346 if (tok != Lexer::Tok_str) { 0347 goto parse_error; 0348 } 0349 user = l->lval(); 0350 if (l->lex() != '\n') { 0351 goto parse_error; 0352 } 0353 key = makeKey(0, m_Host, user, command); 0354 if (repo->remove(key) < 0) { 0355 qCDebug(KSUD_LOG) << "Unknown command: " << command; 0356 respond(Res_NO); 0357 } else { 0358 qCDebug(KSUD_LOG) << "Deleted command: " << command << ", user = " << user; 0359 respond(Res_OK); 0360 } 0361 break; 0362 0363 case Lexer::Tok_delVar: // "DELV name:string \n" 0364 { 0365 tok = l->lex(); 0366 if (tok != Lexer::Tok_str) { 0367 goto parse_error; 0368 } 0369 name = l->lval(); 0370 tok = l->lex(); 0371 if (tok != '\n') { 0372 goto parse_error; 0373 } 0374 key = makeKey(1, name); 0375 if (repo->remove(key) < 0) { 0376 qCDebug(KSUD_LOG) << "Unknown name: " << name; 0377 respond(Res_NO); 0378 } else { 0379 qCDebug(KSUD_LOG) << "Deleted name: " << name; 0380 respond(Res_OK); 0381 } 0382 break; 0383 } 0384 0385 case Lexer::Tok_delGroup: // "DELG group:string\n" 0386 tok = l->lex(); 0387 if (tok != Lexer::Tok_str) { 0388 goto parse_error; 0389 } 0390 name = l->lval(); 0391 if (repo->removeGroup(name) < 0) { 0392 qCDebug(KSUD_LOG) << "No keys found under group: " << name; 0393 respond(Res_NO); 0394 } else { 0395 qCDebug(KSUD_LOG) << "Removed all keys under group: " << name; 0396 respond(Res_OK); 0397 } 0398 break; 0399 0400 case Lexer::Tok_delSpecialKey: // "DELS special_key:string\n" 0401 tok = l->lex(); 0402 if (tok != Lexer::Tok_str) { 0403 goto parse_error; 0404 } 0405 name = l->lval(); 0406 if (repo->removeSpecialKey(name) < 0) { 0407 respond(Res_NO); 0408 } else { 0409 respond(Res_OK); 0410 } 0411 break; 0412 0413 case Lexer::Tok_set: // "SET name:string value:string group:string timeout:int\n" 0414 tok = l->lex(); 0415 if (tok != Lexer::Tok_str) { 0416 goto parse_error; 0417 } 0418 name = l->lval(); 0419 tok = l->lex(); 0420 if (tok != Lexer::Tok_str) { 0421 goto parse_error; 0422 } 0423 data.value = l->lval(); 0424 tok = l->lex(); 0425 if (tok != Lexer::Tok_str) { 0426 goto parse_error; 0427 } 0428 data.group = l->lval(); 0429 tok = l->lex(); 0430 if (tok != Lexer::Tok_num) { 0431 goto parse_error; 0432 } 0433 data.timeout = l->lval().toInt(); 0434 if (l->lex() != '\n') { 0435 goto parse_error; 0436 } 0437 key = makeKey(1, name); 0438 repo->add(key, data); 0439 qCDebug(KSUD_LOG) << "Stored key: " << key; 0440 respond(Res_OK); 0441 break; 0442 0443 case Lexer::Tok_get: // "GET name:string\n" 0444 tok = l->lex(); 0445 if (tok != Lexer::Tok_str) { 0446 goto parse_error; 0447 } 0448 name = l->lval(); 0449 if (l->lex() != '\n') { 0450 goto parse_error; 0451 } 0452 key = makeKey(1, name); 0453 qCDebug(KSUD_LOG) << "Request for key: " << key; 0454 value = repo->find(key); 0455 if (!value.isEmpty()) { 0456 respond(Res_OK, value); 0457 } else { 0458 respond(Res_NO); 0459 } 0460 break; 0461 0462 case Lexer::Tok_getKeys: // "GETK groupname:string\n" 0463 tok = l->lex(); 0464 if (tok != Lexer::Tok_str) { 0465 goto parse_error; 0466 } 0467 name = l->lval(); 0468 if (l->lex() != '\n') { 0469 goto parse_error; 0470 } 0471 qCDebug(KSUD_LOG) << "Request for group key: " << name; 0472 value = repo->findKeys(name); 0473 if (!value.isEmpty()) { 0474 respond(Res_OK, value); 0475 } else { 0476 respond(Res_NO); 0477 } 0478 break; 0479 0480 case Lexer::Tok_chkGroup: // "CHKG groupname:string\n" 0481 tok = l->lex(); 0482 if (tok != Lexer::Tok_str) { 0483 goto parse_error; 0484 } 0485 name = l->lval(); 0486 if (l->lex() != '\n') { 0487 goto parse_error; 0488 } 0489 qCDebug(KSUD_LOG) << "Checking for group key: " << name; 0490 if (repo->hasGroup(name) < 0) { 0491 respond(Res_NO); 0492 } else { 0493 respond(Res_OK); 0494 } 0495 break; 0496 0497 case Lexer::Tok_ping: // "PING\n" 0498 tok = l->lex(); 0499 if (tok != '\n') { 0500 goto parse_error; 0501 } 0502 respond(Res_OK); 0503 break; 0504 0505 case Lexer::Tok_exit: // "EXIT\n" 0506 tok = l->lex(); 0507 if (tok != '\n') { 0508 goto parse_error; 0509 } 0510 m_needExitCode = true; 0511 if (m_hasExitCode) { 0512 sendExitCode(); 0513 } 0514 break; 0515 0516 case Lexer::Tok_stop: // "STOP\n" 0517 tok = l->lex(); 0518 if (tok != '\n') { 0519 goto parse_error; 0520 } 0521 qCDebug(KSUD_LOG) << "Stopping by command"; 0522 respond(Res_OK); 0523 kdesud_cleanup(); 0524 exit(0); 0525 0526 default: 0527 qCWarning(KSUD_LOG) << "Unknown command: " << l->lval(); 0528 respond(Res_NO); 0529 goto parse_error; 0530 } 0531 0532 delete l; 0533 return 0; 0534 0535 parse_error: 0536 qCWarning(KSUD_LOG) << "Parse error"; 0537 delete l; 0538 return -1; 0539 }