KDEsu
client.cpp
Go to the documentation of this file.
00001 /* vi: ts=8 sts=4 sw=4 00002 * 00003 * This file is part of the KDE project, module kdesu. 00004 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org> 00005 * 00006 * This is free software; you can use this library under the GNU Library 00007 * General Public License, version 2. See the file "COPYING.LIB" for the 00008 * exact licensing terms. 00009 * 00010 * client.cpp: A client for kdesud. 00011 */ 00012 00013 #include "client.h" 00014 00015 #include <config.h> 00016 #include <config-kdesu.h> 00017 00018 #include <stdio.h> 00019 #include <unistd.h> 00020 #include <stdlib.h> 00021 #include <pwd.h> 00022 #include <errno.h> 00023 #include <string.h> 00024 00025 #include <sys/types.h> 00026 #include <sys/socket.h> 00027 #include <sys/un.h> 00028 #include <sys/stat.h> 00029 00030 #include <QtCore/QBool> 00031 #include <QtCore/QFile> 00032 #include <QtCore/QRegExp> 00033 00034 #include <kdebug.h> 00035 #include <kstandarddirs.h> 00036 #include <ktoolinvocation.h> 00037 #include <kde_file.h> 00038 00039 extern int kdesuDebugArea(); 00040 00041 namespace KDESu { 00042 00043 class KDEsuClient::KDEsuClientPrivate { 00044 public: 00045 KDEsuClientPrivate() : sockfd(-1) {} 00046 QString daemon; 00047 int sockfd; 00048 QByteArray sock; 00049 }; 00050 00051 #ifndef SUN_LEN 00052 #define SUN_LEN(ptr) ((socklen_t) (((struct sockaddr_un *) 0)->sun_path) \ 00053 + strlen ((ptr)->sun_path)) 00054 #endif 00055 00056 KDEsuClient::KDEsuClient() 00057 :d(new KDEsuClientPrivate) 00058 { 00059 #ifdef Q_WS_X11 00060 QString display = QString::fromAscii(qgetenv("DISPLAY")); 00061 if (display.isEmpty()) 00062 { 00063 kWarning(kdesuDebugArea()) << k_lineinfo << "$DISPLAY is not set."; 00064 return; 00065 } 00066 00067 // strip the screen number from the display 00068 display.remove(QRegExp("\\.[0-9]+$")); 00069 #elif defined(Q_WS_QWS) 00070 QByteArray display("QWS"); 00071 #else 00072 QByteArray display("NODISPLAY"); 00073 #endif 00074 00075 d->sock = QFile::encodeName( KStandardDirs::locateLocal("socket", 00076 QString("kdesud_").append(display))); 00077 connect(); 00078 } 00079 00080 00081 KDEsuClient::~KDEsuClient() 00082 { 00083 if (d->sockfd >= 0) 00084 close(d->sockfd); 00085 delete d; 00086 } 00087 00088 int KDEsuClient::connect() 00089 { 00090 if (d->sockfd >= 0) 00091 close(d->sockfd); 00092 if (access(d->sock, R_OK|W_OK)) 00093 { 00094 d->sockfd = -1; 00095 return -1; 00096 } 00097 00098 d->sockfd = socket(PF_UNIX, SOCK_STREAM, 0); 00099 if (d->sockfd < 0) 00100 { 00101 kWarning(kdesuDebugArea()) << k_lineinfo << "socket():" << perror; 00102 return -1; 00103 } 00104 struct sockaddr_un addr; 00105 addr.sun_family = AF_UNIX; 00106 strcpy(addr.sun_path, d->sock); 00107 00108 if (::connect(d->sockfd, (struct sockaddr *) &addr, SUN_LEN(&addr)) < 0) 00109 { 00110 kWarning(kdesuDebugArea()) << k_lineinfo << "connect():" << perror; 00111 close(d->sockfd); d->sockfd = -1; 00112 return -1; 00113 } 00114 00115 #if !defined(SO_PEERCRED) || !defined(HAVE_STRUCT_UCRED) 00116 # if defined(HAVE_GETPEEREID) 00117 uid_t euid; 00118 gid_t egid; 00119 // Security: if socket exists, we must own it 00120 if (getpeereid(d->sockfd, &euid, &egid) == 0) 00121 { 00122 if (euid != getuid()) 00123 { 00124 kWarning(kdesuDebugArea()) << "socket not owned by me! socket uid =" << euid; 00125 close(d->sockfd); d->sockfd = -1; 00126 return -1; 00127 } 00128 } 00129 # else 00130 # ifdef __GNUC__ 00131 # warning "Using sloppy security checks" 00132 # endif 00133 // We check the owner of the socket after we have connected. 00134 // If the socket was somehow not ours an attacker will be able 00135 // to delete it after we connect but shouldn't be able to 00136 // create a socket that is owned by us. 00137 KDE_struct_stat s; 00138 if (KDE_lstat(d->sock, &s)!=0) 00139 { 00140 kWarning(kdesuDebugArea()) << "stat failed (" << d->sock << ")"; 00141 close(d->sockfd); d->sockfd = -1; 00142 return -1; 00143 } 00144 if (s.st_uid != getuid()) 00145 { 00146 kWarning(kdesuDebugArea()) << "socket not owned by me! socket uid =" << s.st_uid; 00147 close(d->sockfd); d->sockfd = -1; 00148 return -1; 00149 } 00150 if (!S_ISSOCK(s.st_mode)) 00151 { 00152 kWarning(kdesuDebugArea()) << "socket is not a socket (" << d->sock << ")"; 00153 close(d->sockfd); d->sockfd = -1; 00154 return -1; 00155 } 00156 # endif 00157 #else 00158 struct ucred cred; 00159 socklen_t siz = sizeof(cred); 00160 00161 // Security: if socket exists, we must own it 00162 if (getsockopt(d->sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) == 0) 00163 { 00164 if (cred.uid != getuid()) 00165 { 00166 kWarning(kdesuDebugArea()) << "socket not owned by me! socket uid =" << cred.uid; 00167 close(d->sockfd); d->sockfd = -1; 00168 return -1; 00169 } 00170 } 00171 #endif 00172 00173 return 0; 00174 } 00175 00176 QByteArray KDEsuClient::escape(const QByteArray &str) 00177 { 00178 QByteArray copy; 00179 copy.reserve(str.size() + 4); 00180 copy.append('"'); 00181 for (int i = 0; i < str.size(); i++) { 00182 uchar c = str.at(i); 00183 if (c < 32) { 00184 copy.append('\\'); 00185 copy.append('^'); 00186 copy.append(c + '@'); 00187 } else { 00188 if (c == '\\' || c == '"') 00189 copy.append('\\'); 00190 copy.append(c); 00191 } 00192 } 00193 copy.append('"'); 00194 return copy; 00195 } 00196 00197 int KDEsuClient::command(const QByteArray &cmd, QByteArray *result) 00198 { 00199 if (d->sockfd < 0) 00200 return -1; 00201 00202 if (send(d->sockfd, cmd, cmd.length(), 0) != (int) cmd.length()) 00203 return -1; 00204 00205 char buf[1024]; 00206 int nbytes = recv(d->sockfd, buf, 1023, 0); 00207 if (nbytes <= 0) 00208 { 00209 kWarning(kdesuDebugArea()) << k_lineinfo << "no reply from daemon."; 00210 return -1; 00211 } 00212 buf[nbytes] = '\000'; 00213 00214 QByteArray reply = buf; 00215 if (reply.left(2) != "OK") 00216 return -1; 00217 00218 if (result) 00219 *result = reply.mid(3, reply.length()-4); 00220 return 0; 00221 } 00222 00223 int KDEsuClient::setPass(const char *pass, int timeout) 00224 { 00225 QByteArray cmd = "PASS "; 00226 cmd += escape(pass); 00227 cmd += ' '; 00228 cmd += QByteArray().setNum(timeout); 00229 cmd += '\n'; 00230 return command(cmd); 00231 } 00232 00233 int KDEsuClient::exec(const QByteArray &prog, const QByteArray &user, const QByteArray &options, const QList<QByteArray> &env) 00234 { 00235 QByteArray cmd; 00236 cmd = "EXEC "; 00237 cmd += escape(prog); 00238 cmd += ' '; 00239 cmd += escape(user); 00240 if (!options.isEmpty() || !env.isEmpty()) 00241 { 00242 cmd += ' '; 00243 cmd += escape(options); 00244 for (int i = 0; i < env.count(); ++i) 00245 { 00246 cmd += ' '; 00247 cmd += escape(env.at(i)); 00248 } 00249 } 00250 cmd += '\n'; 00251 return command(cmd); 00252 } 00253 00254 int KDEsuClient::setHost(const QByteArray &host) 00255 { 00256 QByteArray cmd = "HOST "; 00257 cmd += escape(host); 00258 cmd += '\n'; 00259 return command(cmd); 00260 } 00261 00262 int KDEsuClient::setPriority(int prio) 00263 { 00264 QByteArray cmd; 00265 cmd += "PRIO "; 00266 cmd += QByteArray::number(prio); 00267 cmd += '\n'; 00268 return command(cmd); 00269 } 00270 00271 int KDEsuClient::setScheduler(int sched) 00272 { 00273 QByteArray cmd; 00274 cmd += "SCHD "; 00275 cmd += QByteArray::number(sched); 00276 cmd += '\n'; 00277 return command(cmd); 00278 } 00279 00280 int KDEsuClient::delCommand(const QByteArray &key, const QByteArray &user) 00281 { 00282 QByteArray cmd = "DEL "; 00283 cmd += escape(key); 00284 cmd += ' '; 00285 cmd += escape(user); 00286 cmd += '\n'; 00287 return command(cmd); 00288 } 00289 int KDEsuClient::setVar(const QByteArray &key, const QByteArray &value, int timeout, 00290 const QByteArray &group) 00291 { 00292 QByteArray cmd = "SET "; 00293 cmd += escape(key); 00294 cmd += ' '; 00295 cmd += escape(value); 00296 cmd += ' '; 00297 cmd += escape(group); 00298 cmd += ' '; 00299 cmd += QByteArray().setNum(timeout); 00300 cmd += '\n'; 00301 return command(cmd); 00302 } 00303 00304 QByteArray KDEsuClient::getVar(const QByteArray &key) 00305 { 00306 QByteArray cmd = "GET "; 00307 cmd += escape(key); 00308 cmd += '\n'; 00309 QByteArray reply; 00310 command(cmd, &reply); 00311 return reply; 00312 } 00313 00314 QList<QByteArray> KDEsuClient::getKeys(const QByteArray &group) 00315 { 00316 QByteArray cmd = "GETK "; 00317 cmd += escape(group); 00318 cmd += '\n'; 00319 QByteArray reply; 00320 command(cmd, &reply); 00321 int index=0, pos; 00322 QList<QByteArray> list; 00323 if( !reply.isEmpty() ) 00324 { 00325 // kDebug(kdesuDebugArea()) << "Found a matching entry:" << reply; 00326 while (1) 00327 { 00328 pos = reply.indexOf( '\007', index ); 00329 if( pos == -1 ) 00330 { 00331 if( index == 0 ) 00332 list.append( reply ); 00333 else 00334 list.append( reply.mid(index) ); 00335 break; 00336 } 00337 else 00338 { 00339 list.append( reply.mid(index, pos-index) ); 00340 } 00341 index = pos+1; 00342 } 00343 } 00344 return list; 00345 } 00346 00347 bool KDEsuClient::findGroup(const QByteArray &group) 00348 { 00349 QByteArray cmd = "CHKG "; 00350 cmd += escape(group); 00351 cmd += '\n'; 00352 if( command(cmd) == -1 ) 00353 return false; 00354 return true; 00355 } 00356 00357 int KDEsuClient::delVar(const QByteArray &key) 00358 { 00359 QByteArray cmd = "DELV "; 00360 cmd += escape(key); 00361 cmd += '\n'; 00362 return command(cmd); 00363 } 00364 00365 int KDEsuClient::delGroup(const QByteArray &group) 00366 { 00367 QByteArray cmd = "DELG "; 00368 cmd += escape(group); 00369 cmd += '\n'; 00370 return command(cmd); 00371 } 00372 00373 int KDEsuClient::delVars(const QByteArray &special_key) 00374 { 00375 QByteArray cmd = "DELS "; 00376 cmd += escape(special_key); 00377 cmd += '\n'; 00378 return command(cmd); 00379 } 00380 00381 int KDEsuClient::ping() 00382 { 00383 return command("PING\n"); 00384 } 00385 00386 int KDEsuClient::exitCode() 00387 { 00388 QByteArray result; 00389 if (command("EXIT\n", &result) != 0) 00390 return -1; 00391 00392 return result.toInt(); 00393 } 00394 00395 int KDEsuClient::stopServer() 00396 { 00397 return command("STOP\n"); 00398 } 00399 00400 static QString findDaemon() 00401 { 00402 QString daemon = KStandardDirs::locate("bin", "kdesud"); 00403 if (daemon.isEmpty()) // if not in KDEDIRS, rely on PATH 00404 daemon = KStandardDirs::findExe("kdesud"); 00405 00406 if (daemon.isEmpty()) 00407 { 00408 kWarning(kdesuDebugArea()) << k_lineinfo << "daemon not found."; 00409 } 00410 return daemon; 00411 } 00412 00413 bool KDEsuClient::isServerSGID() 00414 { 00415 if (d->daemon.isEmpty()) 00416 d->daemon = findDaemon(); 00417 if (d->daemon.isEmpty()) 00418 return false; 00419 00420 KDE_struct_stat sbuf; 00421 if (KDE::stat(d->daemon, &sbuf) < 0) 00422 { 00423 kWarning(kdesuDebugArea()) << k_lineinfo << "stat():" << perror; 00424 return false; 00425 } 00426 return (sbuf.st_mode & S_ISGID); 00427 } 00428 00429 int KDEsuClient::startServer() 00430 { 00431 if (d->daemon.isEmpty()) 00432 d->daemon = findDaemon(); 00433 if (d->daemon.isEmpty()) 00434 return -1; 00435 00436 if (!isServerSGID()) { 00437 kWarning(kdesuDebugArea()) << k_lineinfo << "kdesud not setgid!"; 00438 } 00439 00440 // kdesud only forks to the background after it is accepting 00441 // connections. 00442 // We start it via kdeinit to make sure that it doesn't inherit 00443 // any fd's from the parent process. 00444 int ret = KToolInvocation::kdeinitExecWait(d->daemon); 00445 connect(); 00446 return ret; 00447 } 00448 00449 }
KDE 4.7 API Reference