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