KDEsu
client.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
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 <kapplication.h>
00035 #include <ktoolinvocation.h>
00036 #include <kde_file.h>
00037
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(900) << k_lineinfo << "$DISPLAY is not set\n";
00062 return;
00063 }
00064
00065
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(900) << k_lineinfo << "socket(): " << perror << "\n";
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(900) << 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
00118 if (getpeereid(d->sockfd, &euid, &egid) == 0)
00119 {
00120 if (euid != getuid())
00121 {
00122 kWarning(900) << "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
00132
00133
00134
00135 KDE_struct_stat s;
00136 if (KDE_lstat(d->sock, &s)!=0)
00137 {
00138 kWarning(900) << "stat failed (" << d->sock << ")";
00139 close(d->sockfd); d->sockfd = -1;
00140 return -1;
00141 }
00142 if (s.st_uid != getuid())
00143 {
00144 kWarning(900) << "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(900) << "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
00160 if (getsockopt(d->sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) == 0)
00161 {
00162 if (cred.uid != getuid())
00163 {
00164 kWarning(900) << "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 = str;
00177 copy.replace('\\', "\\\\");
00178 copy.replace('\"', "\\\"");
00179 copy.prepend("\"");
00180 copy.append("\"");
00181 return copy;
00182 }
00183
00184 int KDEsuClient::command(const QByteArray &cmd, QByteArray *result)
00185 {
00186 if (d->sockfd < 0)
00187 return -1;
00188
00189 if (send(d->sockfd, cmd, cmd.length(), 0) != (int) cmd.length())
00190 return -1;
00191
00192 char buf[1024];
00193 int nbytes = recv(d->sockfd, buf, 1023, 0);
00194 if (nbytes <= 0)
00195 {
00196 kWarning(900) << k_lineinfo << "no reply from daemon\n";
00197 return -1;
00198 }
00199 buf[nbytes] = '\000';
00200
00201 QByteArray reply = buf;
00202 if (reply.left(2) != "OK")
00203 return -1;
00204
00205 if (result)
00206 *result = reply.mid(3, reply.length()-4);
00207 return 0;
00208 }
00209
00210 int KDEsuClient::setPass(const char *pass, int timeout)
00211 {
00212 QByteArray cmd = "PASS ";
00213 cmd += escape(pass);
00214 cmd += ' ';
00215 cmd += QByteArray().setNum(timeout);
00216 cmd += '\n';
00217 return command(cmd);
00218 }
00219
00220 int KDEsuClient::exec(const QByteArray &prog, const QByteArray &user, const QByteArray &options, const QList<QByteArray> &env)
00221 {
00222 QByteArray cmd;
00223 cmd = "EXEC ";
00224 cmd += escape(prog);
00225 cmd += ' ';
00226 cmd += escape(user);
00227 if (!options.isEmpty() || !env.isEmpty())
00228 {
00229 cmd += ' ';
00230 cmd += escape(options);
00231 for (int i = 0; i < env.count(); ++i)
00232 {
00233 cmd += ' ';
00234 cmd += escape(env.at(i));
00235 }
00236 }
00237 cmd += '\n';
00238 return command(cmd);
00239 }
00240
00241 int KDEsuClient::setHost(const QByteArray &host)
00242 {
00243 QByteArray cmd = "HOST ";
00244 cmd += escape(host);
00245 cmd += '\n';
00246 return command(cmd);
00247 }
00248
00249 int KDEsuClient::setPriority(int prio)
00250 {
00251 QByteArray cmd;
00252 cmd += "PRIO ";
00253 cmd += QByteArray::number(prio);
00254 cmd += '\n';
00255 return command(cmd);
00256 }
00257
00258 int KDEsuClient::setScheduler(int sched)
00259 {
00260 QByteArray cmd;
00261 cmd += "SCHD ";
00262 cmd += QByteArray::number(sched);
00263 cmd += '\n';
00264 return command(cmd);
00265 }
00266
00267 int KDEsuClient::delCommand(const QByteArray &key, const QByteArray &user)
00268 {
00269 QByteArray cmd = "DEL ";
00270 cmd += escape(key);
00271 cmd += ' ';
00272 cmd += escape(user);
00273 cmd += '\n';
00274 return command(cmd);
00275 }
00276 int KDEsuClient::setVar(const QByteArray &key, const QByteArray &value, int timeout,
00277 const QByteArray &group)
00278 {
00279 QByteArray cmd = "SET ";
00280 cmd += escape(key);
00281 cmd += ' ';
00282 cmd += escape(value);
00283 cmd += ' ';
00284 cmd += escape(group);
00285 cmd += ' ';
00286 cmd += QByteArray().setNum(timeout);
00287 cmd += '\n';
00288 return command(cmd);
00289 }
00290
00291 QByteArray KDEsuClient::getVar(const QByteArray &key)
00292 {
00293 QByteArray cmd = "GET ";
00294 cmd += escape(key);
00295 cmd += '\n';
00296 QByteArray reply;
00297 command(cmd, &reply);
00298 return reply;
00299 }
00300
00301 QList<QByteArray> KDEsuClient::getKeys(const QByteArray &group)
00302 {
00303 QByteArray cmd = "GETK ";
00304 cmd += escape(group);
00305 cmd += '\n';
00306 QByteArray reply;
00307 command(cmd, &reply);
00308 int index=0, pos;
00309 QList<QByteArray> list;
00310 if( !reply.isEmpty() )
00311 {
00312
00313 while (1)
00314 {
00315 pos = reply.indexOf( '\007', index );
00316 if( pos == -1 )
00317 {
00318 if( index == 0 )
00319 list.append( reply );
00320 else
00321 list.append( reply.mid(index) );
00322 break;
00323 }
00324 else
00325 {
00326 list.append( reply.mid(index, pos-index) );
00327 }
00328 index = pos+1;
00329 }
00330 }
00331 return list;
00332 }
00333
00334 bool KDEsuClient::findGroup(const QByteArray &group)
00335 {
00336 QByteArray cmd = "CHKG ";
00337 cmd += escape(group);
00338 cmd += '\n';
00339 if( command(cmd) == -1 )
00340 return false;
00341 return true;
00342 }
00343
00344 int KDEsuClient::delVar(const QByteArray &key)
00345 {
00346 QByteArray cmd = "DELV ";
00347 cmd += escape(key);
00348 cmd += '\n';
00349 return command(cmd);
00350 }
00351
00352 int KDEsuClient::delGroup(const QByteArray &group)
00353 {
00354 QByteArray cmd = "DELG ";
00355 cmd += escape(group);
00356 cmd += '\n';
00357 return command(cmd);
00358 }
00359
00360 int KDEsuClient::delVars(const QByteArray &special_key)
00361 {
00362 QByteArray cmd = "DELS ";
00363 cmd += escape(special_key);
00364 cmd += '\n';
00365 return command(cmd);
00366 }
00367
00368 int KDEsuClient::ping()
00369 {
00370 return command("PING\n");
00371 }
00372
00373 int KDEsuClient::exitCode()
00374 {
00375 QByteArray result;
00376 if (command("EXIT\n", &result) != 0)
00377 return -1;
00378
00379 return result.toInt();
00380 }
00381
00382 int KDEsuClient::stopServer()
00383 {
00384 return command("STOP\n");
00385 }
00386
00387 static QString findDaemon()
00388 {
00389 QString daemon = KStandardDirs::locate("bin", "kdesud");
00390 if (daemon.isEmpty())
00391 daemon = KStandardDirs::findExe("kdesud");
00392
00393 if (daemon.isEmpty())
00394 {
00395 kWarning(900) << k_lineinfo << "daemon not found\n";
00396 }
00397 return daemon;
00398 }
00399
00400 bool KDEsuClient::isServerSGID()
00401 {
00402 if (d->daemon.isEmpty())
00403 d->daemon = findDaemon();
00404 if (d->daemon.isEmpty())
00405 return false;
00406
00407 KDE_struct_stat sbuf;
00408 if (KDE::stat(d->daemon, &sbuf) < 0)
00409 {
00410 kWarning(900) << k_lineinfo << "stat(): " << perror << "\n";
00411 return false;
00412 }
00413 return (sbuf.st_mode & S_ISGID);
00414 }
00415
00416 int KDEsuClient::startServer()
00417 {
00418 if (d->daemon.isEmpty())
00419 d->daemon = findDaemon();
00420 if (d->daemon.isEmpty())
00421 return -1;
00422
00423 if (!isServerSGID()) {
00424 kWarning(900) << k_lineinfo << "kdesud not setgid!\n";
00425 }
00426
00427
00428
00429
00430
00431 int ret = KToolInvocation::kdeinitExecWait(d->daemon);
00432 connect();
00433 return ret;
00434 }
00435
00436 }