File indexing completed on 2024-05-12 05:46:28
0001 /* 0002 * This file is part of the KDE project, module kdesu. 0003 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org> 0004 * 0005 * This is free software; you can use this library under the GNU Library 0006 * General Public License, version 2. See the file "COPYING.LIB" for the 0007 * exact licensing terms. 0008 * 0009 * client.cpp: A client for kdesud. 0010 */ 0011 0012 #include "client.h" 0013 0014 #include <config-kdesu.h> 0015 0016 #include <errno.h> 0017 #include <sys/socket.h> 0018 #include <sys/un.h> 0019 0020 #include <QFile> 0021 #include <QRegExp> 0022 #include <QStandardPaths> 0023 #include <qplatformdefs.h> 0024 #include <QDebug> 0025 0026 #include <ktoolinvocation.h> 0027 0028 extern int kdesuDebugArea(); 0029 0030 namespace KDESu 0031 { 0032 0033 class Q_DECL_HIDDEN KDEsuClient::KDEsuClientPrivate 0034 { 0035 public: 0036 KDEsuClientPrivate() : sockfd(-1) {} 0037 QString daemon; 0038 int sockfd; 0039 QByteArray sock; 0040 }; 0041 0042 #ifndef SUN_LEN 0043 #define SUN_LEN(ptr) ((QT_SOCKLEN_T) (((struct sockaddr_un *) 0)->sun_path) \ 0044 + strlen ((ptr)->sun_path)) 0045 #endif 0046 0047 KDEsuClient::KDEsuClient() 0048 : d(new KDEsuClientPrivate) 0049 { 0050 #if HAVE_X11 0051 QString display = QString::fromLocal8Bit(qgetenv("DISPLAY")); 0052 if (display.isEmpty()) { 0053 // we might be on Wayland 0054 display = QString::fromLocal8Bit(qgetenv("WAYLAND_DISPLAY")); 0055 } 0056 if (display.isEmpty()) { 0057 qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "$DISPLAY is not set."; 0058 return; 0059 } 0060 0061 // strip the screen number from the display 0062 display.remove(QRegExp(QStringLiteral("\\.[0-9]+$"))); 0063 #else 0064 QString display = QStringLiteral("NODISPLAY"); 0065 #endif 0066 0067 d->sock = QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + 0068 QStringLiteral("/kdesud_") + 0069 display); 0070 connect(); 0071 } 0072 0073 KDEsuClient::~KDEsuClient() 0074 { 0075 if (d->sockfd >= 0) { 0076 close(d->sockfd); 0077 } 0078 delete d; 0079 } 0080 0081 int KDEsuClient::connect() 0082 { 0083 if (d->sockfd >= 0) { 0084 close(d->sockfd); 0085 } 0086 if (access(d->sock.constData(), R_OK | W_OK)) { 0087 d->sockfd = -1; 0088 return -1; 0089 } 0090 0091 d->sockfd = socket(PF_UNIX, SOCK_STREAM, 0); 0092 if (d->sockfd < 0) { 0093 qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "socket():" << strerror(errno); 0094 return -1; 0095 } 0096 struct sockaddr_un addr; 0097 addr.sun_family = AF_UNIX; 0098 strcpy(addr.sun_path, d->sock.constData()); 0099 0100 if (QT_SOCKET_CONNECT(d->sockfd, (struct sockaddr *) &addr, SUN_LEN(&addr)) < 0) { 0101 qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "connect():" << strerror(errno); 0102 close(d->sockfd); d->sockfd = -1; 0103 return -1; 0104 } 0105 0106 #if !defined(SO_PEERCRED) || ! HAVE_STRUCT_UCRED 0107 # if HAVE_GETPEEREID 0108 uid_t euid; 0109 gid_t egid; 0110 // Security: if socket exists, we must own it 0111 if (getpeereid(d->sockfd, &euid, &egid) == 0 && euid != getuid()) { 0112 qWarning() << "socket not owned by me! socket uid =" << euid; 0113 close(d->sockfd); 0114 d->sockfd = -1; 0115 return -1; 0116 } 0117 # else 0118 # ifdef __GNUC__ 0119 # warning "Using sloppy security checks" 0120 # endif 0121 // We check the owner of the socket after we have connected. 0122 // If the socket was somehow not ours an attacker will be able 0123 // to delete it after we connect but shouldn't be able to 0124 // create a socket that is owned by us. 0125 QT_STATBUF s; 0126 if (QT_LSTAT(d->sock.constData(), &s) != 0) { 0127 qWarning() << "stat failed (" << d->sock << ")"; 0128 close(d->sockfd); d->sockfd = -1; 0129 return -1; 0130 } 0131 if (s.st_uid != getuid()) { 0132 qWarning() << "socket not owned by me! socket uid =" << s.st_uid; 0133 close(d->sockfd); d->sockfd = -1; 0134 return -1; 0135 } 0136 if (!S_ISSOCK(s.st_mode)) { 0137 qWarning() << "socket is not a socket (" << d->sock << ")"; 0138 close(d->sockfd); d->sockfd = -1; 0139 return -1; 0140 } 0141 # endif 0142 #else 0143 struct ucred cred; 0144 QT_SOCKLEN_T siz = sizeof(cred); 0145 0146 // Security: if socket exists, we must own it 0147 if (getsockopt(d->sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) == 0 && cred.uid != getuid()) { 0148 qWarning() << "socket not owned by me! socket uid =" << cred.uid; 0149 close(d->sockfd); d->sockfd = -1; 0150 return -1; 0151 } 0152 #endif 0153 0154 return 0; 0155 } 0156 0157 QByteArray KDEsuClient::escape(const QByteArray &str) 0158 { 0159 QByteArray copy; 0160 copy.reserve(str.size() + 4); 0161 copy.append('"'); 0162 for (int i = 0; i < str.size(); i++) { 0163 uchar c = str.at(i); 0164 if (c < 32) { 0165 copy.append('\\'); 0166 copy.append('^'); 0167 copy.append(c + '@'); 0168 } else { 0169 if (c == '\\' || c == '"') { 0170 copy.append('\\'); 0171 } 0172 copy.append(c); 0173 } 0174 } 0175 copy.append('"'); 0176 return copy; 0177 } 0178 0179 int KDEsuClient::command(const QByteArray &cmd, QByteArray *result) 0180 { 0181 if (d->sockfd < 0) { 0182 return -1; 0183 } 0184 0185 if (send(d->sockfd, cmd.constData(), cmd.length(), 0) != (int)cmd.length()) { 0186 return -1; 0187 } 0188 0189 char buf[1024]; 0190 int nbytes = recv(d->sockfd, buf, 1023, 0); 0191 if (nbytes <= 0) { 0192 qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "no reply from daemon."; 0193 return -1; 0194 } 0195 buf[nbytes] = '\000'; 0196 0197 QByteArray reply = buf; 0198 if (reply.left(2) != "OK") { 0199 return -1; 0200 } 0201 0202 if (result) { 0203 *result = reply.mid(3, reply.length() - 4); 0204 } 0205 return 0; 0206 } 0207 0208 int KDEsuClient::setPass(const char *pass, int timeout) 0209 { 0210 QByteArray cmd = "PASS "; 0211 cmd += escape(pass); 0212 cmd += ' '; 0213 cmd += QByteArray().setNum(timeout); 0214 cmd += '\n'; 0215 return command(cmd); 0216 } 0217 0218 int KDEsuClient::exec(const QByteArray &prog, const QByteArray &user, const QByteArray &options, const QList<QByteArray> &env) 0219 { 0220 QByteArray cmd; 0221 cmd = "EXEC "; 0222 cmd += escape(prog); 0223 cmd += ' '; 0224 cmd += escape(user); 0225 if (!options.isEmpty() || !env.isEmpty()) { 0226 cmd += ' '; 0227 cmd += escape(options); 0228 for (int i = 0; i < env.count(); ++i) { 0229 cmd += ' '; 0230 cmd += escape(env.at(i)); 0231 } 0232 } 0233 cmd += '\n'; 0234 return command(cmd); 0235 } 0236 0237 int KDEsuClient::setHost(const QByteArray &host) 0238 { 0239 QByteArray cmd = "HOST "; 0240 cmd += escape(host); 0241 cmd += '\n'; 0242 return command(cmd); 0243 } 0244 0245 int KDEsuClient::setPriority(int prio) 0246 { 0247 QByteArray cmd; 0248 cmd += "PRIO "; 0249 cmd += QByteArray::number(prio); 0250 cmd += '\n'; 0251 return command(cmd); 0252 } 0253 0254 int KDEsuClient::setScheduler(int sched) 0255 { 0256 QByteArray cmd; 0257 cmd += "SCHD "; 0258 cmd += QByteArray::number(sched); 0259 cmd += '\n'; 0260 return command(cmd); 0261 } 0262 0263 int KDEsuClient::delCommand(const QByteArray &key, const QByteArray &user) 0264 { 0265 QByteArray cmd = "DEL "; 0266 cmd += escape(key); 0267 cmd += ' '; 0268 cmd += escape(user); 0269 cmd += '\n'; 0270 return command(cmd); 0271 } 0272 int KDEsuClient::setVar(const QByteArray &key, const QByteArray &value, int timeout, const QByteArray &group) 0273 { 0274 QByteArray cmd = "SET "; 0275 cmd += escape(key); 0276 cmd += ' '; 0277 cmd += escape(value); 0278 cmd += ' '; 0279 cmd += escape(group); 0280 cmd += ' '; 0281 cmd += QByteArray().setNum(timeout); 0282 cmd += '\n'; 0283 return command(cmd); 0284 } 0285 0286 QByteArray KDEsuClient::getVar(const QByteArray &key) 0287 { 0288 QByteArray cmd = "GET "; 0289 cmd += escape(key); 0290 cmd += '\n'; 0291 QByteArray reply; 0292 command(cmd, &reply); 0293 return reply; 0294 } 0295 0296 QList<QByteArray> KDEsuClient::getKeys(const QByteArray &group) 0297 { 0298 QByteArray cmd = "GETK "; 0299 cmd += escape(group); 0300 cmd += '\n'; 0301 QByteArray reply; 0302 command(cmd, &reply); 0303 int index = 0, pos; 0304 QList<QByteArray> list; 0305 if (!reply.isEmpty()) { 0306 while (1) { 0307 pos = reply.indexOf('\007', index); 0308 if (pos == -1) { 0309 if (index == 0) { 0310 list.append(reply); 0311 } else { 0312 list.append(reply.mid(index)); 0313 } 0314 break; 0315 } else { 0316 list.append(reply.mid(index, pos - index)); 0317 } 0318 index = pos + 1; 0319 } 0320 } 0321 return list; 0322 } 0323 0324 bool KDEsuClient::findGroup(const QByteArray &group) 0325 { 0326 QByteArray cmd = "CHKG "; 0327 cmd += escape(group); 0328 cmd += '\n'; 0329 if (command(cmd) == -1) { 0330 return false; 0331 } 0332 return true; 0333 } 0334 0335 int KDEsuClient::delVar(const QByteArray &key) 0336 { 0337 QByteArray cmd = "DELV "; 0338 cmd += escape(key); 0339 cmd += '\n'; 0340 return command(cmd); 0341 } 0342 0343 int KDEsuClient::delGroup(const QByteArray &group) 0344 { 0345 QByteArray cmd = "DELG "; 0346 cmd += escape(group); 0347 cmd += '\n'; 0348 return command(cmd); 0349 } 0350 0351 int KDEsuClient::delVars(const QByteArray &special_key) 0352 { 0353 QByteArray cmd = "DELS "; 0354 cmd += escape(special_key); 0355 cmd += '\n'; 0356 return command(cmd); 0357 } 0358 0359 int KDEsuClient::ping() 0360 { 0361 return command("PING\n"); 0362 } 0363 0364 int KDEsuClient::exitCode() 0365 { 0366 QByteArray result; 0367 if (command("EXIT\n", &result) != 0) { 0368 return -1; 0369 } 0370 0371 return result.toInt(); 0372 } 0373 0374 int KDEsuClient::stopServer() 0375 { 0376 return command("STOP\n"); 0377 } 0378 0379 static QString findDaemon() 0380 { 0381 QString daemon = QFile::decodeName(CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "/kdesud"); 0382 if (!QFile::exists(daemon)) { // if not in libexec, find it in PATH 0383 daemon = QStandardPaths::findExecutable(QStringLiteral("kdesud")); 0384 if (daemon.isEmpty()) { 0385 qWarning() << "kdesud daemon not found."; 0386 } 0387 } 0388 return daemon; 0389 } 0390 0391 bool KDEsuClient::isServerSGID() 0392 { 0393 if (d->daemon.isEmpty()) { 0394 d->daemon = findDaemon(); 0395 } 0396 if (d->daemon.isEmpty()) { 0397 return false; 0398 } 0399 0400 QT_STATBUF sbuf; 0401 if (QT_STAT(QFile::encodeName(d->daemon).constData(), &sbuf) < 0) { 0402 qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "stat():" << strerror(errno); 0403 return false; 0404 } 0405 return (sbuf.st_mode & S_ISGID); 0406 } 0407 0408 int KDEsuClient::startServer() 0409 { 0410 if (d->daemon.isEmpty()) { 0411 d->daemon = findDaemon(); 0412 } 0413 if (d->daemon.isEmpty()) { 0414 return -1; 0415 } 0416 0417 if (!isServerSGID()) { 0418 qWarning() << "[" << __FILE__ << ":" << __LINE__ << "] " << "kdesud not setgid!"; 0419 } 0420 0421 // kdesud only forks to the background after it is accepting 0422 // connections. 0423 // We start it via kdeinit to make sure that it doesn't inherit 0424 // any fd's from the parent process. 0425 int ret = KToolInvocation::kdeinitExecWait(d->daemon); 0426 connect(); 0427 return ret; 0428 } 0429 0430 } // namespace KDESu