• Skip to content
  • Skip to link menu
KDE 4.7 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 <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 }

KDEsu

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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.5
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