• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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 }

KDEsu

Skip menu "KDEsu"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal