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